Skip to content

Commit

Permalink
Adjust the markdown format.
Browse files Browse the repository at this point in the history
Signed-off-by: ChenYing Kuo <[email protected]>
  • Loading branch information
evshary committed Sep 17, 2024
1 parent 7bda266 commit f9dfd6d
Show file tree
Hide file tree
Showing 19 changed files with 1,326 additions and 798 deletions.
80 changes: 50 additions & 30 deletions source/_posts/ELF-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ tags:
- 系統程式
- GNU tool
---
# 簡介
## 簡介

程式設計師很大的機率是脫離不了Linux,而如果我們要在Linux上compile,大概一定會接觸到ELF這個格式。底下來簡單介紹一下ELF的格式是什麼,我們要怎麼從它獲得資訊。

ELF全名是Executable and Linking Format,在Linux中是編譯後的binary、object檔規範,也就是說我們從source code編譯後產生的檔案格式就是ELF了。
Expand Down Expand Up @@ -38,10 +39,12 @@ Link的時候:

兩者最大的差異是Link的時候是以Section為觀點,用Section Header Table來當索引,指向各個Section。執行的時候則是用Segment為觀點,一個Segment可能是多個Section所組成,然後再用Program Header Table指向各個Segment。

# 觀察ELF的方法
## 觀察ELF的方法

那要如何觀察ELF呢?如果你嘗試用記事本打開應該只會看到一團不知所云的亂碼,所以我們底下會透過各種工具的使用教學來解釋ELF格式。

## 查看執行檔 - od
### 查看執行檔 - od

首先我們可以試著使用od這個指令來看檔案內容。od全名是octal dump,顧名思義就是用八進制來印內容,但他並不僅僅如此而已。

od指令的格式:`od -t [顯示格式] -A [偏移量使用的基數] [filename]`
Expand All @@ -51,11 +54,13 @@ od指令的格式:`od -t [顯示格式] -A [偏移量使用的基數] [filenam
* -v:不省略重複的內容

我們最常用格式:

* `od -t x1 -A x [filename]`:代表用16進制來顯示檔案,偏移量是16的倍數
* `od -t x1z -A x [filename]`:同上,但是多加上顯示ASCII code

那我們來看看ELF檔長什麼樣子,這邊以大家最常用的ls為例
```

```bash
$ od -t x1z -A x /bin/ls | less
000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 >.ELF............<
000010 02 00 3e 00 01 00 00 00 90 48 40 00 00 00 00 00 >..>......H@.....<
Expand All @@ -65,26 +70,28 @@ $ od -t x1z -A x /bin/ls | less

可以看到前面有個`7f 45 4c 46`開頭,ASCII是`.ELF`(.代表非可見字元,這邊是0x7f也就是\177),這個就是傳說中的ELF magic code了。不過這邊先停一下,如果我們要繼續用hex來看其實有點累,所以先換個工具來試試吧!

## 使用readelf來觀察ELF資訊
### 使用readelf來觀察ELF資訊

readelf很明顯就是觀察ELF檔案的專門工具,使用方式如下

* 格式:`readelf [選項] [filename]`
* 讀取標頭選項
- -h:印 ELF header
- -l:印 Program Header Table
- -S:印 Section Header Table
- -e:三者都印
* -h:印 ELF header
* -l:印 Program Header Table
* -S:印 Section Header Table
* -e:三者都印
* 讀取資訊選項
- -s:符號表
- -r:重定位資訊
* -s:符號表
* -r:重定位資訊
* 特別用法:
- -a:所有標頭資訊全部印出
- -xn:先用-S看要查看的Section數字,然後n填上該數字就可以hexdump那個section
* -a:所有標頭資訊全部印出
* -xn:先用-S看要查看的Section數字,然後n填上該數字就可以hexdump那個section

那我們來看看ls的ELF header長什麼樣。從下面可以看到,除了剛剛看到的Magic code外,還有版本、適用哪個OS/ABI、在哪個機器平台運行、entry point adddress等等。

值得注意的是這邊有紀錄Program Header、Section Header的開始位址、大小、數量,所以我們可以用這個資訊找到Program/Section Header。
```

```bash
$ readelf -h /bin/ls
ELF 檔頭:
魔術位元組: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Expand All @@ -110,7 +117,7 @@ ELF 檔頭:

而Program Header的部分,我們可以看到有9個Segement,以及實際的位址在哪。另外有個「區段到節區映射中」(Section to Segment mapping),這就是多個Section如何組成一個Segment的對應。

```
```bash
$ readelf -l /bin/ls
Elf 檔案類型為 EXEC (可執行檔案)
進入點 0x404890
Expand Down Expand Up @@ -154,7 +161,7 @@ Elf 檔案類型為 EXEC (可執行檔案)

Section Header的話會仔細列出這個ELF所包含的所有Section以及位址。

```
```bash
$ readelf -S /bin/ls

共有 28 個區段標頭,從偏移量 0x1a700 開始:
Expand Down Expand Up @@ -187,7 +194,8 @@ Key to Flags:
O (extra OS processing required) o (OS specific), p (processor specific)
```

## objdump取得ELF內容
### objdump取得ELF內容

除了看ELF內的資訊外,我們可以進一步得到更細的資訊,包括dump內容和反組譯程式,這時候就要用objdump了

* `objdump -s -j [section] [filename]`:把特定section完整dump出來
Expand All @@ -199,7 +207,7 @@ Key to Flags:

同樣以ls為例,可以看到我們把text section的內容印出來了

```
```bash
$ objdump -s -j .text /bin/ls

/bin/ls: 檔案格式 elf64-x86-64
Expand All @@ -211,31 +219,36 @@ Contents of section .text:
....
```

## objcopy/strip修改ELF檔案
### objcopy/strip修改ELF檔案

objcopy最主要的功能就是可以把文件作轉換,一部份或全部的內容copy另一個文件中

* `objcopy -S -R .comment -R .note [input filename] [output filename]`:把編譯出來的symbol移除不必要的section(-S代表去掉symbol, relocation的訊息)
* `objcopy -O binary -j [section] [input filename] [output filename]`:也可以把某個section拿出來

關於移除不必要的section部分,其實strip就可以做到了,只要用`strip [filename]`即可。

### objcopy進階用法
#### objcopy進階用法

objcopy可以做到把檔案變成ELF格式,提供給我們linking,這樣我們就可以避免檔案的讀取。

這邊用個簡單的範例,假設我們想要把某個文字檔包在程式內部(其實可以用圖片比較有感覺,只是我不想寫太複雜的程式)

先創立text.txt
```

```raw
This is test txt.
```

然後把text.txt變成object file
```

```bash
objcopy -I binary -O elf64-x86-64 -B i386:x86-64 text.txt text.o
```

如果這時候show object資訊的話
```

```bash
$ objdump -x text.o

text.o: 檔案格式 elf64-x86-64
Expand All @@ -256,7 +269,8 @@ SYMBOL TABLE:
```

symsymbola把下面那些symbol放入test.c內,即可使用
```

```c
#include <stdlib.h>

extern char _binary_text_txt_start[];
Expand All @@ -272,21 +286,24 @@ int main()
```

編譯並執行
```

```bash
$ gcc test.o test.o -o a.out
% ./a.out
text.txt=This is test txt.

```

## nm觀察symbol
### nm觀察symbol

剛剛提了那麼多都是以ELF內的各種section為主,但是我們實際開發程式其實還是比較重視symbol,那我們有簡單方式可以看symbol嗎?這時候就要用到nm了。

* `nm [filename]`:可以顯示symbol的數值、型態、名稱
* `nm --size-sort -r -S [filename]`:由大到小顯示symbol的數值、大小、型態、名稱

舉個例子,我們可以看到下面執行結果symbol由大到小排序
```

```bash
$ nm --size-sort -r -S test
00008464 00000064 T __libc_csu_init
00008444 00000020 T main
Expand All @@ -306,15 +323,18 @@ $ nm --size-sort -r -S test
| BSS | B/b |
| 未定義(如extern) | U |

# addr2line從位址轉成symbol
## addr2line從位址轉成symbol

有時候我們執行程式會只知道位址,但是想要從位址得到到底是在程式哪行掛掉

* `addr2line -f -e [filename] [address]`:-f代表要顯示是哪個function,-e代表address是來自該執行檔

# 總結
## 總結

本篇文章主要簡單介紹ELF的結構,然後我們可以用 od、readelf、objdump、objcopy/strip、nm, addr2line 幾個工具觀察ELF的格式。如果想要有進一步的認識,建議可以研究參考的連結。

# 參考
## 參考

* [BINARY HACKS:駭客秘傳技巧一百招](http://www.books.com.tw/products/0010587783)
* [陳鍾誠的網站 - 目的檔格式 (ELF)](http://ccckmit.wikidot.com/lk:elf)
* [ELF 格式解析](https://paper.seebug.org/papers/Archive/refs/elf/Understanding_ELF.pdf)
Expand Down
Loading

0 comments on commit f9dfd6d

Please sign in to comment.