Windows 中 FS 段寄存器 V2

代码运行在 RING0 (系统地址空间)和 RING3 (用户地址空间)时, FS 段寄存器分别指向 GDT( 全局描述符表 ) 中不同段:在 RING3 下, FS 段值是 0x3B (这是 WindowsXP 下值;在 Windows2000 下值为 0x38 。差别就是在 XP 下 RPL=3 );运行在 RING0 下时, FS 段寄存器值是 0x30 。下面以 XP 为例说说。

 

一.           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 获得系统一些重要变量可看博文 Windows XP 内核变量 )。下面就是 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:
808696a1 6a00            push    0
808696a3 55              push    ebp
808696a4 53              push    ebx
808696a5 56              push    esi
808696a6 57              push    edi
808696a7 0fa0            push    fs    ;旧的RING3 下的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
…………
 

你可能感兴趣的:(数据结构,c,windows,XP,byte)