前面幾篇花了不少時間介紹 WinDbg,從基本操作一路看到 Kernel Debugging、Kernel Driver,最後甚至進入 Rootkit 與 SSDT Hooking。
但學到這裡可能會有一個問題:
分析惡意程式的時候,我真的會用到 WinDbg 嗎?
畢竟如果只是分析一般的 .exe,x64dbg 通常已經非常方便。設定 Breakpoint、查看 Register、Memory、Stack,甚至修改 Instruction,都可以直接完成。
那為什麼還需要學 WinDbg?
其實重點不在於「以後所有 Malware 都要用 WinDbg 分析」,而是當分析開始從單一 Process 往 Windows OS 底層深入時,只看 User Mode 就可能不夠了。
所以 WinDbg 系列的最後一篇,就來整理前面學過的內容,以及 WinDbg 在 Malware Analysis 中到底扮演什麼角色。
【聲明】由於時間問題本日文章由 AI 生成QQ
最一開始接觸 Dynamic Analysis 時,我們通常是在 User Mode 分析 Malware。
例如使用:
OllyDbg
x64dbg
WinDbg
開啟一個執行檔,接著觀察 Malware 執行的 Assembly。
最基本的流程可能是:
載入 Malware
↓
設定 Breakpoint
↓
執行程式
↓
Breakpoint 觸發
↓
查看 Registers / Stack / Memory
↓
Single Step
↓
理解 Malware 行為
在這個階段,Debugger 最重要的用途就是讓程式「停下來」。
如果沒有 Debugger,一個 Function 可能在幾毫秒內就執行結束,我們只能看到最後造成的結果。
但是透過 Breakpoint,就可以停在某一條 Instruction 前面,查看當下的:
Registers
Stack
Memory
Instructions
再使用 Single Step 一步一步執行。
WinDbg 和 x64dbg 的介面與操作方式差很多。
x64dbg 很多操作可以直接透過 GUI 完成,而 WinDbg 很常使用 Command。
例如:
g
代表繼續執行程式。
t
代表 Single Step,也就是執行下一條 Instruction。
p
則類似 Step Over,遇到 Function Call 時不會跟進 Function 裡面。
如果想查看 Disassembly,可以使用:
u
查看 Memory 則可以使用:
db
dd
dq
分別以不同大小查看記憶體內容。
而:
lm
可以查看目前載入的 Modules。
這些指令本身不難,真正重要的是知道:
我現在為什麼要看這些資訊?
例如 Malware 呼叫某個 Function 前,可以先查看 Register 與 Stack,了解傳入的 Argument。
Function 執行完成之後,再查看 Return Value。
Debugger 的用途不是單純「一步一步按」,而是讓我們在重要的位置停下來觀察程式狀態。
一般的 Application 大多執行在 User Mode。
但是很多真正的系統操作最後都必須交給 Kernel。
例如一個程式想要開啟 File,可以把流程簡化成:
Application
↓
Windows API
↓
ntdll.dll
↓
Nt* System Service
↓
System Call
↓
Kernel Mode
↓
Windows Kernel / Driver
前面分析 Rootkit 時,就已經看到一個實際例子:
NtCreateFile
User Mode 程式需要進行 File Operation 時,最後可能透過 System Call 進入 Kernel。
所以 User Mode Debugging 與 Kernel Debugging 並不是兩個完全沒有關係的世界。
反而可以把它們理解成不同的觀察範圍。
User Mode Debugger 比較像是在觀察:
某一個 Process 做了什麼?
Kernel Debugger 則可以進一步觀察:
整個 OS 底層正在發生什麼?
如果 Malware 只是一個普通的 .exe,很多時候根本不需要 Kernel Debugging。
使用 x64dbg、Procmon、Process Explorer 等工具,可能就已經可以取得需要的資訊。
但如果 Malware 開始碰到:
Kernel Driver
Rootkit
System Call
Kernel Structure
Interrupt
只觀察 User Mode 就不一定足夠。
例如前一篇介紹的 Rootkit,可以修改 OS 內部功能,讓 User Mode Application 取得被修改過的資訊。
假設:
malware.sys
明明存在,但是 Rootkit 攔截相關操作,最後回傳:
STATUS_OBJECT_NAME_NOT_FOUND
User Mode Application 看到的結果就會是:
File does not exist
這時候如果只相信 User Mode 得到的資訊,就可能真的以為 File 不存在。
因此分析 Kernel Malware 時,需要把觀察位置移到更底層。
Kernel Debugging 和一般 Debugging 還有一個很大的不同:
通常不會直接在同一個環境裡完成。
常見架構是:
Host
↓
WinDbg
↓
Debug Connection
↓
Target VM
↓
Windows Kernel
Host 上執行 WinDbg,而 Target VM 則執行真正要分析的系統。
這樣做的一個原因是,Kernel 本身就是整個 OS 的核心。
User Mode Process Crash,通常只是那個 Process 結束。
但 Kernel 發生嚴重問題,影響的可能是整個系統。
所以 Kernel Debugging 的操作方式和一般開啟 .exe 分析會有明顯不同。
Malware 不一定只有 EXE 或 DLL,也可能包含:
.sys
也就是 Windows Driver。
Driver 可以在 Kernel Mode 執行,因此分析惡意 Driver 時,就需要理解一些 Kernel 相關概念。
使用 WinDbg 可以查看目前載入的 Module。
例如:
lm
在 Kernel Debugging 中,這可以幫助我們查看目前系統載入了哪些 Driver。
如果知道某個 Address:
f7ad94a4
但是不知道它屬於哪個 Driver,也可以透過 Module 的 Address Range 判斷。
例如:
f7ad9000 - f7ada680 Rootkit
而:
f7ad94a4
剛好位於這個範圍內。
那麼就可以知道這個 Address 位於:
Rootkit Driver
之中。
這也是前面分析 Rootkit 時使用到的方法。
Rootkit 是最能說明 Kernel Debugging 價值的例子之一。
前一篇看到的 SSDT Hooking,就是修改:
System Service Descriptor Table
正常情況:
Application
↓
NtCreateFile
↓
System Call
↓
SSDT
↓
Original NtCreateFile
但是 Rootkit 可以修改 SSDT 裡面的 Function Address:
Application
↓
NtCreateFile
↓
System Call
↓
SSDT
↓
Rootkit Hook
Rootkit 就可以先檢查 Request。
如果是普通 File:
Rootkit Hook
↓
Original NtCreateFile
正常執行。
如果是 Rootkit 想隱藏的 File:
Rootkit Hook
↓
STATUS_OBJECT_NAME_NOT_FOUND
如此一來,User Mode Application 就會認為 File 不存在。
這也說明了 Kernel Malware Analysis 和一般 Malware Analysis 最大的差異之一:
不能完全相信 OS 回傳給 User Mode 的結果。
因為如果 Kernel 本身已經遭到修改,User Mode 看到的資訊也可能已經被動過手腳。
學了這麼多 WinDbg,並不代表之後拿到任何 Malware 都要先開 WinDbg。
工具應該根據分析需求選擇。
如果今天只是拿到一個普通的 Windows EXE,希望知道:
它建立了什麼 Process?
連到哪個 IP?
寫入哪些 File?
修改哪些 Registry Key?
使用:
Procmon
Process Explorer
Wireshark
FakeNet-NG
x64dbg
可能就已經足夠。
但是遇到以下情況,WinDbg 的重要性就會提高:
分析 Kernel Driver
Malware 包含 .sys Driver,需要了解它進入 Kernel 後的行為。
分析 Rootkit
惡意程式可能修改 Kernel Function、Hook 系統功能,甚至隱藏自己的存在。
分析 System Call
想進一步了解 User Mode 行為進入 Kernel 之後發生什麼事情。
分析 Crash
程式或 Driver 發生 Exception 或 Crash,需要透過 Dump 找出問題發生的位置。
查看 Kernel Structure
需要了解 Process、Thread、Driver 或其他 Kernel Object 的狀態。
換句話說,WinDbg 比較像是一個:
當一般 User Mode Analysis 不夠時,可以繼續往 Windows 底層觀察的工具。
既然 WinDbg 功能這麼強,是不是之後就不用 x64dbg 了?
其實完全不是。
兩者適合的情境不太一樣。
| 分析情境 | x64dbg | WinDbg |
|---|---|---|
| 一般 EXE Malware | ✓ | ✓ |
| Assembly Trace | ✓ | ✓ |
| Breakpoint | ✓ | ✓ |
| 快速 User Mode 分析 | 很方便 | 可以 |
| Crash Dump | 較有限 | ✓ |
| Kernel Driver | 不適合 | ✓ |
| Kernel Debugging | ✗ | ✓ |
| Rootkit Analysis | ✗ | ✓ |
如果今天只是分析一般 Malware,我自己還是會優先選擇 x64dbg。
它的 GUI 對 Reverse Engineering 很方便,很多操作也比 WinDbg 直覺。
WinDbg 真正重要的地方,是它把分析能力往更底層延伸。
因此不需要問:
「x64dbg 和 WinDbg 哪個比較好?」
而應該問:
「我現在想觀察的東西在哪一層?」
回頭看這幾篇 Debugging 的內容,一開始學的其實都是很基本的操作:
Breakpoint
Single Step
Step Over
Registers
Stack
Memory
Disassembly
接著慢慢開始接觸:
Process
Thread
Module
Memory Map
Address
再往後進入:
User Mode
Kernel Mode
System Call
Kernel Driver
Rootkit
SSDT
Interrupt
看起來學了很多不同的東西,但其實都圍繞著同一件事情:
理解程式現在正在做什麼。
一開始可能只知道:
call eax
接著開始思考:
EAX 裡面是什麼?
這個 Address 屬於哪個 Module?
這個 Function 接收什麼 Argument?
它為什麼要呼叫這個 Windows API?
這個操作有沒有進入 Kernel?
如果有 Driver,它在 Kernel 裡做了什麼?
這才是學習 Debugger 真正重要的部分。
到這裡,WinDbg 系列也差不多告一段落。
從最開始學習 Breakpoint、Registers、Stack 與 Memory,到後來開始接觸 Kernel Debugging、Driver 與 Rootkit,可以發現 Debugger 本身其實只是一個工具。
真正困難的從來不是記住:
g
t
p
u
lm
這些 Command。
而是知道:
什麼時候該停下來?
停下來之後要看什麼?
看到這些資訊之後,又能推論出什麼?
在一般 User Mode Malware 中,x64dbg 可能就已經足夠。
遇到 Kernel Driver、Rootkit 或需要進一步理解 Windows 底層行為時,WinDbg 才會真正發揮它的價值。
所以學 WinDbg 的目的,不是為了把所有 Malware 都丟進 WinDbg。
而是讓自己的 Malware Analysis 不會永遠停留在 User Mode。
當有一天分析的 Malware 從:
EXE
一路走到:
DLL
↓
Windows API
↓
System Call
↓
Kernel
↓
Driver
我們至少知道接下來應該往哪裡看,以及可以使用什麼工具繼續追下去。
而這也是這幾篇學習 WinDbg 最大的收穫。
