如何通过静态分析 AST 手动实现一个简单的 JavaScript 关键词混淆工具

云磊同学_8126

云磊同学_8126

2026-04-15

1043人浏览

原创

直接替换变量名会出错,因正则无法区分作用域和语义,而ast能精准识别identifier节点并仅混淆局部变量和参数,跳过属性名、内置方法、全局变量等。

如何通过静态分析 ast 手动实现一个简单的 javascript 关键词混淆工具

为什么直接替换变量名会出错

手动遍历字符串做正则替换,var a = 1; console.log(a); 中的 a 会被替换成新名字,但 console.log 里的 log 也可能被误伤——因为正则不区分作用域和语义。AST 能精准识别 Identifier 节点,并判断它是否为可混淆的局部变量或函数参数,跳过属性名、内置方法、全局变量等。

用 @babel/parser 解析并遍历 AST 的关键步骤

不用写词法分析器,Babel 提供了稳定、兼容性好的解析能力。重点不是“怎么生成 AST”,而是“怎么安全标记和替换”:

  • 只处理 VariableDeclarator(如 let x = 1 中的 x)和 FunctionDeclaration/ArrowFunctionExpression 的参数节点
  • 跳过 MemberExpression 中的 property(如 obj.name 的 name),否则会破坏对象访问
  • 用 scope.bindings 判断标识符是否为局部绑定(Babel 的 scope 对象在遍历时自动可用)
  • 避免混淆 this、arguments、super 等上下文关键字,它们不是普通 Identifier

示例节选(Babel 插件写法):

OpenJDK 24
OpenJDK 24

OpenJDK 24 Windows x64 官方参考实现(24+36),适合 Windows 环境进行 Java 开发、编译、运行和版本兼容性验证。

下载
export default function({ types: t }) {
  return {
    visitor: {
      Identifier(path) {
        const { node, scope } = path;
        if (node.name === 'this' || node.name === 'arguments') return;
        if (!scope.hasBinding(node.name)) return; // 不是当前作用域声明的,不碰
        if (scope.getBinding(node.name).kind !== 'let' && 
            scope.getBinding(node.name).kind !== 'const' && 
            scope.getBinding(node.name).kind !== 'var') return;
        // 此处生成新名字,如 'a' → '_0x1a2b'
        path.replaceWith(t.identifier('_0x1a2b'));
      }
    }
  };
}

scope.hasBinding() 和 scope.getBinding() 的实际差异

前者只是快速判断名字是否存在绑定,后者返回完整绑定对象,含 kind(var/let/const/param)、referencePaths(所有引用位置)、path(声明节点)。混淆时必须用 getBinding(),否则无法区分 let x 和 obj.x ——两者都通过 hasBinding('x') 返回 false,但后者根本不在作用域中。

  • scope.hasBinding('x') 在 function f(x) { let x = 2; } 中对第一个 x(参数)返回 true,对第二个也返回 true,但它们属于不同绑定
  • 真正要替换的是绑定对象本身,所以必须用 scope.getBinding('x') 获取其声明路径,再决定是否重命名
  • 注意:参数绑定的 kind 是 'param',不是 'let',漏判会导致函数参数没被混淆

混淆后代码执行失败的三个高频原因

不是 AST 没走通,而是语义被破坏:

  • 把 class A { method() {} } 中的 method 当作可混淆标识符——其实它是 MethodDefinition 的 key,不是 Identifier 节点;需额外处理 ObjectMethod 和 ClassMethod 的 key 属性,且仅当 key.type === 'Identifier' 且非计算属性时才考虑
  • 混淆了 import { foo } from './x' 中的 foo,导致模块导入失效——ESM 的导入绑定是只读的,不能重命名;应跳过所有 ImportSpecifier 和 ExportSpecifier 中的 local 和 exported
  • 用固定映射表(如 {x: '_a', y: '_b'})混淆,但没做作用域隔离,导致嵌套函数内同名变量被统一替换,破坏闭包逻辑;必须为每个 Scope 实例维护独立的映射缓存

最易被忽略的是 this 绑定上下文:箭头函数里写的 this 不是标识符,但若混淆了外层函数名,可能让调试时的调用栈完全不可读——混淆工具不该动函数名,除非明确支持 functionName 选项并默认关闭。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

4866

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

1209

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

4784

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

940

4

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

1876

5

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

2023.09.04

3445

4

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

2023.09.12

4473

6

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

2023.09.20

2940

5

java访问控制修饰符介绍
java访问控制修饰符介绍

java访问控制修饰符有四种,分别是public、protected、private、默认访问修饰符。详细介绍:1、public,public是最宽松的访问控制修饰符,被修饰的类、方法和变量可以被任何其他类访问,当一个类、方法或变量被声明为public时,它们可以在任何地方被访问,无论是同一个包中的类还是不同包中的类;2、protected修饰符等等。

2023.09.20

928

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

React 教程
React 教程

共58课时 | 12.2万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.6万人学习