64位下的SSDT HOOK 保护进程

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;


}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值