以下是我自己的逆向工程筆記!有錯還請多多指教XD
┌───────── ASCII ─────────┐
│ 30 0 20 SPACE │
│ 41 A 0d0a CRLF │
│ 61 a │
└─────────────────────────┘
────────────────────── 指令 ──────────────────────
-------------------- gdb --------------------
echo "set disassembly-flavor intel" >> ~/.gdbinit
echo "set follow-fork-mode parent" >> ~/.gdbinit
- fork 時就不會跟丟 parent (gdb-peda 會改掉所以要改回來)
gdb <執行檔>
- 也可以進去以後
file <執行檔>
- 也可以進去以後
b main
新增斷點i b
檢視斷點del <N>
r < in.txt
用檔案當作輸入starti
停在第一行指令 (@loader)
c (continue)
i r rsp
檢視暫存器x/10i $rip
用指令顯示x/xw $rip
用 DWORD 顯示 (little-endian會先翻轉過) (g: qword / b: byte)x/s $rip
用 string 顯示ni
下一行指令si (stepi)
下一行指令 (會進function)fin
跳出 functionj *0x40060
Jumpset var $rdi = 0x5
attach <pid>
掛接新的程序detach
i infer
列出已掛接的程序i proc mappings
列出動態 segment mappingsprint/x &system
印出某 symbol 地址 (也就是system@libc)(Enter)
重複上一個指令 (不會留下重複歷史紀錄)lay asm
沒裝 peda/gef 也可以有動態畫面bt
Back trace stack frame
gcc -S -masm=intel test.c
(數字會用十進位顯示)-m32
編譯出 x86
objdump -D -M intel a.out
-R
看 GOT table
readelf -S <file>
看靜態 sections-s <library>
印 function/bss 的靜態 offset-l
檢查 NX 是否關掉 (GNU_STACK)
vmmap
看動態 segment mappings (gdb-gef)checksec
檢查有無 NX, Canary, PIE, RELRObinwalk <file>
分析未知檔案類型--entropy
可加--save
存成 .jpg 或--verbose
--dd 'png'
取出 png type (也可-e
)
- 遇到不明 binary 時:
strings <exe>
,查 version、gnu、error。 - readelf -S 跟 vmmap 之間可能有些縫隙,可以把資料寫到這邊。
- readelf -s 顯示的沒有錯,但實際跑起來可能是呼叫另外的內部函式。(eg. libc::strlen)
───────────────────── Assembly ─────────────────────
-
Stack:
- 任何常數都是按照 little endian 來放到 stack。(LSB@低位置)
- Array 在 stack 上是從低位置到高位置,字串也是低到高放。
- x64 有時會要求 rsp 是 16-byte 對齊。(eg. system@libc 有 movaps)
-
Reg 不同版本:
-
( |( |(ah|al))) rax eax ax
- rax: 64 / eax: 32 / ax: 16 / ah: 8 / al: 8
-
-
Shellcode:
- x64:
mov rax, "/bin/sh\0" | push rax | mov rdi, rsp | xor rsi, rsi | xor rdx, rdx | mov rax, 0x3b | syscall
- x86:
push "/sh\0" | push "/bin" | mov ebx, esp | xor ecx, ecx | xor edx, edx | mov eax, 0xb | int 0x80
- (x64 也支援 int 80)
- 塞在 C 裡:
char shell[]="\x55\x48..."; void (*ptr)(void)=shell; ptr();
- x64:
-
參數傳遞 (calling convention):
- x64_Linux: rdi, rsi, rdx, rcx, r8, r9
- syscall: rdi, rsi, rdx, r10, r8, r9
- x64_Win: rcx, rdx, r8, r9
- x86_Linux syscall: ebx, ecx, edx, esi, edi
- 多餘的參數會用 stack 傳遞,最右邊先 push。x86 基本上都是用 stack。
- 浮點數是用 xmm0~7 (128-bit)。
- x64_Linux: rdi, rsi, rdx, rcx, r8, r9
-
組語語法:
lea eax, [ebx+3*ecx]
,可看成 eax=(ebx+3*ecx)- 出現 PTR 就代表是個指標,要還原,eg.
mov eax, DWORD PTR [ebx+0x8]
xor eax, eax
(可以用來產生0x0)cmp eax, ebx
(eax - ebx,若結果為零設ZF,若結果為負設SF) (unsigned)test eax, eax
(eax & eax,若結果為零設ZF,若結果為負設SF)cwd / cdq / cqo
(ax 擴充成 dx : ax / edx : eax / rdx : rax)cwde / cdqe
(ax 擴充成 eax / eax 擴充成 rax)hlt
(讓執行暫停,直到 interrupt)scas(b/w/d/q)
(拿 al/ax/eax/rax 跟 rdi 所指的字串比較)retf 0x10
(Far return 並 pop 0x10 個)iret
(從 interrupt return)
-
Word
: 2-byte,DWord
: 4-byte,QWord
: 8-byte -
MacOS 上,func call 前要確保 rsp 是 16-byte 對齊
-
不能:
- 直接 push 64-bit 常數 => 先 mov 到暫存器再 push。
mov QWORD PTR [rax], 64-bit 常數
。
───────────────────── General ────────────────────
-
Buffer Overflow:
gcc -f(no-)stack-protector(-all)
是 Canary。(-no)-(f)pie
是 PIE。-z execstack
會關掉 64-bit NX。-z relro -z now
是 Full RELRO。
echo 0 | tee /proc/sys/kernel/randomize_va_space
會暫時關掉 ASLR。- Tricks:shellcoding,ROP,ret2libc,ret2plt,ret2text,ret2csu,stack pivoting,GOT。
-
未知檔案:
- Entropy 分佈如果很均勻且高, 可能是壓縮或加密。
- Chi Square, Monte Carlo Pi 可以分析是壓縮或加密。
-
面對大量未知 functions:可從每個 string 分別被誰 call 到下手。
-
strncpy, strcat: 遇到 \0 會停止,因此不太能用來蓋地址。
────────────────────── Linux ──────────────────────
-
Library:
- GOT 是用來存 library function pointer 的表 (lazy binding),PLT 則是呼叫它的 code。
- Library 的 base addr,一定是 7f 開頭,000 結尾,中間隨機化。
- 用
LD_LIBRARY_PATH=.
來手動改變 lib path,或patchelf --set-rpath .
。 - libc 有
mprotect()
,可以改變 page rwx 權限。
-
人工解 x64 ELF file:(Offset, Value) 10 Type (WORD) -> 0x3: PIE/library 12 Machine (WORD) -> 0x3e: x64 / 0x3: x86 20 Program Header 段位置 (QWORD) (通常是 0x40) 36 每段 PH 大小 (WORD) 38 PH 段總數 (WORD) 每段 PH:0 Type (DWORD) -> 0x6474e551: Stack 4 Flags (DWORD) (rwx)
-
Debug 沒有 main 的程式 (stripped):
-
starti; break _dl_start_user
。
-
set breakpoint pending on; b __libc_start_main
。
-
-
system("sh")
:- libc 函式,底層仍是
execve("/bin/sh")
。會把字串丟進 shell 執行。 - 有些 libc 版本,rsp 沒 16-byte 對齊會 crash 或沒效果。
- libc 函式,底層仍是
-
execve("/bin/sh", 0, 0)
:- 參數 2, 3 沒清空可能會失敗。
- 參數 2 也可以填
["/bin/sh", "-c", "id"]
。
-
FILE:
- 看到 0xfbad208x 通常代表 standard or file I/O。
struct _IO_FILE_plus *stdin { struct _IO_FILE; struct _IO_jump_t *vtable; }
_IO_FILE
(FILE
):offset +0x70 是 file descriptor,+0x68 是下一個 FILE。(x64)
-
One Gadget:
- 它列出的條件是 "跳進去 libc 後" 須滿足的。
- 當 one gadget 用不了時:
- printf 塞夠多參數,來觸發
malloc_hook
。 stack_check_fail
錯誤訊息malloc_hook
。malloc_hook
先掛realloc
,realloc_hook
再掛 one gadget。
- printf 塞夠多參數,來觸發
-
程式起始流程:
_start@linux-loader
->_start@text
->__libc_start_main@libc
->main@text
-
Stack Pivoting: 小心很多 libc 函式會
sub rsp
很多! (eg. printf) -
read( fd, buffer, count ):\n, \x00 都不會終止它。
-
scanf( %s ):只會停在 whitespaces。
-
main() 的
[rbp+0x18]
是 argv,[rbp+0x24]
是 argc,[rbp+0x28]
是 main。(x64 上) -
通常 .text 段 + 0x200000 會是 .bss 跟 .rodata。(x64 上)
───────────────────── Windows ────────────────────
-
找 Main():
- 很多 debugger 會自動停在 EntryPoint,之後處理 argv,然後呼叫 main,最後 cexit。
- => jmp 一次後找 <_cexit> 上面附近的 call xxxxx,即為 main。
-
人工解 x64 PE file:(Offset, Value) 3C Ptr to PE header (DWORD) PE header:(Offset, Value) 04 Machine (WORD) -> 0x8664: x64 / 0x14c: x86 28 EntryPoint (DWORD) 30 ImageBase (QWORD) 5E DLLCharacteristic (WORD) -> 0x40: PIE
-
用 fs (x86) 或 gs (x64) 來存取 TEB (與 PEB): fs:[0x4 ]: Stack base (DWORD) fs:[0x18]: TEB (self) fs:[0x20]: ProcessId (DWORD) fs:[0x24]: ThreadId (DWORD) fs:[0x30]: PEB: +0x2: BeingDebugged (UCHAR) +0x8: ImageBaseAddress (DWORD) +0x10: ProcessParameters (DWORD) +0x18: ProcessHeap (DWORD)
-
SEH 可以來繞過 Canary。
-
Force ASLR:不論有無
/DYNAMICBASE
flag,一律隨機化。(Win8+)(預設是關的)- 因 Win 不是透過 PIC 隨機化,為了 share page 短時間內 ASLR base 會重用。
-
printf("%n") 被微軟 libc 關掉了
-
scdbg /findsc 可以暴力枚舉 shellcode 執行點並列出 WinAPI calls。
────────────────────── Links ──────────────────────
- x64 opcode: https://www.felixcloutier.com/x86/
- x86 opcode: https://c9x.me/x86/
- opcdoe lookup: http://ref.x86asm.net/coder64.html
- Assembler: https://defuse.ca/online-x86-assembler.htm#disassembly
- Shellcode: http://shell-storm.org/shellcode/
- x64 syscall: https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
- x86 syscall: http://www.unusedino.de/linuxassembly/syscall.html
────────────────────── Tools ──────────────────────
- gdb: 可以一步步看組語、mem、reg。
- gdb-peda: gdb 的擴充,上色、顯示指標內容等。(已停更)
- gdb-gef: 比 peda 更好的版本。
- objdump: 把程式翻成組語。
- PEbear: Win 上看靜態的 PE 檔。
- x64dbg: Win 上的 x64/x86 debugger。
- IDA pro: 組語直接翻成 C,也有 CFG。
- ROPGadget: 方便找 ROP gadget 的工具。
- pwntools: 輕鬆地組譯、轉 endian、送封包到遠端的工具。
- seccomp-tools: 把 seccomp 規則解析成可讀形式。
- one_gadget: 找 libc 中現成的 execve("/bin/sh", NULL, NULL)。
- VirusTotal: 會算 md5, ChiSquare, 開檔, 程序樹, Registry, 危險 calls。
- ent: 測試隨機程度, 有 ChiSquare, MonteCarlo, correlation, distrib。
- scdbg: 從 shellcode 中偵測 WinAPI calls。