系统调用002 KiSystemService函数逆向分析
文章目錄
- 前言
- 保存現場
- _KTRAP_FRAME
- KRPC
- ExceptionList
- _KTHREAD結構體
- 先前模式
- 抬高堆棧
- 判斷當前權限
- 更新_KTRAP_FRAME
- 保存三環的寄存器環境
- 判斷調試狀態
前言
之前我們詳細了解了API從三環進到零環的過程,API會通過兩種方式進入到零環,如果通過中斷門的方式進入零環,最終會進入到KiSystemService這個函數。接下來就來分析KiSystemService這個函數的內部實現細節。
用IDA打開ntkrnlpa.exe,找到KiSystemService函數
程序想要運行必須要有兩樣東西,分別是EIP和ESP。一旦進入零環,就需要把寄存器保存到一個結構體,這個結構體就是_KTRAP_FRAME,也就是零環的堆棧。
保存現場
_KTRAP_FRAME
_KTRAP_FRAME這個結構體由操作系統維護 數據如下所示:
kd> dt _KTRAP_FRAME nt!_KTRAP_FRAME+0x000 DbgEbp : Uint4B+0x004 DbgEip : Uint4B+0x008 DbgArgMark : Uint4B+0x00c DbgArgPointer : Uint4B+0x010 TempSegCs : Uint2B+0x012 Logging : UChar+0x013 Reserved : UChar+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這個結構體的最后四個成員只有在虛擬8086模式下才會用到,保護模式下不用。
當API通過中斷門進入到零環之前,ESP0指向+0x078 HardwareSegSs的這個位置。
接著,中斷門提權后會將SS ESP EFlags CS和EIP壓入堆棧。此時,ESP0指向+0x064 ErrCode的位置。
到這里,我們就知道了KiSystemService函數前幾行匯編代碼的含義:
.text:00465651 push 0 ; 保存ErrCode到esp0 .text:00465653 push ebp ; 保存ebp到esp0 .text:00465654 push ebx ; 保存ebx到esp0 .text:00465655 push esi ; 保存esi到esp0 .text:00465656 push edi ; 保存edi到esp0 .text:00465657 push fs ; 保存fs到esp0KRPC
接下來繼續分析后面的代碼
.text:00465659 mov ebx, 30h .text:0046565E mov fs, bx這里將0x30賦值給FS寄存器,通過拆分0x30段選擇子可以得到GDT表下標為6的段描述符:FFC093DF`F0000001。這個段描述符的基址為FFDFF0000,指向當前的KPCR結構體。
KPCR結構體如下所示:
kd> dt _KPCR nt!_KPCR+0x000 NtTib : _NT_TIB+0x000 Used_ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD+0x004 Used_StackBase : Ptr32 Void+0x008 Spare2 : Ptr32 Void+0x00c TssCopy : Ptr32 Void+0x010 ContextSwitches : Uint4B+0x014 SetMemberCopy : Uint4B+0x018 Used_Self : Ptr32 Void+0x01c SelfPcr : Ptr32 _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 SpareUnused : 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 : _KPRCBKPCR叫CPU控制區(Kernel Processor Control Region),每個CPU有一個。如果想查看當前的CPU數量,可以用下面這條指令。
kd> dd KeNumberProcessors 83fb096c 00000001 83f3cf33 00000001 00000001 83fb097c 00000000 00000000 00000020 1fc10000 83fb098c 00110006 00003c03 776c7058 776c6f58 83fb099c 776c6fc0 776c7008 776b5a8f 776b5a8d 83fb09ac 776b5a64 00000000 00ce6126 842095b0 83fb09bc 841734f2 83ec4d9c 00000000 00000191 83fb09cc 83ec53e4 00000000 00000000 00000000 83fb09dc 00000000 83f236af 00000000 026b2372我當前的虛擬機只有一個核,所以數量是1。用下面這條指令可以查看每個核對應的KPCR分別是什么
kd> dd KiProcessorBlock l2 8055a320 ffdff120 00000000這個地址顯示的是ffdff120,也就是KPCR偏移0x120的位置。KPCR偏移0x120的位置是 _KPRCB,可以理解為擴展的KPCR。
所以下面這兩句代碼執行完之后,FS就指向KPCR
.text:00465659 mov ebx, 30h .text:0046565E mov fs, bx ; 使FS指向KPCRExceptionList
接下來繼續向下分析
.text:00465661 push dword ptr ds:0FFDFF000h .text:00465667 mov dword ptr ds:0FFDFF000h, 0FFFFFFFFh這里將0FFDFF000h壓入堆棧,也就是KPCR的結構體的第一個成員
+0x000 NtTib : _NT_TIB這個NtTib是個子結構體,這個子結構體很大
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這個結構體的第一個成員是ExceptionList
.text:00465661 push dword ptr ds:0FFDFF000h ; 保存老的ExceptionList到esp0 .text:00465667 mov dword ptr ds:0FFDFF000h, 0FFFFFFFFh ; 將新的ExceptionList賦值為-1這兩行代碼首先保存老的ExceptionList,并將新的ExceptionList賦值為-1,。繼續往下分析
.text:00465671 mov esi, ds:0FFDFF124h_KTHREAD結構體
這里將0FFDFF124h保存到esi。KPCR偏移0x120的位置是_KPRCB
+0x120 PrcbData : _KPRCB繼續查看一下_KPRCB結構體0x4的位置
kd> dt _KPRCB nt!_KPRCB+0x000 MinorVersion : Uint2B+0x002 MajorVersion : Uint2B+0x004 CurrentThread : Ptr32 _KTHREADCurrentThread是當前CPU所執行線程的_ETHREAD結構體,繼續查看一下_ETHREAD結構體。
kd> dt _ETHREAD nt!_ETHREAD+0x000 Tcb : _KTHREAD_ETHREAD結構體的第一個成員是_KTHREAD,所以下面這行代碼的具體含義是將當前線程的_KTHREAD結構體保存到esi
.text:00465671 mov esi, ds:0FFDFF124h ; 將當前線程的_KTHREAD結構體保存到esi繼續往下分析
.text:00465677 push dword ptr [esi+140h]這里將esi+140壓入堆棧,esi指向_KTHREAD結構體,查看一下_KTHREAD0x140的位置
kd> dt _KTHREAD nt!_KTHREAD+0x140 PreviousMode先前模式
0x140的位置保存的是先前模式,先前模式的作用就是記錄當前調用的這段代碼之前是被零環調用還是被三環調用
.text:00465677 push dword ptr [esi+140h] ; 保存老的先前模式到esp0抬高堆棧
繼續往下分析
.text:0046567D sub esp, 48h回顧一下當前的堆棧情況
kd> dt _KTRAP_FRAME nt!_KTRAP_FRAME+0x000 DbgEbp : Uint4B+0x004 DbgEip : Uint4B+0x008 DbgArgMark : Uint4B+0x00c DbgArgPointer : Uint4B+0x010 TempSegCs : Uint2B+0x012 Logging : UChar+0x013 Reserved : UChar+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通過前面的代碼分析我們可以知道當前零環的堆棧已經壓入了下面這些值
+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而除去已經壓入堆棧的值,這個結構體正好剩下0x48個字節,sub,esp 0x48將當前的esp0指向_KTRAP_FRAME0x00的位置
.text:0046567D sub esp, 48h ; 將esp0指向_KTRAP_FRAME頭判斷當前權限
繼續往下分析
.text:00465680 mov ebx, [esp+68h+arg_0] .text:00465684 and ebx, 1這里將esp+0x68+4的值保存到ebx,當前的esp指向``_KTRAP_FRAME`頭部,esp+6C的位置就是CS
+0x06c SegCs : Uint4B取出CS之后,和1做與運算。
如果是3環的CS段選擇子,那么最后兩個二進制位是11。11和1進行與運算結果還是1。
如果是0環的CS段選擇子,那么最后兩個二進制位是00。00和1進行與運算結果還是00。
這里實際上是通過與運算的結果來判斷當前代碼是三環還是零環。
.text:00465687 mov [esi+140h], bl ; 賦值新的先前模式接下來將bl賦值給esi+140h的位置,也就是之前分析過的先前模式
.text:0046568D mov ebp, esp ; ebp=esp=_KTRAP_FRAME接下來上面這行代碼執行完成之后ebp和esp都指向_KTRAP_FRAME結構體,繼續往下分析
更新_KTRAP_FRAME
.text:0046568F mov ebx, [esi+134h]這里將esi+0x134的位置保存到ebx,esi指向的是_ETHREAD,查看一下 +0x134的位置保存的內容
+0x134 TrapFrame : Ptr32 _KTRAP_FRAME+0x134的位置保存的_KTRAP_FRAME結構體指針,_KTRAP_FRAME這個結構體以線程為單位保存在_ETHREAD結構體里面,每個線程都有一份。
.text:0046568F mov ebx, [esi+134h] ; 保存_KTRAP_FRAME結構體到ebx .text:00465695 mov [ebp+3Ch], ebx ; 將KTHREAD中的_KTRAP_FRAME保存到[ebp+0x3C]所以這一句的含義實際上是先將_KTRAP_FRAME結構體保存到ebx,然后再存到[ebp+0x3C]的位置。繼續往下分析
.text:00465698 mov [esi+134h], ebp ; 更新當前線程的_KTRAP_FRAME這里將ebp保存到[esi+134h],此時ebp指向_KTRAP_FRAME頭部位置,而[esi+134h]也是_KTRAP_FRAME頭,所以這行代碼執行完,就會更新當前線程的_KTRAP_FRAME結構體。繼續往下分析
保存三環的寄存器環境
.text:0046569F mov ebx, [ebp+60h] .text:004656A2 mov edi, [ebp+68h]當前的ebp=esp指向了_KTRAP_FRAME頭的位置,
kd> dt _KTRAP_FRAME nt!_KTRAP_FRAME+0x060 Ebp : Uint4B+0x064 ErrCode : Uint4B+0x068 Eip : Uint4B[ebp+0x60]的位置是三環的ebp,[ebp+0x68]的位置是三環的eip,
.text:0046569F mov ebx, [ebp+60h] ; 取出三環的ebp放到ebx .text:004656A2 mov edi, [ebp+68h] ; 取出三環的eip放到edi .text:004656A5 mov [ebp+0Ch], edx .text:004656A8 mov dword ptr [ebp+8], 0BADB0D00h .text:004656AF mov [ebp+0], ebx ; 將三環的ebp備份到_KTRAP_FRAME DbgEbp的位置 .text:004656B2 mov [ebp+4], edi ; 將三環的eip備份到_KTRAP_FRAME DbgEip的位置 .text:004656B5 test byte ptr [esi+2Ch], 0FFh這里先將三環的ebp和eip保存到寄存器,然后再備份到_KTRAP_FRAME結構體DbgEbp和DbgEip的位置。然后繼續看中間兩行代碼
.text:004656A5 mov [ebp+0Ch], edx .text:004656A8 mov dword ptr [ebp+8], 0BADB0D00h這里將edx存到ebp+0xC的位置
kd> dt _KTRAP_FRAME nt!_KTRAP_FRAME+0x000 DbgEbp : Uint4B+0x004 DbgEip : Uint4B+0x008 DbgArgMark : Uint4B+0x00c DbgArgPointer : Uint4Bebp+0xC的位置是DbgArgPointer,那么這個edx是什么呢?這里要回顧之前學習的內容。三環進零環的兩種方式,不管是哪一種,都會用到eax和edx兩個寄存器,其中eax保存的是服務號,而edx存儲的是三環的參數開始的位置。
.text:004656A5 mov [ebp+0Ch], edx ; 將三環的參數指針存到DbgArgPointer .text:004656A8 mov dword ptr [ebp+8], 0BADB0D00h ; 將DbgArgMark賦值為0BADB0D00判斷調試狀態
那么這行匯編代碼的作用就是將三環的參數指針存到DbgArgPointer,繼續往下分析
.text:004656B5 test byte ptr [esi+2Ch], 0FFh .text:004656B9 jnz Dr_kss_a當前的esi指向KTHRAD結構體, [esi+2Ch]是位置是DebugActive,這個字段是調試狀態,如果當前的線程處于調試狀態,那么這里面的值不為零
+0x02C DebugActive這里將DebugActive和FF做與運算,根據運算的結果決定是否跳轉,那么這兩句匯編的含義就很明顯了
.text:004656B5 test byte ptr [esi+2Ch], 0FFh ; 判斷KTHREAD結構體的DebugActive是否為零 .text:004656B9 jnz Dr_kss_a ; 如果處于調試狀態 跳轉到此,整個KiSystemService函數保存現場的部分就已經完成了。
FFh
.text:004656B9 jnz Dr_kss_a
這里將DebugActive和FF做與運算,根據運算的結果決定是否跳轉,那么這兩句匯編的含義就很明顯了
.text:004656B5 test byte ptr [esi+2Ch], 0FFh ; 判斷KTHREAD結構體的DebugActive是否為零 .text:004656B9 jnz Dr_kss_a ; 如果處于調試狀態 跳轉到此,整個KiSystemService函數保存現場的部分就已經完成了。
總結
以上是生活随笔為你收集整理的系统调用002 KiSystemService函数逆向分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 系统调用001 API从三环进零环的过程
- 下一篇: 系统调用003 系统服务表