在 【Day 05】你逆 - 逆向工程工具介紹有提過逆向工程所需要的工具,其中包含我常用的 Debugger x64dbg。我們也逆過小算盤,認識基本的 Debugger 使用方法,這篇文章也會拿小算盤下手認識 Debugger。
這篇會介紹 Debugger 究竟是怎麼運作的,為什麼可以停在某個斷點,讓使用者可以慢慢的觀察程式的運行流程。由於不同的 Debugger 在處理同個事件上可能會有不同的實作方式,這篇文會以 x64dbg 為例講解 Debugger 的行為。
下面介紹與實際測試的部分都是在 64-bit 的環境,但是 32-bit 其實大同小異。
這是 PEB 結構中的一個成員 BeingDebugged
,顧名思義它就是用來判斷目前這個 Process 是否正在被 Deubg。開發者在程式中可以利用這個 Flag 判斷是否被 Debug,而做出不同的行為。
Windows API 有相對應的 IsDebuggerPresent 和 CheckRemoteDebuggerPresent 函數檢查這個 Flag 的值,回傳為 TRUE 代表程式正在被 Debug。
用 x64dbg 可以看到 PEB 的第三個 Byte 是 1,也就是正在 Debug。
Software Breakpoint(軟體斷點)有三種實作方式,分別是 INT3、Long INT3、UD2,在 x64dbg 可以點選 Options => Preferences => Engine => Default Breakpoint Type 設定。
x64dbg 預設是使用 int3
,Opcode 為 cc
,執行後觸發 EXCEPTION_BREAKPOINT,然後 Debugger 再去處理這個 Exception,達到斷點的效果。
Debugger 會把下斷點的位址的指令改成 int3
,所以當程式執行到這個位址時,就會觸發上面所提到的 EXCEPTION_BREAKPOINT。執行完 int3
後,因為 Instruction Pointer(RIP/EIP)指向的位址是 int3
的下一個 Byte,所以 Debugger 在處理 Exception 時會把 Instruction Pointer 減 1,並且把原本要執行的指令填回去。
用 IDA 打開 calc.exe,可以看到最後有執行 ShellExecuteW。
這邊我們針對 ShellExecuteW 下斷點。因為 Debugger 不會把它做的改變顯示在 Debugger,方便使用者使用,所以我使用了另一個工具觀察。下圖左邊是 x64dbg 在 ShellExecuteW 函數的位址,並且下了斷點;右邊是開源的工具 HookHunter。
HookHunter 會自動比對檔案與記憶體,看函數有沒有被改動。下圖可以發現,下了斷點後,原本執行的是 push rbp
,機械碼是 40 55
。但是因為下斷點的關係,其中的 40
被改成 cc
。而原本的值 40
會被 Debugger 存起來等等用來復原。
在抵達斷點後可以注意到 HookHunter 已經沒有偵測到檔案和記憶體的不同,因為這時 Debugger 已經把原本被改掉的機械碼 40
填回去了。
之後在執行斷點位址的下一個指令後,斷點的位址又被設回去 int3
,機械碼為 cc
。
Trap Flag(陷阱標誌)是 CPU 中的其中一個 Flag,當 Flag 為 1(set) 時會觸發 EXCEPTION_SINGLE_STEP,其他還有 OF(Overflow Flag)、DF(Direction Flag)、IF(Interrupt Flag)、SF(Sign Flag)、ZF(Zero Flag)、AF(Auxiliary Flag)、PF(Parity Flag)、CF(Carry Flag)。
這邊只說明 Trap Flag 的運作原理。在上面介紹 Software Breakpoint 時,最後一個步驟有說明斷點的位址會在執行完後再填回 int3
,讓斷點繼續發揮作用。因此這邊需要有個機制讓 int3
再被填回去,Debugger 就是利用 Trap Flag。
由於 Trap Flag 為 1(set) 時會觸發 EXCEPTION_SINGLE_STEP,Debugger 就可以透過處理這個 Exception 來達到把 int3
填回原本程式的效果。在觸發 Exception 之後,Trap Flag 則會被 CPU 設為 0(unset)。
一樣在 ShellExecuteW 下斷點並停在這個位址後,可以觀察到右邊視窗有個 TF,代表著 Trap Flag,目前的值為 1(set)。
這時會觸發 EXCEPTION_SINGLE_STEP,而 TF 會被重設為 0(reset)。
Hardware Breakpoint(硬體斷點) 是透過 Debug Register DR0~DR7 來實作的。其中 DR0~DR3 四個暫存器存放下硬體斷點的位址;DR4、DR5 保留;DR6 存放 Debug Status,用來判斷是踩到哪個斷點;DR7 是 Debug Control,用來設定斷點,其中包含要在對 DR0~DR3 做什麼操作才會觸發斷點,例如讀、寫、執行。
使用方法很簡單,就在目標位址按右鍵 => Breakpoint => Hardware,設定斷點。接著就可以在右邊視窗中看到 Debug Register 的改變。