Malware Dynamic Analysis Evasion Techniques: A Survey
来源:https://arxiv.org/pdf/1811.01190.pdf
摘要:网络世界充斥着不断演变的恶意软件,它们很容易渗透到所有的防御机制中,在用户不知情的情况下恶意操作,并暗中窃取敏感数据。了解这些恶意软件的内部工作原理可以有效地对抗它们。这种理解是通过手动或自动进行的动态分析来实现的。因此,恶意软件的作者设计和先进的规避技术来阻止或逃避这些分析。本文对恶意软件动态分析规避技术进行了全面的综述。此外,我们对这些技术进行了详细的分类,并进一步说明了它们对不同类型的检测和分析方法的有效性。我们的观察证明,躲避行为主要是为了探测和躲避沙箱。我们认为,这种恶意软件的主要策略是指纹识别,然后是旨在检测人类交互的反向图灵测试策略的新趋势。此外,我们将假设,当前的防御策略开始于为获得更透明的分析系统而采取的反应性方法,很容易被“零日指纹技术”或其它逃避策略(如拖延)所挫败。因此,我们建议采用更一般的防御战略,重点放在有可能挫败所有规避战术的路径探索技术上。
1介绍
Creeper通过ARPANET网络获得访问权限,并将自己远程复制到其他计算机上,并提示信息:“如果可以,请抓住我”。Creeper是1971年[1]实验中出现的,它没有任何有害的目的,但是,随着它的快速传播,它为个人或实体将其恶意意图并入软件(因此得名恶意软件)的未来打开了一道光,这些软件能够对受感染的系统[2]产生不想要和有害的影响。在那个年代,开发恶意软件只是为了炫耀;但是,在当今世界,随着互联网将每个个人、研究所和组织集成到一个统一的复杂系统中,恶意软件作者的目标已经扩展到更有利可图的目标,即金钱、智力和权力。或者,这些可能性已经深刻地激发了一个新的恶意软件行业,其结果是高度复杂的变种,恰当地渗透系统,并在用户或防御机制不知情的情况下运行恶意软件。
只有了解此类恶意软件的精确内部工作原理,才能设计出能够承受这种复杂程度的对策或技术。恶意软件分析是实现这一理解的途径。最初,在反汇编程序、反编译程序等的帮助下,分析人员检查了恶意软件的二进制代码和代码,以推断其功能。这种方法,也称为静态分析,随着代码混淆策略[4,5,6,7,8]和其他针对静态分析的规避策略(如不透明常数[9]、packers[10]等)的出现和发展,变得更加困难和复杂。作为一种解决方案,采用了一种很有前途的方法,即动态分析,其中分析和检测的基础是文件做什么(行为),而不是文件是什么(二进制和签名)[11]。换句话说,在动态分析中,运行可疑程序的实例,并在运行时检查其行为。这种方法将消除前面提到的静态分析规避策略所带来的障碍。然而,为了阻止这些努力,恶意软件作者转向了一种针对动态分析的新型规避策略。
在本文中,我们确定了手动和自动两种动态分析模式。手动动态分析是一种更为传统的动态分析形式,通常在调试器的帮助下进行。自动化动态分析是一种更新颖的方法,也是对安全供应商每天面临的不断增加的新示例的响应。自动动态分析通常由沙箱技术表示。此外,我们对针对每种分析模式的规避技术进行了全面的调查。
此外,我们还将描绘当前以及正在出现的规避趋势。在手动动态分析规避的情况下,我们假定反调试技术仍在大量实践中。同时,我们将讨论如何出现新的无文件恶意软件,并主导传统的动态分析。此外,在自动动态分析的情况下,我们将规避策略分为两大类:检测依赖和检测无关。我们认为,直到最近,沙箱技术一直专注于主要针对探测依赖规避策略的技术。作为回应,恶意软件作者正逐渐采用越来越独立于检测的策略来规避这种自动化的动态分析环境。
我们在本文中的目标是三重的。首先,考虑到这个主题的重要性,我们的目标是提供一个全面的分类,并希望向一个已建立的分类法迈进。其次,我们渴望揭示规避行为的趋势。因此,我们试图对每一种规避策略的普遍性提供一种对当前形势的看法。我们的第三个目标是找出当前防御策略的缺点,并提供一个我们认为在有效对抗恶意软件方面更有潜力的方向。
我们的贡献是:
•我们提供了一个全面的调查恶意软件动态分析规避技术的手动和自动模式。已经有一些关于恶意软件分析规避的调查,比如[12],它仅仅关注自动动态分析规避,并且只涵盖了一种(我们已经确定的五种)规避策略。其它调查如[13,14]一般调查分析规避,没有提供关于恶意软件动态分析规避的详细概述。
•对于手动和自动模式,我们提供了一个详细的分类恶意软件规避策略和技术。据我们所知,这将是第一次全面调查动态分析规避策略,提供了一个彻底的分类。
•我们描述了自动动态分析规避技术领域的当前趋势,并讨论了未来正确的行动方向。
1.1概述
1.1.1范围
最新的报告证明,Windows恶意软件在2017年以77.22%的惊人比例占据主导地位。统计数据还显示,由于使用了逃避技术,64%的反病毒扫描器在“[16]”1年后无法识别1%的恶意软件。这些数字进一步促使我们集中精力对付针对Windows OS的恶意软件。此外,在我们的研究中,我们努力探索学术界和工业界的交集。因此,我们回顾了学术论文和相关行业提供的文献。
1.1.2方法
我们的主要工作是提供动态分析规避技术的详细和生动的概述。为此,我们将审查结果分为三个层次:类别、策略和技术。每个类别都代表一种特定的方法和目标,通过不同的策略共享和追求。每种策略都是通过不同的技术实现的。在整个调查中,我们详细阐述了战术层面的细节,并讨论了几种具有代表性的技术。在每个部分的末尾,我们提供一个汇总表。
1.1.3评价
为了达到本次调查的目的,我们采用了几个标准,如不同技术的有效性和普遍性,作为相关主题比较的基础。在我们的汇总表中,我们提供了可获得统计数据的已审阅作品的数据,否则,我们根据自己的观察提供评估。
1.1.4组织
在第二部分中,我们讨论了手动动态分析(调试)和相应的反调试策略。在第三部分,我们讨论了自动动态分析和相应的规避策略。第二节和第三节之后是讨论。最后,第四部分对全文进行了总结。图1展示了我们提出的恶意软件动态分析规避策略的分类。
2前言
2.1逃避和透明度
在文献中,恶意软件为了保持隐身、避免检测或阻碍分析工作而使用的一系列技术构成了逃避。一个主要的逃避策略,我们将讨论,是指纹[17]。通过指纹识别,恶意软件试图检测其环境,并验证其是否驻留在生产系统或分析系统中。
同样,一个主要的策略是隐藏可能暴露分析系统的线索。如果一个系统对恶意软件[18]暴露的线索更少,它就会更透明。
2.2手动vs自动
手工分析和自动分析是构成我们分类基础的两个主要术语。手工分析是由专家人工执行分析过程。另一方面,自动化是由机器或软件自动执行的过程。
2.3检测vs分析
以前,没有必要定义这两个术语。检测只是指识别给定文件是否恶意的过程;另一方面,分析是指理解给定恶意软件如何工作的过程。然而,今天,这条分界线却模糊不清。原因是诸如沙箱之类的自动化分析工具的角色现在得到了扩展。除了报告恶意软件行为,沙箱现在还扮演着自动检测机制[18]的核心角色。在本文中,我们使用类似的概念来描述“分析”。对于“手动”,它意味着理解恶意软件行为[19],对于自动,它还意味着检测。
2.4静态vs动态
分析有两种主要类型,即静态分析和动态分析。静态分析是在不执行代码或二进制文件的情况下分析代码或二进制文件的过程。动态分析是研究恶意软件(API、系统调用等)在运行时的行为的过程。这两种类型的分析都可以手动或自动执行。在本文中,我们的重点是动态分析和恶意软件如何试图阻止或逃避这样的分析。
2.5分类、策略、技术
在本文中,我们使用“类别”、“策略”和“技术”( "category", "tactic", and "technique" )这三个术语作为分类的基础。规避类别是我们的高级分类。每一类都有规避的目标,并有具体的实现态度。这种态度与规避的有效性高度相关,是每一类策略所追求的。换句话说,策略是特定的策略或方法,以特定的态度,其父类别逃避。最后,技术是这些策略的各种实际应用。
3手动动态分析规避
如引言中所引,由于采用代码混淆、打包器等静态分析恶意软件已成为一项艰巨的任务。为了避免这种方法的难题和局限性,分析人员选择动态分析,在动态分析中,恶意软件的行为在运行时被检查,并且通常在调试器的帮助下进行检查。这种检查方法有两个主要好处;一是它使我们从包装器、多态性等因素造成的障碍中解脱出来。其次,采用这种方法使我们能够探索仅在运行时[20]中出现的活动,例如程序与OS[11]的交互。我们在术语“手动动态分析”下查看这种由调试器辅助的方法。与此方法相对应的规避策略涉及恶意代码中使用的一组技术,其目标是阻碍、阻碍或逃避分析过程。这些措施包括检测系统上是否存在分析工具(如wireshark、tcpdump等)或检测虚拟机作为分析环境的标志,但是,大多数手动分析规避技术都是针对调试器的,调试器是手动动态分析的主要工具。因此,在手动动态分析规避领域,我们将重点集中在反调试策略上。
简单地说,反调试是一种或多种技术的应用,用来避开、阻碍或逃避手工动态分析(调试)或逆向工程的过程。值得注意的是,与混淆类似的反调试技术是开发人员为保护其软件而进行的合法实践。这意味着反调试有着悠久的历史,随后出现了一系列广泛而多样的技术。在本节中,我们将对与恶意软件上下文相关的最常被引用的手动动态分析规避(反调试)策略和相应技术[11,20,21,22,23,24,25,19,26,27,28]进行调查。此外,对于每种技术,我们使用4个标准来更好地得出相对权重和疗效的概述。我们在表1中总结了我们的反调试研究。我们的比较准则及评估准则如下:
•复杂性:在恶意软件代码中整合和实现该技术的难度。如果该技术涉及检测调试器存在的简单API调用,那么我们将低复杂性与之联系起来;对于那些需要多次调用才能从系统中查询信息的调用,以及需要从返回的信息推断调试器存在的概率的调用,我们将其与中等复杂性联系起来;对于那些难以实现且需要30多行汇编代码的调用,我们将其与高复杂性联系起来。
•抵抗:抵抗属于对抗规避技术的难度级别。如果只是简单的改变一些系统信息(例如随机注册表键或PEB中的一点),我们认为它很低;如果战胜反调试技术需要用户级钩子和dll注入,我们认为是中等难度;如果需要内核级操作,例如驱动程序,我们认为是高等难度;如果对策要求操作低于操作系统(虚拟化、仿真等),我们认为它非常高。最后,我们有N/A,与尚未存在对策的技术有关。
•普遍性:由于恶意软件样本数据集的极端多样性,很难为每种策略的普遍性建立一个基本事实。然而,除了我们自己在该领域的观察和经验外,我们还努力根据其他工作[21,29]对这一指标提供一个公平的看法。
从理论上讲,我们为textbfCountermeasure分配了一个专栏,简要介绍了如何规避反调试技术。在详细研究反调试技术之前,我们将简要介绍调试器、它们的目标、类型和技术。在此基础上,我们可以更好地理解和阐述反调试策略以及它们的可行性。
3.1调试器简介
调试器是一种软件或硬件,用于测试或检查另一个程序[19]的执行情况。在调试器[30]的核心功能中,有几个对恶意软件分析非常关键的特性,比如每次执行一条指令,在需要的地方暂停或停止代码,检查变量,等等。为了提供上面提到的每个功能,调试器依赖于不同的策略,而且每个策略通常都得到特定硬件或软件规定的帮助,这些规定不可避免地会导致系统发生细微的变化。例如,为了提供暂停功能,调试器的策略之一是设置断点。使用断点可以通过特殊的硬件[31](例如CPU的DR寄存器和特定的API来访问/修改它们)或软件(例如特定的操作码等)[32]机制得到进一步的帮助。作为另一个实例,通过触发代码中的异常(由特定标志(trap标志)辅助),可以实现单步执行。恶意软件发现调试器存在的一种方法是通过相同的辅助机制或系统上相应更改的结果来发现调试器的存在。
基于所需的功能,调试器采用不同的方法[33]实现,包括用户模式调试器(如OllyICE、OllyDbg)、内核调试器(如WinDbg、KD)和基于模拟器的调试器(如Ether[34]、BOCHS[35])。每一个设计,都产生了不同层次的透明度。内核级调试器比用户级调试器更透明,并且在环0(与操作系统的权限级别相同)中运行时提供更详细的信息。基于模拟器的调试器拥有比内核调试器更高的特权级别,因为该设置中的操作系统运行在模拟(虚拟/仿真)硬件之上[34,35],因此对恶意软件更加透明。在下一节中,我们将继续手动动态分析规避,或者更具体地说,反调试策略。
3.2提出Anti-Debugging分类
我们提出了三个主要类别,作为恶意软件的初始反调试策略:直接检测(direct detection)、演绎检测(deductive detection)和调试器规避(debugger evasion)。正如前面所述,我们详细介绍了每个类别的战术级别,以及每个战术的一些代表性技术。最后,我们将检查结果总结在表1中。
3.2.1直接检测
我们将这类策略称为“直接检测”,即通过直接观察调试器的副产品和工件来检测恶意软件。可以发现它的存在。恶意软件对调试器的一个主要利用源于调试器最初是用来调试合法软件[20]的。因此,从一开始就没有他们提供的秘密对策。此外,当我们想要启用调试功能时,我们必须使用必要的工具对系统进行测试,这显然会导致在系统的不同级别上产生广泛的跟踪频谱[23,29]。检查系统中调试器工作原理所固有的跟踪,是恶意软件用来检测调试器是否存在的主要策略之一。
在本节中,我们将详细说明这些系统更改以及恶意软件可能如何试图公开它们。这类战术往往容易使用,也容易击败。从用户模式调试器转移到基于模拟的调试器,这类规避在效果上显得苍白无力。
3.2.1.1读取PEB进程环境块(PEB)是系统中每个进程都存在的数据结构,包含该进程[36]的数据。PEB的不同部分包含恶意软件可以探测到的信息,以检测是否存在调试器。最明显的一个是PEB中名为BeingDebugged的字段,它可以直接读取,或者按照微软的建议,通过读取该字段的特定api(即IsDebuggerPresent()、CheckRemoteDebuggerPresent())实现Kronos[37]或Satan RaaS[38]等恶意软件。该技术的实现复杂度很低,可以通过改变被调试的位[39]或API钩子[22]来解决。其他基于PEB的技术包括使用NtGlobalFLags和ProcessHeap,它们稍微复杂一些。总的来说,依赖于PEB的反调试策略构成了恶意软件[21]中观察到的大多数反调试技术。
3.2.1.2查找断点停止执行,调试器设置断点。这可以通过硬件或软件技术来实现。例如,在硬件断点中,断点地址可以保存在CPU DR寄存器或。在软件断点中,调试器将把特殊的操作码0xCC (INT 3指令)写入进程中,该进程是专门为设置断点而指定的。因此,如果恶意软件发现了这些断点的迹象,就假定存在调试器。这可以通过自扫描或完整性检查、查找0xCC或使用GetThreadContext检查CPU寄存器[25]来实现。后一种技术已被用于恶意软件,如CIH[40]或MyDoom。
这种策略是恶意软件库[21]中第二常见的反调试技术,而且易于实现(通常少于10行汇编代码就足够了)。然而,对抗这类战术并非易事。例如,在软件断点的情况下,调试器必须保存并提供由0xCC操作码[39]替换的原始字节的副本。
3.2.1.3系统构件从安装到配置和执行,调试器会在操作系统的不同层次留下痕迹,例如文件系统、注册表、进程名等。因此,恶意软件可以简单地寻找这些痕迹。FindWindow()和FindProcess()是两个api, shcndhss[41]利用它们来检测调试器。例如,恶意软件可以将调试器的名称作为FindWindow()的参数,以验证其进程是否存在于系统中[26,42]。最常见的情况是,简单的反调试技术被轻松击败。针对这类策略的对策是随机化名称,或者通过简单的API挂钩更改上述查询的结果。尽管在文献中被认为是一种反调试技术,但我们很少在野外观察到它。
3.2.1.4父进程检查通常,应用程序要么通过双击图标执行,要么通过命令行执行,父进程ID是可检索的。如果所检查的父进程名属于调试器,或者不等同于explorer.exe[25]的进程名,则它将是调试器的一个显式符号。一种直接的技术是使用CreateToolhelp32Snapshot()并检查父进程名是否与已知调试器[22]的名称匹配。对调试器进程名进行随机化就足以克服这种技术。另一种更复杂的方法是检查父进程ID和explorer.exe的进程ID。像[41]这样的恶意软件利用了以下函数:GetCurrentProcessId()、CreateToolhelp32Snapshot()、Process32First() Process32Next()[21]。如果父进程ID与explorer.exe的ID不匹配,则可能是调试器存在的标志。准确、有效地应用这一技术面临着一些挑战。如果explorer.exe有多个进程,那么它将有多个PID。这将使过程更加复杂,因为父PID可能匹配一个explorer.exe PID而不是另一个。应对这种策略的方法是跳过相关的api[29]。我们的观察表明很少使用这种策略[21]。
3.2.1.5挖掘NtQuerySystemInformation
ntdll NtQuerySystemInformation()函数接受一个参数,该参数是要查询[25]的信息类。这些类中的大多数都没有文档化。恶意软件可以通过多种方式利用这个函数,目的是发现调试器的符号。调试器很容易暴露,例如,如果恶意软件将SystemKernelDebuggerInformation() (0x23)类作为参数传递给这个函数,返回的值是两个标志;其中之一是KdDebuggerNotPresent(),如果该值等于零,它将验证调试器的存在。这种策略的使用并不简单(超过40行汇编代码[25]),也不能避免这种情况。原因是,当调用这个函数时,返回的值来自内核。按照这种策略击败反调试技术需要修补内核。vti-rescan[43]、Wdf01000.sys[44]和Inkasso trojaner[45]就是利用这种策略的一些例子。
如前所述,部署直接探测策略是相对直接和琐碎的,而这些又往往是微不足道的。更微妙的检测和逃避调试器的方法是通过演绎检测,这是下一节的主题。