网上也有很多方法定位到ssdt表,其中较多的文章都是通过查找msr(c0000082)寄存器定位KiSystemCall64,然后通过搜索特诊码的方式找到KiSystemServiceRepeat,这里记录着KeServiceDescriptorTable和KeServiceDescriptorTableShadow的地址,为了方便理解这里使用WinDbg找一下KiSystemCall64和KiSystemServiceRepeat的地址
在往下的位置可以找到KiSystemServiceRepeat这个地址,这里就可以直接看到KeServiceDescriptorTable表的地址了。
那么现在问题来了,我们是否可以在win10 1903 下使用同样的方法定位到KeServiceDescriptorTable,答案是不行的(当然可能是我测试的环境的问题),rdmsr c0000082得到的地址是nt!KiSystemCall64Shadow的地址,如下图所示
看一下我们需要的地址在哪,可以明显的看出KiSystemServiceRepeat的地址在KiSystemCall64Shadow的前面,可能有的朋友就想说,既然在前面那往前搜不就行了吗?
观察一下使用msr寄存器的搜索特诊码的方式,搜索的是4c 8d 15,我们使用同样的方式搜索特诊码,得到这样的结果
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
可以从上图中看出有大量符合要求的位置被收出来了,但是这样的搜索特征码是不适合的,还有一种,通过在_strnicmp函数到KdDebuggerNotPresent之间搜索8b f8 c1 ef 07 83 e7 20 25 ff 0f 00 00的方式定位,这里通过该特诊码能够定位到指定的函数,如下图所示
那么我们是选择记录c0000082继续往上搜索特诊码8b f8 c1 ef 07 83 e7 20 25 ff 0f 00 00还是说搜索在_strnicmp函数到KdDebuggerNotPresent之间的特诊码?
ULONGLONG GetKeServiceDescriptorTable64()
{
char KiSystemServiceStart_pattern[13] =
"\x8B\xF8\xC1\xEF\x07\x83\xE7\x20\x25\xFF\x0F\x00\x00";
ULONGLONG CodeScanStart = (ULONGLONG)&_strnicmp;
ULONGLONG CodeScanEnd = (ULONGLONG)&KdDebuggerNotPresent;
ULONGLONG i, tbl_address, b;
for (i = 0; i < CodeScanEnd - CodeScanStart; i++)
{
if (!memcmp((char*)(ULONGLONG)CodeScanStart +i,
(char*)KiSystemServiceStart_pattern,13))
{
for (b = 0; b < 50; b++)
{
tbl_address = ((ULONGLONG)CodeScanStart+i+b);
if (*(USHORT*) ((ULONGLONG)tbl_address ) == (USHORT)0x8d4c)
return ((LONGLONG)tbl_address +7) + *(LONG*)(tbl_address +3);
}
}
}
return 0;
}
由于上面的代码已经被前人写出来了,所以这里我选择借鉴前人的代码(其实就是抄),你以为这样就结束了?当然没有,如果你使用这份代码去定位那么肯定是定位不到,为什么呀?难道KiSystemServiceRepeat函数不在_strnicmp函数到KdDebuggerNotPresent之间了吗?当然不是,它肯定在,但是如果你直接这样写,你得到的_strnicmp的地址是不正确的,为了证明一下,我写了下面的代码
可以从上图中看到我们得到了两个地址,第一个是直接获取函数地址,第二个是我通过导出表动态得到的地址,为了验证哪个地址是正确的,我们使用windbg看一下
可以看到第一个地址定位到的是我自己的代码的位置,之后尝试在原代码中查找_strnicmp的定义,发现它直接跳到了string.h头文件中,那么可能是这种情况(我自己猜的啊,直接写这个函数的地址使用的不是内核函数导出的地址,而是调用string.h)所以直接写得到的地址是不正确的,可以尝试使用API MmGetSystemRoutineAddress得到函数的地址。
这里我自己写了一个查找导出函数地址的函数,目的是由于当前虽然得到SSDT表,但是不知道哪个序号对应哪个函数呀,所以这里需要得到ntdll,之后根据ntdll的调用号让序号和函数名称对应起来