Android React Native应用逆向分析初探
前言
随着移动互联网时代的到来,用户在移动设备上花费的时间越来越多,不仅是因为移动设备方便携带,而且还因为层出不穷的大量应用提供为用户使用,以往在电脑上才能做的事情,现在仅靠一部手机就可以解决了。
当前的移动设备厂商很多,但是被广泛使用的主流系统却只有两个,Android和iOS,因此现在大多数应用都会有两个版本,Android版本和iOS版本。然而这两种应用的开发方式却完全不同,移动客户端开发人员不得不分成两个队伍,分别开发Android应用和iOS应用,尽管这两个应用的功能、界面、逻辑都是完全相同的,隐形中就带来了重复造轮子的问题。
Facebook在2015年开源了React Native技术,使用这项技术进行开发时主要使用JavaScript语言和React库,只要掌握了这项技术,那么同时开发Android和iOS应用将变得可能。
尽管这项开发技术的应用已经如此的广泛,但是本人通过搜索发现没有任何一个博客或者帖子是对这类Android应用的逆向分析,不论此种类型的Android应用在逆向工程上的难易程度如何,总该有一些博客对此进行阐述,因此也就产生了本篇文章。
原理
React Native原理简介
React Native框架如下图所示:
从上图中可以看出编写一次JS代码就可以在三种不同的前端显示,分别为Android、iOS、Browser,不过针对不同的前端需要做出相应的调整与适配。
当页面真正渲染出来以后,它实际上还是Native代码,React Native的作用就是把JavaScript代码映射成Native代码以及实现两端的通信。可以这么理解,对于开发者编写的JS代码,如果是用于显示UI界面的代码则转化为Android或者iOS界面组件代码并显示,如果是非UI界面的代码则转化为Android或者iOS平台上支持的响应逻辑处理操作。
Android React Native原理简介
Android React Native框架大致分为三层,如下图所示:
- Java层:该层主要提供了Android的UI渲染器UIManager(将JavaScript映射成Android Widget组件)以及一些其他的常用的工具组件(例如:Fresco用于图片加载、Okhttp用于网络请求)等。
- C++层:该层主要完成了Java与JavaScript的通信以及执行JavaScript代码两件工作,其中JavaScriptCore为解析JS的核心部分,iOS使用的是内置的JavaScriptCore,Android使用的是WebKit的jsc.so。
- JS层:该层提供了各种供开发者使用的组件以及一些工具库,也是开发者编写的主JS执行入口。
使用Android React Native技术在对Android应用打包时需要先对所有的JS文件打包,打包JS文件的命令有两个:
- react-native bundle:该命令会整合、优化、混淆所有的JS文件,最后保存到Apk文件中assets目录中的index.android.bundle文件,该文件就是个JS格式的文件。
- react-native unbundle:该命令除了生成上面提到的index.android.bundle文件之外,还会生成各个单独的未整合JS文件(但会被优化),全部放在Apk文件中assets目录中的js-modules目录下。
react-native bundle命令较为常见,react-native unbundle命令较为少见。
逆向
样本
随着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,可阅读性大大改善,如下图所示:
Source Panel中上部的编辑框中是转换之前的JS代码,下部的编辑框是转换之后的JS代码,可以看出JStillery的效果还是不错的。
- 接下来就是阅读庞大的JS代码了,对于之前几乎没有使用JS写过代码的小白的我来说这无疑是一项挑战。不过对于这种广泛使用的技术,肯定有人分析过index.android.bundle文件格式的。
- 通过搜索还是发现了一篇博客,react-native bundle 解释与拆解,可知整个文件可以分为三个部分,如下图所示:
- polyfills : 最早执行的一些function,定义一些JS函数,包括模块声明方法
__d等。 - module difinitations : 模块声明,以
__d开头,每一行代表一个JS的定义,该函数的定义为:__d(factory: FactoryFn, moduleId: number, dependencyMap?: DependencyMap)1.FactoryFn:其可以被当做函数,如有理解错误,望评论!该函数定义为:function(global: Object, _require: RequireFn, module: {exports: {}}, exports: {}, _dependencyMap: ?DependencyMap)
每一个模块的参数含义都是相同的,只是有些被混淆了。如下图所示:1. global:global全局对象。 2. _require:require方法。 3. module:本模块对象。 4. exports:模块暴露。 5. _dependencyMap:模块依赖列表。2.number:本模块ID。 3.DependencyMap:本模块依赖的模块ID列表。 模块示例:
文件中的模块按照模块ID从小到大顺序排列。__d(function(global,_require,module,exports,_dependencyMap){...}, 908, [12,600,354,875,909,346]); - require calls : 执行InitializeCore和Entry File,多行形如require(模块ID);的代码,代码示例:
require(41);这里的模块ID即为模块声明部分的模块ID,执行该行代码时会按照深度优先的方法遍历所有依赖模块。
- polyfills : 最早执行的一些function,定义一些JS函数,包括模块声明方法
- 有了以上文件格式的参考,进而继续进行静态分析将容易一些,不过依然需要人工分析,那么人工分析时就需要从庞大的代码中寻找重要的代码,比较常用的手段是字符串搜索或者代码搜索,都可以定位到想要分析的代码段,静态分析可做的事情不多,下面结合动态分析进行说明。
动态分析
网络请求分析
现在的移动应用越来越离不开网络,应用中展示的内容大多都是通过网络请求得到数据之后展示的内容,那么分析应用的网络请求则是很有必要的。本文使用Charles开启代理服务,在移动设备中设置代理,指向Charles设置的代理端口。打开图曰应用,可在Charles界面中看到该应用产生的网络请求,如下图所示:
从中可以得出图曰应用主要访问了三个域名,主要的逻辑业务接口都在api.app.aituyue.cn域名中,另外两个域名的访问都是获取各种图片的请求。然而从上图中的接口访问详情可以看出,接口访问格式为JSON格式,但是数据请求数据字段和响应数据字段都被加密了,无法得知其中含义,api目录下的接口全部都被加密处理了。
尽管api接口的数据被加密了,但是也不算毫无所获,至少获得了比较关键的api域名和接口地址等信息,那么就可以去JS文件中查找有没有相应的关键代码段了,通过搜索JS文件,可以找到如图所示关键代码段:
从上图中可以看出该JS模块应该属于配置文件模块,其中记录了全局可访问的配置信息,与网络请求相关的配置在api结构中。