淺談FS段寄存器在用戶層和內核層的使用

在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結構圖

相關文章
相關標籤/搜索