Task 175: .ELF File Format

Task 175: .ELF File Format

1. List of Properties Intrinsic to the .ELF File Format

Based on the official ELF specification (Tool Interface Standard Executable and Linking Format), the intrinsic properties of the ELF file format refer to the structural elements defined in the file header, which provide the core identification, layout, and organization of the file. These are fixed-position fields in the ELF header that describe the file's type, architecture, memory layout pointers, and other metadata. ELF supports both 32-bit and 64-bit variants, with field sizes adjusting accordingly (e.g., addresses are 4 bytes in 32-bit and 8 bytes in 64-bit). The header is always at offset 0, and its size is typically 52 bytes (32-bit) or 64 bytes (64-bit).

Here is a comprehensive list of all properties (fields) from the ELF header:

  • e_ident (Identification Array): A 16-byte array for file identification.
  • EI_MAG0 to EI_MAG3 (offsets 0-3): Magic number (0x7F 'E' 'L' 'F') to identify as ELF.
  • EI_CLASS (offset 4): File class (0: Invalid, 1: 32-bit, 2: 64-bit).
  • EI_DATA (offset 5): Data encoding (0: Invalid, 1: Little-endian, 2: Big-endian).
  • EI_VERSION (offset 6): ELF version (1: Current).
  • EI_OSABI (offset 7): OS/ABI identification (e.g., 0: System V, 3: Linux).
  • EI_ABIVERSION (offset 8): ABI version (specific to OS/ABI).
  • EI_PAD (offsets 9-15): Padding bytes (typically 0, reserved for future use).
  • e_type: Object file type (e.g., 0: No type, 1: Relocatable, 2: Executable, 3: Shared object, 4: Core).
  • e_machine: Target machine architecture (e.g., 3: Intel x86, 62: x86-64, various others for ARM, SPARC, etc.).
  • e_version: Object file version (1: Current).
  • e_entry: Virtual address of the entry point (0 if none).
  • e_phoff: File offset to the program header table (0 if none).
  • e_shoff: File offset to the section header table (0 if none).
  • e_flags: Processor-specific flags (e.g., 0 for no flags on Intel architectures).
  • e_ehsize: Size of the ELF header in bytes.
  • e_phentsize: Size of each program header table entry.
  • e_phnum: Number of entries in the program header table.
  • e_shentsize: Size of each section header table entry.
  • e_shnum: Number of entries in the section header table.
  • e_shstrndx: Index of the section header string table (SHN_UNDEF if none).

These properties are "intrinsic" as they define the file's compatibility with the file system and loader on Unix-like systems, enabling parsing without external dependencies. Other components like program headers, section headers, symbol tables, and relocation entries are variable and pointed to by these fields, but they are not fixed "properties" of the format itself.

Here are two direct download links to benign sample .ELF files (minimal "Hello, World!" executables for x86-64 Linux, safe for testing):

3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop ELF Property Dump

This is an embeddable HTML snippet with JavaScript for a Ghost blog post. It creates a drag-and-drop area where users can drop an .ELF file. The script reads the file as an ArrayBuffer, parses the ELF header (handling 32-bit and 64-bit, little/big-endian), and dumps the properties to the screen in a pre-formatted block. It validates the magic number and aborts on invalid files.

Drag and drop an .ELF file here


4. Python Class for ELF File Handling

This Python class opens an .ELF file, decodes the header (handling 32/64-bit and endianness), reads and prints all properties to console. It also supports writing a modified header back to a new file (e.g., changing the entry point).

import struct
import os

class ElfParser:
    def __init__(self, filename):
        self.filename = filename
        self.properties = {}
        self.is_64bit = False
        self.little_endian = True
        self.header_format = None
        self.read_header()

    def read_header(self):
        with open(self.filename, 'rb') as f:
            e_ident = f.read(16)
            if e_ident[:4] != b'\x7fELF':
                raise ValueError("Invalid ELF magic number")
            
            elf_class = e_ident[4]
            endian = e_ident[5]
            self.is_64bit = elf_class == 2
            self.little_endian = endian == 1
            endian_char = '<' if self.little_endian else '>'
            
            # Header format after e_ident
            if self.is_64bit:
                self.header_format = endian_char + 'HHIIQQQIHHHHHH'
            else:
                self.header_format = endian_char + 'HHI II III HHHHHH'
            
            header_data = struct.unpack(self.header_format, f.read(struct.calcsize(self.header_format)))
            self.properties = {
                'EI_CLASS': elf_class,
                'EI_DATA': endian,
                'EI_VERSION': e_ident[6],
                'EI_OSABI': e_ident[7],
                'EI_ABIVERSION': e_ident[8],
                'e_type': header_data[0],
                'e_machine': header_data[1],
                'e_version': header_data[2],
                'e_entry': header_data[3],
                'e_phoff': header_data[4],
                'e_shoff': header_data[5],
                'e_flags': header_data[6],
                'e_ehsize': header_data[7],
                'e_phentsize': header_data[8],
                'e_phnum': header_data[9],
                'e_shentsize': header_data[10],
                'e_shnum': header_data[11],
                'e_shstrndx': header_data[12]
            }

    def print_properties(self):
        for key, value in self.properties.items():
            print(f"{key}: {value}")

    def write_header(self, output_filename, modifications=None):
        if modifications:
            self.properties.update(modifications)
        
        with open(self.filename, 'rb') as f_in:
            original_data = f_in.read()
        
        # Rebuild header
        e_ident = bytearray(16)
        e_ident[:4] = b'\x7fELF'
        e_ident[4] = self.properties['EI_CLASS']
        e_ident[5] = self.properties['EI_DATA']
        e_ident[6] = self.properties['EI_VERSION']
        e_ident[7] = self.properties['EI_OSABI']
        e_ident[8] = self.properties['EI_ABIVERSION']
        
        header_values = [
            self.properties['e_type'], self.properties['e_machine'], self.properties['e_version'],
            self.properties['e_entry'], self.properties['e_phoff'], self.properties['e_shoff'],
            self.properties['e_flags'], self.properties['e_ehsize'], self.properties['e_phentsize'],
            self.properties['e_phnum'], self.properties['e_shentsize'], self.properties['e_shnum'],
            self.properties['e_shstrndx']
        ]
        packed_header = struct.pack(self.header_format, *header_values)
        
        # Write to new file
        with open(output_filename, 'wb') as f_out:
            f_out.write(e_ident + packed_header + original_data[struct.calcsize('16s' + self.header_format[1:]):])

# Example usage:
# parser = ElfParser('example.elf')
# parser.print_properties()
# parser.write_header('modified.elf', {'e_entry': 0x1234})

5. Java Class for ELF File Handling

This Java class opens an .ELF file, decodes the header, reads and prints properties to console. It uses ByteBuffer for handling endianness and bit width. The write method creates a modified copy.

import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;

public class ElfParser {
    private String filename;
    private Map<String, Object> properties = new HashMap<>();
    private boolean is64Bit;
    private ByteOrder endian;
    private int headerSize;

    public ElfParser(String filename) throws IOException {
        this.filename = filename;
        readHeader();
    }

    private void readHeader() throws IOException {
        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
            FileChannel channel = raf.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(64); // Enough for header
            channel.read(buffer);
            buffer.flip();

            // Check magic
            if (buffer.get(0) != 0x7F || buffer.get(1) != 'E' || buffer.get(2) != 'L' || buffer.get(3) != 'F') {
                throw new IOException("Invalid ELF magic");
            }

            byte elfClass = buffer.get(4);
            byte data = buffer.get(5);
            is64Bit = elfClass == 2;
            endian = (data == 1) ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
            buffer.order(endian);

            properties.put("EI_CLASS", (int) elfClass);
            properties.put("EI_DATA", (int) data);
            properties.put("EI_VERSION", (int) buffer.get(6));
            properties.put("EI_OSABI", (int) buffer.get(7));
            properties.put("EI_ABIVERSION", (int) buffer.get(8));

            buffer.position(16);
            properties.put("e_type", (int) buffer.getShort());
            properties.put("e_machine", (int) buffer.getShort());
            properties.put("e_version", buffer.getInt());

            if (is64Bit) {
                properties.put("e_entry", buffer.getLong());
                properties.put("e_phoff", buffer.getLong());
                properties.put("e_shoff", buffer.getLong());
                headerSize = 64;
            } else {
                properties.put("e_entry", (long) buffer.getInt());
                properties.put("e_phoff", (long) buffer.getInt());
                properties.put("e_shoff", (long) buffer.getInt());
                headerSize = 52;
            }

            properties.put("e_flags", buffer.getInt());
            properties.put("e_ehsize", (int) buffer.getShort());
            properties.put("e_phentsize", (int) buffer.getShort());
            properties.put("e_phnum", (int) buffer.getShort());
            properties.put("e_shentsize", (int) buffer.getShort());
            properties.put("e_shnum", (int) buffer.getShort());
            properties.put("e_shstrndx", (int) buffer.getShort());
        }
    }

    public void printProperties() {
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }

    public void writeHeader(String outputFilename, Map<String, Object> modifications) throws IOException {
        if (modifications != null) {
            properties.putAll(modifications);
        }

        try (RandomAccessFile inRaf = new RandomAccessFile(filename, "r");
             RandomAccessFile outRaf = new RandomAccessFile(outputFilename, "rw")) {
            FileChannel inChannel = inRaf.getChannel();
            FileChannel outChannel = outRaf.getChannel();

            // Build new header buffer
            ByteBuffer header = ByteBuffer.allocate(headerSize).order(endian);
            header.put((byte) 0x7F).put((byte) 'E').put((byte) 'L').put((byte) 'F');
            header.put((byte) (int) properties.get("EI_CLASS"));
            header.put((byte) (int) properties.get("EI_DATA"));
            header.put((byte) (int) properties.get("EI_VERSION"));
            header.put((byte) (int) properties.get("EI_OSABI"));
            header.put((byte) (int) properties.get("EI_ABIVERSION"));
            for (int i = 0; i < 7; i++) header.put((byte) 0); // PAD

            header.putShort((short) (int) properties.get("e_type"));
            header.putShort((short) (int) properties.get("e_machine"));
            header.putInt((int) properties.get("e_version"));

            if (is64Bit) {
                header.putLong((long) properties.get("e_entry"));
                header.putLong((long) properties.get("e_phoff"));
                header.putLong((long) properties.get("e_shoff"));
            } else {
                header.putInt((int) (long) properties.get("e_entry"));
                header.putInt((int) (long) properties.get("e_phoff"));
                header.putInt((int) (long) properties.get("e_shoff"));
            }

            header.putInt((int) properties.get("e_flags"));
            header.putShort((short) (int) properties.get("e_ehsize"));
            header.putShort((short) (int) properties.get("e_phentsize"));
            header.putShort((short) (int) properties.get("e_phnum"));
            header.putShort((short) (int) properties.get("e_shentsize"));
            header.putShort((short) (int) properties.get("e_shnum"));
            header.putShort((short) (int) properties.get("e_shstrndx"));

            header.flip();
            outChannel.write(header);

            // Copy rest of file
            inChannel.position(headerSize);
            inChannel.transferTo(headerSize, inChannel.size() - headerSize, outChannel);
        }
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     ElfParser parser = new ElfParser("example.elf");
    //     parser.printProperties();
    //     Map<String, Object> mods = new HashMap<>();
    //     mods.put("e_entry", 0x1234L);
    //     parser.writeHeader("modified.elf", mods);
    // }
}

6. JavaScript Class for ELF File Handling

This JavaScript class (for Node.js) opens an .ELF file, decodes the header, reads and prints properties to console. It uses fs and DataView. The write method creates a modified copy using Buffer.

const fs = require('fs');

class ElfParser {
  constructor(filename) {
    this.filename = filename;
    this.properties = {};
    this.is64Bit = false;
    this.littleEndian = true;
    this.headerSize = 0;
    this.readHeader();
  }

  readHeader() {
    const buffer = fs.readFileSync(this.filename);
    const view = new DataView(buffer.buffer);

    if (view.getUint8(0) !== 0x7F || view.getUint8(1) !== 69 || view.getUint8(2) !== 76 || view.getUint8(3) !== 70) {
      throw new Error('Invalid ELF magic');
    }

    const elfClass = view.getUint8(4);
    const endian = view.getUint8(5);
    this.is64Bit = elfClass === 2;
    this.littleEndian = endian === 1;
    this.headerSize = this.is64Bit ? 64 : 52;

    this.properties = {
      EI_CLASS: elfClass,
      EI_DATA: endian,
      EI_VERSION: view.getUint8(6),
      EI_OSABI: view.getUint8(7),
      EI_ABIVERSION: view.getUint8(8)
    };

    let offset = 16;
    const readHalf = () => { const val = view.getUint16(offset, this.littleEndian); offset += 2; return val; };
    const readWord = () => { const val = view.getUint32(offset, this.littleEndian); offset += 4; return val; };
    const readAddr = () => {
      if (this.is64Bit) {
        const val = view.getBigUint64(offset, this.littleEndian);
        offset += 8;
        return val.toString(); // BigInt as string
      } else {
        return readWord();
      }
    };

    this.properties.e_type = readHalf();
    this.properties.e_machine = readHalf();
    this.properties.e_version = readWord();
    this.properties.e_entry = readAddr();
    this.properties.e_phoff = readAddr();
    this.properties.e_shoff = readAddr();
    this.properties.e_flags = readWord();
    this.properties.e_ehsize = readHalf();
    this.properties.e_phentsize = readHalf();
    this.properties.e_phnum = readHalf();
    this.properties.e_shentsize = readHalf();
    this.properties.e_shnum = readHalf();
    this.properties.e_shstrndx = readHalf();
  }

  printProperties() {
    for (const [key, value] of Object.entries(this.properties)) {
      console.log(`${key}: ${value}`);
    }
  }

  writeHeader(outputFilename, modifications = {}) {
    Object.assign(this.properties, modifications);

    const originalBuffer = fs.readFileSync(this.filename);
    const headerBuffer = Buffer.alloc(this.headerSize);

    // Write e_ident
    headerBuffer[0] = 0x7F;
    headerBuffer[1] = 69; // 'E'
    headerBuffer[2] = 76; // 'L'
    headerBuffer[3] = 70; // 'F'
    headerBuffer[4] = this.properties.EI_CLASS;
    headerBuffer[5] = this.properties.EI_DATA;
    headerBuffer[6] = this.properties.EI_VERSION;
    headerBuffer[7] = this.properties.EI_OSABI;
    headerBuffer[8] = this.properties.EI_ABIVERSION;
    // PAD 9-15: 0

    let offset = 16;
    const view = new DataView(headerBuffer.buffer);
    const writeHalf = (val) => { view.setUint16(offset, val, this.littleEndian); offset += 2; };
    const writeWord = (val) => { view.setUint32(offset, val, this.littleEndian); offset += 4; };
    const writeAddr = (val) => {
      if (this.is64Bit) {
        view.setBigUint64(offset, BigInt(val), this.littleEndian);
        offset += 8;
      } else {
        writeWord(Number(val));
      }
    };

    writeHalf(this.properties.e_type);
    writeHalf(this.properties.e_machine);
    writeWord(this.properties.e_version);
    writeAddr(this.properties.e_entry);
    writeAddr(this.properties.e_phoff);
    writeAddr(this.properties.e_shoff);
    writeWord(this.properties.e_flags);
    writeHalf(this.properties.e_ehsize);
    writeHalf(this.properties.e_phentsize);
    writeHalf(this.properties.e_phnum);
    writeHalf(this.properties.e_shentsize);
    writeHalf(this.properties.e_shnum);
    writeHalf(this.properties.e_shstrndx);

    const newBuffer = Buffer.concat([headerBuffer, originalBuffer.slice(this.headerSize)]);
    fs.writeFileSync(outputFilename, newBuffer);
  }
}

// Example usage:
// const parser = new ElfParser('example.elf');
// parser.printProperties();
// parser.writeHeader('modified.elf', { e_entry: '0x1234' });

7. C Class (Using Struct and Functions) for ELF File Handling

This C implementation uses a struct for the header properties. It opens an .ELF file, decodes/reads the header, prints properties to console. The write function creates a modified copy. It handles 32/64-bit and endianness (assuming host is little-endian; for big-endian hosts, additional swaps needed). Compiled with gcc -o elfparser elfparser.c.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct {
    uint8_t ei_class;
    uint8_t ei_data;
    uint8_t ei_version;
    uint8_t ei_osabi;
    uint8_t ei_abiversion;
    uint16_t e_type;
    uint16_t e_machine;
    uint32_t e_version;
    uint64_t e_entry; // Use 64-bit for both, cast for 32
    uint64_t e_phoff;
    uint64_t e_shoff;
    uint32_t e_flags;
    uint16_t e_ehsize;
    uint16_t e_phentsize;
    uint16_t e_phnum;
    uint16_t e_shentsize;
    uint16_t e_shnum;
    uint16_t e_shstrndx;
    int is_64bit;
    int little_endian;
    size_t header_size;
} ElfProperties;

void read_header(const char* filename, ElfProperties* props) {
    FILE* f = fopen(filename, "rb");
    if (!f) {
        perror("fopen");
        exit(1);
    }

    uint8_t e_ident[16];
    fread(e_ident, 1, 16, f);

    if (e_ident[0] != 0x7F || e_ident[1] != 'E' || e_ident[2] != 'L' || e_ident[3] != 'F') {
        fprintf(stderr, "Invalid ELF magic\n");
        exit(1);
    }

    props->ei_class = e_ident[4];
    props->ei_data = e_ident[5];
    props->is_64bit = props->ei_class == 2;
    props->little_endian = props->ei_data == 1;
    props->header_size = props->is_64bit ? 64 : 52;

    // For simplicity, assume host little-endian; no swap needed if matches
    // In production, add byte swaps if host != file endian

    fread(&props->e_type, 2, 1, f);
    fread(&props->e_machine, 2, 1, f);
    fread(&props->e_version, 4, 1, f);

    if (props->is_64bit) {
        fread(&props->e_entry, 8, 1, f);
        fread(&props->e_phoff, 8, 1, f);
        fread(&props->e_shoff, 8, 1, f);
    } else {
        uint32_t temp;
        fread(&temp, 4, 1, f); props->e_entry = temp;
        fread(&temp, 4, 1, f); props->e_phoff = temp;
        fread(&temp, 4, 1, f); props->e_shoff = temp;
    }

    fread(&props->e_flags, 4, 1, f);
    fread(&props->e_ehsize, 2, 1, f);
    fread(&props->e_phentsize, 2, 1, f);
    fread(&props->e_phnum, 2, 1, f);
    fread(&props->e_shentsize, 2, 1, f);
    fread(&props->e_shnum, 2, 1, f);
    fread(&props->e_shstrndx, 2, 1, f);

    props->ei_version = e_ident[6];
    props->ei_osabi = e_ident[7];
    props->ei_abiversion = e_ident[8];

    fclose(f);
}

void print_properties(ElfProperties* props) {
    printf("EI_CLASS: %u\n", props->ei_class);
    printf("EI_DATA: %u\n", props->ei_data);
    printf("EI_VERSION: %u\n", props->ei_version);
    printf("EI_OSABI: %u\n", props->ei_osabi);
    printf("EI_ABIVERSION: %u\n", props->ei_abiversion);
    printf("e_type: %u\n", props->e_type);
    printf("e_machine: %u\n", props->e_machine);
    printf("e_version: %u\n", props->e_version);
    printf("e_entry: %llu\n", (unsigned long long)props->e_entry);
    printf("e_phoff: %llu\n", (unsigned long long)props->e_phoff);
    printf("e_shoff: %llu\n", (unsigned long long)props->e_shoff);
    printf("e_flags: %u\n", props->e_flags);
    printf("e_ehsize: %u\n", props->e_ehsize);
    printf("e_phentsize: %u\n", props->e_phentsize);
    printf("e_phnum: %u\n", props->e_phnum);
    printf("e_shentsize: %u\n", props->e_shentsize);
    printf("e_shnum: %u\n", props->e_shnum);
    printf("e_shstrndx: %u\n", props->e_shstrndx);
}

void write_header(const char* filename, const char* output_filename, ElfProperties* props, int modify_entry) {
    // Example modification: set e_entry if flag set
    if (modify_entry) {
        props->e_entry = 0x1234;
    }

    FILE* in = fopen(filename, "rb");
    FILE* out = fopen(output_filename, "wb");
    if (!in || !out) {
        perror("fopen");
        exit(1);
    }

    // Write e_ident
    uint8_t e_ident[16] = {0x7F, 'E', 'L', 'F', props->ei_class, props->ei_data, props->ei_version, props->ei_osabi, props->ei_abiversion, 0,0,0,0,0,0,0};
    fwrite(e_ident, 1, 16, out);

    fwrite(&props->e_type, 2, 1, out);
    fwrite(&props->e_machine, 2, 1, out);
    fwrite(&props->e_version, 4, 1, out);

    if (props->is_64bit) {
        fwrite(&props->e_entry, 8, 1, out);
        fwrite(&props->e_phoff, 8, 1, out);
        fwrite(&props->e_shoff, 8, 1, out);
    } else {
        uint32_t temp = (uint32_t)props->e_entry;
        fwrite(&temp, 4, 1, out);
        temp = (uint32_t)props->e_phoff;
        fwrite(&temp, 4, 1, out);
        temp = (uint32_t)props->e_shoff;
        fwrite(&temp, 4, 1, out);
    }

    fwrite(&props->e_flags, 4, 1, out);
    fwrite(&props->e_ehsize, 2, 1, out);
    fwrite(&props->e_phentsize, 2, 1, out);
    fwrite(&props->e_phnum, 2, 1, out);
    fwrite(&props->e_shentsize, 2, 1, out);
    fwrite(&props->e_shnum, 2, 1, out);
    fwrite(&props->e_shstrndx, 2, 1, out);

    // Copy rest of file
    fseek(in, props->header_size, SEEK_SET);
    uint8_t buf[4096];
    size_t len;
    while ((len = fread(buf, 1, sizeof(buf), in)) > 0) {
        fwrite(buf, 1, len, out);
    }

    fclose(in);
    fclose(out);
}

// Example usage:
// int main(int argc, char** argv) {
//     ElfProperties props;
//     read_header("example.elf", &props);
//     print_properties(&props);
//     write_header("example.elf", "modified.elf", &props, 1); // Modify e_entry
//     return 0;
// }