Skip to content

Instantly share code, notes, and snippets.

@davidhcefx
Last active March 7, 2023 03:49
Show Gist options
  • Save davidhcefx/ff63134852494be062c61076ec549504 to your computer and use it in GitHub Desktop.
Save davidhcefx/ff63134852494be062c61076ec549504 to your computer and use it in GitHub Desktop.
My notes while playing with reverse engineering.

以下是我自己的逆向工程筆記!有錯還請多多指教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          跳出 function
  • j *0x40060       Jump
  • set var $rdi = 0x5
  • attach <pid>      掛接新的程序
    • detach
  • i infer         列出已掛接的程序
  • i proc mappings     列出動態 segment mappings
  • print/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, RELRO
  • binwalk <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();
  • 參數傳遞 (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)。
  • 組語語法:

    • 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):

      1. starti; break _dl_start_user
      1. set breakpoint pending on; b __libc_start_main
  • system("sh")

    • libc 函式,底層仍是 execve("/bin/sh")。會把字串丟進 shell 執行。
    • 有些 libc 版本,rsp 沒 16-byte 對齊會 crash 或沒效果。
  • 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 先掛 reallocrealloc_hook 再掛 one gadget。
  • 程式起始流程:_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 ──────────────────────


────────────────────── 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。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment