现在根据实例来配合,现在要得到NtOpenProcess的内存地址
用 winDebug 得到 SSDT这个表的首地址是 0x80505480
进入这个地址看看里面的内容是什么
0: kd> dd poi[KeServiceDescriptorTable]
地址 0 1 2 3
80505480 805a565c 805f243e 805f5c74 805f2470
4 5 6 7
80505490 805f5cae 805f24a6 805f5cf2 805f5d36
8 9 10 11
805054a0 80616d1e 80617a60 805ed83c 805ed494
805054b0 805d5bae 805d5b5e 80617344 805b6fe2
805054c0 80616960 805a9ae6 805b15f6 805d7672
805054d0 8050289c 80617a52 80577b0a 80539c34
805054e0 8060ff2e 805bd55c 805f61ae 80624cf0
805054f0 805fa6c2 805a5d4a 80624f44 805a55fc
看到 0x80505480 这个地址存的第0个地址是 0x805A565C
用 Kerne Detective 这个工具看看这个是什么API
第0个是NtAcceptConnectPort 这个API
可以看到 WinDebug 得到的数据 和 KD 一样
用工具可以很容易找到SSDT的基地址 和 索引号以及NtAPI的内存地址
用编程实现 (精华):
要检查 SSDT 指定的NtAPI有没有被HOOK 就要先 获取到现在的NtAPI地址、获取原来的NtAPI的内存地址,然后将两个地址相比较即可。
获取现在NtAPI的内存地址的具体流程
首先应该得到SSDT的首地址 即 基址
然后将ServiceTableBase 的内存首地址+索引号*4 得到存储着相应索引号内核NtAPI现在的内存地址的地址(ServiceTableBase的偏移地址)
读取该地址得到现在的NtAPI内存地址
获取原来的NtAPI的内存地址的具体流程
通过 MmGetSystemRoutineAddress 可以得到原来的NtAPI 地址
主要是 获取现在的NtAPI比较费心思去理解,只要理解了这个流程,也就理解了SSDT表的结构了。
看看下面的代码以及注释就能理解SSDT表的结构了
左边是用KD工具得到 NOtOpenProcess 内存地址 右边是用WinDebug得到
下图是通过编程得到 NtOpenProcess 内存地址:
代码:
ypedef struct _ServiceDescriptorTable {
PVOID ServiceTableBase; //System Service Dispatch Table 的基地址
PVOID ServiceCounterTable;
//包含着SSDT 中每个服务被调用次数的计数器。这个计数器一般由sysenter 更新。
unsigned int NumberOfServices;//由ServiceTableBase 描述的服务的数目。
PVOID ParamTableBase; //包含每个系统服务参数字节数表的基地址-系统服务参数表
}*PServiceDescriptorTable;
extern PServiceDescriptorTable KeServiceDescriptorTable;
ULONG GetNt_CurAddr() //获取当前SSDT_NtOpenProcess的现在地址
{
LONG *SSDT_Adr,SSDT_NtOpenProcess_Cur_Addr,t_addr;
KdPrint(("驱动成功被加载中.............................\n\n"));
KdPrint(("********************** 计算现在的地址**********************\n\n"));
//读取SSDT表中索引值为x7A的函数
//poi(poi(KeServiceDescriptorTable)+0x7a*4)
t_addr=(LONG)KeServiceDescriptorTable->ServiceTableBase; // 得到ServiceTableBase 的地址
KdPrint(("[得到ServiceTableBase 的基址] \n当前ServiceTableBase地址为0x%X \n\n",t_addr));
// 将该地址里面的内容+ 索引号* 4 就能得到相应索引号内核NtAPI现在的内存地址0x7A为NtOpenProcess在SSDT的索引
SSDT_Adr=(PLONG)(t_addr+0x7A*4);
KdPrint(("[将ServiceTableBase 的内存首地址+索引号*4 \n得到存储着相应索引号内核NtAPI现在的内存地址的地址(ServiceTableBase的偏移地址)]\n"));
KdPrint(("SSDT首地址0x%X + 0x7A * 4= 0x%X 这个地址0x%X 存储着索引号为0x7A 的NtOpenProcess内存地址\n\n", t_addr, SSDT_Adr, SSDT_Adr));
SSDT_NtOpenProcess_Cur_Addr=*SSDT_Adr;
KdPrint(("[读取0x%X 得到NtOpenProcess 函数现在的内存地址] 现在的NtOpenProcess 内存地址为0x%X \n\n",SSDT_Adr, SSDT_NtOpenProcess_Cur_Addr));
KdPrint(("********************** 计算完毕***************************\n\n"));
// 汇编
/*
__asm
{ int 3
push ebx
push eax
mov ebx,KeServiceDescriptorTable
mov ebx,[ebx] //表的基地址 取 KeServiceDescriptorTable 的地址 t_addr=(LONG)KeServiceDescriptorTable->ServiceTableBase;
mov eax,0x7a
shl eax,2//0x7A*4 //imul eax,eax,4//shl eax,2
add ebx,eax//[KeServiceDescriptorTable]+0x7A*4
mov ebx,[ebx] // SSDT_NtOpenProcess_Cur_Addr=*SSDT_Adr; // 取出该地址中存储的NtOpenProcess 函数的地址
mov SSDT_NtOpenProcess_Cur_Addr,ebx
pop eax
pop ebx
}
*/
return SSDT_NtOpenProcess_Cur_Addr; // 将获得的地址返回
}
ULONG GetNt_OldAddr()
{
UNICODE_STRING Old_NtOpenProcess;
ULONG Old_Addr;
KdPrint(("********************** 计算原来的地址**********************\n\n"));
RtlInitUnicodeString(&Old_NtOpenProcess,L"NtOpenProcess");
Old_Addr=(ULONG)MmGetSystemRoutineAddress(&Old_NtOpenProcess);//取得NtOpenProcess的地址
KdPrint(("用MmGetSystemRoutineAddress 取得原来NtOpenProcess 的地址为0x%X\n\n",Old_Addr));
KdPrint(("********************** 计算完毕****************************\n\n"));
return Old_Addr;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING B) //TYPEDEF LONG NTSTATUS
{
ULONG cur,old;
cur=GetNt_CurAddr();// 得到现在的NtAPI 地址
old=GetNt_OldAddr();// 得到原来的NtAPI 地址
if (cur!=old)
KdPrint(("NtOpenProcess被HOOK了"));
else
KdPrint(("NtOpenProcess 没有被HOOK"));
}