1. 程式人生 > >ELF檔案型別 ELF程式頭 ELF節頭 ELF符號

ELF檔案型別 ELF程式頭 ELF節頭 ELF符號

ELF檔案型別

首先ELF檔案可以被標記為以下幾個型別:

  • ET_NONE:未知型別。
  • ET_REL:重定位檔案,型別標記為relocatable意為著該檔案被標記為了一段可重定位的程式碼段,有時也稱為目標檔案。可重定位目標檔案通常是還未被連結到可執行程式的一段位置獨立的程式碼。編譯完是.o的格式。
  • ET_EXEC:可執行檔案,型別為executable,表明這個檔案被標記為可執行檔案。這種型別被稱為程式,是一個程序開始的入口。
  • ET_DYN:共享目標檔案。型別為dynamic,以為著該檔案被標記為了一個動態的可連結的目標檔案,也稱為共享庫。這類共享庫會在程式執行時被裝載連結到程式的程序映象中。
  • ET_CORE:核心檔案。載程式崩潰或者程序傳遞了一個SIGSEGV訊號(分段違規)時,會在核心檔案中記錄整個程序的映象資訊。可以使用GDB讀取這類檔案來輔助除錯並查詢程式崩潰的原因。

用到的一些命令

readelf -h 檢視ELF檔案,可以看到原始的ELF檔案頭。如下所示
readelf -h
ELF檔案頭從檔案0偏移量開始,除了檔案頭之後剩餘部分的一個對映。記錄了ELF型別,結構,和程式開始執行的入口地址並提供其他ELF(節頭和程式頭偏移量)。

程式頭

ELF程式頭是對二進位制檔案中段的描述,是程式裝載必需的部分。段載核心裝載時被解析。描述了磁碟上可執行檔案的記憶體佈局,以及如何對映到記憶體中,可以通過引用原始ELF頭中名為e_phoff(程式頭表偏移量)的偏移量來得到程式頭表。

常見有5種程式頭型別:

PT_LOAD,一個可執行檔案只是一個PT_LOAD型別的段。這類程式頭描述的時可裝載的段。一般一個需要動態連結的ELF可執行檔案通常由兩個可裝載的段:存放程式程式碼的text段,存放全域性變數和動態連結的data段。
上面兩個段會被對映到記憶體,根據p_align中存放的值在記憶體中對齊。程式頭主要描述程式執行時在記憶體中的佈局。
PT_DYNAMIC,動態段時動態連結可執行檔案持有的,包含動態連結器所必有的一些資訊,包含了一些標記值和指標,比如執行是需要連結的共享庫列表,全域性偏移表,重定位條目的相關資訊。
PT_NOTE,儲存了與特定供應商或者系統相關的附加資訊。
PT_INTERP

,將資訊二號位置存放在一個NULL為終止符的字串中,對程式直譯器位置的描述。
PT_PHDR,段儲存了程式頭標本身的位置和大小。Phdr表儲存了所有的Phdr對檔案(以及記憶體映象)中段的描述資訊。

用到的一些命令

readelf -l <檔名>命令檢視檔案的Phdr表:

readelf -l

ELF節頭

段是程式執行的必要組成部分,每個段中,會有程式碼或者資料被劃分為不同的節,節頭表是對這些節位置和大小的描述。用於連結和除錯。沒有節頭表程式仍可以政策執行。因為節頭沒有對程式的記憶體佈局進行描述,這是程式頭表的任務。用readelf可以看到節和段的關係。

  • .text節是儲存了程式程式碼指令的程式碼節。一段可執行程式,存在Phdr,.text就會存在於text段中。由於.text節儲存了程式程式碼,因此節的型別為SHT_PROGBITS。
  • .rodata 儲存只讀資料。型別SHT_PROGBITS。
  • .plt 過程連結表(Procedure Linkage Table),包含動態連結器呼叫從共享庫匯入的函式所必須的相關程式碼。存在於text段中,型別SHT_PROGBITS。
  • .bss節儲存未初始化全域性資料,是data的一部分。程式載入時資料被初始化成0,在程式執行期間可以賦值,未儲存實際資料,型別SHT_NOBITS。
  • .got節儲存全域性偏移表。它和.plt節一起提供了對匯入的共享庫函式訪問的入口。由動態連結器在執行時進行修改。如果攻擊者獲得堆或者.bss漏洞的一個指標大小寫原語,就可以對該節任意修改。型別SHT_PROGBITS。
  • .dynsym節儲存共享庫匯入的動態符號資訊,該節在text段中,型別SHT_DYNSYM。
  • .dynstr儲存動態符號字串表,存放一系列字串,代表了符號的名稱,以空字元作為終止符。
  • .rel節儲存重定位資訊,型別SHT_REL。
  • .hash節,也稱為.gnu.hash,儲存一個查詢符號散列表。
  • .symtab節,儲存了ElfN_Sym型別的符號資訊,型別SHT_SYMTAB。
  • strtab節,儲存符號字串表,表中內容被.symtab的ElfN_Sym結構中的st_name條目引用。型別SHT_SYMTAB。
  • .shstrtab節,儲存節頭字串表,以空字元終止的字串集合,儲存了每個節節名,如.text,.data等。有個e_shsrndx的ELF檔案頭條目會指向.shstrtab節,e_shstrndx中儲存了.shstrtab的偏移量。這節的型別是SHT_SYMTAB。
  • .ctors和.dtors節,前者構造器,後者析構器,指向建構函式和解構函式的函式指標,建構函式是在main函式執行前需要執行的程式碼,析構是main函式之後需要執行的程式碼。
    可以使用 readelf -S 檢視ET_REL檔案的節頭。
[email protected]:~/Desktop# readelf -S UDP_25000
There are 28 section headers, starting at offset 0xebb64:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .note.ABI-tag     NOTE            080480d4 0000d4 000020 00   A  0   0  4
  [ 2] .init             PROGBITS        080480f4 0000f4 000017 00  AX  0   0  4
  [ 3] .text             PROGBITS        08048120 000120 0b5cc0 00  AX  0   0 32
  [ 4] __libc_thread_fre PROGBITS        080fdde0 0b5de0 0000e2 00  AX  0   0  4
  [ 5] __libc_freeres_fn PROGBITS        080fdec4 0b5ec4 000f6e 00  AX  0   0  4
  [ 6] .fini             PROGBITS        080fee34 0b6e34 00001a 00  AX  0   0  4
  [ 7] .rodata           PROGBITS        080fee60 0b6e60 01d89a 00   A  0   0 32
  [ 8] __libc_atexit     PROGBITS        0811c6fc 0d46fc 000004 00   A  0   0  4
  [ 9] __libc_subfreeres PROGBITS        0811c700 0d4700 00003c 00   A  0   0  4
  [10] __libc_thread_sub PROGBITS        0811c73c 0d473c 000004 00   A  0   0  4
  [11] .eh_frame         PROGBITS        0811c740 0d4740 00fc30 00   A  0   0  4
  [12] .gcc_except_table PROGBITS        0812c370 0e4370 00439f 00   A  0   0  4
  [13] .tdata            PROGBITS        08131000 0e9000 000014 00 WAT  0   0  4
  [14] .tbss             NOBITS          08131014 0e9014 000018 00 WAT  0   0  4
  [15] .ctors            PROGBITS        08131014 0e9014 000028 00  WA  0   0  4
  [16] .dtors            PROGBITS        0813103c 0e903c 00000c 00  WA  0   0  4
  [17] .jcr              PROGBITS        08131048 0e9048 000004 00  WA  0   0  4
  [18] .data.rel.ro      PROGBITS        08131060 0e9060 00063c 00  WA  0   0 32
  [19] .got              PROGBITS        0813169c 0e969c 00005c 04  WA  0   0  4
  [20] .got.plt          PROGBITS        081316f8 0e96f8 00000c 04  WA  0   0  4
  [21] .data             PROGBITS        08131720 0e9720 0014b4 00  WA  0   0 32
  [22] .bss              NOBITS          08132be0 0eabd4 008314 00  WA  0   0 32
  [23] __libc_freeres_pt NOBITS          0813aef4 0eabd4 000018 00  WA  0   0  4
  [24] .comment          PROGBITS        00000000 0eabd4 000e6a 00      0   0  1
  [25] .shstrtab         STRTAB          00000000 0eba3e 000126 00      0   0  1
  [26] .symtab           SYMTAB          00000000 0ebfc4 0156b0 10     27 1276  4
  [27] .strtab           STRTAB          00000000 101674 02935f 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

因為可重定位檔案不會存在程式頭,.o型別檔案會被連結到可執行檔案中,但是不會
直接載入到記憶體,所以readelf -l xxx.o 不會得到想要的結果。不過Linux中可載入核心模組(LKM)是個例外,LKM是ET_REL型別的檔案,它會被直接載入進核心的記憶體中,並自動進行重定位。

ELF符號

符號是對某些型別的資料或者程式碼的符號引用。在大多數共享庫和動態連結可執行檔案中,存在兩個符號表。.dynsym和.symtab。
.dynsym儲存了引用來自外部檔案符號的全域性符號,是.symtab的子集,.symtab儲存了所有符號。.dynsym只儲存了動態/全域性符號。

用兩個符號表的原因

使用readelf -S 看到了一部分節被標記了A(ALLOC),WA(WRITE,ALLOC)或者AX(ALLOC/EXEC)。.dynsym被標記了ALLOC的,.symtab則沒有標記。ALLOC表示有該標記的節會在執行時分配並裝載進入記憶體,而.symtab不是執行是必須的,不會被裝載到記憶體中。.dynsym儲存的符號只能在執行時被解析,所以時執行時動態聯結器所需要的唯一符號。.dynsym符號表對於動態連結可執行檔案的執行來說必需的,而.symtab符號表只是用來除錯和連結的。

參考文獻

[1] 《Linux二進位制分析》Ryan O’Neill