現在根據實例來配合,現在要得到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"));
}