Administrator
发布于 2026-08-25 / 27 阅读
0
0

系列文章《Android 工程师的 100 篇进阶笔记》第 47 篇:Android 逆向工程实战:Hook、Frida 与动态调试

上一篇聊了 Wear OS 开发,这篇换个方向——逆向工程。做安全研究的、做竞品分析的、做加固防护的,都需要懂逆向。这篇从静态分析到动态 Hook,梳理 Android 逆向的核心工具和方法。

声明:逆向技术用于安全研究和自有 App 的防护验证。对他人 App 进行逆向可能违反法律和服务条款,请在合法授权范围内使用。

逆向的基本流程

  1. 获取目标:APK 文件或设备上的已安装应用

  2. 静态分析:反编译、看代码逻辑、找入口

  3. 动态调试:运行时 Hook、修改行为、观察数据

  4. 协议分析:抓包、解密网络请求

  5. 防护对抗:了解加固手段,评估自己的 App 是否安全

静态分析

APK 结构

APK 文件(ZIP 格式)
├── AndroidManifest.xml    # 清单文件(二进制 XML)
├── classes.dex             # Dalvik 字节码
├── classes2.dex            # 多 DEX
├── res/                    # 资源文件
├── assets/                 # 原始资源
├── lib/                    # Native 库(.so 文件)
│   ├── arm64-v8a/
│   ├── armeabi-v7a/
│   └── x86_64/
└── META-INF/               # 签名信息

反编译工具

jadx:最常用的反编译工具,把 DEX 反编译成 Java 代码

# 命令行版
jadx -d output/ app.apk

# GUI 版
jadx-gui app.apk

jadx 的反编译质量比 dex2jar + JD-GUI 好不少,对混淆代码的处理也更智能。

apktool:反编译资源和 smali 代码

apktool d app.apk -o output/

apktool 的优势是能把资源文件还原成可读 XML,且修改后可以重新打包。

smali 代码

smali 是 DEX 的汇编表示。虽然可读性不如 Java,但有些场景必须看 smali(比如 jadx 反编译失败时):

# Java: int x = 1 + 2;
# Smali:
const/4 v0, 0x1
const/4 v1, 0x2
add-int v2, v0, v1

修改 smali 后用 apktool b 重新打包,再签名就能运行。这是最传统的"改逻辑重打包"方式。

混淆对抗

ProGuard/R8 混淆会把类名方法名替换成 a、b、c。对抗手段:

  1. 找入口点:从 AndroidManifest.xml 的 Application 和 Launcher Activity 入手

  2. 搜索字符串:用 jadx 搜索关键字(URL、错误提示、Log tag),定位到具体方法

  3. 交叉引用:jadx 的 "Find Usage" 功能追踪方法调用链

  4. 资源 ID:通过 findViewById(R.id.xxx) 反查资源名

动态分析:Frida

Frida 是目前最强大的 Android 动态 Hook 框架,支持 JavaScript 脚本注入:

环境准备

# 电脑端安装
pip install frida-tools

# 设备端运行 frida-server
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &"

需要 root 设备或模拟器。

Hook Java 方法

// hook.js
Java.perform(function() {
    var Log = Java.use("android.util.Log");
    
    // Hook Log.d 方法,打印调用栈
    Log.d.overload('java.lang.String', 'java.lang.String').implementation = function(tag, msg) {
        console.log("[Log.d] " + tag + ": " + msg);
        console.log(Java.use("android.util.Log").getStackTraceString(
            Java.use("java.lang.Exception").$new()
        ));
        return this.d(tag, msg);
    };
});
frida -U -l hook.js -f com.example.app  # -U USB设备 -f 启动App

Hook Native 方法

Interceptor.attach(Module.findExportByName("libnative.so", "Java_com_example_NativeLib_decrypt"), {
    onEnter: function(args) {
        // args[0] = JNIEnv, args[1] = jobject
        // args[2+] = 实际参数
        console.log("decrypt input: " + args[2]);
    },
    onLeave: function(retval) {
        console.log("decrypt output: " + retval);
    }
});

常用 Hook 场景

  1. SSL Pinning 绕过:Hook OkHttp 的 CertificatePinner 或 TrustManager

  2. 加密算法追踪:Hook javax.crypto.Cipher,打印加密前后的数据

  3. SharedPreferences 监控:Hook SharedPreferences.getString 等,看 App 存了什么

  4. 网络请求拦截:Hook OkHttpClient.newCall,打印请求 URL 和 body

// SSL Pinning 绕过(简化版)
Java.perform(function() {
    var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
    var TrustManager = Java.registerClass({
        name: "com.frida.TrustManager",
        implements: [X509TrustManager],
        methods: {
            checkClientTrusted: function() {},
            checkServerTrusted: function() {},
            getAcceptedIssuers: function() { return []; }
        }
    });
    
    var SSLContext = Java.use("javax.net.ssl.SSLContext");
    SSLContext.init.overload(
        '[Ljavax.net.ssl.KeyManager;',
        '[Ljavax.net.ssl.TrustManager;',
        'java.security.SecureRandom'
    ).implementation = function(km, tm, sr) {
        this.init(km, [TrustManager.$new()], sr);
    };
});

抓包

Charles / Proxyman:HTTP/HTTPS 抓包

# 设置代理
adb shell settings put global http_proxy 192.168.1.100:8888

# 清除代理
adb shell settings put global http_proxy :0

HTTPS 需要安装证书。Android 7+ 默认不信任用户证书,需要:

  • 把证书放到 /system/etc/security/cacerts/(需要 root)

  • 或在 network_security_config.xml 中配置信任用户证书

对抗方案:很多 App 用了 SSL Pinning,抓包会失败。这时候用 Frida 绕过 Pinning,或用 objection 工具:

pip install objection
objection -g com.example.app explore
# 进入交互式 shell
android sslpinning disable

加固检测

了解逆向手段的目的是评估自己 App 的防护强度。常见加固检测:

  1. Root 检测:检查 /system/app/Superuser.apk、su 命令、Magisk 特征

  2. Frida 检测:扫描端口(27042)、线程名、内存特征

  3. 调试器检测:android.os.Debug.isDebuggerConnected()

  4. 模拟器检测:检查 CPU 型号、传感器、IMSI 等特征

  5. Xposed 检测:遍历已安装 App 列表查 Xposed Installer

但注意:所有检测都能被绕过。检测的价值在于提高逆向成本,不是绝对防护。

防护建议

  1. 核心逻辑放 Native 层:C/C++ 代码比 Java 更难逆向

  2. VMP(虚拟机保护):把关键逻辑转成自定义指令集,大幅增加逆向成本

  3. 字符串加密:敏感字符串不要明文存储,运行时解密

  4. 完整性校验:App 启动时校验自身签名和 DEX 完整性

  5. 服务端验证:关键数据不要只靠客户端判断,服务端必须二次验证

小结

逆向工程是安全攻防的基础。了解 Frida Hook、抓包、反编译的方法,才能知道自己的 App 有哪些薄弱环节。防护的核心不是"防不住"而是"提高成本"——让逆向者的投入产出比低到不值得做。

下一篇聊聊 Compose Multiplatform:跨平台 UI 的新可能。


评论