欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

win10 x64中inlineHook SSDT里面的函数

程序员文章站 2022-05-28 11:08:03
...

inlineHook的原理:

win10 x64中inlineHook SSDT里面的函数

为了方便好理解,一些变量名和函数名在这里使用中文命名,有些编译器不支持中文命名,在这里要注意(我的是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);
	RtlFillMemory((void*)HOOK函数地址, 改写的长度, 0x90);
	memcpy((PVOID)HOOK函数地址, &跳到代理函数, 14);
	开启写保护(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)
{
	KdPrint(("读内存\n"));
	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;
}

效果 

win10 x64中inlineHook SSDT里面的函数

win10 x64中inlineHook SSDT里面的函数 

相关标签: 好玩的技术