目录
系统调用就是操作系统为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
宏用于进入系统调用,填充系统调用框架,为后续真正的系统调用保存现场提供信息
- 保存寄存器状态:保存重要的寄存器(如
ebp
,ebx
,esi
,edi
等),这些寄存器的值在陷阱帧中会被恢复。 - 处理FS段寄存器:条件地保存FS寄存器并设置为PCR(Processor Control Region)段。
- 处理异常链表:保存当前异常链表并初始化一个新的空链表,用于异常处理。
- 处理previous mode(先前模式):保存并设置新的previous mode,用于区分内核模式和用户模式。
- 设置陷阱帧:保存当前陷阱帧地址并将新的陷阱帧地址存储在当前线程控制块中(TSS)。
- 处理调试信息:如果调试器处于活动状态,会跳转到处理调试器的代码路径。
- 启用中断:最后启用中断,使得系统调用可以处理中断。执行系统调用时不用发生线程切换
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
宏主要用于恢复系统调用框架,退出系统调用或异常处理程序,并恢复现场。
- 参数:
NoRestoreSegs
:控制是否恢复段寄存器(gs
、fs
)。NoRestoreVolatiles
:控制是否恢复通用寄存器(eax
、ecx
、edx
)。NoPreviousMode
:控制是否恢复先前模式。
- 全局变量设置:
?adjesp
:初始化为TsSegGs
,并根据条件调整。?RestoreAll
:默认为1,表示恢复所有寄存器。如果NoRestoreSegs
或NoRestoreVolatiles
被指定,则调整恢复行为。
- 入口检查:
- 检查中断标志和堆栈指针,确保它们符合预期,以保证正确的陷阱帧格式。
- 调试检查:
- 检查调试信息是否激活。如果调试激活,会跳转到对应的调试处理代码。
- 恢复寄存器和处理器状态:
- 根据宏参数条件,恢复段寄存器(
fs
,gs
,es
,ds
)和通用寄存器(如eax
,ecx
,edx
)。 - 恢复先前模式,如果指定了
NoPreviousMode
,则不进行恢复。 - 恢复异常链表(
ExceptionList
)和调用框架其他字段。
- 根据宏参数条件,恢复段寄存器(
- 处理特殊情况:
- 检查是否是在虚拟8086模式下运行,并进行相应处理。
- 针对特定模式(如平面内存模式)进行不同的恢复操作。
- 最终返回:
- 使用
iret
或iretd
指令将控制权返回给调用者,恢复执行流。
- 使用
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的堆栈中