杀软用户模式HOOK(UMH)现状

关于前文R0 Anti Antivirus的一些更详尽的补充内容, 请先看前文

UMH介绍

UMH一般是使用DLL注入技术, 前文提到杀软可以利用Kernel Callback接收到进程创建回调, 如此杀软不必定时刷新进程列表进行注入, 而是可以实时响应. 在实际中杀软并不会对每个程序都进行注入, 有一些判定规则, 比如是否有证书签名等, 各家杀软判断规则不尽相同, 但是证书都是一个重要的参考

UMH检测

关于UMH的检测, 我在实际中测试某不知名杀软时, 发现一个有趣的事情, 使用VS直接进行调试的时候, 杀软无法成功注入DLL, 猜测是VS已经附加了调试器, 其他进程便无法再打开句柄.
UMH一般是直接使用JMP指令, 也就是把将API函数入口修改成JMP到杀软自己的DLL中的函数, 然后杀软可以对参数内容进行检测和处理. 代码实现就是获取API函数地址, 然后反汇编该地址查看是否指令是JMP. 想要了解更多: https://www.ired.team/offensive-security/code-injection-process-injection/how-to-hook-windows-api-using-c++
我这里使用github开源项目进行UMH的检测: https://github.com/Mr-Un1k0d3r/EDRs/blob/main/hook_finder64.exe?raw=true
使用方式如下:

hook_finder64.exe C:\windows\system32\ntdll.dll

hooks.png

可以看到, 杀软HOOK了许多API函数, 我这里测试的杀软叫做Vipre

UMH现状

为什么这里没有使用卡巴斯基作为演示呢? 因为现在UMH已经被主流杀软抛弃了, 包括卡巴斯基, 360, 火绒, 原因当然很简单, 因为太容易绕过了, 而且有更强大的Kernel Callback

前文验证

之前提到UMH依赖Kernel Callback进行实时响应, 但是没有给出验证过程, 接下来我们一起来验证一下. 既然UMH依赖Kernel Callback, 那么我们把Kernel Callback干掉, UMH也应该失效.
由于Vipre不会注入有签名的程序, 所以我们需要自己写个程序, 观察注入情况. 代码很简单:

#include <iostream>

int main() {
	system("pause");
	return 0;
}

中间让程序停住, 方便我们观察差异.
此处使用Process Hacker观察加载的模块信息 module.png 我们的程序加载了一个名为atcuf64.dll的模块, 右键打开文件位置, 即可看见文件在Vipre路径下, 从模块描述也可以看出是Bitdefender的用户态过滤器, Vipre钱挣得真容易.
接下来使用Windows-Kernel-Explorer(WKE)将Kernel Callback移除 kernel-callback.png 移除之后, 再次运行我们写的程序, 观察模块加载情况 no-module.png atcuf64.dll模块已经没有被加载了

思考

现在主流杀软已经不再使用UMH技术了, 但是还有很多人搞免杀还在直接系统调用, unhook. 还有的人在利用小众语言进行免杀, 还有的人会进行PPID欺骗之类的技术. 我个人不使用这些, PPID欺骗, 很明显的IOC, 想要骗过别人, 先骗过自己, 要让杀软将我们判定为正常程序, 首先我们自己要把程序当成正常程序来写. 每一个功能都要有合理的借口.
使用小众语言免杀, 这是一种很浮躁的表现, 包括ChatGPT, 人们总想要立刻看到成果, 立刻得到答案, 立刻得到愉悦. 短视频是对全世界人专注力的挑战, ChatGPT是对全世界科学技术等领域研究人员的挑战. ChatGPT有时会非常有优势, 比如查找SSSDT相关资料的时候, 能从ChatGPT那里快速精准的获取到相关描述, 但Google却无法查到准确的信息. 但是信息查询, 整合的能力, 是一个研究人员的生命, 技术研究绝大部分时间都是阅读理解, 极少数时间是创新.