浅谈FS段寄存器在用户层和内核层的使用

来源:互联网 发布:unity3d创建材质球 编辑:程序博客网 时间:2024/06/05 20:48

//weibo: @少仲


在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之间的互相转换

nt!KiSystemService:808696a1 6a00            push    0808696a3 55              push    ebp808696a4 53              push    ebx808696a5 56              push    esi808696a6 57              push    edi808696a7 0fa0            push    fs    //旧的R3 下的FS 保存入栈 808696a9 bb30000000      mov     ebx,30h808696ae 668ee3          mov     fs,bx  //FS=0X30  FS 值变成了0X30808696b1 64ff3500000000  push    dword ptr fs:[0]808696b8 64c70500000000ffffffff mov dword ptr fs:[0],0FFFFFFFFh808696c3 648b3524010000  mov     esi,dword ptr fs:[124h]  //ESI=_ETHEAD808696ca ffb640010000    push    dword ptr [esi+140h]     //PreviousMode808696d0 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     edi8086994e 5e              pop     esi8086994f 5b              pop     ebx80869950 5d              pop     ebp80869951 66817c24088000  cmp     word ptr [esp+8],80h

二.R3下的FS

当线程运行在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段描述符的基地址值.

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  : Ptr32FS:[0X18] 就是TEB 所在的地址;FS:[0X30] 就是PEB 所在的地址

三.在R0下的FS

kd> dg 8 0x40                                  P Si Gr Pr LoSel    Base     Limit     Type    l ze an es ng Flags---- -------- -------- ---------- - -- -- -- -- --------0008 00000000 ffffffff Code RE    0 Bg Pg P  Nl 00000c9a0010 00000000 ffffffff Data RW    0 Bg Pg P  Nl 00000c920018 00000000 ffffffff Code RE    3 Bg Pg P  Nl 00000cfa0020 00000000 ffffffff Data RW    3 Bg Pg P  Nl 00000cf20028 80042000 000020ab TSS32 Busy 0 Nb By P  Nl 0000008b0030 ffdff000 00001fff Data RW    0 Bg Pg P  Nl 00000c920038 00000000 00000fff Data RW Ac 3 Bg By P  Nl 000004f30040 00000400 0000ffff Data RW    3 Nb By P  Nl 000000f2

当线程运行在R0下时, FS指向的段是GDT中的0x30段.该段的长度也为4K,基地址为0xFFDFF000.该地址指向系统的处理器控制区域(KPCR).这个区域中保存这处理器相关的一些重要数据值,如GDT、IDT表的值等等.
kd> dt nt!_kpcrnt!_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_TIBnt!_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!_kprcbnt!_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 _KPRCBnt!_KPRCB   +0x000 MinorVersion     : Uint2B   +0x002 MajorVersion     : Uint2B   +0x004 CurrentThread    : Ptr32 _KTHREAD   +0x008 NextThread       : Ptr32 _KTHREAD   +0x00c IdleThread       : Ptr32 _KTHREADkd> dt _KTHREADnt!_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_STATEkd> dt _KAPC_STATEnt!_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结构图



0 0