最近在学习64位驱动,涉及到了SSDT的知识,结果发现64位下的SSDT和32位下的SSDT有所不同。
开始发现64位下的KeServiceDescriptorTable是未导出的函数。首先要找到KeServiceDescriptorTable的地址。
方法1:
读取c0000082寄存器
[cpp] view plain copy kd> rdmsr c0000082 msr[c0000082] = fffff800`03e82640
它记录了KiSystemCall64,
之后反汇编KiSystemCall64。
如下:
[cpp] view plain copy kd> uf KiSystemCall64 nt!KiSystemCall64: fffff800`03e82640 0f01f8 swapgs fffff800`03e82643 654889242510000000 mov qword ptr gs:[10h],rsp fffff800`03e8264c 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h] fffff800`03e82655 6a2b push 2Bh fffff800`03e82657 65ff342510000000 push qword ptr gs:[10h] fffff800`03e8265f 4153 push r11 fffff800`03e82661 6a33 push 33h fffff800`03e82663 51 push rcx fffff800`03e82664 498bca mov rcx,r10 fffff800`03e82667 4883ec08 sub rsp,8 fffff800`03e8266b 55 push rbp fffff800`03e8266c 4881ec58010000 sub rsp,158h fffff800`03e82673 488dac2480000000 lea rbp,[rsp+80h] fffff800`03e8267b 48899dc0000000 mov qword ptr [rbp+0C0h],rbx fffff800`03e82682 4889bdc8000000 mov qword ptr [rbp+0C8h],rdi fffff800`03e82689 4889b5d0000000 mov qword ptr [rbp+0D0h],rsi fffff800`03e82690 c645ab02 mov byte ptr [rbp-55h],2 fffff800`03e82694 65488b1c2588010000 mov rbx,qword ptr gs:[188h] fffff800`03e8269d 0f0d8bd8010000 prefetchw [rbx+1D8h] fffff800`03e826a4 0fae5dac stmxcsr dword ptr [rbp-54h] fffff800`03e826a8 650fae142580010000 ldmxcsr dword ptr gs:[180h] fffff800`03e826b1 807b0300 cmp byte ptr [rbx+3],0 fffff800`03e826b5 66c785800000000000 mov word ptr [rbp+80h],0 fffff800`03e826be 0f848c000000 je nt!KiSystemCall64+0x110 (fffff800`03e82750) ---------------------------------------省略若干代码---------------------------------------------- nt!KiSystemCall64+0x110: fffff800`03e82750 fb sti fffff800`03e82751 48898be0010000 mov qword ptr [rbx+1E0h],rcx fffff800`03e82758 8983f8010000 mov dword ptr [rbx+1F8h],eax fffff800`03e8275e 4889a3d8010000 mov qword ptr [rbx+1D8h],rsp fffff800`03e82765 8bf8 mov edi,eax fffff800`03e82767 c1ef07 shr edi,7 fffff800`03e8276a 83e720 and edi,20h fffff800`03e8276d 25ff0f0000 and eax,0FFFh nt!KiSystemServiceRepeat: fffff800`03e82772 4c8d15c7202300 lea r10,[nt!KeServiceDescriptorTable (fffff800`040b4840)] fffff800`03e82779 4c8d1d00212300 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`040b4880)] fffff800`03e82780 f7830001000080000000 test dword ptr [rbx+100h],80h fffff800`03e8278a 4d0f45d3 cmovne r10,r11 fffff800`03e8278e 423b441710 cmp eax,dword ptr [rdi+r10+10h] fffff800`03e82793 0f83e9020000 jae nt!KiSystemServiceExit+0x1a7 (fffff800`03e82a82)
如上红色代码。在KiSystemServiceRepeat里面找到了KeServiceDescriptorTable
方法2:
打开WinDbg
[cpp] view plain copy kd> u nt!zwclose l10 nt!ZwClose: fffff800`03e7bdc0 488bc4 mov rax,rsp fffff800`03e7bdc3 fa cli fffff800`03e7bdc4 4883ec10 sub rsp,10h fffff800`03e7bdc8 50 push rax fffff800`03e7bdc9 9c pushfq fffff800`03e7bdca 6a10 push 10h fffff800`03e7bdcc 488d059d300000 lea rax,[nt!KiServiceLinkage (fffff800`03e7ee70)] fffff800`03e7bdd3 50 push rax fffff800`03e7bdd4 b80c000000 mov eax,0Ch fffff800`03e7bdd9 e9e2670000 jmp nt!KiServiceInternal (fffff800`03e825c0) fffff800`03e7bdde 6690 xchg ax,ax
跟踪nt!KiServiceInternal
[cpp] view plain copy kd> u KiServiceInternal l20 nt!KiServiceInternal: fffff800`03e825c0 4883ec08 sub rsp,8 fffff800`03e825c4 55 push rbp fffff800`03e825c5 4881ec58010000 sub rsp,158h fffff800`03e825cc 488dac2480000000 lea rbp,[rsp+80h] fffff800`03e825d4 48899dc0000000 mov qword ptr [rbp+0C0h],rbx fffff800`03e825db 4889bdc8000000 mov qword ptr [rbp+0C8h],rdi fffff800`03e825e2 4889b5d0000000 mov qword ptr [rbp+0D0h],rsi fffff800`03e825e9 fb sti fffff800`03e825ea 65488b1c2588010000 mov rbx,qword ptr gs:[188h] fffff800`03e825f3 0f0d8bd8010000 prefetchw [rbx+1D8h] fffff800`03e825fa 0fb6bbf6010000 movzx edi,byte ptr [rbx+1F6h] fffff800`03e82601 40887da8 mov byte ptr [rbp-58h],dil fffff800`03e82605 c683f601000000 mov byte ptr [rbx+1F6h],0 fffff800`03e8260c 4c8b93d8010000 mov r10,qword ptr [rbx+1D8h] fffff800`03e82613 4c8995b8000000 mov qword ptr [rbp+0B8h],r10 fffff800`03e8261a 4c8d1d3d010000 lea r11,[nt!KiSystemServiceStart (fffff800`03e8275e)] fffff800`03e82621 41ffe3 jmp r11 fffff800`03e82624 666666666666660f1f840000000000 nop word ptr [rax+rax] fffff800`03e82633 66666666660f1f840000000000 nop word ptr [rax+rax] nt!KiSystemCall64: fffff800`03e82640 0f01f8 swapgs fffff800`03e82643 654889242510000000 mov qword ptr gs:[10h],rsp fffff800`03e8264c 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h] fffff800`03e82655 6a2b push 2Bh fffff800`03e82657 65ff342510000000 push qword ptr gs:[10h] fffff800`03e8265f 4153 push r11 fffff800`03e82661 6a33 push 33h fffff800`03e82663 51 push rcx fffff800`03e82664 498bca mov rcx,r10 fffff800`03e82667 4883ec08 sub rsp,8 fffff800`03e8266b 55 push rbp fffff800`03e8266c 4881ec58010000 sub rsp,158h fffff800`03e82673 488dac2480000000 lea rbp,[rsp+80h]
继续跟踪nt!KiSystemServiceStart
[cpp] view plain copy kd> u KiSystemServiceStart nt!KiSystemServiceStart: fffff800`03e8275e 4889a3d8010000 mov qword ptr [rbx+1D8h],rsp fffff800`03e82765 8bf8 mov edi,eax fffff800`03e82767 c1ef07 shr edi,7 fffff800`03e8276a 83e720 and edi,20h fffff800`03e8276d 25ff0f0000 and eax,0FFFh nt!KiSystemServiceRepeat: fffff800`03e82772 4c8d15c7202300 lea r10,[nt!KeServiceDescriptorTable (fffff800`040b4840)] fffff800`03e82779 4c8d1d00212300 lea r11,[nt!KeServiceDescriptorTableShadow (fffff800`040b4880)] fffff800`03e82780 f7830001000080000000 test dword ptr [rbx+100h],80h
同样找到了KeServiceDescriptorTable 的踪影。
之后我们看一下KeServiceDescriptorTable:
[cpp] view plain copy kd> dq KeServiceDescriptorTable fffff800`040b4840 fffff800`03e84300 00000000`00000000 fffff800`040b4850 00000000`00000191 fffff800`03e84f8c fffff800`040b4860 00000000`00000000 00000000`00000000 fffff800`040b4870 00000000`00000000 00000000`00000000 fffff800`040b4880 fffff800`03e84300 00000000`00000000 fffff800`040b4890 00000000`00000191 fffff800`03e84f8c fffff800`040b48a0 fffff960`001b1f00 00000000`00000000 fffff800`040b48b0 00000000`0000033b fffff960`001b3c1c因为根据公式:
newAddress-oldAddress-7(指令长度)=bytearray
SSDT = fffff800`03e82772 + 237847 + 7 = fffff800`040b4840
所以KeServiceDescriptorTable基地址:fffff800`040b4840
我们编写程序的时候可以匹配一下4c8d15c7202300 这个特种就可以找到KeServiceDescriptorTable了。
接下来就演示算出SSDT表中第一个函数(也就是INDEX=0)的地址(这里为NtMapUserPhysicalPagesScatter),之后的以此类推就可以了。
64位和32位算法有所不同,64位需要得到 **KeServiceDescriptorTable = 040d9a00
下面红色标记。
[cpp] view plain copy kd> dd fffff800`03e84300 fffff800`03e84300 040d9a00 02f55c00 fff6ea00 02e87805 fffff800`03e84310 031a4a06 03116a05 02bb9901 02b4f200 fffff800`03e84320 0312cc40 03dd7400 02c84700 02e7d100 fffff800`03e84330 02f68100 02e02301 02dd0601 02d96100 fffff800`03e84340 02df4602 02f18600 02ad0500 02cefe01 fffff800`03e84350 02d01d02 02f69902 03101101 0323ca01 fffff800`03e84360 0455c305 02ed29c0 02b2e703 ffec1d00 fffff800`03e84370 043c2800 02f51040 02c52c01 03126c00
公式:ServiceTableBase[Index] >> 4 + ServiceTableBase
ServiceTableBase[Index] = ServiceTableBase + Index * 4
即:
040d9a00 >> 4 = 40D9A0 + *KeServiceDescriptorTable = FFFFF80004291CA0
如下:
[cpp] view plain copy kd> u FFFFF80004291CA0 nt!NtMapUserPhysicalPagesScatter: fffff800`04291ca0 48895c2408 mov qword ptr [rsp+8],rbx fffff800`04291ca5 4c89442418 mov qword ptr [rsp+18h],r8 fffff800`04291caa 55 push rbp fffff800`04291cab 56 push rsi fffff800`04291cac 57 push rdi fffff800`04291cad 4154 push r12 fffff800`04291caf 4155 push r13 fffff800`04291cb1 4156 push r14
对比XT:
结果正确。
接下来用C实现以下找SSDT基址:
[cpp] view plain copy VOID GetSSDTBaseAddr() { 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) { memcpy(&templong,i+3,4); //核心部分 //kd> db fffff800`03e8b772 //fffff800`03e8b772 4c 8d 15 c7 20 23 00 4c-8d 1d 00 21 23 00 f7 83 L... #.L...!#... //templong = 002320c7 ,i = 03e8b772, 7为指令长度 addr = (ULONGLONG)templong + (ULONGLONG)i + 7; break; } } } DbgPrint("b1 = %0x\n",b1); DbgPrint("b2 = %0x\n",b2); DbgPrint("b3 = %0x",b3); DbgPrint("templong = %0x\n",templong); DbgPrint("addr = %0x\n",addr); }