首页 > 系统相关 >Windows 系统调用

Windows 系统调用

时间:2024-09-26 15:23:31浏览次数:7  
标签:调用 esp Windows text 系统 mov 00000000 push ebx

目录

系统调用就是操作系统为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

标签:调用,esp,Windows,text,系统,mov,00000000,push,ebx
From: https://www.cnblogs.com/ylc0x01/p/18433524

相关文章