Skip to content

File format

Image : link.

ELF File format

strip - Remove symbol table and sections.

Remove a section :

$ gcc main.c -o binary
$ objdump -s --section .comment ./binary

./binary:     file format elf64-x86-64

Contents of section .comment:
 0000 4743433a 2028474e 55292031 312e312e  GCC: (GNU) 11.1.
 0010 3000                                 0.
$ strip -R .comment binary
$ objdump -s --section .comment ./binary

./binary:     file format elf64-x86-64

objdump: section '.comment' mentioned in a -j option, but not found in any input file

Removing the Section Headers Table

The section headers table is useful for a reverse engineer because it breaks down the binary’s address space into very specific chunks.

The section headers table isn’t actually needed for execution. You can remove it entirely.

There are four variables from the ELF header that are used to find, parse, and display the section headers table:

  1. Start of sections headers
  2. Size of section headers
  3. Number of section headers
  4. Section header string table index

Little Endian or Big Endian?

The sixth byte is called EI_DATA and it indicates the endianness of the binary.

This field isn't necessary to execute a binary. A system is either little-endian or big-endian (unless its ARM which can be bi-endian). As such, a loader probably doesn't need to check this byte because it can only execute one or the other.

Little endian :

$ gcc test.c -o test
$ readelf -a ./test | head
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:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
$ ./test
I'm still working !!!

Let's manually overwrite the EI_DATA field, to fake a big endian binary :

$ printf '\x02' | dd conv=notrunc of=./test bs=1 seek=5
1+0 records in
1+0 records out
1 byte copied, 3.3036e-05 s, 30.3 kB/s

Testing readelf :

$ readelf -a ./test | head
readelf: Warning: The e_shentsize field in the ELF header is larger than the size of an ELF section header
readelf: Error: Reading 125829120 bytes extends past end of file for section headers
readelf: Error: Section headers are not available!
readelf: Error: Too many program headers - 0xd00 - the file is not that big
readelf: Error: ELF Header:
Too many program headers - 0xd00 - the file is not that big
  Magic:   7f 45 4c 46 02 02 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, big endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              <unknown>: 300
  Machine:                           <unknown>: 0x3e00
  Version:                           0x1000000

Testing gdb :

$ gdb ./test
GNU gdb (GDB) 11.1

For help, type "help".
Type "apropos word" to search for commands related to "word"...
"/tmp/./test": not in executable format: file format not recognized
(gdb) Quit

Testing radare2 :

$ r2 ./test
 -- There is no F5 key in radare2 yet
[0x4010000000000000]> aaa
[af: Cannot find function at 0x4010000000000000try0 (aa)
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Finding and parsing C++ vtables (avrr)
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information (aanr)
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x4010000000000000]> pdf @main
Invalid address (main)
|ERROR| Invalid command 'pdf @main' (0x70)

Executing the binary :

$ ./test
I'm still working !!!

readelf, gdb and radare2 seems to be broken, but the binary is still working.

Flipping the Executable bit

An important part of the program headers is the flags field. The flag field describes if a segment is executable, writeable, and/or readable.

You can create a fake section headers table that reverses the program headers flag fields.

.init section

There are two special sections that you’ll often come across in ELF binaries : .init and .fini.

These sections contain code that execute before and after the main() function.

If you include .init and .fini sections in the fake section headers table can you potentially force the disassembler to disassemble at bad locations. It can be harder for a disassembler to find the entrypoint.

Mixing the Symbols

This section will discuss an anti reverse engineering technique that requires the dynamic symbol table to be present (no static compilation).

In this technique, you’ll append a fake dynamic symbol table to the end of the binary. Then you’ll repoint the offset in the .dynmsym section header to point to the fake symbol table. Finally, you’ll mix all of the symbol name pointers for the FUNC symbols. This will cause the disassemblers that rely on the sections table, instead of the program headers, to display incorrect function names in the disassembly