原文
窗口
上的系统调用通过,每个由系统调用(x64
)或sysenter(x86)CPU
指令调用的NTDLL.dll
,如NTDLL
的NtCreateFile
的以下输出
所示:
这里
0:000> u
ntdll!NtCreateFile:
00007ffc`c07fcb50 4c8bd1 mov r10,rcx
00007ffc`c07fcb53 b855000000 mov eax,55h
00007ffc`c07fcb58 f604250803fe7f01 test byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
00007ffc`c07fcb60 7503 jne ntdll!NtCreateFile+0x15 (00007ffc`c07fcb65)
00007ffc`c07fcb62 0f05 syscall
00007ffc`c07fcb64 c3 ret
00007ffc`c07fcb65 cd2e int 2Eh
00007ffc`c07fcb67 c3 ret
EAX
的值是系统服务编号
(在本例中为0x55
).接着是系统调用指令
(测试的条件一般没有分支).
系统调用(syscall)
导致内核传输到负责分发到执行器
中的实际系统调用实现
的系统服务分发器例程
.最终,必须将EAX寄存器
作为,每个系统服务编号
(索引)都指向实际例程
的系统服务分发表
(SSDT
)的查找索引
.
在x64
版本的窗口
上,SSDT
在内核调试器中的nt!KiServiceTable
符号中:
lkd> dd nt!KiServiceTable
fffff804`13c3ec20 fced7204 fcf77b00 02b94a02 04747400
fffff804`13c3ec30 01cef300 fda01f00 01c06005 01c3b506
fffff804`13c3ec40 02218b05 0289df01 028bd600 01a98d00
fffff804`13c3ec50 01e31b00 01c2a200 028b7200 01cca500
fffff804`13c3ec60 02229b01 01bf9901 0296d100 01fea002
你可能期望SSDT
中的值是,直接指向系统服务
(这是x86
系统上使用的方案)的64
位指针.在x64
上,这些值为32
位,它们是SSDT
自身开头的偏移.
但是,偏移不包括最后十六进制数
字(4位):最后值
是系统调用的参数个数
.
来看看这是否适合NtCreateFile
.正如用户模式
,它的服务编号是0x55
的,因此要取得实际的偏移
,需要简单计算
:
kd> dd nt!KiServiceTable+55*4 L1
fffff804`13c3ed74 020b9207
现在,需要取(不带最后十六进制数
字的)此偏移,在SSDT
中添加它,它应该指向NtCreateFile
:
lkd> u nt!KiServiceTable+020b920
nt!NtCreateFile:
fffff804`13e4a540 4881ec88000000 sub rsp,88h
fffff804`13e4a547 33c0 xor eax,eax
fffff804`13e4a549 4889442478 mov qword ptr [rsp+78h],rax
fffff804`13e4a54e c744247020000000 mov dword ptr [rsp+70h],20h
事实上,这是NtCreateFile
.参数个数呢?存储的值
是7
.下面是NtCreateFile
的原型(在WDK
中记录为ZwCreateFile
):
NTSTATUS NtCreateFile(PHANDLE FileHandle,ACCESS_MASK DesiredAccess,POBJECT_ATTRIBUTES ObjectAttributes,PIO_STATUS_BLOCK IoStatusBlock,PLARGE_INTEGER AllocationSize,ULONG FileAttributes,ULONG ShareAccess,ULONG CreateDisposition,ULONG CreateOptions,PVOID EaBuffer,ULONG EaLength);
显然,有11
个参数,而不仅是7个
.为什么会有差异?存储的值
是使用栈传递的参数个数
.在x64
调用约定中,前4个参数
使用寄存器
传递:RCX,RDX,R8,R9
(按此顺序).
现在回到文章标题
.下面是SSDT
中的前几个项:
lkd> dd nt!KiServiceTable
fffff804`13c3ec20 fced7204 fcf77b00 02b94a02 04747400
fffff804`13c3ec30 01cef300 fda01f00 01c06005 01c3b506
前两个项的数字
要大得多.试对第一个值
(索引0
)应用相同逻辑:
kd> u nt!KiServiceTable+fced720
fffff804`2392c340 ??
???^ Memory access error in 'u nt!KiServiceTable+fced720'
(按二进制
补码)该值实际上是一个负值
,因此需要符号扩展
到64
位,然后加起来(如前省略最后十六进制数
字):
kd> u nt!KiServiceTable+ffffffff`ffced720
nt!NtAccessCheck:
fffff804`1392c340 4c8bdc mov r11,rsp
fffff804`1392c343 4883ec68 sub rsp,68h
fffff804`1392c347 488b8424a8000000 mov rax,qword ptr [rsp+0A8h]
这是NtAccessCheck
.该函数
的实现在比SSDT
自身更低的地址中.来试用1索引
执行相同练习:
kd> u nt!KiServiceTable+ffffffff`ffcf77b0
nt!NtWorkerFactoryWorkerReady:
fffff804`139363d0 4c8bdc mov r11,rsp
fffff804`139363d3 49895b08 mov qword ptr [r11+8],rbx
现在得到1
系统调用号:NtWorkerFactoryWorkerReady
.
对那些喜欢WinDbg
脚本的人,编写一个脚本
来很好地显示所有系统调用函数及其索引
.