[驱动开发] 手写 r0 hook(NtOpenProcess 为例)

inlineHook的原理:

在这里插入图片描述
为了方便好理解,一些变量名和函数名在这里使用中文命名,有些编译器不支持中文命名,在这里要注意(我的是VS2019)
hook.h:

#pragma once
#include<ntifs.h>
#include<ntddk.h>
#pragma intrinsic(__readmsr)
 
//SSDT的结构
typedef struct _SYSTEM_SERVICE_TABLE {
    
    
	PLONG  		ServiceTableBase;
	PVOID  		ServiceCounterTableBase;
	ULONGLONG  	NumberOfServices;
	PVOID  		ParamTableBase;
} SYSTEM_SERVICE_TABLE, * PSYSTEM_SERVICE_TABLE;
 
 
/*全局变量*/
extern PSYSTEM_SERVICE_TABLE SSDT地址;
 
//函数声明
UINT64 获取SSDT地址();
UINT64 获取函数地址(ULONG dwIndex);
KIRQL 关闭写保护();
VOID 开启写保护(KIRQL irql);
VOID 开始HOOK(UINT64 HOOK函数地址,UINT64 代理函数地址,USHORT 改写的长度, PVOID* 原函数);
VOID 恢复HOOK(UINT64 HOOK函数地址, USHORT 改写的长度, PVOID 原函数);

hook.c

#include"hook.h"
 
PSYSTEM_SERVICE_TABLE SSDT地址 = 0;
 
UINT64 获取SSDT地址()
{
    
    
	PUCHAR msr = (PUCHAR)__readmsr(0xC0000082);
	PUCHAR startaddr = 0, Endaddr = 0;
	PUCHAR i = NULL;
	UCHAR b1, b2, b3;
	ULONG temp = 0;
	ULONGLONG addr = 0;
 
	KdPrint(("msr c0000082的值:%x\n", msr));
	if (*(msr + 0x9) == 0x00)
	{
    
    
		startaddr = msr;
		Endaddr = startaddr + 0x500;
	}
	else if (*(msr + 0x9) == 0x70)
	{
    
    
		PUCHAR pKiSystemCall64Shadow = msr;
		PUCHAR EndSearchAddress = pKiSystemCall64Shadow + 0x500;
		PUCHAR i = NULL;
		INT Temp = 0;
		for (i = pKiSystemCall64Shadow; i < EndSearchAddress; i++)
		{
    
    
			if (MmIsAddressValid(i) && MmIsAddressValid(i + 5))
			{
    
    
				if (*i == 0xe9 && *(i + 5) == 0xc3)
				{
    
    
					memcpy(&Temp, i + 1, 4);
					startaddr = Temp + (i + 5);
					KdPrint(("KiSystemServiceUser的地址:%x\n", startaddr));
					Endaddr = startaddr + 0x500;
				}
			}
		}
	}
 
	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;
			KdPrint(("SSDT地址:%x\n", addr));
			return addr;
		}
	}
	return 0;
}
 
UINT64 获取函数地址(ULONG dwIndex)
{
    
    
	PULONG lpBase = SSDT地址->ServiceTableBase;
	ULONG dwCount = (ULONG)SSDT地址->NumberOfServices;
	UINT64 lpAddr = 0;
	ULONG dwOffset = lpBase[dwIndex];
 
	if (dwOffset & 0x80000000)
		dwOffset = (dwOffset >> 4) | 0xF0000000;
	else
		dwOffset >>= 4;
	lpAddr = (UINT64)((PUCHAR)lpBase + (LONG)dwOffset);
 
	return lpAddr;
}
 
KIRQL 关闭写保护()
{
    
    
	KIRQL irql = KeRaiseIrqlToDpcLevel();
	ULONG_PTR cr0 = __readcr0();
 
	cr0 &= 0xfffffffffffeffff;
	_disable();
	__writecr0(cr0);
 
	return irql;
}
 
VOID 开启写保护(KIRQL irql)
{
    
    
 
	ULONG_PTR cr0 = __readcr0();
	cr0 |= 0x10000;
	__writecr0(cr0);
	_enable();
 
	KeLowerIrql(irql);
}
 
/*
要改写的指令长度网上一些方法是用LDE这个反编译引擎计算,但有时候这并不靠谱,比如我在HOOK NtOpenProcess的时候
发现需要改写的字节数为20,但LDE只算出了16,导致跳回原函数的地址与实际我们需要的地址差4个字节
这里的 原函数 是用来储存原API函数被修改掉的函数的指令及,后面需要调用它跳回原API函数
*/
VOID 开始HOOK(UINT64 HOOK函数地址, UINT64 代理函数地址,USHORT 改写的长度,PVOID *原函数)
{
    
    
	UCHAR 跳到代理函数[] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
	UCHAR 跳回原函数[]= "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
	memcpy(跳到代理函数 + 6, &代理函数地址, 8);
 
	/*
	下面的数值14是跳转指令的总长度 假设该指令地址为0x410000
	0x410000 jmp qword ptr [0x410006] 
	0x410006 xxxxxxxx
	其中0x410006中储存代理函数的地址
	*/
	UINT64 跳回原函数的地址 = HOOK函数地址 + 改写的长度;
	memcpy(跳回原函数 + 6, &跳回原函数的地址, 8);
	*原函数 = ExAllocatePool(NonPagedPool, 改写的长度+ 14);
	RtlFillMemory(*原函数, 改写的长度 + 14, 0x90);
 
	KIRQL irql = 关闭写保护();
	memcpy(*原函数, (PVOID)HOOK函数地址, 改写的长度);
	memcpy((PCHAR)(*原函数)+ 改写的长度, 跳回原函数, 14);
 
	KIRQL dpc_irql = KeRaiseIrqlToDpcLevel();
	RtlFillMemory((void*)HOOK函数地址, 改写的长度, 0x90);
	memcpy((PVOID)HOOK函数地址, &跳到代理函数, 14);
	KeLowerIrql(dpc_irql);
	开启写保护(irql);
}
 
VOID 恢复HOOK(UINT64 HOOK函数地址,USHORT 改写的长度,PVOID 原函数)
{
    
    
	KIRQL irql = 关闭写保护();
	memcpy((PVOID)HOOK函数地址, 原函数, 改写的长度);
	开启写保护(irql);
	ExFreePool(原函数);
}

调用例子:inlinehook NtOpenProcess实现防止“NewTzmTool.exe”的进程被OpenProcess
inlinehook NtReadVirtualMemory,有程序调用ReadProcessMemory时显示被读取得程序的名字

main.c

#include"hook.h"
 
PVOID S_OpenProcess;
PVOID S_ReadVritualMemory;
 
//要HOOK的函数原型声明,win x64系统的API函数调用约定为__fastcall
typedef NTSTATUS(__fastcall* pMyOpenProcess)(OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId OPTIONAL);
typedef NTSTATUS(__fastcall* pMyReadVirtualMemory)(IN HANDLE ProcessHandle, IN PVOID BaseAddress, OUT PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnLength OPTIONAL);
 
//用到的内核函数,该函数使用前需要声明
PCHAR PsGetProcessImageFileName(PEPROCESS Process);
 
NTSTATUS MyOpenProcess(OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId OPTIONAL)
{
    
    
	PEPROCESS process = 0;
	if (STATUS_SUCCESS == PsLookupProcessByProcessId(ClientId->UniqueProcess, &process))
	{
    
    
		if (strcmp(PsGetProcessImageFileName(process), "NewTzmTool.exe") == 0)
		{
    
    
			KdPrint(("受保护进程:%s", PsGetProcessImageFileName(process)));
			return STATUS_PNP_INVALID_ID;
		}
	}
	
	return ((pMyOpenProcess)S_OpenProcess)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);;
}
 
NTSTATUS MyReadVirtualMemory(IN HANDLE ProcessHandle, IN PVOID BaseAddress, OUT PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnLength OPTIONAL)
{
    
    
	NTSTATUS    status;
	PEPROCESS   process;
	
	status = ObReferenceObjectByHandle(ProcessHandle,
		PROCESS_ALL_ACCESS,
		*PsProcessType,
		KernelMode,
		(PVOID*)&process,
		NULL);
	if (NT_SUCCESS(status))
	{
    
    
		KdPrint((PsGetProcessImageFileName(process)));
	}
	return ((pMyReadVirtualMemory)S_ReadVritualMemory)(ProcessHandle,BaseAddress,Buffer,BufferLength,ReturnLength);
}
 
void DriverUnLoad(PDRIVER_OBJECT driver)
{
    
    
	KdPrint(("驱动卸载\n"));
	恢复HOOK(获取函数地址(63),17, S_ReadVritualMemory);
	恢复HOOK(获取函数地址(38),20, S_OpenProcess);
}
 
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING path)
{
    
    
	KdPrint(("驱动加载\n"));
	driver->DriverUnload = DriverUnLoad;
	SSDT地址 = (PSYSTEM_SERVICE_TABLE)获取SSDT地址();
 
	if (SSDT地址 == NULL)
	{
    
    
		KdPrint(("SSDT地址获取失败!\n"));
		return STATUS_SUCCESS;
	}
 
	开始HOOK(获取函数地址(63), (UINT64)&MyReadVirtualMemory, 17,&S_ReadVritualMemory);
	开始HOOK(获取函数地址(38), (UINT64)&MyOpenProcess,20,&S_OpenProcess);
 
	return STATUS_SUCCESS;
}

效果

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Simon798/article/details/109580213
今日推荐