逆向工程实战手册 第 8 课

第八课:手机里的钟——拆解 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/比较)。

动手做

  1. 找一个测试 APK(或让 AI 生成一个带密码的 App),用 jadx 反编译,找到密码校验代码。
  2. 练习搜索:在 jadx 输出里搜 password/check/equals,追踪密码从哪来。
  3. 想一想:如果密码不在 Java 层(在 .so 原生库里),怎么找?(提示:jadx 解不出原生代码,要换 IDA/Ghidra)

想学更深的

见原书 第 10 章《Android APK 逆向》——smali 修改、重打包签名、脱壳、JNI/.so 分析。