导语:本文的研究者试图提出一种适用于真实移动设备的Android应用程序动态逆向工程保护方案。

前言

之所以Android应用程序的逆向工程很简单,是因为它是用高级但简单的字节码语言编写的。字节码(Byte-code)是一种包含执行程序,由一序列op代码或数据对组成的二进制文件,是一种中间码。由于恶意逆向工程攻击,许多Android应用被篡改和重新包装成恶意应用。为了保护Android应用程序不受逆向工程的影响,研究人员已经研究出了诸如混淆、打包(打包可执行文件)、加密和反调试等反逆向工程技术。

不过混淆、打包和加密是针对静态逆向工程的防御技术,并不能防止内存转储和运行时调试(runtime debugging)等动态逆向工程。而现有的针对动态逆向工程的防御技术,通常只会通过确定应用程序是否在基于仿真的分析环境中执行,如果是,则会停止在仿真器上的执行过程来保护应用程序。不过道高一尺魔高一丈,目前攻击者可以直接在真实移动设备上采用动态逆向工程技术,这意味着基于检测仿真器的保护技术已经失效。

本文的研究者试图提出一种适用于真实移动设备的Android应用程序动态逆向工程保护方案。该方案会检测应用程序运行的设备是否是具有root权限的设备或应用程序是否正在调试。如果是,该方案将立即停止执行应用程序。实际验证的结果表明,通过挂钩方式来逃脱根或调试环境检测的攻击技术,都可以被该方案检测到。

该方案的优点之一是它不是作为应用程序源代码的一部分实现的,而是作为单独的可执行文件实现的。这意味着,该方案可以应用于源代码无法实现的应用程序中。

动态逆向工程攻击的介绍

软件逆向工程是分析一个程序结构及其行为过程的方式,其目的是了解程序如何工作和运行。逆向工程可以用于学习程序的工作方式,还可以通过了解现有程序的结构和行为过程,以低成本的方式创建新的应用程序。通过对可执行文件进行反编译或逆向工程,分析人员可以获得程序的源代码,进而了解程序背后的应用技术,最后借鉴现有程序的一些思想设计新的程序。更重要的是逆向工程可以用来篡改或侵入移动应用程序达到非法目的,如绕过认证或支付过程,而入侵或篡改控制医疗设备的移动应用程序甚至会威胁到人类的生命。根据著名应用保护解决方案提供商Arxan Technologies的报告,97%的付费Android应用和80%的免费Android应用都被黑客攻击过。逆向攻击过程一般包括如下5个步骤:

1.选择一个目标应用程序;

2.对应用程序进行逆向分析;

3.提取和窃取机密数据;

4. 将破解的程序重新再创建或对其中的内容进行篡改,目的就是将恶意软件其伪装成正常的应用程序或新版本补丁,来感染目标设备;

5.司机将恶意程序传播出去;

于是程序开发员为了保护Android应用程序的程序代码不受逆向工程攻击,就会研究出许多针对动态逆向工程攻击的Android应用保护方案。

反逆向工程有助于防止软件盗版、保护知识产权和保护应用程序免受恶意攻击。不过,反逆向工程之所以会被如此重视,是因为应用程序开发人员有兴趣保护他们的应用程序。为了防止Android应用程序的版权侵权,目前已有许多关于混淆技术和反静态逆向工程技术的研究。然而,这些技术并没有考虑到动态逆向工程的行为,如内存转储。虽然目前有一些技术可以防止基于仿真器的动态逆向工程,但是这些技术对于基于真实设备的动态逆向工程却起不到任何防护作用。另一个问题是,现有的反动态逆向工程技术是作为应用程序源代码的一部分实现的,这意味着这些技术不能应用于源代码无法实现的应用程序中。

在本文中,研究人员提出了一种保护Android应用程序不受真实移动设备动态逆向工程影响的方案。该方案包括:

1.根或调试环境检测技术;

2.基于调用堆栈的逃脱检测技术,用于检测挂钩攻击(用于逃脱根/调试环境检测);

3.能够保护编译(打包)可执行文件的动态代码加载技术;

实际验证的结果表明,通过挂钩方式来逃脱根或调试环境检测的攻击技术,都可以被该方案检测到。验证结果还发现,该方案可以应用于任何Android应用程序包,而不需要源代码。

针对动态逆向工程攻击技术的预防过程

为了保护软件免受逆向工程攻击,很多研究人员已经进行了研究。目前流行的反逆向工程技术包括程序代码的混淆、可执行文件的加密、反调试等。

ProGuard是一个压缩、优化和混淆Java字节码文件的免费的工具,它可以删除无用的类、字段、方法和属性。可以删除没用的注释,最大限度地优化字节码文件。它还可以使用简短的无意义的名称来重命名已经存在的类、字段、方法和属性。常常用于Android开发用于混淆最终的项目,增加项目被反编译的难度。研究人员Patrick Schulz提出了基于Android平台的几种代码混淆方法,如标识符混淆、字符串混淆、动态代码加载、死代码管理和自修改代码。标识符混淆可以将原始标识符进行模糊化处理,使其不容易被逆向分析出来;字符串混淆会将原始字符串转换为另一个字符串,而动态代码加载方法则会使用两个组件:加密的可执行文件(加密的dex文件)和解密存根。解密存根会执行三个主要发热混淆步骤:第一步是通过从内部数据结构提取加密的dex文件或从远程服务器下载文件,将加密的可执行文件提取到内存中;第二步是解密已加密的dex文件并还原原始dex文件,获取原始dex文件后,最后一步就是将原始dex文件加载到Dalvik虚拟机(DVM)并开始执行。其中的检测方法包含:

1.检测已安装的软件包,目的是检测是否安装了与root相关的应用程序;

2.检测已安装文件,目的是检测二进制文件(如busybox,su)是否存在,这些文件经常出现在安装有root权限的设备上;

3.检测BUILD标签,目的是检测设备上的Android镜像是库镜像还是由第三方开发人员构建的自定义映像;

4.检测系统属性,目的是检测系统属性的值——ro.debuggable和ro.secure是否含有具有root权限的shell;

5.检测目录权限;目的是检测是否为这些目录提供了写入权限,正常情况下目录应该只具有只读权限;

6.检测进程、服务和任务,目的是检测具有root权限的应用程序是否正在运行;

7.使用shell命令检测root权限,目的是检测使用su和ps等shell命令在单独的进程中检测具有root权限的功能;

而Sudipta Ghosh等人提出了一种使程序代码更加复杂的代码混淆技术,该技术专注于增加应用程序控制流程的复杂性,从而起到让分析式感到逆向充满了挑战性。该方法会使得获取嵌入在应用程序中的业务逻辑变得更加困难,不过其缺点是逆向工程专家可以破解出这种模糊技术。

Junfeng Xu等人提出了保护Android应用程序不受动态调试影响的调试状态和调试环境的检测方法。调试状态检测是通过检测父进程、读取进程状态或使用ptrace来检测调试器,如GDB、IDA和strace。调试环境检测是通过验证仿真器特定的属性值和特性来检测仿真器的环境。但是,这些技术不能防御使用Lime工具进行内存转储之类的攻击,Lime可以在Android设备上完全捕获内存,它还可以在采集过程中最大限度地减少用户和内核空间进程之间的交互,从而使其能够产生比其他Linux内存采集工具更为合理的内存捕获。由于内存转储攻击肯定会需要root权限,因此需要检测具有root权限的运行环境以防止动态逆向工程攻击。

San-Tsai Sun等在分析了几种现有的使用root权限的方法后,提出了相应的检测方法,该方法基于Android设备的特点。其中的检测方法就是上面讲过的7种。然而,由于这些root权限检测方法通常需要调用Android API,因此攻击者可以通过使用Xposed之类的框架挂钩API来避免被检测到。因此,我们需要一种能够检测Android设备上的那些试图绕过安全检测的攻击(挂钩)的技术。如何保护Android应用程序不受动态逆向工程的影响

大多数反逆向工程技术都会通过检测含有root权限的环境或调试环境来保护Android应用程序免受动态分析的影响。然而,这些技术很容易被诸如挂钩之类的逃脱方法所攻击。在本文中,研究人员提出了一种针对动态逆向工程的Android应用程序保护方案。该方案包括对含有root权限或调试环境的检测技术、基于调用栈的那些试图绕过安全检测的攻击(挂钩)检测技术和动态代码加载技术。本文所讲的方案,其最终是要实现含有root权限的DEX文件,然后此文件被添加到APK (Android应用程序包),也就是说,APK会被重新打包。启动应用程序时,存根DEX试图检测含有根的调试环境和逃脱方法。如果没有找到,存根DEX将动态加载原始DEX文件(classes.dex)。由于此方案不是作为应用程序源代码的一部分实现的,所以它可以应用于已经打包的apk。

1.jpg

应用本文所讲的APK方案前后的对比结构

存根DEX和动态代码加载

该方案最终是要实现一个名为存根DEX的Android可执行文件。存根DEX的主要任务是含有root权限属性的检测、调试环境检测、调用栈分析和动态代码加载。在应用本文所讲的方案之前,APK的结构如图1左边所示。通常情况下,APK包含AndroidManifest.xml(描述诸如应用程序名称、版本和访问权限等信息)、class .dex(可执行文件)、META-INF目录和assets目录。

而应用了此方案后,如上图的右边所示,存根DEX被添加到APK中。为了在启动APK时首先执行存根DEX,研究人员首先设置了位于AndroidManifest.xml中<application>元素的属性android:name的值,目的就是设置存根DEX(类应用程序或应用程序的一个子类)的应用类。原始可执行文件classes.dex被移动到assets目录,存根DEX被命名为classes.dex。然后,APK被重新包装,并伺机传播。

如果重新打包的APK在用户的智能手机中启动,存根DEX将首先执行。它会执行root权限属性检测和调试环境检测。在这些任务期间,存根DEX会分析它的调用堆栈,以检测是否被挂钩,即调用堆栈中是否出现存根DEX方法以外的任何方法调用。如果存在,存根DEX会终止应用程序,把它假设为一种那些试图绕过安全检测的攻击方法。如果不存在,任务将完成并返回检测结果。根据检测结果,存根DEX会决定是终止应用程序还是加载原始可执行文件assets或classes.dex。

如果没有检测到root权限或调试环境,存根DEX将使用DexClassLoader()为原始可执行文件创建类加载器。然后使用makeApplication()将类加载器替换为新的类加载器。原始可执行文件调用其start组件的onCreate()方法。这个过程如下图所示:

2.jpg

使用动态代码加载改变执行流程

检测表和存根DEX的检测方法:

· 自定义镜像闪烁(custom image flashing)用detectTestKeys ()方法检测;

· 更改系统属性用checkForSystemProperies()方法检测;

· 安装的su二进制文件用checkForBinary ()方法检测;

· 来自busybox的新命令用checkForBusybox()方法检测;

· 文件系统属性用checkForFilesystem ()方法检测;

· 与root相关的应用用detectRootManagementApps ()方法检测;

· 正在运行的root进程用detectRootProcess()方法检测;

本文介绍了这个新保护方案所采用的检测方法和内容部分结构,下一篇,我们会介绍,该方案是如何对root权限和调试环境进行检测,以及在真实设备上验证该方案的实际检测结果。

源链接

Hacking more

...