系统调用

MSR寄存器

MSR是一组用于控制CPU的寄存器。

MSR(Model Specific Register,模型专用寄存器)是x86/x64架构中央处理器(CPU)中的一组64位寄存器,主要用于配置硬件参数、监控运行状态及支持特定功能。其操作需通过RDMSR和WRMSR指令执行,并依赖于ECX寄存器指定目标地址,通常在特权级别0或实模式下运行。
MSR涵盖温度控制、频率调节、电源管理(C State)、微码更新及缓存控制等核心模块,涉及14类功能特性。典型应用包括通过IA32_SYSENTER系列寄存器实现快速系统调用(sysenter/sysexit),以及借助EFER寄存器管理64位模式切换。不同厂商或型号CPU的MSR功能可能存在差异,具体定义需参考处理器手册。在ARM架构中,类似功能的系统寄存器也通过MSR/MRS指令访问,但在指令集和寄存器命名上存在差异。

每一个MSR寄存器都有一个Index标识,当在rcx中写入index后,就可以用RDMSR和WRMSR来读取或写入某个MSR寄存器,目标或源均为EDX:EAX。仅r0可读可写

比较重要的有:

  • MSR_LSTAR (0xC0000082) 保存 64 位系统调用入口地址。当用户态执行 syscall 指令时,CPU 会读取 MSR_LSTAR 的值跳转到内核的系统调用处理入口。
  • MSR_STAR (0xC0000081) 保存系统调用时使用的段描述符信息,包括用户态和内核态的代码段选择符。它在系统调用和返回时确保段选择符得到正确设置。
  • MSR_FMASK (0xC0000084) 保存着在从用户态进入内核态过程中需要屏蔽的 EFLAGS 位(比如中断标志等)。这可以防止在执行系统调用处理时出现不安全的中断或其他异常情况。
  • MSR_CSTAR (0xC0000083) 在一些系统上用于 32 位兼容系统调用的入口地址(针对 x86 模式下的系统调用)。但在纯64位环境下通常主要使用 MSR_LSTAR。

windbg中可以使用rdmsr 0xC0000082来查看具体的值

1
2
3
4
5
6
7
8
9
10
11
12
kd> rdmsr 0xC0000082
msr[c0000082] = fffff803`ede8c040
kd> u fffff803`ede8c040
nt!KiSystemCall64:
fffff803`ede8c040 0f01f8 swapgs
fffff803`ede8c043 654889242510000000 mov qword ptr gs:[10h],rsp
fffff803`ede8c04c 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h]
fffff803`ede8c055 6a2b push 2Bh
fffff803`ede8c057 65ff342510000000 push qword ptr gs:[10h]
fffff803`ede8c05f 4153 push r11
fffff803`ede8c061 6a33 push 33h
fffff803`ede8c063 51 push rcx

syscall和sysret

syscall是intel的快速调用指令,一般在x64使用,x86用sysenter。

基本的格式是:(ntdll.dll中的ZwTestAlert)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.text:00000001800A3DC0                     public ZwTestAlert
.text:00000001800A3DC0 ZwTestAlert proc near ; CODE XREF: sub_18007455C+A7↑p
.text:00000001800A3DC0 ; sub_1800A4260+D↓p
.text:00000001800A3DC0 ; DATA XREF: ...
.text:00000001800A3DC0 000 mov r10, rcx ; NtTestAlert
.text:00000001800A3DC3 000 mov eax, 1D0h
.text:00000001800A3DC8 000 test byte ptr ds:7FFE0308h, 1
.text:00000001800A3DD0 000 jnz short loc_1800A3DD5
.text:00000001800A3DD2 000 syscall ; Low latency system call
.text:00000001800A3DD4 000 retn
.text:00000001800A3DD5 ; ---------------------------------------------------------------------------
.text:00000001800A3DD5
.text:00000001800A3DD5 loc_1800A3DD5: ; CODE XREF: ZwTestAlert+10↑j
.text:00000001800A3DD5 000 int 2Eh ; DOS 2+ internal - EXECUTE COMMAND
.text:00000001800A3DD5 ; DS:SI -> counted CR-terminated command string
.text:00000001800A3DD7 000 retn
.text:00000001800A3DD7 ZwTestAlert endp

这里将传参放入r10,将调用号放入eax,检测的地址是KiSystemCall64表的一位,它表明了用用int 2e中断进入或者syscall进入内核

syscall执行时,CPU将rflags放入r11,将下一条指令地址放入rcx。并以如下方式获取r0的上下文(代码段、指令指针、堆栈段和标志):

  • 从MSR的IA32_LSTAR域中加载r0的RIP值
  • 从MSR的IA32_FMASK域加载mask并对R11中的rflags进行掩码操作(清除IA32_FMASK置位的位),一般来说会把Interrupt flag清零,防止可屏蔽硬件中断插入执行
  • 从MSR的IA32_STAR域的47:32派生值加载cs和ss段选择子。但是它们的段选择器caches不会由它们在gdt或ldt中指向的值获取,而是加载固定值。SYSCALL不保证这些内容的合理
  • SYSCALL不保存栈指针

这样就实现了上下文的转换。

sysret从r0返回r3

从MSR的IA32_STAR域63:48加载cs和ss段选择子,其caches也是固定值,同时不修改栈指针

同时将rcx的值加载到rip,将r11的值加载到rflags

KUSER_SHARED_DATA

在刚刚的代码中看到了test byte ptr ds:7FFE0308h, 1,其中7FFE0000存放了KUSER_SHARED_DATA结构,该结构有4kb,是内核和用户虚拟内存的共享区间,用户可读,内核可读可写。内核:0xFFFFF78000000000到 0xFFFFF78000000FFF。用户:0x7FFE0000到0x7FFE0FFF。

具体内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
6: kd> dt _KUSER_SHARED_DATA 0xFFFFF78000000000
Wdf01000!_KUSER_SHARED_DATA
+0x000 TickCountLowDeprecated : 0
+0x004 TickCountMultiplier : 0xfa00000
+0x008 InterruptTime : _KSYSTEM_TIME
+0x014 SystemTime : _KSYSTEM_TIME
+0x020 TimeZoneBias : _KSYSTEM_TIME
+0x02c ImageNumberLow : 0x8664
+0x02e ImageNumberHigh : 0x8664
+0x030 NtSystemRoot : [260] "C:\WINDOWS"
+0x238 MaxStackTraceDepth : 0
+0x23c CryptoExponent : 0
+0x240 TimeZoneId : 0
+0x244 LargePageMinimum : 0x200000
+0x248 AitSamplingValue : 0
+0x24c AppCompatFlag : 0
+0x250 RNGSeedVersion : 0xb
+0x258 GlobalValidationRunlevel : 0
+0x25c TimeZoneBiasStamp : 0n4
+0x260 NtBuildNumber : 0x65f4
+0x264 NtProductType : 1 ( NtProductWinNt )
+0x268 ProductTypeIsValid : 0x1 ''
+0x269 Reserved0 : [1] ""
+0x26a NativeProcessorArchitecture : 9
+0x26c NtMajorVersion : 0xa
+0x270 NtMinorVersion : 0
+0x274 ProcessorFeatures : [64] ""
+0x2b4 Reserved1 : 0x7ffeffff
+0x2b8 Reserved3 : 0x80000000
+0x2bc TimeSlip : 0
+0x2c0 AlternativeArchitecture : 0 ( StandardDesign )
+0x2c4 BootId : 0x33
+0x2c8 SystemExpirationDate : _LARGE_INTEGER 0x0
+0x2d0 SuiteMask : 0x110
+0x2d4 KdDebuggerEnabled : 0x3 ''
+0x2d5 MitigationPolicies : 0xa ''
+0x2d5 NXSupportPolicy : 0y10
+0x2d5 SEHValidationPolicy : 0y10
+0x2d5 CurDirDevicesSkippedForDlls : 0y00
+0x2d5 Reserved : 0y00
+0x2d6 CyclesPerYield : 0x1d
+0x2d8 ActiveConsoleId : 1
+0x2dc DismountCount : 0
+0x2e0 ComPlusPackage : 0xffffffff
+0x2e4 LastSystemRITEventTickCount : 0x24d89
+0x2e8 NumberOfPhysicalPages : 0xffe5f
+0x2ec SafeBootMode : 0 ''
+0x2ed VirtualizationFlags : 0x1 ''
+0x2ee Reserved12 : [2] ""
+0x2f0 SharedDataFlags : 0x10e
+0x2f0 DbgErrorPortPresent : 0y0
+0x2f0 DbgElevationEnabled : 0y1
+0x2f0 DbgVirtEnabled : 0y1
+0x2f0 DbgInstallerDetectEnabled : 0y1
+0x2f0 DbgLkgEnabled : 0y0
+0x2f0 DbgDynProcessorEnabled : 0y0
+0x2f0 DbgConsoleBrokerEnabled : 0y0
+0x2f0 DbgSecureBootEnabled : 0y0
+0x2f0 DbgMultiSessionSku : 0y1
+0x2f0 DbgMultiUsersInSessionSku : 0y0
+0x2f0 DbgStateSeparationEnabled : 0y0
+0x2f0 SpareBits : 0y000000000000000000000 (0)
+0x2f4 DataFlagsPad : [1] 0
+0x2f8 TestRetInstruction : 0xc3
+0x300 QpcFrequency : 0n10000000
+0x308 SystemCall : 0
+0x30c Reserved2 : 0
+0x310 FullNumberOfPhysicalPages : 0xffe5f
+0x318 SystemCallPad : [1] 0
+0x320 TickCount : _KSYSTEM_TIME
+0x320 TickCountQuad : 0x33e4
+0x320 ReservedTickCountOverlay : [3] 0x33e4
+0x32c TickCountPad : [1] 0
+0x330 Cookie : 0x4c0dee
+0x334 CookiePad : [1] 0
+0x338 ConsoleSessionForegroundProcessId : 0n6536
+0x340 TimeUpdateLock : 0x32410
+0x348 BaselineSystemTimeQpc : 0x00000001`ba5c0849
+0x350 BaselineInterruptTimeQpc : 0x00000001`ba5c0849
+0x358 QpcSystemTimeIncrement : 0x80000000`00000000
+0x360 QpcInterruptTimeIncrement : 0x80000000`00000000
+0x368 QpcSystemTimeIncrementShift : 0x1 ''
+0x369 QpcInterruptTimeIncrementShift : 0x1 ''
+0x36a UnparkedProcessorCount : 8
+0x36c EnclaveFeatureMask : [4] 0
+0x37c TelemetryCoverageRound : 1
+0x380 UserModeGlobalLogger : [16] 0
+0x3a0 ImageFileExecutionOptions : 0
+0x3a4 LangGenerationCount : 1
+0x3a8 Reserved4 : 0
+0x3b0 InterruptTimeBias : 0
+0x3b8 QpcBias : 0xffffffff`e13b5ad0
+0x3c0 ActiveProcessorCount : 8
+0x3c4 ActiveGroupCount : 0x1 ''
+0x3c5 Reserved9 : 0 ''
+0x3c6 QpcData : 0x83
+0x3c6 QpcBypassEnabled : 0x83 ''
+0x3c7 QpcReserved : 0 ''
+0x3c8 TimeZoneBiasEffectiveStart : _LARGE_INTEGER 0x01dbe45f`c996b395
+0x3d0 TimeZoneBiasEffectiveEnd : _LARGE_INTEGER 0x01dc7a6e`845dc000
+0x3d8 XState : _XSTATE_CONFIGURATION
+0x720 FeatureConfigurationChangeStamp : _KSYSTEM_TIME
+0x72c Spare : 0
+0x730 UserPointerAuthMask : 0
+0x738 Reserved10 : [210] 0

308(SystemCall)这里指定了调用的方式,ProcessorFeatures指明了CPU支持的特性。KdDebuggerEnabled指明是否有内核调试器。

trap frame

trap frame是一个保存了R3进程内容的结构体,具体内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
6: kd> dt nt!_KTRAP_FRAME ffffbd8d`acc674a0
+0x000 P1Home : 0xffff990e`d2559080
+0x008 P2Home : 0xffffbd8d`acc67520
+0x010 P3Home : 0
+0x018 P4Home : 0
+0x020 P5 : 0
+0x028 PreviousMode : 1 ''
+0x028 InterruptRetpolineState : 0x1 ''
+0x029 PreviousIrql : 0 ''
+0x02a FaultIndicator : 0 ''
+0x02a NmiMsrIbrs : 0 ''
+0x02b ExceptionActive : 0x2 ''
+0x02c MxCsr : 0x1fa0
+0x030 Rax : 0x1004
+0x038 Rcx : 0x000000f0`786ff078
+0x040 Rdx : 0
+0x048 R8 : 0
+0x050 R9 : 0
+0x058 R10 : 0x000000f0`786ff078
+0x060 R11 : 0x000000f0`786ff078
+0x068 GsBase : 0x000000f0`784d6000
+0x068 GsSwap : 0x000000f0`784d6000
+0x070 Xmm0 : _M128A
+0x080 Xmm1 : _M128A
+0x090 Xmm2 : _M128A
+0x0a0 Xmm3 : _M128A
+0x0b0 Xmm4 : _M128A
+0x0c0 Xmm5 : _M128A
+0x0d0 FaultAddress : 0x00007fff`1adfc198
+0x0d0 ContextRecord : 0x00007fff`1adfc198
+0x0d8 Dr0 : 0
+0x0e0 Dr1 : 0
+0x0e8 Dr2 : 0
+0x0f0 Dr3 : 0
+0x0f8 Dr6 : 0
+0x100 Dr7 : 0
+0x0d8 ShadowStackFrame : 0
+0x0e0 Spare : [5] 0
+0x108 DebugControl : 0
+0x110 LastBranchToRip : 0
+0x118 LastBranchFromRip : 0
+0x120 LastExceptionToRip : 0
+0x128 LastExceptionFromRip : 0
+0x130 SegDs : 0
+0x132 SegEs : 0
+0x134 SegFs : 0
+0x136 SegGs : 0
+0x138 TrapFrame : 0
+0x140 NmiPreviousSpecCtrl : 0x786ff078
+0x144 NmiPreviousSpecCtrlPad : 0xf0
+0x140 Rbx : 0x000000f0`786ff078
+0x148 Rdi : 0x00000242`c8c80a60
+0x150 Rsi : 0
+0x158 Rbp : 0x000000f0`786ff099
+0x160 ErrorCode : 4
+0x160 ExceptionFrame : 4
+0x168 Rip : 0x00007fff`1e1c1314
+0x170 SegCs : 0x33
+0x172 Fill0 : 0 ''
+0x173 Logging : 0 ''
+0x174 Fill1 : [2] 0
+0x178 EFlags : 0x246
+0x17c Fill2 : 0
+0x180 Rsp : 0x000000f0`786fef88
+0x188 SegSs : 0x2b
+0x18a Fill3 : 0
+0x18c Fill4 : 0

它由R0维护

其作用看下一节

KiSystemCall64

上面说到syscall和sysret不会保存栈指针,在intel-IA32手册中说:这个步骤由操作系统来实现,指令只是用来切换CPL。

使用syscall会固定进入KiSystemCall64函数(由IA32_LSTAR加载),如果开启了内核页表隔离KPTI则会进入KiSystemCall64Shadow,其中Shadow函数在经过自己的检查和初始化(在这之中会进行对trap frame的读写)后进入KiSystemCall64的KiSystemServiceUser标签。

1
2
3
4
5
6
7
8
9
10
11
12
kd> rdmsr 0xc0000082
msr[c0000082] = fffff803`da88c040
kd> u fffff803`da88c040
nt!KiSystemCall64:
fffff803`da88c040 0f01f8 swapgs
fffff803`da88c043 654889242510000000 mov qword ptr gs:[10h],rsp
fffff803`da88c04c 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h]
fffff803`da88c055 6a2b push 2Bh
fffff803`da88c057 65ff342510000000 push qword ptr gs:[10h]
fffff803`da88c05f 4153 push r11
fffff803`da88c061 6a33 push 33h
fffff803`da88c063 51 push rcx

KiSystemCall64可以在ntoskrnl.exe,也就是Windows内核实现文件中查看。ntoskrnl.exe也可能是ntkrpamp.exe这说明cpu支持多核

KiSystemCall64首先进行了栈切换

1
2
3
4
5
6
7
swapgs
mov gs:10h,rsp
mov rsp,gs:1A8h

000 swapgs
000 mov gs:10h, rsp
000 mov rsp, gs:1A8h

swapgs切换了gs寄存器指向的内容,在r3中,gs指向TEB,而在内核中,其需要指向KPCR。KPCR地址记录在MSR寄存器的IA32_GS_BASE域。

然后,在内核栈上构造trap frame结构

1
2
3
4
5
000                 push    2Bh ; '+'
000 push qword ptr gs:10h
000 push r11
000 push 33h ; '3'
000 push rcx

然后是和shadow stack有关的检查,其中setssbsy标记或设置与影子栈相关的状态,rstorssp [rcx]saveprevssp恢复或保存影子栈指针。

1
2
3
4
5
6
000                 mov     rcx, gs:95A8h
000 test rcx, rcx
000 jz short loc_140429A3E
000 setssbsy
000 rstorssp qword ptr [rcx]
000 saveprevssp

然后继续填充_KTRAP_FRAME,似乎还等同于创建栈帧?

1
2
3
4
5
6
7
8
mov     rcx, r10
sub rsp, 8
push rbp
sub rsp, 158h
lea rbp, [rsp+190h+var_110]
mov [rbp+0C0h], rbx
mov [rbp+0C8h], rdi
mov [rbp+0D0h], rsi

然后是KeSmapEnabled检查,如果有,set AC flag,使得内核可以访问用户空间

1
2
3
4
5
6
7
test    byte ptr cs:KeSmapEnabled, 0FFh
jz short loc_140429A7F
test byte ptr [rbp+0F0h], 1
jz short loc_140429A7F
stac
loc_140429A7F:

继续填充_KTRAP_FRAME

1
2
3
4
5
6
7
8
9
10
11
mov     [rbp-50h], rax
mov [rbp-48h], rcx
mov [rbp-40h], rdx
mov rcx, gs:188h
mov rcx, [rcx+220h]
mov rcx, [rcx+9E0h]
mov gs:858h, rcx
mov cx, gs:850h
mov gs:852h, cx
mov cx, gs:860h
mov gs:854h, cx

似乎和分支预测有关

1
2
3
4
5
6
7
8
movzx   eax, word ptr gs:866h
cmp gs:864h, ax
jz short loc_140429AF5
mov gs:864h, ax
mov ecx, 48h ; 'H'
xor edx, edx
wrmsr
loc_140429AF5:

后面一堆add和call似乎是和硬件有关的东西

然后是shadow stack相关的内容

1
2
3
4
5
6
7
loc_140429C45:
add rsp, 8
mov eax, 0DADAh
test byte ptr gs:862h, 8
jz short loc_140429C60
mov al, 20h ; ' '
incsspq rax

分支预测相关以及通过各种检查跳转到KiSystemServiceUser

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
loc_140429C60:
test edx, 200h
jz short loc_140429C6D
call KiFlushBhbDuringTrapEntryOrExit
loc_140429C6D:
lfence
mov byte ptr gs:856h, 0
cmp cs:KiUserCetPl3SspCanonicalizeUpperMask, 0
jz short KiSystemServiceUser
mov ecx, 6A7h
rdmsr
cmp edx, 0
jz short KiSystemServiceUser
mov ecx, edx
and edx, cs:KiUserCetPl3SspCanonicalizeUpperMask
cmp edx, ecx
jz short KiSystemServiceUser
mov ecx, 6A7h
wrmsr

然后就进入了KiSystemServiceUser,准备调用。在这里,大致进行了优化读写,保存MXCSR寄存器,判断异步扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 KiSystemServiceUser:
mov byte ptr [rbp-55h], 2
mov rbx, gs:188h
prefetchw byte ptr [rbx+90h]
stmxcsr dword ptr [rbp-54h];sse处理
ldmxcsr dword ptr gs:180h;sse处理
cmp byte ptr [rbx+3], 0
mov word ptr [rbp+80h], 0
jz short loc_140429D48
test byte ptr [rbx+3], 3
mov [rbp-38h], r8
mov [rbp-30h], r9
jz short loc_140429CE4;不确定检查了什么,可能是检查调试,如果是调试状态执行后面的内容
call KiSaveDebugRegisterState
test byte ptr [rbx+3], 24h
jz short loc_140429D40;似乎是判断是否需要保存寄存器?不确定
mov [rbp-20h], r10
mov [rbp-28h], r10
movaps xmmword ptr [rbp-10h], xmm0
movaps xmmword ptr [rbp+0], xmm1
movaps xmmword ptr [rbp+10h], xmm2
movaps xmmword ptr [rbp+20h], xmm3
movaps xmmword ptr [rbp+30h], xmm4
movaps xmmword ptr [rbp+40h], xmm5
sti ;该指令恢复中断,之前进入时通过mask与屏蔽了中断效果
mov rcx, rsp
call PsSyscallProviderDispatch
cmp al, 1
jz short loc_140429D40
mov rax, [rbp-50h]
jl short loc_140429D31
mov ecx, 0C000001Ch
xor edx, edx
mov r8, [rbp+0E8h]
call KiExceptionDispatch
int 3 ; 主动触发int 3断点
test byte ptr [rbx+3], 4
jz KiSystemServiceExit
jmp KiSystemServiceExitPico
loc_140429D40:
mov r8, [rbp-38h]
mov r9, [rbp-30h]
loc_140429D48:
mov rax, [rbp-50h]
mov rcx, [rbp-48h]
mov rdx, [rbp-40h]
sti ;该指令恢复中断,之前进入时通过mask与屏蔽了中断效果

然后就是解析服务表了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
   mov     [rbx+88h], rcx
mov [rbx+80h], eax;从Trap_Frame中读出用户态值
db 66h, 66h, 66h, 66h, 66h, 66h;疑似会被识别未冗余前缀跳过执行
nop word ptr [rax+rax+00000000h]
KiSystemServiceStart: ; .data:0000000140C02EF0↓o
mov [rbx+90h], rsp
mov edi, eax
shr edi, 7
and edi, 20h;右移7再and20相当于取第12位,但是这里是and,所以edi等于0x20或者0
and eax, 0FFFh;取低三位

KiSystemServiceRepeat: ; CODE XREF: KiSystemCall64+C84↓j
lea r10, KeServiceDescriptorTable;非GUI线程使用
lea r11, KeServiceDescriptorTableShadow;GUI线程使用
test dword ptr [rbx+78h], 80h
jz short loc_140429DAE;如果不是GUI线程
test dword ptr [rbx+78h], 200000h
jz short loc_140429DAB;如果不是受限制的GUI线程
lea r11, KeServiceDescriptorTableFilter

loc_140429DAB: ; CODE XREF: KiSystemCall64+3A2↑j
mov r10, r11

loc_140429DAE: ; CODE XREF: KiSystemCall64+399↑j
cmp eax, [r10+rdi+10h]
jnb loc_14042A64B;eax低三位是否小于table+edi+0x10,应该是判断长度是否小于表长,如果大于直接调用退出的代码
mov r10, [r10+rdi]
movsxd r11, dword ptr [r10+rax*4];解密运算?*(*(table+0x20/0x0)+code&0xfff)
mov rax, r11
sar r11, 4
add r10, r11;最后是*(table+0x20/0x0)[*(*(table+0x20/0x0)+code&0xfff)>>4]的值,r10是例程地址
cmp edi, 20h ;判断GUI
jnz short loc_140429E20;如果不是,跳转
;感觉上,r10一开始是基指针,加rdi之后才是服务表


mov r11, [rbx+0F0h]

KiSystemServiceGdiTebAccess: ; DATA XREF: KiSystemServiceHandler+D↑o
cmp dword ptr [r11+1740h], 0
jz short loc_140429E20
mov [rbp-50h], rax
mov [rbp-48h], rcx
mov [rbp-40h], rdx
mov rbx, r8
mov rdi, r9
mov rsi, r10
mov ecx, 7
xor edx, edx
xor r8, r8
xor r9, r9
call PsInvokeWin32Callout
mov rax, [rbp-50h]
mov rcx, [rbp-48h]
mov rdx, [rbp-40h]
mov r8, rbx
mov r9, rdi
mov r10, rsi
nop dword ptr [rax]

loc_140429E20: ; CODE XREF: KiSystemCall64+3CE↑j
; KiSystemCall64+3DF↑j
and eax, 0Fh
jz KiSystemServiceCopyEnd;如果低四位是0跳转,似乎表明参数个数
shl eax, 3
lea rsp, [rsp-70h];分配新空间存参数,最多14个qword
lea rdi, [rsp+100h+var_E8]
mov rsi, [rbp+100h]
lea rsi, [rsi+20h]
test byte ptr [rbp+0F0h], 1
jz short loc_140429E60
cmp rsi, cs:MmUserProbeAddress
cmovnb rsi, cs:MmUserProbeAddress
nop dword ptr [rax+00000000h]

loc_140429E60: ; CODE XREF: KiSystemCall64+448↑j
lea r11, KiSystemServiceCopyEnd
sub r11, rax;减去参数个数左移3的值即*8,下面CopyStart里面每一个参数复制需要8字节
jmp r11
align 10h
KiSystemServiceCopyStart:
mov rax, [rsi+70h]
mov [rdi+70h], rax
mov rax, [rsi+68h]
mov [rdi+68h], rax
mov rax, [rsi+60h]
mov [rdi+60h], rax
mov rax, [rsi+58h]
mov [rdi+58h], rax
mov rax, [rsi+50h]
mov [rdi+50h], rax
mov rax, [rsi+48h]
mov [rdi+48h], rax
mov rax, [rsi+40h]
mov [rdi+40h], rax
mov rax, [rsi+38h]
mov [rdi+38h], rax
mov rax, [rsi+30h]
mov [rdi+30h], rax
mov rax, [rsi+28h]
mov [rdi+28h], rax
mov rax, [rsi+20h]
mov [rdi+20h], rax
mov rax, [rsi+18h]
mov [rdi+18h], rax
mov rax, [rsi+10h]
mov [rdi+10h], rax
mov rax, [rsi+8]
mov [rdi+8], rax
KiSystemServiceCopyEnd: ; CODE XREF: KiSystemCall64+423↑j
test cs:KiDynamicTraceMask, 1
jnz loc_14042A6E9;和trance有关,会记录CallSystemEntry
test dword ptr cs:PerfGlobalGroupMask+8, 40h
jnz loc_14042A75D;和PerfInfoLog有关
mov rax, r10
call rax;调用例程函数
nop dword ptr [rax]
loc_140429F08: ; CODE XREF: KiSystemCall64+D58↓j
inc dword ptr gs:2EB8h

执行完之后就是恢复寄存器,中间还调用了USERAPC,好像基本都是反着写的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
loc_140429F08:                          ; CODE XREF: KiSystemCall64+D58↓j
inc dword ptr gs:2EB8h
KiSystemServiceExit: ; CODE XREF: KiSystemCall64+335↑j
mov rbx, [rbp+0C0h]
mov rdi, [rbp+0C8h]
mov rsi, [rbp+0D0h]
mov r11, gs:188h

test byte ptr [rbp+0F0h], 1
jz loc_14042A30F;如果是内核的直接调用
mov rcx, cr8
or cl, [r11+24Ah]
or ecx, [r11+1E4h]
jnz loc_14042A6B5
cli
loc_140429F54: ; CODE XREF: KiSystemCall64+5BD↓j
mov rcx, gs:188h
test byte ptr [rcx+0C2h], 3
jz short loc_140429FBF
mov [rbp-50h], rax;将rax放入trap_frame.rax
xor eax, eax
mov [rbp-48h], rax
mov [rbp-40h], rax
mov [rbp-38h], rax
mov [rbp-30h], rax
mov [rbp-28h], rax
mov [rbp-20h], rax
pxor xmm0, xmm0
movaps xmmword ptr [rbp-10h], xmm0
movaps xmmword ptr [rbp+0], xmm0
movaps xmmword ptr [rbp+10h], xmm0
movaps xmmword ptr [rbp+20h], xmm0
movaps xmmword ptr [rbp+30h], xmm0
movaps xmmword ptr [rbp+40h], xmm0
mov ecx, 1
mov cr8, rcx
sti
call KiInitiateUserApc
cli
mov ecx, 0
mov cr8, rcx
mov rax, [rbp-50h]
jmp short loc_140429F54
loc_140429FBF: ; CODE XREF: KiSystemCall64+564↑j
test byte ptr gs:86Ch, 2
jz short loc_140429FD9
mov [rbp-50h], rax
xor ecx, ecx
call KiUpdateStibpPairing
mov rax, [rbp-50h]
loc_140429FD9: ; CODE XREF: KiSystemCall64+5C8↑j
mov rcx, gs:188h

test dword ptr [rcx], 8000000h
jz short loc_14042A029
mov [rbp-50h], rax
xor eax, eax
mov [rbp-48h], rax
mov [rbp-40h], rax
mov [rbp-38h], rax
mov [rbp-30h], rax
mov [rbp-28h], rax
mov [rbp-20h], rax
pxor xmm0, xmm0
movaps xmmword ptr [rbp-10h], xmm0
movaps xmmword ptr [rbp+0], xmm0
movaps xmmword ptr [rbp+10h], xmm0
movaps xmmword ptr [rbp+20h], xmm0
movaps xmmword ptr [rbp+30h], xmm0
movaps xmmword ptr [rbp+40h], xmm0
call KiRestoreSetContextState
loc_14042A029: ; CODE XREF: KiSystemCall64+5E8↑j
mov rcx, gs:188h

test dword ptr [rcx], 10000h
jz short loc_14042A056
mov [rbp-50h], rax
test byte ptr [rcx+2], 1
jz short loc_14042A052
call KiCopyCounters
mov rcx, gs:188h

loc_14042A052: ; CODE XREF: KiSystemCall64+642↑j
mov rax, [rbp-50h]

loc_14042A056: ; CODE XREF: KiSystemCall64+638↑j
ldmxcsr dword ptr [rbp-54h]
xor r10, r10
cmp word ptr [rbp+80h], 0
jz short loc_14042A0A8
mov [rbp-50h], rax
call KiRestoreDebugRegisterState
mov rax, gs:188h

mov rax, [rax+0B8h]
mov rax, [rax+3D8h]
or rax, rax
jz short loc_14042A0A4
cmp word ptr [rbp+0F0h], 33h ; '3'
jnz short loc_14042A0A4
mov r10, [rbp+0E8h]
mov [rbp+0E8h], rax

loc_14042A0A4: ; CODE XREF: KiSystemCall64+68A↑j
mov rax, [rbp-50h]

loc_14042A0A8: ; CODE XREF: KiSystemCall64+665↑j
mov rcx, gs:188h

bt dword ptr [rcx+74h], 16h
jnb short loc_14042A0E2
xor ecx, ecx
rdsspq rcx
mov r8, gs:95A8h
add r8, 8
cmp rcx, r8
jnz short loc_14042A0E2
mov rcx, gs:95A0h
rstorssp qword ptr [rcx]
saveprevssp
loc_14042A0E2: ; CODE XREF: KiSystemCall64+6B6↑j
mov [rbp-50h], rax
mov byte ptr gs:856h, 0
movzx eax, word ptr gs:86Ah
cmp gs:864h, ax
jz short loc_14042A115
mov gs:864h, ax
mov ecx, 48h ; 'H'
xor edx, edx
wrmsr
loc_14042A115: ; CODE XREF: KiSystemCall64+701↑j
btr word ptr gs:860h, 2

jnb short loc_14042A130
mov eax, 1
xor edx, edx
mov ecx, 49h ; 'I'
wrmsr

loc_14042A130: ; CODE XREF: KiSystemCall64+720↑j
btr word ptr gs:860h, 5
jnb loc_14042A278
call loc_14042A254;硬件相关检查

然后是结束部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
loc_14042A25D:                          ; CODE XREF: KiSystemCall64+74A↑p
add rsp, 8
mov eax, 0DADAh
test byte ptr gs:862h, 8

jz short loc_14042A278
mov al, 20h ; ' '
incsspq rax

loc_14042A278: ; CODE XREF: KiSystemCall64+73B↑j
; KiSystemCall64+86F↑j
test word ptr gs:860h, 80h
jz short loc_14042A291
xor eax, eax
xor edx, edx
mov ecx, 1
div rcx

loc_14042A291: ; CODE XREF: KiSystemCall64+883↑j
mov rax, [rbp-50h]
mov r8, [rbp+100h];RSP
mov r9, [rbp+0D8h];RBP
xor edx, edx
pxor xmm0, xmm0
pxor xmm1, xmm1
pxor xmm2, xmm2
pxor xmm3, xmm3
pxor xmm4, xmm4
pxor xmm5, xmm5
mov rcx, [rbp+0E8h];RIP
mov r11, [rbp+0F8h];EFLAG
test cs:KiKvaShadow, 1
jnz KiKernelSysretExit;如果有内核页表隔离KPTI
mov rbp, r9
mov rsp, r8
xor r9d, r9d
rdsspq r9
test r9, r9
jz short loc_14042A2F3
clrssbsy qword ptr [r9]
xor r9d, r9d

loc_14042A2F3: ; CODE XREF: KiSystemCall64+8E9↑j
test word ptr gs:860h, 100h

jz short loc_14042A309
verw word ptr gs:0A02Ah


loc_14042A309: ; CODE XREF: KiSystemCall64+8FE↑j
swapgs ;切换gs寄存器
sysret ;退出内核

loc_14042A30F: ; CODE XREF: KiSystemCall64+535↑j
;如果是内核的直接调用
mov rdx, [rbp+0B8h]
mov [r11+90h], rdx
mov dl, [rbp-58h]
mov [r11+232h], dl
cli
mov rsp, rbp
mov rbp, [rbp+0D8h]
mov rsp, [rsp+90h+arg_68]
sti
retn

SSDT

KeServiceDescriptorTable是系统服务描述符表,具体结构如下:

1
2
3
4
5
6
7
typedef struct _SERVICE_DESCIPTOR_TABLE
{
PULONG ServiceTableBase; // SSDT基址,8字节大小
PVOID ServiceCounterTableBase; // SSDT中服务被调用次数计数器,8字节大小
ULONGLONG NumberOfService; // SSDT服务函数的个数,8字节大小
PVOID ParamTableBase; // 系统服务参数表基址,8字节大小。实际指向的数组是以字节为单位的记录着对应服务函数的参数个数
}SSDTEntry, *PSSDTEntry;

其表中ServiceTableBase每一项均为一个ULONG,但是里面存的不是地址(因为只有32位)而是相对于ServiceTableBase的偏移。

由上一节的计算过程可得:

1
2
3
4
5
6
7
8
9
10
11
12
13
PVOID getFunc(DWORD code)
{
if(rdi==0){
code &= 0xfff;
DWORD64 pTableBase = KeServiceDescriptorTable[rdi];//实际就是0
PBYTE pBaseByte = pTableBase
PVOID pTargetFunc = pBaseByte[pTableBase[code]>>4];
return pTargetFunc;
}
else{
//GUI线程操作
}
}

参考

使用 SYSENTER 和 SYSEXIT 指令执行对系统过程的快速调用

x64内核实验5-API进0环_kisystemcall64

Windows内核逆向

SSDT(系统服务描述符表 system services descriptor table)