ELF文件格式解析
前言:由于.KO文件是ELF格式的文件,所以這里先學習一下ELF文件格式
一、ELF文件格式
1.1 ELF文件介紹
ELF(Executable and Linking Format)是一種對象文件的格式,用于定義不同類型的對象文件(Object files)中都放了什么東西、以及都以什么樣的格式去放這些東西。
首先我們需要知道對象文件有哪些:
| 可重定位的對象文件(Relocatable file) | .o;.a;.ko |
| 可執行的對象文件(Executable file) | vi、gdb、及我們用鏈接器生成的可執行文件、bash shell 程序 |
| 可被共享的對象文件(Shared object file) | .so |
| 核心轉儲文件(Core Dump File) | 當進程意外終止時,系統可以將該進程的地址空間的內容及終止時的一些其他信息轉儲到核心轉儲文件 core dump |
想要知道一個對象文件屬于以上類型的哪一種,我們可以使用file +對象文件名命令來查看,例如:
file sum.o sub.o test.o libsub.so test sum.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped sub.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped test.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped libsub.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped test: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped那對于 file 命令來說,它又能如何知道這些信息?答案是在ELF對象文件的最前面有一個ELF文件頭,里面記載了所適用的處理器、對象文件類型等各種信息。
首先,ELF文件格式提供了兩種視圖,分別是鏈接視圖和執行視圖。
鏈接視圖是以節(section)為單位,執行視圖是以段(segment)為單位。鏈接視圖就是在鏈接時用到的視圖,而執行視圖則是在執行時用到的視圖。上圖左側的視角是從鏈接來看的,右側的視角是執行來看的。總個文件可以分為四個部分:
程序頭部表(Program Header Table),如果存在的話,告訴系統如何創建進程映像。
節區頭部表(Section Header Table)包含了描述文件節區的信息,比如大小、偏移等。
到這里可能大家伙還是不明白節區跟段的區別與聯系,這里在說明一下:
一個程序中最重要的部分是段和節,他們是真正的程序體,存儲程序執行所需要的數據,程序中有很多段,常見的有代碼段和數據段,段是由節組成的。多個節經過鏈接之后被合并成一個段。
段和節的信息用header來描述,程序頭是program header,節頭是section header。
程序中段的大小和數量不固定,節也是如此,因此需要一個專門的數據結構來描述他們,這個就是程序頭表和節頭表,他們用來存儲多個程序頭和節頭,相當于數組的概念。
2.2 ELF文件詳細內容解析
這里我們先分析一個.KO文件的內容,包括以下部分
① ELF Header
② section
③ 節區頭部表
接下來我們具體分析每個結構體:
2.2.1 ELF Header
我們打開/usr/include/elf.h文件,開始處是一個ELF頭部(ELF Header),用來描述整個文件的組織,這些信息獨立于處理器,也獨立于文件中的其余內容。
typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf32_Half e_type; /* Object file type */ Elf32_Half e_machine; /* Architecture */ Elf32_Word e_version; /* Object file version */ Elf32_Addr e_entry; /* Entry point virtual address */ Elf32_Off e_phoff; /* Program header table file offset */ Elf32_Off e_shoff; /* Section header table file offset */ Elf32_Word e_flags; /* Processor-specific flags */ Elf32_Half e_ehsize; /* ELF header size in bytes */ Elf32_Half e_phentsize; /* Program header table entry size */ Elf32_Half e_phnum; /* Program header table entry count */ Elf32_Half e_shentsize; /* Section header table entry size */ Elf32_Half e_shnum; /* Section header table entry count */ Elf32_Half e_shstrndx; /* Section header string table index */ } Elf32_Ehdr;我們來分析這個結構體:
① e_ident數組(16 bytes)
給出了ELF的一些標識信息
1) EI_MAG: 魔數(4 bytes): 標志此文件是一個 ELF 目標文件
1.1) e_ident[0]: 0x7f
1.2) e_ident[1]: ‘E’
1.3) e_ident[2]: ‘L’
1.4) e_ident[3]: ‘F’
2) EI_CLASS: 文件的類別(1 byte):
或者說是位寬
2.1) ELFCLASSNONE: 0: 非法類別
2.2) ELFCLASS32: 1: 32位目標
2.3) ELFCLASS64: 2: 64位目標
3) EI_DATA: 處理器特定數據的數據編碼方式
3.1) ELFDATANONE: 0: 非法數據編碼
3.2) ELFDATA2LSB: 1: 高位在前
3.3) ELFDATA2MSB: 2: 低位在前
4) EI_VERSION: ELF 頭部的版本號碼,此值必須是EV_CURRENT
5) EI_PAD: 標記e_ident 中未使用字節的開始,初始化為0
② e_type(2 bytes): 目標文件類型
1) ET_NONE: 0: 未知目標文件格式
2) ET_REL: 1: 可重定位文件
3) ET_EXEC: 2: 可執行文件
4) ET_DYN: 3: 共享目標文件
5) ET_CORE: 4: Core文件(轉儲格式0
6) ET_LOPROC: 0xff00: 特定處理器文件
7) ET_HIPROC: 0xffff: 特定處理器文件
ET_LOPROC和ET_HIPROC之間的取值用來標識與處理器相關的文件格式
③ e_machine(2 bytes): 文件的目標體系結構類型
1) EM_NONE: 0: 未指定
2) EM_M32: 1: AT&T WE 32100
3) EM_SPARC: 2: SPARC
4) EM_386: 3: Intel 80386
5) EM_68K: 4: Motorola 68000
6) EM_88K: 5: Motorola 88000
7) EM_860: 7: Intel 80860
8) EM_MIPS: 8: MIPS RS3000
④ e_version(4 bytes): 目標文件版本
1) EV_NONE: 0: 非法版本
2) EV_CURRENT: 1: 當前版本
⑤ e_entry(4 bytes): 程序入口的虛擬地址,如果目標文件沒有程序入口,可以為0
⑥ e_phoff(4 bytes): 程序頭部表格(Program Header Table)的偏移量(按字節計算),如果文件沒有程序頭部表格,可以為0
⑦ e_shoff(4 bytes): 節區頭部表格(Section Header Table)的偏移量(按字節計算)。如果文件沒有節區頭部表格,可以為0
⑧ e_flags(4 bytes): 保存與文件相關的,特定于處理器的標志。標志名稱采用EF_machine_flag的格式
⑨ e_ehsize(2 bytes): ELF頭部的大小(以字節計算)
⑩ e_phentsize(2 bytes): 程序頭部表格的表項大小(按字節計算)
11. e_phnum(2 bytes): 程序頭部表格的表項數目,可以為0
12. e_shentsize(2 bytes): 節區頭部表格的表項大小(按字節計算)
13. e_shnum(2 bytes): 節區頭部表格的表項數目,可以為0
14. e_shstrndx(2 bytes): 節區頭部表格中與節區名稱字符串表相關的表項的索引。如果文件沒有節區名稱字符串表,此參數可以為SHN_UNDEF
這里我們展示讀取到的頭部信息:
2.2.2 節區
節區滿足以下條件:
2.2.3 節區頭部表
typedef struct {Elf32_Word sh_name; /* Section name (string tbl index) */Elf32_Word sh_type; /* Section type */Elf32_Word sh_flags; /* Section flags */Elf32_Addr sh_addr; /* Section virtual addr at execution */Elf32_Off sh_offset; /* Section file offset */Elf32_Word sh_size; /* Section size in bytes */Elf32_Word sh_link; /* Link to another section */Elf32_Word sh_info; /* Additional section information */Elf32_Word sh_addralign; /* Section alignment */Elf32_Word sh_entsize; /* Entry size if section holds table */ } Elf32_Shdr;sh_name(4 bytes): 節區名稱,是"節區頭部字符串表節區"(Section Header String Table Section)的索引。名字是一個NULL結尾的字符串。
所謂"節區頭部字符串表節區",就是一段連續的保存每個節區名字的ascii字符的地址空間
sh_type(4 bytes): 為節區的內容和語義進行分類
1) SHT_NULL: 0: 此值標志節區頭部是非活動的,沒有對應的節區。此節區頭部中的其他成員取值無意義
2) SHT_PROGBITS: 1: 此節區包含程序定義的信息,其格式和含義都由程序來解釋
3) SHT_SYMTAB: 2: 此節區包含一個符號表。目前目標文件對每種類型的節區都只能包含一個,不過這個限制將來可能發生變化,通常情況下,SHT_SYMTAB節區提供用于鏈接編輯(指ld而言)的符號,盡管也可用來實現動態
鏈接。
4) SHT_STRTAB: 3: 此節區包含字符串表。目標文件可能包含多個字符串表節區。
5) SHT_RELA: 4: 此節區包含重定位表項,其中可能會有補齊內容(addend),例如32位目標文件中的Elf32_Rela類型。目標文件可能擁有多個重定位節區
6) SHT_HASH: 5: 此節區包含符號哈希表。所有參與動態鏈接的目標都必須包含一個符號哈希表。目前,一個目標文件只能包含一個哈希表,不過此限制將來可能會解除。
7) SHT_DYNAMIC: 6: 此節區包含動態鏈接的信息。目前一個目標文件中只能包含一個動態節區,將來可能會取消這一限制。
8) SHT_NOTE: 7: 此節區包含以某種方式來標記文件的信息。
9) SHT_NOBITS: 8: 這種類型的節區不占用文件中的空間,其他方面和SHT_PROGBITS相似。盡管此節區不包含任何字節,成員sh_offset中還是會包含概念性的文件偏移
10) SHT_REL: 9: 此節區包含重定位表項,其中沒有補齊(addends),例如32位目標文件中的Elf32_rel類型。目標文件中可以擁有多個重定位節區
11) SHT_SHLIB: 10: 此節區被保留,不過其語義是未規定的。包含此類型節區的程序與ABI不兼容。
12) SHT_DYNSYM: 11: 作為一個完整的符號表,它可能包含很多對動態鏈接而言不必要的符號。因此,目標文件也可以包含一個SHT_DYNSYM節區,其中保存動態鏈接符號的一個最小集合,以節省空間
13) SHT_LOPROC(0x70000000)~SHT_HIPROC(0x7FFFFFFF): 這一段(包括兩個邊界),是保留給處理器專用語義的
14) SHT_LOUSER(0X80000000): 此值給出保留給應用程序的索引下界
15) SHT_HIUSER(0X8FFFFFFF): 此值給出保留給應用程序的索引上界
sh_flags(4 bytes): sh_flags字段定義了一個節區中包含的內容是否可以修改、是否可以執行等信息。如果一個標志位被設置,則該位取值為1。未定義的各位都設置為0(這是一種bitmap位圖表示法)
1) SHF_WRITE: 0x1: 節區包含進程執行過程中將可寫的數據
2) SHF_ALLOC: 0x2: 此節區在進程執行過程中占用內存。某些控制節區并不出現于目標文件的內存映像中,對于那些節區,此位應設置為0
3) SHF_EXECINSTR: 0x4: 節區包含可執行的機器指令
4) SHF_MASKPROC: 0xF0000000: 所有包含于此掩碼中的四位都用于處理器專用的語義
sh_addr(4 bytes): 如果節區將出現在進程的內存映像中,此成員給出節區的第一個字節應處的位置。否則,此字段為0
sh_offset(4 bytes): 此成員的取值給出節區的第一個字節與文件頭之間的偏移。不過,SHT_NOBITS類型的節區不占用文件的空間,因此其sh_offset成員給出的是其概念性的偏移
sh_size(4 bytes): 此成員給出節區的長度(字節數)。除非節區的類型是SHT_NOBITS,否則節區占用文件中的sh_size 字節。類型為SHT_NOBITS的節區長度可能非零,不過卻不占用文件中的空間
sh_link(4 bytes): 此成員給出節區頭部表索引鏈接。其具體的解釋依賴于節區類型
根據節區類型的不同,sh_link和sh_info 的具體含義也有所不同
sh_type sh_link sh_info
SHT_DYNAMIC 此節區中條目所用到的字符串表格的節區頭部索引 0
SHT_HASH 此哈希表所適用的符號表的節區頭部索引 0
SHT_REL、SHT_RELA 相關符號表的節區頭部索引 重定位所適用的節區的節區頭部索引
SHT_SYMTAB、SHT_DYNSYM 相關聯的字符串表的節區頭部索引 最后一個局部符號(綁定 STB_LOCAL)的符號表索引值加一
其它 SHN_UNDEF 0
sh_info(4 bytes): 此成員給出附加信息,其解釋依賴于節區類型
sh_addralign(4 bytes): 某些節區帶有地址對齊約束。例如,如果一個節區保存一個doubleword,那么系統必須保證整個節區能夠按雙字對齊。sh_addr對sh_addralign取模,結果必須為0。目前僅允許取值為0和2的冪
次數。數值0和1表示節區沒有對齊約束
sh_entsize(4 bytes): 某些節區中包含固定大小的項目,如符號表。對于這類節區,此成員給出每個表項的長度字節數。如果節區中并不包含固定長度表項的表格,此成員取值為0
這里我們展示讀取到的節區頭部表:
2.2.4 部分節區說明
1 .rel.xxxxx
對應xxxxx section的relocate表,用于符號重定位,比如說 .rel.text 就是 .text 的重定向表所在的節區
Relocation section '.rel.text' at offset 0xe52c contains 67 entries:Offset Info Type Sym.Value Sym. Name 00000050 00000e02 R_ARM_ABS32 00000000 .rodata.str1.4 00000054 00005802 R_ARM_ABS32 00000000 memcpy 00000058 00006902 R_ARM_ABS32 00000000 printk 0000005c 00000e02 R_ARM_ABS32 00000000 .rodata.str1.4 00000120 00006c02 R_ARM_ABS32 00000000 _ctype 00000124 00007502 R_ARM_ABS32 00000000 strlen 000001c8 00007502 R_ARM_ABS32 00000000 strlen 000001cc 00006402 R_ARM_ABS32 00000000 match_int 000001d0 00006c02 R_ARM_ABS32 00000000 _ctype 000001d4 00007302 R_ARM_ABS32 00000000 match_hex 000001fc 0000521c R_ARM_CALL 00000128 parse_interger 0000023c 00006302 R_ARM_ABS32 00000000 kallsyms_lookup_name 00000290 0000741c R_ARM_CALL 00000060 str_trim 0000029c 0000521c R_ARM_CALL 00000128 parse_interger2 .symtab 模塊中所有的符號記錄都在這里面
eadelf -s ssp.ko | moreSymbol table '.symtab' contains 53529 entries:Num: Value Size Type Bind Vis Ndx Name0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 SECTION LOCAL DEFAULT 1 2: 00000000 0 SECTION LOCAL DEFAULT 3 3: 00000000 0 SECTION LOCAL DEFAULT 5 4: 00000000 0 SECTION LOCAL DEFAULT 7 5: 00000000 0 SECTION LOCAL DEFAULT 8 6: 00000000 0 SECTION LOCAL DEFAULT 10 7: 00000000 0 SECTION LOCAL DEFAULT 13 8: 00000000 0 SECTION LOCAL DEFAULT 23 9: 00000000 0 SECTION LOCAL DEFAULT 24 10: 00000000 0 SECTION LOCAL DEFAULT 31 11: 00000000 0 NOTYPE LOCAL DEFAULT 1 $a12: 00000088 0 NOTYPE LOCAL DEFAULT 1 $d13: 00000000 0 NOTYPE LOCAL DEFAULT 28 $d14: 00000098 0 NOTYPE LOCAL DEFAULT 1 $a15: 00000150 0 NOTYPE LOCAL DEFAULT 1 $d16: 00000164 0 NOTYPE LOCAL DEFAULT 1 $a3…shstrtab
節區名稱字符串表,所有的節區名稱字符串都放在這里
readelf -p 35 ssp.ko String dump of section '.shstrtab':[ 1] .symtab[ 9] .strtab[ 11] .shstrtab[ 1b] .rel.text[ 25] .rel.text.unlikely[ 38] .rel.init.text[ 47] .rel.rodata[ 53] .rodata.str1.4[ 62] .rel.pv_table[ 70] .ARM.extab.text.unlikely[ 89] .rel.ARM.exidx.text.unlikely[ a6] .ARM.extab.init.text[ bb] .rel.ARM.exidx.init.text[ d4] .ARM.extab.exit.text[ e9] .rel.ARM.exidx.exit.text[ 102] .modinfo[ 10b] .ARM.extab[ 116] .rel.data[ 120] .rel.gnu.linkonce.this_module[ 13e] .rel.ARM.exidx[ 14d] .note.gnu.build-id[ 160] .bss[ 165] .comment[ 16e] .note.GNU-stack[ 17e] .ARM.attributes4 .strtab
符號名稱字符串表,這里記錄了所有的符號的名稱字符串,其格式即字符串表格式,和節區頭部名稱字符串表格式相同。
5 .gnu.linkonce.this_module
struct module 實例所在的節區
總結
- 上一篇: 新开的淘宝店铺要怎么操作才能排名靠前?
- 下一篇: python截取视频_python+ff