Getting the big picture with readelf
Linux executables are in the Executable and Linkable Format (ELF). An ELF file is an object file, which can be viewed in two different ways, depending on what you are trying to do with it. Figure 10.9 shows the two views and different parts of the file that are relevant to each view.
FIGURE 10.9
The two views of an ELF file.
As shown in Figure 10.9, all ELF files have a header. When the file is being executed, the Program Header Table (PHT) that follows the header is read. The PHT describes various segments (large chunks) in the file. In the linking view the PHT is ignored and the Section Header Table (SHT) at the end of the file is read. The SHT describes various sections (which are subparts of segments) in the file.
The readelf utility parses different parts of an ELF file. Thanks to this handy program, there is little need to dig into the details of the ELF structures. The command readelf –file-header <file> will display the header information. The results of running this command against xingyi_bindshell are shown in Figure 10.10. From the figure, we see this is a 64-bit executable with nine program headers and thirty section headers. All ELF files begin with the “magic number” 0x7F, followed by the string “ELF”, or just 0x7F 0x45 0x4C 0x46 in hexadecimal.
FIGURE 10.10
The ELF header information for xingyi_bindshell.
The readelf –section-headers <file> command is used to display section information. The output from running readelf –section-headers -W xingyi_bindshell is shown in Figure 10.11. The -W option specifies wide format (not restricted to 80 characters of width). The sections are described in Table 10.2. The sections from this file are fairly typical.
FIGURE 10.11
Sections from the xingyi_bindshell file.
Table 10.2. Sections from xingyi_bindshell.
Name | Description |
---|---|
Null | |
.interp | Dynamic linker name |
.note.ABI-tag | Note containing “GNU” followed by architecture information |
.note.gnu.build-id | Unique ID that is same for debug and stripped programs (displayed by file) |
.gnu.hash | Describes a hash table (don’t worry if you don’t know what this is) |
.dynsym | Symbol table for dynamic linking |
.dynstr | Strings that are required for dynamic linking |
.gnu.version | Symbol Version Table that corresponds to .dynsym |
.gnu.version_r | Required symbol version definitions |
.rela.dyn | Relocation information for .dynamic |
.rela.plt | Relocation information for .plt |
.init | Initialization code for this program |
.plt | Procedure Linkage Table |
.text | The actual executable code (machine code) |
.fini | Termination code for this program |
.rodata | Read-only data |
.eh_frame_hdr | Exception handling C++ code for accessing .eh_frame |
.eh_frame | Exception handling (exceptions are used for error processing) |
.init_array | List of initialization code to call on startup |
.fini_array | List of termination code to call on termination |
.jcr | Information to register compiled Java classes |
.dynamic | Dynamic linking information |
.got | Global Offset Table (used for address resolution during relocation) |
.got.plt | Global Offset Table (used for address resolution during relocation) |
.data | Initialized data |
.bss | Unitialized data |
.comment | Comment (normally for version control) |
.shstrtab | Section names (section header string table) |
.symtab | Symbol Table |
.strtab | Symbol Table entry names |
Do not be overly concerned if you don’t understand all the of the sections described in Table 10.2. I would posit that the majority of professional programmers do not know about all of these either. The most import ones for our purposes are .text, .data, and .bss, which contain program code, initialized data, and uninitialized data, respectively. The fact that our file has all these sections suggests that the program was written in C, or similar language, and compiled with GCC (the GNU Compiler Collection). In theory, this should make it easier to reverse engineer than handcrafted Assembly code.
The command readelf –program-headers <file> is used to parse the
Program Header Table (PHT). The results of running this command on xingyi_bindshell are shown in Figure 10.12. As can be seen from the figure, most segments consist of a list of sections. Notable exceptions to this are segments 00 and 07 which contain the Program Header Table and stack, respectively. The description of each of these segments can be found in Table 10.3. The PHT also specifies where each segment should be loaded and what byte-alignment it requires.
FIGURE 10.12
Program Header Table for xingyi_bindshell.
Table 10.3. Segments from xingyi_bindshell.
Number and Type | Description |
---|---|
00 – PHDR | Program Header Table |
01 – INTERP | Dynamic linker to use (/lib64/ld-linux-x86-64.so.2) |
02 – LOAD | Portion of file to load into memory (first one) |
03 – LOAD | Portion of file to load into memory (second one) |
04 – DYNAMIC | Dynamic linking information |
05 – NOTE | Extra information |
06 - GNU_EH_FRAME | Exception handling information |
07 – GNU_STACK | The program stack |
08 – GNU_RELRO | Memory that should be read-only after relocation is done |
If a file has not been stripped, readelf can be used to list symbols. Partial output from running readelf –symbols -W xingyi_bindshell is shown in Figure
10.13. Notice that this output is more verbose than that produced by nm. It is also a bit more orderly.
FIGURE 10.13
Partial output from running readelf –symbols against xingyi_bindshell.
Each of the sections may be displayed using the command readelf –hex-dump=
FIGURE 10.14
Dumping a few sections from xingyi_bindshell with readelf –hex-dump.