官术网_书友最值得收藏!

ELF section headers

Now that we've looked at what program headers are, it is time to look at section headers. I really want to point out here the distinction between the two; I often hear people calling sections, segments, and vice versa. A section is not a segment. Segments are necessary for program execution, and within each segment, there is either code or data pided up into sections. A section header table exists to reference the location and size of these sections and is primarily for linking and debugging purposes. Section headers are not necessary for program execution, and a program will execute just fine without having a section header table. This is because the section header table doesn't describe the program memory layout. That is the responsibility of the program header table. The section headers are really just complimentary to the program headers. The readelf –l command will show which sections are mapped to which segments, which helps to visualize the relationship between sections and segments.

If the section headers are stripped (missing from the binary), that doesn't mean that the sections are not there; it just means that they can't be referenced by section headers and less information is available for debuggers and disassembler programs.

Each section contains either code or data of some type. The data could range from program data, such as global variables, or dynamic linking information that is necessary for the linker. Now, as mentioned previously, every ELF object has sections, but not all ELF objects have section headers, primarily when someone has deliberately removed the section header table, which is not the default.

Usually, this is because the executable has been tampered with (for example, the section headers have been stripped so that debugging is harder). All of GNU's binutils such as objcopy, objdump, and other tools such as gdb rely on the section headers to locate symbol information that is stored in the sections specific to containing symbol data. Without section headers, tools such as gdb and objdump are nearly useless.

Section headers are convenient to have for granular inspection over what parts or sections of an ELF object we are viewing. In fact, section headers make reverse engineering a lot easier since they provide us with the ability to use certain tools that require them. For instance, if the section header table is stripped, then we can't access a section such as .dynsym, which contains imported/exported symbols describing function names and offsets/addresses.

Note

Even if a section header table has been stripped from an executable, a moderate reverse engineer can actually reconstruct a section header table (and even part of a symbol table) by getting information from certain program headers since these will always exist in a program or shared library. We discussed the dynamic segment earlier and the different DT_TAG that contain information about the symbol table and relocation entries. We can use this to reconstruct other parts of the executable as shown in Chapter 8, ECFS – Extended Core File Snapshot Technology.

The following is what a 32-bit ELF section header looks like:

typedef struct {
uint32_t   sh_name; // offset into shdr string table for shdr name
    uint32_t   sh_type; // shdr type I.E SHT_PROGBITS
    uint32_t   sh_flags; // shdr flags I.E SHT_WRITE|SHT_ALLOC
    Elf32_Addr sh_addr;  // address of where section begins
    Elf32_Off  sh_offset; // offset of shdr from beginning of file
    uint32_t   sh_size;   // size that section takes up on disk
    uint32_t   sh_link;   // points to another section
    uint32_t   sh_info;   // interpretation depends on section type
uint32_t   sh_addralign; // alignment for address of section
uint32_t   sh_entsize;  // size of each certain entries that may be in section
} Elf32_Shdr;

Let's take a look at some of the most important sections and section types, once again allowing room to study the ELF(5) man pages and the official ELF specification for more detailed information about the sections.

The .text section

The .text section is a code section that contains program code instructions. In an executable program where there are also Phdr's, this section would be within the range of the text segment. Because it contains program code, it is of section type SHT_PROGBITS.

The .rodata section

The rodata section contains read-only data such as strings from a line of C code, such as the following command are stored in this section:

printf("Hello World!\n");

This section is read-only and therefore must exist in a read-only segment of an executable. So you will find .rodata within the range of the text segment (not the data segment). Because this section is read-only, it is of type SHT_PROGBITS.

The .plt section

The procedure linkage table (PLT) will be discussed in depth later in this chapter, but it contains code necessary for the dynamic linker to call functions that are imported from shared libraries. It resides in the text segment and contains code, so it is marked as type SHT_PROGBITS.

The .data section

The data section, not to be confused with the data segment, will exist within the data segment and contain data such as initialized global variables. It contains program variable data, so it is marked SHT_PROGBITS.

The .bss section

The bss section contains uninitialized global data as part of the data segment and therefore takes up no space on disk other than 4 bytes, which represents the section itself. The data is initialized to zero at program load time and the data can be assigned values during program execution. The bss section is marked SHT_NOBITS since it contains no actual data.

The .got.plt section

The Global offset table (GOT) section contains the global offset table. This works together with the PLT to provide access to imported shared library functions and is modified by the dynamic linker at runtime. This section in particular is often abused by attackers who gain a pointer-sized write primitive in heap or .bss exploits. We will discuss this in the ELF Dynamic Linking section of this chapter. This section has to do with program execution and therefore is marked SHT_PROGBITS.

The .dynsym section

The dynsym section contains dynamic symbol information imported from shared libraries. It is contained within the text segment and is marked as type SHT_DYNSYM.

The .dynstr section

The dynstr section contains the string table for dynamic symbols that has the name of each symbol in a series of null terminated strings.

The .rel.* section

Relocation sections contain information about how parts of an ELF object or process image need to be fixed up or modified at linking or runtime. We will discuss more about relocations in the ELF Relocations section of this chapter. Relocation sections are marked as type SHT_REL since they contain relocation data.

The .hash section

The hash section, sometimes called .gnu.hash, contains a hash table for symbol lookup. The following hash algorithm is used for symbol name lookups in Linux ELF:

uint32_t
dl_new_hash (const char *s)
{
        uint32_t h = 5381;

        for (unsigned char c = *s; c != '\0'; c = *++s)
                h = h * 33 + c;

        return h;
}
Note

h = h * 33 + c is often seen coded as h = ((h << 5) + h) + c

The .symtab section

The symtab section contains symbol information of type ElfN_Sym, which we will analyze more closely in the ELF symbols and relocations section of this chapter. The symtab section is marked as type SHT_SYMTAB as it contains symbol information.

The .strtab section

The .strtab section contains the symbol string table that is referenced by the st_name entries within the ElfN_Sym structs of .symtab and is marked as type SHT_STRTAB since it contains a string table.

The .shstrtab section

The shstrtab section contains the section header string table that is a set of null terminated strings containing the names of each section, such as .text, .data, and so on. This section is pointed to by the ELF file header entry called e_shstrndx that holds the offset of .shstrtab. This section is marked SHT_STRTAB since it contains a string table.

The .ctors and .dtors sections

The .ctors (constructors) and .dtors (destructors) sections contain function pointers to initialization and finalization code that is to be executed before and after the actual main() body of program code.

Note

The __constructor__ function attribute is sometimes used by hackers and virus writers to implement a function that performs an anti-debugging trick such as calling PTRACE_TRACEME so that the process traces itself and no debuggers can attach to it. This way the anti-debugging code gets executed before the program enters into main().

There are many other section names and types, but we have covered most of the primary ones found in a dynamically linked executable. One can now visualize how an executable is laid out with both phdrs and shdrs.

The text segments will be as follows:

  • [.text]: This is the program code
  • [.rodata]: This is read-only data
  • [.hash]: This is the symbol hash table
  • [.dynsym ]: This is the shared object symbol data
  • [.dynstr ]: This is the shared object symbol name
  • [.plt]: This is the procedure linkage table
  • [.rel.got]: This is the G.O.T relocation data

The data segments will be as follows:

  • [.data]: These are the globally initialized variables
  • [.dynamic]: These are the dynamic linking structures and objects
  • [.got.plt]: This is the global offset table
  • [.bss]: These are the globally uninitialized variables

Let's take a look at an ET_REL file (object file) section header with the readelf –S command:

ryan@alchemy:~$ gcc -c test.c
ryan@alchemy:~$ readelf -S test.o

The following are 12 section headers, starting at offset 0 x 124:

  [Nr] Name              Type            Addr           Off
       Size              ES              Flg  Lk   Inf   Al
  [ 0]                   NULL            00000000    000000
       000000            00                   0    0     0
  [ 1] .text             PROGBITS        00000000       000034
       000034            00              AX   0    0     4
  [ 2] .rel.text         REL             00000000       0003d0
       000010            08                   10   1     4
  [ 3] .data             PROGBITS        00000000 000068
       000000            00              WA   0    0     4
  [ 4] .bss              NOBITS          00000000       000068
       000000            00              WA   0    0     4
  [ 5] .comment          PROGBITS        00000000       000068
       00002b            01              MS   0    0     1
  [ 6] .note.GNU-stack   PROGBITS        00000000       000093
       000000            00                   0    0     1
  [ 7] .eh_frame         PROGBITS        00000000       000094
       000038            00              A    0    0     4
  [ 8] .rel.eh_frame     REL             00000000       0003e0
       000008            08                   10   7     4
  [ 9] .shstrtab         STRTAB          00000000       0000cc
       000057            00                   0    0     1
  [10] .symtab           SYMTAB          00000000       000304
       0000b0            10                   11   8     4
  [11] .strtab           STRTAB          00000000       0003b4
       00001a            00                   0    0     1

No program headers exist in relocatable objects (ELF files of type ET_REL) because .o files are meant to be linked into an executable, but not meant to be loaded directly into memory; therefore, readelf -l will yield no results on test.o. Linux loadable kernel modules are actually ET_REL objects and are an exception to the rule because they do get loaded directly into kernel memory and relocated on the fly.

We can see that many of the sections we talked about are present, but there are also some that are not. If we compile test.o into an executable, we will see that many new sections have been added, including .got.plt, .plt, .dynsym, and other sections that are related to dynamic linking and runtime relocations:

ryan@alchemy:~$ gcc evil.o -o evil
ryan@alchemy:~$ readelf -S evil

The following are 30 section headers, starting at offset 0 x 1140:

  [Nr] Name              Type            Addr           Off
       Size              ES              Flg  Lk  Inf   Al
  [ 0]                   NULL            00000000       000000
       000000            00                   0   0     0
  [ 1] .interp           PROGBITS        08048154       000154
       000013            00              A    0   0     1
  [ 2] .note.ABI-tag     NOTE            08048168       000168
       000020            00              A    0   0     4
  [ 3] .note.gnu.build-i NOTE            08048188       000188
       000024            00              A    0   0     4
  [ 4] .gnu.hash         GNU_HASH        080481ac       0001ac
       000020            04              A    5   0     4
  [ 5] .dynsym           DYNSYM          080481cc       0001cc
       000060            10              A    6   1     4
  [ 6] .dynstr           STRTAB          0804822c       00022c
       000052            00              A    0   0     1
  [ 7] .gnu.version      VERSYM          0804827e       00027e
       00000c            02              A    5   0     2
  [ 8] .gnu.version_r    VERNEED         0804828c       00028c
       000020            00              A    6   1     4
  [ 9] .rel.dyn          REL             080482ac       0002ac
       000008            08              A    5   0     4
  [10] .rel.plt          REL             080482b4       0002b4
       000020            08              A    5   12    4
  [11] .init             PROGBITS        080482d4       0002d4
       00002e            00              AX   0   0     4
  [12] .plt              PROGBITS        08048310       000310
       000050            04              AX   0   0     16
  [13] .text             PROGBITS        08048360       000360
       00019c            00              AX   0   0     16
  [14] .fini             PROGBITS        080484fc       0004fc
       00001a            00              AX   0   0     4
  [15] .rodata           PROGBITS        08048518       000518
       000008            00              A    0   0     4
  [16] .eh_frame_hdr     PROGBITS        08048520       000520
       000034            00              A    0   0     4
  [17] .eh_frame         PROGBITS        08048554       000554
       0000c4            00              A    0   0     4
  [18] .ctors            PROGBITS        08049f14       000f14
       000008            00              WA   0   0     4
  [19] .dtors            PROGBITS        08049f1c       000f1c
       000008            00              WA   0   0     4
  [20] .jcr              PROGBITS        08049f24       000f24
       000004            00              WA   0   0     4
  [21] .dynamic          DYNAMIC         08049f28       000f28
       0000c8            08              WA   6   0     4
  [22] .got              PROGBITS        08049ff0       000ff0
       000004            04              WA   0   0     4
  [23] .got.plt          PROGBITS        08049ff4       000ff4
       00001c            04              WA   0   0     4
  [24] .data             PROGBITS        0804a010       001010
       000008            00              WA   0   0     4
  [25] .bss              NOBITS          0804a018       001018
       000008            00              WA   0   0     4
  [26] .comment          PROGBITS        00000000       001018
       00002a            01              MS   0   0     1
  [27] .shstrtab         STRTAB          00000000       001042
       0000fc            00                   0   0     1
  [28] .symtab           SYMTAB          00000000       0015f0
       000420            10                   29  45    4
  [29] .strtab           STRTAB          00000000       001a10
       00020d            00                   0   0

As observed, a number of sections have been added, most notably the ones related to dynamic linking and constructors. I strongly suggest that the reader follows the exercise of deducing which sections have been changed or added and what purpose the added sections serve. Consult the ELF(5) man pages or the ELF specifications.

主站蜘蛛池模板: 凌海市| 高陵县| 乌鲁木齐县| 明溪县| 台北市| 盐亭县| 兰州市| 土默特右旗| 钟山县| 正安县| 乾安县| 武宁县| 建德市| 诏安县| 苍山县| 公安县| 双牌县| 儋州市| 肃北| 大城县| 共和县| 石渠县| 富顺县| 彭州市| 怀化市| 佳木斯市| 怀来县| 保德县| 长治市| 开阳县| 万年县| 白山市| 龙陵县| 北宁市| 广元市| 桐城市| 原平市| 囊谦县| 弋阳县| 宝兴县| 龙海市|