Windows中FS段寄存器 V2

原文链接:

http://blog.csdn.net/misterliwei/article/details/4391580

 

Windows  FS  段寄存器  V2

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

 

    代码运行在 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地址方法的解析http://blog.csdn.net/misterliwei/article/details/3575267中曾被引用,来说明如何获取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
…………

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值