Android React Native应用逆向分析初探

Android React Native应用逆向分析初探

前言

随着移动互联网时代的到来,用户在移动设备上花费的时间越来越多,不仅是因为移动设备方便携带,而且还因为层出不穷的大量应用提供为用户使用,以往在电脑上才能做的事情,现在仅靠一部手机就可以解决了。

当前的移动设备厂商很多,但是被广泛使用的主流系统却只有两个,Android和iOS,因此现在大多数应用都会有两个版本,Android版本和iOS版本。然而这两种应用的开发方式却完全不同,移动客户端开发人员不得不分成两个队伍,分别开发Android应用和iOS应用,尽管这两个应用的功能、界面、逻辑都是完全相同的,隐形中就带来了重复造轮子的问题。

Facebook在2015年开源了React Native技术,使用这项技术进行开发时主要使用JavaScript语言和React库,只要掌握了这项技术,那么同时开发Android和iOS应用将变得可能。

尽管这项开发技术的应用已经如此的广泛,但是本人通过搜索发现没有任何一个博客或者帖子是对这类Android应用的逆向分析,不论此种类型的Android应用在逆向工程上的难易程度如何,总该有一些博客对此进行阐述,因此也就产生了本篇文章。

原理

React Native原理简介

React Native框架如下图所示:Alt pic

从上图中可以看出编写一次JS代码就可以在三种不同的前端显示,分别为Android、iOS、Browser,不过针对不同的前端需要做出相应的调整与适配。

当页面真正渲染出来以后,它实际上还是Native代码,React Native的作用就是把JavaScript代码映射成Native代码以及实现两端的通信。可以这么理解,对于开发者编写的JS代码,如果是用于显示UI界面的代码则转化为Android或者iOS界面组件代码并显示,如果是非UI界面的代码则转化为Android或者iOS平台上支持的响应逻辑处理操作。

Android React Native原理简介

Android React Native框架大致分为三层,如下图所示: Alt pic
– 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安装包。

静态分析

  1. 使用解压缩软件(WinRAR等)打开tuyue.apk,在assets目录中发现了index.android.bundle文件,可判断出该应用使用了Android React Native框架。可在工作目录创建新文件夹tuyue_files用于存放分析过程中产生的文件等,进而把index.android.bundle解压到tuyue_files文件夹中。
  2. 使用Notepad++打开index.android.bundle可以发现该JS文件被压缩和混淆过,难以阅读,因此需要去混淆和格式化,可以使用JStillery进行在线转换,使用Beautify和Deobfuscate功能并复制到本地Notepad++中并保存为文件index.android.bundle.jstillery.js,可阅读性大大改善,如下图所示: Alt pic Source Panel中上部的编辑框中是转换之前的JS代码,下部的编辑框是转换之后的JS代码,可以看出JStillery的效果还是不错的。
  3. 接下来就是阅读庞大的JS代码了,对于之前几乎没有使用JS写过代码的小白的我来说这无疑是一项挑战。不过对于这种广泛使用的技术,肯定有人分析过index.android.bundle文件格式的。
  4. 通过搜索还是发现了一篇博客,react-native bundle 解释与拆解,可知整个文件可以分为三个部分,如下图所示: Alt pic
    • 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:模块依赖列表。
      

      每一个模块的参数含义都是相同的,只是有些被混淆了。如下图所示: Alt pic

      2.number:本模块ID。

      3.DependencyMap:本模块依赖的模块ID列表。

      模块示例:

      __d(function(global,_require,module,exports,_dependencyMap){...},
          908,
          [12,600,354,875,909,346]);
      

      文件中的模块按照模块ID从小到大顺序排列。

    • require calls : 执行InitializeCore和Entry File,多行形如require(模块ID);的代码,代码示例:require(41);这里的模块ID即为模块声明部分的模块ID,执行该行代码时会按照深度优先的方法遍历所有依赖模块。
  5. 有了以上文件格式的参考,进而继续进行静态分析将容易一些,不过依然需要人工分析,那么人工分析时就需要从庞大的代码中寻找重要的代码,比较常用的手段是字符串搜索或者代码搜索,都可以定位到想要分析的代码段,静态分析可做的事情不多,下面结合动态分析进行说明。

动态分析

网络请求分析

现在的移动应用越来越离不开网络,应用中展示的内容大多都是通过网络请求得到数据之后展示的内容,那么分析应用的网络请求则是很有必要的。本文使用Charles开启代理服务,在移动设备中设置代理,指向Charles设置的代理端口。打开图曰应用,可在Charles界面中看到该应用产生的网络请求,如下图所示: Alt pic

从中可以得出图曰应用主要访问了三个域名,主要的逻辑业务接口都在api.app.aituyue.cn域名中,另外两个域名的访问都是获取各种图片的请求。然而从上图中的接口访问详情可以看出,接口访问格式为JSON格式,但是数据请求数据字段和响应数据字段都被加密了,无法得知其中含义,api目录下的接口全部都被加密处理了。

尽管api接口的数据被加密了,但是也不算毫无所获,至少获得了比较关键的api域名和接口地址等信息,那么就可以去JS文件中查找有没有相应的关键代码段了,通过搜索JS文件,可以找到如图所示关键代码段: Alt pic Alt pic

从上图中可以看出该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,如下图所示: Alt pic

在移动设备上安装图曰Android应用,使用adb命令进入图曰APP的私有目录/data/data/net.aituyue.app/,在files/cache目录下发现可疑文件2.1.8.main.bundle,文件位置如下图所示: Alt pic

使用adb pull命令把2.1.8.main.bundle文件下载到本地,使用Notepad++打开后发现其内容与index.android.bundle文件几乎完全相同,因此猜测图曰应用在运行时会解析其私有目录中的2.1.8.main.bundle文件,那么通过修改该JS文件是否就能够注入JS代码了?答案是肯定的,下图中我在JS文件中第一个模块(模块ID为11)入口处添加了一行日志代码,如下图所示: Alt pic

使用adb push命令把修改后的2.1.8.main.bundle文件上传到设备,并且替换掉原始/data/data/net.aituyue.app/files/cache目录下的2.1.8.main.bundle文件,需要注意保持文件的权限和所有者不变,重新运行图曰APP,可在Logcat中发现注入的JS代码产生的日志输出,如下图所示: Alt pic

由于会多次修改并替换2.1.8.main.bundle文件,为了方便操作,编辑了个小bash文件,代码如下:

adb push 2.1.8.main.bundle /data/local/tmp
adb shell su -c "chown u0_a155:u0_a155 /data/local/tmp/2.1.8.main.bundle"
adb shell su -c "chmod 600 /data/local/tmp/2.1.8.main.bundle"
adb shell su -c "cp /data/local/tmp/2.1.8.main.bundle /data/data/net.aituyue.app/files/cache/"
pause

去除屏蔽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也全部奏效,最后结果如下图所示: Alt pic

从图中可以看出Logcat中输出了所有我想要得到的网络请求API的请求数据和响应数据,解密之后的JSON格式的数据。

总结

总体来看,Android React Native应用的逆向分析并不难,主要原因有两点:
1. 作为应用主要逻辑的JS代码(index.android.bundle)完全暴露在分析人员手中,尽管JS代码存在一定的混淆策略,不过这种混淆并没有对开发人员编写的代码进行混淆,可读性依然较高。
2. Android React Native并没有在运行时对JS代码进行校验,导致逆向分析人员可以实施JS代码注入,这就让逆向分析人员具备了动态调试的能力。

另外,Android React Native应用的安全性很大程度上取决于React Native技术的安全性,目前看来只实施了JS代码的简单混淆这么一个很弱的防护手段,而且现在的第三方加固厂商并没有针对于Android React Native应用的加固。正如此图曰应用,尽管采用了乐固加固,但是几乎不影响逆向人员的分析与调试。

留下评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据