64x 获取 SSDT
在 WIN32 下,第一个问题就根本不是问题,因为 KeServiceDescriptorTable 直接被导
出了。但是 WIN64 下 KeServiceDescriptorTable 没有被导出。所以我们必须搜索得到它的
地址。首先反汇编一下 KiSystemCall64:
lkd> uf KiSystemCall64 Flow analysis was incomplete, some code may be missing nt!KiSystemCall64: fffff800`03cc7ec0 0f01f8 swapgs fffff800`03cc7ec3 654889242510000000 mov qword ptr gs:[10h],rsp fffff800`03cc7ecc 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h] fffff800`03cc7ed5 6a2b push 2Bh fffff800`03cc7ed7 65ff342510000000 push qword ptr gs:[10h] fffff800`03cc7edf 4153 push r11 fffff800`03cc7ee1 6a33 push 33h fffff800`03cc7ee3 51 push rcx fffff800`03cc7ee4 498bca mov rcx,r10 fffff800`03cc7ee7 4883ec08 sub rsp,8 fffff800`03cc7eeb 55 push rbp fffff800`03cc7eec 4881ec58010000 sub rsp,158h fffff800`03cc7ef3 488dac2480000000 lea rbp,[rsp+80h] fffff800`03cc7efb 48899dc0000000 mov qword ptr [rbp+0C0h],rbx fffff800`03cc7f02 4889bdc8000000 mov qword ptr [rbp+0C8h],rdi fffff800`03cc7f09 4889b5d0000000 mov qword ptr [rbp+0D0h],rsi fffff800`03cc7f10 c645ab02 mov byte ptr [rbp-55h],2 fffff800`03cc7f14 65488b1c2588010000 mov rbx,qword ptr gs:[188h] fffff800`03cc7f1d 0f0d8bd8010000 prefetchw [rbx+1D8h] fffff800`03cc7f24 0fae5dac stmxcsr dword ptr [rbp-54h] fffff800`03cc7f28 650fae142580010000 ldmxcsr dword ptr gs:[180h] fffff800`03cc7f31 807b0300 cmp byte ptr [rbx+3],0 fffff800`03cc7f35 66c785800000000000 mov word ptr [rbp+80h],0 fffff800`03cc7f3e 0f848c000000 je nt!KiSystemCall64+0x110 (fffff800`03cc7fd0) 【省略大量无关代码】 nt!KiSystemCall64+0x110: fffff800`03cc7fd0 fb sti fffff800`03cc7fd1 48898be0010000 mov qword ptr [rbx+1E0h],rcx fffff800`03cc7fd8 8983f8010000 mov dword ptr [rbx+1F8h],eax fffff800`03cc7fde 4889a3d8010000 mov qword ptr [rbx+1D8h],rsp fffff800`03cc7fe5 8bf8 mov edi,eax fffff800`03cc7fe7 c1ef07 shr edi,7 fffff800`03cc7fea 83e720 and edi,20h fffff800`03cc7fed 25ff0f0000 and eax,0FFFh nt!KiSystemServiceRepeat: fffff800`03cc7ff2 4c8d1547782300 lea r10,[nt!KeServiceDescriptorTable (fffff800`03eff840)] fffff800`03cc7ff9 4c8d1d80782300 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`03eff880)] fffff800`03cc8000 f7830001000080000000 test dword ptr [rbx+100h],80h fffff800`03cc800a 4d0f45d3 cmovne r10,r11 fffff800`03cc800e 423b441710 cmp eax,dword ptr [rdi+r10+10h] fffff800`03cc8013 0f83e9020000 jae nt!KiSystemServiceExit+0x1a7 (fffff800`03cc8302) nt!KiSystemServiceRepeat+0x27: fffff800`03cc8019 4e8b1417 mov r10,qword ptr [rdi+r10] fffff800`03cc801d 4d631c82 movsxd r11,dword ptr [r10+rax*4] fffff800`03cc8021 498bc3 mov rax,r11 fffff800`03cc8024 49c1fb04 sar r11,4 fffff800`03cc8028 4d03d3 add r10,r11 fffff800`03cc802b 83ff20 cmp edi,20h fffff800`03cc802e 7550 jne nt!KiSystemServiceGdiTebAccess+0x49 (fffff800`03cc8080) 【省略大量无关代码】
最终,我们在 KiSystemServiceRepeat 里找到了 KeServiceDescriptorTable 的踪影。
可能会有人问,为什么不直接反汇编 KiSystemServiceRepeat 呢?原因很简单,因为你找
不到 KiSystemServiceRepeat 的地址。虽然 KiSystemCall64 和 KiSystemServiceRepeat 都
没有由 ntoskrnl.exe 导出,但是我们能直接找到 KiSystemCall64 的地址。怎么找?直接
读取指定的 msr 得出。很多人只听过通用寄存器和调试寄存器,其实还有很多其它的寄存
器(你想想再古老的 586 CPU 的一级缓存都有 32KB 呢,而现在的 AMD64 CPU 的每个核心的
一级缓存正好有 64KB)。Msr 的中文全称是就是“特别模块寄存器”(model specific
register),它控制 CPU 的工作环境和标示 CPU 的工作状态等信息(例如倍频、最大 TDP、
危险警报温度),它能够读取,也能够写入,但是无论读取还是写入,都只能在 Ring 0 下
进行。我们通过读取 C0000082 寄存器,能够得到 KiSystemCall64 的地址,然后从
KiSystemCall64 的地址开始,往下搜索 0x500 字节左右(特征码是 4c8d15),就能得到
KeServiceDescriptorTable 的地址了。同理,我们换一下特征码(4c8d1d),就能获得
KeServiceDescriptorTableShadow 的地址了。
先用 WINDBG 证明一下(输入 rdmsr c0000082):

代码实现如下:
ULONGLONG MyGetKeServiceDescriptorTable64()
{
PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082);
PUCHAR EndSearchAddress = StartSearchAddress + 0x500;
PUCHAR i = NULL;
UCHAR b1=0,b2=0,b3=0;
ULONG templong=0;
ULONGLONG addr=0;
for(i=StartSearchAddress;i<EndSearchAddress;i++)
{
if( MmIsAddressValid(i) && MmIsAddressValid(i+1) && MmIsAddressValid(i+2) )
{
b1=*i;
b2=*(i+1);
b3=*(i+2);
if( b1==0x4c && b2==0x8d && b3==0x15 ) //4c8d15
{
memcpy(&templong,i+3,4);
addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
return addr;
}
}
}
return 0;
}
浙公网安备 33010602011771号