以下內容參考黑客防線2012合訂本第294頁
其實沒什么好說的,直接上代碼:
ssdt的結構,和win32差不多,但是要注意這里的指針類型不能用ULONG替代,如果要非要替代應該用ULONGLONG,原因就不說了.
//SSDT的結構 typedef struct _SystemServiceDescriptorTable { PVOID ServiceTableBase; PVOID ServiceCounterTableBase; ULONGLONG NumberOfService; PVOID ParamTableBase; }SystemServiceTable, *PSystemServiceTable; PSystemServiceTable KeServiceDescriptorTable;
獲取上面的結構的地址的代碼;
ULONGLONG GetKeSeviceDescriptorTable64() { /* 思路是讀取0xC0000082 這個寄存器的值是KiSystemCall64函數地址,然后通過特征碼搜索即可 ssdt特征碼是 0x4c8d15 接着就是ssdt的地址值的偏移了,然后通過公式: 真實地址 = 當前地址+當前指令長度+偏移 得到ssdt地址 找shadow ssdt地址類似 */ PUCHAR startSearchAddress = (PUCHAR)__readmsr(0xC0000082); PUCHAR endSearchAddress = startSearchAddress + 0x500; PUCHAR i = 0; UCHAR b1 = 0, b2 = 0, b3 = 0; ULONG temp = 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(&temp, i + 3, 4); addr = (ULONGLONG)temp + (ULONGLONG)i + 7;//加上指令長度 KdPrint(("find ssdt is %p\n", addr)); return addr; } } } KdPrint(("find ssdt error\n")); return 0; }
遍歷所有Native API 地址:
void througnAllServiceFuncAddr() { ULONG dwTemp = 0; PULONG ServiceTableBase = 0; ULONG i = 0; for ( i = 0; i < KeServiceDescriptorTable->NumberOfService; i++) { if (MmIsAddressValid(KeServiceDescriptorTable->ServiceTableBase)) { ServiceTableBase = (PULONG)KeServiceDescriptorTable->ServiceTableBase; dwTemp = ServiceTableBase[i]; dwTemp = dwTemp >> 4; DbgPrint("the %dth func addr is %p!\n", i,\ ((ULONGLONG)dwTemp + (ULONGLONG)ServiceTableBase) & 0xffffffff0fffffff); } else { DbgPrint("ServiceTableBase is fault!\n"); return 0; } } }
測試結果:
windbg查看的結果:
以ZwOpenProcess為例:
ida中發現他的id是0x23 也就是 35 對應 測試結果是 fffff8000419b038
windbg結果:
測試無誤.
但是如果想hook是比win32麻煩很多的, 因為ServiceTableBase這個數組里面的元素只有4字節,在win32下自然能夠遍歷整個內存空間,
但是在win64下,內存空間有16T(限制為44bit尋址) 完全可以一個驅動占一個4GB空間,還有大量空間用不到. 所以直接修改這個index根本
夠不着我們的驅動函數地址. 所以可以通過先跳轉到一個跳板函數,這個跳板函數的地址在系統nt模塊中,也就是在4GB范圍內,然后修改那個函數
的首地址為jmp 我們的驅動hook函數 就能實現hook了.
比如使用KeBugCheckEx這個函數, 這個函數的功能是在系統掛掉的時候才會調用的函數, 因此可以作為跳板,當然如果能找到其他閑置的
內存空間也可以作為跳板.
整理一下思路,下面貼代碼.
ssdt hook的一個流程:
1.先調用GetKeSeviceDescriptorTable64給KeServiceDescriptorTable全局變量賦值,也就是找到
ssdt
2. 調用GetSSDTFuncAddrById得到目標函數地址並保存到全局變量real_NtTerminateProcess
3. 在函數Fuck_KeBugCheckEx中修改掉KeBugCheckEx代碼前12字節作為跳板
4. 得到目標函數的偏移並保存到全局變量real_NtTerminateProcessOffset
5. 計算KeBugCheckEx 函數的偏移並寫入到 ssdt表中
還原hook:
1.直接將保存的目標函數偏移寫入到ssdt表中即可
這里無需還原KeBugCheckEx 函數,因為這里本來就不會執行到,如果執行到了也藍屏死機了
KIRQL WPOFFx64() //類似win32關閉內存寫保護 { KIRQL irql = KeRaiseIrqlToDpcLevel(); UINT64 cr0 = __readcr0(); cr0 &= 0xfffffffffffeffff; __writecr0(cr0); _disable(); return irql; } void WPONx64(KIRQL irql)//類似win32開啟內存寫保護 { UINT64 cr0 = __readcr0(); cr0 |= 0x10000; _enable(); __writecr0(cr0); KeLowerIrql(irql); } ULONGLONG GetSSDTFuncAddrById(ULONG id) { ULONG dwTemp = 0; PULONG ServiceTableBase = 0; if (MmIsAddressValid(KeServiceDescriptorTable->ServiceTableBase)) { ServiceTableBase = (PULONG)KeServiceDescriptorTable->ServiceTableBase; dwTemp = ServiceTableBase[id]; dwTemp = dwTemp >> 4; //return ((ULONGLONG)dwTemp + (ULONGLONG)ServiceTableBase) & 0xffffffff0fffffff; return ((ULONGLONG)dwTemp + (ULONGLONG)ServiceTableBase); } else { DbgPrint("ServiceTableBase is fault!\n"); return 0; } } ULONG GetOffsetBySSDTFuncAddress(ULONGLONG funcAddr) { ULONG dwtemp = 0; PULONG ServiceTableBase = 0; ServiceTableBase = (PULONG)KeServiceDescriptorTable->ServiceTableBase; dwtemp = (ULONG)(funcAddr - (ULONGLONG)ServiceTableBase); return dwtemp << 4; } NTSTATUS __fastcall Fuck_NtTerminateProcess( IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus ) { PEPROCESS pe; NTSTATUS status; status = ObReferenceObjectByHandle(ProcessHandle, 0, *PsProcessType, KernelMode, &pe, 0); DbgPrint("enter Fuck_NtTerminateProcess!!!\n"); if (!NT_SUCCESS(status)) { DbgPrint("ObReferenceObjectByHandle failed !!!\n"); return real_NtTerminateProcess(ProcessHandle, ExitStatus); } if (!_stricmp(PsGetProcessImageFileName(pe),"calc.exe")) { return STATUS_ACCESS_DENIED; } return real_NtTerminateProcess(ProcessHandle, ExitStatus); } void Fuck_KeBugCheckEx() { KIRQL irql; ULONGLONG myFunc = (ULONGLONG)Fuck_NtTerminateProcess; UCHAR jmp_code[] = "\x48\xB8\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xE0"; memcpy(jmp_code + 2, &myFunc, 8); irql = WPOFFx64(); memset(KeBugCheckEx, 0x90, 15);//填充15個nop memcpy(KeBugCheckEx, jmp_code, 12); WPONx64(irql); } void hookSSDT64() { ULONGLONG dwtemp = 0; PULONG ServiceTableBase = 0; KIRQL irql; //UNICODE_STRING funcName; //RtlInitUnicodeString(&funcName, L"NtTerminateProcess"); real_NtTerminateProcess = GetSSDTFuncAddrById(NtTerminateProcessId); //2 DbgPrint("real_NtTerminateProcess is %p\n", real_NtTerminateProcess); //DbgPrint("search real_NtTerminateProcess is %p\n", MmGetSystemRoutineAddress(&funcName)); Fuck_KeBugCheckEx(); //3 ServiceTableBase = (PULONG)KeServiceDescriptorTable->ServiceTableBase; real_NtTerminateProcessOffset = ServiceTableBase[NtTerminateProcessId];//4 //進行hook 5 irql = WPOFFx64(); ServiceTableBase[NtTerminateProcessId] = GetOffsetBySSDTFuncAddress((ULONGLONG)KeBugCheckEx); WPONx64(irql); DbgPrint("KeBugCheckEx: %p\n", (ULONGLONG)KeBugCheckEx); DbgPrint("NtTerminateProcess: %p\n", GetSSDTFuncAddrById(NtTerminateProcessId)); } void unHookSSDT64() { KIRQL irql; ULONGLONG dwtemp = 0; PULONG ServiceTableBase = 0; DbgPrint("enter unHookSSDT64\n "); ServiceTableBase = (PULONG)KeServiceDescriptorTable->ServiceTableBase; irql = WPOFFx64(); ServiceTableBase[NtTerminateProcessId] = real_NtTerminateProcessOffset; WPONx64(irql); DbgPrint("KeBugCheckEx: %p\n", (ULONGLONG)KeBugCheckEx); DbgPrint("NtTerminateProcess: %p\n", GetSSDTFuncAddrById(NtTerminateProcessId)); }