ELF Files

Executable and Linkable Format

Container for executable files, object files, shared libraries, and core dumps.

  • And other things out of this context like in Android.

Composed of several headers and sections:

  • Executable Header.

  • Several Program Headers (optional).

  • Several Sections, with a header and content.

Headers

Executable Header

Mandatory header, with basic information about the file.

  • Architecture.

  • Entry Point.

  • Header locations and number.

  • Type.

  • Type of data.

Follow the structure Elf64_Ehdr

  • defined in /usr/include/elf.h

Section Headers

Sections are unstructured placeholders of data (frequently coded) targeting the Linker.

  • Some sections are well-known and follow a defined structure.

  • Some sections can be arbitrary binary blobs.

  • Some sections may contain content not useful for execution.

  • Section order is irrelevant.

  • Symbols and relocation information are stored in sections.

Headers describe the properties of each section.

  • Name, type, flags, address when loaded, file offset, size, information…

Files without linking may omit section headers.

Sections

.init and .fini

Contains executable code required before/after the binary entry point is executed.

  • Initialization tasks to prepare/clean the memory space.

Some uses:

  • prepare profiling tasks (__gmon_start__)

  • Invoke global constructors/destructors (C++).

  • Save program arguments.

.text

It contains the main program code.

  • The main target of a Reverse Engineering activity.

  • They are allocated as executable and read-only.

  • It contains the user code, and additional code created by the compiler.

    • Cleanup/initialization functions, stack guards, etc.

In this section resides the program entry point.

  • When the binary is loaded, the execution flow is transferred to that address.

  • Related to the main function in a C program (but not the main).

.bss, .data, .rodata

.rodata: Read-only data.

  • Stores constant values.

  • Mapped to a page marked as read-only.

.data: Area with information to initialize variables.

  • As the data can be modified, the section is writable.

.bss: Uninitialized variables.

  • Memory is allocated for a variable that may be required, but nothing else is done.

  • As there is no data associated, the .bss doesn’t take space on the binary. Only instructs the system to reserve memory.

.plt, .got, .got.plt

Procedure Linkage Table and Global Offset Table.

  • .PLT: Code to relocate symbols.

  • .GOT: Array with addresses of each symbol requiring relocation.

    • .got is similar to .got.plt but it’s writable, while .got may be marked as Read Only as a security measure (-z relro).

    • Using a table (GOT) allows patching this table, while keeping libraries in the same address, shared to multiple processes.

Sections required for lazy binding (real-time relocation).

  • The linker needs to resolve the effective address of a code identified by a symbol (e.g., puts).

  • The code may be on the program, or an external library, mapped to the virtual memory.

  • .plt and .got ensure the symbol location is found and the code jumps around correctly.

  • This is executed as the symbols are required! (LAZY).

  • On Linux, the Env Variable LD_BIND_NOW forces linking by the linker (on program load).

    • Will increase performance during execution, but will slow down startup.

.rel.*, .rela.*

Tables containing information to the dynamic linker about the required relocations.

  • R_X86_64_GLOB_DAT: GOT offset should be filled with the symbol address (Lines 8-12).

  • R_X86_64_JUMP_SLO: Jump Slots to be represented in the .got.plt and .plt sections as shown previously (Line 16).

.dynamic

Contains information instructing the operating system/dynamic linker to load the binary.

  • Address of important tables.

  • Flags.

  • Required libraries.

  • Debug flags.

  • INIT/FINI addresses.

Last updated