昨天在最後有提到,
不管是 hello.o 還是最後產生的 hello
如果使用 file 指令去查看的話,都會看到一個東西:
file hello.o
file hello
可能會分別看到:
hello.o: ELF 64-bit LSB relocatable, x86-64, ...
以及:
hello: ELF 64-bit LSB pie executable, x86-64, ...
所以今天就來看看:
ELF 到底是什麼?
ELF 的全名是:
Executable and Linkable Format
它是一種常見的 Executable File Format
在 Linux 上
Executable、Object File、Shared Library 等等
很多其實都是使用 ELF 格式
所以像昨天產生的:
hello.o
以及:
hello
雖然一個是 Object File,一個是 Executable ,其實都是 ELF
只是 Type 不一樣而已
如果直接用:
file hello
查看:
ELF 64-bit LSB pie executable, x86-64, ...
這邊已經可以看到一些資訊:
ELF 64-bit
LSB
pie executable
x86-64
這些資訊都可以從 ELF Header 裡面找到
可以使用:
readelf -h hello
查看 ELF Header
可能會看到:
ELF Header:
Magic: 7f 45 4c 46 ...
Class: ELF64
Data: 2's complement, little endian
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
Entry point address: 0x1050
這邊先看幾個比較重要的東西
7f 45 4c 46
其中:
45 = E
4c = L
46 = F
所以 ELF 檔案的開頭其實會是:
\x7fELF
這種放在檔案開頭
用來辨識檔案格式的值通常會被稱作 Magic Number
也可以直接使用:
xxd hello | head
查看 Binary 最前面的內容
如果看到:
Class: ELF64
代表這是一個 64-bit ELF
如果是:
ELF32
則代表是 32-bit
像是:
Data: 2's complement, little endian
這邊的 little endian
是在描述 Multi-byte Data 在 Memory 中的儲存順序。
例如:
0x12345678
使用 Little Endian 儲存時,
可能會看到:
78 56 34 12
這個東西之後在 Pwn 裡面會一直遇到
Machine: Advanced Micro Devices X86-64
代表這個 Binary 使用的 Architecture 是:
x86-64
也就是說
之後如果把 Machine Code Disassemble 回 Assembly
就會按照 x86-64 的 Instruction Set 去解析
在 ELF Header 中還會看到:
Entry point address: 0x1050
Entry Point 可以先理解成:
程式開始執行的位置
也就是當這個 ELF 被 Loader 載入並開始執行時
會從這個 Address 開始執行
不過這邊先記得一件事:
Entry Point 不一定就是
main()
在 C 程式真正進到 main() 之前
其實還會經過一些初始化流程
這部分之後再繼續看
除了 ELF Header 之外
ELF 裡面還會被分成很多不同的 Section
可以使用:
readelf -S hello
查看。
會看到很多像是:
.text
.data
.bss
.rodata
這些不同的 Section 會拿來存放不同種類的資料
.text 主要存放:
Machine Code
也就是 CPU 實際會執行的 Instructions
.data 通常會放已經初始化的 Global Variable 或 Static Variable
例如:
int number = 1337;
.bss 通常會放沒有明確初始化的 Global Variable 或 Static Variable
例如:
int number;
.rodata 是 Read Only Data。
像是程式裡面的字串:
printf("Hello World!\n");
其中:
Hello World!
就可能會被放到 .rodata。
可以使用:
readelf -p .rodata hello
查看 .rodata 裡面的 String
可能會看到:
String dump of section '.rodata':
[ 4] Hello World!
所以我們原本寫在 Source Code 裡面的字串
最後真的會被放進 Binary 中
如果只是想快速看看一個 Binary 裡面有哪些可讀字串
可以使用:
strings hello
可能會看到:
/lib64/ld-linux-x86-64.so.2
libc.so.6
Hello World!
...
strings 會從 Binary 中把看起來像是可讀字串的內容找出來
在 Reverse Engineering 時
這通常也是一個很簡單但是很有用的起手式
比如說今天拿到一個 CTF Challenge,
執行後只看到:
Wrong Password!
那就可以先:
strings challenge
看看 Binary 裡面還有哪些字串
有時候可以從這些 String 中找到:
等等線索
所以 ELF 並不是只有一整坨 Machine Code。
裡面其實會包含很多不同的資訊,
像是:
ELF Header
Section
Machine Code
Read Only Data
Global Data
...
今天先知道幾個常見的工具:
file hello
readelf -h hello
readelf -S hello
readelf -p .rodata hello
xxd hello | head
strings hello
透過這些工具
已經可以開始從 Binary 本身取得不少資訊
而剛剛有提到:
.text
裡面放的是 CPU 真正會執行的 Machine Code
如果把這些 Machine Code 轉成人比較能閱讀的形式
就會變成我們接下來很常看到的 Assembly
所以下一篇就來看看:Assembly 到底在寫什麼?