Skip to content

Instantly share code, notes, and snippets.

@bric3
Last active July 1, 2025 11:25
Show Gist options
  • Save bric3/27e5641c37f62a017cebe5208e347303 to your computer and use it in GitHub Desktop.
Save bric3/27e5641c37f62a017cebe5208e347303 to your computer and use it in GitHub Desktop.
Read the program header in an ELF binary. Based on `elf` man page.
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
/**
* ELF64 Program Interpreter Reader.
* <p>
* Reads the PT_INTERP (program interpreter) section from a 64-bit ELF file.
* This assumes the ELF file is little endian, but ELF file can be either, see
* <code>e_ident[EI_DATA]</code>.
*
* See https://man7.org/linux/man-pages/man5/elf.5.html
*/
public class ElfInterpreterReader {
/**
* Size of the e_ident[] array in ELF header (see ELF spec).
*/
private static final int EI_NIDENT = 16;
/**
* ELF magic numbers (first four bytes of ELF file).
*/
private static final byte ELFMAG0 = 0x7f;
private static final byte ELFMAG1 = 0x45; // 'E'
private static final byte ELFMAG2 = 0x4c; // 'L'
private static final byte ELFMAG3 = 0x46; // 'F'
/**
* <b>ELF64 Header (Elf64_Ehdr) field types and sizes</b>
* <table border="1" cellpadding="2">
* <tr><th>Type</th><th>Spec Term</th><th>Size (bytes)</th></tr>
* <tr><td>Elf64_Half</td> <td>e_type, e_machine, e_phentsize, e_phnum</td> <td>2</td></tr>
* <tr><td>Elf64_Word</td> <td>e_version</td> <td>4</td></tr>
* <tr><td>Elf64_Addr</td> <td>e_entry</td> <td>8</td></tr>
* <tr><td>Elf64_Off</td> <td>e_phoff</td> <td>8</td></tr>
* </table>
*/
private static final int E_IDENT_OFFSET = 0x00; /**< e_ident[16] (unsigned char[16]) */
private static final int E_TYPE_OFFSET = 0x10; /**< e_type (Elf64_Half) */
private static final int E_MACHINE_OFFSET = 0x12; /**< e_machine (Elf64_Half) */
private static final int E_VERSION_OFFSET = 0x14; /**< e_version (Elf64_Word) */
private static final int E_ENTRY_OFFSET = 0x18; /**< e_entry (Elf64_Addr) */
private static final int E_PHOFF_OFFSET = 0x20; /**< e_phoff (Elf64_Off) */
private static final int E_PHENTSIZE_OFFSET = 0x36; /**< e_phentsize (Elf64_Half) */
private static final int E_PHNUM_OFFSET = 0x38; /**< e_phnum (Elf64_Half) */
/**
* <b>ELF64 Program Header (Elf64_Phdr) layout</b>
* <table border="1" cellpadding="2">
* <tr><th>Field</th><th>Spec Term</th><th>Offset (hex)</th><th>Size (bytes)</th></tr>
* <tr><td>p_type</td> <td>Elf64_Word</td> <td>0x00</td> <td>4</td></tr>
* <tr><td>p_flags</td> <td>Elf64_Word</td> <td>0x04</td> <td>4</td></tr>
* <tr><td>p_offset</td> <td>Elf64_Off</td> <td>0x08</td> <td>8</td></tr>
* <tr><td>p_vaddr</td> <td>Elf64_Addr</td> <td>0x10</td> <td>8</td></tr>
* <tr><td>p_paddr</td> <td>Elf64_Addr</td> <td>0x18</td> <td>8</td></tr>
* <tr><td>p_filesz</td> <td>Elf64_Xword</td> <td>0x20</td> <td>8</td></tr>
* <tr><td>p_memsz</td> <td>Elf64_Xword</td> <td>0x28</td> <td>8</td></tr>
* <tr><td>p_align</td> <td>Elf64_Xword</td> <td>0x30</td> <td>8</td></tr>
* </table>
*/
private static final int PHDR_P_TYPE_OFFSET = 0x00; /**< p_type (Elf64_Word) */
private static final int PHDR_P_FLAGS_OFFSET = 0x04; /**< p_flags (Elf64_Word) */
private static final int PHDR_P_OFFSET_OFFSET = 0x08; /**< p_offset (Elf64_Off) */
private static final int PHDR_P_FILESZ_OFFSET = 0x20; /**< p_filesz (Elf64_Xword) */
/**
* ELF Program header type for PT_INTERP (holds program interpreter path).
* Value from ELF spec: PT_INTERP = 3
*/
private static final int PT_INTERP = 3;
/**
* Reads the program interpreter (PT_INTERP) from a 64-bit ELF file.
* @param args Command line arguments (first arg: path to ELF file)
* @throws IOException on file I/O errors
*/
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.err.println("Usage: java ElfInterpreterReader <ELF64-binary>");
System.exit(1);
}
String filename = args[0];
File elfFile = new File(filename);
if (!elfFile.exists()) {
System.err.println("Error: File not found: " + filename);
System.exit(2);
}
if (!elfFile.isFile() || !elfFile.canRead()) {
System.err.println("Error: File is not readable: " + filename);
System.exit(3);
}
try (RandomAccessFile file = new RandomAccessFile(filename, "r");
FileChannel channel = file.getChannel()) {
ByteBuffer buffer = ByteBuffer.allocate(64);
buffer.order(ByteOrder.LITTLE_ENDIAN); // Most Linux x86_64 ELF are little-endian
channel.read(buffer, 0);
buffer.flip();
if (buffer.get(E_IDENT_OFFSET) != ELFMAG0 ||
buffer.get(E_IDENT_OFFSET+1) != ELFMAG1 ||
buffer.get(E_IDENT_OFFSET+2) != ELFMAG2 ||
buffer.get(E_IDENT_OFFSET+3) != ELFMAG3) {
throw new IllegalArgumentException("Not an ELF file");
}
buffer.position(E_PHOFF_OFFSET);
long e_phoff = buffer.getLong();
buffer.position(E_PHENTSIZE_OFFSET);
int e_phentsize = buffer.getShort() & 0xFFFF;
buffer.position(E_PHNUM_OFFSET);
int e_phnum = buffer.getShort() & 0xFFFF;
for (int i = 0; i < e_phnum; i++) {
ByteBuffer phdr = ByteBuffer.allocate(e_phentsize);
phdr.order(ByteOrder.LITTLE_ENDIAN);
channel.read(phdr, e_phoff + i * e_phentsize);
phdr.flip();
int p_type = phdr.getInt(PHDR_P_TYPE_OFFSET);
if (p_type == PT_INTERP) {
long p_offset = phdr.getLong(PHDR_P_OFFSET_OFFSET);
long p_filesz = phdr.getLong(PHDR_P_FILESZ_OFFSET);
ByteBuffer interpBuf = ByteBuffer.allocate((int)p_filesz);
channel.read(interpBuf, p_offset);
interpBuf.flip();
byte[] interpBytes = new byte[(int)p_filesz];
interpBuf.get(interpBytes);
int end = 0;
while (end < interpBytes.length && interpBytes[end] != 0) end++;
String interpreter = new String(interpBytes, 0, end, StandardCharsets.US_ASCII);
System.out.println("Program Interpreter: " + interpreter);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment