lea eax,SEH1[ebp] ;本身的異常處理函數地址 push eax ;把該異常處理函數地址壓棧 push fs:[0] ;fs:[0]指向的是TIB[Thread information Block]結構中的 ;EXCEPTION_REGISTRATION 結構 mov fs:[0],esp ;讓fs:[0]指向一個新的EXCEPTION_REGISTRATION 結構(就像鏈表插入一個新節點) mov esi,0 ;這兩行指令就是用來處罰這個異常處理函數被調用的代碼 mov eax,[esi];make a error for SEH
SEH結構爲鏈表,fs:[0]指向表頭數據結構
struct{函數
pointer:next;spa
pointer:handleFunction.net
}線程
FS寄存器指向當前活動線程的TEB結構(線程結構)指針
偏移 說明
000 指向SEH鏈指針 //fs:[0]
004 線程堆棧頂部 //fs:[4]
008 線程堆棧底部
00C SubSystemTib
010 FiberData
014 ArbitraryUserPointer
018 FS段寄存器在內存中的鏡像地址
020 進程PID
024 線程ID
02C 指向線程局部存儲指針
030 PEB結構地址(進程結構)
034 上個錯誤號
獲得KERNEL32.DLL基址的方法
assume fs:nothing ;打開FS寄存器
mov eax,fs:[30h] ;獲得PEB結構地址
mov eax,[eax + 0ch] ;獲得PEB_LDR_DATA結構地址
mov esi,[eax + 1ch] ;InInitializationOrderModuleList
lodsd ;獲得KERNEL32.DLL所在LDR_MODULE結構的InInitializationOrderModuleList地址
mov edx,[eax + 8h] ;獲得BaseAddress,既Kernel32.dll基址code
lodsb指令,將esi指向的地址處的數據取出來賦給AL寄存器,esi=esi+1; lodsw指令則取得是一個字。 lodsd指令,取得是雙字節,即mov eax,[esi],esi=esi+4; stosb指令,將AL寄存器的值取出來賦給edi所指向的地址處。mov [edi],AL;edi=edi+1; stosw指令去的是一個字。 stosd指令,取得是雙字節,mov [edi],eax;edi=edi+4;
https://blog.csdn.net/liujiayu2/article/details/77604226?locationNum=7&fps=1orm
代碼運行在RING0(系統地址空間)和RING3(用戶地址空間)時,FS段寄存器分別指向GDT(全局描述符表)中不一樣段:在RING3下,FS段值是0x3B(這是WindowsXP下值;在Windows2000下值爲0x38。差異就是在XP下RPL=3);運行在RING0下時,FS段寄存器值是0x30。下面以XP爲例說說。blog
一. RING3下的FS進程
當代碼運行在Ring3下時,FS值爲指向的段是GDT中的0x38段(RPL爲3)。該段的長度爲4K,基地址爲當前線程的線程環境塊(TEB),因此該段也被稱爲「TEB段」。
WINXPSP1及之前的Windows2000等系統中,進程環境塊(PEB)的地址固定爲0X7FFDF000,該進程的第一個線程的TEB地址爲0X7FFDE000,第二個TEB的地址爲0X7FFDD000…..可是自從WindowsXP SP2開始PEB和TEB的地址都是隨機映射的(詳見博文:MiCreatePebOrTeb函數註釋)。
下圖是WindowsXP SP3下的TEB結構(大小爲0XFB8):
nt!_TEB +0x000 NtTib : _NT_TIB +0x000 ExceptionList : Ptr32 +0x004 StackBase : Ptr32 +0x008 StackLimit : Ptr32 +0x00c SubSystemTib : Ptr32 +0x010 FiberData : Ptr32 +0x010 Version : Uint4B +0x014 ArbitraryUserPointer : Ptr32 +0x018 Self : Ptr32 <—— +0x01c EnvironmentPointer : Ptr32 +0x020 ClientId : _CLIENT_ID +0x000 UniqueProcess : Ptr32 +0x004 UniqueThread : Ptr32 +0x028 ActiveRpcHandle : Ptr32 +0x02c ThreadLocalStoragePointer : Ptr32 +0x030 ProcessEnvironmentBlock : Ptr32 <—— +0x034 LastErrorValue : Uint4B +0x038 CountOfOwnedCriticalSections : Uint4B +0x03c CsrClientThread : Ptr32 +0x040 Win32ThreadInfo : Ptr32 ……
|
FS:[0X18]就是TEB所在的地址;FS:[0X30]就是PEB所在的地址。因爲每一個線程的TEB不盡相同,因此GDT中0X30描述符的基地址會隨着線程的切換而改變的。咱們來看看在什麼地方變換的.看XP SP2 下的SwapContext的代碼(該段代碼在博文 pjf得到SwapContext地址方法的解析 中曾被引用,來講明如何獲取SwapContext地址):
………… 8086dd6c 8b4b40 mov ecx,dword ptr [ebx+40h] 8086dd6f 894104 mov dword ptr [ecx+4],eax 8086dd72 8b6628 mov esp,dword ptr [esi+28h] 8086dd75 8b4620 mov eax,dword ptr [esi+20h]//這兩條指令將新線程的TEB保存在KPRC 8086dd78 894318 mov dword ptr [ebx+18h],eax //的0X18中 8086dd7b fb sti 8086dd7c 8b4744 mov eax,dword ptr [edi+44h] 8086dd7f 3b4644 cmp eax,dword ptr [esi+44h] 8086dd82 c6475000 mov byte ptr [edi+50h],0 8086dd86 7440 je nt!SwapContext+0xe8 (8086ddc8) 8086dd88 8b7e44 mov edi,dword ptr [esi+44h] 8086dd8b 8b4b48 mov ecx,dword ptr [ebx+48h] 8086dd8e 314834 xor dword ptr [eax+34h],ecx 8086dd91 314f34 xor dword ptr [edi+34h],ecx 8086dd94 66f74720ffff test word ptr [edi+20h],0FFFFh 8086dd9a 7571 jne nt!SwapContext+0x12d (8086de0d) 8086dd9c 33c0 xor eax,eax 8086dd9e 0f00d0 lldt ax 8086dda1 8d8b40050000 lea ecx,[ebx+540h] 8086dda7 e850afffff call nt!KeReleaseQueuedSpinLockFromDpcLevel (80868cfc) 8086ddac 33c0 xor eax,eax 8086ddae 8ee8 mov gs,ax 8086ddb0 8b4718 mov eax,dword ptr [edi+18h] 8086ddb3 8b6b40 mov ebp,dword ptr [ebx+40h] 8086ddb6 8b4f30 mov ecx,dword ptr [edi+30h] 8086ddb9 89451c mov dword ptr [ebp+1Ch],eax 8086ddbc 0f22d8 mov cr3,eax 8086ddbf 66894d66 mov word ptr [ebp+66h],cx 8086ddc3 eb0e jmp nt!SwapContext+0xf3 (8086ddd3) 8086ddc5 8d4900 lea ecx,[ecx] 8086ddc8 8d8b40050000 lea ecx,[ebx+540h] 8086ddce e829afffff call nt!KeReleaseQueuedSpinLockFromDpcLevel (80868cfc) 8086ddd3 8b4318 mov eax,dword ptr [ebx+18h]//這幾句就是將新線程的TEB的地址 8086ddd6 8b4b3c mov ecx,dword ptr [ebx+3Ch]//更新到GDT的0X38描述符的基地址 8086ddd9 6689413a mov word ptr [ecx+3Ah],ax //中去。 8086dddd c1e810 shr eax,10h // 8086dde0 88413c mov byte ptr [ecx+3Ch],al // 8086dde3 88613f mov byte ptr [ecx+3Fh],ah // 8086dde6 ff464c inc dword ptr [esi+4Ch] ......... |
二. RING0下的FS
當線程運行在Ring0下時, FS指向的段是GDT中的0x30段。該段的長度也爲4K,基地址爲0xFFDFF000(個人P4單核XPSP3下除了0FFDFF000外還會有其它值,不是是何緣由?)。該地址指向系統的處理器控制區域(KPCR)。這個區域中保存這處理器相關的一些重要數據值,如GDT、IDT表的值等等(關於經過KPCR得到系統一些重要變量可看博文WindowsXP內核變量)。下面就是WindowsXP sp3中的KPCR數據結構:
nt!_KPCR +0x000 NtTib : _NT_TIB +0x000 ExceptionList : Ptr32 +0x004 StackBase : Ptr32 +0x008 StackLimit : Ptr32 +0x00c SubSystemTib : Ptr32 +0x010 FiberData : Ptr32 +0x010 Version : Uint4B +0x014 ArbitraryUserPointer : Ptr32 +0x018 Self : Ptr32 <---- +0x01c SelfPcr : Ptr32 <----- +0x020 Prcb : Ptr32 +0x024 Irql : UChar +0x028 IRR : Uint4B +0x02c IrrActive : Uint4B +0x030 IDR : Uint4B +0x034 KdVersionBlock : Ptr32 +0x038 IDT : Ptr32 +0x03c GDT : Ptr32 +0x040 TSS : Ptr32 +0x044 MajorVersion : Uint2B …………… |
看兩個地址0x18和0x1C。在TEB中0x18指向本身,即TEB。而KPCR中指向本身的確是0x1C;0x18倒是指向當前線程的TEB,因此0x18字段名叫作Self-used比較確切(WIN2K源碼如此定義)。總之,無論是在RING3仍是RING0,FS:[0x18]老是指向當前線程的TEB。
三. RING0與RING3之間的變換
RING0和RING3之間的變換一般是發生在系統調用與返回時,關於系統調用,可參看博文WINDOWS系統調用和 SYSENTER系統服務調用過程。
FS在RING0和RING3中是不一樣的值,在KiFastCallEntry / KiSystemService中FS值由0x3B變成0x30;在KiSystemCallExit / KiSystemCallExitBranch / KiSystemCallExit2中再將RING3的FS恢復。
下面來看看KiSystemService的開頭部分代碼(KiFastCallEntry也是同樣):
nt!KiSystemService: ………… |
再看看下面的KiSystemCallExit部分代碼:
………… |