(47)逆向分析 KiSystemService 函数填充 _KTRAP_FRAME 部分
一、回顧
之前的課程,我們學習了API系統調用在3環部分做的事情,有兩種方式進0環,分別是中斷門和快速調用,分別調用兩個不同的函數 KiSystemService 和 KiFastCallEntry。本文我們來逆向分析 KiSystemService 函數填充 _KTRAP_FRAME 部分的反匯編代碼。
分析之前,思考四個問題:
-  
進0環后,原來的寄存器存在哪里?
 -  
如何根據系統調用號(eax中存儲)找到要執行的內核函數?
 -  
調用時參數是存儲到3環的堆棧,如何傳遞給內核函數?
 -  
2種調用方式是如何返回到3環的?
 
要分析 KiSystemService 和 KiFastCallEntry ,我們需要先了解幾個結構體,_Trap_Frame,_ETHREAD,_KTHREAD,_KPCR,_NT_TIB 和 _KPRCB。
接下來,按照 KiSystemService 代碼執行順序,依次介紹涉及到的結構體。
二、_KTRAP_FRAME
首先復習一下TSS表,TSS表只有一張,但是系統里有成百上千的線程,線程進0環時,假設使用中斷門,0環的ESP和SS從TSS表獲取,怎么保證每個線程都有自己的堆棧,不互相沖突呢?答案是,線程切換時會修改TSS表,確保每個線程執行時,TSS里的ESP,SS都對應當前線程。
_Trap_Frame 和0環棧密切相關。用戶定義中斷進0環,涉及提權時,CPU會把5個寄存器的值壓入0環堆棧,這是我們在中斷門的課程里學習過的:
 上圖是不完整的。
實際上,完整的0環的棧是長這樣的,中斷門進0環后,新的ESP指向偏移 0x068(Eip)處:
_KTRAP_FRAME
nt!_KTRAP_FRAME +0x000 DbgEbp : Uint4B +0x004 DbgEip : Uint4B +0x008 DbgArgMark : Uint4B +0x00c DbgArgPointer : Uint4B +0x010 TempSegCs : Uint4B +0x014 TempEsp : Uint4B +0x018 Dr0 : Uint4B +0x01c Dr1 : Uint4B +0x020 Dr2 : Uint4B +0x024 Dr3 : Uint4B +0x028 Dr6 : Uint4B +0x02c Dr7 : Uint4B +0x030 SegGs : Uint4B +0x034 SegEs : Uint4B +0x038 SegDs : Uint4B +0x03c Edx : Uint4B +0x040 Ecx : Uint4B +0x044 Eax : Uint4B +0x048 PreviousPreviousMode : Uint4B +0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD +0x050 SegFs : Uint4B +0x054 Edi : Uint4B +0x058 Esi : Uint4B +0x05c Ebx : Uint4B +0x060 Ebp : Uint4B +0x064 ErrCode : Uint4B +0x068 Eip : Uint4B +0x06c SegCs : Uint4B +0x070 EFlags : Uint4B +0x074 HardwareEsp : Uint4B +0x078 HardwareSegSs : Uint4B +0x07c V86Es : Uint4B +0x080 V86Ds : Uint4B +0x084 V86Fs : Uint4B +0x088 V86Gs : Uint4B也不能叫0環的棧長這樣,這個 _Trap_Frame 結構體,實際上是windows對內核棧的使用約定,它約定了不同的偏移分別用來存儲什么數據。
我們待會逆向 KiSystemService 的時候會發現,第一條指令就是 push 0,意思就是設置 ErrCode = 0。
第一個問題:進0環后,原來的寄存器存在哪里?
看到這個結構,相信你已經有了答案,我們可以通過逆向 KiSystemService 來驗證猜想。
三、_KPCR, _NT_TIB, _KPRCB
這三個結構體存儲了當前CPU的信息,其實這三個結構體是一個結構體,只不過抽象成三部分而已。地址固定是 0xffdff000。
_KPCR
 KPCR(Processor Control Region)是CPU控制區的意思,每個CPU都有一個KPCR結構。
_NT_TIB
 主要存儲了SEH結構化異常鏈表和一個指向自己的指針。
_KPRCB
nt!_KPRCB+0x000 MinorVersion : Uint2B+0x002 MajorVersion : Uint2B+0x004 CurrentThread : Ptr32 _KTHREAD+0x008 NextThread : Ptr32 _KTHREAD+0x00c IdleThread : Ptr32 _KTHREAD+0x010 Number : Char+0x011 Reserved : Char+0x012 BuildType : Uint2B+0x014 SetMember : Uint4B+0x018 CpuType : Char+0x019 CpuID : Char+0x01a CpuStep : Uint2B+0x01c ProcessorState : _KPROCESSOR_STATE+0x33c KernelReserved : [16] Uint4B+0x37c HalReserved : [16] Uint4B+0x3bc PrcbPad0 : [92] UChar+0x418 LockQueue : [16] _KSPIN_LOCK_QUEUE+0x498 PrcbPad1 : [8] UChar+0x4a0 NpxThread : Ptr32 _KTHREAD+0x4a4 InterruptCount : Uint4B+0x4a8 KernelTime : Uint4B+0x4ac UserTime : Uint4B+0x4b0 DpcTime : Uint4B+0x4b4 DebugDpcTime : Uint4B+0x4b8 InterruptTime : Uint4B+0x4bc AdjustDpcThreshold : Uint4B+0x4c0 PageColor : Uint4B+0x4c4 SkipTick : Uint4B+0x4c8 MultiThreadSetBusy : UChar+0x4c9 Spare2 : [3] UChar+0x4cc ParentNode : Ptr32 _KNODE+0x4d0 MultiThreadProcessorSet : Uint4B+0x4d4 MultiThreadSetMaster : Ptr32 _KPRCB+0x4d8 ThreadStartCount : [2] Uint4B+0x4e0 CcFastReadNoWait : Uint4B+0x4e4 CcFastReadWait : Uint4B+0x4e8 CcFastReadNotPossible : Uint4B+0x4ec CcCopyReadNoWait : Uint4B+0x4f0 CcCopyReadWait : Uint4B+0x4f4 CcCopyReadNoWaitMiss : Uint4B+0x4f8 KeAlignmentFixupCount : Uint4B+0x4fc KeContextSwitches : Uint4B+0x500 KeDcacheFlushCount : Uint4B+0x504 KeExceptionDispatchCount : Uint4B+0x508 KeFirstLevelTbFills : Uint4B+0x50c KeFloatingEmulationCount : Uint4B+0x510 KeIcacheFlushCount : Uint4B+0x514 KeSecondLevelTbFills : Uint4B+0x518 KeSystemCalls : Uint4B+0x51c SpareCounter0 : [1] Uint4B+0x520 PPLookasideList : [16] _PP_LOOKASIDE_LIST+0x5a0 PPNPagedLookasideList : [32] _PP_LOOKASIDE_LIST+0x6a0 PPPagedLookasideList : [32] _PP_LOOKASIDE_LIST+0x7a0 PacketBarrier : Uint4B+0x7a4 ReverseStall : Uint4B+0x7a8 IpiFrame : Ptr32 Void+0x7ac PrcbPad2 : [52] UChar+0x7e0 CurrentPacket : [3] Ptr32 Void+0x7ec TargetSet : Uint4B+0x7f0 WorkerRoutine : Ptr32 void +0x7f4 IpiFrozen : Uint4B+0x7f8 PrcbPad3 : [40] UChar+0x820 RequestSummary : Uint4B+0x824 SignalDone : Ptr32 _KPRCB+0x828 PrcbPad4 : [56] UChar+0x860 DpcListHead : _LIST_ENTRY+0x868 DpcStack : Ptr32 Void+0x86c DpcCount : Uint4B+0x870 DpcQueueDepth : Uint4B+0x874 DpcRoutineActive : Uint4B+0x878 DpcInterruptRequested : Uint4B+0x87c DpcLastCount : Uint4B+0x880 DpcRequestRate : Uint4B+0x884 MaximumDpcQueueDepth : Uint4B+0x888 MinimumDpcRate : Uint4B+0x88c QuantumEnd : Uint4B+0x890 PrcbPad5 : [16] UChar+0x8a0 DpcLock : Uint4B+0x8a4 PrcbPad6 : [28] UChar+0x8c0 CallDpc : _KDPC+0x8e0 ChainedInterruptList : Ptr32 Void+0x8e4 LookasideIrpFloat : Int4B+0x8e8 SpareFields0 : [6] Uint4B+0x900 VendorString : [13] UChar+0x90d InitialApicId : UChar+0x90e LogicalProcessorsPerPhysicalProcessor : UChar+0x910 MHz : Uint4B+0x914 FeatureBits : Uint4B+0x918 UpdateSignature : _LARGE_INTEGER+0x920 NpxSaveArea : _FX_SAVE_AREA+0xb30 PowerState : _PROCESSOR_POWER_STATE四、_ETHREAD, _KTHREAD
這倆其實也是同一個結構體,剛才介紹的 _KPRCB + 0x04 里用到了 _KTHREAD。這倆結構體存儲的是線程相關的信息。
_ETHREAD
nt!_ETHREAD+0x000 Tcb : _KTHREAD+0x1c0 CreateTime : _LARGE_INTEGER+0x1c0 NestedFaultCount : Pos 0, 2 Bits+0x1c0 ApcNeeded : Pos 2, 1 Bit+0x1c8 ExitTime : _LARGE_INTEGER+0x1c8 LpcReplyChain : _LIST_ENTRY+0x1c8 KeyedWaitChain : _LIST_ENTRY+0x1d0 ExitStatus : Int4B+0x1d0 OfsChain : Ptr32 Void+0x1d4 PostBlockList : _LIST_ENTRY+0x1dc TerminationPort : Ptr32 _TERMINATION_PORT+0x1dc ReaperLink : Ptr32 _ETHREAD+0x1dc KeyedWaitValue : Ptr32 Void+0x1e0 ActiveTimerListLock : Uint4B+0x1e4 ActiveTimerListHead : _LIST_ENTRY+0x1ec Cid : _CLIENT_ID+0x1f4 LpcReplySemaphore : _KSEMAPHORE+0x1f4 KeyedWaitSemaphore : _KSEMAPHORE+0x208 LpcReplyMessage : Ptr32 Void+0x208 LpcWaitingOnPort : Ptr32 Void+0x20c ImpersonationInfo : Ptr32 _PS_IMPERSONATION_INFORMATION+0x210 IrpList : _LIST_ENTRY+0x218 TopLevelIrp : Uint4B+0x21c DeviceToVerify : Ptr32 _DEVICE_OBJECT+0x220 ThreadsProcess : Ptr32 _EPROCESS+0x224 StartAddress : Ptr32 Void+0x228 Win32StartAddress : Ptr32 Void+0x228 LpcReceivedMessageId : Uint4B+0x22c ThreadListEntry : _LIST_ENTRY+0x234 RundownProtect : _EX_RUNDOWN_REF+0x238 ThreadLock : _EX_PUSH_LOCK+0x23c LpcReplyMessageId : Uint4B+0x240 ReadClusterSize : Uint4B+0x244 GrantedAccess : Uint4B+0x248 CrossThreadFlags : Uint4B+0x248 Terminated : Pos 0, 1 Bit+0x248 DeadThread : Pos 1, 1 Bit+0x248 HideFromDebugger : Pos 2, 1 Bit+0x248 ActiveImpersonationInfo : Pos 3, 1 Bit+0x248 SystemThread : Pos 4, 1 Bit+0x248 HardErrorsAreDisabled : Pos 5, 1 Bit+0x248 BreakOnTermination : Pos 6, 1 Bit+0x248 SkipCreationMsg : Pos 7, 1 Bit+0x248 SkipTerminationMsg : Pos 8, 1 Bit+0x24c SameThreadPassiveFlags : Uint4B+0x24c ActiveExWorker : Pos 0, 1 Bit+0x24c ExWorkerCanWaitUser : Pos 1, 1 Bit+0x24c MemoryMaker : Pos 2, 1 Bit+0x250 SameThreadApcFlags : Uint4B+0x250 LpcReceivedMsgIdValid : Pos 0, 1 Bit+0x250 LpcExitThreadCalled : Pos 1, 1 Bit+0x250 AddressSpaceOwner : Pos 2, 1 Bit+0x254 ForwardClusterOnly : UChar+0x255 DisablePageFaultClustering : UChar_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+0x04c ContextSwitches : Uint4B+0x050 IdleSwapBlock : UChar+0x051 Spare0 : [3] UChar+0x054 WaitStatus : Int4B+0x058 WaitIrql : UChar+0x059 WaitMode : Char+0x05a WaitNext : UChar+0x05b WaitReason : UChar+0x05c WaitBlockList : Ptr32 _KWAIT_BLOCK+0x060 WaitListEntry : _LIST_ENTRY+0x060 SwapListEntry : _SINGLE_LIST_ENTRY+0x068 WaitTime : Uint4B+0x06c BasePriority : Char+0x06d DecrementCount : UChar+0x06e PriorityDecrement : Char+0x06f Quantum : Char+0x070 WaitBlock : [4] _KWAIT_BLOCK+0x0d0 LegoData : Ptr32 Void+0x0d4 KernelApcDisable : Uint4B+0x0d8 UserAffinity : Uint4B+0x0dc SystemAffinityActive : UChar+0x0dd PowerState : UChar+0x0de NpxIrql : UChar+0x0df InitialNode : UChar+0x0e0 ServiceTable : Ptr32 Void+0x0e4 Queue : Ptr32 _KQUEUE+0x0e8 ApcQueueLock : Uint4B+0x0f0 Timer : _KTIMER+0x118 QueueListEntry : _LIST_ENTRY+0x120 SoftAffinity : Uint4B+0x124 Affinity : Uint4B+0x128 Preempted : UChar+0x129 ProcessReadyQueue : UChar+0x12a KernelStackResident : UChar+0x12b NextProcessor : UChar+0x12c CallbackStack : Ptr32 Void+0x130 Win32Thread : Ptr32 Void+0x134 TrapFrame : Ptr32 _KTRAP_FRAME+0x138 ApcStatePointer : [2] Ptr32 _KAPC_STATE+0x140 PreviousMode : Char+0x141 EnableStackSwap : UChar+0x142 LargeStack : UChar+0x143 ResourceIndex : UChar+0x144 KernelTime : Uint4B+0x148 UserTime : Uint4B+0x14c SavedApcState : _KAPC_STATE+0x164 Alertable : UChar+0x165 ApcStateIndex : UChar+0x166 ApcQueueable : UChar+0x167 AutoAlignment : UChar+0x168 StackBase : Ptr32 Void+0x16c SuspendApc : _KAPC+0x19c SuspendSemaphore : _KSEMAPHORE+0x1b0 ThreadListEntry : _LIST_ENTRY+0x1b8 FreezeCount : Char+0x1b9 SuspendCount : Char+0x1ba IdealProcessor : UChar+0x1bb DisableBoost : UChar五、逆向分析 KiSystemService
學習了上面的5個結構體,我們就能看懂 KiSystemService 函數如何初始化 _Trap_Frame 了。
.text:00466481 ; =============== S U B R O U T I N E ======================================= .text:00466481 .text:00466481 .text:00466481 _KiSystemService proc near ; CODE XREF: ZwAcceptConnectPort(x,x,x,x,x,x)+Cp .text:00466481 ; ZwAccessCheck(x,x,x,x,x,x,x,x)+Cp ... .text:00466481 .text:00466481 var_104 = dword ptr -104h .text:00466481 var_100 = dword ptr -100h .text:00466481 var_D0 = dword ptr -0D0h .text:00466481 var_CC = dword ptr -0CCh .text:00466481 var_C8 = dword ptr -0C8h .text:00466481 var_B0 = dword ptr -0B0h .text:00466481 var_AC = dword ptr -0ACh .text:00466481 var_A8 = dword ptr -0A8h .text:00466481 var_A3 = byte ptr -0A3h .text:00466481 var_73 = byte ptr -73h .text:00466481 arg_0 = dword ptr 4 .text:00466481 arg_64 = dword ptr 68h .text:00466481 arg_69 = byte ptr 6Dh .text:00466481 .text:00466481 push 0 ; 錯誤碼初始化為0 .text:00466483 push ebp ; 保存3環寄存器的值 .text:00466484 push ebx .text:00466485 push esi .text:00466486 push edi .text:00466487 push fs .text:00466489 mov ebx, 30h .text:0046648E mov fs, bx ; 設置 fs 選擇子為 0x30 .text:0046648E ; 查GDT表得到 ffc093df`f0000001 .text:0046648E ; fs.base = ffdff000,指向當前CPU的KPCR結構 .text:00466491 assume fs:nothing .text:00466491 push dword ptr ds:0FFDFF000h ; 保存舊的 ExceptionList,然后把新的清成-1 .text:00466497 mov dword ptr ds:0FFDFF000h, 0FFFFFFFFh .text:004664A1 mov esi, ds:0FFDFF124h ; esi 指向 CurrentThread .text:004664A7 push dword ptr [esi+140h] ; 保存 CurrentThread.PreviousMode .text:004664A7 ; PreviousMode = 0 表示從0環調用過來 .text:004664A7 ; PreviousMode != 0 表示從3環調用過來 .text:004664AD sub esp, 48h ; esp 指向 _KTRAP_FRAME .text:004664B0 mov ebx, [esp+68h+arg_0] .text:004664B4 and ebx, 1 .text:004664B7 mov [esi+140h], bl ; 舊CS 與 1 的結果存入 PreviousMode .text:004664BD mov ebp, esp ; ebp 指向 _KTRAP_FRAME .text:004664BF mov ebx, [esi+134h] .text:004664C5 mov [ebp+3Ch], ebx ; _KTRAP_FRAME.Edx 指向舊的 CurrentThread.TrapFrame .text:004664C8 mov [esi+134h], ebp ; CurrentThread.TrapFrame 指向當前 _KTRAP_FRAME .text:004664CE cld ; df = 0 .text:004664CF mov ebx, [ebp+60h] ; 3環ebp .text:004664D2 mov edi, [ebp+68h] ; 3環eip .text:004664D5 mov [ebp+0Ch], edx ; _KTRAP_FRAME.DbgArgPointer = edx .text:004664D5 ; 這一步是保存3環API參數指針 .text:004664D8 mov dword ptr [ebp+8], 0BADB0D00h .text:004664DF mov [ebp+0], ebx ; _KTRAP_FRAME.DbgEbp = _KTRAP_FRAME.Ebp .text:004664E2 mov [ebp+4], edi ; _KTRAP_FRAME.DbgEip = _KTRAP_FRAME.Eip .text:004664E5 test byte ptr [esi+2Ch], 0FFh .text:004664E9 jnz Dr_kss_a ; 測試 CurrentThread.DebugActive .text:004664E9 ; 如果正被調試,保存調試相關的寄存器到 _KTRAP_FRAME .text:004664EF .text:004664EF loc_4664EF: ; CODE XREF: Dr_kss_a+10j .text:004664EF ; Dr_kss_a+7Cj .text:004664EF sti ; 允許中斷 .text:004664F0 jmp loc_4665CD六、總結
我們一開始提出的四個問題,通過逆向,第一個和第三個已經解決了。
-  
進0環后,原來的寄存器存在哪里?
 -  
如何根據系統調用號(eax中存儲)找到要執行的內核函數?
 -  
調用時參數是存儲到3環的堆棧,如何傳遞給內核函數?
 -  
2種調用方式是如何返回到3環的?
 
原來的寄存器存儲到了 _Trap_Frame 結構體里,3環API參數指針通過EDX傳給0環。
總結
以上是生活随笔為你收集整理的(47)逆向分析 KiSystemService 函数填充 _KTRAP_FRAME 部分的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: (46)分析 INT 0x2E 和 sy
 - 下一篇: (48)逆向分析 KiFastCallE