Android逆向工程之“矛”-攻击篇
一、Android逆向工程
特别提示:本文只为更好的防范可能的攻击做技术上的讨论,破解他人的App获取非法利益属于违法行为,请慎重!
1、拿到要拆解的目标文件。在安卓中一般是apk文件。
2、反编译拆解apk包。这一步会借用一些第三方的拆解工具,例如脱壳,apktool反编译dex和res资源,将dex文件反编译为jar包等。
3、分析源码逻辑,修改中间文件,达成修改目的。修改的方式丰富多样,相关的工具也是百花齐放,这一步也是逆向工程攻防对抗最关键的地方。例如apktool反编译后会生成Smali文件,可以在里面插入一些自定义代码,例如输出关键结果的log,或直接修改某个逻辑条件,例如在针对SO文件调试分析时,可以借助IDA工具,这里又会涉及到关于调试debug开关的对抗等。
4、代码修改完成后,需要回编译重新生成apk包,这时生成的包可能是没有签名sign的,需要重新签名以支持安装。
5、调试修改的apk应用,循环应用上述流程,直到达成修改目标。
二、逆向工程之 “矛”
知己知彼,百战不殆 - 《孙子兵法·谋攻篇》
1、获取Apk目标文件
# 显示已连接adb的设备列表$ adb devices>>>输出:List of devices attachedMDX5T20911002698 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 文件。
2、分析Apk文件
拿到apk文件后,下一步就是进行分析。分析之前先来看下apk包的结构:
3、反编译Apk文件
Apktool 工具先将apk包反编译,修改处理后再回编译成apk。Apktool 工具会将apk包中的dex文件反编译成Smali文件(Smali 是 Android 的虚拟机所使用的一种 dex 格式的中间语言),同时也会反编译res和清单文件,变成可读版本。3.1、Apktool工具和使用
从其网站 ( https://ibotpeaches.github.io/Apktool/ )下载并安装最新版本的 apktool
可以使用以下命令反编译APK:
# 默认反编译dex文件和resapktool d app.apk# 也可以支持只反编译dex,保留resapktool d -s app.apk#或者只反编译res,保留dexapktool d -r app.apk
按默认设置反编译后的目录如下:
3.2、Smali 文件格式
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;->()V,其中 invoke-direct 标识直接调用该方法。关于smali修改用到的常用指令,可以参考下表来修改:
3.3、定位分析方式
核心信息查找法
可以以关键字全局搜索,定位到要修改的信息,信息包括res中的资源名、toast弹出的文案、变量名称等。
代码动态调试法
可以在指定位置,强制抛出Exception的方式来定位调用的堆栈。
代码注入法
先用java在demo中写log输出代码,然后反编译demo,将封装的方法手动注入到指定的 Smali 逻辑中。例如有一个java的log工具类,类路径为: 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-dirdist文件夹,最终生成的apk文件会输出到里面。要注意此apk是未签名的apk,需要进行重新签名。5、重签名
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开发中,了解常用的攻防手段,才能做到成竹在胸,做出安全高质量的产品。