Last active
July 1, 2025 11:25
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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