FS寄存器指向當前活動線程的TEB結構(線程結構)
偏移 說明
000 指向SEH鏈指針
004 線程堆棧頂部
008 線程堆棧底部
010 FiberData
014 ArbitraryUserPointer
018 FS段寄存器在內存中的鏡像地址
020 進程PID
024 線程ID
030 PEB結構地址(進程結構)
034 上個錯誤號
在shellcode中用它來找KERNEL32.DLL基地址是常見的算法了,經典的三種算法都用到了FS寄存器!她們是:
1. 通過PEB(FS:[30])獲取KERNEL32.DLL基地址
2. 通過TEB(FS:[18])獲取KERNEL32.DLL基地址
3. 通過SEH(FS:[00])獲取KERNEL32.DLL基地址
下面分別證明之。
命題一:通過PEB(FS:[30])獲取KERNEL32.DLL基地址
算法描述:
mov eax,fs:[30h] ;得到PEB結構地址
mov eax,[eax + 0ch] ;得到PEB_LDR_DATA結構地址
mov esi,[eax + 1ch]
lodsd ; 得到KERNEL32.DLL所在LDR_MODULE結構的
; InInitializationOrderModuleList地址
mov edx,[eax + 8h] ;得到BaseAddress,既Kernel32.dll基址
證明:
1. 隨便open一個exe,內存中的KERNEL32.DLL基地址是不變的;2. 獲取PEB基地址,
0:000> dd fs:
003b:00000030 7ffd6000
看到了,7ffd6000
3. 獲取PEB_LDR_DATA結構地址7ffd6000+
peb的結構定義:
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x
......
0:000> dd 7ffd6000+
7ffd
PEB_LDR_DATA-> 00181ea0
4. 獲取InInitializationOrderModuleList的地址
說一下這個PEB_LDR_DATA,她是ntdll.dll中的undocumented的一個結構,PEB_LDR_DATA的結構定義:
0:000> dt _PEB_LDR_DATA
+0x000 Length : Uint4B
+0x004 Initialized : UChar
+0x008 SsHandle : Ptr32 Void
+0x
+0x014 InMemoryOrderModuleList : _LIST_ENTRY
+0x
+0x024 EntryInProgress : Ptr32 Void
0:000> dd 00181ea0+
00181ebc
InInitializationOrderModuleList->
5. 獲取kernel32的基地址
0:000> dd
check一下:
0:000> dd kernel
啊!竟然不是啊,
命題二:通過TEB(FS:[18])獲取KERNEL32.DLL基地址
算法描述:
本地線程的棧裡偏移18H的指針指向kernel32.dll內部,而fs :[ 0x18 ] 指向當前線程而且往裡四個字節指向線程棧,結合棧頂指針進行對齊遍歷,找到PE文件頭(DLL的文件格式)的“MZ”MSDOS標志,就拿到了kernel32.dll基址。
xor esi , esi
mov esi , fs :[ esi + 0x18 ] // TEB
mov eax , [ esi + 4 ] // 這個是需要的棧頂
mov eax , [ eax - 0x
find_kernel32_base :
dec eax // 開始地毯式搜索Kernel32空間
xor ax , ax
cmp Word ptr [ eax ], 0x
jne find_kernel32_base // 循 環遍 歷 ,找到 則 返回 eax
證明:
1. 找到TEB,這個好辦:
0:000> dd fs:
003b:00000018 7ffdd000
TEB->7ffdd000
2. 找到棧頂指針:
0:000> dd 7ffdd000+
7ffdd004 00070000
3. 進入Kernel32空間:
0:000> dd 00070000
0006ffe4
0:000> db
......一直搞下去
0:000> db
找到了吧,哈哈。有點效率問題,shellcode有時候是要犧牲效率的,沒辦法,還是藝術問題。
命題三:通過SEH(FS:[00])獲取KERNEL32.DLL基地址
算法描述:
注意:FS:[ 0 ] 指向的是SHE,它指向kernel32.dll內部鏈,這樣就可以順籐摸瓜了。FS:[ 0 ] 指向的是SHE的內層鏈,為了找到頂層異常處理,我們向外遍歷找到prev成員等於 0xffffffff 的EXCEPTION_REGISTER結構,該結構的handler值就是系統 默 認的處理例程;這裡有個細節,DLL的裝載是64K邊界對齊的,所以需要利用遍歷到的指向最後的異常處理的指針進行頁查找,再結合PE文件MSDOS標志部分,只要在每個 64K 邊界查找 “MZ ”字符就能找到kernel32.dll基址。
xor ecx , ecx
mov esi , fs :[ ecx ]
find_seh :
mov eax ,[ esi ]
mov esi , eax
cmp [ eax ], ecx
jns find_seh // 0xffffffff
mov eax , [ eax + 0x04 ] // handler
find_kernel32_base :
dec eax
xor ax , ax
cmp Word ptr [ eax ], 0x
jne find_kernel32_base
證明:
1. &n. Kernel32空間的大搜索:
0:000> db
......一直搞下去
0:000> db
找到了吧,哈哈。有點效率問題,shellcode有時候是要犧牲效率的,沒辦法,還是藝術問題。
命題三:通過SEH(FS:[00])獲取KERNEL32.DLL基地址
算法描述:
注意:FS:[ 0 ] 指向的是SHE,它指向kernel32.dll內部鏈,這樣就可以順籐摸瓜了。FS:[ 0 ] 指向的是SHE的內層鏈,為了找到頂層異常處理,我們向外遍歷找到prev成員等於 0xffffffff 的EXCEPTION_REGISTER結構,該結構的handler值就是系統 默 認的處理例程;這裡有個細節,DLL的裝載是64K邊界對齊的,所以需要利用遍歷到的指向最後的異常處理的指針進行頁查找,再結合PE文件MSDOS標志部分,只要在每個 64K 邊界查找 “MZ ”字符就能找到kernel32.dll基址。
xor ecx , ecx
mov esi , fs :[ ecx ]
find_seh :
mov eax ,[ esi ]
mov esi , eax
cmp [ eax ], ecx
jns find_seh // 0xffffffff
mov eax , [ eax + 0x04 ] // handler
find_kernel32_base :
dec eax
xor ax , ax
cmp Word ptr [ eax ], 0x
jne find_kernel32_base
證明:
1. 找到當前SEH:
0:000> dd fs:
003b:00000000 0006fedc
2. 找到最外層SEH:
round 1:
0:000> dd 0006fedc L1
0006fedc 0006ffb0 ; esi
0:000> dd 0006ffb
0006ffb0 0006ffe0 ; [eax]
round 2:
0:000> dd 0006ffb
0006ffb0 0006ffe0 ; esi
0:000> dd 0006ffe
0006ffe0 ffffffff ; [eax]
不錯,第二趟就找到了!此時,eax=0006ffe0
3. 找到MZ:
0:000> dd 0006ffe0+
0006ffe4
0:000> db
......又是一直搞下去
0:000> db
找到!
知其然,更要知其所以然!