Windows中FS段寄存器 V2

Windows FS 段寄存器 V2

 

[ 注意:本文是以前文章《 Windows FS段寄存器 》的修订版。 ]

 

代码运行在 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 不尽相同,所以GDT0X30 描述符的基地址会随着线程的切换而改变的。我们来看看在什么地方变换的.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] // 更新到GDT0X38 描述符的基地址

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)