ELF Files
Last updated
Last updated
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.
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
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.
.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.