概 述

近年来,Android恶意软件数量不断攀升,其采用的攻击和对抗技术也变得越来越复杂。为了对抗安全分析,绕过安全检测,窃取敏感数据或破坏系统安全,恶意软件利用各种对抗技术来伪装和隐藏其恶意行为和代码逻辑。这些对抗技术中,针对资源、文件、字节码指令及机器码指令的混淆技术被广泛地引入到恶意软件中,成为高级黑客与逆向工程师对抗的重要战场。

混淆技术的核心目标是增加分析复杂性来对抗逆向工程,同时也能提高免杀能力,主要对抗目标包含逆向工程师、静态分析工具、动态调试工具以及自动化检测系统。混淆技术通过设置分析陷阱、改变文件结构、增加指令复杂度、虚拟化指令、隐藏代码和资源等各种繁杂的技术来掩盖其行为特征和攻击意图,同时给分析人员尽可能大的制造分析障碍,延后其恶意行为暴露的时间。同时,恶意软件通过组合运用各种各样的混淆与分析对抗技术,对现有安全检测工具和防护机制形成了严峻挑战。这需要不断跟进各种技术的发展,对新的手段进行深入研究,制定有效且快速的应对方法,强化分析工具和检测工具,同时也有助于快速应对高级且复杂的恶意软件攻击。

本文将对我们分析Android恶意软件过程中所遇到的常见混淆技术进行总结和分析,从恶意软件混淆与对抗的视角来阐述另一面的安全攻防技术,以帮助安全工程师和用户们更加深入理解这一领域的技术情况,同时也有助于分析人员更高效地分析恶意代码。

第二章

Android恶意软件混淆技术的演进

随着移动安全领域对抗的不断升级,Android 恶意软件的混淆技术也经历了显著的发展,从简单的标识符混淆逐步演进到复杂的虚拟化保护(VMP)技术,呈现出由浅入深、逐步升级的趋势。为更清晰地理解这一变化,我们将混淆技术的发展大致划分为早期、中期和当前阶段,并从不同层面进行详细解析。

2.1 早期阶段:基础伪装与简单加密

在早期,恶意软件的混淆技术主要以掩盖代码和资源内容为目标,手段较为简单直接,其混淆技术主要集中于Java层,利用Android应用以Java语言开发的特性,通过简单的混淆手段规避安全分析和检测。在这一时期,恶意软件开始探索文件格式相关的混淆策略。例如,通过修改ZIP格式的APK伪加密,以及2012年黑帽大会上首次提出的利用DEX头隐藏代码的技术,这一策略随后被Syrup恶意软件所采用。同时,木马Obad利用商业混淆工具DexGuard(ProGuard的增强版)实现了Manifest字段的复杂混淆以及基于clinit方法的动态代码解密,被誉为“史上最强Android木马”。

这一阶段,短信拦截类和锁机勒索类恶意软件广泛采用多种混淆手段,包括代码混淆、字符串加密、代码分割、垃圾代码注入,以及商业加固工具的使用。这些技术尽管相对基础,但在当时的安全环境中,已经显著提高了恶意软件的隐蔽性和抗检测能力。下图展示了一款短信拦截木马的混淆效果。

图1 一个短信拦截木马的混淆示例

2.2 中期阶段:逻辑复杂化与动态对抗

随着安全检测技术的不断提升,恶意软件逐步引入更加复杂的混淆手段,以提高隐蔽性并有效规避分析。为规避静态分析,它们利用动态加载和反射机制,通过反射动态调用隐藏的关键代码。同时,恶意软件采用多重加密与解压策略,对关键代码进行多层次加密,并在运行时通过复杂的解密过程逐步释放真实的恶意逻辑。例如:在我们发布的分析报告《新型Android银行木马“MoqHao”利用社交网络隐藏C&C服务器》中,恶意软件将真正的恶意dex文件加密后,以Base64编码的形式保存在原始APK的assets目录下。在这种情况下,原始APK仅作为一个外壳存在,其在运行时会动态解密并加载真正的恶意dex文件,以实现其攻击目的,其过程如下图所示:

图2 Java层混淆dex文件的示例

为了扰乱分析路径,恶意软件引入控制流混淆技术。通过插入无关的分支、循环和跳转语句,恶意软件使代码执行路径更加复杂,阻碍分析人员还原其逻辑。而在结合商业加固解决方案后,通过内存加载、指令抽取和指令转换等技术,进一步提升了对抗分析的能力,使得分析人员难以准确还原其实际的执行逻辑。

随着反混淆技术的进步和现代化反编译工具的普及,恶意软件逐渐将混淆策略向Native层转移,以进一步提升逆向分析的难度。其常用技术包括对会话数据加密以防止敏感信息泄露、加密关键函数体以隐藏恶意行为、插入花指令干扰反汇编工具分析、利用LLVM框架实施复杂的代码混淆,以及通过反调试技术检测和阻止调试器介入。这些策略相辅相成,不仅显著增加了逆向工程的复杂性,也进一步提高了安全研究人员分析的技术门槛。例如:在我们发布的《一款通过SO进行自保护的银行APP劫持木马深度分析报告》中,详细分析了该木马的运行行为。具体而言,libload.so模块负责解密并加载实际的恶意代码。该模块利用Java反射机制调用javax.crypto包中的加解密函数,对存储在assets目录下的mycode.so文件进行AES解密。解密后的文件通过自定义的DexClassLoader加载并执行,执行完毕后,解密的文件会被删除。这些精心设计的混淆手段有效隐藏了恶意行为,显著增加了安全分析的复杂性,使得恶意软件的检测和分析变得更加困难。加载Native解密库的部分代码如下图所示:

图3 Natvie层混淆示例

此外,针对文件格式的混淆策略也变得更加多样和复杂。它们巧妙利用 Android系统对某些文件格式字段宽松校验的特性,同时借助反编译工具对文件格式的严格解析逻辑,设计了多种干扰和隐藏机制。通过对这些文件格式的灵活利用,恶意软件得以实现更高程度的隐蔽性,进一步提高了逆向分析和检测的难度,同时也为混淆技术的持续发展奠定了基础。

2.3 当前阶段:复杂混淆与多层对抗

在当前阶段,恶意软件的混淆技术已经高度复杂化,广泛结合动态和静态对抗手段,以增强隐蔽性并对抗多种检测方法。通过将Java层代码混淆、动态加载与Native层加壳技术融合使用,恶意软件将核心逻辑分布于不同层次中,大幅提高了检测和分析的复杂性。以下是目前常见的混淆策略:

  • Java层与Native层相结合:恶意软件通过将Java层的代码混淆、动态加载、VMP等技术与Native层的代码混淆手段相结合,将恶意逻辑分散在不同层次中。这种跨层混淆策略显著增加了检测和分析的复杂度,要求分析人员同时掌握多种技术,才能全面理解其运行机制。

  • 使用其他开发语言:恶意软件常利用Lua、GoLang、Flutter、易语言等非传统的 C/C++ 语言进行开发,从而显著增加逆向分析的难度。这些语言独特的特性进一步提高了分析的复杂性,同时削弱了传统逆向分析工具的效率和效果。

  • 运行环境检测及反调试:恶意软件通过检查设备的系统属性、文件系统结构、系统权限状态等,判断目标设备是否为模拟器或已获取Root权限,并在检测到这些特征时阻止恶意逻辑的运行。同时,它们调用系统API检查调试状态,监测调试工具特有的行为,甚至主动引发异常以捕获调试工具的反应,有效阻止动态分析工具的干扰。

  • 商业加固保护技术:一些恶意软件使用商业加固保护技术,进一步加强了代码的抗逆向能力,使得传统的安全工具和分析方法难以奏效。

  • 代码虚拟化技术:通过将代码翻译为自定义的虚拟指令集,并在运行时通过嵌入的虚拟机解释执行,这种技术极大地提高了逆向分析的难度。

例如,某恶意软件通过使用Native代码实现对Frida和IDA的检测,其代码如下:

图4 Natvie层检测调试器的代码示例

Android恶意软件的混淆技术已经从简单的代码混淆逐渐发展到如今复杂的跨层结合形式,其隐蔽性持续增强,技术手段也愈发多样化。这种发展趋势对传统安全检测工具而言是严峻的挑战,同时也促使研究人员必须不断创新应对策略,以此来应对恶意软件日益复杂的混淆手段。在此背景下,深入了解并解析常见的混淆技术,是提升恶意软件分析和检测能力的关键。接下来,我们将通过实际工作中常遇到的恶意软件混淆技术进行详细分析,探讨这些技术的应用方式及相应的防范对策。

第三章

常见混淆手段与技术解析

在分析Android恶意软件时,安全研究人员通常需要从静态分析和动态分析两方面入手。静态分析往往是最先进行的步骤,而混淆技术通过修改代码的结构和表达方式(如字符串加密、代码拆分、动态加载等),使恶意软件的分析难度显著增加。本文将以安全研究人员分析APK文件的过程为切入点,逐步介绍各种反混淆策略,帮助研究人员有效应对不同层次的混淆技术挑战。

3.1 APK反编译工具的对抗

在分析Android应用时,首先需要使用APK反编译工具(如Apktool、Jadx)提取APK文件中的源代码和资源。然而,恶意软件往往会通过多种混淆手段对APK进行保护,以干扰反编译过程。例如,恶意软件可能利用Android在解析APK文件时未严格校验ZIP格式的某些字段,从而通过篡改APK文件的ZIP格式字段来绕过基于ZIP格式解析的反编译工具。这种手段尤其常见于一些基于Android的银行木马(如BianLian、Cerberus和TeaBot)中,目的是阻止安全研究人员和自动化分析平台有效提取APK文件内容,进而对抗静态分析和反病毒检测。

为了更好地理解恶意软件的混淆策略,尤其是如何通过篡改ZIP格式字段来干扰反编译过程,有必要了解一些ZIP文件结构的基本信息。虽然ZIP格式的详细结构超出了本文的讨论范围,但我们可以简单介绍一些常见的、恶意软件经常篡改的字段。我们以 Python 的zipfile.py 模块中的 ZipInfo 类为例,展示了 ZIP文件元数据中的多个关键字段。具体字段及其含义如图所示:

图5 Zipinfo类的结构信息

其中,compress_type、extract_version、reserved和flag_bits都是恶意软件常常篡改的字段。这些修改可以导致反编译工具在解析文件时出现错误或无法正确读取文件内容,从而有效地阻碍静态分析过程。通过篡改这些字段,恶意软件不仅能够规避常见的反编译工具,还能增加被检测的难度,提高其在静态分析中的隐蔽性。

在了解了恶意软件如何通过篡改ZIP格式字段来对抗静态分析后,接下来我们需要关注Android应用中另一个常见的反编译手段——AndroidManifest.xml文件(以下简称“清单文件”)。清单文件是反编译过程中必须解析的关键部分,它包含了应用的核心配置,如组件、权限等信息。因此,提取、读取和分析清单文件是静态分析APK样本时的首要步骤。为了规避反编译工具的识别,恶意软件通常通过精心篡改清单文件来干扰其正常解析。由于清单文件在静态分析中的重要性和复杂性,它成为了恶意软件常用的反编译对抗手段之一。如果读者对清单文件的具体结构不太了解,可以自行访问以下链接(https://bbs.kanxue.com/thread-194206.html)查阅相关信息。

接下来,我们将介绍几种常见的清单文件混淆手段,探讨恶意软件是如何通过这些手段对抗反编译工具并隐藏其恶意行为的:

(1)修改XML文件的魔术:恶意软件通过篡改清单文件中的魔术字段,使得静态分析工具在解析时出现错误。这些修改通常不会影响应用的正常运行,但会导致分析工具无法正确解析清单文件的结构,甚至可能引发反编译工具的异常错误,从而阻止反编译过程,无法提取源代码。

图6 左图为正常的魔术,右图为混淆的魔术示例

(2)篡改关键字段:恶意软件可能会篡改清单文件中的关键字段,例如通过修改StringPool中字符串的个数。虽然Android系统在运行时并不依赖这个字段来计算字符串的个数,而是通过动态计算来处理字符串,但一些反编译工具却会依赖这个字段来解析XML文件中的内容。通过篡改这个字段,恶意软件可以干扰反编译工具的正常解析,导致工具读取到错误的字符串个数,从而无法正确解析清单文件的结构,进而影响静态分析的准确性和完整性。这种手段有效增加了静态分析工具的分析难度,提升了恶意软件的隐蔽性。

图7 篡改StringPoolSize的混淆示例

在上图中,我们可以看到,stringCount的值为2907,而StringOffsets开始于偏移位置36,大小为 11628。StringOffsets是一个包含每个字符串在字符串池中的相对偏移量的数组,其大小为 stringCount * 4,即 11628。反编译工具按照混淆后的stringCount值计算StringOffsets大小,并进行解析,因此解析结果出错。这种不一致性是导致反编译工具解析失败的原因。然而,Android系统在处理清单文件时并未直接依赖stringCount字段的值。通过查看 Android 源码可以发现,Android系统在运行时根据实际数据动态计算 stringPoolSize,而非直接使用文件中提供的数值。

图 8 Android系统源码中计算mStringPoolSize的代码示例

(3)插入脏数据:有些恶意软件会故意在清单中插入一些脏数据。这种数据不会被实际使用,但会破坏清单文件的格式,使得解析工具难以识别其中的有效信息。

图9 左图显示`startEle0`内容,右图展示`startEle0`和`startEle1`的偏移量及大小

上图展示了一个混淆后的清单文件。在左图中,第一个startElement的大小字段为196,但header中的size字段为224,意味着startElement后附加了28字节的未知数据。右图中,第二个startElement的起始位置为0x15A4,正好位于插入的这28字节垃圾数据之后(右图中绿色部分所示)。这些多余的28字节会导致反编译工具在后续解析时出错。Android系统能够正常解析,是因为系统计算每个startElement的位置时,仅依赖上一个startElement的起始位置和大小字段,从而自动跳过垃圾数据。

(4)插入超长字符串或特殊字符:为了干扰静态分析工具的正常解析,恶意软件可能会在特定字段中插入超长字符串、特殊符号、emoji表情或不可见字符。这些字符虽然不会影响应用的正常功能,但它们显著增加了分析工具的处理难度。如果反编译工具的容错能力较差,这些修改可能导致工具崩溃或无法正确解析清单文件,从而使静态分析变得更加困难。

图10 通过不可见字符实现超长应用名称(label)的示例

上图中,清单文件中的label字段从string.xml中的app_name字段读取,而app_name字段则包含了不可见字符(如\\u0000)和超长字符串。通过这种方式,恶意软件可以有效干扰反编译工具的解析过程。如果自动化分析平台的容错机制不足,如数据库字段对app_name或version字段长度有限制,这些篡改可能导致异常,进而使恶意软件在自动化分析平台上“隐身”,难以被检测到。

在正常的清单文件中,Start Namespace Chunk通常存储命名空间信息,其中Prefix是一个4字节的索引,指向字符串池中相应的字符串,用于标识命名空间前缀;Uri也是一个索引,指向字符串池中表示命名空间URI的字符串。例如,在一个未经过混淆的清单文件中,我们可以看到Prefix的值对应的字符串为android,即它引用了标准的Android命名空间。

图11 正常的prefix前缀示例

然而,通过对恶意软件中发Prefix字段进行恶意篡改,攻击者可以导致反编译工具在解析时发生错误。这种混淆方式利用了反编译工具对Prefix的依赖,使得其在解析时发生异常,干扰分析人员对恶意软件的判断。下图是一个经过混淆的清单文件。

图12 混淆后的prefix示例

从图中可以看到,命名空间的Prefix显示为非标准字符串。Prefix的索引值指向字符串池中的位置67,而Uri显示正常。查看字符串池中索引67的内容,我们发现该字符串是一段长度为20470的乱码字符。如下图所示:

图13 索引值67处的prefix字符串的值示例

这种超长字符串会导致Apktool在反编译时崩溃。在Apktool的错误日志中,显示了“Array Size”异常信息。

图14 Apktool反编译时出现的异常错误示例

经过对崩溃问题的绕过处理后,生成的AndroidManifest.xml文件达到了16.7MB,且包含大量乱码,难以阅读。

图15 包含大量垃圾字符的AndroidManifest.xml文件示例

一个有效的处理方法是将Prefix的索引值指向字符串池中较短的字符串,以便生成的清单文件内容正常显示,便于后续分析和阅读。

此外,一些恶意软件会在清单中插入符号或表情符号,若反编译工具在解析时未正确处理这些特殊字符,也可能导致反编译工具崩溃。插入表情符号进行混淆的清单如下图所示:

图16 通过表情符号进行混淆的示例

除了通过篡改ZIP格式字段和混淆清单文件来对抗反编译工具外,恶意软件还常常采用一种强有力的策略——混淆resources.arsc文件。resources.arsc文件是Android应用中存储资源映射关系的核心文件,它维护了资源ID与实际资源文件之间的映射。恶意软件通过篡改这个文件,能够有效干扰反编译工具对资源的正确解析,甚至可能导致反编译工具崩溃或异常退出。例如,BOOMSLANG(树蚺)移动欺诈家族就利用这一策略对抗Apktool的反编译过程,下图是其反编译失败时Apktool提示的错误信息。

图17 混淆后的BOOMSLANG样本导致Apktool反编译失败

3.2 代码混淆与反混淆技术

一旦研究人员突破了APK包的初步混淆,接下来便会进入到代码层面的分析。在这一层面,恶意软件通常采用各种混淆技术,大幅地增加了静态和动态分析的难度。

3.2.1 标识符混淆

标识符重命名是代码混淆中最常见且最有效的技术之一,旨在通过将有意义的包名、类名、方法名和变量名替换为无意义的、随机生成的名称,从而干扰逆向工程师的分析过程。此外,标识符混淆还能有效避开一些自动化分析平台,这些平台通常依赖于标识符来制定检测规则。通过混淆标识符,恶意软件能够规避基于标识符的检测,减少被识别的风险。常见的标识符混淆策略包括使用随机字符组合(如数字、字母或特殊符号)、采用非英语语言的标识符(如中文、日语、韩语、俄文等)、使用超长字符标识符,或通过替换形状相似但含义不同的字符(如字母“O”与数字“0”、字母“I”与小写字母“l”等)来干扰分析。这些混淆策略使恶意软件能够有效隐藏其功能并增加逆向分析的难度,从而延缓被检测和识别的时间。为了应对这种混淆,安全研究人员可以使用带有反混淆功能的反编译工具,如Gda、Jeb、Jadx等,这些工具有助于快速恢复被混淆的代码并帮助识别恶意行为。标识符混淆示例如下图所示:

图18 标识符混淆示例

3.2.2 字符串加密

恶意软件通常采用编码或加密手段对代码中的敏感字符串进行处理,以防止恶意关键字(如恶意URL、命令或其他敏感数据)在反编译过程中被直接暴露。这些技术有效地阻止了分析人员从反编译结果中提取关键信息,尤其在对抗自动化分析平台的静态分析时,具有较强的防护效果。常见的字符串混淆方法包括字符拆分和编码混淆。字符拆分将敏感字符串分割成多个部分,程序在运行时再拼接成完整的字符串;而编码混淆则通过对字符串进行加密或使用编码技术(如Base64编码、简单加密算法等),在程序运行时再进行解码。这些手段不仅增加了静态分析的难度,也使得分析工具无法直接识别出恶意行为。这种技术使得静态分析工具无法直接看到关键数据。安全研究人员可以使用动态分析工具,如Frida,来捕捉运行时的解密过程,揭示恶意软件在运行时所做的操作。字符串加密示例如下图所示:

图19 字符串加密混淆示例

3.2.3 控制流混淆

控制流混淆技术通过插入无意义的控制结构(如多余的条件分支、循环或冗余代码),故意改变程序的执行路径,代码块重新排序等手段使得分析工具在尝试解析程序时,陷入过于复杂的代码结构中,导致无法准确地还原程序的真实逻辑。由于控制流混淆引入了大量不必要的执行路径,静态分析工具可能无法有效提取出程序的实际行为。

为了应对这种混淆,安全研究人员通常需要通过模拟执行以及动态分析技术来辅助手动追踪程序的执行流,逐步还原程序的真实逻辑。下图展示了一个简单的控制流混淆示例,展示了在混淆前后的控制流结构差异。

图20 控制流混淆前后的差异对比

3.2.4 反射机制与动态加载

动态加载机制是恶意软件常用的反静态分析技术,主要包括本地动态加载和远程动态加载两种方式。本地动态加载通过加载本地存储的动态库或dex文件,动态调用方法或类,使得恶意行为在静态分析阶段无法被发现。远程动态加载则通过在运行时从远程服务器下载动态库或dex文件来加载并执行恶意代码,这种方式进一步规避了静态分析工具的检测,因为恶意代码并未出现在apk文件中。恶意软件往往通过反射技术结合动态加载,利用运行时的反射调用机制隐藏恶意行为,使得初期静态分析无法识别这些恶意活动。为了应对这一挑战,安全研究人员通常依赖动态分析手段,如通过Frida注入脚本、使用调试工具动态跟踪或通过日志分析捕捉反射调用的过程,从而揭示恶意代码的真实行为。这些动态分析方法能够绕过静态分析的限制,识别通过反射和动态加载隐藏的恶意行为。

接下来,我们来看一个利用本地动态加载的恶意代码示例。下图展示了该恶意代码的目录结构。

图21 目录树信息

虽然从表面上看,该目录仅包含几个方法,但深入分析后发现,恶意代码通过attachBaseContext方法调用了一个名为b的方法,进一步实现了从assets目录加载加密的代码文件。该文件经过异或解密后,通过动态调用的方式被加载并执行,代码如下图所示:

图22 动态加载后通过反射调用代码示例

3.2.5 Native混淆

Native混淆技术是通过多种手段加大对恶意软件逆向分析的难度,常见的技术包括以下几种:

(1)JNI接口混淆:恶意软件通过JNI(Java Native Interface)将关键逻辑转移到.so文件中,并通过混淆的JNI接口调用本地代码。通过将原本清晰的本地方法名替换为无意义的符号或随机字符,恶意代码的功能和结构变得更加难以理解和追踪。

图23 JNI接口混淆示例

(2)Session加密:将关键方法存储在自定义的.section中,并对这些自定义的.section内容进行加密。由于.so文件在加载时会优先执行.init_array段,因此将解密逻辑嵌入到.init_array中。在运行时,通过解密方法获取内存中各个.section的起始地址和大小,对加密的.section进行解密还原,从而恢复关键方法的正常执行。

图24 Session加密的示例

(3)函数加密:解析.so文件,通过方法名定位目标方法后,对其进行加密。在加载.so文件时,通过指定方法的地址调用解密逻辑,将加密的方法动态解密还原,以实现对关键逻辑的保护。

图25 JNI加解函数经解密还原的部分代码示例

(4)字符串加密与动态解密:恶意软件通过加密关键字符串(如URL、命令、密钥等),并在运行时通过动态解密恢复其原始内容。加密的字符串通常存储在静态数据区域,而解密则通过特定算法或在内存中动态完成,从而使得静态分析工具无法直接提取恶意信息。

图26 Native解密字符串示例

(5)花指令与垃圾代码插入:通过在代码中插入伪指令或无效代码,恶意软件能够混淆程序的实际行为。这些花指令没有实际功能,但增加了逆向工程的复杂性。垃圾代码不仅增加了程序的体积,还使得追踪和理解恶意代码变得更加困难。

图27 一个简单的垃圾指令示例

(6)代码加壳与自修改代码:在Native层,恶意软件经常使用加壳技术来隐藏核心恶意代码。加壳后,恶意代码以加密或压缩形式存储,只有在运行时才会解密或解压。自修改代码技术则允许恶意软件在运行时动态生成、修改和执行代码,进一步阻止静态分析工具识别完整的恶意行为。

图28 通过Hook修改代码的示例

(7)反调试与模拟器检测:为了抵抗动态分析,恶意软件往往会在Native层嵌入反调试机制,例如通过检测调试器的存在(如gdb或Frida)来中断分析过程。此外,恶意软件也会进行模拟器检测,通过检查设备是否运行在模拟器或沙盒环境中来避免被动态分析工具捕捉。

图29 检测模拟器示例

(8)控制流混淆:控制流混淆通过改变程序的执行路径,使得分析人员难以理解程序的实际流程。常见的方法包括插入无效的控制流(如跳转、分支语句)、无用的循环和函数调用,扰乱分析工具追踪指令的顺序。

图30 一段控制流混淆代码示例

(9)LLVM混淆:LLVM是一种强大的编译器框架,恶意软件通过使用LLVM技术来对Native代码进行复杂的混淆处理。LLVM混淆能通过优化编译过程,生成难以阅读和理解的二进制文件,同时对文件大小影响较小。这种技术有效增加了逆向工程的复杂度。

图31 LLVM混淆前后对比示例

这些Native混淆技术通常被组合使用,构建多层次的保护机制,从而使传统的静态分析和动态分析方法面临巨大的挑战。借助这些技术,恶意软件能够有效隐藏其真实行为,规避安全研究人员的检测以及防护系统的拦截。此外,商业加固保护进一步提升了防护的全面性。恶意软件常利用商业加固产品对自身进行保护,甚至黑灰产软件也频繁采用此类技术进行防护,如下图所示:

图32 使用商用加固的诈骗应用

3.2.6 代码虚拟化

代码虚拟化是目前最先进的混淆手段之一,它通过将原始代码转换为自定义的虚拟机指令,极大地提高了代码复杂度和分析难度。与传统的控制流混淆不同,虚拟化技术将关键代码的执行逻辑封装在虚拟机中,使得核心功能和控制流几乎无法通过常规反编译手段还原。以下从DEX虚拟化和SO虚拟化两个维度进行分析:

DEX虚拟化技术主要针对Android应用中的Dalvik字节码。这种技术通过将DEX中的字节码转换成自定义的虚拟机指令,然后由Native层虚拟机解释执行,从而保护关键代码和核心逻辑。这种转换使得传统的反编译工具难以还原代码的原始逻辑,因为它们无法识别转换后的指令集。例如,某电商平台通过漏洞提权实施恶意行为,并为隐藏其核心代码逻辑,采用了一套基于JVM构建的虚拟机保护(VMP)技术对代码进行加固。下图展示了其保护后的二进制内容,可以明显看出,代码已被虚拟化为高度抽象的虚拟机指令,使得传统的反编译和静态分析方法几乎无效。

图33 解析经vmp保护的dex文件示例

SO虚拟化技术则关注于保护应用中的Native代码,即SO文件。这种技术可以通过隐藏符号表、资源加密等方式来保护SO文件中的功能不被轻易分析和篡改。例如,Virbox Protector提供了对SO文件的保护选项,包括隐藏符号表和对特定SO文件的加密保护。通过这种方式,即使攻击者能够访问到SO文件,也无法轻易理解文件中的函数和逻辑,从而保护了应用的安全。

图34 某商业加固产品对DEX和SO的虚拟化保护介绍

3.3 其他混淆手段

一些恶意软件通过巧妙利用Windows和Android文件系统的差异性,有效对抗静态分析和逆向工程。在Windows系统中,文件名不区分大小写,但会保留其原始格式,而Android文件系统则区分大小写。这种差异使得某些在Windows系统上可能引发冲突或错误的文件名,能够在Android设备上正常使用。此外,Windows系统对文件名中的特殊字符(如 > < : " / \\ | * ?)有严格限制,而Android系统允许这些字符的存在。恶意软件常通过在文件名中嵌入这些特殊字符,干扰基于Windows的分析工具,导致文件无法读取或处理,从而增加逆向分析的难度。

不仅如此,恶意软件还可能利用Android文件系统允许同名文件和文件夹共存的特性,进一步增加分析的复杂性。例如,在Android中,一个名为AndroidManifest.xml的文件和一个名为AndroidManifest.xml的文件夹可以同时存在,但在Windows系统中则会因命名冲突而无法实现。这种差异可能导致基于Windows的分析工具处理这些结构时出错或跳过解析这些关键文件或文件夹,进而为恶意软件提供伪装。例如,BOOMSLANG恶意软件的一个在野样本采用了特殊字符混淆和同名文件与文件夹共存的对抗技术,其APK文件在使用ZIP工具打开时如图所示:

图35 同名文件和文件夹混淆示例

从图中可以看出,恶意软件会同时创建 “\\AndroidManifest.xml”和“AndroidManifest.xml”目录,以及 “\\classes.dex” 和“classes.dex”目录。这种操作会在Windows系统上导致文件夹相互覆盖,造成文件内容丢失,从而影响反编译的完整性。此外,恶意软件还会使用非法文件名,导致这些文件在使用apktool反编译时被忽略,如下图所示:

图36 apktool忽略非法文件名的示例

一些恶意软件还会在文件中嵌入同名但大小写不同的文件,通过利用Windows文件名不区分大小写的特性干扰分析工具,造成解析错误或异常。以下图片展示了一例包含同名但大小写不同文件的恶意APK示例。

图37 Windows文件系统不区分文件名大小写的示例

在Android恶意软件中,资源混淆是一种常见的对抗技术,广泛应用于assets和res等文件夹中的资源文件。常见的资源混淆手段包括:对assets目录中的资源文件进行加密处理,创建深层嵌套的目录结构或使用畸形目录名称,插入大量无用文件或伪装资源文件,将关键资源与无效资源混杂在一起,从而增加分析难度;对res目录中的资源文件进行混淆,例如篡改资源映射关系、删除资源文件名或更改文件扩展名等。这些手段旨在干扰分析工具的解析过程,使得分析人员难以快速定位和识别关键数据,进一步提高逆向工程的难度。下图是一个插入垃圾资源的示例:

图38 插入垃圾资源示例

一些恶意软件通过将原始DEX文件分割成多个小的DEX文件或插入垃圾代码,增加静态和自动化分析的难度。分割DEX文件使分析工具需要逐个处理,增加了分析时间和复杂度,且这些文件通常通过动态加载技术在运行时才会合并,难以在静态分析中识别。此外,插入的垃圾代码不执行实际操作,但通过虚假的逻辑和复杂的控制流干扰分析,掩盖恶意功能。此类策略使得恶意软件的行为更加隐蔽,迫使分析人员采用更复杂的动态分析和手动逆向工程方法。一个apk中包含大量dex的例子如下图所示:

图39 分割多个dex示例

除了上面提到的混淆技术,还有一些其他常见但未详细介绍的技术,这些技术能进一步增强恶意软件的逆向分析难度。以下是一张混淆工具的示例图片,其中展示了多种混淆功能,如:增加包体积、内建独立签名证书、DEX代码混淆、资源混淆、APK防篡改等多个功能。

图40 一种混淆工具示例

第四章

实际案例解析

为了更好地理解混淆技术的实际应用,以下是几个常见的Android恶意软件的混淆技术案例。

4.1 Joker恶意软件混淆方法

Joker(中文名称“小丑”,也被称为Bread)是一个在Google Play商店中极为活跃的恶意软件家族。自2016年12月首次被检测到以来,Joker家族的活动一直在持续,并且其恶意软件的变种数量也在不断增加。本月最新披露的Google Play商店中Joker样本信息如下:

图41 GooglePlay上的Joker木马信息

Joker家族的恶意软件能够反复进入Google官方应用市场,其关键原因在于其采用了多种高级代码混淆技术,从而绕过了Google Play Store的安全检测和审查机制。这些混淆技术包括但不限于:

(1)字符串加密:通过加密关键字符串,如API请求和配置信息,避免静态分析工具的直接识别。

(2)动态加载:将恶意代码分离到单独的Payload文件中,仅在运行时加载,以躲避静态代码扫描。

(3)反射调用:利用反射机制动态调用方法,隐藏关键功能调用路径。

(4)动态下发配置:通过网络请求动态获取配置或恶意指令,降低被静态检测的风险。

(5)利用第三方服务:使用Github页面和存储库存储恶意配置或代码,进一步混淆来源和流量。

这些技术的组合使Joker恶意软件能够成功规避静态分析检测,其真实行为往往隐藏在复杂的动态流程中。为了揭示其行为,分析人员通常需要依赖动态调试和运行时解密技术。针对这些混淆手段,安全研究人员可以借助动态分析工具(如Frida和Xposed)追踪解密过程,提取关键数据并识别恶意 API 调用,从而全面还原其恶意行为。

4.2 BadPack的混淆手段

BadPack是恶意软件通过篡改ZIP文件结构头,使Apktool和Jadx等分析工具无法正常解析。其通过使用非标准的压缩算法来干扰反编译工具的正常运行。经010 Editor分析该APK文件格式,发现其压缩方法值为0xF753,如下图所示:

图42 篡改apk文件压缩算法标识示例

这一数值并不属于任何已知的标准压缩方法,因此导致反编译工具无法识别和处理该文件。根据标准ZIP格式规范,压缩方法的取值通常如图所示。

图43 ZIP支持的各压缩方法的标识值示例

根据Android系统源代码,当系统遇到非COMP_DEFLATE(即 0x08)压缩方法时,会默认将输入文件按 “未压缩” (COMP_STORED,即 0x00)方式处理。具体而言,系统会直接读取文件的未压缩数据长度,并以此进行解析。因此,恶意软件利用了Android系统对ZIP格式的容错机制,通过使用非法的压缩算法进行混淆,从而达到规避反编译工具的目的。这种混淆技术简单却有效。在分析Android恶意软件时,若遇到类似情况,可将压缩方法修改为0以便正确解压和分析。

APK伪加密通过修改ZIP文件的头部,将加密标志设置为true,但并未实际对文件内容进行加密。这种技术利用了Android系统在处 ZIP文件时不会验证头部的加密标志,而普通的解压软件则会检测该标志。例如,当使用RAR工具解压该APK文件时,会提示用户输入密码,如下图所示:

图44 伪加密的混淆示例

由于Jeb和Jadx都使用标准的ZIP库,因此无法正常解压并反编译该文件。它们的反编译错误信息如下图所示:

图45 无法使用Jadx和Jeb打开的混淆APK示例

通过采用BadPack技术,恶意软件将自身伪装为异常格式的文件,利用反编译工具对文件解析的严格性触发异常,从而显著增加静态分析的难度。这种策略有效干扰了安全研究人员的分析流程,提升了恶意软件的隐蔽性。

针对这一技术,可以通过以下方法进行应对:

(1)使用修订后的ZIP库:通过修改ZIP解压库的解析逻辑,忽略非标准字段或异常格式,确保文件内容能够正确解压。

(2)编写修复脚本:分析BadPack文件的异常字段,针对特定格式设计脚本,自动修正文件结构,使其恢复为标准格式以便后续分析。

这些应对措施能够有效绕过BadPack技术的混淆手段,为静态分析提供可靠的数据支持。

4.3 SpyNote恶意软件混淆策略

SpyNote是一种间谍软件,常通过短信和钓鱼网站等方式进行传播。攻击者通常发送包含恶意链接的SMS消息或创建伪装成合法网站的钓鱼页面,诱导目标用户点击链接并下载伪装成合法应用程序的恶意软件。自2016年在恶意软件论坛首次曝光以来,SpyNote一直作为一种持续威胁的恶意工具在全球范围内活跃。公开数据显示,今年1月和2月期间,SpyNote的新增样本数量已超过3400个,表明其威胁活动仍在快速扩散。最新披露的威胁情报显示,SpyNote伪装成安全应用程序实施攻击,其主要目标是加密货币账户,窃取私钥和余额信息,尤其针对比特币、以太坊和Tether等热门数字资产。相关攻击事件如下图所示:

图46 SpyNote仿冒安全软件实施攻击

SpyNote 除了通过多种渠道进行传播,还采用了一系列高级混淆与隐藏策略,以规避检测和分析。以下是其主要混淆技术的简要概述:

(1)篡改 APK 文件的 ZIP 格式,从而导致反编译APKTool在解析时失败,如下图所示:

图47 修改zip格式导致Apktool反编译失败的示例

(2)篡改清单文件魔术、插入垃圾字符串。修改的魔术示例如下图:

图48 修改魔术混淆示例

(3)利用相近字符实现代码混淆,如下图所示:

图49 相近字符代码混淆示例

(4)对resources.arsc文件混淆,使jadx反编译工具在分析时出错,如下图所示:

图50 Jadx无法解析resources.arsc文件

第五章

结论与未来展望

Android恶意软件的混淆技术不断进化,从简单的代码重命名到复杂的动态加载与反调试技术,恶意软件通过这些手段极大地提升了恶意代码的隐蔽性和分析难度。随着混淆技术的不断发展,安全研究人员也需要不断更新自己的分析手段,从静态分析扩展到动态分析、行为分析等多维度的分析方法。

未来,随着机器学习、人工智能等技术的引入,混淆技术和逆向分析技术将继续呈现出更加复杂和高效的态势。在这种情况下,开发更强大的自动化检测和分析工具将是破解恶意软件防护的关键。同时,开发者和安全专家也应加强对混淆技术的研究,制定出更具针对性的应对策略,以确保Android平台的安全性。

通过持续的研究和技术创新,我们有望能够更好地对抗恶意软件的混淆技术,保护用户的隐私与安全。

声明:本文来自ADLab,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。