天天看點

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

gcc生成執行檔案過程為:源檔案(*.c檔案)編譯成對象檔案(*.o檔案);

連結程式ld,把對象檔案(*.o檔案)連結成可執行程式。

是以要透徹連結的過程, 需要先了解對象檔案(*.o檔案)是怎樣構成的?

下面用個簡單的例子來說明:

#includeint global_var=5;extern int other_file_var;int main(){ int a=1;

int b=a other_file_var; return 0;

}gcc -c test.c -o test.o 生成test.o;

檢視test.o内容(vim test.o):

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

圖1

看到的是一堆亂碼,因為檢視方式不對, 就像mp3格式的檔案需要用音樂播放器才能播放一樣,對象檔案(*.o)是elf格式的, 需要用objdump, readelf 工具來檢視。

從elf格式的官方文檔,可以了解到elf格式檔案的結構如下圖所示:

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

圖2

下面将一個個部分來分析。

1. ELF檔案頭(Header)分析

readelf -h test.o檢視elf檔案頭部資訊

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

主要字段的含義已在圖中辨別;

由Size of this headers可知,頭部占用了64位元組;

用hexdump -n 64 test.o 檢視頭部64位元組資料内容(16進制格式);圖中紅框裡的資料就是test.o檔案的前64位元組,也就是elf頭部,對比上面兩圖,(圖1)中的魔數7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 剛好與(圖2)中的前16位元組(小段編碼)對應,後面每個字段的含義也是一一對應的;

頭部資訊結構可以參考/usr/include/elf.h 裡ELf64_Ehdr, 32位的可以參考ELf32_Ehdr結構typedef struct{

unsigned char e_ident[EI_NIDENT];     

Elf64_Half    e_type;                 

Elf64_Half    e_machine;              

Elf64_Word    e_version;              

Elf64_Addr    e_entry;                

Elf64_Off     e_phoff;                

Elf64_Off     e_shoff;                

Elf64_Word    e_flags;                

Elf64_Half    e_ehsize;               

Elf64_Half    e_phentsize;            

Elf64_Half    e_phnum;                

Elf64_Half    e_shentsize;            

Elf64_Half    e_shnum;                

Elf64_Half    e_shstrndx;             } Elf64_Ehdr;由字段Start Of Section Header可知,在test.o檔案的656位元組處有一個'段的頭部表'。

2. ELF段頭部表(Section Header)分析

用readelf -S test.o檢視'段的頭部表',在這個表裡儲存了檔案裡所有段的屬性資訊,如段的名字,段在檔案的開始位置, 段的長度等:

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

圖4

由圖可知主要字段的含義已從圖中辨別;

段表的資料在偏離檔案開始的0x290處,跟ELF Header Start Of Section Header字段保持一緻;

這個段表裡總共有12項,不同的項描述了不同段的屬性;

2.1 text段

text段在偏離test.o檔案開頭0x40位元組處,長度為0x20位元組,用hexdump -s 0x40 -n 0x20 test.o,檢視text段的16進制内容。

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

圖5

然後objdump -d test.o 列印出程式的反彙編代碼,

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

圖6

由上面兩圖可知,text部分的資料恰好是main函數的機器碼,也就是text段裡儲存的是代碼段。

2.2 data段

data段在偏離test.o檔案開頭0x60位元組處,長度為0x4位元組,用hexdump -s 0x60 -n 0x4 test.o,檢視data段的16進制内容,

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

圖7

4個位元組剛好是個int的長度,裡面儲存的數值是5,也就是全局變量global_var的值,驗證了已初始化的全局變量儲存在data段。

2.3 bss段

2.4 .rela.text (text的重定位段)

main函數裡的global_var定義在别的檔案,後面連結需要根據别的檔案來确定它的虛拟記憶體位址,由于text中有需要重定位的變量,是以就有了.rela.text段。readelf -r test.o

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

圖8

offset清單示需要重定位的符号在該段中的偏移,這裡表示偏離text段0xd處;

info列高4個位元組是該符号在.symtab中的索引,見下圖;

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

圖9

2.5 .symtab 符号表

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

圖9

ndx列是該符号在段表中的索引:(1)比如global_var的索引為3,圖4中索引3表示data段,

也就是說global_var這個符号在data段;(2)如果是“UND“則表示該符号定義在别的檔案,需要重定位,重定位資訊見“2.4 .rela.text”。

elf檔案反編譯C語言,圖文并茂,講透C語言靜态連結,ELF檔案篇

圖10