Chương 10: Kernel Debugging với WinDbg
1. Tổng quan
WinDbg là debugger miễn phí từ Microsoft, phát âm là “Windbag”. Điểm mạnh lớn nhất so với OllyDbg là khả năng kernel debugging — debug trực tiếp nhân hệ điều hành Windows, phục vụ phân tích rootkit và malware kernel-mode.
2. Drivers và Kernel Code
2.1 Windows Device Driver là gì?
Driver cho phép third-party developer chạy code trong Windows kernel. Khác với DLL (chạy trong user space), driver tồn tại thường trực trong kernel và phản hồi các yêu cầu từ ứng dụng.
Lưu ý quan trọng: Ứng dụng user-space không giao tiếp trực tiếp với driver, mà thông qua device objects.
Ví dụ USB Flash Drive:
App (user space)
→ gửi request đến "F: drive" (device object)
→ kernel định tuyến đến USB driver
→ driver xử lý phần cứngCắm thêm USB thứ hai → Windows tạo thêm device object “G: drive”, cùng driver xử lý nhưng qua device object khác.
2.2 Cơ chế hoạt động của Driver
Vòng đời của một driver:
- Driver được load vào kernel (giống DLL load vào process)
DriverEntryđược gọi đầu tiên (tương đươngDLLMaincho DLL)DriverEntrynhận driver object structure từ Windows, đăng ký các callback functions- Driver tạo ra một device có thể truy cập từ user space
- User-space app tương tác với driver qua device đó
Khác biệt DLL vs Driver:
| DLL | Driver | |
|---|---|---|
| Expose chức năng qua | Export table | Callback functions (đăng ký trong DriverEntry) |
| Chạy trong | Process (user space) | Kernel |
| Entry point | DLLMain | DriverEntry |
2.3 Các loại request phổ biến
DeviceIoControl — request phổ biến nhất từ malicious kernel component:
- User-space gửi buffer đầu vào tùy ý
- Nhận buffer đầu ra tùy ý
- Đây là cơ chế giao tiếp generic nhất giữa user space và kernel driver
ReadFile / WriteFile — cũng có thể định tuyến đến driver, khi app đọc/ghi file handle của device.
2.4 Malicious Driver tương tác với gì?
Malicious driver thường không điều khiển phần cứng, mà tương tác với:
ntoskrnl.exe— code cho các core OS functionshal.dll— code tương tác với phần cứng chính
Malware import function từ một hoặc cả hai file này để thao túng kernel.
3. Thiết lập Kernel Debugging
3.1 Tại sao dùng VMware?
Khi debug kernel, toàn bộ OS bị freeze → không thể chạy debugger trên cùng máy. Giải pháp: dùng VMware với hai máy:
- Guest VM — máy bị debug (chạy malware)
- Host — máy chạy WinDbg
Giao tiếp qua virtual serial port (named pipe).
3.2 Cấu hình boot.ini trên Guest VM (Windows XP)
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
; Entry bình thường
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional"
/noexecute=optin /fastdetect
; Entry thêm vào để enable kernel debugging
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional with Kernel Debugging"
/noexecute=optin /fastdetect /debug /debugport=COM1 /baudrate=115200Ý nghĩa các flag:
| Flag | Ý nghĩa |
|---|---|
/debug | Bật kernel debugging |
/debugport=COM1 | Cổng kết nối với debugger |
/baudrate=115200 | Tốc độ kết nối |
3.3 Cấu hình VMware Serial Port (Named Pipe)
Các bước:
- VM → Settings → Add → Serial Port
- Chọn Output to Named Pipe
- Nhập
\\.\pipe\com_1làm tên pipe - Chọn This end is the server và The other end is an application
- Tick Yield CPU on poll
3.4 Kết nối WinDbg từ Host
- Mở WinDbg
- File → Kernel Debug → Tab COM
- Baud Rate:
115200 - Port:
\\.\pipe\com_1 - Tick checkbox Pipe → OK
4. Sử dụng WinDbg
WinDbg dùng command-line interface cho hầu hết chức năng.
4.1 Đọc Memory — lệnh d
dx addressToRead| Option | Mô tả |
|---|---|
da | Hiển thị dưới dạng ASCII text |
du | Hiển thị dưới dạng Unicode text |
dd | Hiển thị dưới dạng 32-bit double words |
Ví dụ:
da 0x401020Đọc và hiển thị chuỗi ASCII tại địa chỉ 0x401020.
4.2 Ghi Memory — lệnh e
ex addressToWrite dataToWriteDùng cùng ký tự x như lệnh d.
4.3 Arithmetic Operators
Có thể tính toán trực tiếp trên memory/register:
du dwo(esp+4)esp+4→ địa chỉ của argument đầu tiêndwo(...)→ dereference con trỏ 32-bit tại địa chỉ đódu→ hiển thị wide character string tại địa chỉ đó
Hỗ trợ: +, -, *, /
4.4 Đặt Breakpoint — lệnh bp
bp GetProcAddress "da dwo(esp+8); g"- Đặt breakpoint tại
GetProcAddress - Mỗi khi hit: tự động in argument thứ hai (tên function), rồi tiếp tục (
g= go) - Không dừng chương trình → nhanh hơn nhiều so với manual
Command string hỗ trợ:
.ifstatements.whileloops- Scripts phức tạp
Deferred breakpoint — lệnh bu:
bu newModule!exportedFunctionĐặt breakpoint trên code chưa được load — sẽ kích hoạt khi module tương ứng được load.
bu $iment(driverName)Đặt breakpoint tại entry point của driver trước khi bất kỳ code nào của driver chạy.
4.5 Liệt kê Modules — lệnh lm
lmLiệt kê tất cả modules được load (executables, DLLs ở user space, kernel drivers). Hiển thị địa chỉ bắt đầu và kết thúc mỗi module.
4.6 Tìm kiếm Symbols — lệnh x
Format của symbol trong WinDbg:
moduleName!symbolNameVí dụ tìm tất cả function liên quan đến CreateProcess trong ntoskrnl:
x nt!*CreateProcess*Output:
805c736a nt!NtCreateProcessEx
805c7420 nt!NtCreateProcess
805c6a8c nt!PspCreateProcess
804fe144 nt!ZwCreateProcess
804fe158 nt!ZwCreateProcessEx
8055a300 nt!PspCreateProcessNotifyRoutineCount
805c5e0a nt!PsSetCreateProcessNotifyRoutine
8050f1a2 nt!MmCreateProcessAddressSpace
8055a2e0 nt!PspCreateProcessNotifyRoutineTìm symbol gần nhất cho một địa chỉ — lệnh ln:
ln 805717aaOutput:
(805717aa) nt!NtReadFile | (80571d38) nt!NtReadFileScatter
Exact matches:
nt!NtReadFile = <no type information>- Dòng đầu: hai match gần nhất
- Dòng cuối: exact match (chỉ có nếu địa chỉ khớp chính xác)
4.7 Xem cấu trúc dữ liệu — lệnh dt
dt nt!_DRIVER_OBJECTOutput (một phần):
+0x000 Type : Int2B
+0x002 Size : Int2B
+0x00c DriverStart : Ptr32 Void ← địa chỉ load trong memory
+0x010 DriverSize : Uint4B
+0x018 DriverExtension : Ptr32 _DRIVER_EXTENSION
+0x01c DriverName : _UNICODE_STRING
+0x02c DriverInit : Ptr32 long ← entry point (DriverEntry)
+0x030 DriverStartIo : Ptr32 void
+0x034 DriverUnload : Ptr32 void
+0x038 MajorFunction : [28] Ptr32 long ← bảng callback functionsOverlay dữ liệu thực lên structure:
dt nt!_DRIVER_OBJECT 828b2648Output có giá trị thực:
+0x00c DriverStart : 0xf7adb000
+0x01c DriverName : _UNICODE_STRING "\Driver\Beep"
+0x02c DriverInit : 0xf7adb66c long Beep!DriverEntry+0
+0x030 DriverStartIo: 0xf7adb51a void Beep!BeepStartIo+0
+0x034 DriverUnload : 0xf7adb620 void Beep!BeepUnload+0
+0x038 MajorFunction: [28] 0xf7adb46a long Beep!BeepOpen+04.8 Cấu hình Microsoft Symbols
Symbol cung cấp tên cho các địa chỉ memory (function name, variable name) giúp đọc assembly dễ hơn nhiều.
Cấu hình online symbol server:
File → Symbol File Path:
SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols| Phần | Ý nghĩa |
|---|---|
SRV | Dùng symbol server |
c:\websymbols | Local cache |
| URL | Microsoft symbol server |
WinDbg sẽ tự động tải đúng symbols cho version OS đang debug.
5. Kernel Debugging thực tế — Phân tích FileWriter Driver
5.1 Phân tích User-Space Code
Malware dùng các bước sau để tương tác với kernel driver:
Bước 1: Tạo kernel driver service:
push 1 ; dwServiceType = 0x01 → Kernel Driver
push 0F01FFh ; dwDesiredAccess
call CreateServiceABước 2: Lấy handle đến device object:
push edi ; lpFileName = "\\.\FileWriterDevice"
call CreateFileABước 3: Gửi data đến driver:
push 9C402408h ; dwIoControlCode
push [ebp+hObject]
call DeviceIoControl5.2 Tìm Driver trong Kernel
Với verbose output bật, WinDbg thông báo khi driver load:
ModLoad: f7b0d000 f7b0e780 FileWriter.sysTìm driver object:
!drvobj FileWriterOutput:
Driver object (827e3698) is for:
\Driver\FileWriter
Device Object list: 826eb030Xem cấu trúc driver object:
dt nt!_DRIVER_OBJECT 0x827e3698+0x00c DriverStart : 0xf7b0d000
+0x01c DriverName : "\Driver\FileWriter"
+0x02c DriverInit : 0xf7b0dfcd
+0x034 DriverUnload : 0xf7b0da2a
+0x038 MajorFunction : [28] 0xf7b0da065.3 Tìm Function xử lý DeviceIoControl
Major Function Table:
- Mỗi index = một loại request (IRP_MJ_*)
IRP_MJ_DEVICE_CONTROL= index0xeIRP_MJ_READ= index0x3- Mỗi entry là pointer 4 bytes
Tính địa chỉ function xử lý DeviceIoControl:
dd 827e3698+0x38+e*4 L1827e3698→ địa chỉ driver object+0x38→ offset đến MajorFunction table+e*4→ index 0xe × 4 bytes/pointerL1→ chỉ hiển thị 1 DWORD
Output:
827e3708 f7b0da66Kiểm tra code tại địa chỉ đó:
u f7b0da66FileWriter+0xa66:
f7b0da66 push 68h
f7b0da68 push offset FileWriter+0x938
f7b0da6d call FileWriter+0x4945.4 Phân tích Kernel-Mode Code (IDA Pro + WinDbg)
Code trong driver dùng kernel equivalents thay vì Win32 API:
| User space | Kernel equivalent |
|---|---|
CreateFile | NtCreateFile / ZwCreateFile |
WriteFile | NtWriteFile / ZwWriteFile |
GetProcAddress | MmGetSystemRoutineAddress |
Đặc điểm:
- String trong kernel dùng
UNICODE_STRINGstructure (khác wide char thông thường) - Dùng
RtlInitUnicodeStringđể tạo kernel strings - Filename phải có dạng:
\DosDevices\C:\secretfile.txt(fully qualified object name)
5.5 Tìm Driver Object từ Device Object
!devobj FileWriterDeviceOutput:
Device object (826eb030) is for:
Rootkit \Driver\FileWriter DriverObject 827e3698Tìm user-space app đang dùng device:
!devhandles 826eb030Output (abbreviated):
PROCESS 82752da0 ... Image: FileWriterApp.exe
07b8: Object: 826eb0e8 GrantedAccess: 0012019f6. Rootkits
6.1 Rootkit là gì?
Rootkit sửa đổi nội bộ OS để ẩn sự tồn tại của mình:
- Ẩn files
- Ẩn processes
- Ẩn network connections
- Ẩn resources khác
Hầu hết rootkit hoạt động bằng cách sửa đổi kernel.
6.2 SSDT Hooking — Kỹ thuật phổ biến nhất
System Service Descriptor Table (SSDT) — còn gọi là System Service Dispatch Table:
- Dùng nội bộ bởi Microsoft để tra cứu function calls vào kernel
- Không được truy cập bởi third-party apps/drivers bình thường
Cơ chế SYSENTER (từ user space vào kernel):
; ntdll.dll - NtCreateFile implementation
7C90D682 mov eax, 25h ; function number cho NtCreateFile
7C90D687 mov edx, 7FFE0300h
7C90D68C call dword ptr [edx] ; → sysenter
7C90D68E retn 2Ch; Code tại 7FFE0300:
7c90eb8b mov edx, esp
7c90eb8d sysenter ; chuyển sang kernel modeEAX = 0x25→ index vào SSDT- Kernel gọi function tại
SSDT[0x25]
SSDT entries (ví dụ):
SSDT[0x23] = 80603be0 (NtCreateEvent)
SSDT[0x24] = 8060be48 (NtCreateEventPair)
SSDT[0x25] = 8056d3ca (NtCreateFile) ← index 0x25
SSDT[0x26] = 8056bc5c (NtCreateIoCompletion)6.3 Cách rootkit hook SSDT
SSDT[0x25] = 8056d3ca (NtCreateFile gốc)
↓ Rootkit thay đổi
SSDT[0x25] = f7ad94a4 (Hook function của rootkit)Hook function hoạt động:
; Rootkit hook function
000104A4 mov edi, edi
000104A6 push ebp
000104A7 mov ebp, esp
000104A9 push [ebp+arg_8] ; ObjectAttributes
000104AC call sub_10486 ; kiểm tra file có cần ẩn không
000104B1 test eax, eax
000104B3 jz short loc_104BB
000104B5 pop ebp
000104B6 jmp NtCreateFile ; cho phép → gọi function gốc
000104BB ;---
000104BB mov eax, 0C0000034h ; STATUS_OBJECT_NAME_NOT_FOUND
000104C0 pop ebp
000104C1 retn 2Ch ; chặn → báo file không tồn tạisub_10486kiểm tra ObjectAttributes (filename) → trả về non-zero nếu cho phép, zero nếu chặn- User-space app nhận lỗi “file không tồn tại”
6.4 Phân tích Rootkit thực tế
Kiểm tra SSDT để tìm hook:
lm m nt ; lấy địa chỉ bắt đầu/kết thúc của ntoskrnlntoskrnl: 804d7000 → 806cd580
; Xem SSDT
8050128c 8060be48 f7ad94a4 8056bc5c 805ca3ca
↑
Địa chỉ này NẰMN NGOÀI range của ntoskrnl!
→ Bị hook bởi rootkitTìm driver chứa hook address f7ad94a4:
lmf7ac7000 f7ac8580 intelide
f7ac9000 f7aca700 dmload
f7ad9000 f7ada680 Rootkit ← f7ad94a4 nằm trong range này!
f7aed000 f7aee280 vmmouse6.5 Code cài đặt SSDT hook
; Tạo strings cho NtCreateFile và KeServiceDescriptorTable
00010D0D push offset aNtcreatefile ; "NtCreateFile"
00010D12 lea eax, [ebp+NtCreateFileName]
00010D16 call RtlInitUnicodeString ; ① tạo kernel string
00010D1E push offset aKeservicedescr ; "KeServiceDescriptorTable"
00010D27 call RtlInitUnicodeString ; ② tạo kernel string
; Lấy địa chỉ runtime
00010D2C push eax ; NtCreateFileName
00010D33 call MmGetSystemRoutineAddress ; ③ địa chỉ NtCreateFile
00010D35 mov ebx, eax
00010D3A push eax ; KeServiceDescriptorTableString
00010D3B call MmGetSystemRoutineAddress ; ④ địa chỉ SSDT
; Loop tìm entry NtCreateFile trong SSDT
00010D41 add ecx, 4
00010D44 cmp [ecx], ebx ; ⑤ so sánh với địa chỉ NtCreateFile
00010D46 jz short loc_10D51
00010D48 inc edx
00010D49 cmp edx, 11Ch ; ⑥ giới hạn vòng lặp
; Ghi đè địa chỉ hook
00010D57 mov dword_10A08, ebx
00010D5D mov dword ptr [ecx], offset sub_104A4 ; ⑦ overwrite SSDT entry!6.6 Interrupt Descriptor Table (IDT)
Rootkit đôi khi dùng interrupts để can thiệp vào system events.
Xem IDT:
!idtOutput bình thường:
37: 806cf728 hal!PicSpuriousService37
3d: 806d0b70 hal!HalpApcInterrupt
62: 8298b7e4 atapi!IdePortInterrupt
63: 826ef044 NDIS!ndisMIsr
93: 826c315c i8042prt!I8042KeyboardInterruptService
...7. Load Driver thủ công (không có user-space component)
Dùng OSR Driver Loader (miễn phí, cần đăng ký):
- Chỉ định đường dẫn driver (
.sysfile) - Click Register Service
- Click Start Service
8. Lưu ý cho Windows Vista/7 và x64
8.1 Thay thế boot.ini
Từ Vista trở đi, boot.ini không còn dùng nữa → thay bằng BCDEdit:
bcdedit /debug on
bcdedit /dbgsettings serial debugport:1 baudrate:1152008.2 PatchGuard (Kernel Patch Protection)
Áp dụng từ Windows XP x64 trở đi:
- Ngăn third-party code sửa đổi kernel
- Bảo vệ: kernel code, SSDT, IDT, các patching technique khác
- Gây tranh cãi vì ảnh hưởng cả security products hợp lệ (antivirus, firewall)
8.3 Driver Signing (x64 Vista+)
Driver phải được digitally signed mới load được. Malware thường không có chữ ký → rào cản hiệu quả.
Bypass (test/lab):
bcdedit /set nointegritychecks on9. Tóm tắt các lệnh WinDbg quan trọng
| Lệnh | Chức năng |
|---|---|
da addr | Đọc ASCII string tại địa chỉ |
du addr | Đọc Unicode string tại địa chỉ |
dd addr | Đọc 32-bit double words |
du dwo(esp+4) | Dereference pointer và hiển thị Unicode string |
bp addr "cmd; g" | Breakpoint với auto-command |
bu module!func | Deferred breakpoint |
bu $iment(driver) | Breakpoint tại entry point driver |
lm | Liệt kê tất cả loaded modules |
x nt!*Pattern* | Tìm symbol theo wildcard |
ln addr | Symbol gần nhất với địa chỉ |
dt nt!_STRUCT addr | Overlay structure lên địa chỉ |
!drvobj DriverName | Xem driver object |
!devobj DeviceName | Xem device object |
!devhandles handle | Tìm process đang dùng device |
!idt | Xem Interrupt Descriptor Table |
u addr | Disassemble tại địa chỉ |
dd base+0x38+e*4 L1 | Tìm IRP_MJ_DEVICE_CONTROL handler |