折腾Frida:从一个JS“大泥球”到我的模块化框架诞生记

折腾Frida:从一个JS“大泥球”到我的模块化框架诞生记

|

这是一篇纯粹的个人学习​记录,用来整理和回顾我最近在捣鼓 Frida - Gadget 时,为我那个DOF服务端学习环境量身打造一个小框架的心路历程。

🤔 梦开始的地方:那个劝退我的上千行JS

说起这事儿,得追溯到我刚开始折腾 DOF服务端 那会儿。当时我虽然懂点编程,但对 Frida 可以说是个纯纯的小白。第一次接触到的就是一个现成的脚本,打开一看,好家伙,一个庞大的.js文件,成千上万行代码把所有功能都揉在了一起,简直就是个 “代码泥球”

这让我萌生了第一次念头:能不能把它拆了? 像写 Node.js 一样,一个功能一个文件,多优雅!但当时的我对 Frida 的认知基本为零,这想法也就只停留在了想法。

后来用得久了,自己也开始动手改改代码,想加点新功能。于是,模块化的念头又一次浮现在脑海。我去贴吧搜了一下,发现确实有大佬实现了类似的方案,但大多是工程化的,每次修改都需要重新编译打包,感觉不够 “动态”,不是我想要的那种即改即用的感觉,于是这事儿又搁置了。

第三次,也是最终让我下定决心动手的一次,是看到了 dp_s 插件,非常羡慕这种功能模块化,可惜是 Squirrel 。正好当时工作中一个项目给了我灵感,于是自己着手实现一个轻量级的模块加载器!但生活嘛,总有各种事情插队,项目写了一个雏形又被搁置了。直到最近总算有了点空闲,才重新捡起来,把它打磨完善,最终变成了现在的样子。

🚀 灵光一闪:自己动手,造个mini-require轮子!

整个框架的灵魂,就是我捣鼓出来的这个超迷你的模块加载器—— mini-require。它的使命只有一个:在Frida的 JS环境 里,模仿 Node.js 的行为,让我的脚本也能优雅地进行模块化加载。

它主要干了这么几件事:

  • 📦 模块缓存: 一个模块加载一次就够了,它的 exports 会被缓存起来,下次再 require 就直接从缓存里拿,省时省力。
  • 🔒 独立空间: 每个模块的代码都包在一个函数里执行,模块之间互不干扰,再也不用担心变量名冲突打架了。
  • 💉 全局“上下文”: 我创建了一个叫 context 的全局对象,把日志、配置等公共API都挂在上面,任何模块想用,直接 context.main.log() 就行,非常方便。

✨ 解放双手:像搭积木一样管理Hook

以前每次加Hook,都得手写一堆 Interceptor.attach ,又长又丑。现在,我把它封装成了一种 “声明式” 的配置。你只需要在你的模块里,像下面这样定义一个 hooks 数组就行了。

模式一:监听函数(Attach)

想知道函数什么时候被调用,传入了什么参数?用这个就对了。

// my-module.js
module.exports = {
    hooks: [
        {
            address: "0x12345678", // 目标地址
            onEnter(args) {
                console.log("函数进来了!");
            },
            onLeave(retval) {
                console.log("函数执行完了!");
            }
        }
    ]
};

模式二:替换函数(Replace)

想把原来的函数逻辑整个换掉?也没问题。

// another-module.js
module.exports = {
    hooks: [
        {
            address: "0x87654321", // 目标地址
            replace(arg1, arg2) {
                console.log("函数被我“劫持”了!");
                return 10086; // 返回一个新值
            },
            retType: 'int',
            argTypes: ["pointer", "int"]
        }
    ]
};

写完这些配置,框架就会自动帮你处理好所有繁琐的 Hook 挂载和卸载工作,我只管关心自己的逻辑,爽!

🔥 我的“杀手锏”:敲个指令就热重载!

我知道,Frida-Gadget 本身是支持文件变动后自动重载整个脚本的。但在我的模块化架构下,我想要的是更精细的控制,比如:我只想更新某一个模块,或者在不修改文件的情况下重新加载所有模块。

于是,我基于这个想法,做了一套通过 GM指令 来控制的热重载系统。现在,开发调试的流程变成了这样:

  1. 修改 my-module.js 文件。
  2. 在目标程序里发送一个指令,比如 //reload my-module
  3. 搞定!新代码立刻生效,整个过程行云流水,目标进程都不带重启一下的。

看看日志,重载过程一目了然:

[2025-9-11 13:11:47.812] [INFO] ================ Reloading ALL modules ================
[2025-9-11 13:11:47.812] [INFO] [Module] 'GM指令' dispose completed.
[2025-9-11 13:11:47.812] [INFO] [Module] 'GM指令' unloaded successfully.
[2025-9-11 13:11:47.815] [INFO] [Module] 'GM指令' initialization completed.
[2025-9-11 13:11:47.815] [INFO] [Module] 'GM指令' loaded successfully.
[2025-9-11 13:11:47.819] [INFO] ================ ALL modules reloaded ================

这个功能绝对是提升幸福感的利器,谁用谁知道!

🎉 小结一下,这次折腾值了!

回顾整个过程,从一个模糊的想法,到踩坑、搁置,再到最终实现,收获真的非常大。这套自己搭建的小框架,不仅解决了最初那个 “大泥球” 代码的维护难题,更重要的是,在这个过程中,我对 Frida 的运行机制、对 JavaScript 的闭包和模块化都有了一定的理解。

这便是我在Frida学习之路上的一次小小的探索与沉淀。它不一定是最优解,但它解决了我的问题,这就足够了。💪

【LSky Pro 改造】升级图片查询接口 & 上传支持指定相册 2025-12-31

评论区

© 2026 Tim's Blog