AndroNeo:通过预测规避启发式技术增强 安卓恶意软件沙箱
1 引言
安卓驱动了大多数移动设备,并且最近已超过Windows,成为互联网上使用最广泛的操作系统[1]。反病毒公司迈克菲报告称[2],在2016年第四季度,他们收集到的唯一移动恶意软件样本数量相比第三季度增长了72%,仅第四季度就检测到超过240万个恶意软件。这些惊人的数字使得自动化恶意软件分析工具对分析人员而言至关重要。一种流行的自动化恶意软件分析方法是使用恶意软件分析沙箱,在该环境中,分析人员设置一个运行恶意软件样本的环境,同时收集其相关操作和行为以供分析。诺伊纳等人[3]对现有的安卓恶意软件沙箱进行了有趣的比较,其中系统模拟在提供安全检查环境方面起着核心作用。复杂的恶意软件通常会采用一些技术来检测并阻碍沙箱的分析。一种常见的方法被称为双面性恶意软件[4],其通用规避方法如下:
if(isSandbox()){
System.exit();
} else{
continueMaliciousOperations();
}
这种代码模式允许恶意软件根据所处环境是否为沙箱,表现出两种不同的行为,即执行
System.exit()
或
continueMaliciousOperations()
。区分因素由
isSandbox()
函数确定,该函数中实现了沙箱检测技术。这些检测可以是一行简单的验证,也可以是更复杂的检查,例如性能分析。此外,恶意软件还可以将恶意操作转移到仅在真实设备上才可能触发的事件处理器中(例如短信接收事件)。
在本研究中,通过开发AndroNeo,我们展示了自动生成基于模拟器的沙箱检测技术的能力,同时为沙箱提供了规避和禁用恶意软件“分裂人格”行为的手段。换句话说,我们成功预测了
isSandbox()
函数内部进行的检查,并确保这些启发式检查即使在沙箱环境中运行时也会失败。这确保了
continueMaliciousOperations()
的行为能够暴露给沙箱探测机制。此外,我们证明了Jing的[5]启发式预测基础为我们的工作提供了一个可靠的起点。我们分析了一组真实的恶意软件样本,揭示了所识别出的启发式数据的存在。结果表明,大量恶意软件样本实例使用了所发现的启发式方法,表明其可能采用了某种模拟器或沙箱检测技术。我们还开发了一个原型实现,利用启发式预测过程中收集的数据,自动生成沙箱加固能力。我们的评估表明,所提出的技术能够在无需事先了解所采用的启发式检查的情况下,预测并禁用沙箱检测能力。
2 背景与相关工作
2.1 沙箱检测
使用模拟器是沙箱构建的基础(例如 Ananas [6]和 Mobile-sandbox [7]),因为它能轻松提供隔离性和高效系统状态恢复。然而,模拟器为恶意软件通过沙箱检测技术逃避沙箱分析提供了易于攻击的目标。例如,Jing 等人 [5]开发了一种工具(Morpheus),能够自动生成可检测安卓模拟器的启发式规则。
Pestas 等人 [8]概述了三类逃避技术,分别基于静态属性、动态传感器信息以及安卓模拟器的虚拟机相关复杂性,这些均可用于沙箱检测。Maier 等人 [9]开发了一种名为 Sand-Finger 的工具,该工具采用指纹识别方法,收集多个沙箱的信息并利用
这些信息来识别正在使用的特定沙箱。Vidas 和 Christin [10]根据行为、性能、硬件组件和软件组件的差异,将沙箱检测技术分为四类。这些沙箱检测技术对恶意软件分析人员来说是个问题,因为它们暴露了沙箱潜在的弱点,而这也正是沙箱加固发挥作用的地方。
2.2 沙箱加固
加固沙箱是指不容易被绕过的沙箱。Gajrani 等人[11]考虑了这一威胁,并着手开发技术,帮助恶意软件分析人员通过识别常用的沙箱检测技术,并应用模拟器修改、系统镜像修改以及运行时钩子来修补这些漏洞,从而构建加固的沙箱分析环境。另一种沙箱加固方法是使用裸机设备,从而完全避免模拟的需求。
Mutti 等人使用工具 BareDroid [12] 应用了裸机方法。Kirat 等人 [13] 提到,恶意软件沙箱在质量、隐蔽性和效率之间始终存在矛盾。试图改善其中一个因素通常会导致另外两个方面的妥协。裸机方法在成本和时间方面带来了许多负面的效率影响。例如,在 BareDroid 的实验中,作者提到每次分析后所需的完整恢复耗时 141 秒,从而导致可扩展性问题。相比之下,模拟器上的系统恢复时间几乎可以忽略不计。他们还指出,由于依赖系统模拟的探针(如对原生代码的污点分析)无法使用,分析质量常常受到不利影响。此外,在尝试解决这些问题时,使用裸机沙箱可能会产生新的沙箱检测启发式规则。显然,由于系统模拟所提供的优势,裸机方法并非万能方案,基于模拟器的沙箱不会被裸机沙箱完全取代。在我们的研究中,我们将专注于加固那些仍基于模拟的沙箱。
沙箱加固可以应用于沙箱的不同组件。一种方法涉及修改模拟器属性[11]。某些属性易于修改,但其他属性无法直接修改,需要调整硬件模拟。这些修改具有较高的敏感性,因为任何配置错误都可能轻易导致系统损坏。除了修改模拟器之外,还可以选择直接修改应用程序的字节码。通过直接在应用程序中修改用于检测沙箱的代码部分,可以强制应用程序在沙箱中运行时仍无法通过沙箱检测检查。另一种加固技术涉及修补Android框架。通过修改Android框架的API,可以控制类方法调用的结果,从而操纵沙箱检测验证检查。尽管这种方法可能非常有效,但缺点是需要针对每个Android操作系统版本进行定制化修改。
3 AndroNeo
AndroNeo基于一种逃逸启发式生成技术Morpheus[5]。虽然Morpheus专注于自动生成沙箱检测启发式规则,AndroNeo则旨在提供自动化的沙箱加固能力。所提出的技术包括利用生成的启发式规则以及在生成过程中获得的属性,以生成针对恶意软件Dalvik字节码的沙箱加固补丁。
区分特征的概念是一个核心组成部分:区分特征指可用于将环境分类为沙箱或真实设备的独特特征。这些区分特征可分为两类:沙箱特征区分符或设备配置文件区分器。沙箱特征区分符指的是一组包含大多数沙箱环境中常见属性的区分器。换句话说,如果正在分析恶意软件的系统包含沙箱配置文件中的某个属性,则该系统很可能是沙箱。另一方面设备配置文件区分器指的是一组包含大多数移动设备中常见属性的区分器。换句话说,如果正在分析恶意软件的系统包含设备配置文件中的某个属性,则该系统很可能是真实设备而非模拟器。
如图1所示,区分特征生成阶段分为两个阶段。第一阶段从侦察任务开始,通过对真实设备和沙箱样本进行操作,生成包含候选区分特征的侦察数据集。该阶段的第二步是生成设备配置文件和沙箱配置文件,这些配置文件由计算得出的配置文件区分器表征,从而识别其类型。生成的区分器配置文件将在第二阶段用于自动强化沙箱。这些区分器配置文件本身提供了定位补丁点以及生成禁用逃避行为的代码补丁所需的必要信息。以下各节将详细描述各个步骤。
3.1 侦察
设 S 为 n 样本沙箱的集合,D 为 m 样本设备的集合,S={s1, s2,…, sn}和 D={d1, d2,…, dm}。令 X 表示所有样本沙箱和设备的集合,即 X= S∪D。
侦察(recon)阶段涉及从沙箱和设备中提取数据,这些数据可能用于识别沙箱。侦察通过解析 S 和 D 中每个元素的所有 Android API类 [14] ,并调用其所有可用的方法以及读取类常量来实现。每次在沙箱或设备上执行侦察都会生成我们称之为侦察数据集的数据。设 Mx 为可从沙箱或设备访问的所有 Android API类 的类常量和方法的集合 x:Mx={m|m ∈(ApiMethodCalls ∪ ApiClassConstants) ∃x ∈X},例如:Mx={getDeviceId()…, getLineNumber() Build.DEVICE,…, Build.SERIAL}。函数 v(m, x) 表示在 X 的上下文中调用/读取 m 时返回的值,例如: v( getDeviceId() s1)=
“000000000000000”。集合 Rx 表示通过调用设备或沙箱可用的所有方法和常量所获得的键值对结果集,即:Rx={(m, v(m, x)) ∀m ∈Mx, ∃x ∈X},例如:Rs1={(getDeviceId(),”000000000000000”)…, (getLineNumber(),”15555215554”)}。集合 RX 表示来自 X 中设备和/或沙箱集合的所有 recons 的集合,即:RX={Rx|∀x ∈X}。
3.2 计算区分器
设 r=(m, v(m, x)) ∈ Rx ,其中 m ∈ Mx且 x ∈ X。我们关注比值 |rS | | | S和 |rD | | | D,其中 |rS| 和 |rD| 分别表示r在S和 D中出现的次数。例如,如果在我们的样本沙箱集合中,每个沙箱的 getDeviceId() 都返回 “000000000000000”,那么我们关注比值 |(getDeviceId(),“000000000000000”) S | | | = 1 S。该比值告诉我们所有沙箱都返回了”000000000000000”这一值,因此
指向一个可用于识别沙箱的属性,即沙箱配置文件区分特征。
另一方面,例如,如果在我们的样本设备集合中,类常量Build.TAGS的值对所有设备而言均为”release-keys”,那么我们关注的是比值|(Build.TAGS,“release−keys”)D| | D | = 1。该比值告诉我们所有设备均返回了“release‐keys”值,因此这指向了一种可用于识别设备的属性,即设备配置文件区分器。
沙箱配置文件 。集合 PS 表示所有沙箱配置文件区分器:PS={(m, v) | (m, v) =(m, v(m, si)) ∈Rsi, |rS| | S| > τ>|rD| | | D , ∃si ∈S}。PS中的元素为我们提供了一份可用于识别沙箱的属性列表。假设我们有一个设备或沙箱 x0,我们想要确定 x0 是否为一个沙箱。对于每一个 (m, v(m, x0)) ∈ Rx0,如果 (m, v(m, x0)) ∈ PS,则表明 x0 是一个沙箱。
设备配置文件 。集合 PD 表示所有设备配置文件区分器:PD={(m, v) | (m, v) =(m, v(m, dj)) ∈R dj, |rD| | D| > τ>|rS| S , ∃dj ∈D}。PD 中的元素为我们提供了一组可用于识别沙箱(或更确切地说,缺乏真实设备)的属性。假设我们有一个设备或沙箱x0,我们想要确定 x0是否为沙箱。那么对于每一个 (m, v(m, x0)) ∈Rx0,如果 (m, v(v(m, dj))∈/PD,则表明 x0不是真实设备,因此是沙箱。沙箱和设备配置文件分别对应 Morpheus[5] 所称的 S-pool 和 D-pool。此外,我们的配置文件使用了一个可调节的阈值 (τ),并在补丁生成阶段保留所获得的设备数据集值。
3.3 补丁生成
设 s0为需要加固的沙箱。由于 s0实际上是一个沙箱,因此对于每个(m, v) ∈ PS,很可能也存在(m, v(m, s0)) ∈ PS。这些情况可能为恶意软件提供沙箱检测能力。为了隐藏这些值的存在,我们将重新使用在侦察阶段收集的数据。对于每个(m, v) ∈ PS,我们需要找到一个对应的(m, v(m, di)) ∈ Rd i,其中 di ∈ D且(m, v(m, di))∈/ PS。然后可以修改字节码,以确保对于每个(m, v) ∈PS, v (m, s0)返回 v(m, di),从而模拟真实设备的值。
对于设备配置文件区分器而言,由于 s0是沙箱,因此很可能对于每一个( m, v) ∈PD,(m, v(m, s0))∈/PD。这一次,需要以如下方式修改字节码:对于每一个(m, v) ∈ PD,v(m, s0)返回 v,从而模拟真实设备的值(因为此时 v表示设备上常见的值)。使用以下过程来修补恶意软件的字节码:
输入:检测沙箱的恶意软件APK
输出:已修补的恶意软件APK′
bytecode = apk.getBytecode();
对于(m, v) ∈ PS执行
deviceVal = v(m, di) | (m, v(m, di)) ∈Rdi, ∃di ∈ D,并且(m, v(m, di))∈/ PS
bytecode.scan(m).replaceWith(deviceVal);
结束
对于(m, v) ∈ PD执行
bytecode.scan(m).replaceWith(v);
结束
返回 apk.rebuild() -> apk′;
4 实验
为了评估自动生成的沙箱检测启发式方法的能力,同时验证刚刚提出的加固过程的有效性,进行了一系列实验。
4.1 实验设置
我们开发了一款安卓应用程序,通过使用反射API调用所有可能的Android API类方法和常量值来收集数据。使用Apktool对应用程序进行解码和重新编译,同时通过一系列bash脚本根据识别出的区分器对Apktool生成的smali代码进行修补。实验所使用的环境包括多个Android沙箱和模拟器(Sanddroid [15], NVISO ApkScan[16], Droidbox[17],Android 7.0 模拟器、Android 6.0 模拟器),以及一组Android设备(三星Galaxy S4、Nexus 5x、Nexus 5、 Nexus 6P、OnePlus X)。此外,我们还使用了来自VirusShare的7160个真实恶意软件样本[18]。在我们的案例研究中,我们采用了流行的DroidBox沙箱,它也是多个Android沙箱底层的动态分析工具[3]。
4.2 区分器配置文件
为了验证所识别的配置文件区分器的相关性,并测试其在识别规避检测方面的潜力,我们对恶意软件样本进行了交叉验证。表1列出了前10个沙箱配置文件区分器的结果。该结果通过统计包含(m, v) ∈ PS实例的恶意软件样本来计算得出,其中 m和v出现在同一个类中。例如,结果显示,在7160个恶意软件样本中,我们发现有2395个应用程序调用了方法getDeviceId()。单独调用此方法本身不一定可疑,但当我们发现其中有794个应用程序还在同一类中查找字符串”000000000000000”时,这一事实便增加了该调用用于模拟器检测目的的可能性。表1中的数值在一定程度上较为保守,因为它们仅表示调用实例(例如: getDeviceId())和相应的区分特征值(例如: “000000000000000”)出现在同一个类中的情况。很可能还存在一些额外的情况,其中区分特征值被定义在与区分特征方法/字段调用不同的类中。此外,也有可能区分特征值经过加密或哈希处理,因此在对恶意软件样本进行交叉检查时未被识别。然而,在实际修补过程中,这些情况仍将通过字节码修改方法进行修补,因为被修改的是字段或方法调用(例如: getDeviceId())。这些结果表明,自动生成的启发式规则极有可能被野外的恶意软件家族用于检测沙箱或模拟器,从而验证了Jing的[5]假设。
| 方法/字段 | 计数值 | 计数 | |
|---|---|---|---|
| Build.MODEL | 2478 | sdk | 2352 |
| getDeviceId | 2395 | 000000000000000 | 794 |
| getNetworkOperatorName | 1522 | 安卓 | 665 |
| Build.DEVICE | 1150 | generic | 594 |
| Build.BOARD | 992 | 未知 | 561 |
| Build.MANUFACTURER | 1404 | 未知 | 255 |
| Build.CPU ABI | 306 | x86 | 95 |
| getSubscriberId | 1830 | 310260000000000 | 53 |
| getSimOperatorName | 432 | 安卓 | 23 |
| Build.TAGS | 378 | 测试密钥 | 17 |
4.3 案例研究
为了验证检测到的配置文件区分器包含实际的规避检测,并且所提出的补丁生成步骤能有效禁用它们,我们选择了两个具有代表性的样本进行更深入的调查。
我们选择这些样本的原因是它们属于两种广泛传播的恶意软件家族Crosate和 Pincer,并且有关其行为的详细文档可查[19]。这些信息提供了用于与 AndroNeo获得的结果进行比较的真实基准。通过Droidbox,我们对原始样本和已修补样本进行了分析,并将两种情况下的实际行为与真实基准进行对比。
我们生成了一个字节码加固类,其中包含AndroNeo伪造已识别区分器返回值所需的数据
(即 bytecode.scan(m).replaceWith(v) 步骤,见第3节C部分。3以下代码是加固类的一个片段。
# Field Declarations
const-string v0,"release-keys"
sput-object v0, Lharden/Harden;->FIELD18:Ljava/lang/String;....
# Method Declarations
.method public static method14()Ljava/lang/String;....
const-string v0,"353627074120224"
return-object v0
.end method
Crosate . Crosate 是一种能够窃取短信、通话记录、联系人信息,发送短信,录制通话并拨打电话的机器人程序。然而,在 Droidbox 中运行时,它会自行终止,从而隐藏所有与命令与控制(CNC)服务器的通信。下面的代码列表展示了 Crosate 中进行沙箱检测的部分:
public void onCreate(){
....
String BotID= tm.getDeviceId();
....
if(BotID.indexOf("000000000000000")!=-1){
System.exit(0);
}
在最后三行中,应用程序检查BotID的值,该值返回getDeviceId()的结果(即手机的IMEI)。如果发现IMEI包含”000000000000000”,则调用 System.exit(0)。下面的代码列出了Crosate的修补版本:
public void onCreate(){
....
String BotID= AGHardening.method14();....
if(BotID.indexOf("000000000000000")!=-1){
System.exit(0);
}
在这里可以看到,代码再次检查了BotID的值,并将其与”000000000000000”进行比较。然而,这一次BotID并未返回getDeviceId(),而是调用了我们加固类中的 method14()。正如我们之前所见,method14()现在返回的是”353627074120224”,而不是沙箱的IMEI。因此,在我们的修补版本中,该检查将失败,System.exit(0)方法不会被调用,从而能够像在真实设备上一样执行所有恶意操作。该应用程序还调用了其他多个方法,例如getLineNumber1()和getNetworkOperatorName(),这些方法也可用于识别沙箱环境。所有这些实例均已被替换为
加固类中找到的相应设备值。通过比较原始Crosate样本与加固版本的分析报告,暴露了二者行为上的差异。原始版本表现出极少的活动,整个报告仅生成 21条日志条目。而修改后的版本生成了191条日志条目,清楚地显示出启动了额外的活动和服务,并且设备数据被泄露。正如对恶意软件代码的审查所证实的,这多出的170条日志条目是由那些仅在沙箱检测代码失败时才会启动的服务和活动生成的,因此System.exit(0)未被调用。这也与尼加姆记录的预期行为一致 [19]。这表明成功实现了对恶意软件沙箱检测能力的自动修补,揭示了恶意软件的分裂行为。
Pincer . Pincer 是另一种仅在检测到设备为真实设备而非沙箱时才与CNC服务器通信的机器人。因此,我们假设一旦修补了该恶意软件样本,就能观察到与 CNC的网络通信。下文列出了Pincer为区分沙箱和设备所执行的多项检查。幸运的是,我们的工具能够预测这些检查并相应地进行修补。
if( C0024b.m108d(context).toLowerCase().equals("android") ||
C0024b.m106b(context).equals("000000000000000") ||
C0024b.m107c(context).equals("15555215554") ||
AGHardening.FIELD21.toLowerCase().equals("sdk") ||
AGHardening.FIELD21.toLowerCase().equals("generic")){
C0018a.m68a(context, true);
} else{
C0014a.m50a(context, jSONObject, new C0023d());
}
下表显示了调用区分器的方法,以及它们是如何被识别和修改的。
public static String m106b(Context context){
return AGHardening.method14();
}
public static String m107c(Context context){
return AGHardening.method6();
}
public static String m108d(Context context){
return AGHardening.method8();
}
因此,所有参与沙箱检测检查的方法和常量都被替换为加固类中的我们的方法和字段,强制它们返回实际设备的值,而不是沙箱的值。Pincer 是一个有趣的案例研究,因为最初修补版本和原始版本似乎返回相同的日志。修补本身并未失败。然而,发生分裂行为的代码部分仅在收到
来自CNC的命令后才会执行。只有在我们模拟接收CNC命令后,才能观察到行为上的差异。此问题超出了AndroNeo的范围,但包含在范围扩展中。通过比较原始Pincer样本与加固版本的分析报告,揭示了行为上的差异。原始版本未显示任何与我们模拟的CNC命令相对应的网络通信。而修改版本则报告了网络连接以及CNC的IP地址,并响应了我们请求的命令。此外,修改版本还尝试访问SD卡上的图像文件,这种行为在原始版本中并不存在。这个案例研究再次清楚地证明了恶意软件沙箱检测能力的成功修补,暴露了恶意软件的分裂行为。修补后的恶意软件行为与尼加姆记录的预期行为一致[19]。
需要注意的是,在我们的测试中,区分器的检测和修补均以自动化方式进行。从样本沙箱和设备中获取的已识别区分器能够预测并修补这些恶意软件样本所使用的沙箱检测技术,而无需明确告知需要查找的内容。
5 范围扩展
5.1 局限性
尽管评估结果令人鼓舞,且原型系统已成功以自动化方式修补了众所周知的恶意软件样本,但仍需注意,当前尚未处理范围内的高度混淆恶意软件。目前的实现中仍存在一些无法识别和修补的规避技术。例如,通过使用原生代码、直接Binder IPC调用或恶意软件加壳器进行混淆的恶意软件将无法被该原型系统处理。另一个限制是,目前我们的侦察实现仅利用Java反射来调用方法和类字段并收集系统信息。而Morpheus的工件收集器则实现了额外的技术,例如使用目录遍历器来识别模拟器特定文件和文件夹的存在,从而基于这些工件生成启发式规则。
5.2 提出的扩展
AndroNeo 计划在以下方面根据进一步的实验进行扩展:
改进字节码修补 。 在当前状态下,AndroNeo 无法应对采用运行时类加载等技术来隐藏其恶意代码的自修改恶意软件。为解决这一问题,AndroNeo 需要实现某种形式的动态修补。
实现 Morpheus 的所有工件提取器 。 将 AndroNeo 扩展以包含其他此类系统信息来源(例如 Morpheus 所实现的),可以生成额外的启发式规则。
扩展到所有沙箱检测技术 。 当前范围仅限于基于静态模拟器属性的沙箱检测技术的加固。然而,所提出的方法可以进一步发展以应对基于事件或基于用户存在的技术。启发式预测是否能够应对这类规避技术,仍需进一步研究。此外,一个有趣的方向可能是将我们的技术与其他技术相结合,例如 Pooryousef 和 Amini [20],提出的旨在解决事件驱动操作暴露问题的技术。此外,还可以扩展 AndroNeo 以基于其他启发式生成技术[21]来修补其相应生成的启发式规则。
6 结论
安卓恶意软件家族表现出利用检测启发式方法来识别恶意软件分析沙箱的能力。为解决这一问题,我们提出了AndroNeo,这是一种可自动增强恶意软件分析沙箱安全性的工具,以禁用恶意软件的沙箱检测能力。AndroNeo能够识别 Android应用程序中的沙箱检测功能,并修改或禁用其相关功能。此外,我们展示了AndroNeo的原型实现,验证了其在真实恶意软件家族上的有效性。未来,AndroNeo可通过直接复用Morpheus等方式进行简单扩展,也可通过进一步研究实现更复杂的扩展,例如修补基于事件或基于用户存在的沙箱检测技术。
433

被折叠的 条评论
为什么被折叠?



