Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hack The Planet: 二進位忍者的逆向工程試煉

Avatar for CX330 CX330
October 14, 2026
0

Hack The Planet: 二進位忍者的逆向工程試煉

Avatar for CX330

CX330

October 14, 2026

Transcript

  1. Link Tree whoami • 常用 ID:CX330、Cyber Vampire • 成大資安社現任社長 •

    Security Research Intern @ CyCraft • 2025 TSC CTF 3rd • 2025 MyFirstCTF Golden Prize • 2025 技能競賽全國賽佳作 • CWES (CBBH) Holder • 血液中是咖啡 • Binary Ninja 傳教士 I use Neovim btw…
  2. Outline & Schedule • 上半場:逆向工程基礎原理及靜態分析 ◦ What is 逆向工程 ◦

    Endianess ◦ 執行檔原理與分析 ◦ x86-64 指令集架構(ISA) ◦ Calling conventions ◦ Decompilers 初探 ◦ Entry Point • 下半場:深入記憶體,動靜結合的藝術 ◦ Debuggers ▪ x64dbg ▪ GDB ◦ Memory layout ◦ Stack frame ◦ Data Alignment ◦ Enum & Structs ◦ and more (.NET, Python, etc)
  3. 先備條件: • 可執行 PE 的環境 ◦ • 需要一台 Windows 或是虛擬機

    可執行 ELF 的環境 ◦ 需要一台 Linux 或是虛擬機 • 已安裝 Binary Ninja Free • 已安裝 x64dbg • 已註冊上課所需的題目平台 • 想要變強的心
  4. What is 逆向工程:狹義 • • 定義 ◦ 從機器碼一步一步回推開發 目標 ◦

    執行流程 人員想法 ◦ ◦ 寫外掛更改程式原本的正常 透過工具反組譯+反編譯讓 ◦ 研究程式底層漏洞 逆向工程師更方便理解 ◦ 惡意程式分析 盡可能還原工程細節 ◦ 軟體行為分析 ◦ 破解軟體註冊機(Keygen) ◦ 抄襲產品
  5. Endianess:複習重要觀念 0x__ = 0b_ _ _ _ _ _ _

    _ = 1 Byte 16 * 16 = 256 = 2 ^ 8 = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2
  6. Endianess • • 大端序 ◦ 第一個是 MSB (Most Significant Byte)

    ◦ 對人類而言好讀 小端序 ◦ 第一個是 LSB (Least Significant Byte) ◦ 對機器而言好處理 ◦ 型態轉換很方便快速 ◦ 舉個 🌰 0xAA 在 little endian 機器上 aa 00 00 00 int aa 00 00 00 short
  7. 執行檔原理與分析:執行檔常見家族 • • PE (Portable & Executable) ◦ 作業系統:Windows ◦

    副檔名:.exe、.dll、.sys ELF (Exetutable & Linkable Format) ◦ • 作業系統:Linux Mach-O (Mach Object) ◦ 作業系統:MacOS、iOS ◦ 本課程不包含
  8. 執行檔原理與分析: Sections 功能 PE ELF Mach-O 程式碼 .text .text __TEXT,__text

    常數資料 .rdata .rodata __TEXT,__const __TEXT,__cstring 可寫資料 .data .data __DATA,__data 未初始化資料 .bss .bss __DATA,__bss 入口點 address in .text _start _main / some addr
  9. 執行檔原理與分析: Sections 功能 PE ELF Mach-O 程式碼 .text .text __TEXT,__text

    常數資料 .rdata .rodata __TEXT,__const __TEXT,__cstring 可寫資料 .data .data __DATA,__data 未初始化資料 .bss .bss __DATA,__bss 入口點 address in .text _start _main / some addr
  10. 執行檔原理與分析: file & readelf • 查看檔案類型 ◦ • 顯示所有資訊 ◦

    • readelf -all <binary> 查看 headers ◦ • file <file_name> readelf --headers <binary> 查看 symbols ◦ readelf --symbols <binary>
  11. 執行檔原理與分析: ELF Format ELF Header Program Header Table Section 0

    Section 1 Section 2 … Section Header Table • 裡面的結構都可以在 elf.h 裡面找 • 可以使用 readelf 來解析他的結構
  12. 執行檔原理與分析: ELF Format ELF Header Program Header Table • 裡面的結構都可以在

    elf.h 裡面找 • 可以使用 readelf 來解析他的結構 那我們來看看一個檔案的 raw bytes 是怎麼 Section 0 被Section mapping 到這些結構的吧 1 Section 2 … Section Header Table
  13. 執行檔原理與分析: Program Header 前面有看到,每一個 Program header 是 0x38 的大小 Program

    header 會告訴 loader 這些 segment 要怎麼被映射 到記憶體裡,每個 program header entry 都描述一個 segment
  14. 執行檔原理與分析: Program Header 04 00 00 00 00 00 00

    04 Little Endian 00000004 = 0x04 =4 = 0b100 0b100 r--
  15. 執行檔原理與分析: Program Header 04 00 00 00 00 00 00

    04 Little Endian 00000004 = 0x04 =4 = 0b100 0b100 r--
  16. 執行檔原理與分析: Virtual Address • Virtual Address 是作業系統在執行程式時,為每個 process 所建 立的一組邏輯記憶體空間。它是一種間接記憶體存取方式,不直接

    對應實體記憶體,而是透過 Memory management unit 轉換為 Physical address。而這麼做的好處是: ◦ 每個 Process 可以有獨立的記憶體空間 ◦ 可以 ASLR (Address Space Layout Randomization) ◦ 支援大於 Physical address 空間的記憶體配置
  17. x86-64 指令集架構: CPU • 計算機的大腦,處理絕大部分的運算 • 運作流程(4-stage pipeline): ◦ Fetch:從記憶體抓取指令

    ◦ Decode:解碼指令的 Opcode 及 Operand ◦ Execute:執行指令,會涉及 ALU(Arithmetic Logic Unit)運算和記憶 體存取 ◦ Write Back:儲存運算結果回暫存器(Register)或記憶體(Memory)
  18. x86-64 指令集架構 電腦 跟 0 啊 ! ! ? 只看

    ? 啊 得 麼 懂 什 是 0跟 底 1 1到
  19. x86-64 指令集架構 ! 電腦 ! ? 只看 ? 啊 得

    麼 Machine Code!也就是指令真正的樣態 什懂 0 跟 是 底 到 1 1 跟 0 啊
  20. x86-64 指令集架構: Machine Code • 由 0 跟 1 組成一條條機器可以直接執行的

    Instructions • 如果釐清了每個 0 跟 1 的位置,就可以知道對應的指令 ◦ • 例如:`b8 01 00 00 00` 就是 `mov eax, 00000001` ◦ • 這叫做 Instruction Encoding 詳細原因後面會解釋 用 `objdump -d -M intel <filename>` 看 Assembly & Machine code
  21. x86-64 指令集架構: Machine Code • • Instruction ◦ 完整的一條指令 ◦

    e.g. `mov eax, 1` Mnemonic 助記符 ◦ • Opcode ◦ • e.g. `mov` e.g. `b8` Machine Code (opcode + operand) ◦ e.g. `b8 01 00 00 00`
  22. x86-64 指令集架構: Machine Code • • Instruction ◦ 完整的一條指令 ◦

    e.g. `mov eax, 1` 的 來 麼 Mnemonic 助記符 ◦ e.g. `mov` 怎 西 東 8這 b ◦ e.g. `b8` • Opcode • Machine Code (opcode + operand) ◦ e.g. `b8 01 00 00 00`
  23. x86-64 指令集架構: Machine Code 去查 x86-64 Instruction reference, from Intel®

    64 and IA-32 Architectures Software Developer’s Manual.
  24. x86-64 指令集架構: Machine Code 去查 x86-64 Instruction reference, from Intel®

    64 and IA-32 Architectures Software Developer’s Manual.
  25. x86-64 指令集架構: Machine Code mov r32, imm32 r32 代表 32

    位元的暫存器( Register) mov eax, 1 imm32 代表 32 位元的立即數
  26. x86-64 指令集架構: Machine Code mov r32, imm32 r32 代表 32

    位元的暫存器( Register) mov eax, 1 imm32 代表 32 位元的立即數 剛看到 mov r32, imm32 的 opcode 是 B8+rd id。而在這 rd 為 0, 所以整個 Instruction 的 machine code 就會是 B8 加上 imm32, 我們將 1 使用小端序 32 位元表示就是 01 00 00 00,故為 B8 01 00 00 00
  27. x86-64 指令集架構: Machine Code mov r32, imm32 r32 代表 32

    位元的暫存器( Register) mov eax, 1 imm32 代表 32 位元的立即數 剛看到 mov r32, imm32 的 opcode 是 B8+rd id。而在這 rd 為 0, 所以整個 Instruction 的 machine code 就會是 B8 加上 imm32, 我們將 1 使用小端序 32 位元表示就是 01 00 00 00,故為 B8 01 00 00 00
  28. x86-64 指令集架構:常見 ISA • • ISA (Instruction Set Architecture) ◦

    剛剛範例都是用 mov 這個 Instruction 居多,但肯定不止這個 ◦ 一系列的 Instructions 組合就叫指令集,如此直觀 常見的 ISA 包括但不限於 ◦ x86-64,泛指基於 Intel 8086 且向下兼容的 ISA ◦ ARM(蘋果家族) ◦ RISC-V(嵌入式系統、物聯網等) ◦ MIPS(Nintendo 64、嵌入式系統)
  29. x86-64 指令集架構: Assembly • 組合語言 Comes from 機器語言 • 剛剛看到的

    mov eax, 1 就是 x86-64 組合語言 • ◦ 由一堆 Mnemonics 來幫助記憶 CPU 指令 ◦ 如此一來便不必瘋狂查表對照 x86-64 常見兩種格式 ◦ Intel Syntax(dest, src) ▪ ◦ mov eax, 1 AT&T Syntax (src, dest) ▪ movl $1, %eax
  30. x86-64 指令集架構: GP Registers • General Purpose Registers 通用暫存器 •

    RAX ◦ • RBX ◦ • Base Register,指標基準或 Base Address RCX ◦ • Accumulator Register,常用於存放運算節果 Counter Register,用於迴圈計數 RDX ◦ Data Register,存放資料,作為 A 暫存器的擴展
  31. x86-64 指令集架構: GP Registers • Index Registers ◦ RSI ▪

    ◦ Source Index,資料來源位址 RDI ▪ Destination Index,資料目的地位址
  32. x86-64 指令集架構: GP Registers • Pointer Registers ◦ RSP ▪

    ◦ RBP ▪ ◦ Stack Pointer,指向 Stack 頂端 Base Pointer,指向 Stack 底端 RIP ▪ Instruction Pointer,指向下一條將執行的指令
  33. x86-64 指令集架構: Flags Register • 一如既往,64 位元叫 RFLAGS,32 位元叫 EFLAGS

    • 各個 Flag 是第幾位元,可以對照前頁的表 • CF • ◦ Carry Flag,表示運算中是否有進位 ◦ 有進位則為 1 PF ◦ Parity Flag ◦ 運算結果低八位元的 1 的數量若為偶數則為 1
  34. x86-64 指令集架構: Flags Register • • • ZF • AC

    ◦ Zero Flag ◦ Alignment Check ◦ 結果為 0 時設為 1 ◦ 檢查記憶體訪問是否有對齊 SF ◦ Sign Flag ◦ 結果為負數設為 1 OF ◦ Overflow Flag ◦ 有號數溢位的時候設為 1
  35. x86-64 指令集架構: Syntax • 這邊用 Intel Syntax 做介紹 • <mnemonics>

    destination, source [, optional] ◦ • • e.g. add eax, 5 [reg] 是取該 Register 指針的值 ◦ 用 C 語言概念理解: ◦ mov eax, rbx 可以表示為 eax = rbx; ◦ mov eax, [rbx] 可以表示為 eax = *rbx; ◦ 所以我們就可以用他做指針的偏移量取值等操作 用 ; 符號寫註解
  36. Calling Conventions • ABI(Application Binary Interface)中很重要的部分 ◦ ABI 包含但不限於 Stack

    對齊規則、Binary 格式、系統呼叫規範 等等 • 中文叫呼叫約定,或是呼叫慣例 • 規範函數的參數如何傳遞、回傳值、暫存器管理、Stack 管理等等 • 如果 Caller 和 Callee 沒有對齊相同的 Calling convention,Stack 的清 理就會亂掉導致程式崩潰 • 不同 OS 和不同的 CPU 架構都有不同的 Calling convention • Zig 的 extern、Python 的 ctypes、C++ 的 extern “C” 皆需指定
  37. Calling Conventions:x86 & x86-64 • • • x86 有各種神奇的 Calling

    Conventions ◦ cdecl, stdcall, fastcall 等 ◦ 參數的傳遞大部分透過 Stack(右到左 ◦ 回傳值放在 edx:eax(高出 32 位元的部分放在 edx) x64 則是透過 Registers 傳參數 ◦ 大幅簡化設計,但 Windows 和 Linux 的 Registers 順序不同 ◦ 回傳值放在 rax 還有很多細節,有興趣可以參考 ◦ 引數傳遞和命名慣例、X86呼叫慣例、x64 呼叫慣例
  38. Calling Conventions:x86 cdecl • cdecl 是 C declaration 的縮寫 •

    參數全部放到 Stack 上面 • 回傳值放在 edx:eax • C ABI 預設 • 更適合實作參數不定長的函數 ◦ e.g. printf() • Stack 由 Caller 清理 • 編譯後函數名 _func
  39. Calling Conventions:x86 stdcall • Win32 Standard Call • 跟 cdecl

    很相似 • 參數全部放到 Stack 上面 • 回傳值放在 edx:eax • Stack 由 Callee 清理 • 編譯後函數名 _func@<byte> ◦ <byte> 是參數總數大小
  40. Calling Conventions:x86 fastcall • Microsoft/GCC fastcall • 前兩個參數放在 ecx, edx

    • 其他參數全部放到 Stack 上面 • 回傳值放在 edx:eax • Stack 由 Callee 清理 • 編譯後函數名 @func@<byte> ◦ <byte> 是參數總數大小
  41. Decompilers 初探:Intro • Decompilers 最主要的用途是便於逆向工程師靜 態分析,協助逆向工程師理解 Binary 內部解構和 行為 •

    Disassemblers 可以把 Machine code 轉回 Assembly • Decompilers 可以把 Machine code 轉回 High level language,例如 Pseudo C / Pseudo Rust 等 • 常見的工具如 Binary Ninja、IDA、Ghidra 等 ◦ 本課程將使用 Binary Ninja
  42. Decompilers 初探:Binary Ninja • 支援 x86、x86_64、ARM、PowerPC、RISC-V、 MIPS 等多種架構 • 同時是

    Disassembler 和 Decompiler • 支援動態分析:Built-In Debugger • 很好讀懂的 CFG(Control Flow Graph)幫助判斷 Binary 執行流程 • 可擴充性高,提供 Python API,幫助自動化與 Plugins 編寫 • 很便於使用的 Patch 功能 • 總之十分強大,祝各位也成為 Binary Ninja!
  43. Decompilers 初探:Cheat Sheet • `n`:重新命名變數 • [ESC]:跳回上個地方 • `y`:重新定義型別 •

    [Space]:Linear/Graph View 切換 • `r`:顯示為 ASCII 字元 • [F5] / [Tab]:反編譯/反組譯切換 • `h`:切換為 Hex View • u:取消定義 • `g`:跳至某處(Symbol或地址) • `;`:註解 • [Cmd+p] / [Ctrl+p]:指令面板 • [Cmd+f] / [Ctrl+f]:搜尋
  44. Debuggers:Intro • 剛剛的 Decompilers 是用來靜態分析,而 Debuggers 則是用來動態分析 • GDB (GNU

    Debugger) ◦ 用來分析 ELF ◦ 純 CLI 介面(或說 TUI) ◦ 可擴展性強,可以裝多種 Plugins ▪ • 個人是用 pwndbg 居多,本堂課會安裝這個 x64dbg(x32dbg & x64dbg 的總稱) ◦ 用來分析 PE 居多 ◦ 同樣支援插件和腳本 ◦ 有 GUI 可以使用,看起來會舒服一些
  45. Debuggers:Debugging Principles Break point Modify Execute Examine Debugging 是一種透過「下斷點 →

    執行 → 檢查 → 修改」四步驟 不斷循環,來觀察並控制程式行 為的技術。 藉由這個流程,開發者可以掌握 程式每一步的執行流程、記憶體 內容、參數傳遞、暫存器變化, 並對 Binary 的內部結構進行動 態分析。
  46. Debuggers:Step Over v.s. Step Into 下個指令 吃飯 睡覺 打東東 吃飯

    睡覺 打東東 吃飯 睡覺 打東東 Step Over Step Over
  47. Debuggers:Step Over v.s. Step Into 下個指令 吃飯 睡覺 打東東 Step

    Into 睡覺 吃飯 點外賣 拿外賣 整個都是「吃飯」這個 Function 吃掉 打東東
  48. Debuggers:GDB Cheat Sheet • ni:Next Instruction ◦ • • b

    [<symbol>/*<address>]: ◦ • 開始執行 設立斷點在某個符號(函數)或地址 continue / c: ◦ 繼續執行直到結束或是下個斷點 finish / fin:Finish ◦ • 執行下一行 Instruction 並追入 Function r / run <args>: ◦ • 執行下一行 Instruction(Step over) si:Step Into ◦ • context / ctx: ◦ • 執行到下一個 ret 指令 重新印出 Debug 上下文 exit / quit / q: ◦ 退出
  49. Debuggers:GDB Cheat Sheet • info / i <subcommand>: 顯示資訊 ◦

    打印變數或運算式 ◦ info breakpoints / i b: ◦ pi ◦ ◦ 顯示所有斷點 info register / i r: ▪ ◦ 顯示函數反組譯組合語言 打印暫存器 rax 的值 p *ptr ▪ ◦ 打印變數 i 的地址 p $rax ▪ ◦ 打印變數 i 的值 p &i ▪ 刪除該斷點 disassemble / disass <function_name>: ◦ ◦ 顯示函數資訊 del <num>: ◦ ▪ 顯示所有暫存器資訊 info functions / i functions: ▪ • print / p ◦ ▪ • • 解引用並打印 ptr 指向的地址的 值 p/x 1234 ▪ 以 16 進位打印 1234
  50. Debuggers:GDB Cheat Sheet • x ◦ Examine ,檢視記憶體位址的 內容 ◦

    x/<count><format><unit> <address> ◦ <count>:要顯示幾個單位 ◦ <unit>:單位大小 ▪ ◦ b (byte), h (half), w (word), g (giant) <format>:資料格式 ▪ x (hex), s (string), c (char), i (instruction) ◦ x/8wx $rsp:以 hex 顯示 rsp 開始的 8 個 4-byte ◦ x/16bx 0xdeadbeef:以 hex 顯示 0xdeadbeef 開始的 16 個 bytes ◦ x/s $rsi:以字串顯示 rsi 的值 ◦ x/i $rip:以 Instruction 顯示 rip 的值
  51. Debuggers:x64dbg Cheat Sheet • F2:設立斷點 • F7:Step Into • F8:Step

    Over • F9:運行 • G:切換 CFG 與 Linear 視圖 • Ctrl + G:跳轉到某地址 • Ctrl + F:搜尋 • Ctrl + E:修改選中的記憶體 值(Patch/Edit) • 在不同的 Panel 操作會對應到不同的區塊 ◦ 比如在 Memory Dump 區域跳到某地址上 面的 CPU 區並不會跳轉
  52. Memory Layout Low Address • .text section Stack 由高位址向低位址生長 ◦

    跟資料結構中的 Stack 很像 ◦ LIFO ◦ 同樣也有 Push/Pop 的操作 ◦ 低位址放上面是為了講解 .data section .bss section Heap Stack 的操作 • Read from program Heap 相反,由低位址向高位址生 長 Stack High Address Kernel space Init to 0
  53. Stack Frame:Stack Frame Low Address • 每次的 Function Call 都會在

    Stack 上 開闢一小塊區塊,屬於該函數專屬的記 憶體空間,稱為 Stack frame ◦ 其中會包含 Return address、 Local data 等等 ◦ Stack frame 3 當 Function 執行完後,Frame 會 Stack fram 2 被清除 Stack fram 1 High Address Stack fram 0
  54. Stack Frame:Functino Pro/Epilogue Low • • Function Prologue ◦ Function

    開始前的前置作業 ◦ 規劃出所需的 Stack 空間 ◦ 建立新的 Function Context Prologue Epilogue Stack frame 3 Stack frame 3 Stack fram 2 Stack fram 2 Stack fram 1 Stack fram 1 Stack fram 0 Stack fram 0 Function Epilogue ◦ Function 結束後的後置作業 ◦ 把 Stack 還原成執行前的樣子 ◦ 清理自己的 Stack Frame High
  55. Stack Frame:Functino Prologue Curr Low 還記得 call 這個指令等於 push rip

    ; return addr jmp <func> 這兩個指令 rsp High Previous stack frame rbp
  56. Stack Frame:Functino Prologue Low Curr rsp Space of 0x10 for

    data rbp Saved rbp Return address High Previous stack frame
  57. Stack Frame:Functino Prologue 至此,Function prologue 結束 Low Curr 至於 Function

    Body 不會也不該再 變更這個函數的 Stack frame 大小 rsp Space of 0x10 for data rbp Saved rbp 我們接著看 Function Epilogue Return address High Previous stack frame
  58. Stack Frame:Functino Epilogue Low rsp Space of 0x10 for data

    rbp Saved rbp Return address Curr High Previous stack frame
  59. Stack Frame:Functino Epilogue Low leave 指令其實等於 mov rsp, rbp pop

    rbp 這兩個指令 rsp Space of 0x10 for data rbp Saved rbp Return address Curr High Previous stack frame
  60. Stack Frame:Functino Epilogue Low rsp Space of 0x10 for data

    rbp Saved rbp Return address Curr = mov rsp, rbp + pop rbp High Previous stack frame
  61. Stack Frame:Functino Epilogue Low Space of 0x10 for data rbp

    Saved rbp Return address = mov rsp, rbp + pop rbp High Curr Previous stack frame rsp
  62. Stack Frame:Functino Epilogue Low Space of 0x10 for data Saved

    rbp rsp Return address = mov rsp, rbp + pop rbp High Curr Previous stack frame rbp
  63. Stack Frame:Functino Epilogue Low ret 指令其實等於 pop rip 這兩個指令 Space

    of 0x10 for data Saved rbp rsp Return address = mov rsp, rbp + pop rbp High Curr Previous stack frame rbp
  64. Stack Frame:Functino Epilogue Low Space of 0x10 for data Saved

    rbp rsp Return address Curr = pop rip High Previous stack frame rbp
  65. Stack Frame:Functino Epilogue Low Space of 0x10 for data Saved

    rbp Return address rsp Curr = pop rip High Previous stack frame rbp
  66. Stack Frame:Functino Epilogue Low 至此,Function epilogue 也結束了 Stack 也變回了函數執行前的樣子 讓我們來比一比

    Space of 0x10 for data Saved rbp Return address rsp Curr = pop rip High Previous stack frame rbp
  67. Stack Frame:Functino Epilogue Low Low Before After Space of 0x10

    for data Saved rbp Return address rsp High Previous stack frame rbp rsp High Previous stack frame rbp
  68. Stack Frame:Let’s Do It Again! Low Curr rsp <main> saved

    rbp <main> return address High Previous stack frame rbp
  69. Stack Frame:Let’s Do It Again! Low rsp Curr <main> saved

    rbp <main> return address High Previous stack frame rbp
  70. Stack Frame:Let’s Do It Again! Low rsp <main> 0x10 space

    rbp Curr <main> saved rbp <main> return address High Previous stack frame
  71. Stack Frame:Let’s Do It Again! Low rsp <first…> return address

    <main> 0x10 space rbp <main> saved rbp <main> return address Curr High Previous stack frame
  72. Stack Frame:Let’s Do It Again! Curr Low rsp <first…> return

    address <main> 0x10 space rbp <main> saved rbp <main> return address High Previous stack frame
  73. Stack Frame:Let’s Do It Again! Curr Low rsp <first…> saved

    rbp <first…> return address <main> 0x10 space rbp <main> saved rbp <main> return address High Previous stack frame
  74. Stack Frame:Let’s Do It Again! Low Curr rsp <first…> saved

    rbp <first…> return address <main> 0x10 space <main> saved rbp <main> return address High Previous stack frame rbp
  75. Stack Frame:Let’s Do It Again! Low rsp <first…> 0x10 space

    Curr rbp <first…> saved rbp <first…> return address <main> 0x10 space <main> saved rbp <main> return address High Previous stack frame
  76. Stack Frame:Let’s Do It Again! Low <first…> 0x10 space rbp

    <first…> saved rbp Curr <first…> return address <main> 0x10 space <main> saved rbp <main> return address High Previous stack frame rsp
  77. Stack Frame:Let’s Do It Again! Low <first…> 0x10 space <first…>

    saved rbp rsp Curr <first…> return address <main> 0x10 space rbp <main> saved rbp <main> return address High Previous stack frame
  78. Stack Frame:Let’s Do It Again! Low <first…> 0x10 space <first…>

    saved rbp <first…> return address Curr rsp <main> 0x10 space rbp <main> saved rbp <main> return address Next High Previous stack frame
  79. Stack Frame:Let’s Do It Again! Low <first…> 0x10 space <first…>

    saved rbp <first…> return address <main> 0x10 space rbp <main> saved rbp <main> return address Curr High Previous stack frame rsp
  80. Stack Frame:Let’s Do It Again! Low <first…> 0x10 space <first…>

    saved rbp <first…> return address <main> 0x10 space <main> saved rbp rsp <main> return address Curr High Previous stack frame rbp
  81. Stack Frame:Let’s Do It Again! Low <first…> 0x10 space <first…>

    saved rbp <first…> return address <main> 0x10 space <main> saved rbp <main> return address rsp Curr High Previous stack frame rbp
  82. Stack Frame:Let’s Do It Again! Low Low Before After <first…>

    0x10 space <first…> saved rbp <first…> return address <main> 0x10 space <main> saved rbp <main> return address rsp High Previous stack frame rbp rsp High Previous stack frame rbp
  83. Data Alignment • • 我們寫 C 語言的時候會指定很多資料的型別 ◦ 這些資料會存在記憶體中 ◦

    每個資料大小不一 ◦ e.g. int, char, long, long long 等 本章節要討論不同型別的資料如何存儲
  84. Data Alignment:Basic Types int main() { long long a; //

    8 bytes int b; // 4 bytes } int b long long a
  85. Data Alignment:Basic Types int main() { long long a; //

    8 bytes int b; // 4 bytes int c; // 4 bytes } int b int c long long a
  86. Data Alignment:Basic Types int main() { long long a; //

    8 bytes int b; // 4 bytes int c; // 4 bytes char d; // 1 byte } d int b int c long long a
  87. Data Alignment:Basic Types int main() { long long a; //

    8 bytes int b; // 4 bytes int c; // 4 bytes char d; // 1 byte long long e; // 8 bytes } long long e d 7 bytes padding int b int c long long a
  88. Data Alignment:Basic Types int main() { long long a; //

    8 bytes int b; // 4 bytes int c; // 4 bytes char d; // 1 byte long long e; // 8 bytes } 證明一下 long long e d 7 bytes padding int b int c long long a
  89. Data Alignment:Proof w/ GDB int main() { long long a;

    // 8 bytes int b; // 4 bytes int c; // 4 bytes char d; // 1 byte long long e; // 8 bytes }
  90. Data Alignment:Proof w/ GDB long long e d 7 bytes

    padding int b int c long long a
  91. Data Alignment:Proof w/ GDB long long e Padding 是為了讓 CPU

    在讀取記憶體時 d 7 bytes padding 有更好的效能,否則若常常要在未對齊的 記憶體中讀取會極大的降低效能 int b int c long long a 這也是為什麼在 Struct 中的成員排列順 序會影響到執行的效能!
  92. Data Alignment:Pointers int main() { long long a = 0x1234;

    // 8 bytes int b = 0x5678; // 4 bytes long long *ptr_a = &a; // 8 bytes int *ptr_b = &b; // 8 bytes } // NOTE: // pointers are same size // x86: 4 bytes // x86_64: 8 bytes int *ptr_b long long *ptr_a int b long long a
  93. Data Alignment:Pointers int main() { long long a = 0x1234;

    // 8 bytes int b = 0x5678; // 4 bytes long long *ptr_a = &a; // 8 bytes int *ptr_b = &b; // 8 bytes int *ptr_b long long *ptr_a int b 引入「地址」的概念 } // NOTE: // pointers are same size // x86: 4 bytes // x86_64: 8 bytes long long a
  94. Data Alignment:Pointers int main() { long long a = 0x1234;

    // 8 bytes int b = 0x5678; // 4 bytes long long *ptr_a = &a; // 8 bytes int *ptr_b = &b; // 8 bytes } // NOTE: // pointers are same size // x86: 4 bytes // x86_64: 8 bytes 0x1012 0x1024 0x5678 0x1012 0x1234 0x1000 0x1008 0x1016 0x1024 0x1032 0x1040 0x1048
  95. Enum & Struct:Add Types In Binja • Create Types from

    C Source ◦ 可以用 C 語法創建 Struct、Enum、Union 等
  96. Enum & Struct:Add Types In Binja • Import Header File

    ◦ 可以從本地匯入 Header 文件,直接獲取 該 Header 文件中的所有 Types,例如 SDL_events.h
  97. Enum & Struct:LostInLinks • Reconnaissance ◦ 先看 main 函數 ◦

    這題沒有 strip,大家看起來應該比較輕鬆 ◦ read_flag 會讀取 flag.txt ◦ encrypt_flag 加密 flag ◦ write_encrypted 把加密過的輸出到 encrypted.txt ◦
  98. Enum & Struct:LostInLinks • encrypt_flag ◦ 這應該是我們最關心的 ◦ 有個 for

    迴圈會去對 (arg1 + (&var_d8)[i]) 和 var_15 做 XOR,看起來是加密的重點 ◦ 每次循環結束會去把 (result_1 + 8) 的值指向 result ◦ 再讓 result = result_1 ◦ 上面的操作看起來很多地址偏移量 和指針操作,遇到這種情況, 大機率該變量是較複雜的型別, 如 union 或 struct ◦ 前面取了 offset 0,後面又取了 offset 8,猜測為 struct
  99. Enum & Struct:LostInLinks • encrypt_flag ◦ 原本的 result 指向 null

    ◦ 後續每次迭代都將新的 struct 指向舊的 struct(result),再把 result 指向新的 struct (聽不懂沒事,待會圖解) ◦ 還有個很怪的地方,前面的 var_d8 到 var_c4 看起來都是一個 int,且後面是 透過 [i] 存取,前面卻被分為多個變 數,感覺原本應該是個 array • 結論: ◦ var_d8 應該是個 array,長度 0x2d + 1 ◦ result_1 應該是 struct,大小 0x10
  100. Enum & Struct:LostInLinks • 還原 result_1 ◦ struct,大小 0x10 ◦

    offset 8 為 x64 指針, 大小為 8,剩下 8 bytes ◦ offset 0 與 var_15 XOR, 推測為一個 char
  101. Enum & Struct:LostInLinks • 還原 result_1 ◦ struct,大小 0x10 ◦

    offset 8 為 x64 指針, 大小為 8,剩下 8 bytes ◦ offset 0 與 var_15 XOR, 推測為一個 char
  102. Q&A

  103. Other Resources • HackTheBox Challenges • Crackmes.one • Binary Ninja

    Blog • Hex-Rays Blog • Flare-On Challenges • The Cyber Yeti • Malware Unicorn