Android APK逆向解析

众所周知,Android系统是开源的,运行其上的Android应用APK即Android Package也是可以通过各种工具进行反编译或者逆向工程,从而可以对其他APP进行研究或者借鉴学习,亦或从APK中获取相关信息(比如清单文件AndroidManifest.xml里面的package、versionName和versionCode等等)。

Android的构建流程

要真正进行Android反编译和逆向,首先需要对Android的编译和构建过程有一个大致的了解。因为反编译和编译的过程是相逆的。本文从Android开发者官网摘取了关于Android构建的描述,详细信息请参见Android构建流程

构建流程涉及许多将您的项目转换成 Android 应用软件包 (APK) 的工具和流程。构建流程非常灵活,因此了解它的一些底层工作原理会很有帮助。典型Android应用模块的构建流程如下图所示:
build-process

如上图所示,典型 Android 应用模块的构建流程通常依循下列步骤:

  1. 编译器(这里涉及到JDK、aapt等工具)将您的源代码转换成 DEX(Dalvik Executable) 文件(其中包括运行在 Android 设备上的字节码),将所有其他内容转换成已编译资源。
  2. APK 打包器将 DEX 文件和已编译资源合并成单个 APK。不过,必须先签署 APK,才能将应用安装并部署到 Android 设备上。
  3. APK 打包器使用调试或发布密钥库签署您的 APK:
    • a.如果您构建的是调试版本的应用(即专用于测试和分析的应用),打包器会使用调试密钥库签署您的应用。Android Studio 自动使用调试密钥库配置新项目。
    • b.如果您构建的是打算向外发布的发布版本应用,打包器会使用发布密钥库签署您的应用。要创建发布密钥库,请阅读在 Android Studio 中签署您的应用。
  4. 在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,减少其在设备上运行时的内存占用。

Android APK逆向解析

首先,要对APK文件进行分析,需要了解APK文件的结构。其实APK文件就是一个zip文件,所以采用解压缩软件可以查看里面的子项。典型的APK文件的解压缩视图如下:

Android APK文件解压缩视图

  • classes.dex:classes.dex是java源码编译后生成的java字节码文件(非传统JVM解释执行的class字节码)。但由于Android使用的dalvik虚拟机与标准的java虚拟机是不兼容的,dex文件与class文件相比,不论是文件结构还是opcode都不一样。Android模拟器中提供了一个dex文件的反编译工具,dexdump。用法为首先启动Android模拟器,把要查看的dex文件用adb push上传的模拟器中,然后通过adb shell登录,找到要查看的dex文件,执行dexdump xxx.dex(或者从压缩文件取出dex文件,直接在PC上面找到dexdump.exe的路径,然后执行命令)。另外Dedexer是一个反编译dex文件的开源工具,需要自己编译源代码。而当前,还有比如dex2jar(https://github.com/pxb1988/dex2jar)甚至封装好的GUI的工具(比如安卓逆向助手等等,可网上搜索下载)可以反编译dex文件为jar文件,再利用jd-gui(http://jd.benow.ca/)工具打开jar文件,就可以阅读java源代码了。

  • resources.arsc:编译Android资源文件后的生成的二进制文件,resources.arsc有固定的格式,包括了资源索引和字符串资源池等,详细可参见resources.arsc解析

  • AndroidManifest.xml:清单文件,该文件是每个应用都必须定义和包含的,它描述了应用的名字、版本、权限、引用的库文件等等信息,如要把apk上传到Google Market上,也要对这个xml做一些配置。在apk中的AndroidManifest.xml是经过编译的,即是二进制格式,如果直接用编辑器打开,显示会是乱码。因此需要先进行反编译处理,得到原始的AndroidManifest.xml文件。可以通过AXMLPrinter2或aapt工具解开或解析,具体命令为:java -jar AXMLPrinter2.jar AndroidManifest.xml

  • assets:assets目录可以存放一些配置文件(比如webview本地资源、图片资源、音视频等等),不会经过编译,和原始工程里面的文件是一致的,这些文件的内容在程序运行过程中可以通过相关的API(android.content.res.AssetManager)获得。

  • lib:lib目录下的子目录存放的是一些与手机CPU架构对应的C/C++代码编译生成的so文件,一般用于JNI开发。

  • META-INF:META-INF目录下存放的是签名信息,用来保证apk包的完整性和系统的安全。在eclipse编译生成一个apk包时,会对所有要打包的文件做一个校验计算,并把计算结果放在META-INF目录下。这就保证了apk包里的文件不能被随意替换。比如拿到一个apk包后,如果想要替换里面的一幅图片,一段代码, 或一段版权信息,想直接解压缩、替换再重新打包,基本是不可能的。如此一来就给病毒感染和恶意修改增加了难度,有助于保护系统的安全。

  • res:res目录存放经过编译后的资源文件,虽与原始工程目录下的res目录层次结构类似,但是实际上是经过编译处理的,只有图片资源和原始工程是一致,其他类型的资源则是编译处理过,无法直接查看。

从上面对APK内部文件的分析来看,要想反编译或者逆向工程获取信息,其实主要涉及两个方面,一是对classes.dex反编译,转换成jar或者smali等,可以利用dex2jar(https://github.com/pxb1988/dex2jar)等工具生成jar,二是结合resources.arsc和res对资源进行反编译,从而得到原始的资源文件。本文罗列平时工作和搜集到的一些常见的Android APK反编译和逆向分析工具,对工具的使用不做过多阐述,关于各个工具的详细使用和技巧,可以参见下文中提及的工具的官方链接。

全能型选手-apktool

apktool可以对Android APK直接进行反编译(也可以在反编译得到的目录基础上进行回编,生成apk)。其是Android领域反编译最常用的开源工具,虽然有商业级的反编译工具Jeb(https://www.pnfsoftware.com/),但鉴于收费且不开源,更多的程序开发者则选择apktool,其官方链接:https://ibotpeaches.github.io/Apktool/
Note:apktool反编译之后,得到是目录和文件,因此适用于需要对原始文件和代码进行分析的场景。dex通过apktool依赖的backsmali工具反编译之后得到的是smali文件,关于smali/backsmali可以参见https://github.com/JesusFreke/smali

jadx

可以将dex文件和apk文件反编译成可阅读的格式,主要涉及java代码和AndroidManifest.xml查看。jadx的github链接地址为https://github.com/skylot/jadx。适用场景:可以用于快速查看APK内的java代码和清单文件内容。

ClassyShark

google官方为Android开发者推出的独立的二进制文件检查工具,因此其权威性和强大毋庸赘言,且得到较好的维护和更新。其github地址为:[https://github.com/google/android-classyshark] ,其支持多种文件格式的解析,包括:库文件 (.dex, .aar, .so), 可执行文件 (.apk, .jar, .class) 和所有的Android二进制XML格式文件如AndroidManifest, resources, layouts等等。另外,很重要的一点,其能分析得出dex文件里面包含的方法数,这对于Android一个著名的问题(方法数超限65535,见https://developer.android.com/studio/build/multidex.html)的解决很有帮助。

AXMLParser

AXMLParser仅仅是用于解析APK里面的二进制AndroidManifest.xml,从而得到与原始工程里的AndroidManifest.xml内容一致的可读xml。较轻量级,适用于只想查看和AndroidManifest.xml里面包含的信息,比如包名package,versionCode和versionName,启动Activity等。其原始链接和下载地址为:https://code.google.com/archive/p/xml-apk-parser/

Note:该工具很久没有更新了

AAPT(aapt)

Android SDK中自带的appt工具(位于%ANDROID_HOME%\build-tools\%Android version%\aapt.exe,API Level 24及以后版本新增了aapt2,即%ANDROID_HOME%\build-tools\%Android version%\aapt2.exe。其中%ANDROID_HOME%是Android SDK的安装目录,%Android version%表示构建版本,完整示例比如"F:\Android\sdk\build-tools\27.0.0\aapt.exe"),其实构建APK的过程中,aapt工具起到了非常关键的作用,比如将原始资源等进行编译,生成R.id等。反过来,aapt也可以用于解析APK,包括清单文件AndroidManifest.xml中的信息。appt工具的功能很强大,子命令和参数非常丰富,具体可以进入%ANDROID_HOME%\build-tools\%Android version%目录,然后在该目录的命令行输入:

cd %ANDROID_HOME%\build-tools\%Android version%

然后

appt

可得到下面提示(这里重点说明aapt解析zip,jar,apk的命令使用方式。主要使用到appt的两个子命令list和dump,命令的使用请多实践或查阅命令帮助,这里不详细赘述):

示例1:读取APK的包名(Windows下可以使用findstr对输出信息过滤,linux则是grep。从而对信息进行过滤筛选,比如包名packageName)

aapt list -v -a D:\UserProfiles\CoulsonChen\Desktop\QR.apk | findstr package

示例2:读取APK的packageName、versionCode、applicationLabel、launcherActivity、permission等各种详细信息(可以使用findstr对输出信息过滤,关注某一个具体的内容,比如包名packageName)

aapt dump badging D:\UserProfiles\CoulsonChen\Desktop\QR.apk
aapt dump badging D:\UserProfiles\CoulsonChen\Desktop\QR.apk | findstr launchable-activity

通过aapt方式解析APK,需要熟悉命令的使用,也比较繁琐。但好处在于可以将其封装进脚本,用于编写自动化工具去解析和执行,这对于构建自动化测试或者自动打包流程等非常有用,而不依赖于GUI

新版Android studio(对应于Android SDK里面附带的命令行工具apkanalyzer)

Android studio 2.x及更新版本提供了一个新功能,可以分析APK文件,得到dex的方法数,查看清单文件等。如下图所示:

而实际上,在底层,就是调用Android SDK tools里面附带的apkanalyzer工具,位于$android_sdk/tools/bin/apkanalyzer,其中$android_sdk表示Android sdk的安装目录。而apkanalyzer真正的逻辑在$android_sdk/tools/lib/apkanalyzer-cli.jar$android_sdk/tools/lib/apkanalyzer.jar两个jar包中实现,可以用jd-gui等类似工具查看其java源码,学习里面的实现方法。有了apkanalyzer工具,也可以用命令行或者用脚本语言封装,从而在自动化开发或者其他分析场景使用。apkanalyzer工具可以在Android开发者官网找到说明文档