第八课:手机里的钟——拆解 App
对应原书第 10 章《Android APK 逆向》
第八天,小林正在擦柜台,老周风风火火闯进来,手里举着一部手机。
"陈师傅!救命!"老周喘着气,"我儿子的手机里有个 App,他自己瞎搞设了个'家长锁',现在打不开了,他让我帮忙——可这 App 压根没有'忘记密码'选项!"
陈师傅接过手机看了看:"锁在 App 里?"
"对,输入密码才让用。可我哪知道密码!"
"这好办。"陈师傅把手机递给小林,"小林,昨天学的'听诊器'用得上。但今天我们先学点更基础的——把 App 拆开看看它到底是个什么东西。"
小林看着手机屏幕上的 App 图标,有点懵:"拆开?手机上也能拆?"
"能。App 本质上是个'包装盒'(APK),里面有说明书(Manifest)、零件(代码)、材料(资源)。我们把盒子拆开,密码的秘密就藏不住。"
课程正文
APK 逆向 = 把 Android 应用(APK)拆开分析。APK 是个 ZIP 包,里面装着:
app.apk(其实就是个 ZIP)
├── classes.dex ← Java 代码(核心逻辑!)
├── AndroidManifest.xml ← 说明书(组件/权限/入口)
├── res/ ← 资源(界面/字符串)
├── lib/*.so ← 原生库(高级代码,C/C++ 写的)
└── assets/ ← 附加文件
拆开它(两大工具)
# jadx: 把 dex 反编译成 Java 源码(最好读)
jadx -d jadx_out app.apk
# 结果: 直接看"接近源码"的 Java 代码
# apktool: 解出资源和 smali(Dalvik 汇编)
apktool d app.apk -o apktool_out
# 结果: AndroidManifest.xml + smali/ + res/
找"密码校验"(三板斧)
1. 看 Manifest: 谁是主入口(MainActivity)?
2. 看主逻辑: MainActivity 里怎么校验密码?
3. 找比较点: 密码和什么比?(第 3 课的三板斧!)
在 jadx 输出里搜索:
login / password / check / verify / key / secret
实战演示(一个示例)
// jadx 反编译出的 MainActivity(片段)
public void onClick(View v) {
String input = etPassword.getText().toString();
if (input.equals("12345678")) { // ← 密码就在这里!
unlock();
} else {
Toast.makeText(this, "密码错误", Toast.LENGTH_SHORT).show();
}
}
看到了吗——密码校验就是一个 equals 比较。用第 3 课的方法(找比较点)直接在反编译代码里找。
动态 Hook(第 6 课联动)
// 如果代码里找不全,用 Frida Hook Java 方法
Java.perform(function () {
var MainActivity = Java.use("com.example.app.MainActivity");
MainActivity.unlock.implementation = function () {
console.log("unlock 被调用了!");
this.unlock(); // 直接调用,绕过密码
};
});
陈师傅的比喻
"App 是个包装盒。盒子里的'说明书'和'零件'都是给人看的——只是平时包着。拆开盒子,密码就是一行字。"
本课口诀
App 是个盒子,拆开看说明书(Manifest),找密码校验(equals/比较)。
动手做
- 找一个测试 APK(或让 AI 生成一个带密码的 App),用 jadx 反编译,找到密码校验代码。
- 练习搜索:在 jadx 输出里搜
password/check/equals,追踪密码从哪来。 - 想一想:如果密码不在 Java 层(在 .so 原生库里),怎么找?(提示:jadx 解不出原生代码,要换 IDA/Ghidra)
想学更深的
见原书 第 10 章《Android APK 逆向》——smali 修改、重打包签名、脱壳、JNI/.so 分析。