https://blog.csdn.net/u013761036/article/details/66477613
这篇博客对原理讲的比较清楚,博客的作者说那段代码会蓝屏,他没有具体说是什么现象的蓝屏,所以我也不清楚到底他指的蓝屏是什么,但是我在实际使用过程中发现有两个蓝屏问题:
我利用博客代码对KeAcquireSpinLockAtDpcLevel和KeReleaseSpinLock两个函数进行了hook,欣喜的是能顺利hook到两个函数里面,但是不一会儿就蓝屏了,蓝屏界面如下:
这个蓝屏导致的原因是win7 64位系统的patch guard机制,系统会不定时检测内核是否被篡改,如果有篡改就会促发蓝屏。
(1)对破解软件的尝试,我尝试了网上说的一些方法,比如我下载了no_pg_ds_v3这个工具,然后按照文档进行部署,部署成功后重启,会提示如下界面:
这时候选中“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就关掉了,经过测试可用。
因为对于我来说我只是开发一个调试工具,所以调试模式对我来说是可接受的,至于如何在普通场景下绕过去的方式我目前还没找到。
第二个蓝屏界面如下:
一般发生在加载驱动的时候,且刚开机的时候加载驱动特别容易重现。
我通过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);
}