64位下的SSDT HOOK与32位不同.由于SSDT服务表里面存放的只是函数的偏移,并且大小只有2的28次方.
无法直接跳转到指定的函数地址处,所以这里采用了先跳转到BugCheckEx函数,然后在BugCheckEx函数开头跳转到自己的处理函数中.
BugCheckEx充当了一个跳板的作用.
代码在WIN7 64 7600版本中测试通过,卸载函数没有还原BugCheckEx函数的12字节 有需要自己去还原一下吧.
注意在测试的时候点击进程,不要直接点应用程序图标.
#include<ntddk.h>
#include <ntstatus.h>
KIRQL WPOFF()
{
KIRQL OldIrql = KeRaiseIrqlToDpcLevel();
//ULONG_PTR 这个类型在x86上是32位 x64就是64位
ULONG_PTR cr0 = __readcr0();
#ifdef _X86_
cr0 &= 0xfffeffff;
#else
cr0 &= 0xfffffffffffeffff;
#endif
_disable(); //关闭中断
__writecr0(cr0); //关闭写保护位
return OldIrql;
}
VOID WPON(KIRQL irql)
{
ULONG_PTR cr0 = __readcr0();
cr0 |= 0x10000;
__writecr0(cr0); //恢复写保护位
_enable(); //恢复中断
KeLowerIrql(irql);
}
typedef struct _KSYSTEM_SERVICE_TABLE {
PULONG ServiceTableBase; //函数表的基址
PULONG ServiceCounterTableBase;
ULONG NumberOfServices;//系统服务表的limit
PUCHAR ParamTableBase; //函数参数的个数
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;
//在64位系统下获取SSDT服务表的基址
PKSYSTEM_SERVICE_TABLE GetSSDT64Base()
{
PKSYSTEM_SERVICE_TABLE pServiceTable = NULL;
PCHAR Base;
PCHAR FuncAddr;
LONG offset;
LONG funoffset;
//读取KiSystemCall64的地址
Base = (PCHAR)__readmsr(0xc0000082);
//偏移313是dff9地址,-4就是服务表偏移
Base += 313;
//获得服务表偏移
offset = *(PLONG)(Base - 4);
//服务表的地址=base+offset
pServiceTable = (PKSYSTEM_SERVICE_TABLE)(Base + offset);
return pServiceTable;
}
//定义函数指针
typedef NTSTATUS(_stdcall *Real_ZwTerminateProcess)(
HANDLE ProcessHandle,
NTSTATUS ExitStatus);
Real_ZwTerminateProcess pReal_ZwTerminateProcess;
ULONG OriginalOffset;
VOID DriverUnload(PDRIVER_OBJECT pdriver) {
PKSYSTEM_SERVICE_TABLE pServiceTable = GetSSDT64Base();
KIRQL OldIrql = WPOFF();
pServiceTable->ServiceTableBase[41] = OriginalOffset;
WPON(OldIrql);
DbgPrint("卸载成功");
}
NTKERNELAPI
PUCHAR
PsGetProcessImageFileName(
IN PEPROCESS Process
);
NTSTATUS _stdcall MyZwTerminateProcess(
IN HANDLE ProcessHandle OPTIONAL,
IN NTSTATUS ExitStatus
)
{
PCHAR pStrProcName;
PEPROCESS pEProcess;
ANSI_STRING strProcName;
// 通过进程句柄来获得该进程所对应的 FileObject 对象,由于这里是进程对象,自然获得的是 EPROCESS 对象
NTSTATUS status = ObReferenceObjectByHandle(ProcessHandle, FILE_ALL_ACCESS, *PsProcessType, KernelMode,&pEProcess, NULL);
if (!NT_SUCCESS(status))
{
KdPrint(("引用对象错误,错误码:%x\n",status));
return status;
}
if (strcmp(PsGetProcessImageFileName(pEProcess), "calc.exe") == 0)
{
if (PsGetCurrentProcess()!=pEProcess)
{
// 如果该进程是所保护的的进程的话,则返回权限不够的异常即可
ObDereferenceObject(pEProcess);
return STATUS_ACCESS_DENIED;
}
}
// 对于非保护的进程可以直接调用原来 SSDT 中的 NtTerminateProcess 来结束进程
ObDereferenceObject(pEProcess);
return pReal_ZwTerminateProcess(ProcessHandle, ExitStatus);
}
VOID HookBugCheckEx()
{
KIRQL Irql;
//反汇编是: mov rax,0xxxxxx
// jmp eax 共12字节
//JmpCode从第三字节开始是跳转地址
char JmpCode[] = {"\x48\xb8\xff\xff\xff\xff\xff\xff\xff\x00\xff\xe0"};
PVOID BugAddr;
ULONGLONG JmpAddr = (ULONGLONG)MyZwTerminateProcess;
//构造好跳转的地址 然后写入BugCheckEx函数
RtlCopyMemory(&JmpCode[2], &JmpAddr, 8);
Irql = WPOFF();
//写入KeBugCheckEx函数的开头
RtlCopyMemory(KeBugCheckEx, JmpCode, sizeof(JmpCode));
WPON(Irql);
}
VOID hOOKTerminateProcess64()
{
PCHAR FuncAddr = (PCHAR)KeBugCheckEx;
HookBugCheckEx();
//获取服务表基址
PKSYSTEM_SERVICE_TABLE pServiceBase = GetSSDT64Base();
//函数地址-服务表的基址=偏移量
LONG_PTR Offset = (LONG_PTR)(FuncAddr - (PCHAR)pServiceBase->ServiceTableBase);
//偏移量左移4位 就是写入SSDT表的值
Offset <<= 4;
//保存原来的函数 SSDT表基址+对应的偏移右移4位
ULONG_PTR TableBase =(ULONG_PTR) pServiceBase->ServiceTableBase;
ULONG OldOffset = pServiceBase->ServiceTableBase[41];
OriginalOffset = OldOffset;
OldOffset >>= 4;
pReal_ZwTerminateProcess =( Real_ZwTerminateProcess)(TableBase + OldOffset);
KIRQL Irql = WPOFF();
pServiceBase->ServiceTableBase[41] =(ULONG) Offset;
WPON(Irql);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS status =STATUS_SUCCESS;
pDriverObject->DriverUnload=DriverUnload;
hOOKTerminateProcess64();
return status;
}