ELF32-i386 文件格式簡要分析

通過簡單的一些 Case骏庸,通過實際操作對 ELF32-i386 文件做概要分析者吁。

使用 Docker 來統(tǒng)一整體的開發(fā)窘俺、測試環(huán)境,適用于 Linux复凳、Windows瘤泪、MacOS(包括 Intel、M1 芯片)育八。

如有排版上的問題对途,可訪問:https://zhoukekestar.github.io/

ELF

The Executable and Linking Format (中文名為:可執(zhí)行與可鏈接格式)是一種在計算機領域,通用的標準化的可執(zhí)行文件(同時髓棋,也用于目標代碼 obj实檀、共享庫 so 等文件)。

ELF 文件是 Linux 下最常用的可執(zhí)行文件的格式按声。在 gcc hello.cc 后生成的 a.out 可執(zhí)行文件就是 ELF 格式的膳犹。

ELF 文件格式

ELF 文件的總體格式如下:

<img src="https://github.com/user-attachments/assets/a06a37f6-f6cb-4b0f-a16f-1a66c916aafb" style="width: 200px"></img>

因為 ELF 格式的目標文件,是參與了程序構建签则、程序執(zhí)行兩個流程须床,所以 ELF 文件也分別對這兩個流程,有兩種視圖:一種是鏈接視圖(Linking View)渐裂,一種是執(zhí)行視圖(Executing View)豺旬。[5]

同時也對應了上圖的不同方向的箭頭指向。

<img src="https://github.com/user-attachments/assets/ba2daefa-3826-45ce-a094-fabc76479743" style="width: 400px"></img>

ELF 格式結構

根據(jù) Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification 的規(guī)范描述柒凉。

約定的基礎類型有:

Name Size Alignment Purpose
Elf32_Addr 4 4 Unsigned program address
Elf32_Half 2 2 Unsigned medium integer
Elf32_Off 4 4 Unsigned file offset
Elf32_Sword 4 4 Signed large integer
Elf32_Word 4 4 Unsigned large integer
unsigned char 1 1 Unsigned small integer

ELF Header

#define EI_NIDENT 16
typedef struct {
  unsigned char e_ident[EI_NIDENT];
  Elf32_Half e_type;
  Elf32_Half e_machine;
  Elf32_Word e_version;
  Elf32_Addr e_entry;
  Elf32_Off e_phoff;
  Elf32_Off e_shoff;
  Elf32_Word e_flags;
  Elf32_Half e_ehsize;
  Elf32_Half e_phentsize;
  Elf32_Half e_phnum;
  Elf32_Half e_shentsize;
  Elf32_Half e_shnum;
  Elf32_Half e_shstrndx;
} Elf32_Ehdr;

Program Header

typedef struct {
  Elf32_Word p_type;
  Elf32_Off p_offset;
  Elf32_Addr p_vaddr;
  Elf32_Addr p_paddr;
  Elf32_Word p_filesz;
  Elf32_Word p_memsz;
  Elf32_Word p_flags;
  Elf32_Word p_align;
} Elf32_Phdr;

Selection Header

typedef struct {
  Elf32_Word sh_name;
  Elf32_Word sh_type;
  Elf32_Word sh_flags;
  Elf32_Addr sh_addr;
  Elf32_Off sh_offset;
  Elf32_Word sh_size;
  Elf32_Word sh_link;
  Elf32_Word sh_info;
  Elf32_Word sh_addralign;
  Elf32_Word sh_entsize;
} Elf32_Shdr;

構建環(huán)境

構建鏡像

本文采用 docker 來進行環(huán)境的統(tǒng)一族阅,避免各種操作系統(tǒng)、各種版本膝捞、環(huán)境坦刀、CPU 所帶來的差異。

新建并編輯 Dockerfile 如下:

# 指定 AMD64
# 按需替換為自己的加速站點,比如 dockerpull.com/ubuntu:22.04
FROM --platform=linux/amd64 mirror.gcr.io/ubuntu:22.04

# 使用清華源
COPY <<EOF /etc/apt/sources.list
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse
EOF

RUN apt-get update

# 安裝必要的基礎工具
RUN apt-get install gcc nasm make xxd -y

WORKDIR /home

執(zhí)行構建命令

$ docker build -t gcc-amd64 .

別忘了最后的 .鲤遥,用于指定是當前目錄的 Dockerfile 文件央渣。

運行鏡像

為了方便文件的操作,一般會新建一個文件夾和 docker 環(huán)境進行共享渴频,這樣復制、添加北启、刪除比較多的文件時卜朗,就可以直接在非 docker 命令行下操作。

$ docker run -it --rm -v .:/home gcc-amd64
  • -it interactive & terminal咕村,進入交互式命令行終端
  • --rm 運行后刪除场钉,避免退出容器后,保留過多不必要的運行容器
  • -v 掛載當面目錄至 docker /home 目錄下

最簡單的匯編程序

為避免傳統(tǒng) Hello World 較為復雜的字符串數(shù)據(jù)存儲懈涛、中斷逛万、標準輸入輸出等概念,本文使用更為簡單的加法程序批钠,并使用 return 進行輸出返回宇植。

加法匯編源碼

新建 plus.s 文件,參考 Assembly Programming Tutorial埋心,編輯代碼如下:

注意此處為 nasm 匯編指郁,非 GAS 匯編,相關內(nèi)容參考:Linux assemblers: A comparison of GAS and NASM

section .text     ; .text 段落拷呆,用于存放具體執(zhí)行代碼

global _start     ; 聲明入口函數(shù)

_start:           ;

   mov   ax, 1    ; ax 寄存器放入 1
   mov   bx, 2    ; bx 寄存器放入 2
   add   bx, ax   ; 執(zhí)行加法闲坎,并將結果放入 bx 寄存器

   ; 執(zhí)行程序退出
   mov  ax,1     ; 中斷號 1
   int  0x80     ; 執(zhí)行內(nèi)核中斷,中斷號為 ax 的 1

編譯執(zhí)行

此處都在 docker 環(huán)境中執(zhí)行茬斧。

# 編譯匯編代碼為 obj 代碼
$ nasm -f elf plus.s

# 鏈接生成可執(zhí)行文件
$ ld -m elf_i386 -s -o ./plus ./plus.o

# 執(zhí)行代碼
$ ./plus

# 打印程序 exit code腰懂,此處打印出 3,說明正確執(zhí)行了 1 + 2
$ echo $?

查看程序執(zhí)行完成后的 exit code项秉,可以使用 echo $? 命令绣溜。

ELF 格式簡析

使用 xxd ./plus 查看可執(zhí)行加法程序的二進制文件格式。

# 解析 plus 可執(zhí)行程序為二進制伙狐,并保持到 ./plus.txt
$ xxd ./plus > ./plus.txt

后續(xù)就可以基于這個文件涮毫,分字節(jié)來做解析了。

另外贷屎,可以通過 readelf 命令罢防,來快速查看相關二進制的說明。

$ readelf -a ./plus

ELF Header

ELF 文件頭解析唉侄,一般為 52 個字節(jié)咒吐。

[圖片上傳失敗...(image-25191e-1729842189078)]

0x00
  • e_ident[EI_NIDENT]
    • .ELF 標記
    • class ELF32 0x01, 2 為 64 位
    • data data 2's complement, little endian 0x01,小端編碼
    • version 1 (current) 0x01
    • padding, 補齊到 16 位 0x00 0000 0000 0000
0x10
  • e_type EXEC (Executable file) 0x02
  • e_machine Intel 80386 0x03
  • e_version version 0x01
  • e_entry entry 0x08049000
  • e_phoff Start of program headers 52 (bytes into file) 0x34
0x20
  • e_shoff Start of section headers 4132 (bytes into file), 0x1024
  • e_flags flags 0x00
  • e_ehsize Size of this header): 52 (bytes) 0x34
  • e_phentsize Size of program headers):32 (bytes) 0x20
  • e_phnum Number of program headers): 2 0x02
  • e_shentsize Size of section headers): 40 (bytes) 0x28
0x30
  • e_shnum Number of section headers): 3 0x03
  • e_shstrndx Section header string table index):2 0x02
readelf
$ readelf -h ./plus
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8049000
  Start of program headers:          52 (bytes into file)
  Start of section headers:          4132 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         2
  Size of section headers:           40 (bytes)
  Number of section headers:         3
  Section header string table index: 2

Program Header

Program Header 為以下兩者相乘:

  • Size of program headers: 32 (bytes)

  • Number of program headers: 2

    即 64 個字節(jié)恬叹,并有兩個 Program Headers

[圖片上傳失敗...(image-457c85-1729842189078)]

0100 0000   : p_type: TYPE LOAD
0000 0000   : p_offset 0x00000000
0080 0408   : p_vaddr
0080 0408   : p_paddr
7400 0000   : p_filesz 0x74
7400 0000   : p_memsz
0400 0000   : p_flags
0010 0000   : p_align

0100 0000   : p_type: TYPE LOAD
0010 0000   : p_offset 0x00001000
0090 0408   : p_vaddr
0090 0408   : p_paddr
1100 0000   : p_filesz 0x11 17字節(jié)
1100 0000   : p_memsz
0500 0000   : p_flags
0010 0000   : p_align
readelf
$ readelf -l ./plus

Elf file type is EXEC (Executable file)
Entry point 0x8049000
There are 2 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x08048000 0x08048000 0x00074 0x00074 R   0x1000
  LOAD           0x001000 0x08049000 0x08049000 0x00011 0x00011 R E 0x1000

 Section to Segment mapping:
  Segment Sections...
   00
   01     .text
  • 第一個程序頭起始為 0x00候生,大小為 0x74 = 52 (ELF Header) + 32 * 2 (Program Headers),也就是 ELF Header + Program Headers 的長度
  • 第二個程序頭起始為 0x001000, 長度為 0x11绽昼,也就是真正的加法程序

Program

真正的程序代碼在文件位置的 0x00001000唯鸭,虛擬地址的 0x08049000,也就是讀取 .text 段落的代碼:

讀取文件地址的二進制(指定長度為 0x11硅确,即為所有程序代碼):

$ xxd -s 0x1000 -l 0x11 ./plus
00001000: 66b8 0100 66bb 0200 6601 c366 b801 00cd  f...f...f..f....
00001010: 80

使用 readelf 讀取并轉為虛擬地址:

$ readelf --hex-dump=.text ./plus

Hex dump of section '.text':
  0x08049000 66b80100 66bb0200 6601c366 b80100cd f...f...f..f....
  0x08049010 80                                  .

另外目溉,可以通過反編譯,將二進制轉換為匯編代碼:

$ objdump -S ./plus
./plus:     file format elf32-i386
Disassembly of section .text:
08049000 <.text>:
 8049000:       66 b8 01 00             mov    $0x1,%ax
 8049004:       66 bb 02 00             mov    $0x2,%bx
 8049008:       66 01 c3                add    %ax,%bx
 804900b:       66 b8 01 00             mov    $0x1,%ax
 804900f:       cd 80                   int    $0x80

String Table

[圖片上傳失敗...(image-6bd106-1729842189078)]

根據(jù)后面的 section headers 中 STRTAB 的描述菱农,offset 為 0x1011, length 為 0x11:

$ xxd -s 0x1011 -l 0x11 ./plus
00001011: 002e 7368 7374 7274 6162 002e 7465 7874  ..shstrtab..text
00001021: 00

Section Headers

[圖片上傳失敗...(image-fe0743-1729842189078)]

# section 0
0000 0000 sh_name
0000 0000 sh_type
0000 0000 sh_flags
0000 0000 sh_addr
0000 0000 sh_offset
0000 0000 sh_size
0000 0000 sh_link
0000 0000 sh_info
0000 0000 sh_addralign
0000 0000 sh_entsize

# section 1
0b00 0000  sh_name
0100 0000  sh_type 0x01 PROGBITS
0600 0000  sh_flags
0090 0408  sh_addr: 0x08049000
0010 0000  sh_offset: 0x00001000
1100 0000  sh_size: 0x000011
0000 0000  sh_link
0000 0000  sh_info
1000 0000  sh_addralign: 0x10
0000 0000  sh_entsize

# section 2
0100 0000   sh_name 0x01 => .shstrtab
0300 0000   sh_type 0x03 STRTAB
0000 0000   sh_flags
0000 0000   sh_addr
1110 0000   sh_offset 0x00001011
1100 0000   sh_size 0x11
0000 0000   sh_link
0000 0000   sh_info
0100 0000   sh_addralign 0x01
0000 0000   sh_entsize
readelf
$ readelf -S ./plus
There are 3 section headers, starting at offset 0x1024:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        08049000 001000 000011 00  AX  0   0 16
  [ 2] .shstrtab         STRTAB          00000000 001011 000011 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), p (processor specific)

小結

至此缭付,通過一個最簡單的加法,解析了一個最簡單的 ELF 文件循未,從前到后分別是:

  • ELF Header (52字節(jié))文件起始位置:0x00

  • Program Header(32*2=64字節(jié))文件起始位置: 0x34

  • Program (11字節(jié))文件起始位置: 0x1000(由 Program Header 指定)

  • String Table (11字節(jié))文件起始位置: 0x1011 (由 Section Table 指定)

  • Section Table (40*3=120字節(jié))文件起始位置: 0x1024(由 ELF Header 指定)

    最小的一個 ELF 文件由上述組成

References

  1. Executable_and_Linkable_Format
  2. 可執(zhí)行和可鏈接格式
  3. wiki.osdev.org/ELF_Tutorial
  4. github.com/bminor/binutils-gdb
  5. Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification
  6. Assembly Programming Tutorial
  7. Linux assemblers: A comparison of GAS and NASM
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末陷猫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子的妖,更是在濱河造成了極大的恐慌绣檬,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件羔味,死亡現(xiàn)場離奇詭異河咽,居然都是意外死亡,警方通過查閱死者的電腦和手機赋元,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門忘蟹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搁凸,你說我怎么就攤上這事媚值。” “怎么了护糖?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵褥芒,是天一觀的道長。 經(jīng)常有香客問我嫡良,道長锰扶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任寝受,我火速辦了婚禮坷牛,結果婚禮上,老公的妹妹穿的比我還像新娘很澄。我一直安慰自己京闰,他們只是感情好颜及,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蹂楣,像睡著了一般俏站。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痊土,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天肄扎,我揣著相機與錄音,去河邊找鬼赁酝。 笑死反浓,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的赞哗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼辆雾,長吁一口氣:“原來是場噩夢啊……” “哼肪笋!你這毒婦竟也來了?” 一聲冷哼從身側響起度迂,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤藤乙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后惭墓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坛梁,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年腊凶,在試婚紗的時候發(fā)現(xiàn)自己被綠了划咐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡钧萍,死狀恐怖褐缠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情风瘦,我是刑警寧澤队魏,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站万搔,受9級特大地震影響胡桨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瞬雹,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一昧谊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挖炬,春花似錦揽浙、人聲如沸状婶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽膛虫。三九已至,卻和暖如春钓猬,著一層夾襖步出監(jiān)牢的瞬間稍刀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工敞曹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留账月,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓澳迫,卻偏偏與公主長得像局齿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子橄登,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359