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:


alternate text

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