系統調用002 KiSystemService函數逆向分析

前言

之前我們詳細瞭解了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到esp0

KRPC

接下來繼續分析後面的代碼

.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         : _KPRCB

KPCR叫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指向KPCR

ExceptionList

接下來繼續向下分析

.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 _KTHREAD

CurrentThread是當前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    : Uint4B

ebp+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


當前的esi指向KTHRAD結構體, [esi+2Ch]是位置是DebugActive,這個字段是調試狀態,如果當前的線程處於調試狀態,那麼這裏面的值不爲零

```assembly
+0x02C DebugActive

這裏將DebugActive和FF做與運算,根據運算的結果決定是否跳轉,那麼這兩句彙編的含義就很明顯了

.text:004656B5                 test    byte ptr [esi+2Ch], 0FFh ; 判斷KTHREAD結構體的DebugActive是否爲零
.text:004656B9                 jnz     Dr_kss_a        ; 如果處於調試狀態 跳轉

到此,整個KiSystemService函數保存現場的部分就已經完成了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章