1、拿到要拆解的目标文件。在安卓中一般是apk文件。
2、反编译拆解apk包。这一步会借用一些第三方的拆解工具,例如脱壳,apktool反编译dex和res资源,将dex文件反编译为jar包等。
3、分析源码逻辑,修改中间文件,达成修改目的。修改的方式丰富多样,相关的工具也是百花齐放,这一步也是逆向工程攻防对抗最关键的地方。例如apktool反编译后会生成Smali文件,可以在里面插入一些自定义代码,例如输出关键结果的log,或直接修改某个逻辑条件,例如在针对SO文件调试分析时,可以借助IDA工具,这里又会涉及到关于调试debug开关的对抗等。
4、代码修改完成后,需要回编译重新生成apk包,这时生成的包可能是没有签名sign的,需要重新签名以支持安装。
5、调试修改的apk应用,循环应用上述流程,直到达成修改目标。
知己知彼,百战不殆 - 《孙子兵法·谋攻篇》
# 显示已连接adb的设备列表
$ adb devices
>>>输出:
List of devices attached
MDX5T20911002698 device
此时在控制台输出了已处于连接状态的我的设备MDX5T20911002698
。接下来我们以设备已安装的《微信》App为例,获取到当前版本的Apk文件:
# 1. 打开目标应用,获取当前应用的相关包名
adb shell dumpsys window | grep mCurrentFocus
# [window:adb shell dumpsys window | findstr mCurrentFocus]
>>>输出:
mCurrentFocus=Window{4c1c551 u128 com.tencent.mm/com.tencent.mm.ui.LauncherUI}
命令正常执行的话,应该可以拿到微信app的包名:com.tencent.mm,下一步我们将apk包从设备拉取到本地:
# 2. 获取对应包名在设备中的apk包路径
adb shell pm path com.tencent.mm
>>>输出:
package:/data/app/~~LAjt0dVDv2d3IFq1WbamYA==/com.tencent.mm-G2ejabdWZcEqDZJFvaw-1g==/base.apk
# 3. 将相关apk包,下载到本地设备
adb pull /data/app/~~LAjt0dVDv2d3IFq1WbamYA==/com.tencent.mm-G2ejabdWZcEqDZJFvaw-1g==/base.apk ./
>>>输出:
1 file pulled. 35.4 MB/s (249620210 bytes in 6.731s)
此时,在执行adb命令的目录,已获取到微信App对应的 base.apk 文件。
拿到apk文件后,下一步就是进行分析。分析之前先来看下apk包的结构:
Apktool
工具先将apk包反编译,修改处理后再回编译成apk。Apktool 工具会将apk包中的dex文件反编译成Smali文件(Smali 是 Android 的虚拟机所使用的一种 dex 格式的中间语言),同时也会反编译res和清单文件,变成可读版本。# 默认反编译dex文件和res
apktool d app.apk
# 也可以支持只反编译dex,保留res
apktool d -s app.apk
#或者只反编译res,保留dex
apktool d -r app.apk
按默认设置反编译后的目录如下:
com/gameapp/sqwy/BuildConfig.java
路径下的java文件生成的对应 Smali:# class.class public final Lcom/gameapp/sqwy/BuildConfig;.super Ljava/lang/Object;.source "BuildConfig.java" # static fields.field public static final APK_SUFFIX:Ljava/lang/String; = "base" .field public static final APPLICATION_ID:Ljava/lang/String; = "com.gameapp.sqwy" .field public static final BUILD_TYPE:Ljava/lang/String; = "release" .field public static final DEBUG:Z = true .field public static final VERSION_CODE:I = 0x5a .field public static final VERSION_NAME:Ljava/lang/String; = "2.6.2" # direct methods.method public constructor <init>()V .locals 0 .line 6 invoke-direct {p0}, Ljava/lang/Object;-><init>()V return-void.end method
可以看到 Smali 由类声明区、字段区、方法区构成,其中都涉及到各种字节码类型的描述符,
如下表:
类声明区。Java 中的对象在 Smali 中都是以Lpackage/name/ObjectName;
的形式来表示,在上面的Smali实例 Lcom/gameapp/sqwy/BuildConfig
,其中前面的L 表示这是一个对象类型, com/gameapp/sqwy/
表示该对象所在的包路径,BuildConfig
是对象的名字,;
表示对象名称的结束。
字段区。即 java 中类的成员变量,表示格式实例:APK_SUFFIX:Ljava/lang/String; = "base",字段名与字段类型是以冒号 :
分隔,其中冒号后的 Ljava/lang/String
表示该字段的类型。
方法区。方法的声明以 .method
开头,.end method
结尾,方法的调用示例:invoke-direct {p0}, Ljava/lang/Object;->
,其中 invoke-direct
标识直接调用该方法。关于smali修改用到的常用指令,可以参考下表来修改:
com.test.utils.LogUtils
,方法名为 debug(Object o)
#Obj 为需要打印的变量
invoke-static {Obj}, Lcom/test/utils/LogUtils;->debug(Ljava/lang/Object;)V
4、回编译
当完成 Smali 或者so里面代码逻辑的定位和修改后,就可以继续使用apktool
工具将包回编译生成apk文件,命令如下:
apktool b you-compile-dir
dist
文件夹,最终生成的apk文件会输出到里面。要注意此apk是未签名的apk,需要进行重新签名。jarsigner
工具完成最终签名,命令如下:jarsigner-keystore ./YOU_KEYSTORE_FILE.jks-storepass PASSWORD-keypass PASSWORD-signedjar XXX_signed.apk XXX.apk SIGN_ALIAS-digestalg SHA1 -sigalg SHA1withRSA
最终生成一个已签名的apk文件,可以正常安装到设备中测试功能了。
本文讲解了逆向工程大概的攻击流程,带大家了解逆向工程攻击的情况,只是作为基础入门,真正的攻防策略比这更复杂,每一步骤都有更深入的应用。攻防策略总是在不断对抗中升级进化,在我们日常的Android开发中,了解常用的攻防手段,才能做到成竹在胸,做出安全高质量的产品。