二探win10 x64 SSDT
0x0 C0000082的通解
背景介绍
在上一篇学习笔记中,我们曾说过,在win10 x64 1809专业版上
通过C0000082 MSR寄存器,我所得到的地址并非是nt!KiSystemCall64,而是nt!KiSystemCall64Shadow。
因此,我的权宜之计是读取地址后取一个偏移量将开始进行特征码搜索的位置重新定位到合理的位置,再在其中进行搜索,当然,这显然不是一个有效的方式,待后续解决。
详见上篇文章:初探win10 x64 SSDT(驱动学习笔记五)
为此,我们在此篇文章中来解决这个问题。
寻找SSDT
- 首先我们还是通过c0000082找到nt!KiSystemCall64的地址,并查看其反汇编,在其中并没有直接找到SSDT的地址。
1: kd> rdmsr c0000082
msr[c0000082] = fffff804`2f7e2140
1: kd> u fffff804`2f7e2140
nt!KiSystemCall64Shadow:
fffff804`2f7e2140 0f01f8 swapgs
fffff804`2f7e2143 654889242510700000 mov qword ptr gs:[7010h],rsp
fffff804`2f7e214c 65488b242500700000 mov rsp,qword ptr gs:[7000h]
fffff804`2f7e2155 650fba24251870000001 bt dword ptr gs:[7018h],1
fffff804`2f7e215f 7203 jb nt!KiSystemCall64Shadow+0x24 (fffff804`2f7e2164)
fffff804`2f7e2161 0f22dc mov cr3,rsp
fffff804`2f7e2164 65488b242508700000 mov rsp,qword ptr gs:[7008h]
nt!KiSystemCall64ShadowCommon:
fffff804`2f7e216d 6a2b push 2Bh
fffff804`2f7e216f 65ff342510700000 push qword ptr gs:[7010h]
fffff804`2f7e2177 4153 push r11
fffff804`2f7e2179 6a33 push 33h
fffff804`2f7e217b 51 push rcx
fffff804`2f7e217c 498bca mov rcx,r10
fffff804`2f7e217f 4883ec08 sub rsp,8
fffff804`2f7e2183 55 push rbp
fffff804`2f7e2184 4881ec58010000 sub rsp,158h
...
fffff804`2f7e236a 4883c408 add rsp,8
fffff804`2f7e236e e8eeffffff call nt!KiSystemCall64ShadowCommon+0x1f4 (fffff804`2f7e2361)
fffff804`2f7e2373 4883c408 add rsp,8
fffff804`2f7e2377 0faee8 lfence
fffff804`2f7e237a 65c604255308000000 mov byte ptr gs:[853h],0
fffff804`2f7e2383 e9e35ce9ff jmp nt!KiSystemServiceUser (fffff804`2f67806b)
fffff804`2f7e2388 c3 ret
fffff804`2f7e2389 0000 add byte ptr [rax],al
fffff804`2f7e238b 0000 add byte ptr [rax],al
...
- 通过对反汇编的观察,我们发现了一个找到SSDT的途径,即我们可以通过nt!KiSystemCall64ShadowCommon中的KiSystemServiceUser找到SSDT,我们查看其反汇编如下:
1: kd> u KiSystemServiceUser
nt!KiSystemServiceUser:
fffff804`2f67806b c645ab02 mov byte ptr [rbp-55h],2
fffff804`2f67806f 65488b1c2588010000 mov rbx,qword ptr gs:[188h]
fffff804`2f678078 0f0d8b90000000 prefetchw [rbx+90h]
fffff804`2f67807f 0fae5dac stmxcsr dword ptr [rbp-54h]
fffff804`2f678083 650fae142580010000 ldmxcsr dword ptr gs:[180h]
fffff804`2f67808c 807b0300 cmp byte ptr [rbx+3],0
fffff804`2f678090 66c785800000000000 mov word ptr [rbp+80h],0
...
fffff804`2f678154 48898b88000000 mov qword ptr [rbx+88h],rcx
fffff804`2f67815b 898380000000 mov dword ptr [rbx+80h],eax
fffff804`2f678161 666666666666660f1f840000000000 nop word ptr [rax+rax]
nt!KiSystemServiceStart:
fffff804`2f678170 4889a390000000 mov qword ptr [rbx+90h],rsp
fffff804`2f678177 8bf8 mov edi,eax
fffff804`2f678179 c1ef07 shr edi,7
fffff804`2f67817c 83e720 and edi,20h
fffff804`2f67817f 25ff0f0000 and eax,0FFFh
nt!KiSystemServiceRepeat:
fffff804`2f678184 4c8d15f5663900 lea r10,[nt!KeServiceDescriptorTable (fffff804`2fa0e880)]
fffff804`2f67818b 4c8d1deee73700 lea r11,[nt!KeServiceDescriptorTableShadow (fffff804`2f9f6980)]
fffff804`2f678192 f7437880000000 test dword ptr [rbx+78h],80h
fffff804`2f678199 7413 je nt!KiSystemServiceRepeat+0x2a (fffff804`2f6781ae)
fffff804`2f67819b f7437800002000 test dword ptr [rbx+78h],200000h
fffff804`2f6781a2 7407 je nt!KiSystemServiceRepeat+0x27 (fffff804`2f6781ab)
fffff804`2f6781a4 4c8d1d55e83700 lea r11,[nt!KeServiceDescriptorTableFilter (fffff804`2f9f6a00)]
fffff804`2f6781ab 4d8bd3 mov r10,r11
fffff804`2f6781ae 413b443a10 cmp eax,dword ptr [r10+rdi+10h]
fffff804`2f6781b3 0f832c050000 jae nt!KiSystemServiceExitPico+0x160 (fffff804`2f6786e5)
fffff804`2f6781b9 4d8b143a mov r10,qword ptr [r10+rdi]
fffff804`2f6781bd 4d631c82 movsxd r11,dword ptr [r10+rax*4]
fffff804`2f6781c1 498bc3 mov rax,r11
fffff804`2f6781c4 49c1fb04 sar r11,4
fffff804`2f6781c8 4d03d3 add r10,r11
fffff804`2f6781cb 83ff20 cmp edi,20h
fffff804`2f6781ce 7550 jne nt!KiSystemServiceGdiTebAccess+0x49 (fffff804`2f678220)
fffff804`2f6781d0 4c8b9bf0000000 mov r11,qword ptr [rbx+0F0h]
nt!KiSystemServiceGdiTebAccess:
fffff804`2f6781d7 4183bb4017000000 cmp dword ptr [r11+1740h],0
...
- 由此,我们可以得到了一个获取SSDT的思路
代码实现
由于目前C0000082会出现两种情况,我们有必要观察下这二者的区别:
KiSystemCall64的汇编代码如下:
1: kd> u KiSystemCall64
nt!KiSystemCall64:
fffff804`2f677e40 0f01f8 swapgs
fffff804`2f677e43 654889242510000000 mov qword ptr gs:[10h],rsp
fffff804`2f677e4c 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h]
fffff804`2f677e55 6a2b push 2Bh
fffff804`2f677e57 65ff342510000000 push qword ptr gs:[10h]
fffff804`2f677e5f 4153 push r11
fffff804`2f677e61 6a33 push 33h
fffff804`2f677e63 51 push rcx
...
KiSystemCall64Shadow的汇编代码如下
1: kd> u KiSystemCall64Shadow
nt!KiSystemCall64Shadow:
fffff804`2f7e2140 0f01f8 swapgs
fffff804`2f7e2143 654889242510700000 mov qword ptr gs:[7010h],rsp
fffff804`2f7e214c 65488b242500700000 mov rsp,qword ptr gs:[7000h]
fffff804`2f7e2155 650fba24251870000001 bt dword ptr gs:[7018h],1
fffff804`2f7e215f 7203 jb nt!KiSystemCall64Shadow+0x24 (fffff804`2f7e2164)
fffff804`2f7e2161 0f22dc mov cr3,rsp
fffff804`2f7e2164 65488b242508700000 mov rsp,qword ptr gs:[7008h]
nt!KiSystemCall64ShadowCommon:
fffff804`2f7e216d 6a2b push 2Bh
...
不难发现,这两个函数的特征区别比较明显,KiSystemCall64是将rsp放在了gs段偏移为10h的位置上,而 KiSystemCall64Shadow是将rsp放在了gs段偏移为7010h的位置,通过这个特征,我们可以很容易判断从msr寄存器上获取到的地址到底是KiSystemCall64的地址还是是KiSystemCall64Shadow的地址,核心代码如下:
PUCHAR msr = (PUCHAR)__readmsr(0xC0000082);
if (*(msr + 0x9) == 0x00)
{
PUCHAR pKiSystemCall64 = msr;
PUCHAR EndSearchAddress = pKiSystemCall64 + 0x500;//通常搜索范围在500字节就够了
KeServiceDescriptorTable = SearchforKeSeriviceDescriptorTable(pKiSystemCall64, EndSearchAddress);
return KeServiceDescriptorTable;
}
else if (*(msr + 0x9) == 0x70)
{
PUCHAR pKiSystemCall64Shadow = msr;
PUCHAR EndSearchAddress = pKiSystemCall64Shadow + 0x500;
PUCHAR i = NULL;
INT Temp = 0;
for (i = pKiSystemCall64Shadow; i < EndSearchAddress; i++)
{
//fffff804`3fb46383 e9e35ce9ff jmp nt!KiSystemServiceUser (fffff804`3f9dc06b)
//fffff804`3fb46388 c3 ret
if (MmIsAddressValid(i) && MmIsAddressValid(i + 5))
{
if (*i==0xe9&&*(i+5)==0xc3)
{
memcpy(&Temp, i + 1, 4);
PUCHAR pKiSystemServiceUser = Temp + (i + 5); //计算pKiSystemServiceUser的地址
PUCHAR EndSearchAddress = pKiSystemServiceUser + 0x500;
KeServiceDescriptorTable = SearchforKeSeriviceDescriptorTable(pKiSystemServiceUser, EndSearchAddress);
return KeServiceDescriptorTable;
}
}
}
}
至此,我们完成了上一篇文章所说的相对来说较为通用的寻找SSDT的后续方案。但需要注意的是,不同版本的操作系统仍然可能存在差异,本文的方法适用于win10 1809 x64专业版,其他版本仍需测试验证。但是最重要的仍然是寻找SSDT的思路,这也是写下此片文章的初衷,欢迎大家讨论勘误,也请大神轻喷。
To be continue…