昨天我們學習了一堆關於記憶體的觀念,今天終於要開始來到執行的部分了!今天會介紹執行的選項和下斷點的種類與方式,以便日後分析時能夠順利操作。
如何執行程式在 Debug 中是一個非常重要的部分,x64dbg 提供了許多不同選項,以下是幾個常見的選項

在 Debug 的時候可以根據不同需求去跑,例如最簡單的方式:Run 和 Pause,就是可以開啟或停止一個程式,不過 Pause 很少用到,因為直接暫停可能會停在沒什麼用的地方,像是 library code。真正要觀察的時候,會使用後面提到的 break point。
x64dbg 也提供了幾種逐步執行程式碼的方法,像是 stepping 指的是一次只執行一條指令,接著立刻暫停程式,這樣可以一條指令接著一條指令地追蹤程式的執行狀況。x64dbg 提供上一章介紹過的兩種逐步執行方式:single-stepping(單步執行,也稱為 stepping-into)以及 stepping-over。
single-stepping 是最簡單的一種逐步執行方式,代表x64dbg 只會執行一條指令,接著就暫停,不論目前執行的是什麼類型的指令都是如此。
例如,如果你對以下指令進行 single-step:
call 01007568
x64dbg 會停在位址 01007568,這是因為 call 指令會把 eip 轉移到該位址。
從概念上來說,stepping-over 幾乎和 single-stepping 一樣簡單,如下圖

如果對 call 指令使用 step-over,x64dbg 會直接在 010073A9 暫停,也就是 call 指令之後的:
xor ebx, ebx
這條指令
這非常有用,因為你可能並不想進入位於 01007568 的子程序裡面分析。
雖然從概念上來說,stepping-over 很簡單,但是在底層實作上,它其實複雜得多,x64dbg 會在 010073A9 設定一個中斷點,然後恢復程式執行,就像按下 Run 按鈕一樣。接著,當該子程序最後執行 ret 指令並 return 時,程式會因為先前設定的隱藏中斷點,而在 010073A9 暫停。
然後雖然在幾乎所有情況下,stepping-over 都會按照預期運作,但是在少數情況下,經過混淆的程式碼或惡意程式碼可能會利用這個機制。例如,位於 01007568 的子 process 可能永遠不會執行 ret 指令,或者,它可能是一種所謂的 get-eip 操作,會把儲存在 stack 上的返回位址取出,在這些少見的情況下,使用 stepping-over 可能會導致程式恢復執行之後,再也沒有暫停下來,因此,我們需要謹慎使用 stepping-over。
breakpoint 有很多種不同類型,而 x64dbg 支援所有這些類型,預設情況下,x64dbg 使用 software breakpoint(軟體中斷點),但是也可以使用 hardware breakpoint(硬體中斷點)。除此之外,還可以設定 conditional breakpoint(條件中斷點),以及在記憶體上設定 breakpoint。
可以在反組譯視窗中選取某條指令,然後按下 F2 來加入或移除 breakpoint,然後可以透過選擇 View > Breakpoints,或點擊工具列上的 B 圖示,查看目前程式中所有有效的 breakpoint。
關閉或終止正在除錯的程式之後,x64dbg 通常會儲存你所設定的 breakpoint 位置,如此一來,下次再次除錯相同程式時,就可以使用原本的 breakpoint,而不必重新設定。
下圖列出了 x64dbg 所支援的完整 breakpoint 類型

Software breakpoint 在除錯字串解碼函式時特別有用,字串可以提供許多關於程式功能的重要線索,因此惡意程式作者經常會試圖混淆字串,當惡意程式作者這麼做時,他們通常會使用一個 string decoder,也就是字串解碼器,而且每次使用某個字串之前,都會先呼叫這個解碼器。
下圖顯示了一個例子,其中經過混淆的資料先被壓入 stack,接著呼叫 String_Decoder。

經過混淆的資料通常會被解碼成一個有意義的字串,並儲存在 stack 上,因此,要看到這個字串,往往必須等字串解碼函式執行完畢後,再查看 stack,所以,如果想查看所有被解碼出來的字串,最適合設定 breakpoint 的地方就是 string decoder routine 的結尾。如此一來,每當在 x64dbg 中選擇 Play,程式就會繼續執行,並且在某個字串被解碼、準備使用時停止。這種方式只能辨識程式在實際執行過程中使用到的字串,而且只有在那些字串被使用時才會看到它們。
conditional breakpoint 是一種只有在某個特定條件成立時才會中斷的 software breakpoint,x64dbg 允許使用 expression(運算式)設定 conditional breakpoint。每次 software breakpoint 被觸發時,x64dbg 都會計算該 expression,如果 expression 的結果不是 0,程式就會暫停執行。
不過使用 conditional breakpoint 時要小心,設定 conditional breakpoint 可能會讓程式執行速度大幅下降,此外,如果設定的條件不正確,程式有可能永遠都不會停止。
Conditional software breakpoint 在下列情況中特別有用:如果某個 API function 被非常頻繁地呼叫,而你只想在某個特定參數被傳入該 API 時才讓程式停止,就可以使用 conditional breakpoint 來節省大量時間。
舉例來說,可以使用 conditional breakpoint 來偵測超過特定大小的記憶體配置。以 Poison Ivy 為例,Poison Ivy 是一種常見的 backdoor,它會透過網際網路接收由攻擊者控制的 command-and-control server 所傳送的指令。這些指令以 shellcode 的形式實作,而 Poison Ivy 會配置記憶體來存放接收到的 shellcode,然而,Poison Ivy 執行的大多數記憶體配置都很小,而且沒有太大的分析價值,除了 command-and-control server 傳送大量 shellcode,準備讓 Poison Ivy 執行時。
要捕捉 Poison Ivy 為這些 shellcode 所進行的記憶體配置,最好的方法是在 Kernel32.dll 的 VirtualAlloc 函式上設定 conditional breakpoint。VirtualAlloc 是 Poison Ivy 用來動態配置記憶體的 API function,因此,如果你設定一個條件,只有當記憶體配置大小大於 100 bytes 時才觸發 breakpoint,那麼那些較小、而且更頻繁發生的記憶體配置就不會讓程式停止。
為了進行這個設定,我們可以先在 VirtualAlloc 函式的開頭設定一個一般 breakpoint,然後執行程式,直到這個 breakpoint 被觸發。
下圖顯示了程式在 VirtualAlloc 開頭的 breakpoint 被觸發時,stack window 的狀況

圖中顯示了 stack 最上面的五個項目,第一個是 return address,接下來則是 VirtualAlloc 的四個參數:Address、Size、AllocationType 與 Protect。每個參數的值以及它們在 stack 中的位置,都會顯示在旁邊,在這個例子中,程式準備配置 0x29 bytes 的記憶體。由於 ESP register 指向 stack 的最頂端,因此如果要取得 Size 欄位,就必須透過記憶體位置 [ESP+8] 來引用它。
下圖顯示了當 breakpoint 停在 VirtualAlloc 開頭時的 disassembler window

我們設定 conditional breakpoint:
[ESP+8] > 100
這樣就可以在 Poison Ivy 即將接收大量 shellcode 時把它攔下來。
如果要設定這個 conditional software breakpoint,可以依照以下步驟操作:
[ESP+8]>100
OllyDbg 可以使用專用的 hardware registers 來設定 hardware breakpoint,Hardware breakpoint 非常強大,因為它不會修改你的程式碼、stack,或任何目標資源,也不會降低程式的執行速度。
hardware breakpoint 最大的問題是一次最多只能設定四個,如果要在某條指令上設定 hardware breakpoint,可以對該指令按右鍵,選擇:
Breakpoint → Hardware, on Execution
也可以透過 Debugging Options menu,讓 x64dbg 預設使用 hardware breakpoint,而不是 software breakpoint。
這麼做的一個原因,是為了防禦某些 anti-debugging 技術,例如 software breakpoint scanning。
x64dbg 也支援 memory breakpoint,Memory breakpoint 可以設定在某一段記憶體上,只要程式存取了這段記憶體,執行就會被中斷。x64dbg 支援 software 和 hardware 兩種形式的 memory breakpoint,你也可以指定想在哪一種存取行為發生時觸發 breakpoint,例如:read、write、execute,或任何形式的 access。
如果要設定基本的 memory breakpoint,可以在 memory dump window 中選取一段記憶體,或者在 memory map 中選取某個 section,接著按右鍵並選擇:
Breakpoint → Memory, on Access
一次只能設定一個 memory breakpoint,如果你設定新的 memory breakpoint,之前設定的 memory breakpoint 就會被移除。
x64dbg 實作 software memory breakpoint 的方式,是修改包含所選記憶體區域的 memory block attributes,然而,這種技術並不總是可靠,而且可能產生相當大的效能負擔,因此,memory breakpoint 應該謹慎使用,不要大量使用。
Memory breakpoint 在惡意程式分析中特別有用的一種情況,是當你想知道某個已載入的 DLL 究竟什麼時候開始被使用。你可以利用 memory breakpoint,在程式開始執行該 DLL 裡面的程式碼時立刻停止。
方法如下:
當程式的執行流程進入該 DLL 的 .text section 時,程式就應該會中斷。
今天了解了 Debug 時執行的種類與下斷點的方法,接下來會繼續介紹如何追蹤與 patch 等,並且以實例示範~
