随着移动互联网时代的到来,用户在移动设备上花费的时间越来越多,不仅是因为移动设备方便携带,而且还因为层出不穷的大量应用提供为用户使用,以往在电脑上才能做的事情,现在仅靠一部手机就可以解决了。
当前的移动设备厂商很多,但是被广泛使用的主流系统却只有两个,Android和iOS,因此现在大多数应用都会有两个版本,Android版本和iOS版本。然而这两种应用的开发方式却完全不同,移动客户端开发人员不得不分成两个队伍,分别开发Android应用和iOS应用,尽管这两个应用的功能、界面、逻辑都是完全相同的,隐形中就带来了重复造轮子的问题。
Facebook在2015年开源了React Native技术,使用这项技术进行开发时主要使用JavaScript语言和React库,只要掌握了这项技术,那么同时开发Android和iOS应用将变得可能。
尽管这项开发技术的应用已经如此的广泛,但是本人通过搜索发现没有任何一个博客或者帖子是对这类Android应用的逆向分析,不论此种类型的Android应用在逆向工程上的难易程度如何,总该有一些博客对此进行阐述,因此也就产生了本篇文章。
逆向
样本
随着React Native技术的广泛使用,现在也有越来越多的应用开始尝试使用这项技术进行开发,本文使用图曰Android版本进行逆向分析,可以下载并解压出tuyue.apk安装包。
静态分析
使用解压缩软件(WinRAR等)打开tuyue.apk,在assets目录中发现了index.android.bundle文件,可判断出该应用使用了Android React Native框架。可在工作目录创建新文件夹tuyue_files用于存放分析过程中产生的文件等,进而把index.android.bundle解压到tuyue_files文件夹中。使用Notepad++打开index.android.bundle可以发现该JS文件被压缩和混淆过,难以阅读,因此需要去混淆和格式化,可以使用JStillery进行在线转换,使用Beautify和Deobfuscate功能并复制到本地Notepad++中并保存为文件index.android.bundle.jstillery.js,可阅读性大大改善接下来就是阅读庞大的JS代码了,对于之前几乎没有使用JS写过代码的小白的我来说这无疑是一项挑战。不过对于这种广泛使用的技术,肯定有人分析过index.android.bundle文件格式的。通过搜索还是发现了一篇博客,react-native bundle 解释与拆解,可知整个文件可以分为三个部分,如下图所示:
polyfills : 最早执行的一些function,定义一些JS函数,包括模块声明方法__d等。module difinitations : 模块声明,以__d开头,每一行代表一个JS的定义,该函数的定义为:1.__d(factory: FactoryFn, moduleId: number, dependencyMap?: DependencyMap)2.number:本模块ID。3.DependencyMap:本模块依赖的模块ID列表require calls : 执行InitializeCore和Entry File,多行形如require(模块ID)这里的模块ID即为模块声明部分的模块ID,执行该行代码时会按照深度优先的方法遍历所有依赖模块。
有了以上文件格式的参考,进而继续进行静态分析将容易一些,不过依然需要人工分析,那么人工分析时就需要从庞大的代码中寻找重要的代码,比较常用的手段是字符串搜索或者代码搜索,都可以定位到想要分析的代码段,静态分析可做的事情不多,下面结合动态分析进行说明。动态分析
网络请求分析
现在的移动应用越来越离不开网络,应用中展示的内容大多都是通过网络请求得到数据之后展示的内容,那么分析应用的网络请求则是很有必要的。本文使用Charles开启代理服务,在移动设备中设置代理,指向Charles设置的代理端口。
从中可以得出图曰应用主要访问了三个域名,主要的逻辑业务接口都在api.app.aituyue.cn域名中,另外两个域名的访问都是获取各种图片的请求。然而从上图中的接口访问详情可以看出,接口访问格式为JSON格式,但是数据请求数据字段和响应数据字段都被加密了,无法得知其中含义,api目录下的接口全部都被加密处理了。
尽管api接口的数据被加密了,但是也不算毫无所获,至少获得了比较关键的api域名和接口地址等信息,那么就可以去JS文件中查找有没有相应的关键代码段了,通过搜索JS文件,可以找到如图所示关键代码段
从上图中可以看出该JS模块应该属于配置文件模块,其中记录了全局可访问的配置信息,与网络请求相关的配置在api结构中。
JS代码注入
首先需要知道图曰Android应用的包名是什么,可以使用Apktool工具对apk文件进行反编译,进而得到AndroidManifest.xml文件,其中包名为:net.aituyue.app。
然后我们知道Android应用的日志输出是非常重要的,特别是在开发阶段可以利用日志来调试应用,那么我们要设置下日志过滤,使用adb logcat命令,可以使用React Native官方推荐的命令:adb logcat *:S ReactNative:V ReactNativeJS:V,也可以使用Android studio中的Logcat视图来设置过滤选项,本文使用的是Android studio中的Logcat来查看日志输出,设置查找字符串ReactNative,如下图所示:
在移动设备上安装图曰Android应用,使用adb命令进入图曰APP的私有目录/data/data/net.aituyue.app/,在files/cache目录下发现可疑文件2.1.8.main.bundle。
使用adb pull命令把2.1.8.main.bundle文件下载到本地,使用Notepad++打开后发现其内容与index.android.bundle文件几乎完全相同,因此猜测图曰应用在运行时会解析其私有目录中的2.1.8.main.bundle文件,那么通过修改该JS文件是否就能够注入JS代码了?答案是肯定的,下图中我在JS文件中第一个模块(模块ID为11)入口处添加了一行日志代码
去除屏蔽console
在本人对图曰应用进行JS代码注入的过程中,主要是添加console日志的过程中发现,有些console能够在Logcat中看到结果,就像上述示例JS代码注入过程,但有些console则不起作用,特别是在网络请求相关的部分插入的console代码丝毫不起作用。
经过调研,猜测在JS代码的运行过程中存在屏蔽console的代码,但是JS代码如此的庞大,想要找到屏蔽console的代码段犹如大海捞针,因此不得不转换思路,想要通过手动构造异常来获取日志信息。
但是,在经过长时间的JS代码中的漫游,在手动查看所有的API调用的处理逻辑时,意外的发现了如下代码:
e.console={info:function(){},log:function(){},warn:function(){},error:function(){}}
根据代码上下文判断变量e代表global,那么猜测这行代码应该就是屏蔽console的代码了,通过注释掉或者删掉这行代码重新运行图曰应用,可以发现多出来了很多日志调试信息,自己手动添加的console也全部奏效
总结
总体来看,Android React Native应用的逆向分析并不难,主要原因有两点:1. 作为应用主要逻辑的JS代码(index.android.bundle)完全暴露在分析人员手中,尽管JS代码存在一定的混淆策略,不过这种混淆并没有对开发人员编写的代码进行混淆,可读性依然较高。2. Android React Native并没有在运行时对JS代码进行校验,导致逆向分析人员可以实施JS代码注入,这就让逆向分析人员具备了动态调试的能力。
另外,Android React Native应用的安全性很大程度上取决于React Native技术的安全性,目前看来只实施了JS代码的简单混淆这么一个很弱的防护手段,而且现在的第三方加固厂商并没有针对于Android React Native应用的加固。正如此图曰应用,尽管采用了乐固加固,但是几乎不影响逆向人员的分析与调试。