Win64 driver开发中遇到的第二个问题是如何通过SSDT表引用系统服务内核函数。
MS在64系统中引入了全新的PatchGuard技术,使得原本在32位下可轻易hook的SSDT表在64位系统中无法再patch(会引发BSOD).
当然,目前我并非是要hook SSDT表,而是需要使用到SSDT表中的一个未导出的系统服务内核函数。在32位driver中可以通过extern "C" 外部声明来直接获得SSDT表KeServiceDescriptorTable的引用。在64位driver下虽然KeServiceDescriptorTable仍然被ntoskrnl.exe导出,但驱动程序已无法通过extern “C"外部声明来获得。因此目前的做法只能是通过特征码搜索找出KeServiceDescriptorTable的地址。
KiSystemServiceRepeat()函数对于KeServiceDe script orTable有直接的引用(win7 64为例)
nt!KiSystemServiceRepeat:
fffff800`0268eff2 4c8d1547782300 lea r10,[nt!KeServiceDescriptorTable (fffff800`028c6840)]
fffff800`0268eff9 4c8d1d80782300 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`028c6880)]
然而KiSystemServiceRepeat()本身就是一个未导出函数,要找到KiSystemServiceRepeat()本身就不是一件容易的事。因此目前方法采用了通过另一个导出函数KeAddSystemServiceTable()来搜索KeServiceDescriptorTable的地址, 导出函数地址可以通过MmGetSystemRoutineAddress()来获得
[WINXP 32]
nt!KeAddSystemServiceTable+0xb:
805a1319 8b4518 mov eax,dword ptr [ebp+18h]
805a131c c1e004 shl eax,4
805a131f 83b800c7558000 cmp dword ptr nt!KeServiceDescriptorTable (8055c700)[eax],0
805a1326 7551 jne nt!KeAddSystemServiceTable+0x6b (805a1379)
nt!KeAddSystemServiceTable+0x1a:
805a1328 8d88c0c65580 lea ecx,nt!KeServiceDescriptorTableShadow (8055c6c0)[eax]
805a132e 833900 cmp dword ptr [ecx],0
[WIN7 64]
nt!KeAddSystemServiceTable+0x9:
fffff800`02a00cd9 4c8bd0 mov r10,rax
fffff800`02a00cdc 4c8d1d1dd3c1ff lea r11,[nt!MmIsSessionAddress <PERF> (nt+0x0) (fffff800`0261e000)]
fffff800`02a00ce3 49c1e205 shl r10,5
fffff800`02a00ce7 4b83bc1a40882a0000 cmp qword ptr [r10+r11+2A8840h],0
fffff800`02a00cf0 7553 jne nt!KeAddSystemServiceTable+0x75 (fffff800`02a00d45)
nt!KeAddSystemServiceTable+0x22:
fffff800`02a00cf2 4b83bc1a80882a0000 cmp qword ptr [r10+r11+2A8880h],0
对比32位系统,可以发现该函数会判断KeServiceDescriptorTable是否为0. 在win7 64位下KeServiceDescriptorTable地址其实隐藏在如下两条指令中
fffff800`02a00cdc 4c8d1d1dd3c1ff lea r11,[nt!MmIsSessionAddress <PERF> (nt+0x0) (fffff800`0261e000)]
fffff800`02a00ce7 4b83bc1a40882a0000 cmp qword ptr [r10+r11+2A8840h],0
查询Intel® 64 and IA-32 Architectures Software Developer's Manual,解释如下两条指令:
fffff800`02a00cdc 4c8d1d1dd3c1ff lea r11,[nt!MmIsSessionAddress <PERF> (nt+0x0) (fffff800`0261e000)]
LEA – Load Effective Address
in qwordregister 0100 1RXB : 1000 1101 : modA qwordreg r/m
4 c 8 d 1d 1dd3c1ff
计算出基地址是EIP+ff3c1d=fffff800`02a00ce3+ffc1d31d=fffff800`0261e000
fffff800`02a00ce7 4b83bc1a40882a0000 cmp qword ptr [r10+r11+2A8840h],0
CMP – Compare Two Operands
immediate8 with memory64 0100 1RXB 1000 0011 : mod 111 r/m : imm8
4 b 8 3 bc 40882a00 00
偏移地址就是2a8840
所以计算出来的KeServiceDescriptorTable地址就是fffff800`0261e000+2a8840=fffff800`028c6840
kd> dd KeServiceDescriptorTable
fffff800`028c6840 02690b00 fffff800 00000000 00000000
在实际的driver中,只需要将lea(0x8d4c)和cmp(0x834b)作为特征码,就可以通过内存搜索的方式来获取KeServiceDescriptorTable的地址了
经过测试,在2k以后各版本win64(不包含win8)上均正常工作