在R0和R3時,FS段寄存器分別指向GDT中的不一樣段:在R3下,FS段寄存器的值是0x3B,在R0下,FS段寄存器的值是0x30.分別用OD和Windbg在R3和R0下查看寄存器(XP3),下圖:線程
FS寄存器的改變是從R3進入R0後和從R0退回到R3前完成的,也就是說:都是在R0下給FS賦不一樣值的.(FS在R0和R3中是不一樣的值,在KiFastCallEntry / KiSystemService中FS值由0x3B變成0x30在 KiSystemCallExit / KiSystemCallExitBranch / KiSystemCallExit2 中再將R3的FS恢復)進程
一.R3與R0之間的互相轉換ci
nt!KiSystemService:
808696a1 6a00 push 0
808696a3 55 push ebp
808696a4 53 push ebx
808696a5 56 push esi
808696a6 57 push edi
808696a7 0fa0 push fs //舊的R3 下的FS 保存入棧
808696a9 bb30000000 mov ebx,30h
808696ae 668ee3 mov fs,bx //FS=0X30 FS 值變成了0X30
808696b1 64ff3500000000 push dword ptr fs:[0]
808696b8 64c70500000000ffffffff mov dword ptr fs:[0],0FFFFFFFFh
808696c3 648b3524010000 mov esi,dword ptr fs:[124h] //ESI=_ETHEAD
808696ca ffb640010000 push dword ptr [esi+140h] //PreviousMode
808696d0 83ec48 sub esp,48h
808696d3 8b5c246c mov ebx,dword ptr [esp+6Ch]
KiSystemCallExit部分代碼
80869945 8d6550 lea esp,[ebp+50h]
80869948 0fa1 pop fs // 恢復 FS 值
8086994a 8d6554 lea esp,[ebp+54h]
8086994d 5f pop edi
8086994e 5e pop esi
8086994f 5b pop ebx
80869950 5d pop ebp
80869951 66817c24088000 cmp word ptr [esp+8],80h
源碼
二.R3下的FSit
當線程運行在R3下時,FS指向的段是GDT中的0x3B段.該段的長度爲4K,基地址爲當前線程的線程環境塊(TEB),因此該段也被稱爲「TEB段」.由於Windows中線程是不停切換的,因此該段的基地址值將隨線程切換而改變的.Windows2000中進程環境塊(PEB)的地址爲0X7FFDF000,該進程的第一個線程的TEB地址爲0X7FFDE000,第二個TEB的地址爲0X7FFDD000…..可是在WindowsXP SP3 下這些結構的地址都是隨機映射的.因此進程的PEB的地址只能經過FS:[0x30]來獲取了.Windows中每一個線程都有一個ETHREAD結構,該結構的TEB成員(實際上是KTHREAD中的成員,而KTHREAD又是ETHREAD的成員)是用來保存線程的TEB地址的,當線程切換時,Windows就會用該值來更改GDT的0x30段描述符的基地址值.io
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 //TEB
+0x01c EnvironmentPointer : Ptr32
+0x020 ClientId : _CLIENT_ID
+0x000 UniqueProcess : Ptr32
+0x004 UniqueThread : Ptr32
+0x028 ActiveRpcHandle : Ptr32
+0x02c ThreadLocalStoragePointer : Ptr32
+0x030 ProcessEnvironmentBlock : Ptr32 //PEB
+0x034 LastErrorValue : Uint4B
+0x038 CountOfOwnedCriticalSections : Uint4B
+0x03c CsrClientThread : Ptr32
+0x040 Win32ThreadInfo : Ptr32
FS:[0X18] 就是TEB 所在的地址;FS:[0X30] 就是PEB 所在的地址
asm
三.在R0下的FSast
kd> dg 8 0x40
P Si Gr Pr Lo
Sel Base Limit Type l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0008 00000000 ffffffff Code RE 0 Bg Pg P Nl 00000c9a
0010 00000000 ffffffff Data RW 0 Bg Pg P Nl 00000c92
0018 00000000 ffffffff Code RE 3 Bg Pg P Nl 00000cfa
0020 00000000 ffffffff Data RW 3 Bg Pg P Nl 00000cf2
0028 80042000 000020ab TSS32 Busy 0 Nb By P Nl 0000008b
0030 ffdff000 00001fff Data RW 0 Bg Pg P Nl 00000c92
0038 00000000 00000fff Data RW Ac 3 Bg By P Nl 000004f3
0040 00000400 0000ffff Data RW 3 Nb By P Nl 000000f2sed
當線程運行在R0下時, FS指向的段是GDT中的0x30段.該段的長度也爲4K,基地址爲0xFFDFF000.該地址指向系統的處理器控制區域(KPCR).這個區域中保存這處理器相關的一些重要數據值,如GDT、IDT表的值等等.List
kd> dt nt!_kpcr
nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x01c SelfPcr : Ptr32 _KPCR //KPCR
+0x020 Prcb : Ptr32 _KPRCB
+0x024 Irql : UChar
+0x028 IRR : Uint4B
+0x02c IrrActive : Uint4B
+0x030 IDR : Uint4B
+0x034 KdVersionBlock : Ptr32 Void
+0x038 IDT : Ptr32 _KIDTENTRY
+0x03c GDT : Ptr32 _KGDTENTRY
+0x040 TSS : Ptr32 _KTSS
+0x044 MajorVersion : Uint2B
+0x046 MinorVersion : Uint2B
+0x048 SetMember : Uint4B
+0x04c StallScaleFactor : Uint4B
+0x050 DebugActive : UChar
+0x051 Number : UChar
+0x052 Spare0 : UChar
+0x053 SecondLevelCacheAssociativity : UChar
+0x054 VdmAlert : Uint4B
+0x058 KernelReserved : [14] Uint4B
+0x090 SecondLevelCacheSize : Uint4B
+0x094 HalReserved : [16] Uint4B
+0x0d4 InterruptMode : Uint4B
+0x0d8 Spare1 : UChar
+0x0dc KernelReserved2 : [17] Uint4B
+0x120 PrcbData : _KPRCB
展開_NT_TIB
kd> dt _NT_TIB
nt!_NT_TIB
+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 StackBase : Ptr32 Void
+0x008 StackLimit : Ptr32 Void
+0x00c SubSystemTib : Ptr32 Void
+0x010 FiberData : Ptr32 Void
+0x010 Version : Uint4B
+0x014 ArbitraryUserPointer : Ptr32 Void
+0x018 Self : Ptr32 _NT_TIB //TEB
看兩個地址0x18和0x1C.在TEB中0x18指向本身,即 TEB.而 KPCR 中指向本身的確是 0x1C ;0x18 倒是指向當前線程的 TEB ,因此 0x18 字段名叫作Self-used 比較確切( WIN2K 源碼如此定義).總之,無論是在R3仍是R0 ,FS:[0x18] 老是指向當前線程的 TEB.
實例1:
獲取KTHREAD
展開KPCR後,再次展開kpcrb
kd> dt nt!_kprcb
nt!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD
+0x008 NextThread : Ptr32 _KTHREAD
+0x00c IdleThread : Ptr32 _KTHREAD
展開KPRCB結構繼續觀察能夠看到FS:[0x124]指向了KTHREAD結構
實例2:
獲取IdleProcess
kd> dt _KPRCB
nt!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD
+0x008 NextThread : Ptr32 _KTHREAD
+0x00c IdleThread : Ptr32 _KTHREAD
kd> dt _KTHREAD
nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 MutantListHead : _LIST_ENTRY
+0x018 InitialStack : Ptr32 Void
+0x01c StackLimit : Ptr32 Void
+0x020 Teb : Ptr32 Void
+0x024 TlsArray : Ptr32 Void
+0x028 KernelStack : Ptr32 Void
+0x02c DebugActive : UChar
+0x02d State : UChar
+0x02e Alerted : [2] UChar
+0x030 Iopl : UChar
+0x031 NpxState : UChar
+0x032 Saturation : Char
+0x033 Priority : Char
+0x034 ApcState : _KAPC_STATE
kd> dt _KAPC_STATE
nt!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY
+0x010 Process : Ptr32 _KPROCESS
+0x014 KernelApcInProgress : UChar
+0x015 KernelApcPending : UChar
+0x016 UserApcPending : UChar
VOID GetIdleProcess()
{
PEPROCESS IdleProcess;
_asm
{
mov eax,fs:[0x20] //取KPCRB
mov eax,[eax+0xC] //取IdleThread
mov eax,[eax+0x44]//取ApcState->Process
mov IdleProcess,eax
}
}
最後貼一張KPCR結構圖