Windowsドライバ開発研究ノート(F) - インラインHOOK

序文

まず、から学ぶ滴水编程达人中級コース、官网:https://bcdaren.com
2、ハイトン教師岩!

SSDTのHOOK

短所

  1. 周りを取得するのは簡単見つけることは容易で、
  2. HOOKシステムは、専用テーブルサービスとして機能することができます

インラインフック

JMP的偏移 = 跳转地址 - 补丁地址 -5

フック

説明:

実装プロセス

ここに画像を挿入説明

デカップリング

ここに画像を挿入説明

実験1:3のリングインラインフック

#include <stdio.h>
#include <windows.h>

BYTE* ProcAddr = 0;			//原函数地址
DWORD OldProtect = 0;		//保存旧的内存保护
BYTE  OldCmd[20] = {0};		//存储原函数头部原始指令 在最后面构造返回到原函数的指令

BYTE* HookProcAddr  = 0;	//钩子函数的地址

//函数指针 将数据作为指令执行
typedef void (*POLDCMD)();
POLDCMD pOldCmd = (POLDCMD)(char*)OldCmd;
//printf的格式
char *format = "%x";


DWORD add(DWORD a, DWORD b)
{
	return a + b;
}

void __declspec(naked) HookProc()
{
	//保存现场
	__asm
	{
		pushad
		pushfd
	}

	//获得参数1
	printf("a = ");

	__asm
	{
		mov eax, esp
		add eax, 40

		push [eax]
		push format
		call printf
		add esp, 8
	}

	//获得参数2
	printf("\nb = ");

	__asm
	{
		mov eax, esp
		add eax, 44

		push [eax]
		push format
		call printf
		add esp, 8
	}

	printf("\n");

	//还原现场
	__asm
	{

		popfd
		popad
		
		//执行原函数头部指令 并返回到原函数下一行继续执行
		jmp pOldCmd
	}
}

void InlineHook()
{
	//获得需要挂钩的函数地址
	ProcAddr  =   (BYTE*)add + 1;
	ProcAddr += *(DWORD*)ProcAddr + 4;
	//修改内存保护
	VirtualProtect(ProcAddr, 6, PAGE_EXECUTE_READWRITE, &OldProtect);
	//将被覆盖的指令复制到数组中
	strncpy((char*)OldCmd, (char*)ProcAddr, 6);
	//数组末尾添上跳转指令 用于返回到被挂钩的函数
	OldCmd[6] = 0xE9;
	*(DWORD*)&OldCmd[7] = (DWORD)(ProcAddr+6) - (DWORD)&OldCmd[6] - 5;

	//获得钩子函数的地址
	HookProcAddr  =   (BYTE*)HookProc + 1;
	HookProcAddr += *(DWORD*)HookProcAddr + 4;
	//修改原函数的头部 跳转到钩子函数
	ProcAddr[0] = 0xE9;
	*(DWORD*)&ProcAddr[1] = HookProcAddr - ProcAddr - 5;
	//跳转指令只占五个字节 原函数头部三行指令占六个字节 因此将多余字节填充为nop
	ProcAddr[5] = 0x90;

}

void UnInlineHook()
{
	//判断函数是否被挂钩
	if(ProcAddr[0] == 0xE9)
	{
		strncpy((char*)ProcAddr, (char*)OldCmd, 6);
	}
}

int main()
{
	DWORD sum;

	//挂钩
	InlineHook();

	sum = add(1, 2);
	printf("sum = %x\n", sum);

	//脱钩
	UnInlineHook();

	sum = add(3, 4);
	printf("sum = %x\n", sum);

	return 0;
}

結果
ここに画像を挿入説明

実験II:インラインはリング0フック

#include <ntddk.h>
#include <ntstatus.h>

PUCHAR pNtOpenProcess;
UCHAR  OldCmd[20] = {0};
//函数指针 用于将数据作为指令执行
typedef VOID (*POLDCMD)();
POLDCMD pOldCmd = (POLDCMD)(PUCHAR)OldCmd;

PCHAR format = "%x %x %x %x \r\n";

typedef NTSTATUS (*NTOPENPROCESS)(
	PHANDLE ProcessHandle,
	ACCESS_MASK DesiredAccess,
	POBJECT_ATTRIBUTES ObjectAttributes,
	PCLIENT_ID ClientId);

typedef struct _KSYSTEM_SERVICE_TABLE  
{  
	PULONG  ServiceTableBase;			// 服务函数地址表基址  
	PULONG  ServiceCounterTableBase;	// SSDT函数被调用的次数
	ULONG   NumberOfService;			// 服务函数的个数  
	PULONG   ParamTableBase;			// 服务函数参数表基址   
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;  

typedef struct _KSERVICE_TABLE_DESCRIPTOR  
{  
	KSYSTEM_SERVICE_TABLE   ntoskrnl;		// ntoskrnl.exe 的服务函数  
	KSYSTEM_SERVICE_TABLE   win32k;			// win32k.sys 的服务函数(GDI32.dll/User32.dll 的内核支持)  
	KSYSTEM_SERVICE_TABLE   notUsed1;  
	KSYSTEM_SERVICE_TABLE   notUsed2;  
}KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;

// KeServiceDescriptorTable 是 ntoskrnl.exe 所导出的全局变量 申明一下就可以直接使用了
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;

VOID PageProtectOn()
{
	__asm{//开启内存保护
		mov  eax,cr0
		or   eax,10000h
		mov  cr0,eax
		sti
	}
}

VOID PageProtectOff()				
{
	__asm{//关闭内存保护
		cli					
		mov  eax,cr0
		and  eax,not 10000h
		mov  cr0,eax
	}
}

VOID __declspec(naked) HookProc(){
	__asm
	{
		//保存现场
		pushad
		pushfd
	
		mov eax, esp
		add eax, 40		//eflags + 8个通用寄存器 + 返回地址
		add eax, 12		//定位到最后一个参数
		push [eax]		//ClientId
		push [eax-0x4]	//ObjectAttributes
		push [eax-0x8]	//DesiredAccess
		push [eax-0xC]	//ProcessHandle
		push format
		call DbgPrint
		add esp, 20

		//还原现场
		popfd
		popad

		//调用原本的指令,然后返回到原函数
		jmp pOldCmd
	}
}

NTSTATUS InlineHook()
{
	NTSTATUS status;
	status = STATUS_SUCCESS;

	PageProtectOff();

	//保存原来的指令
	pNtOpenProcess = (PUCHAR)KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7A];
	RtlMoveMemory(OldCmd, pNtOpenProcess, 5);

	//构造回去的指令,用于返回到原函数
	OldCmd[5] = 0xE9;
	*(PULONG)&OldCmd[6] = ((ULONG)pNtOpenProcess+5) - (ULONG)&OldCmd[5] - 5;

	//内核函数不存在中间人(call后通过jmp跳转),函数地址为实际地址
	pNtOpenProcess[0] = 0xE9;
	*(PULONG)&pNtOpenProcess[1] = (ULONG)HookProc - (ULONG)pNtOpenProcess - 5;

	PageProtectOn();

	return status;
}

NTSTATUS UnInlineHook()
{
	NTSTATUS status;
	status = STATUS_SUCCESS;

	if(pNtOpenProcess[0] == 0xE9)
	{
		PageProtectOff();
		RtlMoveMemory(pNtOpenProcess, OldCmd, 5);
		PageProtectOn();
	}

	return status;
}

//卸载函数
VOID DriverUnload(PDRIVER_OBJECT driver)
{
	//卸载驱动时脱钩
	UnInlineHook();

	DbgPrint("驱动程序已停止.\r\n");
}

//驱动程序入口函数,相当于控制台的main函数
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
	DbgPrint("驱动程序已运行.\r\n");
	//运行驱动时挂钩
	InlineHook();

	//设置一个卸载函数  便于退出
	DriverObject->DriverUnload = DriverUnload;

	return STATUS_SUCCESS;
}

結果
ここに画像を挿入説明

公開された45元の記事 ウォンの賞賛2 ビュー1819

おすすめ

転載: blog.csdn.net/qq_41988448/article/details/103557383