首页
社区
课程
招聘
[翻译]系统服务0和1去哪里了?
2020-9-20 20:28 9199

[翻译]系统服务0和1去哪里了?

2020-9-20 20:28
9199

Windows上的系统调用是通过Ntdll.dll来调用的。发生系统的调用的地方都会有一条syscall(x64)或者 sysenter(x86) cpu指令,比如说下面输出的NtCreateFile函数的反汇编

1
2
3
4
5
6
7
8
9
10
0: kd> u ntdll!NtCreateFile
ntdll!NtCreateFile:
00007ffa`f67fcb50 4c8bd1          mov     r10,rcx
00007ffa`f67fcb53 b855000000      mov     eax,55h
00007ffa`f67fcb58 f604250803fe7f01 test    byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
00007ffa`f67fcb60 7503            jne     ntdll!NtCreateFile+0x15 (00007ffa`f67fcb65)
00007ffa`f67fcb62 0f05            syscall
00007ffa`f67fcb64 c3              ret
00007ffa`f67fcb65 cd2e            int     2Eh
00007ffa`f67fcb67 c3              ret

其中最重要的指令是mov eax,55hsyscallEAX的值就是系统服务号,在这个例子中是0x55。syscall下面的另一条分支一般不会执行。syscall指令会转到系统服务派遣函数(负责调度到执行体里真正的系统调用实现)。我不会在这里进一步讨论细节,但最终EAX寄存器会被用来索引系统服务派遣表,每一个系统服务号都对应着一个实际的服务例程。

 

在64位系统上,SSDT可以通过内核调试器使用nt!KiServiceTable符号查看:

1
2
3
4
5
6
7
8
9
0: kd> dd nt!KiServiceTable
fffff803`4e224c50  fced7204 fcf77b00 02b9be02 0474a000
fffff803`4e224c60  01cf0a00 fda01f00 01c07705 01c3cc06
fffff803`4e224c70  02216f05 028b9401 028d8b00 01a9a400
fffff803`4e224c80  01e33200 01c2b900 028d2700 01ccbc00
fffff803`4e224c90  02227f01 01bfb001 02988600 01feb702
fffff803`4e224ca0  01a8a600 01e16f00 01d17501 01cf6402
fffff803`4e224cb0  022c2d02 01f57101 01fc9601 0289ff05
fffff803`4e224cc0  02298b00 028eb003 0236bd00 0461ab00

你可能认为SSDT中的值是一个指针,直接指向系统服务函数。(没错,x86系统上使用这个方案)。在64系统上,他是一个32位的值,用于表示与SSDT起始地址的偏移值。然而,这个偏移不包含最低4位(16进制里的最低位),他表示系统调用的参数个数。

 

让我们来看看这对于NtCreateFile是否成立。我们从用户模式看到他的服务号是0x55,为了得到实际的偏移,我们需要做一个简单的计算。

1
2
0: kd> dd nt!KiServiceTable+55*4 L1
fffff803`4e224da4  020ba907

现在我们要用这个偏移(去除低4位),加上SSDT地址应该就指向NtCreateFile了。

1
2
3
4
5
6
7
8
9
10
0: kd> u nt!KiServiceTable+020ba90
nt!NtCreateFile:
fffff803`4e4306e0 4881ec88000000  sub     rsp,88h
fffff803`4e4306e7 33c0            xor     eax,eax
fffff803`4e4306e9 4889442478      mov     qword ptr [rsp+78h],rax
fffff803`4e4306ee c744247020000000 mov     dword ptr [rsp+70h],20h
fffff803`4e4306f6 89442468        mov     dword ptr [rsp+68h],eax
fffff803`4e4306fa 4889442460      mov     qword ptr [rsp+60h],rax
fffff803`4e4306ff 89442458        mov     dword ptr [rsp+58h],eax
fffff803`4e430703 8b8424e0000000  mov     eax,dword ptr [rsp+0E0h]

确实如此!那么参数个数呢?低四位存储的值是7。这是NtCreateFile的原型(WDK里文档化的版本是ZwCreateFile):

1
2
3
4
5
6
7
8
9
10
11
12
13
NTSTATUS NtCreateFile(
  OUT PHANDLE           FileHandle,
  IN ACCESS_MASK        DesiredAccess,
  IN POBJECT_ATTRIBUTES ObjectAttributes,
  OUT PIO_STATUS_BLOCK  IoStatusBlock,
  IN PLARGE_INTEGER     AllocationSize,
  IN ULONG              FileAttributes,
  IN ULONG              ShareAccess,
  IN ULONG              CreateDisposition,
  IN ULONG              CreateOptions,
  IN PVOID              EaBuffer,
  IN ULONG              EaLength
);

可以明显看到,这里有11个参数,不是7个。为什么会出现这种差异呢?实际上这里存储的值的大小表示通过栈传递进来的参数个数。在x64调用约定中,前四个参数依次使用这几个寄存器:RCX,RDX,R8,R9来传递。

 

回到我们的博客的标题。这里让我们再次看看SSDT的前几项。

1
2
3
0: kd> dd nt!KiServiceTable
fffff803`4e224c50  fced7204 fcf77b00 02b9be02 0474a000
fffff803`4e224c60  01cf0a00 fda01f00 01c07705 01c3cc06

前两项看起来很特殊,有着较大的数值。让我们用刚才的逻辑验证一下第一个值(索引0)

1
2
3
0: kd> u nt!KiServiceTable+fced720
fffff803`5df12370 ??              ???
                                 ^ Memory access error in 'u nt!KiServiceTable+fced720'

很明显不对。因为这个值事实上是个负数(二进制补码),所以我们需要符号扩展到64bit,然后再进行加法运算(和之前一样移除低4位):

1
2
3
4
5
6
7
8
9
10
0: kd> u nt!KiServiceTable+ffffffff`ffced720
nt!NtAccessCheck:
fffff803`4df12370 4c8bdc          mov     r11,rsp
fffff803`4df12373 4883ec68        sub     rsp,68h
fffff803`4df12377 488b8424a8000000 mov     rax,qword ptr [rsp+0A8h]
fffff803`4df1237f 4533d2          xor     r10d,r10d
fffff803`4df12382 458853f0        mov     byte ptr [r11-10h],r10b
fffff803`4df12386 498943e8        mov     qword ptr [r11-18h],rax
fffff803`4df1238a 488b8424a0000000 mov     rax,qword ptr [rsp+0A0h]
fffff803`4df12392 498943e0        mov     qword ptr [r11-20h],rax

可以看到这个函数是NtAccessCheck。这个函数的实现低于SSDT的起始地址。让我们尝试用索引1再练习一次。

1
2
3
4
5
6
7
8
9
10
0: kd> u nt!KiServiceTable+ffffffff`ffcf77b0
nt!NtWorkerFactoryWorkerReady:
fffff803`4df1c400 4c8bdc          mov     r11,rsp
fffff803`4df1c403 49895b08        mov     qword ptr [r11+8],rbx
fffff803`4df1c407 49897318        mov     qword ptr [r11+18h],rsi
fffff803`4df1c40b 57              push    rdi
fffff803`4df1c40c 4883ec50        sub     rsp,50h
fffff803`4df1c410 498363d000      and     qword ptr [r11-30h],0
fffff803`4df1c415 33c0            xor     eax,eax
fffff803`4df1c417 498943d8        mov     qword ptr [r11-28h],rax

我们可以得到系统服务1:NtWorkerFactoryWorkerReady

 

对于那些喜欢Windbg脚本的人来说,可以写一个脚本来很好的显示所有的系统调用函数和他们的索引了。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回