本节只是对 jadx 的使用做简单介绍
我们知道,每个 Android 都有对应的安装包,是以 apk 为名字后缀的文件,App 的实现逻辑都包含在这个文件中。 apk 文件往往包含资源文件(如图标,字体等),由 java 代码编译而成的 dex 文件(可通过反编译 dex 文件得到 java 代码), 和一些相关的配置文件( 如 AndroidManifest.html 文件)。 关于其中细节可以去了解 Android 开发的相关知识
逆向中关键的一部就是反编译 apk 文件, 将其还原成可读性高的 java 代码, 在多数情况下,我们通过观察并分析这个 Java 代码就能找到想要的核心逻辑。 工欲善其事,必先利其器。用来反编译 apk 文件的工具有很多,例如 jadx , JEB , Apktool 等, 不同工具的用法和定位也有所不同
jadx 的简介
jadx 是一款使用广泛的反编译工具, 可以一键把 apk 文件还原成 java 代码, 使用起来简单,功能强大,还具有一些附加功能可以辅助代码追查。 其 github 地址为 : https://github.com/skylot/jadx
安装
下载: Release 1.5.0 · skylot/jadx (github.com)
进入到 bin 目录
主要功能如下:
除了反编译 apk 文件, 还可以反编译 jar , class,dex, aar 等文件和 zip 文件中的 Dalvik 字节码
解码 AndroidManifest.xml文件和一些来自 resources.arsc 中的资源文件
一些 pak 文件在打包过程中增加了 java 代码的混淆机制,对此 jadx 提供反混淆的支持
jadx 本身是一个命令行工具, 仅仅通过 Jadx 这个命令就可以反编译一个 apk 文件。除此之外,它也有配套的图形界面工具---jadx.gui , 这个使用起来更加方便,能直接以图形界面的方式打开一个 apk 文件。 同时, jadx-gui 对反编译后得到的 java 代码和其他资源文件增加了高亮支持(就像 在IDE 中打开这些内容一样), 还具有快速定位,引用搜索,全文搜索等功能。所以我们往往直接使用 jadx-gui 完成一些反编译操作
准备工作
本节会以一个 App 为例介绍 jadx 的命令和 jadx-gui 的使用方法,在开始前需要安装好 jadx 和 app5 (https://app5.scrape.center)
jadx 命令
使用 jadx 的命令执行文件的反编译操作,主要是指定一些输入参数和输出参数,这些参数的设置细节直接参考说明即可, 运行 jadx -h 命令 查看 jadx 命令的用法
jadx -h
jadx - dex to java decompiler, version: 1.5.0
usage: jadx [command] [options] <input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .jadx.kts)
commands (use '<command> --help' for command options):
plugins - manage jadx pluginsoptions:
-d, --output-dir - output directory
-ds, --output-dir-src - output directory for sources
-dr, --output-dir-res - output directory for resources
-r, --no-res - do not decode resources
-s, --no-src - do not decompile source code
--single-class - decompile a single class, full name, raw or alias
--single-class-output - file or dir for write if decompile a single class
--output-format - can be 'java' or 'json', default: java
-e, --export-gradle - save as android gradle project
-j, --threads-count - processing threads count, default: 2
-m, --decompilation-mode - code output mode:
可以看到, 参数 <input files > 就是输入文件的路径,其他参数如 -d 可以指定反编译后输出文件的路径, -r 可以指定不解析资源文件(能够提升整体反比编译的速度)。于是我们可以使用下面的命令对已经下载好的 scrape-app5-apk 文件进行编译
jadx scrape-app5.apk -d scrape-app5
运行完毕后会生成一个 scrape-app5 文件夹
从中可以看到, AndroidManifest.xml 文件,资源文件和原始的 java 文件等都成功还原出来了。例如 com.glodz.mvvmhabit.ui.MainActivity.java 文件的内容还原结果如下
package com.goldze.mvvmhabit.ui; import android.os.Bundle; import com.goldze.mvvmhabit.R; import com.goldze.mvvmhabit.ui.index.IndexFragment; import defpackage.l; import me.goldze.mvvmhabit.base.BaseActivity; /* loaded from: classes.dex */ public class MainActivity extends BaseActivity<l, MainViewModel> { @Override // me.goldze.mvvmhabit.base.BaseActivity public int initContentView(Bundle bundle) { return R.layout.activity_main; } @Override // me.goldze.mvvmhabit.base.BaseActivity public int initVariableId() { return 2; } @Override // me.goldze.mvvmhabit.base.BaseActivity public void initParam() { super.initParam(); setRequestedOrientation(-1); } /* JADX INFO: Access modifiers changed from: protected */ @Override // me.goldze.mvvmhabit.base.BaseActivity, com.trello.rxlifecycle2.components.support.RxAppCompatActivity, android.support.v7.app.AppCompatActivity, android.support.v4.app.FragmentActivity, android.support.v4.app.SupportActivity, android.app.Activity public void onCreate(Bundle bundle) { super.onCreate(bundle); startContainerActivity(IndexFragment.class.getCanonicalName()); }
可见还原效果还是比较理想的。就这样,我们只用一条命令就完成了对 apk 文件的反编译,其中 java 代码逻辑一览无余
jadx-gui 的使用方法
jadx-gui 是一个图形界面工具,它就像一个 IDE , 支持很多方便快捷的交互式操作(例如把一个 apk 文件拖到 jadx-gui 后, 它会直接打开这个文件,之后高亮显示反编译后的代码), 以及代码搜索,定位等,相比 jadx , 更推荐 jadx-gui 的使用
启动和反编译
直接使用命令启动
jadx-gui
可以通过文件路径打开示例 apk 文件,也可以直接将 apk 文件拖入到 jadx-gui 的窗口中,还可以从菜单栏中的 “文件” -- “打开文件” 调出资源管理器来打开 apk 文件。 文件打开后,稍等片刻,反编译就完成了。
从界面中的左侧可以发现,反编译后的 Java 代码以一个个包的形式组织在一起,另外还有资源文件,其中包括图片文件,布局文件和 AndroidManifest.xml 文件(内含 apk 文件的基础信息)等。 在左侧展开想要查看的包,右侧就会出现对应的 java 代码,可以看出,Java 源码的还原度还是很高的
保存为 Gradle 项目
我们可以把反编译的文件另存为 Gradle 项目, Gradle 项目就是开发版本的 Android 项目
文件----另存为 Gradle 项目
导出后的目录结构和我们在 jadx-gui 界面看到的结构基本一致,这个项目是可以被 Android Studio 工具打开的, 打开后的代码一般是无法直接运行的,因为毕竟整个项目是反编译出来的,我们不大可能完全还原出开发版本的 Android 项目。 如果你对 Android 开发比较了解,可以试着修改一下源码和 Gradle 配置,是可以使项目正常运行的。 即使不能运行也没有关系, 因为我们的目的并不是运行这个代码,而是分析其中的逻辑, 所以要把目光聚焦到查找和定位目标方法与逻辑定义上, Android Studio 能够更方便的完成成这些操作,当然 jadx-gui 也提供了查找和定位的相关功能,现在我们回到 jadx-gui ,了解一下其常见的用法
文本搜索
首先我们可以通过 mitmdump 之类的工具知道请求的 URL 是
https://app5.scrape.center/api/movie/?offset=0&limit=10&token=YmM2Mzg2MTg2ZjlmZDE4MGMyY2Q5NGFjZ
具体操作查看
我们发现请求中是带有token 的,而在学习本章之前,我们只能通过抓包的方式获取 token ,现在我们可以反编译 apk 文件, 得到了 java 源代码, 就有办法找出这个 token 的生成逻辑。可以先寻找一些突破口,例如搜索固定字符串,想这里的 URL 中的 api/movie 和 token 这个字符串都是可以的,因为在构造 URL 的时候, 它们经常就是写死的常量, 如果能找到对一个的字符串,就可以顺藤摸瓜找到 token 的生成逻辑
那我们在源代码中搜索一下 URL 中的 api/movie 字符串,可以使用 jadx-gui 提供的搜索功能,打开菜单栏里面的 导航-----搜索文本 , 这时 jadx-gui 会显示一个搜索框,在搜索文本下方填入 /api/movei 同时可以从类名, 方法名, 变量名和代码中选择搜索为,自行勾选即可,下方会显示搜索结果
可以看到,搜索到了两处包含 /api/movie 字符串的位置,可以依次看一下这两处的内容,先选中第一个搜索结果然后点击 “转到” 按钮, 即可跳转到对应的代码出
可以看到,代码中有一个名为 i 的类, 里面有一个 index 方法, 该方法接收两个参数,分别是 i 和 i2 ,目前还不知道它的作用
不防先看看 index 方法的逻辑。 方法内首先构造了一个 ArrayList 对象并赋值给了 arrayList 变量,这相当于 python 中初始化空列表, 然后调用 add 方法往 arrayList 中添加了一个字符串 /api/movie , 接着调用了一个 encrypt 方法,并传入参数 arrayList , 通过名字大致可以猜到 encrypt 方法实现的是加密过程, 加密后的结果被赋值为 encrypt 变量, 最后调用 index 方法本身,并传入参数 i 和 i2 的组合计算结果以及刚刚得到的 encrypt 变量,并返回最终的结果
现在我们大致了解了整个流程,但对其中的一些参数和调用过程还是一头雾水,难道这里就包含 token 逻辑,似乎不好确认
逆向过程其实就包含一些不确定性,在查到一些蛛丝马迹之后,如果不确定查到的内容实时不是我们想要的,就继续深入研究,这就是一个推敲和追查的过程
查找方法的声明
我们可以试着寻找一下 encrypt 方法的声明,右击 encrypt 方法名,会打开一个菜单,选择跳到声明,这样我们就能找到 encrypt 的方法位置
可以看到,这里显示了 encrypt 方法的源代码,初步观察期逻辑是传入一个包含字符串的 List 对象,然后经过一些加密处理返回一个高度意思 Base64 编码的字符串,而我们之前看到的 token 字符串的格式也符合 Base64 的编码格式,至于这里究竟是不是 token 的生成过程,我们会在后面继续验证,这里主要是了解 jadx-gui 的一些用法
刚才我们通过 “ 跳到声明” 选项到了声明 encrypt 方法的位置, 那能不能通过该声明,找到调用 encrypt 方法的位置呢?
查找用例
右击声明处的 encrypt 方法名, 在打开的菜单中可以看到一个 “查找用例” 的选项,点击之后,查找结果
在搜到的结果上直接双击,或者先选中结果,再点击 “转到” 按钮,都可以跳转到对应的代码处。
反混淆
jadx-gui 还有一个强大的功能,就是反混淆。(这里的图是上面跳转过来的)
我们看到 encrypt 方法所在的类是 i ,它实现了一个接口 h ,仅从这些字母并不好推测究竟是什么意思,这时 APP 在编译和打包阶段做了一些混淆操作导致的结果,和 JS 中的变量混淆很相似
针对这个问题, jadx-gui 具有反混淆功能, 我们可以打开反混淆开关,点击菜单中的 “工具”---“反混淆” 可以看到原来的类名和接口都还原出来了
代码的可读性就打打增加了
设置选项
jadx-gui 还提供了很多设置功能,可以点击工具栏中的 “更多设置” 按钮
然后回打开一个设置页面
这其实是一个总的设置页面,我们可以在这里配置 jadx-gui 的各个选项,如是否启用反混淆,反编译过程允许的并行线程数,系统是否区分大小写,是否反编译资源文件等,这些和 jadx 的一些命令动能是一致的
日志查看
在 jadx-gui 运行的过程中,还可以查看运行日志,点击工具栏中的 “日志” 按钮即可打开日志查看器
可以通过上方的选择框选择日志等级,例如这里选择了 ERROR 级别,即显示错误日志,如果在反编译过程中出现了错误,可以在这里查看错误细节
常见问题
如果有些 apk 文件比较大, jadx-gui 反编译所需的事件和消耗的资源就会更多,所以有时候会出现类似 OutOfMemoryError 类型的错误,表示内存溢出,对于一些比较大的 apk 文件,是会出现这种错误,我们可以尝试下面两种解决方案
增加 JVM 的最大内存, 设置 JVM_OPTS , 把 JVM 的最大内存调大,
减少线程数,线程多了,反编译过程消耗内存自然也会增多,可以在运行 jadx 命令的时候通过 -j 命令适当将线程数设置为更小的值