iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
Security

《Nyahello!從零開始的 30 天 malware 學習日誌》系列 第 16

【Day 16】進階動態分析:DLL 除錯、Tracing、例外處理與 Patching

  • 分享至 

  • xImage
  •  

前言

經過了執行方法與下斷點的介紹,我們今天終於要來到如何追蹤與 patch 掉惡意程式的介紹~patch 與追蹤在分析時都非常時候,像是追蹤可以對執行資訊進行檢查,而 patch 可以在 debugger 中直接改掉它的指令,強迫程式去走我們想觀察的路徑,以便達到需要的結果。

載入 DLL

除了可以載入執行檔以及 attach 到執行中的程式之外,x64dbg 也可以對 DLL 進行除錯,但是因為 DLL 無法直接執行,x64dbg 會使用一個名為 loaddll.exe 的 dummy program 來載入 DLL。這個技巧非常有用,因為惡意程式經常會以 DLL 的形式封裝,而且大部分的程式碼可能都包含在它的 DllMain 函式中。DllMain 是 DLL 被載入到某個 process 時所呼叫的初始化函式,預設情況下,當 DLL 載入完成之後,x64dbg 會在 DLL 的 entry point(也就是 DllMain)中斷執行。

如果要呼叫正在除錯的 DLL 裡面那些需要參數的 exported functions,首先需要使用 x64dbg 載入這個 DLL,接著,當程式在 DLL 的進入點暫停時,按下 Run 按鈕,讓 DllMain 以及 DLL 所需要的其他初始化程序執行,如下圖。

https://ithelp.ithome.com.tw/upload/images/20260912/20183878kXk24uy3lG.png

接下來,x64dbg 會再次暫停,接著可以從主選單選擇:

Debug → Call DLL Export

藉此呼叫特定的匯出函式、傳入參數,並對它們進行 Debug。

例如,在下圖中,我們將 DLL 檔案載入 x64dbg,然後呼叫位於標記 ① 的 ntohl 函式,這個函式會將一個 32-bit 數值從 network byte order 轉換成 host byte order。

https://ithelp.ithome.com.tw/upload/images/20260912/201838784EK5ABma5e.png

在左側,我們可以加入所需要的任何參數,在這個例子中,我們加入一個參數,也就是 127.0.0.1,它以 network byte order 表示為 0x7F000001,如標記 ② 所示。並且只有我們實際提供參數的位置,左側的框框才需要打勾。

我們可以按下 Follow in Disassembler 按鈕,快速查看 ntohl 函式對應的組合語言指令,右下角的 Hide on call 核取方塊,可以讓這個視窗在執行函式呼叫之後自動隱藏,Pause after call 核取方塊則可以讓程式在匯出函式呼叫完成後立刻暫停。這可以作為設定 breakpoint 之外的另一種實用方法。

當設定好參數以及需要的 registers 之後,按下右下角的 Call 按鈕,就可以強制執行這次函式呼叫,接著,x64dbg 視窗會顯示函式呼叫前後所有暫存器的數值。

如果要對這個匯出函式進行除錯,一定要在按下 Call 之前設定好需要的 breakpoint,或者勾選 Pause after call,在上圖中,可以看到函式的執行結果被儲存在 eax 中,這個結果是以 host byte order 表示的 127.0.0.1,也就是 0x0100007F,如標記 ③ 所示。

追蹤

Tracing 是一種強大的 Debug 技術,它會記錄詳細的程式執行資訊,讓我們之後可以進行檢查。x64dbg 支援多種 tracing 功能,包括 standard back trace、call stack trace,以及 run trace。

1. Standard Back Trace

當使用 Step Into 和 Step Over 功能,在反組譯視窗中逐步執行程式時,x64dbg 都會記錄你的移動過程。可以使用鍵盤上的減號(-)鍵往回移動,查看先前執行過的指令,加號(+)鍵則可以讓你向前移動。

如果之前使用的是 Step Into,那麼你可以追蹤當時執行過的每一個步驟,如果之前使用的是 Step Over,那麼只能查看當初實際逐步執行過的區域,沒有辦法回到過去之後,再決定進入一個當時沒有進入的區域。

2. Call Stack

可以利用 x64dbg 的 call stack trace,查看程式是經過什麼樣的執行路徑才到達目前這個函式,如果要查看 call stack,可以從主選單選擇:

View → Call Stack

接著會看到一個視窗,其中顯示程式為了抵達目前位置所經過的一連串函式呼叫。

如果想沿著 call stack 查看,可以點擊 Call Stack 視窗中的 Address 或 Called From 區域。但是,當你查看這些過去的位置時,暫存器和 stack 並不會顯示「當時」在那個位置時的狀態,除非你正在進行 run trace。

3. Run Trace

Run trace 可以讓程式執行,同時讓 x64dbg 儲存每一條實際執行過的指令,以及所有對 registers 和 flags 造成的變化。以下有幾種方法可以啟用 run tracing。

  • 在反組譯視窗中選取你想要追蹤的程式碼,按右鍵,然後選擇:Run Trace > Add Selection。當這段程式碼執行完成之後,選擇:View > Run Trace,就可以看到剛才實際執行過的指令。可以使用鍵盤上的 - 和 + 鍵在這些程式碼之間移動,就像前面「Standard Back Trace」所介紹的一樣。使用這種方法時,當你逐條查看指令,可以看到每一條指令造成的各個暫存器變化。
  • 使用 Trace Into 以及 Trace Over 選向,這兩種方式可能比 Add Selection 更容易使用,因為不需要事先選取想要追蹤的程式碼。Trace Into 會進入函式內部,並記錄所有實際執行的指令,直到遇到 breakpoint 為止。Trace Over 則只會記錄目前正在執行的函式中所發生的指令。
  • 選擇 Debug > Set Condition,這樣可以讓程式持續進行 tracing,直到某個條件成立為止,當條件成立時,程式就會暫停。如果希望在某個特定情況發生時停止 tracing,然後從那個位置往回追蹤,了解這件事情是「如何發生」或「為什麼發生」,這項功能會非常有用。

備註:如果使用 Trace Into 或 Trace Over 時沒有事先設定 breakpoint,OllyDbg 會嘗試追蹤整個程式,這可能需要非常長的時間,而且會消耗大量記憶體。

追蹤 Poison Ivy

回想昨天提到的,Poison Ivy backdoor 經常會配置一塊記憶體,用來存放從 command-and-control server(C2 server)接收到的 shellcode。Poison Ivy 會下載 shellcode,把它複製到動態配置的記憶體位置,然後執行它,在某些情況下,可以利用 tracing,在 eip 位於 heap 時捕捉到 shellcode 的執行,Trace 可以讓我們看到 shellcode 是如何開始執行的。

下圖 顯示了我們為了捕捉 Poison Ivy 在 heap 中執行程式碼所設定的條件

https://ithelp.ithome.com.tw/upload/images/20260912/2018387809HrJAQO8D.png

我們設定使其在 EIP 小於一般 image 所在的位置時暫停,而這裡使用的典型 image 位置是:

0x400000

在簡單程式中,比這個位置低的區域通常會放置 stack、heap,以及其他動態配置的記憶體,正常程式的 eip 不應該出現在這些位置(這是 32-bit 的時候)。

接著,我們選擇 Trace Into,這樣整個程式都會被追蹤,直到 shellcode 即將開始執行為止,在這個案例中,當 eip 為:

0x142A88

時,程式暫停了,這個位置就是 shellcode 的起始位置,接著,我們可以使用 - 鍵往回查看,了解 shellcode 究竟是如何被執行的。

例外處理

預設情況下,當 x64dbg 已經 attached 到一個程式,而程式執行過程中發生例外時,程式會停止執行,並且會先將控制權交給 debugger。Debugger 可以選擇自己處理這個 exception,也可以將 exception 傳遞給原本的程式處理。當 exception 發生時,x64dbg 會暫停程式執行,而你可以透過下列其中一種方式,決定將 exception 傳遞給程式:

  • SHIFT-F7:Step Into exception
  • SHIFT-F8:Step Over exception
  • SHIFT-F9:執行 exception handler

x64dbg 也提供了 exception handling 的相關設定,如下圖

https://ithelp.ithome.com.tw/upload/images/20260912/20183878e0Ua5uodwd.png

這些選項可以讓 debugger 忽略某些特定的 exception,並直接把這些 exception 傳遞給程式本身,在進行惡意程式分析時,通常可以考慮忽略所有 exception,因為我們的目的並不是為了修正程式中的錯誤而進行 debugging。

Patching

x64dbg 可以很方便地修改幾乎所有執行中的資料,例如 registers 與 flags,也可以讓我們直接將組合語言指令組譯並 patch 進程式中。我們可以選取一段區域、按下滑鼠右鍵,然後選擇:

Binary → Edit

來修改指令或記憶體,接著會跳出一個視窗,讓我們加入任何 opcode 或資料(OllyDbg 也提供一些特殊功能,可以將指定區域填入 00,或者填入 NOP 指令)

下圖顯示了一段來自受密碼保護的惡意程式的程式碼。這個惡意程式要求輸入一組特殊的 key,才能對 malware 進行設定。

https://ithelp.ithome.com.tw/upload/images/20260912/20183878Ub8I4qri60.png

我們可以看到,在標記 ① 的位置有一個重要的檢查,以及一個 conditional jump 指令 JNZ,用來決定輸入的 key 是否被接受。如果發生跳躍,程式會顯示:

Bad key

否則,它會顯示:

Key Accepted!

要強制程式走向「key accepted」這條路徑,一個簡單的方法就是套用 patch,如上圖所示,選取 conditional jump 指令,按下滑鼠右鍵,然後選擇:

Binary → Fill with NOPs

如標記 ② 所示,這樣就會將原本的 JNZ 指令修改成 NOP 指令,於是程式就會認為輸入的 key 已經被接受。

備註:這個 patch 目前只存在於這一次執行中的 process 的即時記憶體(live memory)裡,而我們還可以進一步把這個修改複製到執行檔中。

這是一個兩步驟的流程,如下圖所示

https://ithelp.ithome.com.tw/upload/images/20260912/20183878cnMhOoSVov.png

如果要套用這項修改,在你剛才修改程式碼的 disassembler 視窗中按下滑鼠右鍵,然後選擇:

Copy to Executable → All Modifications

如標記 ① 所示,這會把在 live memory 中所做的所有修改複製出來,並開啟一個新的視窗,如上圖下方所示,接著選擇 Save File,如標記 ② 所示,將修改後的檔案儲存到磁碟。

不過要注意,上圖中包含與上上圖相同的程式碼,不同之處在於原本的 JNZ 指令已經被兩個 NOP 指令取代,這個操作會永久地將 NOP 儲存在磁碟上的 executable 中對應的位置,這表示之後無論輸入任何 key,這個 malware 都會永久接受它。當希望永久修改某個 malware,使它更容易進行分析時,這項技巧會非常有用。

結語

冗長的觀念環節終於結束啦!從記憶體配置、執行方法到如何追蹤、下斷點,再到將程式 patch 掉,我們終於學會了進階動態分析大致上的流程,因此,從明天開始就可以以課程 lab 來進行示範!這本書所用的範例都是 32-bit,所以介紹上圖中其實都是 OllyDbg,在 win 10 的虛擬機中沒辦法示範,不過跟 x32dbg 的介面其實也是大同小異,操作起來沒有差太多,只是希望之後在虛擬機上跑的時候不會 crash 掉。

參考資料

  • Practical Malware Analysis (Chapter 9)

本日貓味

https://ithelp.ithome.com.tw/upload/images/20260912/201838785DfJy08Jyu.jpg


上一篇
【Day 15】使用 x64dbg 動態分析:執行與斷點
下一篇
【Day 17】實戰 Debugger!進行動態分析偵查
系列文
《Nyahello!從零開始的 30 天 malware 學習日誌》17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言