以下内容参考黑客防线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)); }