[2025]阻止漏洞驱动利用(byovd)技术致盲安全软件 huoji 驱动漏洞,EDR,漏洞驱动 2025-09-15 1359 次浏览 2 次点赞 ## 前言 byovd,在2014年左右这些在业内还是被叫做"vulnerable driver" 也不是什么神秘的东西,我知道的到现在也就几十年了吧**(实际可能更早,比如NSA的那个远控XP时代就用了)** 这些驱动有几个类型,**但是无非就是跟R3交互接口没验证签名导致函数被滥用。** ## 内核驱动调用ZwTerminateProcess 现在信息安全领域见得多的还是这种内核发terminalprocess的东西,毕竟没游戏安全对抗那么激烈。大部分情况下关闭就行不做其他的操作  就比如银狐木马用的:  这种情况跟上面说的一样,内核IO管理权限校验没做好,让人可以对任意进程发ZwTerminateProcess. ## 缓解 实际上,在2020年左右Aidan Khoury的faceit反作弊(也负责后来的VGK反作弊) 就已经开始做缓解了(这个作者长得很帅,建议github star一下) https://github.com/ajkhoury  缓解方法如下: ```cpp 开机启动 标记/阻止 漏洞驱动加载 API 挂钩监控 前两个没什么好说的,无非就是拉黑驱动签名和HASH,但是漏洞驱动太多了.我们重点说第三点 ``` 在windows系统中,随便挂钩系统驱动可能会被PG蓝屏.但是挂钩第三方驱动不会的.而保险的方式是挂钩驱动IAT,比如欧洲bro挂的BE的驱动的IAT导入函数列表  是的,其实卡巴斯基和一些国外的EDR也挂了很多常见的驱动IAT或者IO HANDLE,目的就是为了防止漏洞驱动利用.比如**为什么SentinelOne EDR能阻止漏洞驱动利用,就是挂了IAT**  ### 缓解POC 让我们以一个卡饭流行的样本为例子,我们如何缓解他致盲edr:  代码真的非常非常简单,我们只需要注册一个loadimage回调,然后判断是否是驱动,如果是,则hook导入表:  标准的导出表hook:  hook后,我们判断是否是受保护进程,我这里以defender的msmpeng.exe为例子:   完整代码: ```cpp /// @brief 根据进程句柄返回进程路径,必须要释放 /// @param ProcessHandle /// @return 成功返回unicode_string* 否则返回null auto QueryNameByHandle(HANDLE ProcessHandle) -> PUNICODE_STRING { void* allocMemory = nullptr; uint32_t bufferSize = 0; bool isSuccess = false; do { if (ZwQueryInformationProcess(ProcessHandle, ProcessImageFileName, NULL, 0, (PULONG)&bufferSize) != STATUS_INFO_LENGTH_MISMATCH) { break; } if (bufferSize == 0) { break; } allocMemory = ExAllocatePoolWithTag(PagedPool, bufferSize, HUOJI_TAG); if (allocMemory == nullptr) { break; } memset(allocMemory, 0x0, bufferSize); auto ntStatus = ZwQueryInformationProcess( ProcessHandle, ProcessImageFileName, allocMemory, bufferSize, (PULONG)&bufferSize); if (NT_SUCCESS(ntStatus) == false) { break; } isSuccess = true; } while (false); if (isSuccess == false) { if (allocMemory != nullptr) { ExFreePoolWithTag(allocMemory, HUOJI_TAG); allocMemory = nullptr; } } return reinterpret_cast(allocMemory); }; namespace Hooks { /// @brief hook的漏洞利用函数 /// @param ProcessHandle /// @param ExitStatus /// @return NTSTATUS NTAPI HookZwTerminateProcess(_In_opt_ HANDLE ProcessHandle, _In_ NTSTATUS ExitStatus) { const static UNICODE_STRING defenderProcess = RTL_CONSTANT_STRING(L"msmpeng.exe"); PUNICODE_STRING TargetProcessName = nullptr; PUNICODE_STRING CurrentProcessName = nullptr; bool isBlock = false; do { if (ProcessHandle == (HANDLE)-1) { break; } TargetProcessName = QueryNameByHandle(ProcessHandle); if (TargetProcessName == nullptr) { break; } CurrentProcessName = QueryNameByHandle((HANDLE)-1); if (CurrentProcessName == nullptr) { break; } if (RtlSuffixUnicodeString( &defenderProcess, reinterpret_cast(TargetProcessName), true) == false) { break; } DebugPrint("Detect vulnerable Driver Exploit\n"); DebugPrint("Process for exploit: %wZ \n", CurrentProcessName); DebugPrint("try to kill: %wZ \n", TargetProcessName); isBlock = true; } while (false); if (TargetProcessName != nullptr) { ExFreePoolWithTag(TargetProcessName, HUOJI_TAG); TargetProcessName = nullptr; } if (CurrentProcessName != nullptr) { ExFreePoolWithTag(CurrentProcessName, HUOJI_TAG); CurrentProcessName = nullptr; } return isBlock ? STATUS_UNSUCCESSFUL : ZwTerminateProcess(ProcessHandle, ExitStatus); } }; // namespace Hooks auto LoadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo) -> void { if (ProcessId != 0) { return; } DriverUtil::IATHook(ImageInfo->ImageBase, (char*)"ZwTerminateProcess", &Hooks::HookZwTerminateProcess); } auto InstallHook() -> void { PsSetLoadImageNotifyRoutine(&LoadImageNotifyRoutine); } ``` ## 效果 一旦发现目标正在尝试结束我们受保护进程,我们可以直接阻止他,这是效果,我们可以看到我们成功检测并且阻止了漏洞驱动利用:  ## 代价? 是的,如此简单背后是有自己的代价的: 他极其不稳定,比如闹得沸沸扬扬的火绒和卡巴斯基不兼容就是卡巴斯基挂了火绒的iat:  此外一旦安装,无法热卸载驱动,怎么卸载都是不安全的.并且可能会有多个安全软件hook iat,到时候谁卸整个系统都会蓝屏.梦回XP时代杀毒软件互相打架。 而且这与任何现代游戏反作弊都不兼容,现在的游戏反作弊都也会hook iat,导致进一步冲突. **一般来说,客户在发现游戏和杀毒软件冲突的时候,会优先卸载杀毒软件.** ## 扩展阅读:直接物理内存读写 在早期我印象中的漏洞驱动,都是一些主板刷bios/cpuz/gpuz的驱动,这些驱动有一些物理内存读写的API,忘记做校验导致被利用.利用去关闭dse 这里以GPUZ漏洞利用为例:  不做任何校验,直接用了MMIO:  所以可以给gpuz发控制码读物理内存  而我们只有读物理内存的权利,所以还需要寻找到system PML4 这样才能翻译系统内存。  有了系统PML4后,就可以做地址翻译,把虚拟地址翻译为物理地址.然后进行读写  能做内核任意读写了,就可以干任何事情了,比如关闭驱动DSE  暴力搜特征码关的  关掉后就可以加载任意驱动了,或者跟之前一样, 遍历handle table拉高自己的handle权限 然后关闭安全软件 不过这个技术在系统windows1809后这种很少见了,因为windows加了一个机制,禁止这些物理内存读写的API访问系统CR3,如果访问了会被蓝屏 打开IDA,可以看到在iMapContiguousMemory的实现里面有这个代码  可以看到,它通过MiFillSystemPtes ```cpp if ( (int)MiFillSystemPtes(v16, v12, v24, v5, v17, (__int64)&v23) < 0 ) { MiReleasePtes(&qword_43C0A0, v16, v14); return 0i64; } ``` 阻止了这些API对系统CR3的访问。访问不了系统CR3,也就没办法做后续了。 当然还有后续,后续有个全新的方法继续绕过继续利用,毕竟对抗永无止境: 《[2024]从驱动直接读写物理内存漏洞 到 内存加载驱动分析》 https://key08.com/index.php/2024/08/18/2001.html  对杀毒软件对抗这些感兴趣吗? 感兴趣具体更多可以参考: [2024]深度了解现代安全软件对抗与缓解措施 https://key08.com/index.php/2024/11/23/2268.html 本文由 huoji 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。 点赞 2
”标准的导出表hook:“打错字了师傅