上一篇聊了 Wear OS 开发,这篇换个方向——逆向工程。做安全研究的、做竞品分析的、做加固防护的,都需要懂逆向。这篇从静态分析到动态 Hook,梳理 Android 逆向的核心工具和方法。
声明:逆向技术用于安全研究和自有 App 的防护验证。对他人 App 进行逆向可能违反法律和服务条款,请在合法授权范围内使用。
逆向的基本流程
获取目标:APK 文件或设备上的已安装应用
静态分析:反编译、看代码逻辑、找入口
动态调试:运行时 Hook、修改行为、观察数据
协议分析:抓包、解密网络请求
防护对抗:了解加固手段,评估自己的 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。对抗手段:
找入口点:从
AndroidManifest.xml的 Application 和 Launcher Activity 入手搜索字符串:用 jadx 搜索关键字(URL、错误提示、Log tag),定位到具体方法
交叉引用:jadx 的 "Find Usage" 功能追踪方法调用链
资源 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 场景
SSL Pinning 绕过:Hook
OkHttp的CertificatePinner或TrustManager加密算法追踪:Hook
javax.crypto.Cipher,打印加密前后的数据SharedPreferences 监控:Hook
SharedPreferences.getString等,看 App 存了什么网络请求拦截: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 的防护强度。常见加固检测:
Root 检测:检查
/system/app/Superuser.apk、su命令、Magisk 特征Frida 检测:扫描端口(27042)、线程名、内存特征
调试器检测:
android.os.Debug.isDebuggerConnected()模拟器检测:检查 CPU 型号、传感器、IMSI 等特征
Xposed 检测:遍历已安装 App 列表查 Xposed Installer
但注意:所有检测都能被绕过。检测的价值在于提高逆向成本,不是绝对防护。
防护建议
核心逻辑放 Native 层:C/C++ 代码比 Java 更难逆向
VMP(虚拟机保护):把关键逻辑转成自定义指令集,大幅增加逆向成本
字符串加密:敏感字符串不要明文存储,运行时解密
完整性校验:App 启动时校验自身签名和 DEX 完整性
服务端验证:关键数据不要只靠客户端判断,服务端必须二次验证
小结
逆向工程是安全攻防的基础。了解 Frida Hook、抓包、反编译的方法,才能知道自己的 App 有哪些薄弱环节。防护的核心不是"防不住"而是"提高成本"——让逆向者的投入产出比低到不值得做。
下一篇聊聊 Compose Multiplatform:跨平台 UI 的新可能。