APP下载

基于angr的对抗恶意代码沙箱检测方法的研究

2019-04-01张君涛王轶骏

计算机应用与软件 2019年2期
关键词:沙箱注册表寄存器

张君涛 王轶骏 薛 质

(上海交通大学电子信息与电气工程学院 上海 200240)

0 引 言

远控木马、僵尸网络、蠕虫以及近些年爆发的勒索软件、挖矿木马等各类恶意代码影响着用户对计算机的正常使用。针对特定目标的APT攻击,采用高度定制化的恶意代码进行鱼叉攻击,成为了国家信息安全面临的重大威胁。因而,恶意代码一直受到网络安全从业人员以及研究人员的广泛关注。

十多年前,研究者就已经采用沙箱技术来分析恶意代码。布谷鸟(Cuckoo)是一款开源且使用广泛的沙箱软件,利用虚拟化技术构造出与真实环境隔离且拥有快速还原能力的系统,研究人员通过分析代码在沙箱中的所有行为,进而判断是否为恶意代码。因此,针对恶意软件的分析沙箱被认为是防御APT的最后一道防线。病毒检测厂商比如CrowdStrike、Malwr以及国内的微步科技都利用沙箱技术提供恶意代码在线检测的服务。

恶意代码开发者为了逃避沙箱检测,防止自身暴露,会在恶意代码中加入环境探测代码,当发现自身运行于异常环境中时,就会停止恶意行为,运行无害代码或者结束运行,达到逃避检测的目的。据McAfee 2017年发布的《威胁报告》显示,反沙箱机制超过反调试、反监视、代码混淆等方法,成为恶意代码使用最为广泛的逃避检测手段[1]。此外,Lastline实验室在2015年指出,具有逃避检测功能的恶意代码的比例从2014年年初的35%上升至2014年12月的80%,并且往往结合数10种方法[2]。

本文采用angr对恶意代码进行动态分析。angr是一款基于Python实现,可利用动态符号执行进行二进制代码分析的调试器[3]。相比于现有的分析方法,动态符号执行可以遍历程序不同的分支,获得恶意代码更多的行为特征。这能有效避免外部环境比如C&C服务器的失效对分析恶意代码产生的影响[4]。相对的,反沙箱机制却会导致符号执行面临路径爆炸的难题,特别是当符号值作为循环语句的参数时,产生大量无害分支,导致难以发现真正执行恶意行为的分支[5]。

因而本文将首先对使用最为广泛的恶意代码沙箱检测机制进行分析;接着说明我们为angr制定的拓展程序,用于对抗不同类型的反沙箱机制以及原型系统的实现方法;最后通过对沙箱检测软件al-khaser以及paranoid fish的测试,证明我们开发的原型系统可以绕过常见的反沙箱机制,有效防止符号执行的路径爆炸问题。

1 沙箱检测常用技术

在恶意代码分析领域,Sandboxie、布谷鸟、Anubis等是常见的沙箱软件。然而,沙箱与真实环境存在一定的差别,沙箱为了监控恶意代码的行为、阻止恶意代码从沙盒逃逸,部分采用进程注入、函数挂钩等机制监控恶意代码的行为[6-8]。有些沙箱利用虚拟化技术构建真实操作系统,但这些虚拟机存在特定的指纹信息[9-11]。因而,恶意代码反沙箱方法可以分为基于沙箱的检测以及基于虚拟机的检测。

1.1 基于沙箱的检测

Sandboxie与被分析的恶意代码同处一个操作系统内,为了避免恶意代码对真实系统产生影响,沙箱内的程序对真实系统的文件、注册表、进程的访问都需要获得Sandboxie的授权。Sandboxie通过在内核驱动上挂载SbieDrv.sys实现对系统服务描述符表(SSDT)、Shadow SSDT表的挂钩。通过将SbideDll.dll注入到进入沙盒的程序中,实现对所有用户态API函数的挂钩。因而恶意代码通过检测程序自身是否被注入SbideDll.dll,即可成功绕过Sandboxie沙箱的检测。

布谷鸟沙盒是蜜网社区2010年谷歌编程之夏的开源项目,经过数年的开发,其功能与性能不断升级,成为了目前较为流行的沙箱系统。布谷鸟沙箱可分为主从节点,主节点主要负责任务的分发以及对恶意代码运行结果的分析展示。从节点为恶意代码的动态运行提供真实的系统环境,为了能够实现系统的快速还原,从节点一般为虚拟机。布谷鸟同样采取dll注入的方式来监控Windows API的调用。此外,布谷鸟还采用API挂钩的方法,因而David编写的anti-cuckoo通过检测ntdll.dll中的某几个关键函数是否被篡改来探测布谷鸟程序。此外,anti-cuckoo通过检查进程的内存空间中是否存在敏感词,比如cuckoomon、HookHandle、retaddr-check等来探测是否运行于布谷鸟中。

1.2 基于虚拟机的检测

由于虚拟机具有快速还原的特点且与真实系统隔离,不仅仅是上一节叙述的布谷鸟、Anubis等沙箱使用虚拟机作为恶意代码分析的宿主机,反病毒研究员在日常分析恶意代码行为时,也大都使用虚拟机作为分析系统。常用的虚拟机有VirtualBox、Vmware、KVM、Xen等。恶意代码针对虚拟机的检测如图1所示。

图1 针对虚拟机检测方法的分类

根据虚拟机操作系统的属性进行探测是恶意代码检测虚拟机的各类方法中手段最为丰富的一种方法。在虚拟机操作系统内,注册表、文件系统、进程、服务中存在各种虚拟机指纹特征。据统计,安装在Vmware上的WindowsXP虚拟机有超过50个与“Vmware”、“vmx”有关的文件,在注册表中有超过500项键或键值与“Vmware”有关。此外,分析人员可能为用于恶意代码分析的虚拟机设定特别的用户名或重新命名恶意代码程序,因而恶意代码可以通过将这些敏感位置的名称与自定义的黑名单列表匹配,来检测是否为虚拟机。

虚拟机的硬件并非真实的硬件,因而通过模拟出的硬件信息也能够探测虚拟机。和操作系统一样,硬件也存在多种多样的指纹信息,其指纹检测包括对CPU、BIOS、硬盘类型的判断以及对CPU名称、网卡地址、网卡名称、可插拔硬件设备的检测。除此之外,考虑到为虚拟机分配的硬件资源往往小于真实计算机的硬件资源,因而可以通过对CPU核心数、内存大小、硬盘容量的检测来判断是否为虚拟机。在虚拟机中,指令运行的速度远远慢于真实系统,因而部分恶意代码通过计算指令运行的时间来检测虚拟机。

基于进程的检测是恶意代码对自身运行时虚拟内存空间的探测,常见的方法是检测中断描述符表(IDT)、全局描述符表(GDT)以及局部描述符表(LDT)在内存中的地址。真实系统中,这些描述符表的位置基本在确定的地址附近,且完全不同于虚拟机,因而具有较高的检测正确率。

最后,是对特殊检测方法的汇总。反图灵测试是主动探测人机交互的行为,在一定的时间间隔内,两次获取当前鼠标在屏幕上的位置坐标,比较两次位置是否发生变化,从而检测虚拟机。休眠是指在程序运行后,休眠一段较长的时间,再执行恶意行为,往往虚拟机在恶意代码真正运行前已停止工作,因而可以有效地躲避分析。

2 对抗沙箱检测方法

2.1 angr介绍

angr是美国圣巴巴拉大学ShellPhish团队研发的一款二进制分析框架,其源代码由Python编写,在2015年项目得到开源,目前项目已迭代至第七个版本。angr可以实现动静态符号执行、控制流平坦化、自动化生成ROP链,应用于漏洞挖掘与软件破解,同时也是CTF比赛中逆向分析的常用工具之一[3,12-13]。使用者通过导入angr库并编写Python脚本实现对二进制代码的加载与分析。

angr由以下几个子模块组成,每个子模块都可以独立加载:

CLE(CLE Loads Everything)将可执行程序加载进内存空间。例如,对于EXE程序,通过解析文件头,获得加载地址、各段对应的文件与内存的映射关系,进而将主文件加载至虚化的内存空间。接着,递归加载程序需要的dll文件。

Archinfo针对不同架构的指令集,如x86、ARM、MIPS、PowerPC等,提供专有的类。每个类中都涉及寄存器类型、处理器位数、数据在内存中的存储方式以及是否支持VEX、Capstone、Unicorn、KeyStone等特性。

PyVEX可以将机器代码转化为Valgrind使用的中间语言VEX。通过VEX模拟执行程序的代码并修改内存、寄存器的值。

Claripy是一个符号执行求解器,其后端为Z3。

利用angr编写Python脚本,对二进制程序进行分析的整体流程如图2所示。首先,通过Project方法,加载程序至内存空间,并得到包含加载对象名称、各段地址空间、导入函数地址空间等属性的project对象。接着,利用State方法初始化程序堆栈以及寄存器,得到可以动态执行的对象SimState。通过SimState对象可以查看、修改、符号化内存空间以及寄存器的值,并且提供用于保存用户自定义属性的插件。由于动态符号执行会产生分支,得到多个Simstate对象,仿真管理器(Simulation Managers)提供了对所有分支进行访问以及管理的功能。此外,仿真管理器在程序运行到某个地址或满足某种条件时,停止对应分支执行或者丢弃分支。

图2 基于angr的程序分析流程

2.2 angr对抗沙箱检测方法的原理

具有沙箱检测功能的恶意代码,大部分结合了十种以上的检测机制,基于符号执行的特点,会带来路径爆炸的问题,淹没恶意行为的分支。由于angr动态的模拟程序运行,可以方便地对Win32 API函数挂钩、重写VEX指令、完善内存结构。因而,针对于各种恶意代码对沙箱的探测,都可以采取措施进行对抗。

2.2.1 Win32 API挂钩

恶意代码与普通EXE程序一样,在运行时动态链接系统提供的库函数。angr在模拟程序动态运行时,提供了两种方法实现Win32 API函数。由于CLE Loader已经加载主程序以及其导入的dll文件至相应的地址空间,因而可以直接运行API函数对应的代码。对于大小写变换、字符串比较型的函数,比如tolower()、wcsstr()、lstrcmpiA(),这种方法可以达到很好的效果。但是,对于大多数需要系统信息或者进行内核函数调用的函数,比如CreateFileA()、GetModuleHandleA(),往往会产生众多分支,导致路径爆炸甚至出现分支异常终止。第二种方法是对Win32 API函数挂钩。首先,通过构建SimProcedure类,并在类中定义与实际API函数具有相同参数数量以及类型的run函数模拟API的实现。在程序加载后,对project对象使用hook_symbol方法替换Win32 API函数。在程序执行时,当指令指针指向Win32 API函数的首地址时,实际执行的是run函数中的Python代码。

注册表、文件、进程、服务的指纹特征是恶意代码检测沙箱最为常见的方法。表1展示了沙箱检测方法对应的API函数调用序列。挂钩这些函数并修改返回参数以及返回值即可绕过沙箱检测。

表1 沙箱检测方法调用的API函数序列

以RegOpenKeyExA函数为例,查阅微软Win32 API手册,其参数分别为主键(hKey)、子键(lpSubKey)、ulOptions、访问权限(samDesired)、注册表句柄(phkResult)。如图3所示。

图3 RegOpenKeyExA函数的挂钩流程

函数存在以下三种情况:

(1) 首先定义敏感键列表,包括vm、vmware、virtualbox、qemu等对注册表检测的核心词语。接着,传入的参数子键与敏感键列表匹配,如果匹配成功,则函数返回值为2(ERROR_FILE_NOT_FOUND),表示注册表项不存在。这种方式,可以成功对抗恶意代码对注册表特定键值的检测。如果匹配失败,则执行第二种情况。

(2) 首先定义敏感键值字典,字典中的键表示注册表的子键,值表示注册表的键名和键值,比如“HARDWAREDEVICEMAPSCSISCSI PORT 0SCSI BUS 0TARGET ID 0LOGICAL UNIT ID 0′: {′IDENTIFIER′: ′INTEL′}”。接着,比较传入参数的子键是否等于敏感键值字典中的键,如果相等,则随机产生注册表句柄值并且函数返回值为0,表示注册表存在并且成功打开。

(3) 如果前两种情况都不满足,根据符号执行的特点,将程序分支并符号化输出。对于RegOpenKeyExA函数,存在打开文件成功并写入注册表句柄以及打开文件未能成功两种情况。前一种情况,符号化句柄且函数返回值为0;对于后一种情况,无需对句柄进行赋值,只需符号化返回值。

如表1所示,打开注册表键值需要两个函数,对于后序的RegQueryValueEx函数,其传入的注册表句柄参数是一个随机化的数值。为了获得其对应的子键名称,进而结合键名、键值两个参数与定义的敏感键值字典进行匹配,需要在RegOpenKeyExA函数中映射注册表句柄与子键名称。利用angr提供的自定义插件功能,可以为当前SimState增加属性,从而实现多个函数之间的状态共享。

2.2.2 VEX指令修补

angr在模拟程序动态执行时依据中间语言VEX完成各项操作。VEX兼容大部分x86指令,实现对寄存器、内存的修改,但是对于某些特殊指令,如表所示,当程序分支执行这些指令时,该分支的状态变为错误并且停止运行。虽然angr对极少部分VEX指令,比如CPUID进行了修补,但不能满足实际恶意代码分析的需要,因而,我们对表2中的VEX指令都进行修补。

表2 x86特殊指令与VEX指令的对照表

以指令CPUID为例,如图4所示。eax寄存器是指令的输入参数,eax、ebx、ecx、edx寄存器保存指令执行后返回信息,根据输入参数不同,可以获得CPU类型、制造商、商标、序列号、缓存等一系列信息。在沙箱检测中,当输入参数为0x1时,往往是对返回值中ecx寄存器的最高位:hypervisor CPU位的检测,此时将ecx置为0,其他寄存器符号化。当输入参数为0x40000000时,执行指令获得CPU型号并保存在ebx、ecx、edx寄存器中。每个寄存器代表4个字符,共同构成12字符的型号名称。通过与″KVMKVMKVM″、″VMwareVMware″、″XenVMMXenVMM″等字符比较,进而判断是否为虚拟机环境。当输入参数分别为0x80000002、0x80000003、0x80000004且执行三次CPUID指令,可以获得共48个字符的CPU名称。通过赋值普通字符,比如″Intel(R) Core(TM)2 CPU 6600 @2.4 GHz″可以对抗检测。

图4 CPUID指令执行流程

2.2.3 内存结构完善

angr利用state方法初始化程序,设定寄存器以及内存空间的值,虽然可以实现代码执行,但不同于正常操作系统,没有在内存中设置完整的PEB、TEB、TLS等结构体。由于恶意代码通过读取结构体对应位置的值获取硬件信息,比如PEB进程环境块的0x64字节代表CPU数量,进而判断是否运行在虚拟机中。因而需要对angr的初始化文件打补丁,完善程序内存中的结构体。

2.2.4 angr特性

angr自身的特性同样能够对抗恶意代码对沙箱的部分检测方法。对于dll注入的检测,由于angr从外部实现对恶意代码执行流程的监控,其内存空间中不存在附加的dll函数。同时,angr采用的Win32 API挂钩并没有改变内存空间中API函数的内容,从而绕过将API函数第一个操作码与跳转指令对应的操作码比较的检测方法。

通过上文所述的各种方法,可以对抗大多数恶意代码对沙箱的检测。对于未知的检测手段,基于angr动态符号执行的特点,需要做到在其所有分支路径中包含恶意行为的分支并且尽可能减少产生的分支数目。考虑到执行Win32 API函数内部的代码会造成路径爆炸,因而挂钩所有Win32 API函数(除2.2.1节描述的字符串变换、比较型的函数),且在函数体内不执行任何操作,仅仅将函数的返回值设定为符号值。并且由于angr将未赋值的内存空间作为符号值代入动态执行,虽然可能增加路径分支的数目,却可以确保恶意行为分支不被遗漏。

2.3 angr对抗沙箱检测方法的实现

基于angr对抗恶意代码沙箱检测的原型系统的实现如图5所示。首先,在程序加载时,将auto_load_libs以及except_missing_libs两个参数设置为真,并指定加载dll文件的路径;其次,利用analyses对象中提供的CalleeCleanupFinder方法,对所有导入的API函数挂钩,挂钩的函数体内不执行任何操作;接着,将Win32_API_Hooking.py文件中定义的函数挂钩程序,对于同一个API函数,这一步的挂钩将覆盖CalleeCleanupFinder中的挂钩;之后,利用angr提供的SimStatePlugin对象,注册可以增加SimState属性的插件。最后,使用动态符号执行模拟程序的运行。

图5 对抗恶意代码沙箱检测的原型系统

对于2.2节中提到的方法,在原型系统实现时,对于Win32 API函数挂钩,只需要在Win32_API_Hooking.py文件中创建与API函数名称一致的类并实现函数执行的方法,就可以通过angr提供的接口函数hook_symbol将API函数挂钩。而对于VEX指令修补以及内存结构完善两种方法,需要通过修改angr的源代码,从而实现功能,对应修改的文件分别是angr/engines/vex/dirty.py以及angr/simos/windows.py。

表3列举了原型系统对抗沙箱检测各类方法时采取的应对措施。考虑到不同指令、函数功能上的差异,相应的修补方式也不尽相同,修补代码的实现遵循这两条原则:对于单指令、单函数的修补,首先判断输入的参数与沙箱检测时使用的参数是否一致,如果能够匹配,使用对抗沙箱检测的方法,否则符号化函数的输出参数以及返回值;对于序列化的函数,采用的修补方式不仅需要满足单函数修补原则,并且利用插件功能记录程序执行的状态,后序函数实现时需要考虑前序函数及其参数是否已经满足沙箱检测的条件。

表3 对抗沙箱检测的方法

续表3

3 测试与分析

基于angr对抗沙箱检测方法的原型系统使用最新的angr7,并且通过OSX Sierra系统下Python2.7中的测试。用于angr加载恶意代码的dll文件拷贝于32位Windows XP SP3系统中的system32文件夹。al-khaser以及paranoid fish是两款用于对沙箱、虚拟机进行检测的开源项目,均使用C++编程实现,其搜集并总结了122类恶意代码使用的方法,并且al-khaser仍处于不断更新的状态。为了便于分析,我们使用32位编译器生成用于分析的EXE文件。

微步科技提供的微步云沙箱,内核集成了人机交互、反沙箱检测、内核级分析、网络模拟等多个高级分析模块。CrowdStrike公司的VxStream沙箱是基于机器学习的下一代恶意软件扫描器。值得一提的是,可以在其扫描配置中将反沙箱检测设为强等级。对于al-khaser以及paranoid fish,我们分别使用布谷鸟沙箱2.0.4版本、微步云沙箱以及强反沙箱检测等级的VxStream进行扫描,其分析环境均为Windows 7,与原型系统分析比较的结果如表4所示,其中T代表恶意代码检测出沙箱环境、F代表未能检测出沙箱环境。为了简化表格,表4中没有列出四款系统均能识别的沙箱检测技巧以及paranoid fish与al-khaser中重复的检测技巧。

表4 针对al-khaser以及paranoid fish的检测结果

续表4

从表4的结果可知,相比于开源的布谷鸟平台,微步云沙箱以及VxStream沙箱在对抗恶意代码检测沙箱环境方面均有一定的提升,特别是设置了强反沙箱等级的VxStream检出率仅仅为7/122。但是对于由汇编代码直接获取系统信息的检测方法,比如cpuid、rdtsc,这几种沙箱都未能成功对抗。本文设计的原型系统能够成功绕过大部分检测方法,因而显著优于现有的在线沙箱检测平台。

4 结 语

本文基于angr提出了一种能够对抗恶意代码针对沙箱环境进行探测的二进制代码分析方法。原型系统采用Win32 API函数挂钩、VEX指令修补以及内存结构完善三种方法,针对常见的恶意代码反沙箱机制采取相应的修补措施。实验表明,本文提出的方法在对抗恶意代码沙箱检测方面,优于其他传统的方法。因此,在本文的基础上,利用angr动态符号执行的特点,自动化地捕获恶意代码的行为特征是我们下一步的研究工作。

猜你喜欢

沙箱注册表寄存器
Lite寄存器模型的设计与实现
常用电子测速法在某数字信号处理器中的应用*
Removing a stone
巧用沙箱检测文件安全
移位寄存器及算术运算应用
更上一层楼 用好注册表编辑器
用上所有的力量
文件检测方法及沙箱
注册表的便捷用法
注册表编辑器也玩“失忆”