HumanifyJS 反混淆实战指南:用 LLM 把压缩 JS 还原成人话

发布时间:2026/8/14 19:04:52
HumanifyJS 反混淆实战指南:用 LLM 把压缩 JS 还原成人话 HumanifyJS 反混淆实战指南用 LLM 把压缩 JS 还原成人话【免费下载链接】humanifyDeobfuscate Javascript code using ChatGPT项目地址: https://gitcode.com/gh_mirrors/hu/humanify凌晨两点你接手了一个老项目。构建产物里躺着一个 200KB 的bundle.min.js报错堆栈指向第 1 行第 3921 列打开一看满屏都是function a(e,t){var n[]...}。这种场景下HumanifyJS 这类 AI 辅助的 JavaScript 反混淆工具就是为救你而生的它调用大语言模型把压缩代码里那些a、e、t全部重命名为inputString、chunkSize这样的可读名字让你能在十分钟内读懂原本要花一整天破解的代码。本文不写套话直接带你从一个真实事故现场出发把工具的用法、原理和坑位一次讲透。一段压缩代码引发的事故现场 ️先还原一下你的处境。报错堆栈长这样at a (bundle.min.js:1:3921) at n.push (bundle.min.js:1:4047)你找到了对应位置看到的却是function a(e,t){var n[];var re.length;var i0;for(;ir;it){if(itr){n.push(e.substring(i,it))}else{n.push(e.substring(i,r))}}return n}四个字母a、e、t、n撑起了整个函数。你想加个断点却不知道该观察哪个变量你想搜e.length理解业务逻辑搜出来的全是不相干的匹配。这不是代码写得烂——是压缩工具干的它天生就该把名字缩短。你开始手动重命名e是入参t也是入参n是个数组r是个长度……五分钟过去你还原了这个函数但文件里还有另外几百个这样的标识符。手动逆向显然走不通。这时候你需要的不是再努力一点而是一个能把起名字这个体力活外包出去的帮手。在动手之前先看清它到底能做什么 HumanifyJS 的官方定位一句话就能说清用 LLM 为压缩/混淆后的 JavaScript 重新命名标识符。它的适用人群很明确接手含压缩产物、需要读代码排障的前端开发者分析第三方库、SDK 内部实现的逆向爱好者需要审计线上dist目录里到底跑了什么逻辑的安全工程师它有一个很容易被忽略的边界它只做一件事——改名字。它不会帮你格式化代码、不会拆解 webpack 模块、不会把 UglifyJS 压缩的合并语句还原成原本的写法。格式化你可以交给 Prettier解包 webpack 产物可以交给 webcrack而给几千个标识符起一个贴切的名字这种需要理解语义的活恰好是 LLM 的强项也是 HumanifyJS 专注的领域。搞清楚边界之后你会发现只做一件事反而是一种美德流程清晰、输出可预测、出问题也好排查。3 分钟跑通你的第一条命令 ⚡v3 版本的 HumanifyJS 是一个 Rust 编写的单文件静态二进制——不需要 Node、不需要 npm、不需要 Python 环境下载下来就能跑。如果你想从源码构建一条命令即可git clone https://gitcode.com/gh_mirrors/hu/humanify cd humanify cargo build --release编译完成后把target/release/humanify放到 PATH 里然后验证humanify --help运行逻辑非常 Unix 风格输入可以是一个文件路径也可以是-表示从标准输入读取输出默认打到标准输出也可以用-o指定文件。六个子命令对应六种 LLM 来源humanify openai|gemini|anthropic|ollama|openrouter|requesty [FLAGS] INPUT拿最常用的 OpenAI 举例把环境变量配好之后你实际敲的命令只有一行export OPENAI_API_KEY你的密钥 humanify openai bundle.min.js -o bundle.readable.js十几秒后bundle.readable.js里那些a、e、n就都变成了有含义的名字。整套流程从装好工具到拿到结果三分钟绰绰有余。打开引擎盖看流程它怎么处理一个标识符 加一个--verbose参数重跑一遍你就能亲眼看到工具的内部工作日志humanify openai bundle.min.js -o bundle.readable.js --verbose --progress日志会告诉你一共找到了多少个标识符当前在处理第几个每个标识符原来的名字和建议的新名字分别是什么。这一层进度可见性特别适合排查问题——比如某个名字一直没被改你就能定位到是哪一步出了问题。而它的处理顺序也藏着设计心思从最大的作用域开始逐层向内。先把整个函数/模块级别的标识符定下来再处理里面的局部变量。这就像先给一栋楼贴好楼层号再给每扇门装门牌避免内层名字先定、外层一改名又把语境搅乱。每个标识符的处理路径大致是从代码中截取该标识符所在作用域的上下文默认截取 500 个字符把这段代码 这个标识符发给 LLM要求返回一个 JSON 格式的建议名字收到建议后做合法性检查——LLM 偶尔会返回foo bar、this.x甚至static这种不能直接用的名字工具会把它规范化成合法的标识符检查作用域冲突必要时加数字后缀在符号表里完成改名输出时自动保证所有引用同步更新如果 LLM 调用失败或者返回了没法用的名字工具会保留原名字继续跑而不是中断整个任务。这个宁可不动不可改错的策略保证了任何情况下输出都是可运行的有效 JavaScript。为什么AI 负责起名AST 负责动手最稳妥 ️很多人第一次听到用 AI 重写代码会本能地担心它会不会顺手改了我的逻辑HumanifyJS 的分工设计恰好把这种风险压到了最低。整条链路的核心代码在src/rename/目录下解析用的是 oxc高性能 JS 解析器重命名发生在抽象语法树AST层LLM 全程只做一件事——给出一个名字建议。它没有权限改动代码结构代码生成器会忠实地把 AST 重新打印出来。这个分工很像外科医生与助手的关系助手LLM只负责递上工具、给出建议下刀的是外科医生AST 重命名器每一步都精准、可控、可回退。具体到安全重命名有三个细节值得你注意它们共同构成了防呆网上下文窗口工具不会把整个文件都发给 LLM而是以每个标识符为圆心截取其所在作用域的一段代码默认 500 字符可用--context-size调整。这让 LLM 有足够的侦查范围理解语义又不至于因为文件太大而烧掉太多 token。名称规范化LLM 可能建议出user account name、static这类非法标识符。src/rename/safe_name.rs会把它们规范化为userAccountName、_static保证生成的名字永远是合法的 JavaScript 标识符。作用域感知的冲突处理两个兄弟函数里的同名局部变量互不干扰可以都叫index但同一作用域里撞名时会通过数字后缀区分——如果 LLM 建议了item2而该名字已被占用下一个会变成item3而不是尴尬的item22。另外两类名字它坚决不碰对象属性名和类的方法名/私有字段如#x。原因很简单——这些名字可能被外部代码以字符串形式访问改了就会破坏契约。一个文件的前后对比从 a(e,t) 到 splitString 把文章开头那个让你崩溃的函数交给它处理前的样子function a(e,t){var n[];var re.length;var i0;for(;ir;it){if(itr){n.push(e.substring(i,it))}else{n.push(e.substring(i,r))}}return n}处理之后function splitString(inputString, chunkSize) { var chunks []; var stringLength inputString.length; var startIndex 0; for (; startIndex stringLength; startIndex chunkSize) { if (startIndex chunkSize stringLength) { chunks.push(inputString.substring(startIndex, startIndex chunkSize)); } else { chunks.push(inputString.substring(startIndex, stringLength)); } } return chunks; }a变成了splitStringe变成了inputStringt变成了chunkSizen变成了chunks。你没有猜错这就是一个把字符串按指定长度切片的工具函数——但你不需要靠猜了名字已经把意图写在了脸上。注意看splitString这个函数名本身没有被改动。如果 LLM 判断一个名字已经足够有表达力它会原样返回不做无意义的折腾。这也从侧面印证了它改名的依据是语义而不是见一个改一个。再补一个进阶场景。如果你的目标是 webpack 打包产物直接处理效果会差很多——因为里面还裹着一层模块加载器。正确的姿势是先拆包、再改名Unix 管道在这里发挥得淋漓尽致npx webcrack bundle.min.js | humanify openai - -o bundle.jsnpx webcrack把模块结构还原成普通脚本通过标准输出直接喂给humanifyhumanify 读-标准输入把结果写到bundle.js。两个工具各司其职拼成一条完整的webpack 产物可读化流水线。关于费用、速度和精力的三笔账 用 LLM 反混淆不是做慈善你需要对成本有心理预期。最重要的一条每遇到一个标识符工具就会调用一次 LLM。一个中等规模的压缩文件大约有 500 个标识符也就是说一次完整处理会发起约 500 次请求。费用500 个标识符的文件用 OpenAI 的小模型大约花 $0.10$1.00用 Gemini 的免费额度通常够用用本地 Ollama 或 OpenRouter 的免费模型则基本零成本。速度取决于标识符数量和模型响应速度。本地模型最慢某些 CPU 环境下单次请求可能要等很久所以工具给本地模式设置了长达 1800 秒的请求超时而云端 API 的超时只有 60 秒。精力处理完别急着删原始文件。把原文件、新文件都纳入版本管理改完跑一遍测试套件确认行为一致再谈其他。想粗略估算一下 token 消耗工具给的公式很朴素大概等于文件字符数的两倍。用wc -c量一下你的文件就知道大概量级了echo $((2 * $(wc -c yourscript.min.js)))对预算敏感的开发者推荐从 Gemini 免费层或本地 Ollama 起步跑通流程后再决定要不要上更强的付费模型。三个高频坑位与排雷手册 实战中最容易踩的坑我帮你提前排掉三个坑位一模型没配好跑起来全是原样返回症状日志里每个标识符的-前后名字一模一样。 排查先--verbose看配置是否解析成功模型名、API Key、base URL再确认网络能连通。LLM 调用失败时工具会静默保留原名所以名字没变往往是上游出问题的信号而不是模型觉得名字已经够好。坑位二webpack 产物直接处理效果一塌糊涂症状改完的代码里还残留大量__webpack_require__、__d(function(...){...})之类的结构局部变量改名了但整体依然读不懂。 排查记住 HumanifyJS 只重命名标识符。先过一遍 webcrack 把模块结构解开再交给 humanify 改名两者通过管道串联。坑位三处理超大文件时把预算烧穿症状跑完一看账单费用远超预期。 排查先用wc -c估算 token 量再用--context-size把上下文窗口调小比如 256牺牲一点命名准确度换成本可控。也可以先用 webcrack 拆包让每个文件变小后再分别处理。再提醒一个容易误判的点你以为反混淆会改逻辑不会。前面说过所有改动都发生在 AST 层的标识符重命名程序的行为保持不变。这正是它可以放心接入 CI/CD 流程的原因——跑完对比测试不红就能合入。下一步把它变成你的日常工具 你不需要等到半夜加班才想起它。几个可以立刻上手的用法拿到陌生库的压缩版源码先跑一遍再阅读排查生产环境的报错堆栈把bundle.min.js还原成可读版本对照行号配合 webcrack把历史遗留的 webpack 老产物整理成可维护的源码雏形最后把命令备忘收藏一下这一套流程你大概率会反复用到# 免费方案Gemini 免费层 export GEMINI_API_KEY你的密钥 humanify gemini app.min.js -o app.js # 本地私有方案Ollama humanify ollama app.min.js -o app.js # 预算充足追求最佳效果OpenAI humanify openai app.min.js -o app.jsHumanifyJS 是 MIT 许可的开源项目代码完全开放可审查。想深入了解或参与改进可以 clone 它的仓库https://gitcode.com/gh_mirrors/hu/humanify看看src/rename/下的实现那里藏着它所有防呆设计的细节。现在去把那个让你头疼的bundle.min.js拖进来跑一遍吧。十分钟之后你会回来感谢自己——那些a、e、t背后藏着的逻辑终于不用靠猜了。【免费下载链接】humanifyDeobfuscate Javascript code using ChatGPT项目地址: https://gitcode.com/gh_mirrors/hu/humanify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考