ELF Format¶

Duality sections/segments¶

In the ELF format we have two kinds of containers:

• Sections

• Segments

A first sight it’s a bit disturbing because it’s two representations of the same data. In fact these two representations are used differently.

Sections are usually used at link time by the linker (e.g. /bin/ld) whereas segments are usually used at load time by kernel and loader (e.g. /lib/ld-linux.so).

Sections header table is not mandatory so you can execute an ELF binary without sections header table. Let’s take an example:

$readelf -Sh /bin/ls ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x4022f0 Start of program headers: 64 (bytes into file) Start of section headers: 37840 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 9 Size of section headers: 64 (bytes) Number of section headers: 28 Section header string table index: 27 Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .interp PROGBITS 0000000000400238 00000238 000000000000001c 0000000000000000 A 0 0 1 [ 2] .note.ABI-tag NOTE 0000000000400254 00000254 0000000000000020 0000000000000000 A 0 0 4 [ 3] .note.gnu.build-i NOTE 0000000000400274 00000274 0000000000000024 0000000000000000 A 0 0 4 [ 4] .gnu.hash GNU_HASH 0000000000400298 00000298 0000000000000060 0000000000000000 A 5 0 8 [ 5] .dynsym DYNSYM 00000000004002f8 000002f8 00000000000006d8 0000000000000018 A 6 1 8 [ 6] .dynstr STRTAB 00000000004009d0 000009d0 00000000000002f1 0000000000000000 A 0 0 1 [ 7] .gnu.version VERSYM 0000000000400cc2 00000cc2 0000000000000092 0000000000000002 A 5 0 2 ... [27] .shstrtab STRTAB 0000000000000000 000092d6 00000000000000f8 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), l (large) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)  Now we remove sections header with LIEF: from lief import ELF binary = ELF.Parse("/bin/ls") # Build an ELF binary header = binary.header header.section_header_offset = 0; header numberof_section_headers = 0; binary.write("out.bin");  Now if we run out.bin: $ ./out.bin
elf_reader.py elf_remove_section_table.py library_symbols_obfuscation out.bin
elf_rebuilder.py elf_symbol_obfuscation.py nm.py pe_reader.py


We can check that sections header table has been removed:

\$ readelf -Sh ./out.bin
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x4022f0
Start of program headers: 64 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 0
Section header string table index: 27 <corrupt: out of range>

There are no sections in this file.


As content can be used and updated by both sections and segments we can’t store it in sections/segments. Instead we use an interface to manage content. The manager interface looks like this:

The manager is implemented in the ELF::DataHandler::Handler classe.