在 x86 Windows 使用者模式下,FS 暫存器指向當前執行緒的 TEB (Thread Environment Block)。TEB 偏移 0x30 指向 PEB (Process Environment Block)。這兩個結構是核心物件,記錄了進程與執行緒的關鍵資訊,也是 Shellcode 定位 KERNEL32.DLL 基址的核心依據。
| 偏移 | 名稱 | 說明 |
|---|---|---|
| 0x00 | SEH Chain | 結構化例外處理鏈表頭 |
| 0x04 | Stack Top | 執行緒堆疊頂端 |
| 0x08 | Stack Bottom | 執行緒堆疊底端 |
| 0x18 | Self | FS 暫存器在記憶體中的鏡像地址(指向 TEB 自身) |
| 0x30 | PEB | 指向進程 PEB 結構 |
0x0C:Ldr — 指向 PEB_LDR_DATA 結構,管理載入模組鏈表。mov eax, fs:[30h] ; 取得 PEB 地址
mov eax, [eax + 0Ch] ; 取得 PEB_LDR_DATA 地址
mov esi, [eax + 1Ch] ; 取得 InInitializationOrderModuleList 連結頭
lodsd ; ESI 指向第一個 LDR_MODULE(通常為 ntdll.dll),EAX = 下一個結構地址
mov edx, [eax + 08h] ; 取得 BaseAddress,即 KERNEL32.DLL 基址
0:000> dd fs:30 L1
003b:00000030 7ffd6000 ; PEB = 0x7ffd6000
0:000> dd 7ffd6000+0c L1
7ffd600c 00181ea0 ; PEB_LDR_DATA = 0x00181ea0
0:000> dd 00181ea0+1c L1
00181ebc 00181f58 ; InInitializationOrderModuleList = 0x00181f58
0:000> dd 00181f58+8 L1
00181f60 7c920000 ; BaseAddress = 0x7c920000 (此處為 ntdll.dll)
; 注意:在 Shellcode 環境中,第一個模組通常是 kernel32.dll,故直接取第一個即可。
; 實際 Shellcode 需遍歷鏈表比對模組名稱(Unicode),但受限於體積常假設順序固定。
TEB+0x04 指向堆疊頂。主執行緒啟動時,堆疊上殘留 KERNEL32!BaseProcessStart 或相關函數的返回位址,該位址落在 KERNEL32.DLL 映像範圍內。從該位址向下按 64KB 對齊掃描 MZ (0x5A4D) PE 標誌,即可定位基址。
xor esi, esi
mov esi, fs:[esi + 0x18] ; TEB
mov eax, [esi + 4] ; Stack Top
mov eax, [eax - 0x1C] ; 取得指向 KERNEL32 內部的返回位址
find_kernel32_base:
dec eax
xor ax, ax ; 對齊 64KB 邊界
cmp word ptr [eax], 0x5A4D ; "MZ"
jne find_kernel32_base
; EAX 即為 KERNEL32.DLL 基址
0:000> dd fs:18 L1
003b:00000018 7ffdd000 ; TEB
0:000> dd 7ffdd000+4 L1
7ffdd004 00070000 ; Stack Top
0:000> dd 00070000-1c L1
0006ffe4 7c839aa8 ; 指向 KERNEL32 內部位址
; 向下掃描...
0:000> db 7c800000 L4
7c800000 4d 5a 90 00 ; 找到 MZ 標誌,基址 = 0x7c800000
FS:[0] 指向當前 SEH 記錄鏈表頭。遍歷至最外層(Next = 0xFFFFFFFF)的 EXCEPTION_REGISTRATION,其 Handler 指向系統預設例外處理器(位於 KERNEL32.DLL)。同樣按 64KB 對齊向下掃描 MZ 標誌。
xor ecx, ecx
mov esi, fs:[ecx] ; SEH 头
find_seh:
mov eax, [esi] ; Next
mov esi, eax
cmp [eax], ecx ; Next == 0xFFFFFFFF ?
jns find_seh
mov eax, [eax + 4] ; Handler (指向 KERNEL32 內部)
find_kernel32_base:
dec eax
xor ax, ax
cmp word ptr [eax], 0x5A4D
jne find_kernel32_base
0:000> dd fs:0 L1
003b:00000000 0006fedc ; 第一層 SEH
; 遍歷兩次到達頂層...
0:000> dd 0006ffe0+4 L1
0006ffe4 7c839aa8 ; Handler 指向 KERNEL32 內部
; 向下掃描同演算法二,最終定位 0x7c800000
FS 暫存器存取 TEB/PEB/SEH 這三個核心結構,本質上都是從已知的執行緒/進程狀態出發,定位到 KERNEL32.DLL 映像範圍內的某個位址,再透過 PE 標誌回溯基址。