逆向工程实战手册 第 6 课

第六课:给钟装上听诊器——Frida 动态插桩

对应原书第 6 章《Frida 动态插桩》


第六天,铺子里多了一位客人——陈师傅的老朋友,手机维修铺的老周。他带来一台"中了邪"的手机:屏幕好好的,App 也能开,就是有个 App 每次打开都弹出"检测到可疑环境"然后闪退。

"陈师傅,这 App 是防着人拆它。"老周说,"我给它装到越狱机上都一样,一检测到'有人动过手脚'就自杀。"

"有意思。"陈师傅接过手机,转手递给小林,"小林,今天学新东西。这 App 是个会'咬人'的钟——你一动它,它就炸。我们不能'拆开'它,得想个办法'隔空'观察它。"

"隔空?"

"对。给它装个听诊器——不切开它的外壳,但能听见它心脏的每一下跳动。"

陈师傅在电脑上敲了一个命令:

frida -U -f com.example.app -l listen.js

"现在它启动的时候,我们的'听诊器'已经贴好了。它每调用一个'比较'函数,我们都听得见。"

手机上,那个 App 启动了。电脑屏幕立刻滚出几行字:

[strcmp] a=检测到可疑环境
[strcmp] b=正常
→ 程序在比较这两句话!它发现环境不对,就走了闪退分支

"看见了吗?"陈师傅指着屏幕,"它'心里'在嘀咕什么,全被我们听见了。现在我们改它一下——让它'以为'环境正常。"


课程正文

Frida 是给运行中的程序"贴听诊器"的工具:不改程序文件,运行时注入代码,观察/修改函数调用。

为什么需要它(和 GDB 的区别)

GDB: 暂停程序 → 看状态(像"按住钟表")
Frida: 程序正常运行 → 从外面监听(像"听诊器")

Frida 的优势:
  □ 不修改程序文件(检测不到的修改)
  □ 运行中实时观察(不用断点暂停)
  □ 手机/桌面都能用(Android/iOS/Windows/Linux)
  □ 可以改参数/改返回值(不只是看!)

第一个 Hook(听 strcmp 的心跳)

// listen.js —— 观察所有 strcmp 比较
const strcmp = Module.findExportByName(null, "strcmp");
Interceptor.attach(strcmp, {
    onEnter(args) {
        console.log("[strcmp] a=" + Memory.readUtf8String(args[0]));
        console.log("[strcmp] b=" + Memory.readUtf8String(args[1]));
    },
    onLeave(retval) {
        console.log("[strcmp] => " + retval.toInt32());
    }
});
# 启动 App 并注入
frida -U -f com.example.app -l listen.js
# -U = USB 设备, -f = 冷启动(App 从零开始), -l = 加载脚本

从"听"到"改"(Hook 的威力)

// 不但听,还改返回值 —— 让"检测"永远通过
const strcmp = Module.findExportByName(null, "strcmp");
Interceptor.attach(strcmp, {
    onLeave(retval) {
        retval.replace(0);    // 强制返回 0(相等)→ 检测永远"通过"
    }
});
原理: 程序调用 strcmp 判断"环境是否正常"
  → 我们让它永远返回"相等"
  → 程序以为环境正常 → 不再闪退

Android App 的常见 Hook(防检测)

// Hook Java 层的 Root 检测
Java.perform(function () {
    var RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
    RootBeer.isRooted.implementation = function () { return false; };
});
为什么有效: App 里用"检测函数"判断 root
  → 我们替换实现,永远返回"没 root"
  → 程序相信自己很安全

陈师傅的比喻

"有的钟,你一拧螺丝就炸(反调试)。但听诊器不用拧螺丝——贴在表面,它心跳什么都听得到。Frida 就是那把听诊器。"


本课口诀

不拆开它,也能听见它的心跳。听得见,就能改得动。

动手做

  1. 在一个测试 App(或本地程序)上跑一遍"监听 strcmp",观察比较内容。
  2. 把"监听"改成"修改返回值",让程序永远走成功分支。
  3. 想一想:如果程序不用 strcmp(自己逐字节比较),怎么监听?(提示:断在比较指令,或 Hook 更底层的函数)

想学更深的

见原书 第 6 章《Frida 动态插桩》——Java 层 Hook、Objection、SSL Pinning 绕过、Frida Gadget 免 root 注入。