这篇文章是根据作者Tesla.Angela 在 看雪论坛 发布的文章所写
这个 X86还是比较好实现的 但是 X64 就有些绕了 而且 比较麻烦吧 其实x64做的保护 听容易看出来的
他先把ssdt 搞成动态 然后 又把 ssdt 放入的地址搞成偏移地址 这个让我有点奇怪
我先发一下 作者 Tesla.Angela的原话
知道了这一套机制,HOOK SSDT 就很简单了,首先获得待 HOOK 函数的序号 Index,然后通过公式把自己的代理函数的地址转化为偏移地址,然后把偏移地 址的数据填入 ServiceTableBase[Index]。也许有些读者看到这里,已经觉得胜 利在望了,我当时也是如此。但实际上我在这里栽了个大跟头,整整郁闷了很长 时间!因为我低估了设计这套算法的工程师的智商,我没有考虑一个问题,为什 么 WIN64 的 SSDT 表存放地址的形式这么奇怪?只存放偏移地址,而不存放完整 地址?难道是为了节省内存?这肯定是不可能的,要知道现在内存白菜价。那么 不是为了节省内存,唯一的可能性就是要给试图挂钩 SSDT 的人制造麻烦!要知 道,WIN64 内核里每个驱动都不在同一个 4GB 里,而 4 字节的整数只能表示 4GB 的范围!所以无论你怎么修改这个值,都跳不出 ntoskrnl 的手掌心。如果你想 通过修改这个值来跳转到你的代理函数,那是绝对不可能的。因为你的驱动的地 址不可能跟 ntoskrnl 在同一个 4GB 里。然而,这位工程师也低估了我们中国人 的智商,在中国有两句成语,这位工程师一定没听过,叫“明修栈道,暗渡陈仓” 以及“上有政策,下有对策”。虽然不能直接用 4 字节来表示自己的代理函数所 在的地址,但是还是可以修改这个值的。要知道,ntoskrnl 虽然有很多地方的代 码通常是不会被执行的,比如 KeBugCheckEx。所以我的办法是:修改这个偏移地 址的值,使之跳转到 KeBugCheckEx,然后在 KeBugCheckEx 的头部写一个 12 字 节的 mov - jmp,这是一个可以跨越 4GB 的跳转,跳到我们的函数里!
其实 这里我现在还不是很了解 win64内核每个驱动都不在同一个4gb 这句话不是很了解
(今天问了一下 很多大佬 他们 和我说 可以理解成和虚拟进程差不多)
64位实际只用了48位 这个是我通过问别人得出来的 这个是根据系统而言来定的 然后 我们就可以
直接撸代码 了 但是 问了很多人 也没有个回答 等以后懂了在添上去把
其实这里 作者 Tesla.Angela 做的方法 还是比较 可以的
因为 KeBugCheckEx 如果执行的话也是蓝屏 那么作者的想法就是 也不用修复了
只要把 我们hook的那个函数 给填回去就好了
然后我们 在x86 只需改寄存器的值 就能够 把空间改成可写 这里作者 Tesla.Angela 也是 写了两个函数
KIRQL WPOFFx64()
{
KIRQL irql=KeRaiseIrqlToDpcLevel();
UINT64 cr0=__readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return irql;
}
void WPONx64(KIRQL irql)
{
UINT64 cr0=__readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
}
下面就是代码 ~~~~~~ 看起来 下章的 UNHOOK 好像更难一些~~~
#include <ntddk.h>
#include <windef.h>
#pragma intrinsic(__readmsr)
typedef struct _SYSTEM_SERVICE_TABLE{
PLONG ServiceTableBase;
PVOID ServiceCounterTableBase;
ULONGLONG NumberOfServices;
PVOID ParamTableBase;
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;
PSYSTEM_SERVICE_TABLE ssdt;
typedef NTSTATUS(__fastcall *NTTERMINATEPROCESS)(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus);
NTKERNELAPI
UCHAR *
PsGetProcessImageFileName(PEPROCESS Process);
NTTERMINATEPROCESS NtTerminateProcess = NULL;
ULONG addr = 0;
ULONGLONG GetSsdt()
{
PUCHAR startaddr = (PUCHAR)__readmsr(0xc0000082);//这里是两个下划线
PUCHAR Endaddr = startaddr + 0x500;
PUCHAR i = NULL;
UCHAR b1, b2, b3;
ULONG temp = 0;
ULONGLONG addr = 0;
for (i = startaddr; i < Endaddr; i++)
{
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;
return addr;
}
}
return 0;
}
KIRQL OFF()
{
KIRQL f = KeRaiseIrqlToDpcLevel();
UINT64 cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return f;
}
VOID ON(KIRQL f)
{
UINT64 cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(f);
}
ULONG GetOffsetAddress(ULONGLONG FuncAddr)
{
ULONG dwtmp = 0;
PULONG ServiceTableBase = NULL;
ServiceTableBase = (PULONG)ssdt->ServiceTableBase;
dwtmp = (ULONG)(FuncAddr - (ULONGLONG)ServiceTableBase);
return dwtmp << 4;
}
ULONGLONG GetFunctionAddress(ULONGLONG index)
{
PULONG ServiceTableBase = NULL;
LONG Temp;
ULONGLONG temp,ret=0;
ServiceTableBase = (PULONG)ssdt->ServiceTableBase;
Temp = ServiceTableBase[index];
Temp = Temp >> 4;
ret = (LONGLONG)ServiceTableBase + (LONGLONG)Temp;
return ret;
}
NTSTATUS __fastcall Fake_NtTerminateProcess(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus)
{
PEPROCESS Process;
NTSTATUS status = ObReferenceObjectByHandle(ProcessHandle, 0 ,* PsProcessType, KernelMode, &Process, NULL);
KdPrint(("hook已经开始"));
if (NT_SUCCESS(status))
{
if (!_stricmp(PsGetProcessImageFileName(Process), "loaddrv.exe") || !_stricmp(PsGetProcessImageFileName(Process), "calc.exe"))
{
return STATUS_ACCESS_DENIED;
}
else
return NtTerminateProcess(ProcessHandle, ExitStatus);
}
else
return STATUS_ACCESS_DENIED;
}
VOID FuckKeBugCheckEx()//跳转 HOOK 函数
{
KIRQL irql;
ULONGLONG fun;
fun = (ULONGLONG)Fake_NtTerminateProcess;
UCHAR jmp[] = "\x48\xB8\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xE0";
memcpy(jmp + 0x2, &fun, 8);
irql = OFF();
memset(KeBugCheckEx, 0x90, 15);
memcpy(KeBugCheckEx, jmp, 12);
ON(irql);
}
VOID HookSsdt()
{
KIRQL f;
PULONG ServiceTableBase = NULL;
NtTerminateProcess = (NTTERMINATEPROCESS)GetFunctionAddress(41);
FuckKeBugCheckEx();
ServiceTableBase = ssdt->ServiceTableBase;
addr = ServiceTableBase[41];
f = OFF();
ServiceTableBase[41] = GetOffsetAddress((ULONGLONG)KeBugCheckEx);
ON(f);
KdPrint(("KeBugCheckEx: %llx", (ULONGLONG)KeBugCheckEx));
KdPrint(("New_NtTerminateProcess: %llx", GetFunctionAddress(41)));
}
VOID UnHookSsdt()
{
KIRQL f;
PULONG ServiceTableBase = NULL;
ServiceTableBase = ssdt->ServiceTableBase;
f = OFF();
ServiceTableBase[41] =addr;
ON(f);
KdPrint(("NtTerminateProcess: %llx", GetFunctionAddress(41)));
}
VOID DriverUnload(PDRIVER_OBJECT driver)
{
UnHookSsdt();
KdPrint(("goodbye"));
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
ULONGLONG i = 0;
PSYSTEM_SERVICE_TABLE add=NULL;
ULONGLONG addxr = GetSsdt();
if (addxr== 0)
{
KdPrint(("大哥对不起 没有找到ssdt!\n"));
return STATUS_SUCCESS;
}
ssdt = (PSYSTEM_SERVICE_TABLE)addxr;//得到ssdt的表
HookSsdt();
DriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}