AST解析JS混淆代码可行但有局限:仅适用于字符串拼接型(如atob、fromCharCode),遇eval、Function等动态执行需补环境;Webpack代码应先正则定位再局部AST;Python复现需处理JS隐式类型转换。

直接用 AST 解析 JS 混淆代码是可行的,但必须明确一点:AST 不是万能解药,它只负责“看懂结构”,不负责“执行逻辑”。很多加密函数依赖 window、document、atob 或动态环境检测,纯 AST 解析拿不到最终字符串——你得先判断这段 JS 是“可静态还原”还是“必须补环境执行”。
怎么快速判断该不该上 AST?
打开混淆 JS 文件,在浏览器控制台里搜这几个关键词:
-
atob(、btoa(、String.fromCharCode(、.join(→ 这类属于“字符串拼接型”,AST 可安全提取 -
eval(、Function(、setTimeout(、location.href→ 这类含动态执行或 DOM 依赖,AST 无法还原,得换方案 -
void 0、!1、~[]→ 这些是无副作用干扰项,AST 遍历时可跳过,不用硬匹配
如果搜索结果集中在第一类,就适合 AST;第二类占比高,别浪费时间写遍历器,直接切到 PyExecJS 或 RPC。
用 esprima-python 提取 base64 和 fromCharCode 字符串
注意:不要用已停更的 pyjsparser,它对 ES6+ 支持差,遇到箭头函数或模板字符串会直接抛 ParseError: Unexpected token。推荐 esprima-python(绑定 Esprima 4.x)或轻量级 slimit。
立即学习“Python免费学习笔记(深入)”;
核心逻辑是递归遍历 CallExpression 节点,识别调用名和参数类型:
def extract_strings(node):
if isinstance(node, esprima.nodes.CallExpression):
if hasattr(node.callee, 'name') and node.callee.name == 'atob':
if isinstance(node.arguments[0], esprima.nodes.Literal) and isinstance(node.arguments[0].value, str):
return base64.b64decode(node.arguments[0].value).decode('utf-8')
elif (hasattr(node.callee, 'property') and
hasattr(node.callee.property, 'name') and
node.callee.property.name == 'fromCharCode'):
nums = []
for arg in node.arguments:
if isinstance(arg, esprima.nodes.Literal) and isinstance(arg.value, int):
nums.append(arg.value)
if nums:
return ''.join(chr(n) for n in nums)
return None
这个函数只处理最常见两种模式,不试图覆盖所有混淆变体——实际项目中,90% 的字符串混淆都落在这两类里。
调用 Cutout.Pro 视觉处理 API 进行背景移除、人像抠图和照片增强,支持文件上传与图片 URL 输入。
webpack 打包代码怎么拆?别硬 parse 整个 bundle
混淆 JS 来自 webpack 打包时,整个文件可能有 10k+ 行,但真正生成 sign 的逻辑往往藏在某个闭包里,比如:function r(e){...} 或 var t={...}。硬解析整棵树效率低还容易内存溢出。
更实用的做法是先定位目标函数再局部 AST:
- 用正则粗筛:搜索
/function\s+\w+\s*\([^)]*\)\s*\{[^}]*sign[^}]*\}/或/\.sign\s*=\s*[\w$]+/ - 把匹配到的代码块(比如从
function getSign到对应右括号)单独拎出来,喂给esprima.parseScript() - 这样 AST 树深度可控,遍历快,且避免被打包器注入的无关模块干扰
别忘了 webpack 常把字符串数组存成 ["a","b","c"].join("") 形式,这种要额外检查 ArrayExpression + CallExpression.callee.property.name === "join" 节点。
AST 还原后 Python 复现失败?大概率漏了隐式类型转换
JS 里 "1" + 2 是字符串拼接,Python 里会报错;!![] 在 JS 是 True,但 AST 解析时可能被当成布尔字面量或忽略。这类细节不会出现在 AST 节点里,但直接影响加密结果。
最容易被忽略的是:
-
+运算符的多义性:数值相加 vs 字符串拼接 → Python 复现时得手动判断操作数类型 -
==和===的隐式转换差异 → JS 中"0" == false为真,Python 里不能直接用==模拟 - 数字字面量的进制:JS 支持
0x1f(十六进制)、0o755(八进制),AST 中Literal.value是解析后的十进制数,但原始写法可能影响调试理解
所以 AST 还原出来的“伪代码”,只是逻辑骨架;填上类型判断和 JS 特有行为,才是能跑通的 Python 实现。

















