今天的重頭戲,正是逆向昨天剛寫好的 Hello world,從反方向觀察程式碼的長相。如果運用得宜,你可以更了解程式的具體行為。在生活中,逆向工程使用時機就好比,你看到一個 App 或程式、很想複製類似的功能,你可以嘗試逆逆看,看程式背後底在賣什麼膏藥。以工程界為例,香港有一位工程師因業餘興趣逆向 Facebook 的產品,透過社群平台比該公司更早流出官方未來產品走向,更在2018年的時候撼動股價!Reference
在 terminal 中,下指令:file helloworld
會得到結果:
重點在:Mach-O 64-bit executable x86_64
發現是一個可以執行在 Mac OS 的 x64 的檔案
Wikipedia:
objdump 是在類 Unix 作業系統上顯示關於目的檔的各種資訊的命令行程式。例如,它可用作反組譯器來以組譯代碼形式檢視可執行檔。
在 terminal 中,下指令:objdump -D helloworld
這裡的 helloworld 是 Day2 的時候所寫的程式,得到結果:
helloworld: file format Mach-O 64-bit x86-64
Disassembly of section __TEXT,__text:
0000000100000f91 start:
100000f91: b8 04 00 00 02 movl $33554436, %eax
100000f96: bf 01 00 00 00 movl $1, %edi
100000f9b: 48 be 00 10 00 00 01 00 00 00 movabsq $4294971392, %rsi
100000fa5: ba 0e 00 00 00 movl $14, %edx
100000faa: 0f 05 syscall
100000fac: b8 01 00 00 02 movl $33554433, %eax
100000fb1: bf 00 00 00 00 movl $0, %edi
100000fb6: 0f 05 syscall
Disassembly of section __TEXT,__unwind_info:
0000000100000fb8 __unwind_info:
100000fb8: 01 00 addl %eax, (%rax)
100000fba: 00 00 addb %al, (%rax)
100000fbc: 1c 00 sbbb $0, %al
100000fbe: 00 00 addb %al, (%rax)
100000fc0: 00 00 addb %al, (%rax)
100000fc2: 00 00 addb %al, (%rax)
100000fc4: 1c 00 sbbb $0, %al
100000fc6: 00 00 addb %al, (%rax)
100000fc8: 00 00 addb %al, (%rax)
100000fca: 00 00 addb %al, (%rax)
100000fcc: 1c 00 sbbb $0, %al
100000fce: 00 00 addb %al, (%rax)
100000fd0: 02 00 addb (%rax), %al
100000fd2: 00 00 addb %al, (%rax)
100000fd4: 91 xchgl %ecx, %eax
100000fd5: 0f 00 00 sldtw (%rax)
100000fd8: 34 00 xorb $0, %al
100000fda: 00 00 addb %al, (%rax)
100000fdc: 34 00 xorb $0, %al
100000fde: 00 00 addb %al, (%rax)
100000fe0: b9 0f 00 00 00 movl $15, %ecx
100000fe5: 00 00 addb %al, (%rax)
100000fe7: 00 34 00 addb %dh, (%rax,%rax)
100000fea: 00 00 addb %al, (%rax)
100000fec: 03 00 addl (%rax), %eax
100000fee: 00 00 addb %al, (%rax)
100000ff0: 0c 00 orb $0, %al
100000ff2: 01 00 addl %eax, (%rax)
100000ff4: 10 00 adcb %al, (%rax)
100000ff6: 01 00 addl %eax, (%rax)
...
Disassembly of section __DATA,__data:
0000000100001000 msg:
100001000: 48 65 gs
100001002: 6c insb %dx, %es:(%rdi)
100001003: 6c insb %dx, %es:(%rdi)
100001004: 6f outsl (%rsi), %dx
100001005: 2c 20 subb $32, %al
100001007: 77 6f ja 111 <msg+0x78>
100001009: 72 6c jb 108 <msg+0x77>
10000100b: 64 21 0a andl %ecx, %fs:(%rdx)
密密麻麻的,光看到這個顯示的語法就不想看了。於是,我們可以透過 -x86-asm-syntax 設定參數,將其轉換為比較好懂的 Intel 語法。
objdump -D -x86-asm-syntax=intel helloworld
使用 -D 來進行 disassemble(反組譯),透過 -x86-asm-syntax=intel,將其語法轉成 intel。接下來看下面的 code,不覺得清爽多了嗎XD
helloworld: file format Mach-O 64-bit x86-64
Disassembly of section __TEXT,__text:
0000000100000f91 start:
100000f91: b8 04 00 00 02 mov eax, 33554436
100000f96: bf 01 00 00 00 mov edi, 1
100000f9b: 48 be 00 10 00 00 01 00 00 00 movabs rsi, 4294971392
100000fa5: ba 0e 00 00 00 mov edx, 14
100000faa: 0f 05 syscall
100000fac: b8 01 00 00 02 mov eax, 33554433
100000fb1: bf 00 00 00 00 mov edi, 0
100000fb6: 0f 05 syscall
Disassembly of section __TEXT,__unwind_info:
0000000100000fb8 __unwind_info:
100000fb8: 01 00 add dword ptr [rax], eax
100000fba: 00 00 add byte ptr [rax], al
100000fbc: 1c 00 sbb al, 0
100000fbe: 00 00 add byte ptr [rax], al
100000fc0: 00 00 add byte ptr [rax], al
100000fc2: 00 00 add byte ptr [rax], al
100000fc4: 1c 00 sbb al, 0
100000fc6: 00 00 add byte ptr [rax], al
100000fc8: 00 00 add byte ptr [rax], al
100000fca: 00 00 add byte ptr [rax], al
100000fcc: 1c 00 sbb al, 0
100000fce: 00 00 add byte ptr [rax], al
100000fd0: 02 00 add al, byte ptr [rax]
100000fd2: 00 00 add byte ptr [rax], al
100000fd4: 91 xchg eax, ecx
100000fd5: 0f 00 00 sldt word ptr [rax]
100000fd8: 34 00 xor al, 0
100000fda: 00 00 add byte ptr [rax], al
100000fdc: 34 00 xor al, 0
100000fde: 00 00 add byte ptr [rax], al
100000fe0: b9 0f 00 00 00 mov ecx, 15
100000fe5: 00 00 add byte ptr [rax], al
100000fe7: 00 34 00 add byte ptr [rax + rax], dh
100000fea: 00 00 add byte ptr [rax], al
100000fec: 03 00 add eax, dword ptr [rax]
100000fee: 00 00 add byte ptr [rax], al
100000ff0: 0c 00 or al, 0
100000ff2: 01 00 add dword ptr [rax], eax
100000ff4: 10 00 adc byte ptr [rax], al
100000ff6: 01 00 add dword ptr [rax], eax
...
Disassembly of section __DATA,__data:
0000000100001000 msg:
100001000: 48 65 gs
100001002: 6c insb byte ptr es:[rdi], dx
100001003: 6c insb byte ptr es:[rdi], dx
100001004: 6f outsd dx, dword ptr [rsi]
100001005: 2c 20 sub al, 32
100001007: 77 6f ja 111 <msg+0x78>
100001009: 72 6c jb 108 <msg+0x77>
10000100b: 64 21 0a and dword ptr fs:[rdx], ecx
稍稍說明一下各分區(Section)的用途。第18行的 section __TEXT,__unwind_info主要是用來回溯資料資訊結構是用來記錄函式對堆疊指標的影響。簡單來說,主要作為Debug使用,我們先不用理會。接著,我們來看第4行的 section __TEXT,__text,他是分析時的重要入口。仔細觀察後,不難發現,它與我們在 Day2 寫的 code 架構是大致相同的(比方說,第7~14行都是先 mov,再 syscall,再接著 mov,最後再 syscall)。主要差別就在於後面的數字變了,至於為什麼會變,牽扯到16進位轉換成10進位的問題,之後會再討論。接著看第51行的 section __DATA,__data,用來儲存 helloworld 資料(無法直接從程式碼看到資料,只能看到他如何儲存資料)。
所以,透過 objdump 指令,我們可以發現一件事。就算沒有 source code (原本的程式碼),我們也可以透過逆向的方式來大致了解該程式的運行邏輯。
有了一些逆向的經驗後,明天會接續說明組合語言的分段(Section),重點在電腦是如何儲存程式碼的片段!