win7 x64 inline hook的尝试

最近因为虚机性能问题,需要验证下是不是因为内核态加锁时间过长导致,所以需要在内核态hook两个内核函数:
KeAcquireSpinLockAtDpcLevel和KeReleaseSpinLock,虚机的操作系统是win7 64位。
所以在内核态hook,我采用inline hook的方式,有借鉴的blog:

https://blog.csdn.net/u013761036/article/details/66477613

这篇博客对原理讲的比较清楚,博客的作者说那段代码会蓝屏,他没有具体说是什么现象的蓝屏,所以我也不清楚到底他指的蓝屏是什么,但是我在实际使用过程中发现有两个蓝屏问题:

patch guard导致的蓝屏

我利用博客代码对KeAcquireSpinLockAtDpcLevel和KeReleaseSpinLock两个函数进行了hook,欣喜的是能顺利hook到两个函数里面,但是不一会儿就蓝屏了,蓝屏界面如下:

win7 x64 inline hook的尝试_第1张图片

这个蓝屏导致的原因是win7 64位系统的patch guard机制,系统会不定时检测内核是否被篡改,如果有篡改就会促发蓝屏。

(1)对破解软件的尝试,我尝试了网上说的一些方法,比如我下载了no_pg_ds_v3这个工具,然后按照文档进行部署,部署成功后重启,会提示如下界面:

win7 x64 inline hook的尝试_第2张图片

这时候选中“PatchGuard Disabled V3”,然后按F8去掉驱动程序签名强制,这时候就进入系统了,然而并没有什么卵用,系统还是会触发一样的蓝屏。

(2)对于调试模式的尝试,据说微软在调试模式会自动去掉patch guard机制,要不然windbg怎么做到打断点之类的功能的,于是我设置了一个调试模式的引导项,可以参照https://blog.csdn.net/chenyujing1234/article/details/8090297,重启后进入该引导项(上图的DebugEntry引导),还是没解决问题,最后找到了一篇外国的博客https://www.codeproject.com/Articles/28318/Bypassing-PatchGuard,需要在调试模式下再加一条命令:Bcdedit /dbgsettings SERIAL DEBUGPORT:1 BAUDRATE:115200 /start AUTOENABLE /noumex。这样patch guard就关掉了,经过测试可用。

因为对于我来说我只是开发一个调试工具,所以调试模式对我来说是可接受的,至于如何在普通场景下绕过去的方式我目前还没找到。

反编译引擎LDE造成的蓝屏

第二个蓝屏界面如下:

win7 x64 inline hook的尝试_第3张图片

一般发生在加载驱动的时候,且刚开机的时候加载驱动特别容易重现。

我通过windbg对dump文件进行分析,发现每次蓝屏模块都不一样,也不是我写的那个程序模块,这样就比较难定位了。
我通过排除法,定位到蓝屏原因出在GetPatchSize函数,他是通过国外一个网友的一个shellcode来计算指令长度。

所以我直接没有调用这个函数,比如说我要hook KeAcquireSpinLockAtDpcLevel函数,我用GetPatchSize函数获取到他的指令长度为23,因为它指令长度不会变化,所以我就写死23。 其他函数也一样,先写一个demo,使用GetPatchSize计算指令长度,然后写死就行了。

核心代码:

ULONG lock_patch_size = 23;       
ULONG unlock_patch_size = 20;

PVOID HookKernelApi(IN PVOID ApiAddress, IN PVOID Proxy_ApiAddress, OUT PVOID *Original_ApiAddress, IN ULONG PatchSize)
{
	/* 参数检查 */
	if (ApiAddress == nullptr || Proxy_ApiAddress == nullptr || Original_ApiAddress == nullptr) {
		return nullptr;
	}
	KIRQL irql;
	UINT64 tmpv;
	PVOID head_n_byte = nullptr, ori_func = nullptr;

	UCHAR jmp_code[] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
	UCHAR jmp_code_orifunc[] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";

	//step 1: Read current data
	head_n_byte = kmalloc(PatchSize);
	if (head_n_byte == nullptr) {
		return nullptr;
	}

	irql = WPOFFx64();
	memcpy(head_n_byte, ApiAddress, PatchSize);
	WPONx64(irql);

	//step 2: Create ori function
	ori_func = kmalloc(PatchSize + 14);    //原始机器码+跳转机器码
	if (ori_func == nullptr) {
		return head_n_byte;
	}

	RtlFillMemory(ori_func, PatchSize + 14, 0x90);
	tmpv = (ULONG64)ApiAddress + PatchSize;    //跳转到没被打补丁的那个字节

        memcpy(jmp_code_orifunc + 6, &tmpv, 8);
	memcpy((PUCHAR)ori_func, head_n_byte, PatchSize);
	memcpy((PUCHAR)ori_func + PatchSize, jmp_code_orifunc, 14);
	*Original_ApiAddress = ori_func;

	//step 3: fill jmp code
	tmpv = (UINT64)Proxy_ApiAddress;
	memcpy(jmp_code + 6, &tmpv, 8);

	//step 4: Fill NOP and hook
	irql = WPOFFx64();
	RtlFillMemory(ApiAddress, PatchSize, 0x90);
	memcpy(ApiAddress, jmp_code, 14);
	WPONx64(irql);
	return head_n_byte;
}

VOID HookSpinlockFunctions()
{
	/* 加锁函数 */
	lock_head_n_byte = (PUCHAR)HookKernelApi(GetFunctionAddr(L"KeAcquireSpinLockAtDpcLevel"),
		(PVOID)Proxy_KeAcquireSpinLockAtDpcLevel,
		&ori_lock,
		lock_patch_size);

	/* 解锁函数 */
	unlock_head_n_byte = (PUCHAR)HookKernelApi(GetFunctionAddr(L"KeReleaseSpinLock"),
		(PVOID)Proxy_KeReleaseSpinLock,
		&ori_unlock,
		unlock_patch_size);
}

你可能感兴趣的:(windows驱动)