Windows 系統呼叫

ylc0x01發表於2024-09-26

目錄
  • xp _NtReadFile
    • 0xFFE0300h
  • 系統呼叫
    • xp _KiIntSystemCall
      • int 2Eh
    • xp _KiSystemService 原始碼asm
      • ENTER_SYSCALL macro
      • EXIT_ALL macro
      • xp _KiSystemService 原始碼asm
      • SSDT結構
    • xp _KiSystemService 反編譯asm
  • 快速系統呼叫
    • 入口方式不同 xp _KiFastSystemCall 反編譯ASM
    • 使用者空間資訊儲存不同 xp _KiFastCallEntry 原始碼asm
  • 執行流程
    • 系統呼叫
    • 快速系統呼叫

系統呼叫就是作業系統為R3和R0(或R0與R0(僅限windows))提供函式呼叫的一種機制

xp _NtReadFile

ntdll.dll中追蹤NTSTATUS __stdcall NtReadFile(x,x,x,x,x,x,x,x,x)函式如下

.text:77F062B8 ; NTSTATUS __stdcall NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key)
.text:77F062B8                 public _NtReadFile@36
.text:77F062B8 _NtReadFile@36  proc near               ; CODE XREF: RtlGetSetBootStatusData(x,x,x,x,x,x)+72↑p
.text:77F062B8                                         ; RtlGetSetBootStatusData(x,x,x,x,x,x):loc_77F4E893↓p ...
.text:77F062B8
.text:77F062B8 FileHandle      = dword ptr  4
.text:77F062B8 Event           = dword ptr  8
.text:77F062B8 ApcRoutine      = dword ptr  0Ch
.text:77F062B8 ApcContext      = dword ptr  10h
.text:77F062B8 IoStatusBlock   = dword ptr  14h
.text:77F062B8 Buffer          = dword ptr  18h
.text:77F062B8 Length          = dword ptr  1Ch
.text:77F062B8 ByteOffset      = dword ptr  20h
.text:77F062B8 Key             = dword ptr  24h
.text:77F062B8
.text:77F062B8                 mov     eax, 111h       	; NtReadFile系統呼叫號
.text:77F062BD                 mov     edx, 7FFE0300h	;系統呼叫入口
.text:77F062C2                 call    dword ptr [edx]	;執行系統呼叫
.text:77F062C4                 retn    24h				  ;堆疊平衡
.text:77F062C4 _NtReadFile@36  endp
.text:77F062C4
.text:77F062C4 ; ---------------------------------------------------------------------------
  • 111為預定義的核心函式系統呼叫號,實際為SSDT(SSDT擴充套件)表索引
  • 0xFFE0300h為系統呼叫入口

0xFFE0300h

0x7ffe0000(低2GB)與0xffdf0000(高2GB)為預定義資料共享段,內容完全相同,windbg如下:

kd> dd 7ffe0000
7ffe0000  00000000 0f99a027 32c8a556 00000677
7ffe0010  00000677 5d00846f 01db0fc0 01db0fc0
7ffe0020  f1dcc000 ffffffbc ffffffbc 014c014c
7ffe0030  003a0043 0057005c 006e0069 006f0064
7ffe0040  00730077 00000000 00000000 00000000
7ffe0050  00000000 00000000 00000000 00000000
7ffe0060  00000000 00000000 00000000 00000000
7ffe0070  00000000 00000000 00000000 00000000
kd> dd ffdf0000
ffdf0000  00000000 0f99a027 32c8a556 00000677
ffdf0010  00000677 5d00846f 01db0fc0 01db0fc0
ffdf0020  f1dcc000 ffffffbc ffffffbc 014c014c
ffdf0030  003a0043 0057005c 006e0069 006f0064
ffdf0040  00730077 00000000 00000000 00000000
ffdf0050  00000000 00000000 00000000 00000000
ffdf0060  00000000 00000000 00000000 00000000
ffdf0070  00000000 00000000 00000000 00000000

共享資料段定義為_KUSER_SHARED_DATA結構體,windbg如下:

kd> dt _KUSER_SHARED_DATA
nt!_KUSER_SHARED_DATA
   +0x000 TickCountLowDeprecated : Uint4B
   +0x004 TickCountMultiplier : Uint4B
   +0x008 InterruptTime    : _KSYSTEM_TIME
   +0x014 SystemTime       : _KSYSTEM_TIME
   +0x020 TimeZoneBias     : _KSYSTEM_TIME
   +0x02c ImageNumberLow   : Uint2B
   +0x02e ImageNumberHigh  : Uint2B
   +0x030 NtSystemRoot     : [260] Wchar
   +0x238 MaxStackTraceDepth : Uint4B
   +0x23c CryptoExponent   : Uint4B
   +0x240 TimeZoneId       : Uint4B
   +0x244 LargePageMinimum : Uint4B
   +0x248 Reserved2        : [7] Uint4B
   +0x264 NtProductType    : _NT_PRODUCT_TYPE
   +0x268 ProductTypeIsValid : UChar
   +0x26c NtMajorVersion   : Uint4B
   +0x270 NtMinorVersion   : Uint4B
   +0x274 ProcessorFeatures : [64] UChar
   +0x2b4 Reserved1        : Uint4B
   +0x2b8 Reserved3        : Uint4B
   +0x2bc TimeSlip         : Uint4B
   +0x2c0 AlternativeArchitecture : _ALTERNATIVE_ARCHITECTURE_TYPE
   +0x2c4 AltArchitecturePad : [1] Uint4B
   +0x2c8 SystemExpirationDate : _LARGE_INTEGER
   +0x2d0 SuiteMask        : Uint4B
   +0x2d4 KdDebuggerEnabled : UChar
   +0x2d5 NXSupportPolicy  : UChar
   +0x2d8 ActiveConsoleId  : Uint4B
   +0x2dc DismountCount    : Uint4B
   +0x2e0 ComPlusPackage   : Uint4B
   +0x2e4 LastSystemRITEventTickCount : Uint4B
   +0x2e8 NumberOfPhysicalPages : Uint4B
   +0x2ec SafeBootMode     : UChar
   +0x2ed TscQpcData       : UChar
   +0x2ed TscQpcEnabled    : Pos 0, 1 Bit
   +0x2ed TscQpcSpareFlag  : Pos 1, 1 Bit
   +0x2ed TscQpcShift      : Pos 2, 6 Bits
   +0x2ee TscQpcPad        : [2] UChar
   +0x2f0 SharedDataFlags  : Uint4B
   +0x2f0 DbgErrorPortPresent : Pos 0, 1 Bit
   +0x2f0 DbgElevationEnabled : Pos 1, 1 Bit
   +0x2f0 DbgVirtEnabled   : Pos 2, 1 Bit
   +0x2f0 DbgInstallerDetectEnabled : Pos 3, 1 Bit
   +0x2f0 DbgSystemDllRelocated : Pos 4, 1 Bit
   +0x2f0 DbgDynProcessorEnabled : Pos 5, 1 Bit
   +0x2f0 DbgSEHValidationEnabled : Pos 6, 1 Bit
   +0x2f0 SpareBits        : Pos 7, 25 Bits
   +0x2f4 DataFlagsPad     : [1] Uint4B
   +0x2f8 TestRetInstruction : Uint8B
   +0x300 SystemCall       : Uint4B			// 系統呼叫
   +0x304 SystemCallReturn : Uint4B
   +0x308 SystemCallPad    : [3] Uint8B
   +0x320 TickCount        : _KSYSTEM_TIME
   +0x320 TickCountQuad    : Uint8B
   +0x320 ReservedTickCountOverlay : [3] Uint4B
   +0x32c TickCountPad     : [1] Uint4B
   +0x330 Cookie           : Uint4B
   +0x334 CookiePad        : [1] Uint4B
   +0x338 ConsoleSessionForegroundProcessId : Int8B
   +0x340 Wow64SharedInformation : [16] Uint4B
   +0x380 UserModeGlobalLogger : [16] Uint2B
   +0x3a0 ImageFileExecutionOptions : Uint4B
   +0x3a4 LangGenerationCount : Uint4B
   +0x3a8 Reserved5        : Uint8B
   +0x3b0 InterruptTimeBias : Uint8B
   +0x3b8 TscQpcBias       : Uint8B
   +0x3c0 ActiveProcessorCount : Uint4B
   +0x3c4 ActiveGroupCount : Uint2B
   +0x3c6 Reserved4        : Uint2B
   +0x3c8 AitSamplingValue : Uint4B
   +0x3cc AppCompatFlag    : Uint4B
   +0x3d0 SystemDllNativeRelocation : Uint8B
   +0x3d8 SystemDllWowRelocation : Uint4B
   +0x3dc XStatePad        : [1] Uint4B
   +0x3e0 XState           : _XSTATE_CONFIGURATION

其中0x300偏移處儲存系統呼叫入口,windbg如下:

kd> dd 7ffe0300
7ffe0300  778b70b0 778b70b4 00000000 00000000
7ffe0310  00000000 00000000 00000000 00000000
7ffe0320  02b7593f 00000000 00000000 00000000
7ffe0330  af2059b8 00000000 0000052c 00000000
7ffe0340  00000000 00000000 00000000 00000000
7ffe0350  00000000 00000000 00000000 00000000
7ffe0360  00000000 00000000 00000000 00000000
7ffe0370  00000000 00000000 00000000 00000000

0x7ffe0300處值為778b70b0,解析後得出為快速系統呼叫函式地址,因為此處解析需要涉及分頁機制,所以沒有展示解析過程,下面先解析系統呼叫過程而後再解析快速系統呼叫過程

系統呼叫

xp _KiIntSystemCall

反編譯 xp C:\Windows\System32\ntdll.dll _KiIntSystemCall函式如下

.text:77F070C0 ; Exported entry 108. KiIntSystemCall
.text:77F070C0
.text:77F070C0 ; =============== S U B R O U T I N E =======================================
.text:77F070C0
.text:77F070C0
.text:77F070C0 ; _DWORD __stdcall KiIntSystemCall()
.text:77F070C0                 public _KiIntSystemCall@0
.text:77F070C0 _KiIntSystemCall@0 proc near            ; DATA XREF: .text:off_77EF61B8↑o
.text:77F070C0
.text:77F070C0 arg_4           = byte ptr  8
.text:77F070C0
.text:77F070C0                 lea     edx, [esp+arg_4]
.text:77F070C4                 int     2Eh             ; DOS 2+ internal - EXECUTE COMMAND
.text:77F070C4                                         ; DS:SI -> counted CR-terminated command string
.text:77F070C6                 retn
.text:77F070C6 _KiIntSystemCall@0 endp
.text:77F070C6
.text:77F070C6 ; ---------------------------------------------------------------------------
.text:77F070C7                 align 4
.text:77F070C8 ; Exported entry 1103. RtlRaiseException
.text:77F070C8

  • ESP+8:esp棧頂指標,壓入第一個引數的地址。
    • lea:esp+arg_4的地址放入到edx
    • _NtReadFile_KiIntSystemCall都為__stdcall 呼叫約定,傳參方式為從右向左的順序傳遞引數,因為棧是從高地址往低地址增長,+4是EIP,+8是壓入堆疊的第一個引數

int 2Eh

自陷指令,通用系統服務入口點,int 2eh代表呼叫中斷向量表中第47箇中斷:83e7ee00 0008ffee,解析後函式地址為:83e7ffee。windbg如下:

kd> !pcr
KPCR for Processor 0 at 83f6dc00:
    Major 1 Minor 1
	NtTib.ExceptionList: 83f6a0ac
	    NtTib.StackBase: 00000000
	   NtTib.StackLimit: 00000000
	 NtTib.SubSystemTib: 801e4000
	      NtTib.Version: 4cf55ad2
	  NtTib.UserPointer: 00000001
	      NtTib.SelfTib: 00000000

	            SelfPcr: 83f6dc00
	               Prcb: 83f6dd20
	               Irql: 0000001f
	                IRR: 00000000
	                IDR: ffffffff
	      InterruptMode: 00000000
	                IDT: 80b99400 	// 中斷向量表
	                GDT: 80b99000
	                TSS: 801e4000

	      CurrentThread: 83f77380
	         NextThread: 00000000
	         IdleThread: 83f77380

	          DpcQueue: 
kd> dq 80b99400 l50
80b99400  83e88e00`00080fc0 83e88e00`00081150
80b99410  00008500`00580000 83e8ee00`000815c0
80b99420  83e8ee00`00081748 83e88e00`000818a8
80b99430  83e88e00`00081a1c 83e88e00`00082018
80b99440  00008500`00500000 83e88e00`00082478
80b99450  83e88e00`0008259c 83e88e00`000826dc
80b99460  83e88e00`0008293c 83e88e00`00082c2c
80b99470  83e88e00`000832fc 83e88e00`000836b0
80b99480  83e88e00`000837d4 83e88e00`00083914
80b99490  00008500`00a00000 83e88e00`00083a80
80b994a0  83e88e00`000836b0 83e88e00`000836b0
80b994b0  83e88e00`000836b0 83e88e00`000836b0
80b994c0  83e88e00`000836b0 83e88e00`000836b0
80b994d0  83e88e00`000836b0 83e88e00`000836b0
80b994e0  83e88e00`000836b0 83e88e00`000836b0
80b994f0  83e88e00`000836b0 83e28e00`00085af8
80b99500  00000000`00080000 00000000`00080000
80b99510  00000000`00080000 00000000`00080000
80b99520  00000000`00080000 00000000`00080000
80b99530  00000000`00080000 00000000`00080000
80b99540  00000000`00080000 00000000`00080000
80b99550  83e8ee00`0008063a 83e8ee00`000807c0
80b99560  83e8ee00`000808fc 83e8ee00`00081498
80b99570  83e7ee00`0008ffee 83e88e00`000836b0
kd> u 83e7ffee
nt!KiSystemService:
83e7ffee 6a00            push    0
83e7fff0 55              push    ebp
83e7fff1 53              push    ebx
83e7fff2 56              push    esi
83e7fff3 57              push    edi
83e7fff4 0fa0            push    fs
83e7fff6 bb30000000      mov     ebx,30h
83e7fffb 668ee3          mov     fs,bx    

下面分析_KiSystemService函式

xp _KiSystemService 原始碼asm

ENTER_SYSCALL macro

NT\base\ntos\ke\i386\kimacro.inc

ENTER_SYSCALL macro     AssistLabel, TargetLabel, NoFSLoad

.FPO ( FPO_LOCALS, FPO_PARAMS, FPO_PROLOG, FPO_REGS, FPO_USE_EBP, FPO_TRAPFRAME )

ifdef KERNELONLY

;
; 構造陷阱幀 (Trap Frame)。
;
; 注意:陷阱幀的初始部分是透過在堆疊上推送值來構造的。
;       如果陷阱幀的格式發生更改,則下面的程式碼也必須進行更改。
;

        push    0                       ; 在堆疊上放置錯誤佔位符
        push    ebp                     ; 儲存不可變暫存器
        push    ebx                     ;
        push    esi                     ;
        push    edi                     ;

ifb <NoFSLoad>
        push    fs                      ; 儲存FS並將FS設定為PCR
        mov     ebx,KGDT_R0_PCR         ; 設定PCR段號
        mov     fs,bx                   ;
else
        ; FS已經包含KGDT_R0_PCR(透過PentiumPro快速系統呼叫進入)
        push    KGDT_R3_TEB OR RPL_MASK ;
endif  ; NoFSLoad

;
; 在陷阱幀中儲存舊的異常連結串列並初始化一個新的空異常連結串列。
;

        push    PCR[PcExceptionList]    ; 儲存舊的異常連結串列
        mov     PCR[PcExceptionList],EXCEPTION_CHAIN_END ; 設定新的空連結串列

;
; 在陷阱幀中儲存舊的previous mode(以前的模式),
; 分配陷阱幀的其餘部分,並設定新的previous mode。
;

        mov     esi,PCR[PcPrcbData+PbCurrentThread] ; 獲取當前執行緒地址
        push    [esi]+ThPreviousMode    ; 儲存舊的previous mode
        sub     esp,TsPreviousPreviousMode ; 分配陷阱幀的其餘部分
        mov     ebx,[esp+TsSegCS]       ; 計算新的previous mode
        and     ebx,MODE_MASK           ;
        mov     [esi]+ThPreviousMode,bl ; 設定新的previous mode

;
; 儲存舊的陷阱幀地址並設定新的陷阱幀地址。
;

        mov     ebp,esp                 ; 設定陷阱幀地址
        mov     ebx,[esi].ThTrapFrame   ; 儲存當前陷阱幀地址
        mov     [ebp].TsEdx,ebx         ;
        mov     [esi].ThTrapFrame,ebp   ; 設定新的陷阱幀地址
        cld                             ; 確保方向為前向

        SET_DEBUG_DATA                  ; 注意:這將銷燬edi

        test    byte ptr [esi]+ThDebugActive,-1 ; 檢查除錯是否啟用
        jnz     Dr_&AssistLabel         ; 如果不為零,執行緒上啟用除錯

Dr_&TargetLabel:                        ;
        sti                             ; 啟用中斷

else
        %out    ENTER_SYSCAL outside of kernel
        .err
endif
        endm

ENTER_SYSCALL宏用於進入系統呼叫,填充系統呼叫框架,為後續真正的系統呼叫儲存現場提供資訊

  1. 儲存暫存器狀態:儲存重要的暫存器(如ebp, ebx, esi, edi等),這些暫存器的值在陷阱幀中會被恢復。
  2. 處理FS段暫存器:條件地儲存FS暫存器並設定為PCR(Processor Control Region)段。
  3. 處理異常連結串列:儲存當前異常連結串列並初始化一個新的空連結串列,用於異常處理。
  4. 處理previous mode(先前模式):儲存並設定新的previous mode,用於區分核心模式和使用者模式。
  5. 設定陷阱幀:儲存當前陷阱幀地址並將新的陷阱幀地址儲存在當前執行緒控制塊中(TSS)。
  6. 處理除錯資訊:如果偵錯程式處於活動狀態,會跳轉到處理偵錯程式的程式碼路徑。
  7. 啟用中斷:最後啟用中斷,使得系統呼叫可以處理中斷。執行系統呼叫時不用發生執行緒切換

EXIT_ALL macro

NT\base\ntos\ke\i386\kimacro.inc

EXIT_ALL macro  NoRestoreSegs, NoRestoreVolatiles, NoPreviousMode
local   a, b, f, x
local   Dr_ExitHelp, Dr_ExitHelp_Target
local   Db_NotATrapFrame, Db_A, Db_NotValidEntry, NonFlatPm_Target

;
; 檢查某些值並設定宏的全域性變數
;

?adjesp = TsSegGs
?RestoreAll = 1

ifnb <NoRestoreSegs>
    ; 如果不恢復段暫存器,不恢復所有暫存器
    ?RestoreAll = 0
    ?adjesp = ?adjesp + 12
endif

ifnb <NoRestoreVolatiles>
    if ?RestoreAll eq 1
        %out "EXIT_ALL NoRestoreVolatiles requires NoRestoreSegs"
        .err
    endif
    ?adjesp = ?adjesp + 12
endif

ifb <NoPreviousMode>
ifndef KERNELONLY
        %out    EXIT_ALL 不能在核心外恢復先前模式
        .err
endif
endif

; 所有呼叫者都必須確保在中斷禁用的情況下到達此處。

if DBG
        pushfd
        pop     edx

        test    edx, EFLAGS_INTERRUPT_MASK  ; 檢查中斷標誌
        jnz     Db_NotValidEntry            ; 如果中斷未禁用,跳轉到無效入口處理

        cmp     esp, ebp                    ; 確保 esp 等於 ebp
        jne     Db_NotValidEntry

; 確保 BADB0D00 標記存在。如果沒有,這不是一個陷阱幀!
Db_A:   sub     [esp]+TsDbgArgMark,0BADB0D00h
        jne     Db_NotATrapFrame
endif

        ASSERT_FS  ; 確保 FS 暫存器有效

        mov     edx, [esp]+TsExceptionList  ; 讀取異常連結串列
if DBG
        or      edx, edx
        jnz     short @f
    int 3
@@:
endif
        mov     ebx, fs:[PcDebugActive]     ; (ebx) = DebugActive 標誌
        mov     fs:[PcExceptionList], edx   ; 恢復異常連結串列

ifb <NoPreviousMode>
        mov     ecx, [esp]+TsPreviousPreviousMode ; 恢復先前模式
if DBG
        cmp     ecx, -1     ; 臨時程式碼,確保沒有恢復不正確的模式
        jne     @f          ; 確保沒有人彈出 ThPreviousMode
    int 3
@@:
endif
        mov     esi,fs:[PcPrcbData+PbCurrentThread]
        mov     [esi]+ThPreviousMode,cl
else
if DBG
        mov     ecx, [esp]+TsPreviousPreviousMode
        cmp     ecx, -1     ; 臨時程式碼,確保沒有推送 ThPreviousMode 並
        je     @f           ; 現在退出時不恢復它
    int 3                   ; 如果沒有正確恢復,觸發斷點
@@:
endif
endif

        test    ebx, 0fh
        jnz     Dr_ExitHelp

Dr_ExitHelp_Target:

        test    dword ptr [esp].TsEflags,EFLAGS_V86_MASK
        jnz     V86ExitHelp                ; 如果在虛擬8086模式下,跳轉到處理程式碼

        test    word ptr [esp]+TsSegCs,FRAME_EDITED
        jz      b                          ; 如果幀被編輯,跳轉到相應處理。

if ?RestoreAll eq 0
.errnz MODE_MASK-1
        cmp     word ptr [esp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK ; 設定/清除 ZF
        bt      word ptr [esp]+TsSegCs,0    ; 測試 MODE_MASK,設定/清除 CF
        cmc                                 ; 反轉 CF 標誌
        ja      f                           ; 如果 CF=0 且 ZF=0,跳轉
endif
ifb <NoRestoreVolatiles>
        mov     edx, [esp]+TsEdx            ; 恢復易失性暫存器
        mov     ecx, [esp]+TsEcx
                                            ; 在恢復段暫存器之前,必須先恢復 eax
        mov     eax, [esp].TsEax            ; 參見 trap0e 處理程式
endif

        cmp     word ptr [ebp]+TsSegCs, KGDT_R0_CODE
        jz      short @f

ifb <NoRestoreSegs>
        lea     esp, [ebp]+TsSegGs
        pop     gs                          ; 恢復段暫存器
        pop     es
        pop     ds
endif
NonFlatPm_Target:
        lea     esp, [ebp]+TsSegFs
        pop     fs
@@:
        lea     esp, [ebp]+TsEdi            ; 跳過 PreMode, ExceptList 和 fs

        pop     edi                         ; 恢復不可變暫存器
        pop     esi
        pop     ebx
        pop     ebp

;
; Esp 必須指向堆疊上的錯誤碼。因為我們使用它來儲存進入的 esp。
;

        cmp     word ptr [esp+8], 80h       ; 檢查是否是 abios 程式碼段?
        ja      AbiosExitHelp

        add     esp, 4                      ; 從陷阱幀中移除錯誤碼

ifnb <NoRestoreVolatiles>

        public  _KiSystemCallExitBranch
        public  _KiSystemCallExit
        public  _KiSystemCallExit2
        public  _KiSystemCallExit3

; NoRestoreVolatiles 僅用於從系統服務返回。
; 如果返回到核心模式,則處理器狀態不需要更改(CS, CPL 保持不變),
; 因此可以簡單地展開核心幀並跳轉到儲存的 EIP。

        test    dword ptr [esp+4], MODE_MASK

; 如果以下分支被執行,我們將返回到使用者模式。
; 如果處理器支援 SYSEXIT 指令,分支將在引導時調整以使用適當的程式碼序列。

_KiSystemCallExitBranch:
        jnz     short _KiSystemCallExit

        ; 從系統呼叫返回到核心模式,比 IRETD 更快,
        ; 展開核心幀並跳轉到返回地址。

        pop     edx                         ; 獲取 eip
        pop     ecx                         ; 從堆疊中移除 CS
        popfd                               ; 恢復 eflags
        jmp     edx

if 0

        ; 有一天我們應該測試看看以下程式碼是否比上面的更快
        ; 並且仍然有效。

        sti                                 ; 重新啟用中斷
        ret     8                           ; 返回到 @esp 並彈出 CS 和 EFLAGs
endif


_KiSystemCallExit:

        iretd                               ; 返回

_KiSystemCallExit2:

        pop     edx                         ; 彈出 EIP
        add     esp, 8                      ; 移除 CS 和 EFLAGS
        pop     ecx                         ; 彈出 ESP

        sti                                 ; SYSEXIT 不重新載入標誌

        iSYSEXIT

_KiSystemCallExit3:

        ; AMD

        pop     ecx                         ; 彈出 EIP
        add     esp, 8
        pop     esp
;        mov     esp, [esp+8]               ; 移除 CS 和 EFLAGS,獲取 ESP

        iSYSRET

endif  ;; <NoRestoreVolatiles>

        iretd                               ; 返回

if DBG
Db_NotATrapFrame:
        add     [esp]+TsDbgArgMark,0BADB0D00h   ; 還原原始值
Db_NotValidEntry:
        int 3
        jmp     Db_A
endif

;
;   EXIT_HELPER
;
;       if (PreviousMode == UserMode) {
;           DR* 暫存器 = TF.Dr* 暫存器
;       }
;
;   入口條件:
;
;       DebugActive == TRUE
;       (ebp)->TrapFrame
;
;--

align dword
Dr_ExitHelp:

        test    dword ptr [ebp]+TsEFlags,EFLAGS_V86_MASK
        jnz     short x

        test    dword ptr [ebp]+TsSegCs,MODE_MASK
        jz      Dr_ExitHelp_Target

x:      mov     ebx,0
        mov     esi,[ebp]+TsDr0
        mov     edi,[ebp]+TsDr1
        mov     dr7,ebx
        mov     dr0,esi
        mov     ebx,[ebp]+TsDr2
        mov     dr1,edi
        mov     dr2,ebx
        mov     esi,[ebp]+TsDr3
        mov     edi,[ebp]+TsDr6
        mov     ebx,[ebp]+TsDr7
        mov     dr3,esi
        mov     dr6,edi
        mov     dr7,ebx

        jmp     Dr_ExitHelp_Target
endm

EXIT_ALL 宏主要用於恢復系統呼叫框架,退出系統呼叫或異常處理程式,並恢復現場。

  1. 引數
    • NoRestoreSegs:控制是否恢復段暫存器(gsfs)。
    • NoRestoreVolatiles:控制是否恢復通用暫存器(eaxecxedx)。
    • NoPreviousMode:控制是否恢復先前模式。
  2. 全域性變數設定
    • ?adjesp:初始化為TsSegGs,並根據條件調整。
    • ?RestoreAll:預設為1,表示恢復所有暫存器。如果NoRestoreSegsNoRestoreVolatiles被指定,則調整恢復行為。
  3. 入口檢查
    • 檢查中斷標誌和堆疊指標,確保它們符合預期,以保證正確的陷阱幀格式。
  4. 除錯檢查
    • 檢查除錯資訊是否啟用。如果除錯啟用,會跳轉到對應的除錯處理程式碼。
  5. 恢復暫存器和處理器狀態
    • 根據宏引數條件,恢復段暫存器(fs, gs, es, ds)和通用暫存器(如eax, ecx, edx)。
    • 恢復先前模式,如果指定了NoPreviousMode,則不進行恢復。
    • 恢復異常連結串列(ExceptionList)和呼叫框架其他欄位。
  6. 處理特殊情況
    • 檢查是否是在虛擬8086模式下執行,並進行相應處理。
    • 針對特定模式(如平面記憶體模式)進行不同的恢復操作。
  7. 最終返回
    • 使用iretiretd指令將控制權返回給呼叫者,恢復執行流。

xp _KiSystemService 原始碼asm

\NT\base\ntos\ke\i386\trap.asm

;
; 通用系統服務入口點
;
        PUBLIC  _KiSystemService
_KiSystemService        proc

        ENTER_SYSCALL   kss_a, kss_t    ; 設定陷阱幀並儲存狀態

;
; (eax) = 服務號
; (edx) = 呼叫者的堆疊指標
; (esi) = 當前執行緒地址
;
; 所有其他暫存器已儲存並可用。
;
; 檢查服務號是否在有效範圍內
;
_KiSystemServiceRepeat:
        mov     edi, eax                ; 複製系統服務號
        shr     edi, SERVICE_TABLE_SHIFT ;隔離服務表號,(shr edi,8)整除256=0×100*0×10=0×1000
												;基本的SSDT表小於0x1000
                                         ;擴充套件的SSDT表大於0x1000
                                         ;透過SSDT表查詢3環API對應的核心函式,要判斷系統呼叫號對應的核心函式在哪張表
                                         ;方法就是將系統呼叫號右移8位
        and     edi, SERVICE_TABLE_MASK  ;再和掩碼0x0010做個與運算,就可得出在哪張表
        mov     ecx, edi                 ;ECX變成0×00(SSDT表)或0×10(擴充套件SSDT表呼叫號大於0×1000)
        add     edi, [esi]+ThServiceTable ;計算服務描述符地址
        										;esi指向當前執行緒結構
        										;TIKTHREAD_SERVICE_TABLE(_KTHREAD結構的ServiceTable欄位)
        										;EDI指向描述塊KeServiceDescriptorTable[0]或[1]
        										;KeServiceDescriptorTable是全域性變數
        										;0x10=16個位元組
								;edi=KeServiceDescriptorTable+0x10或者KeServiceDescriptorTable+0x00
        										;kd> dd KeServiceDescriptorTable
												 ;83fac9c0  83ec0d9c 00000000 00000191 83ec13e4 -> 基本SSDT表
												 ;83fac9d0  00000000 00000000 00000000 00000000 -> 擴充套件SSDT表
												 ;擴充套件SSDT表為什麼為空呢?因為擴充套件SSDT表不是全域性變數,要單獨去查
												 ;kd> dd KeServiceDescriptorTableShadow
												 		    ;地址表   計數器(當前執行緒引用次數) 函式個數  參數列(位元組數)
												 ;83faca00  83ec0d9c 00000000 00000191 83ec13e4
												 ;83faca10  95db6000 00000000 00000339 95db702c
												     										
        mov     ebx, eax                ; 儲存系統服務號
        and     eax, SERVICE_NUMBER_MASK  ; 隔離服務表偏移
        										;SERVICE_NUMBER_MASK定義為0XOFFF(取低12為Limit欄位)

;
; 如果指定的系統服務號不在範圍內,則嘗試將執行緒轉換為GUI執行緒並重試服務排程。
;

        cmp     eax, [edi]+SdLimit      ; 檢查是否有效服務
        									 ;檢查系統呼叫號是否越界
											  ;偏移SERVICE_DESCRIPTOR_LIMIT=8
        jae     Kss_ErrorHandler        ; 如果超出範圍,嘗試轉換為GUI執行緒

;
; 如果服務是GUI服務且GDI使用者批處理佇列不為空,
; 則呼叫適當的服務以重新整理使用者批處理。
;

        cmp     ecx, SERVICE_TABLE_TEST ; 檢查是否為GUI服務
        jne     short Kss40             ; 如果不相等,則不是GUI服務
        mov     ecx, PCR[PcTeb]         ; 獲取當前執行緒TEB地址
        xor     ebx, ebx                ; 獲取批處理GDI呼叫的數量

KiSystemServiceAccessTeb:
        or      ebx, [ecx]+TbGdiBatchCount ; 可能導致頁面異常

        jz      short Kss40             ; 如果為零,則沒有批處理呼叫
        push    edx                     ; 儲存使用者引數的地址
        push    eax                     ; 儲存服務號
        call    [_KeGdiFlushUserBatch]  ; 重新整理GDI使用者批處理
        pop     eax                     ; 恢復服務號
        pop     edx                     ; 恢復使用者引數的地址

;
; 引數透過堆疊傳遞。因此它們總是需要被複制,因為為機器狀態幀在堆疊上分配了額外的空間。
; 注意我們不檢查零引數的情況 - 複製總是進行,因為零引數的情況非常少見。
;

Kss40:  inc     dword ptr PCR[PcPrcbData+PbSystemCalls] ; 系統呼叫計數+1

;DBG不用看---------------------------------------------------------------------------
...
;DBG不用看---------------------------------------------------------------------------

        mov     esi, edx                ; (esi)->使用者引數 使ESI指向使用者空間堆疊上的引數塊
        mov     ebx, [edi]+SdNumber     ; 獲取參數列地址,引數個數(位元組為單位)EDI指向具體的系統呼叫表
        xor     ecx, ecx
        mov     cl, byte ptr [ebx+eax]  ; (ecx) = 引數大小,相應的陣列元素裝入暫存器ECX,eax為系統呼叫號,ebx為引數個數表
        mov     edi, [edi]+SdBase       ; 獲取服務表地址,EDI指向具體的系統呼叫表
        mov     ebx, [edi+eax*4]        ; (ebx)->服務例程,函式指標(eax=系統呼叫號)為下標,ebx為對應的核心函式
        sub     esp, ecx                ; 為引數在系統棧上分配空間,ECX為引數位元組數
        shr     ecx, 2                  ; (ecx) = 引數DWORD數量,右移2位,即除以4=引數的個數
        mov     edi, esp                ; (es:edi)->接收第一個引數的位置,目標在系統空間堆找上
        cmp     esi, _MmUserProbeAddress ; 檢查是否為使用者地址,引數塊的位置不得高於MmSystemRangeStart-0x10000
        jae     kss80                   ; 如果大於等於,則不是使用者地址

KiSystemServiceCopyArguments:
        rep     movsd                   ; 將引數複製到堆疊頂部。複製引數,以ESI為源、EDI為目標,ECX為迴圈次數
                                        ; 由於我們通常複製超過3個引數,
                                        ; rep movsd比mov指令更快。

;DBG不用看---------------------------------------------------------------------------
...
;DBG不用看---------------------------------------------------------------------------
;
; ================================== 實際呼叫系統服務 ================================
;
kssdoit:
;CAPSTARTX macro ArgList
;        push eax
;        stdCall __CAP_ThreadID
;        pop eax
;       stdCall __CAP_Start_Profiling, <ArgList>
;endm
;
;CAPENDX macro ArgList
;       stdCall __CAP_End_Profiling, <ArgList>
;        push eax
;        stdCall __CAP_SetCPU
;        pop eax
;endm
        CAPSTARTX <_KiSystemService,ebx>
        call    ebx                     ; 呼叫系統服務,呼叫目標函式---ebx=函式指標
        CAPENDX <_KiSystemService>
kss60:

;DBG不用看---------------------------------------------------------------------------
...
;DBG不用看---------------------------------------------------------------------------

kss61:

; ================================== 呼叫系統服務返回 ================================
; 返回時,(eax)= 狀態碼
;

        mov     esp, ebp                ; 釋放引數的堆疊空間,回到系統呼叫框架

;
; 從當前陷阱幀恢復舊的陷阱幀地址。
;

kss70:  mov     ecx, PCR[PcPrcbData+PbCurrentThread] ; 獲取當前執行緒地址,使ECX指向當前執行緒的KTHREAD
											  ; EDX儲存原系統呼叫框架指標,KTHREAD中儲存原系統呼叫框架
        mov     edx, [ebp].TsEdx        ; 恢復之前的陷阱幀地址,從堆疊中取出儲存著的框架指標
        mov     [ecx].ThTrapFrame, edx  ;恢復KTHREAD結構中的框架指標
        									 	;釋放當前系統呼叫框架,恢復前一個系統呼叫框架

;
;   系統服務的私有版本KiExceptionExit
;   (也用於KiDebugService)
;
;   檢查是否有待處理的APC中斷,如果找到,則排程它們
;   (先儲存eax在幀中)。
;
        public  _KiServiceExit
_KiServiceExit:

        cli                                         ; 禁用中斷,因為在系統呼叫時不允許執行緒切換
        DISPATCH_USER_APC   ebp, ReturnCurrentEax	;檢查是否有執行使用者空間“非同步過程呼叫”即APC請求,
        										;如果有則“遞交(Deliver)”請求,相當於由核心向使用者空間發出中斷請求。
        										;普通使用者執行緒優先順序為0,APC優先順序為1,dispatch執行緒優先順序為2
        										;在從R0 -> R3時會檢查是否有APC,有APC會先執行APC

;
; 從SystemService退出
;

        EXIT_ALL    NoRestoreSegs, NoRestoreVolatile ;退出宏,清理現場

;
; 引數列表的地址不是使用者地址。如果之前的模式是使用者,則返回訪問衝突作為系統服務的狀態。
; 否則,複製引數列表並執行系統服務。
;

kss80:  test    byte ptr [ebp].TsSegCs, MODE_MASK ; 測試之前的模式
        jz      KiSystemServiceCopyArguments ; 如果為零,之前的模式為核心
        mov     eax, STATUS_ACCESS_VIOLATION ; 設定服務狀態
        jmp     kss60                   ;

;++
;
;   _KiServiceExit2 - 與 _KiServiceExit 類似,但恢復完整的trap_frame(陷阱幀)上下文
;
;--
        public  _KiServiceExit2
_KiServiceExit2:

        cli                             ; 禁用中斷
        DISPATCH_USER_APC   ebp

;
; 從 SystemService 退出
;

        EXIT_ALL                            ; 退出宏


;DBG不用看---------------------------------------------------------------------------
...
;DBG不用看---------------------------------------------------------------------------

        ret

;
; 如果在16位模式下執行sysenter指令,生成錯誤而不是嘗試處理系統呼叫。
; 沒有辦法返回到使用者模式下的正確程式碼。
;

Kfsc90:
        push    0                           ; 儲存VX86 Es, Ds, Fs, Gs暫存器
        push    0
        push    0
        push    0

        push    01bh                        ; 儲存轉換CS(程式碼段)
        push    0                           ; 無法知道使用者的ESP
        push    EFLAGS_INTERRUPT_MASK+EFLAGS_V86_MASK+2h; 帶有VX86設定的EFLAGS
        push    01bh                        ; CS(程式碼段)
        push    0                           ; 不知道原始EIP(指令指標)
        jmp     _KiTrap06                   ; 將異常轉換為非法操作。

_KiSystemService endp

SSDT結構

NT\base\ntos\inc\ke.h

//
// System Service Table Descriptor
//
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
    PULONG_PTR Base;	// 設定成指向MainSSDT--基本的系統呼叫函式表
    PULONG Count;		// 系統呼叫次數計數器
    ULONG Limit;		//系統呼叫函式的個數;Limit:存放引數位元組數/4=個數
#if defined(_IA64_)
    LONG TableBaseGpOffset;// 系統服務參數列基址,8位元組大小。實際指向的陣列是以位元組為單位的記錄著對應服務函式的引數個數
#endif
    PUCHAR Number; 			//指向另一個陣列MainSSPT](說明系統呼叫引數的個數,以位元組為單位)
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;

win32/win64中SSDT結構相同

windbg如下:

;kd> dd KeServiceDescriptorTableShadow
		    ;地址表   計數器(當前執行緒引用次數) 函式個數  參數列(位元組數)、引數格式=位元組數/4
;83faca00  83ec0d9c 00000000 00000191 83ec13e4
;83faca10  95db6000 00000000 00000339 95db702c

xp _KiSystemService 反編譯asm

原始碼狀態下,大量邏輯糅合,理解困難。以下反編譯程式碼,更易理解

C:\Windows\System32\ntoskrnl.exe

.text:00407EA6
.text:00407EA6 ; =============== S U B R O U T I N E =======================================
.text:00407EA6
.text:00407EA6
.text:00407EA6 _KiSystemService proc near              ; CODE XREF: ZwAcceptConnectPort(x,x,x,x,x,x)+C↑p
.text:00407EA6                                         ; ZwAccessCheck(x,x,x,x,x,x,x,x)+C↑p ...
.text:00407EA6
.text:00407EA6 arg_0           = dword ptr  4
.text:00407EA6
.text:00407EA6                 push    0               ; _KTRAP_FRAME +0x64 ErrCode,push 0 因為諸如0號異常等本身沒有返回錯誤程式碼,為了保證堆疊平衡,所以直接push 0
.text:00407EA6                                         ; KiSystemCall在R3的共享資料段中
.text:00407EA8                 push    ebp             ; +0x60 EBP
.text:00407EA9                 push    ebx             ; +0x5c ebx
.text:00407EAA                 push    esi             ; +0x58 esi
.text:00407EAB                 push    edi             ; +0x54 edi
.text:00407EAC                 push    fs              ; +0x50 SegFs
.text:00407EAE                 mov     ebx, 30h        ; 為FS暫存器賦值,指向KPCR結構體,KPCR裡存放的是CPU狀態資訊,KPCR結構體也在共享資料段中
.text:00407EB3                 mov     fs, bx          ; windows核心中KPCR機構線性地址為0xffdff000
.text:00407EB3                                         ; fs = 0x30 (selector = 0011 0 000b)
.text:00407EB3                                         ; RPL=0, TI = 0(查GDT表); 索引 = 00110b = 6 (GDT表的第6項)
.text:00407EB3                                         ; KPCR所在的段描述符為: GDT表基址8003F000 + 0×30h(低3位清零)
.text:00407EB3                                         ;
.text:00407EB3                                         ; kd>dq 8003f000
.text:00407EB3                                         ; 8003F000 00000000`00000000 00cf9b00`0000FFFF
.text:00407EB3                                         ; 8003f010 00cf9300`0000FFFF 00cffb00`0000ffff
.text:00407EB3                                         ; 8003F020 30cff300`0000Ffff 80008b04`200020ab
.text:00407EB3                                         ; 8003F030 ffc093df`f0000001 0040F300`00000FFF
.text:00407EB3                                         ; 8003F040 0000F200`0400FFFF 00000000`00000000
.text:00407EB3                                         ; 8003F050 80008955`23800068 80008955`23e80068
.text:00407EB3                                         ; 8003F060 00009302`2F40FFFF 0000920b`80003fff
.text:00407EB3                                         ; 8003f070 FF0092ff`700003FF 80009a40`0000ffff
.text:00407EB3                                         ;
.text:00407EB3                                         ; ffc093df`f0000001: 段基址 = ffdff000 limit = 00001 c表示32位段20位
.text:00407EB6                 push    dword ptr ds:0FFDFF000h ; 儲存老的 ExceptionList -- 異常處理, 既KPRC[0] 地址處_NT_TIB[0]
.text:00407EBC                 mov     dword ptr ds:0FFDFF000h, 0FFFFFFFFh ; 設定新的異常處理鏈-1(為空)
.text:00407EC6                 mov     esi, ds:0FFDFF124h ; 偏移在0x120 對應KPCR結構最後一個欄位KPRCB+4
.text:00407EC6                                         ; nt!_KPRCB
.text:00407EC6                                         ;    +0x000 MinorVersion     : Uint2B              // 子版本
.text:00407EC6                                         ;    +0x002 MajorVersion     : Uint2B              // 主版本
.text:00407EC6                                         ;    +0x004 CurrentThread    : Ptr32 _KTHREAD     // 當前CPU所執行的執行緒
.text:00407ECC                 push    dword ptr [esi+140h] ; 儲存老的先前模式;esi指向當前執行緒KTHREAD結構
.text:00407ED2                 sub     esp, 48h        ; ESP指向KTRAP_FRAME框架0x000處
.text:00407ED5                 mov     ebx, [esp+68h+arg_0] ; 取出KTRAP_FRAME框架0x6c處存放使用者空間CS
.text:00407ED9                 and     ebx, 1          ; 取CS的CPU狀態,並儲存新 的先前模式
.text:00407EDC                 mov     [esi+140h], bl
.text:00407EE2                 mov     ebp, esp
.text:00407EE4                 mov     ebx, [esi+134h] ; 取_KTREAD結構中的Trap_Frane,並儲存在下面的地址處備後面恢復時用
.text:00407EEA                 mov     [ebp+3Ch], ebx
.text:00407EED                 mov     [esi+134h], ebp ; 將新構建的_KTRAP_FRAME指標賦給Trap_frane欄位
.text:00407EF3                 cld
.text:00407EF4                 mov     ebx, [ebp+60h]  ; 3環的EBP
.text:00407EF7                 mov     edi, [ebp+68h]  ; 3環的EIP
.text:00407EFA                 mov     [ebp+0Ch], edx  ; edx中儲存3環引數的指標
.text:00407EFA                                         ; _KiIntSystemCall@0 proc near
.text:00407EFA                                         ; lea edx,[esp+arg_4] ;User函式傳遞引數,eax中儲存系統呼叫號
.text:00407EFA                                         ; int 2Eh
.text:00407EFD                 mov     dword ptr [ebp+8], 0BADB0D00h
.text:00407F04                 mov     [ebp+0], ebx    ; 3環的恩必普儲存到_KTHREAD_FRAME的0x000地址處(DbgEbp)
.text:00407F07                 mov     [ebp+4], edi    ; 儲存3環的EIP
.text:00407F0A                 test    byte ptr [esi+2Ch], 0FFh ; 判斷_KTHREAD 0x2c處 DebugeActive是否為-1
.text:00407F0E                 jnz     Dr_kss_a        ; 如果處於除錯狀態則跳轉
.text:00407F14
.text:00407F14 loc_407F14:                             ; CODE XREF: Dr_kss_a+10↑j
.text:00407F14                                         ; Dr_kss_a+7C↑j
.text:00407F14                 sti                     ; 關中斷
.text:00407F15                 jmp     loc_408000
.text:00407F15 _KiSystemService endp
.text:00407F15
.text:00407F15 ; ---------------------------------------------------------------------------
.text:00407F1A                 db 5 dup(90h)
.text:00407F1F
.text:00407F1F ; =============== S U B R O U T I N E =======================================
.text:00407F1F
.text:00407F1F
.text:00407F1F _KiFastCallEntry2 proc near             ; DATA XREF: _KiTrap01:loc_408CBB↓o
.text:00407F1F                 mov     ecx, 30h
.text:00407F24                 mov     fs, ecx
.text:00407F26                 mov     ecx, 23h
.text:00407F2B                 mov     ds, ecx
.text:00407F2D                 mov     es, ecx
.text:00407F2F                 mov     ecx, ds:0FFDFF040h
.text:00407F35                 mov     esp, [ecx+4]
.text:00407F38                 push    23h
.text:00407F3A                 push    edx
.text:00407F3B                 pushf
.text:00407F3C                 or      byte ptr [esp+1], 1
.text:00407F41                 jmp     short loc_407F89
.text:00407F41 _KiFastCallEntry2 endp
.text:00407F41
.text:00407F43 ; ---------------------------------------------------------------------------
.text:00407F43 ; START OF FUNCTION CHUNK FOR _KiFastCallEntry
.text:00407F43
.text:00407F43 loc_407F43:                             ; CODE XREF: .text:00407F66↓j
.text:00407F43                                         ; _KiFastCallEntry+60↓j
.text:00407F43                 mov     ecx, ds:0FFDFF040h
.text:00407F49                 mov     esp, [ecx+4]
.text:00407F4C                 push    0
.text:00407F4E                 push    0
.text:00407F50                 push    0
.text:00407F52                 push    0
.text:00407F54                 push    23h
.text:00407F56                 push    0
.text:00407F58                 push    20202h
.text:00407F5D                 push    1Bh
.text:00407F5F                 push    0
.text:00407F61                 jmp     _KiTrap06
.text:00407F61 ; END OF FUNCTION CHUNK FOR _KiFastCallEntry
.text:00407F66 ; ---------------------------------------------------------------------------
.text:00407F66                 jmp     short loc_407F43
.text:00407F66 ; ---------------------------------------------------------------------------
.text:00407F68                 db 8Bh, 0FFh
.text:00407F6A                 db 5 dup(90h)
.text:00407F6F
.text:00407F6F ; =============== S U B R O U T I N E =======================================
.text:00407F6F
.text:00407F6F
.text:00407F6F _KiFastCallEntry proc near              ; DATA XREF: _KiTrap01+72↓o
.text:00407F6F                                         ; KiLoadFastSyscallMachineSpecificRegisters(x)+24↓o
.text:00407F6F
.text:00407F6F var_B           = byte ptr -0Bh
.text:00407F6F
.text:00407F6F ; FUNCTION CHUNK AT .text:00407F43 SIZE 00000023 BYTES
.text:00407F6F ; FUNCTION CHUNK AT .text:00408210 SIZE 00000014 BYTES
.text:00407F6F
.text:00407F6F                 mov     ecx, 23h        ; KGDT_R3_DATA|RPL_MASK
.text:00407F74                 push    30h             ; KGDT_RO_PCR
.text:00407F76                 pop     fs              ; 使FS指向KPCR(在WINDOWS核心地址為:0xffdff000)
.text:00407F78                 mov     ds, ecx         ; 將暫存器DS和ES設定成 KGDT_R3_DATA | RPL_MASK
.text:00407F7A                 mov     es, ecx
.text:00407F7C                 mov     ecx, ds:0FFDFF040h ; mov ecx, PCR[KPCR_TSS] 從KPCR獲取TSS段的起點
.text:00407F82                 mov     esp, [ecx+4]    ; mov esp,[ecx+KTSS_ESP0] //從TSS獲取系統空間堆疊指標
.text:00407F85                 push    23h             ; 使用者空間SS:ESP入棧
.text:00407F85                                         ; push KGDT_R3_DATA + RPL_MASK
.text:00407F85                                         ; push edx /*Ring 3 SS:ESP*/
.text:00407F87                 push    edx
.text:00407F88                 pushf                   ; /* Ring 3 EFLAGS */
.text:00407F89
.text:00407F89 loc_407F89:                             ; CODE XREF: _KiFastCallEntry2+22↑j
.text:00407F89                 push    2               ; push 2 /* Ring 0 EFLAGS */
.text:00407F8B                 add     edx, 8          ; /* Skip 略過user parameter list */
.text:00407F8E                 popf                    ; /* EFLAGS暫存器的值改為2 */
.text:00407F8F                 or      [esp+0Ch+var_B], 2 ; 在EFLAGS中重新啟用IRQs,以偽造INT
.text:00407F94                 push    1Bh             ; push 1Bh就是push cs。壓入cs
.text:00407F96                 push    dword ptr ds:0FFDF0304h ; 壓入返回地址,退出時 pop edx
.text:00407F96                                         ; ds:0FFDF0300對應KiInitSystemCall或KiFastSystemCall
.text:00407F96                                         ; ds:0FFDF0304對應KiFastSystemCallRet
.text:00407F96                                         ; 就是 PUSH EIP
.text:00407F9C                 push    0               ; 設定系統呼叫框架堆疊
.text:00407F9E                 push    ebp
.text:00407F9F                 push    ebx
.text:00407FA0                 push    esi
.text:00407FA1                 push    edi
.text:00407FA2                 mov     ebx, ds:0FFDFF01Ch ; 設定當前KPCRB地址
.text:00407FA8                 push    3Bh             ; push GDT_R3_TEB + RPL_MASK
.text:00407FAA                 mov     esi, [ebx+124h] ; 在PCR中拿到當前執行緒指標_KTHREAD
.text:00407FB0                 push    dword ptr [ebx] ; 異常處理鏈入棧
.text:00407FB2                 mov     dword ptr [ebx], 0FFFFFFFFh
.text:00407FB8                 mov     ebp, [esi+18h]  ; /* 使用當前執行緒堆疊 */
.text:00407FB8                                         ; mov ebp,[esi + KTHREAD_INITIAL_STACK]
.text:00407FB8                                         ; 當前ebp指向0環棧頂
.text:00407FBB                 push    1               ; 儲存前一個進入模式,因為在0環不需要使用快速系統呼叫,所以不需要判斷進入模式,直接設定
.text:00407FBD                 sub     esp, 48h        ;  跳過其他暫存器
.text:00407FC0                 sub     ebp, 29Ch       ; 在堆疊上為我們騰出空間
.text:00407FC6                 mov     byte ptr [esi+140h], 1 ; 設定當前進入模式
.text:00407FCD                 cmp     ebp, esp        ; 合理性檢查,防止棧溢位
.text:00407FCF                 jnz     loc_407F43
.text:00407FD5                 and     dword ptr [ebp+2Ch], 0 ; /* Flush DR7 */
.text:00407FD5                                         ; and dword ptr[cbp+KTRAP_FRAMEJDR7],0
.text:00407FD9                 test    byte ptr [esi+2Ch], 0FFh ; 檢查執行緒是否正在被除錯
.text:00407FD9                                         ; test byte ptr [esi+KTHREAD_DEBUG_ACTIUE],OxFF
.text:00407FDD                 mov     [esi+134h], ebp ; 設定執行緒的自陷框架
.text:00407FDD                                         ; mov [esi + KTHREAD_TRAP_FRAHE],ebp
.text:00407FE3                 jnz     Dr_FastCallDrSave
.text:00407FE9
.text:00407FE9 loc_407FE9:                             ; CODE XREF: Dr_FastCallDrSave+10↑j
.text:00407FE9                                         ; Dr_FastCallDrSave+7C↑j
.text:00407FE9                 mov     ebx, [ebp+60h]  ; dbg除錯
.text:00407FE9                                         ; set the trap frane debug header除錯報頭
.text:00407FE9                                         ; Dr_&EndLabcl:
.text:00407FE9                                         ; SET_TF_DEBUG_HEADER /*Enable interrupts*/
.text:00407FEC                 mov     edi, [ebp+68h]
.text:00407FEF                 mov     [ebp+0Ch], edx
.text:00407FF2                 mov     dword ptr [ebp+8], 0BADB0D00h
.text:00407FF9                 mov     [ebp+0], ebx
.text:00407FFC                 mov     [ebp+4], edi
.text:00407FFF                 sti
.text:00408000
.text:00408000 loc_408000:                             ; CODE XREF: _KiBBTUnexpectedRange+18↑j
.text:00408000                                         ; _KiSystemService+6F↑j
.text:00408000                 mov     edi, eax        ; 取出系統呼叫號
.text:00408002                 shr     edi, 8          ; 右移兩位
.text:00408005                 and     edi, 30h        ; 檢測0x000的x位是否為1.如果為1表示大於0x1009,是新擴充套件的呼叫號,需要安裝win32k.sys模組
.text:00408008                 mov     ecx, edi        ; ECX值為0x00或0x10(呼叫號大於0x1000)
.text:0040800A                 add     edi, [esi+0E0h] ; _KTREAD->ServiceTable(0xe0)為SSDT表,如果+0x10即為擴充套件的SSDT表
.text:00408010                 mov     ebx, eax
.text:00408012                 and     eax, 0FFFh      ; 系統呼叫號只需要後12位是函式地址表和函式參數列的索引
.text:00408017                 cmp     eax, [edi+8]    ; 檢查系統呼叫號是否越界SERUICE_DESCRIPTOR_LIMIT定義為8
.text:00408017                                         ; 系統呼叫表描述符大小為0×10
.text:00408017                                         ;  typedef struct _KSERUICE_TABLE_DESCRIPTOR {
.text:00408017                                         ;     PULONG_PTRBase; //設定成指向MainSSDT--基本的系統呼叫函式表
.text:00408017                                         ;     PLILONG Count;  //SSDT中服務被呼叫次數計數器,8位元組大小
.text:00408017                                         ;     ULONGLimit;     //SSDT表中服務函式的總數,設定成常數NUMBER_OF_SYSCALLS(8)
.text:00408017                                         ;  #ifdefined(_IA64)
.text:00408017                                         ;     LONG TableBaseGpoffset;
.text:00408017                                         ;  #endif
.text:00408017                                         ;     PUCHAR Number   //指向另一個陣列MainSSPT[](說明系統呼叫引數的個數,以位元組為單位)
.text:00408017                                         ;  ) KSERUICE_TABLE_DESCRIPTOR , *PKSERUICE_TABLE_DESCRIPTOR;
.text:0040801A                 jnb     _KiBBTUnexpectedRange ; 系統呼叫號越界,有可能是因為大於0x1000,裝入Win32.sys模組(擴充套件系統呼叫表)
.text:00408020                 cmp     ecx, 10h        ; 如果ecx值為0x10則為擴充套件的SSDT表
.text:00408023                 jnz     short loc_40803F ; 沒有越界,不死擴充套件系統呼叫
.text:00408025                 mov     ecx, ds:0FFDFF018h
.text:0040802B                 xor     ebx, ebx
.text:0040802D
.text:0040802D loc_40802D:                             ; DATA XREF: _KiTrap0E+113↓o
.text:0040802D                 or      ebx, [ecx+0F70h]
.text:00408033                 jz      short loc_40803F
.text:00408035                 push    edx
.text:00408036                 push    eax
.text:00408037                 call    ds:_KeGdiFlushUserBatch
.text:0040803D                 pop     eax
.text:0040803E                 pop     edx
.text:0040803F
.text:0040803F loc_40803F:                             ; CODE XREF: _KiFastCallEntry+B4↑j
.text:0040803F                                         ; _KiFastCallEntry+C4↑j
.text:0040803F                 inc     dword ptr ds:0FFDFF638h
.text:00408045                 mov     esi, edx
.text:00408047                 mov     ebx, [edi+0Ch]
.text:0040804A                 xor     ecx, ecx
.text:0040804C                 mov     cl, [eax+ebx]
.text:0040804F                 mov     edi, [edi]
.text:00408051                 mov     ebx, [edi+eax*4]
.text:00408054                 sub     esp, ecx
.text:00408056                 shr     ecx, 2
.text:00408059                 mov     edi, esp
.text:0040805B                 cmp     esi, ds:_MmUserProbeAddress
.text:00408061                 jnb     loc_408210
.text:00408067
.text:00408067 loc_408067:                             ; CODE XREF: _KiFastCallEntry+2A5↓j
.text:00408067                                         ; DATA XREF: _KiTrap0E+109↓o
.text:00408067                 rep movsd				 ;複製引數
.text:00408069                 call    ebx				 ;執行核心函式
.text:0040806B
.text:0040806B loc_40806B:                             ; CODE XREF: _KiFastCallEntry+2B0↓j
.text:0040806B                                         ; DATA XREF: _KiTrap0E+129↓o ...
.text:0040806B                 mov     esp, ebp
.text:0040806D
.text:0040806D loc_40806D:                             ; CODE XREF: _KiBBTUnexpectedRange+38↑j
.text:0040806D                                         ; _KiBBTUnexpectedRange+43↑j
.text:0040806D                 mov     ecx, ds:0FFDFF124h
.text:00408073                 mov     edx, [ebp+3Ch]
.text:00408076                 mov     [ecx+134h], edx
.text:00408076 _KiFastCallEntry endp
.text:00408076
.text:0040807C
.text:0040807C ; =============== S U B R O U T I N E =======================================
.text:0040807C
.text:0040807C
.text:0040807C _KiServiceExit  proc near               ; CODE XREF: _KiSetLowWaitHighThread+7D↓j
.text:0040807C                                         ; NtContinue(x,x)+42↓j ...
.text:0040807C
.text:0040807C arg_C           = dword ptr  10h
.text:0040807C arg_10          = dword ptr  14h
.text:0040807C arg_40          = dword ptr  44h
.text:0040807C arg_44          = dword ptr  48h
.text:0040807C arg_48          = dword ptr  4Ch
.text:0040807C arg_60          = dword ptr  64h
.text:0040807C arg_64          = dword ptr  68h
.text:0040807C arg_68          = dword ptr  6Ch
.text:0040807C arg_6C          = dword ptr  70h
.text:0040807C
.text:0040807C ; FUNCTION CHUNK AT .text:00408188 SIZE 00000088 BYTES
.text:0040807C
.text:0040807C                 cli
.text:0040807D                 test    dword ptr [ebp+70h], 20000h
.text:00408084                 jnz     short loc_40808C
.text:00408086                 test    byte ptr [ebp+6Ch], 1
.text:0040808A                 jz      short loc_4080E4
.text:0040808C
.text:0040808C loc_40808C:                             ; CODE XREF: _KiServiceExit+8↑j
.text:0040808C                                         ; _KiServiceExit+63↓j
.text:0040808C                 mov     ebx, ds:0FFDFF124h
.text:00408092                 mov     byte ptr [ebx+2Eh], 0
.text:00408096                 cmp     byte ptr [ebx+4Ah], 0
.text:0040809A                 jz      short loc_4080E4
.text:0040809C                 mov     ebx, ebp
.text:0040809E                 mov     [ebx+44h], eax
.text:004080A1                 mov     dword ptr [ebx+50h], 3Bh
.text:004080A8                 mov     dword ptr [ebx+38h], 23h
.text:004080AF                 mov     dword ptr [ebx+34h], 23h
.text:004080B6                 mov     dword ptr [ebx+30h], 0
.text:004080BD                 mov     ecx, 1          ; NewIrql
.text:004080C2                 call    ds:__imp_@KfRaiseIrql@4 ; KfRaiseIrql(x)
.text:004080C8                 push    eax
.text:004080C9                 sti
.text:004080CA                 push    ebx
.text:004080CB                 push    0
.text:004080CD                 push    1
.text:004080CF                 call    _KiDeliverApc@12 ; KiDeliverApc(x,x,x)
.text:004080D4                 pop     ecx             ; NewIrql
.text:004080D5                 call    ds:__imp_@KfLowerIrql@4 ; KfLowerIrql(x)
.text:004080DB                 mov     eax, [ebx+44h]
.text:004080DE                 cli
.text:004080DF                 jmp     short loc_40808C
.text:004080DF ; ---------------------------------------------------------------------------
.text:004080E1                 align 4
.text:004080E4
.text:004080E4 loc_4080E4:                             ; CODE XREF: _KiServiceExit+E↑j
.text:004080E4                                         ; _KiServiceExit+1E↑j
.text:004080E4                 mov     edx, [esp+arg_48]
.text:004080E8                 mov     ebx, large fs:50h
.text:004080EF                 mov     large fs:0, edx
.text:004080F6                 mov     ecx, [esp+arg_44]
.text:004080FA                 mov     esi, large fs:124h
.text:00408101                 mov     [esi+140h], cl
.text:00408107                 test    ebx, 0FFh
.text:0040810D                 jnz     short loc_408188
.text:0040810F
.text:0040810F loc_40810F:                             ; CODE XREF: _KiServiceExit+11C↓j
.text:0040810F                                         ; _KiServiceExit+14B↓j
.text:0040810F                 test    [esp+arg_6C], 20000h
.text:00408117                 jnz     loc_408A28
.text:0040811D                 test    word ptr [esp+arg_68], 0FFF8h
.text:00408124                 jz      loc_4081DE
.text:0040812A                 cmp     word ptr [esp+arg_68], 1Bh
.text:00408130                 bt      word ptr [esp+arg_68], 0
.text:00408137                 cmc
.text:00408138                 ja      loc_4081CC
.text:0040813E                 cmp     word ptr [ebp+6Ch], 8
.text:00408143                 jz      short loc_40814A
.text:00408145
.text:00408145 loc_408145:                             ; CODE XREF: _KiServiceExit+15D↓j
.text:00408145                 lea     esp, [ebp+50h]
.text:00408148                 pop     fs
.text:0040814A                 assume fs:nothing
.text:0040814A
.text:0040814A loc_40814A:                             ; CODE XREF: _KiServiceExit+C7↑j
.text:0040814A                 lea     esp, [ebp+54h]
.text:0040814D                 pop     edi
.text:0040814E                 pop     esi
.text:0040814F                 pop     ebx
.text:00408150                 pop     ebp
.text:00408151                 cmp     word ptr [esp-60h+arg_64], 80h
.text:00408158                 ja      loc_408A44
.text:0040815E                 add     esp, 4
.text:00408161                 test    [esp-64h+arg_64], 1
.text:00408161 _KiServiceExit  endp ; sp-analysis failed

快速系統呼叫

系統呼叫與快速系統呼叫僅入口方式與使用者態資訊獲取儲存有些許不同

入口方式不同 xp _KiFastSystemCall 反編譯ASM

C:\Windows\System32\ntdll.dll

.text:77F070B0
.text:77F070B0 ; _DWORD __stdcall KiFastSystemCall()
.text:77F070B0                 public _KiFastSystemCall@0
.text:77F070B0 _KiFastSystemCall@0 proc near           ; DATA XREF: .text:off_77EF61B8↑o
.text:77F070B0                 mov     edx, esp		;將堆疊指標儲存在暫存器EDX中
.text:77F070B2                 sysenter				;進入核心
.text:77F070B2 _KiFastSystemCall@0 endp
.text:77F070B2
.text:77F070B4 ; Exported entry 107. KiFastSystemCallRet
.text:77F070B4
.text:77F070B4 ; =============== S U B R O U T I N E =======================================
.text:77F070B4
.text:77F070B4
.text:77F070B4 ; _DWORD __stdcall KiFastSystemCallRet()
.text:77F070B4                 public _KiFastSystemCallRet@0
.text:77F070B4 _KiFastSystemCallRet@0 proc near        ; DATA XREF: .text:off_77EF61B8↑o
.text:77F070B4                 retn
.text:77F070B4 _KiFastSystemCallRet@0 endp
.text:77F070B4
.text:77F070B4 ; ---------------------------------------------------------------------------
  • 系統呼叫:透過INT 2EH指令進入0環執行呼叫。進入0環時3環資訊(SS/ESP/EFLAGS/CS/EIP)儲存在棧空間,進行傳遞

  • 快速系統呼叫:透過sysenter指令進入0環執行呼叫。進入0環所需的CS/ESP/EIP都已在系統初始化時預定義,SS = CS + 8

    • IA32_SYSENTER_CS (MSR 0x174):儲存核心程式碼段選擇子(CS)的值。

      IA32_SYSENTER_ESP (MSR 0x175):儲存在進入核心模式時要載入到 ESP 的核心堆疊指標。

      IA32_SYSENTER_EIP (MSR 0x176):儲存 sysenter 指令進入核心模式後跳轉的程式碼地址,既 _KiFastCallEntry

winDbg如下:

kd> rdmsr 174
msr[174] = 00000000`00000008
kd> rdmsr 175
msr[175] = 00000000`80790000
kd> rdmsr 176
msr[176] = 00000000`83e800c0

使用者空間資訊儲存不同 xp _KiFastCallEntry 原始碼asm

\NT\base\ntos\ke\i386\trap.asm

_KiFastCallEntry        proc

;
;       返回到緊接著 sysenter 指令之後的指令,該指令位於共享使用者資料結構中的已知位置
;       (這是為了在系統初始化時根據處理器動態放置正確的程式碼)。
;

;       從 Tss.Esp0 載入 ESP。中斷已被禁用,ESP 尚未載入。

ifndef NT_UP

        mov     ecx, KGDT_R0_PCR            ; 載入全域性描述符表(GDT)中的PCR
        mov     fs, ecx                     ; 將 fs 暫存器設定為 PCR
        mov     ecx, PCR[PcTss]             ; 從 PCR 中獲取 TSS 的地址

else

        mov     ecx, ss:PCR[PcTss]          ; 在單處理器系統上,從 ss 段暫存器獲取 TSS

endif ;; NT_UP

        mov     esp, ss:[ecx].TssEsp0       ; 將 ESP 設定為 TSS 的 Esp0
;
;       將 ecx 設定為使用者模式下的返回地址
;

        mov     ecx, MM_SHARED_USER_DATA_VA+UsSystemCall+fscrOffset  ; 獲取使用者模式下系統呼叫返回地址
Kfsc10:
        cmp     esp, PCR[PcInitialStack]    ; 檢查是否從 VDM 呼叫
        je      Kfsc90                      ; 如果從 VDM 呼叫,則跳轉到 Kfsc90
        push    KGDT_R3_DATA  OR RPL_MASK   ; 壓入使用者堆疊段 SS
        push    edx                         ; 壓入 ESP
        add     edx, 8                      ; edx 指向系統呼叫的引數
        push    EFLAGS_INTERRUPT_MASK+2     ; 壓入已清理的 EFLAGS
        push    2                           ; 清理後的核心 EFLAGS
        popfd                               ; 從棧中彈出並載入到 EFLAGS
        push    KGDT_R3_CODE OR RPL_MASK    ; 壓入使用者程式碼段 CS
        push    ecx                         ; 壓入返回地址

ifndef NT_UP

        ; 在多處理器(MP)系統中,FS 已經在上面載入

        ENTER_SYSCALL   kfce_a, kfce_t, NoFSLoad  ; 進入系統呼叫處理
        jmp     _KiSystemServiceRepeat      ; 跳轉到系統服務處理邏輯

endif ;; NT_UP

_KiFastCallEntry endp

  • 系統呼叫R3跳轉到R0時,R3資訊(ss/esp/eflags/cs/eip)由int指令隱式執行儲存到堆疊中並複製到R0堆疊
  • 快速系統呼叫R3跳轉R0時,R3資訊在_KiFastCallEntry 顯示的儲存到了R0的堆疊中

執行流程

系統呼叫

image

快速系統呼叫

image

相關文章