各位读者下午好。在这篇文章中,我准备跟大家讨论一下反病毒软件的实时保护机制。在此之前,我曾对反病毒软件的实时保护机制进行了一段时间的研究,并且发现了一些行之有效的方法来绕过这种防护机制。值得注意的是,这种方法甚至可以帮助恶意软件躲避沙箱环境中的分析检测,但是我还没有对这部分功能进行过测试。
在我的测试过程中,我所使用的反病毒软件为BitDefender。它可以在用户模式下对系统中的每一个进程进行实时保护,并通过监视Windows应用程序编程接口(API)的调用情况来检测可疑的行为模式。
可能有的读者并不了解BitDefender。BitDefender是一款来自罗马尼亚的安全软件,该公司成立于2011年,是SOFTWIN的子公司,公司总部设于罗马尼亚首都布加勒斯特。BitDefender的安全防护套件可以为各种规模的企业和个人用户提供先进的信息安全保护服务。凭借病毒防护软件等多种安全管理工具,BitDefender可以为运行在Windows/Linux/FreeBSD等平台下的桌面计算机,网关,Internet服务器,邮件和文件服务器等网络环境中的一切安全薄弱环节提供全面的安全防护。
事不宜迟,让我们赶紧开始吧。
反病毒软件中的实时保护功能是什么?
虽然通过程序签名来检测恶意软件的这种方法仍然是可行的,但是这种方法并不是最有效的。现在,越来越多的恶意软件更倾向于使用多层加密算法或者代码混淆技术来躲避传统反病毒软件的检测。最新一代的反病毒软件采用了行为监测分析技术,它们会对计算机中的每一个运行进程进行监控,尝试寻找出可疑的行为模式,并通过这些因素来识别计算机是否受到了恶意软件的感染。
现在,我给大家举个例子。假设有一个软件,它并没有提供任何形式的用户接口(例如对话框或程序窗口等等)。当它启动之后,它需要与一台位于罗马尼亚的外部服务器进行连接,并下载服务器中存储的文件。这种行为是非常可疑的,大多数带有实时保护功能的反病毒软件都会阻止这样的操作行为,并将其标记为危险进程。
现在你可能想问,这种实时保护功能到底是怎样工作的?为什么反病毒软件能够知道这些进程打算要做什么?在大多数情况下,防病毒软件会将自己的代码注入至运行态的进程中,然后为特定的Windows API接口安装钩子函数,这种机制将会大大提升安全防护软件的检测效率。在API钩子的帮助下,反病毒软件不仅可以查看到哪一个函数被调用了,而且还可以了解具体函数被调用的时间和传递进来的参数。
接下来,让我们看一看“CreateFileW”API(从kernel32.dll库中引入)的钩子函数是什么样子的。
函数代码的初始形式如下图所示:
如果反病毒软件需要为该函数设置一个钩子,它将会利用一条JMP指令替换函数前几个字节的原始数据,这样就可以将函数的执行流重定向至它的钩子处理函数中。通过这样的方式,反病毒软件就可以用堆栈内存中所有的参数来为这个API进行注册了。在反病毒软件的钩子函数完成了操作之后,它们将会执行函数的初始代码(此前被JMP指令所替换的那部分代码),然后跳转到进程的API函数,并继续运行进程。
注入了JMP指令的函数代码如下图所示:
设置钩子函数的方法有很多,但是这种设置方法的速度是最快的,而且也并不会对代码的执行效率产生过多的影响。
既然大家已经知道反病毒软件实时保护功能的工作机制了,那么我就可以开始跟大家讲解绕过这一保护机制的方法了。
目前市场上绝大多数的反病毒软件都会在内核模式(Ring0)下进行实时监控,但是这一话题已经超出了这篇文章的讨论范围了。在这篇文章中,我只会讨论如何在用户模式(Ring3)下绕过反病毒产品的监控和检测。
移除钩子函数-攻击者扔出的烟雾弹
正如你所知道的那样,实时保护功能依赖于API钩子函数的执行。只有当反病毒软件的钩子处理函数得到了执行,防护软件才能注册并调用这个API,实时监控传递的参数,并映射出进程的活动。
很明显,如果要完全禁用反病毒软件的保护功能,我们需要移除API钩子。这样一来,防护软件就无法检测到我们接下来所进行的任何操作了。
在我们自己的应用程序中,我们可以自由地管理和控制整个进程的内存空间。对于我们的程序而言,反病毒软件和它的注入代码相当于是一个入侵者,它会试图篡改我们的软件功能,但是现在一切都在我们的掌控之中。
操作步骤如下所示:
1. 枚举出当前进程中加载的全部DLL库。
2. 找出每一个DLL库中API函数的入口地址。
3. 移除注入的JMP指令,并将其替换成API函数的初始代码。
操作看起来似乎非常的简单,但是我们可能会在恢复API函数初始代码的这一步操作中遇到一些麻烦。毫无疑问,我们可以从钩子函数中获取到初始数据,但是我们并不知道钩子处理器中的哪一部分代码是API函数的初始代码。那么,接下来我们应该怎么做呢?
答案就是:分别读取磁盘中存储的DLL库文件,并手动获取DLL文件中所包含的完整初始代码。
为了找出“CreateFileW” API代码中前16字节的初始数据,具体的操作步骤如下:
1. 从Windows的系统文件夹中读取kernel32.dll文件的内容,并将数据读入内存。我将引入的模块命名为raw_module。
2. 获取到我们当前进程中所引入的kernel32.dll模块的基地址。我将引入的模块命名为“imported_module”。
3. 利用imported_module的基地址,手动修复raw_module的固定地址。这样就可以让这些模块的固定地址在内存引用中和当前的imported_module中看起来是相同的了。
4. 解析raw_module模块的输出数据,并找到“CreateFileW” API的地址。
5. 根据第四步中得到的API地址,提取出原始的前16个字节的数据。
这样就可以有效地利用API的初始代码重写当前的JMP指令了。
总结
正如我在文中介绍的那样,在用户模式下,反病毒软件和沙箱软件无法检测和防止我所描述的这种绕过技术。只有在内核模式下,它们才能够通过监控内核的进程活动来检测恶意的行为模式。
虽然防护软件能够检测到恶意软件移除钩子函数的行为,并重新为API设置钩子。但是恶意软件又会再一次移除API中的钩子函数,这种猫捉老鼠的游戏将会一直持续下去。为此,我打算对这一系列的运行模式进行更深层次的分析,而且我将会对当前所有流行的沙箱软件进行分析,我会在第一时间将分析结果公布出来。
与之前一样,我希望大家能够各抒己见,而且我也很希望能够了解到大家的观点和看法。各位可以关注我的Twitter账号:@mrgretzky,或者通过电子邮件(kuba@breakdev.org)来与我取得联系。
由于篇幅有限,如需获取完整信息,请您阅读原文。下次再见啦!
原文链接:https://breakdev.org/defeating-antivirus-real-time-protection-from-the-inside/
(责任编辑:安博涛)