简介:面向前端安全分析与JavaScript逆向场景的jsjiami.com.v7代码解密工具,附带详细教程。工具基于AST与Babel插件实现,针对jsjiami v7常见混淆提供了字面量还原、死代码清理、扁平化还原、条件循环语句规范化、特殊函数清理等处理能力,对全局加密内容借助VM2环境执行。压缩包共21个文件,以12个JavaScript源码文件为主,辅以JSON配置、YAML、License、说明文档等,整体仅65KB,轻量且便于快速部署。已有1685人学习下载。资源包含程序入口、插件目录、项目配置及配套教程,并覆盖高频局部混淆、jjencode、sojson及v7变体、obfuscator等常见类型;压缩包内附详细教程,从环境配置到插件机制均有说明,方便二次开发,需要Node.js环境并安装依赖后即可使用,使用者可依据自身场景灵活选择参数,达到快速批量解密的目的,适合有一定JS基础、希望批量净化混淆代码的开发者。
1. 别被 jsjiami.com.v7 唬住:它只是一套 Babel 插件组合
接手的前端逆向项目里,只要看到jsjiami.com.v7的加密特征,比如_0x开头的变量海、乱序的函数定义、夹在中间的atob("..."),新人都容易直接卡死。这套加密表面上是一大坨压缩代码,本质就是基于 AST(抽象语法树)做的混淆变换——字面量展开、控制流扁平化、死代码注入、变量名替换,外加一个假的 VM 环境做反调试混淆。所以解密思路也固定:用 Babel 把混淆后的 AST 还原成正常结构,再按规则做清理。这个jsjiami.com.v7代码解密工具+详细教程.zip就是干这个的。
它依赖 Node.js 环境和 Babel 插件体系,入口在src/main.js,插件目录在src/plugin,支持common、jjencode、sojson、sojsonv7、obfuscator五种解密类型。实际拆过这个工具的工程师会告诉你:它不是一键还原的银弹,但能把 80% 的重复体力活压掉。适合三类人:被 v7 混淆代码困住的逆向工程师、要给批量脚本做每次还原的前端开发、以及想系统学 AST 插件写法的人。接下来按实操路径讲怎么用、参数怎么调、坑在哪。
2. 搭建环境:Node.js 版本与依赖安装的取舍
2.1 确认 Node.js 环境与项目依赖
工具是基于 Babel 写的,Babel 对 Node.js 版本有要求,建议直接用 14 以上的稳定版,我用 16 和 18 跑都没问题。先把压缩包解压到本地,进入根目录后执行npm i安装依赖,依赖写在了package.json里,核心是@babel/core、@babel/parser、@babel/traverse、@babel/types和vm2。其中vm2用于处理全局加密内容时提供隔离环境,注意它只在处理特殊加密段时才真正被执行。
cd jsjiami.com.v7代码解密工具+详细教程 npm i执行完看node_modules是否生成,如果出现权限报错,把目录属主改掉再装。装完后检查package.json的scripts字段,里面预定义了几条命令,后面调工具直接用npm run xxx就行,不需要手动写完整参数。
2.2 先跑一次默认解密验证环境
官方流程走通最关键,先准备一个用 v7 混淆过的input.js,把它放进项目根目录,然后执行预定义命令。默认命令是npm run decode -- -t common -i input.js -o output.js,但package.json里其实帮你写好了别名,你可以直接跑:
npm run decode执行完会在根目录生成output.js。这一步如果顺利,说明依赖没问题。常见问题是@babel/parser对某些 ECMAScript 新语法解析报错,比如 optional chaining,这时候要确认本地 Babel 版本是不是被其他全局包覆盖了,建议在项目内用npx babel --version看实际版本,避免混用全局 Babel。
提示:默认输入输出文件是
input.js和output.js,这两个文件不能包含非混淆代码,注释除外。
3. 解密类型选型:那五种 type 分别对应什么场景
3.1 common:高频局部混淆的默认选项
common是使用频率最高的类型。它负责字面量还原(全局和代码块两种粒度)、死代码清理、扁平化还原、条件与循环语句规范化、特殊函数清理。怎么判断该不该用common?看混淆代码里有没有大量_0x命名、函数内变量被拆得零碎、还夹带了一些根本不会执行的分支。跑完后产物会明显从「压缩 + 混淆」变成「逻辑可读的正常代码」。
npm run decode -- -t common -i input.js -o output.js-t后面是类型,-i和-o分别指定输入输出路径。整个流程跑完如果output.js里_0x变量数量明显减少,说明还原生效。需要注意:common能处理的是「局部混淆」,它不会去解析全局那段用 VM2 包裹的初始化代码,所以遇到window、globalThis级别的加密段,需要先做环境补齐。
3.2 jjencode 与 sojson:老版本 sonjson 混淆的兼容处理
jjencode是 sojson.com 早期版本用的混淆方式,特征非常明显:代码以void function开头,内部用大量$_0x命名,并且夹杂了很多无实意的字符串拼接。sojson则对应 sojson 的新版混淆。
我拆过一批 2020 年以前的站,基本都是jjencode老形式:前半段堆符号定义,后半段才暴露真正的代码逻辑。这两种类型处理时,工具内部走的是不同插件链,所以不要拿common去硬解jjencode,会解出一堆残废变量。
// jjencode 典型特征:变量名带 $_ 前缀、函数体被拆成嵌套调用 var $_0xabc = function(p, a, c, k, e, d) { // 解密壳 };如果你-t jjencode解出来的结果还是扭曲的,优先检查输入文件是否混入了非混淆代码块。这个工具的插件设计是「一次只能识别一个主加密函数」,多段混淆同时存在时,主函数会找错,后面全部白跑。
3.3 sojsonv7 与 obfuscator:针对性更强的还原链路
sojsonv7就是专门对应jsjiami.com.v7这套加密形式的还原插件。它的处理链路比common更激进,针对 v7 的全局加密内容、特殊函数清理做了对应处理。obfuscator用来应对 JavaScript Obfuscator 混淆的代码,那个工具的混淆特征是数组位移、字符串加密、控制流平坦化三件套。
选择建议就在这张表里:
| 混淆类型 | 识别特征 | 对应 type | 适用场景 |
|---|---|---|---|
| 高频局部混淆 | _0x变量、死代码、扁平化 | common | 大多数 v7 项目 |
| 老版 sojson | void function开头、$_0x命名 | jjencode | 2020 年前的站点 |
| sojson 新版 | 结构压缩、数组位移 | sojson | sojson 官方新版 |
| jsjiami v7 | 数组移位 + 全局加密 + 函数分拆 | sojsonv7 | 项目主场景 |
| Obfuscator | 字符串数组 + 控制流平坦化 | obfuscator | JavaScript Obfuscator 产物 |
提示:这五种类型没有重叠兼容。判断错了类型,轻则解释度低,重则直接报错中断。
3.4 主程序入口与插件加载机制
工具的主入口是src/main.js,它负责读输入文件、做词法分析、按-t参数加载对应插件、执行 AST 变换、生成输出。插件目录是src/plugin,每一种 type 对应一个子目录或子文件。如果你想扩展自定义类型,就在src/plugin下新建一个目录,按照现有插件的写法导出 Babel 插件对象。
// src/main.js 的调用核心逻辑示意 const { transformFromAstSync } = require("@babel/core"); const parser = require("@babel/parser"); const fs = require("fs"); const source = fs.readFileSync(config.input, "utf-8"); const ast = parser.parse(source); const pluginPath = `./plugin/${config.type}`; const plugin = require(pluginPath); const result = transformFromAstSync(ast, source, { plugins: [plugin.default || plugin], });parser.parse用的是 Babel 的 parser,只负责把代码变成 AST,真正的还原逻辑都在插件里。transformFromAstSync会执行插件中的 visitor 规则,遇到匹配的节点类型就做对应处理。这一步是整个工具的骨架,理解了它的调用链,后面调试报错就知道该往哪儿看。
4. 自定义封装:把自己的逻辑写进插件链
4.1 继承现有插件扩展专属规则
如果你拿到的混淆不是五种类型里的任何一种,别慌,把现有插件复制一份,改 visitor 逻辑就行。src/plugin下每个插件导出的结构是一致的:一个 Babel plugin 对象,包含 visitor 规则。比如你发现混淆代码里自定义了console.log的代理函数,还原时需要把代理调用替换成真实调用,就可以在 visitor 里加一条CallExpression处理。
// src/plugin/common/index.js 里可追加的 visitor 片段 module.exports = function () { return { visitor: { CallExpression(path) { const callee = path.node.callee; if (callee.type === "Identifier" && callee.name === "_0xlog") { path.node.callee.name = "console.log"; } }, }, }; };逻辑解释:CallExpression是函数调用节点,path.node.callee是函数名部分。当函数名是_0xlog时,直接改名为console.log。这个操作不需要改参数,因为你的目标只是把代理层去掉。完整写好后,在src/main.js的 type 分支里注册自己的插件名,就能通过-t 你的类型名调用。
4.2 处理全局加密内容的 VM2 环境配置
摘要里专门提到一个点:处理全局加密内容时使用 VM2 提供的环境。也就是说,v7 混淆里有一部分代码不是通过 AST 能直接还原的,它需要在一个 fake 的 global 环境里跑一遍,把运行时算出来的值回填到output.js里。这个环节最容易报错,因为 VM2 里跑的加密代码如果调用了process、Buffer这类 Node.js API,会直接被拦截。
// 常见做法:在 main.js 里对全局加密段做 VM2 执行 const { NodeVM } = require("vm2"); const vm = new NodeVM({ console: "inherit", sandbox: {}, require: { external: false, builtin: ["*"], }, }); const result = vm.run(encryptedGlobalCode);NodeVM的require配置控制加密代码能访问哪些内置模块。builtin: ["*"]放开了所有内置模块,这个配置只在确实需要时才用,否则把fs、child_process这类危险模块关掉更安全。如果你解密时遇到ReferenceError: process is not defined,就是因为全局加密段在 VM 环境里用了process,可 VM2 的沙箱默认没有这个对象。解决办法是显式注入:
sandbox: { process: { platform: "linux", env: {} }, Buffer, }核心思路就是:把混淆代码依赖的宿主对象按需注入,让它以为自己在真实浏览器或 Node 环境里运行。每注入一个对象,就离还原成功更近一步。逆向工具的成就感就在这里——你不是在跑脚本,是在给混淆代码做「心理侧写」。
5. 避坑手册:jsjiami v7 解密最常见的六个翻车点
5.1 输入文件混入多段混淆导致主函数识别错乱
现象:解密出来的代码只还原了一部分,剩下的还是_0x变量,或者直接报Expected token语法错误。
原因:工具一次只能识别一个主加密函数。你把多段混淆代码放在同一个文件里,或者把一段非混淆业务代码和混淆代码混在一起了,src/main.js找主加密函数时匹配到了错误的入口。
解决:把输入文件清理干净,只保留待还原的那一段混淆代码。可以在input.js开头加注释说明来源,注释不影响解析。如果一段混淆实在拆不开,就用正则先切分再分段解密。
# 用 sed 把文件内非目标段裁剪掉,再喂给工具 sed -n '/function _0x/,/^}/p' input.js > clean_input.js npm run decode -- -t sojsonv7 -i clean_input.js -o output.js5.2 Node.js 版本太低导致 Babel 解析新语法失败
现象:报错Unexpected token '?',或者@babel/parser抛SyntaxError。
原因:混淆代码里包含可选链?.、空值合并??这类新语法,而本机 Node.js 版本对应的 Babel parser 默认没启用这些插件。
解决:升级 Node.js 到 16.0 以上,或者手动传入 parser plugins:
// 修改 src/main.js,给 parser.parse 传第二个参数 const ast = parser.parse(source, { sourceType: "module", plugins: ["optionalChaining", "nullishCoalescingOperator"], });5.3 全局加密段使用了process和Buffer,VM2 直接报错
现象:ReferenceError: process is not defined,或者Buffer is not defined。
原因:VM2 沙箱默认隔离了宿主环境,但 jsjiami v7 的全局加密代码里有一些环境探测逻辑,检测不到就中断执行。
解决:按 4.2 节的方式在sandbox里注入process和Buffer。注意注入的process属性别给child_process相关能力,只给platform、env就够了。
5.4 解密后代码逻辑颠倒,条件分支顺序错乱
现象:output.js能跑,但业务逻辑不对,if/else分支看起来反向。
原因:控制流扁平化还原时,分发器的状态管理被部分还原,但依赖了运行时才能确定的谓词没有恢复。
解决:检查src/plugin/sojsonv7里扁平化还原插件是否对CatchClause做了处理。常见做法是把扁平化还原拆两步走:先静态重建switch分发器,再做死代码清理,如果一次的插件处理不干净,跑两次:
npm run decode -- -t sojsonv7 -i output.js -o output2.js这个骚操作在解密嵌套混淆时相当管用,第一轮解除大框架,第二轮清理残留的局部混淆,相当于给代码吃了两次药。
5.5 解密后代码报require is not defined
现象:输出代码里有require(...)调用,但你是在浏览器环境里跑的。
原因:原混淆代码是 CommonJS 模块体系,解密还原后保留了require调用,可目标运行环境不支持。或者 VM2 执行全局加密段时已经把公共依赖提取成了require形式。
解决:包装一层模块系统:
// 在解密产物外层做兼容,或用全局函数包裹 (function (module, exports, require) { // 还原后的代码 })(module, exports, require);5.6 自执行函数体过长导致栈溢出
现象:解出来的代码执行时爆Maximum call stack size exceeded。
原因:v7 混淆里有一段深度递归的初始化函数,本质是 antidebug 机制,VM2 执行时递归层级太深。
解决:不要直接在 VM2 里跑,先在 AST 层把自执行的递归调用替换成循环。手写一个循环展开插件,识别该函数名后return掉递归分支,只保留单层逻辑。
6. 进阶用法:给自己打造一个批处理还原管道
走到这一步,说明单个文件的解密流程你已经跑通了。但如果手上有几十个 v7 混淆文件,一个一个跑命令会被逼疯。我一般会写一个批处理脚本,把input/目录下所有文件循环解密,再把输出统一放到output/:
const fs = require("fs"); const path = require("path"); const { execSync } = require("child_process"); const inputDir = path.join(process.cwd(), "input"); const outputDir = path.join(process.cwd(), "output"); if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir); const files = fs.readdirSync(inputDir).filter((f) => f.endsWith(".js")); for (const file of files) { const src = path.join(inputDir, file); const dest = path.join(outputDir, file.replace(/\.js$/, ".dec.js")); try { execSync(`npm run decode -- -t sojsonv7 -i "${src}" -o "${dest}"`, { stdio: "inherit", }); console.log(`[OK] ${file}`); } catch (e) { console.error(`[FAIL] ${file}: ${e.message}`); } }这个批处理脚本每一次循环都重新拉起一条子进程执行 npm 脚本,好处是单个文件崩了我能拿到独立的错误信息,而且不会因为内存泄漏把整个还原任务拖垮。更高效的做法是用worker_threads并行跑,但我实测过,Babel 插件处理属于 CPU 密集任务,并行粒度太细反而上下文切换开销大,所以单线程顺序跑几十个文件反而更快、更稳。
如果你要验证还原是否正确,观察点就是三个:输出的代码里_0x符号覆盖率下降了多少、实际运行逻辑是否和混淆前的行为一致、以及把输出压缩后体积和原混淆体积的对比。这个第三点容易被忽略,其实它最能反映解密彻底程度——如果输出比原混淆还大 40% 以上,说明死代码清理没达标,需要回头看common里的变量折叠节点是否触发。
除此之外,把解密工具链融进自己的日常抓包和研究流程里,你会收获额外的效率提升。比如用 Chrome DevTools 的局部替换功能,把加密的线上脚本直接替换成解密的本地文件,这样在浏览器里调试业务逻辑时,断点能落在可读代码上——这比对着_0x变量名猜逻辑舒服太多了。从那以后,我每次跑完 v7 解密都会强制走一遍「还原 → 生成 diff → 动态执行对照」的验证流程,确认输出代码的行为和线上原版在关键函数上结果一致,才敢把它作为后续逆向分析的地基。这个习惯帮我不止一次避开了「还原成功但行为不一致」的暗坑,希望帮到你。
本文还有配套的精品资源,点击获取