一个完整的AST(抽象语法树)解混淆学习路线,适合零基础学习者从入门到精通。每个知识点都细分为小知识点,配有详细的学习计划。
📚 学习路线概览
本学习计划分为6个阶段,每个阶段都有明确的学习目标、实践项目和进阶路径。预计完整学习周期为3-6个月。
第一阶段:JavaScript基础巩固(2-3周)
1.1 变量与数据类型(3-4天)
1.1.1 变量声明方式
学习内容:
- var的特性:函数作用域、变量提升、可重复声明
- let的特性:块级作用域、暂时性死区、不可重复声明
- const的特性:块级作用域、声明时必须初始化、引用不可变
实践任务:
- [ ] 编写代码验证var的变量提升行为
- [ ] 测试let的暂时性死区(TDZ)
- [ ] 理解const对象属性可修改但引用不可变
- [ ] 对比三种声明方式在循环中的表现
代码示例:
// var变量提升
console.log(a); // undefined
var a = 1;
// let暂时性死区
console.log(b); // ReferenceError
let b = 2;
// const引用不可变
const obj = {name: 'test'};
obj.name = 'changed'; // 可以
obj = {}; // 报错学习时间:1天
检验标准:能解释三种声明方式的区别,能在实际代码中正确选择
1.1.2 基本数据类型
学习内容:
- 7种基本类型:Number、String、Boolean、Undefined、Null、Symbol、BigInt
- 类型检测:typeof运算符的使用和陷阱
- 类型转换:隐式转换和显式转换规则
实践任务:
- [ ] 测试typeof对所有类型的返回值
- [ ] 理解typeof null返回"object"的历史原因
- [ ] 掌握String()、Number()、Boolean()转换规则
- [ ] 理解== vs ===的区别
代码示例:
// 类型检测
typeof 123; // "number"
typeof "hello"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" (历史遗留问题)
typeof Symbol(); // "symbol"
typeof 123n; // "bigint"
// 类型转换
Number("123"); // 123
Number("abc"); // NaN
Boolean(0); // false
Boolean(""); // false
String(123); // "123"学习时间:1天
检验标准:能准确判断数据类型,理解类型转换规则
1.1.3 引用数据类型
学习内容:
- Object对象:键值对、属性访问、属性操作
- Array数组:数组创建、索引访问、长度属性
- Function函数:函数是一等公民、函数对象属性
- 引用传递vs值传递:内存模型理解
实践任务:
- [ ] 创建对象并使用.和[]两种方式访问属性
- [ ] 理解数组的length属性可读可写
- [ ] 验证函数也是对象(可以添加属性)
- [ ] 通过代码理解引用传递的本质
代码示例:
// 对象属性访问
const person = {name: 'Alice', age: 25};
person.name; // "Alice"
person['age']; // 25
// 引用传递示例
let obj1 = {value: 10};
let obj2 = obj1;
obj2.value = 20;
console.log(obj1.value); // 20(共享同一引用)学习时间:1天
检验标准:能区分值类型和引用类型,理解内存模型
1.2 函数(4-5天)
1.2.1 函数定义方式
学习内容:
- 函数声明(Function Declaration):会被提升
- 函数表达式(Function Expression):不会被提升
- 箭头函数(Arrow Function):简洁语法、this绑定
- 函数构造器(new Function):动态创建函数
实践任务:
- [ ] 验证函数声明的提升行为
- [ ] 对比函数表达式与函数声明的执行时机
- [ ] 编写箭头函数的各种形式(单参数、多参数、单行、多行)
- [ ] 理解箭头函数不能作为构造函数
代码示例:
// 函数声明(会提升)
sayHello(); // 可以调用
function sayHello() {
console.log('Hello');
}
// 函数表达式(不会提升)
sayHi(); // 报错
const sayHi = function() {
console.log('Hi');
};
// 箭头函数
const add = (a, b) => a + b;
const square = x => x * x;
const greet = () => console.log('Hello');学习时间:1天
检验标准:能使用多种方式定义函数,理解各自特点
1.2.2 函数参数
学习内容:
- 形参与实参:参数个数不匹配的处理
- 默认参数:ES6默认参数语法
- 剩余参数(Rest Parameters):...args语法
- arguments对象:类数组对象、箭头函数中不可用
实践任务:
- [ ] 测试参数过多或过少时的行为
- [ ] 使用默认参数简化代码
- [ ] 用剩余参数收集不定数量的参数
- [ ] 理解arguments对象与数组的区别
代码示例:
// 默认参数
function greet(name = 'Guest') {
console.log(`Hello, ${name}`);
}
// 剩余参数
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4); // 10
// arguments对象
function showArgs() {
console.log(arguments); // 类数组对象
console.log(Array.from(arguments)); // 转为真数组
}学习时间:1天
检验标准:能灵活使用各种参数形式,理解arguments对象
1.2.3 函数返回值
学习内容:
- return语句:返回值、终止函数执行
- 无return的函数:默认返回undefined
- 返回多个值:通过对象或数组返回
- 链式调用:返回this实现
实践任务:
- [ ] 编写有返回值和无返回值的函数
- [ ] 通过解构接收多个返回值
- [ ] 实现简单的链式调用
代码示例:
// 返回多个值
function getUser() {
return {name: 'Alice', age: 25};
}
const {name, age} = getUser();
// 链式调用
class Calculator {
constructor(value = 0) {
this.value = value;
}
add(n) {
this.value += n;
return this; // 返回this实现链式调用
}
multiply(n) {
this.value *= n;
return this;
}
}
new Calculator(5).add(3).multiply(2); // 16学习时间:0.5天
检验标准:理解返回值机制,能实现链式调用
1.2.4 闭包
学习内容:
- 闭包定义:函数能够访问其词法作用域外的变量
- 闭包原理:作用域链、垃圾回收机制
- 闭包应用:数据私有化、函数工厂、模块化
- 闭包陷阱:内存泄漏、循环中的闭包
实践任务:
- [ ] 创建一个计数器闭包
- [ ] 实现私有变量(模拟类的私有属性)
- [ ] 理解经典的循环闭包问题
- [ ] 使用闭包创建函数工厂
代码示例:
// 计数器闭包
function createCounter() {
let count = 0; // 私有变量
return {
increment: () => ++count,
decrement: () => --count,
getCount: () => count
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
// 循环闭包问题
// 错误示例
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出3次3
}
// 正确示例
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 输出0,1,2
}学习时间:1.5天
检验标准:能解释闭包原理,能用闭包解决实际问题
1.3 对象与数组(3-4天)
1.3.1 对象创建与操作
学习内容:
- 对象字面量:最常用的创建方式
- Object.create():原型继承
- 构造函数:new关键字
- 属性描述符:可枚举、可配置、可写
实践任务:
- [ ] 用多种方式创建对象
- [ ] 使用Object.defineProperty定义属性
- [ ] 理解属性的特性(enumerable、writable、configurable)
- [ ] 使用Object.keys/values/entries遍历对象
代码示例:
// 对象字面量
const person = {
name: 'Alice',
age: 25,
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};
// Object.create
const child = Object.create(person);
child.name = 'Bob';
// 属性描述符
Object.defineProperty(person, 'id', {
value: 1,
writable: false, // 不可修改
enumerable: false, // 不可枚举
configurable: false // 不可删除
});学习时间:1天
检验标准:能创建和操作对象,理解属性特性
1.3.2 对象方法
学习内容:
- Object.assign():对象合并、浅拷贝
- Object.freeze/seal():对象冻结与封闭
- hasOwnProperty():检测自有属性
- in运算符:检测属性(包括原型链)
实践任务:
- [ ] 使用Object.assign合并多个对象
- [ ] 理解浅拷贝与深拷贝的区别
- [ ] 测试freeze和seal的区别
- [ ] 用hasOwnProperty过滤原型链属性
代码示例:
// Object.assign(浅拷贝)
const obj1 = {a: 1, b: {c: 2}};
const obj2 = Object.assign({}, obj1);
obj2.b.c = 3;
console.log(obj1.b.c); // 3(浅拷贝问题)
// freeze vs seal
const frozen = Object.freeze({a: 1});
frozen.a = 2; // 无效
frozen.b = 3; // 无效
const sealed = Object.seal({a: 1});
sealed.a = 2; // 有效
sealed.b = 3; // 无效学习时间:1天
检验标准:能使用对象常用方法,理解浅拷贝深拷贝
1.3.3 数组基础操作
学习内容:
- 数组创建:字面量、Array构造函数、Array.of/from
- 数组索引:正向索引、负向索引(ES2022)
- 数组长度:length属性的读写
- 数组检测:Array.isArray()
实践任务:
- [ ] 用多种方式创建数组
- [ ] 通过修改length截断数组
- [ ] 使用Array.from转换类数组对象
- [ ] 测试数组的稀疏性(空槽)
代码示例:
// 数组创建
const arr1 = [1, 2, 3];
const arr2 = new Array(3); // [empty × 3]
const arr3 = Array.of(1, 2, 3); // [1, 2, 3]
const arr4 = Array.from('hello'); // ['h','e','l','l','o']
// 修改length
const arr = [1, 2, 3, 4, 5];
arr.length = 3;
console.log(arr); // [1, 2, 3]学习时间:0.5天
检验标准:能创建和操作数组,理解数组特性
1.3.4 数组常用方法(重点)
学习内容:
- 添加/删除:push、pop、unshift、shift、splice
- 查找:indexOf、includes、find、findIndex
- 遍历:forEach、map、filter、reduce
- 其他:slice、concat、join、reverse、sort
实践任务:
- [ ] 掌握所有增删改查方法
- [ ] 理解会修改原数组和不修改原数组的方法
- [ ] 用map转换数组元素
- [ ] 用filter筛选数组
- [ ] 用reduce实现复杂计算
代码示例:
// map转换
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(x => x * 2); // [2, 4, 6, 8]
// filter筛选
const evens = numbers.filter(x => x % 2 === 0); // [2, 4]
// reduce累加
const sum = numbers.reduce((acc, cur) => acc + cur, 0); // 10
// 链式调用
const result = [1, 2, 3, 4, 5]
.filter(x => x > 2)
.map(x => x * 2)
.reduce((a, b) => a + b); // 24学习时间:1.5天
检验标准:能熟练使用数组方法解决实际问题
1.3.5 解构赋值
学习内容:
- 数组解构:基本用法、默认值、剩余元素
- 对象解构:基本用法、重命名、默认值
- 嵌套解构:多层结构的解构
- 函数参数解构:简化参数传递
实践任务:
- [ ] 交换变量值(不用临时变量)
- [ ] 从函数返回多个值并解构接收
- [ ] 解构嵌套对象
- [ ] 在函数参数中使用解构
代码示例:
// 数组解构
const [a, b, ...rest] = [1, 2, 3, 4, 5];
// a=1, b=2, rest=[3,4,5]
// 对象解构
const {name, age, city = 'Unknown'} = {name: 'Alice', age: 25};
// 重命名
const {name: userName} = {name: 'Bob'};
// 函数参数解构
function printUser({name, age}) {
console.log(`${name}, ${age}`);
}
printUser({name: 'Alice', age: 25});学习时间:0.5天
检验标准:能在各种场景中使用解构简化代码
1.4 控制流(2-3天)
1.4.1 条件语句
学习内容:
- if/else:基本条件判断
- 三元运算符:简洁的条件表达式
- switch/case:多分支选择
- 逻辑运算符:&&、||、?? 短路求值
实践任务:
- [ ] 使用if/else实现多条件判断
- [ ] 用三元运算符简化简单条件
- [ ] 理解switch的穿透特性(fallthrough)
- [ ] 使用??(空值合并运算符)处理默认值
代码示例:
// 三元运算符
const age = 18;
const status = age >= 18 ? 'adult' : 'minor';
// switch穿透
const day = 3;
switch(day) {
case 1:
case 2:
case 3:
case 4:
case 5:
console.log('工作日');
break;
case 6:
case 7:
console.log('周末');
break;
}
// 空值合并
const value = null ?? 'default'; // 'default'
const value2 = 0 ?? 'default'; // 0(0不是null/undefined)学习时间:1天
检验标准:能根据场景选择合适的条件语句
1.4.2 循环语句
学习内容:
- for循环:经典计数循环
- while/do-while:条件循环
- for...of:遍历可迭代对象(数组、字符串等)
- for...in:遍历对象属性(不推荐用于数组)
- break/continue:循环控制
实践任务:
- [ ] 用for循环遍历数组
- [ ] 用for...of遍历Set和Map
- [ ] 理解for...in的陷阱(会遍历原型链)
- [ ] 使用break和continue控制循环
代码示例:
// for...of(推荐用于数组)
const arr = [1, 2, 3];
for (const item of arr) {
console.log(item);
}
// for...in(用于对象)
const obj = {a: 1, b: 2};
for (const key in obj) {
console.log(key, obj[key]);
}
// break和continue
for (let i = 0; i < 10; i++) {
if (i === 3) continue; // 跳过3
if (i === 7) break; // 在7处停止
console.log(i);
}学习时间:1天
检验标准:能根据场景选择合适的循环方式
1.4.3 异常处理
学习内容:
- try/catch/finally:捕获和处理异常
- throw:抛出自定义异常
- Error对象:错误类型、错误信息、堆栈跟踪
- 自定义错误类:继承Error类
实践任务:
- [ ] 使用try/catch捕获异常
- [ ] 理解finally的执行时机(总是执行)
- [ ] 抛出自定义错误信息
- [ ] 创建自定义错误类
代码示例:
// 基本异常处理
try {
JSON.parse('invalid json');
} catch (error) {
console.error('解析失败:', error.message);
} finally {
console.log('清理工作');
}
// 自定义错误
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
function validateAge(age) {
if (age < 0) {
throw new ValidationError('年龄不能为负数');
}
}学习时间:0.5天
检验标准:能正确处理异常,编写健壮的代码
1.5 ES6+新特性(3-4天)
1.5.1 模板字符串
学习内容:
- 模板字面量:反引号语法
- 插值表达式:$
- 多行字符串:无需转义换行符
- 标签模板:自定义模板处理
实践任务:
- [ ] 使用模板字符串拼接字符串
- [ ] 在模板中嵌入表达式和函数调用
- [ ] 创建多行字符串
- [ ] 理解标签模板的用途
代码示例:
// 基本使用
const name = 'Alice';
const age = 25;
const message = `Hello, ${name}! You are ${age} years old.`;
// 多行字符串
const html = `
<div>
<h1>${name}</h1>
<p>Age: ${age}</p>
</div>
`;
// 标签模板
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
return result + str + (values[i] ? `<mark>${values[i]}</mark>` : '');
}, '');
}
const output = highlight`Name: ${name}, Age: ${age}`;学习时间:0.5天
检验标准:能使用模板字符串简化字符串操作
1.5.2 扩展运算符与剩余参数
学习内容:
- 扩展运算符(Spread):展开数组/对象
- 剩余参数(Rest):收集参数
- 数组复制:浅拷贝
- 对象合并:属性覆盖规则
实践任务:
- [ ] 用扩展运算符复制数组
- [ ] 用扩展运算符合并数组
- [ ] 用扩展运算符复制和合并对象
- [ ] 理解剩余参数与arguments的区别
代码示例:
// 数组展开
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
// 数组复制(浅拷贝)
const copy = [...arr1];
// 对象展开
const obj1 = {a: 1, b: 2};
const obj2 = {...obj1, c: 3}; // {a: 1, b: 2, c: 3}
// 函数参数
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4); // 10学习时间:0.5天
检验标准:能区分扩展运算符和剩余参数的使用场景
1.5.3 Promise基础
学习内容:
- Promise概念:异步编程解决方案
- Promise状态:pending、fulfilled、rejected
- then/catch/finally:链式调用
- Promise静态方法:all、race、allSettled、any
实践任务:
- [ ] 创建一个Promise并处理成功/失败
- [ ] 使用Promise链处理多个异步操作
- [ ] 用Promise.all并行执行多个异步任务
- [ ] 理解Promise的错误传播机制
代码示例:
// 创建Promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve('操作成功');
} else {
reject('操作失败');
}
}, 1000);
});
// 使用Promise
promise
.then(result => {
console.log(result);
return '下一步';
})
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log('完成'));
// Promise.all
Promise.all([
fetch('/api/user'),
fetch('/api/posts'),
fetch('/api/comments')
]).then(([user, posts, comments]) => {
console.log('全部完成');
});学习时间:1.5天
检验标准:能用Promise处理异步操作,理解Promise链
1.5.4 async/await
学习内容:
- async函数:返回Promise的函数
- await关键字:等待Promise解决
- 错误处理:try/catch处理async错误
- 并行vs串行:合理使用await
实践任务:
- [ ] 将Promise链改写为async/await
- [ ] 用try/catch处理async函数的错误
- [ ] 对比串行和并行的async操作性能
- [ ] 理解await只能在async函数中使用
代码示例:
// 基本使用
async function fetchUser() {
try {
const response = await fetch('/api/user');
const user = await response.json();
return user;
} catch (error) {
console.error('获取用户失败:', error);
}
}
// 并行执行
async function fetchAll() {
// 串行(慢)
const user = await fetch('/api/user');
const posts = await fetch('/api/posts');
// 并行(快)
const [user2, posts2] = await Promise.all([
fetch('/api/user'),
fetch('/api/posts')
]);
}学习时间:1天
检验标准:能用async/await简化异步代码
1.5.5 其他ES6+特性
学习内容:
- Class类语法:构造函数、方法、继承
- Symbol:唯一标识符
- Set和Map:新的数据结构
- 可选链(?.)和空值合并(??)
实践任务:
- [ ] 用class创建类并继承
- [ ] 使用Set去重数组
- [ ] 使用Map存储键值对(键可以是对象)
- [ ] 用可选链安全访问嵌套属性
代码示例:
// Class
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, I'm ${this.name}`);
}
}
class Student extends Person {
constructor(name, grade) {
super(name);
this.grade = grade;
}
}
// Set去重
const arr = [1, 2, 2, 3, 3, 4];
const unique = [...new Set(arr)]; // [1, 2, 3, 4]
// Map
const map = new Map();
map.set('key1', 'value1');
map.set({id: 1}, 'object as key');
// 可选链
const user = {profile: {name: 'Alice'}};
const name = user?.profile?.name; // 'Alice'
const age = user?.profile?.age; // undefined(不报错)学习时间:1天
检验标准:理解并能使用ES6+常用特性
第二阶段:AST基础理论(1-2周)
2.1 AST概念理解(2-3天)
2.1.1 什么是AST
学习内容:
- AST定义:代码的树形结构表示
- 为什么需要AST:代码分析、转换、优化的基础
- AST vs 语法分析树(Parse Tree):抽象程度的区别
- AST的应用场景:编译器、代码检查、代码格式化、混淆/反混淆
实践任务:
- [ ] 阅读AST的维基百科定义
- [ ] 列举10个使用AST的工具(Babel、ESLint、Prettier等)
- [ ] 理解AST在编译过程中的位置
- [ ] 用自然语言描述AST的作用
学习资源:
- 维基百科:Abstract Syntax Tree
- 文章:《什么是抽象语法树》
- 视频教程:AST入门
学习时间:1天
检验标准:能清晰解释AST是什么、为什么需要它
2.1.2 编译原理基础
学习内容:
- 编译过程概览:源代码 → 词法分析 → 语法分析 → AST → 代码生成
- 词法分析(Lexical Analysis):将代码分解为Token
- 语法分析(Parsing):将Token组织成AST
- Token的概念:关键字、标识符、运算符、字面量等
实践任务:
- [ ] 手动将一行代码分解为Token
- [ ] 理解Token的类型(Keyword、Identifier、Literal等)
- [ ] 绘制简单代码的Token序列
- [ ] 理解从Token到AST的转换过程
代码示例:
// 示例代码
const a = 1 + 2;
// Token序列
[
{type: 'Keyword', value: 'const'},
{type: 'Identifier', value: 'a'},
{type: 'Punctuator', value: '='},
{type: 'Numeric', value: '1'},
{type: 'Punctuator', value: '+'},
{type: 'Numeric', value: '2'},
{type: 'Punctuator', value: ';'}
]学习时间:1天
检验标准:理解编译过程,能将代码分解为Token
2.1.3 AST的结构
学习内容:
- 节点(Node):AST的基本单位
- 节点类型(Node Type):不同语法结构对应不同节点类型
- 节点属性:type、loc、range等通用属性
- 树形结构:父节点、子节点、兄弟节点关系
实践任务:
- [ ] 在AST Explorer中查看简单代码的AST
- [ ] 识别AST中的节点和属性
- [ ] 理解节点的嵌套关系
- [ ] 手绘一个简单表达式的AST树
代码示例:
// 代码
const a = 1;
// 对应的AST(简化)
{
type: 'Program',
body: [{
type: 'VariableDeclaration',
kind: 'const',
declarations: [{
type: 'VariableDeclarator',
id: {type: 'Identifier', name: 'a'},
init: {type: 'NumericLiteral', value: 1}
}]
}]
}学习时间:1天
检验标准:能看懂AST的JSON表示,理解树形结构
2.2 AST节点类型(4-5天)
2.2.1 表达式节点(Expression)
学习内容:
- BinaryExpression:二元运算表达式(+、-、*、/等)
- UnaryExpression:一元运算表达式(!、-、++等)
- AssignmentExpression:赋值表达式
- CallExpression:函数调用表达式
- MemberExpression:成员访问表达式
实践任务:
- [ ] 在AST Explorer中分析各种表达式
- [ ] 识别表达式的operator、left、right等属性
- [ ] 理解表达式可以嵌套
- [ ] 对比不同表达式的AST结构
代码示例:
// BinaryExpression
1 + 2
// {type: 'BinaryExpression', operator: '+', left: {value: 1}, right: {value: 2}}
// CallExpression
console.log('hello')
// {type: 'CallExpression', callee: {type: 'MemberExpression'}, arguments: [...]}
// MemberExpression
obj.name
// {type: 'MemberExpression', object: {name: 'obj'}, property: {name: 'name'}}学习时间:1.5天
检验标准:能识别所有常见表达式类型
2.2.2 语句节点(Statement)
学习内容:
- ExpressionStatement:表达式语句
- VariableDeclaration:变量声明
- FunctionDeclaration:函数声明
- IfStatement:条件语句
- ForStatement/WhileStatement:循环语句
- ReturnStatement:返回语句
实践任务:
- [ ] 分析各种语句的AST结构
- [ ] 理解语句和表达式的区别
- [ ] 识别语句的body、test等属性
- [ ] 对比声明语句和表达式语句
代码示例:
// IfStatement
if (x > 0) { console.log('positive'); }
// {type: 'IfStatement', test: {...}, consequent: {...}, alternate: null}
// ForStatement
for (let i = 0; i < 10; i++) {}
// {type: 'ForStatement', init: {...}, test: {...}, update: {...}, body: {...}}学习时间:1.5天
检验标准:能识别所有常见语句类型
2.2.3 声明节点(Declaration)
学习内容:
- FunctionDeclaration:函数声明
- VariableDeclaration:变量声明(var、let、const)
- ClassDeclaration:类声明
- ImportDeclaration/ExportDeclaration:模块导入导出
实践任务:
- [ ] 分析函数声明的params、body等属性
- [ ] 理解VariableDeclaration的declarations数组
- [ ] 分析类声明的结构
- [ ] 理解模块声明的用途
代码示例:
// FunctionDeclaration
function add(a, b) { return a + b; }
// {type: 'FunctionDeclaration', id: {name: 'add'}, params: [...], body: {...}}
// VariableDeclaration
const a = 1, b = 2;
// {type: 'VariableDeclaration', kind: 'const', declarations: [{...}, {...}]}学习时间:1天
检验标准:能识别所有声明节点类型
2.2.4 字面量节点(Literal)
学习内容:
- NumericLiteral:数字字面量
- StringLiteral:字符串字面量
- BooleanLiteral:布尔字面量
- NullLiteral:null字面量
- RegExpLiteral:正则表达式字面量
- ArrayExpression:数组字面量
- ObjectExpression:对象字面量
实践任务:
- [ ] 分析各种字面量的AST表示
- [ ] 理解字面量的value属性
- [ ] 分析数组和对象字面量的elements/properties
- [ ] 对比字面量和其他节点类型
代码示例:
// NumericLiteral
42
// {type: 'NumericLiteral', value: 42}
// ArrayExpression
[1, 2, 3]
// {type: 'ArrayExpression', elements: [{value: 1}, {value: 2}, {value: 3}]}
// ObjectExpression
{name: 'Alice', age: 25}
// {type: 'ObjectExpression', properties: [{key: {...}, value: {...}}, ...]}学习时间:0.5天
检验标准:能识别所有字面量类型
2.3 AST可视化工具(2-3天)
2.3.1 AST Explorer使用
学习内容:
- AST Explorer界面:代码区、AST区、设置区
- Parser选择:Babel、Acorn、Esprima等
- 节点导航:点击代码高亮对应节点
- 节点详情:查看节点的所有属性
实践任务:
- [ ] 访问astexplorer.net并熟悉界面
- [ ] 切换不同的Parser观察AST差异
- [ ] 输入各种代码观察AST变化
- [ ] 点击AST节点定位到代码位置
学习资源:
- AST Explorer官网:https://astexplorer.net
- 视频教程:AST Explorer使用指南
学习时间:0.5天
检验标准:能熟练使用AST Explorer分析代码
2.3.2 常见Parser对比
学习内容:
- Babel Parser:支持最新ES特性和JSX
- Acorn:轻量、快速
- Esprima:符合ESTree规范
- TypeScript Parser:支持TypeScript语法
- Parser的选择:根据需求选择合适的Parser
实践任务:
- [ ] 在AST Explorer中切换不同Parser
- [ ] 对比同一代码在不同Parser下的AST
- [ ] 测试新语法在不同Parser中的支持情况
- [ ] 理解ESTree规范
学习时间:0.5天
检验标准:了解主流Parser的特点,能选择合适的Parser
2.3.3 实践分析练习
学习内容:
- 分析简单表达式:算术运算、逻辑运算
- 分析函数:函数声明、函数调用
- 分析对象操作:属性访问、方法调用
- 分析复杂结构:嵌套对象、闭包
实践任务:
- [ ] 分析10个不同复杂度的代码片段
- [ ] 绘制至少3个代码的AST树形图
- [ ] 总结不同语法对应的AST模式
- [ ] 建立AST节点类型速查表
练习代码:
// 练习1:简单表达式
const result = (a + b) * c;
// 练习2:函数调用
console.log(Math.max(1, 2, 3));
// 练习3:对象操作
user.profile.address.city;
// 练习4:条件语句
if (x > 0) {
console.log('positive');
} else {
console.log('negative');
}
// 练习5:循环
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}学习时间:1.5天
检验标准:能独立分析任意JavaScript代码的AST结构
第三阶段:Babel与AST操作(2-3周)
3.1 Babel核心库(3-4天)
3.1.1 @babel/parser(代码解析)
学习内容:
- parse()方法:将代码字符串解析为AST
- parseExpression()方法:解析单个表达式
- Parser选项:sourceType、plugins等配置
- 错误处理:语法错误的捕获
实践任务:
- [ ] 安装@babel/parser并解析简单代码
- [ ] 使用不同的sourceType(module、script)
- [ ] 启用不同的plugins(jsx、typescript等)
- [ ] 捕获并处理解析错误
代码示例:
const parser = require('@babel/parser');
// 基本解析
const code = 'const a = 1;';
const ast = parser.parse(code);
console.log(ast);
// 解析表达式
const expr = parser.parseExpression('1 + 2');
// 使用选项
const jsxAst = parser.parse('<div>Hello</div>', {
sourceType: 'module',
plugins: ['jsx']
});学习时间:1天
检验标准:能使用@babel/parser解析各种代码
3.1.2 @babel/traverse(AST遍历)
学习内容:
- traverse()方法:遍历AST节点
- Visitor模式:定义节点访问器
- 节点类型访问器:针对特定类型的节点
- enter和exit:进入和离开节点的时机
实践任务:
- [ ] 遍历AST并打印所有节点类型
- [ ] 编写针对特定节点类型的访问器
- [ ] 理解enter和exit的执行顺序
- [ ] 统计代码中的函数数量
代码示例:
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const code = 'function add(a, b) { return a + b; }';
const ast = parser.parse(code);
// 遍历所有节点
traverse(ast, {
enter(path) {
console.log('进入:', path.node.type);
},
exit(path) {
console.log('离开:', path.node.type);
}
});
// 针对特定节点
traverse(ast, {
FunctionDeclaration(path) {
console.log('找到函数:', path.node.id.name);
}
});学习时间:1天
检验标准:能使用traverse遍历AST并访问特定节点
3.1.3 @babel/types(节点操作)
学习内容:
- 节点创建:t.identifier()、t.numericLiteral()等
- 节点判断:t.isIdentifier()、t.isNumericLiteral()等
- 节点克隆:t.cloneNode()
- 节点比较:t.isNodesEquivalent()
实践任务:
- [ ] 创建各种类型的AST节点
- [ ] 使用is系列方法判断节点类型
- [ ] 克隆节点并修改属性
- [ ] 对比两个节点是否等价
代码示例:
const t = require('@babel/types');
// 创建节点
const id = t.identifier('myVar');
const num = t.numericLiteral(42);
const binary = t.binaryExpression('+', num, t.numericLiteral(1));
// 判断节点
if (t.isIdentifier(id)) {
console.log('这是一个标识符:', id.name);
}
// 克隆节点
const clone = t.cloneNode(id);
// 比较节点
const areEqual = t.isNodesEquivalent(id, clone); // true学习时间:1天
检验标准:能使用@babel/types创建和操作AST节点
3.1.4 @babel/generator(代码生成)
学习内容:
- generate()方法:将AST转换回代码
- 代码格式化选项:缩进、换行、注释保留
- SourceMap生成:映射生成代码与原代码的关系
- 代码压缩:去除空格和换行
实践任务:
- [ ] 将AST生成为代码字符串
- [ ] 自定义代码格式(缩进、换行)
- [ ] 生成SourceMap
- [ ] 生成压缩代码
代码示例:
const parser = require('@babel/parser');
const generate = require('@babel/generator').default;
const code = 'const a = 1;';
const ast = parser.parse(code);
// 基本生成
const output = generate(ast);
console.log(output.code); // 'const a = 1;'
// 自定义格式
const formatted = generate(ast, {
retainLines: false,
compact: false,
comments: true
});
// 生成SourceMap
const withMap = generate(ast, {
sourceMaps: true
});
console.log(withMap.map);学习时间:1天
检验标准:能将AST转换为代码并控制格式
3.2 访问者模式深入(2-3天)
3.2.1 Visitor模式原理
学习内容:
- 访问者模式的设计思想:分离数据结构和操作
- Visitor对象:键为节点类型,值为处理函数
- 多节点类型访问器:使用|分隔多个类型
- 访问器的执行顺序:深度优先遍历
实践任务:
- [ ] 理解为什么使用访问者模式
- [ ] 编写处理多种节点类型的Visitor
- [ ] 验证访问器的执行顺序
- [ ] 对比手动遍历和Visitor的优劣
代码示例:
traverse(ast, {
// 单个节点类型
Identifier(path) {
console.log('标识符:', path.node.name);
},
// 多个节点类型
'FunctionDeclaration|ArrowFunctionExpression'(path) {
console.log('找到函数');
},
// enter和exit
BinaryExpression: {
enter(path) {
console.log('进入二元表达式');
},
exit(path) {
console.log('离开二元表达式');
}
}
});学习时间:1天
检验标准:理解Visitor模式,能编写复杂的访问器
3.2.2 Path对象详解
学习内容:
- Path是什么:节点的包装对象,包含上下文信息
- Path属性:node、parent、parentPath、scope等
- Path方法:get、find、getFunctionParent等
- Path与Node的区别:Path包含更多上下文
实践任务:
- [ ] 打印Path对象的所有属性
- [ ] 使用path.parent访问父节点
- [ ] 使用path.scope分析作用域
- [ ] 理解为什么Visitor接收Path而非Node
代码示例:
traverse(ast, {
Identifier(path) {
console.log('节点:', path.node);
console.log('父节点:', path.parent);
console.log('父路径:', path.parentPath);
console.log('作用域:', path.scope);
// 查找父函数
const funcParent = path.getFunctionParent();
if (funcParent) {
console.log('所在函数:', funcParent.node.id.name);
}
}
});学习时间:1天
检验标准:理解Path对象,能使用Path的属性和方法
3.3 AST节点操作(4-5天)
3.3.1 节点替换(replaceWith)
学习内容:
- replaceWith():用新节点替换当前节点
- replaceWithMultiple():用多个节点替换
- replaceWithSourceString():用代码字符串替换
- 替换后的Path更新:path指向新节点
实践任务:
- [ ] 将所有var替换为let
- [ ] 将字面量数字替换为字符串
- [ ] 将函数调用替换为内联值
- [ ] 理解替换后Path的变化
代码示例:
// 将所有var替换为let
traverse(ast, {
VariableDeclaration(path) {
if (path.node.kind === 'var') {
path.node.kind = 'let';
}
}
});
// 将数字加倍
traverse(ast, {
NumericLiteral(path) {
const newNode = t.numericLiteral(path.node.value * 2);
path.replaceWith(newNode);
}
});学习时间:1天
检验标准:能使用replaceWith替换节点
3.3.2 节点删除(remove)
学习内容:
- remove():删除当前节点
- 删除后的影响:父节点的children更新
- 删除的时机:在exit中删除更安全
- 删除后不能继续操作Path
实践任务:
- [ ] 删除所有console.log语句
- [ ] 删除所有debugger语句
- [ ] 删除空的if语句
- [ ] 理解删除节点的副作用
代码示例:
// 删除所有console.log
traverse(ast, {
CallExpression(path) {
if (
t.isMemberExpression(path.node.callee) &&
path.node.callee.object.name === 'console' &&
path.node.callee.property.name === 'log'
) {
path.remove();
}
}
});
// 删除debugger
traverse(ast, {
DebuggerStatement(path) {
path.remove();
}
});学习时间:0.5天
检验标准:能安全地删除节点
3.3.3 节点插入(insert)
学习内容:
- insertBefore():在当前节点前插入
- insertAfter():在当前节点后插入
- unshiftContainer/pushContainer():在容器首/尾插入
- 插入位置的选择:根据需求选择合适的方法
实践任务:
- [ ] 在函数开头插入console.log
- [ ] 在return语句前插入日志
- [ ] 在文件开头插入'use strict'
- [ ] 理解不同插入方法的使用场景
代码示例:
// 在每个函数开头插入日志
traverse(ast, {
FunctionDeclaration(path) {
const log = t.expressionStatement(
t.callExpression(
t.memberExpression(t.identifier('console'), t.identifier('log')),
[t.stringLiteral(`进入函数: ${path.node.id.name}`)]
)
);
path.get('body').unshiftContainer('body', log);
}
});
// 在文件开头插入'use strict'
traverse(ast, {
Program(path) {
const useStrict = t.directive(
t.directiveLiteral('use strict')
);
path.unshiftContainer('directives', useStrict);
}
});学习时间:1天
检验标准:能在各种位置插入节点
3.3.4 节点查找(find/get)
学习内容:
- find():向上查找满足条件的Path
- findParent():查找父Path
- get():通过路径字符串获取子Path
- getSibling():获取兄弟节点
实践任务:
- [ ] 查找包含当前节点的函数
- [ ] 通过路径获取嵌套属性
- [ ] 查找所有兄弟节点
- [ ] 理解Path的查找机制
代码示例:
traverse(ast, {
Identifier(path) {
// 查找父函数
const func = path.findParent(p => p.isFunctionDeclaration());
// 获取子节点
const initPath = path.get('init'); // 针对VariableDeclarator
// 查找兄弟
const nextSibling = path.getSibling(path.key + 1);
}
});学习时间:1天
检验标准:能查找和定位AST中的节点
3.3.5 作用域分析(Scope)
学习内容:
- Scope对象:管理当前作用域的绑定
- Binding对象:变量的定义和引用信息
- path.scope.hasBinding():检查变量是否已定义
- path.scope.rename():安全地重命名变量
实践任务:
- [ ] 获取所有局部变量
- [ ] 检测变量是否已定义
- [ ] 安全地重命名变量(避免冲突)
- [ ] 分析变量的引用次数
代码示例:
traverse(ast, {
FunctionDeclaration(path) {
const scope = path.scope;
// 获取所有绑定
console.log('局部变量:', Object.keys(scope.bindings));
// 检查变量
if (scope.hasBinding('myVar')) {
console.log('myVar已定义');
}
// 安全重命名
scope.rename('oldName', 'newName');
},
Identifier(path) {
const binding = path.scope.getBinding(path.node.name);
if (binding) {
console.log('引用次数:', binding.references);
console.log('定义位置:', binding.path.node);
}
}
});学习时间:1.5天
检验标准:理解作用域和绑定,能进行作用域分析
3.4 编写Babel插件(4-5天)
3.4.1 插件基本结构
学习内容:
- Babel插件的本质:返回visitor的函数
- 插件格式:module.exports = function(babel) { return {visitor: {...}}; }
- babel对象:包含types、template等工具
- 插件选项:通过opts传递配置
实践任务:
- [ ] 创建第一个Babel插件
- [ ] 理解插件的返回值结构
- [ ] 使用babel.types简化节点操作
- [ ] 为插件添加配置选项
代码示例:
// my-plugin.js
module.exports = function(babel) {
const t = babel.types;
return {
name: 'my-plugin',
visitor: {
Identifier(path) {
console.log('找到标识符:', path.node.name);
}
}
};
};
// 使用插件
const babel = require('@babel/core');
const myPlugin = require('./my-plugin');
const result = babel.transformSync(code, {
plugins: [myPlugin]
});学习时间:1天
检验标准:能编写基本的Babel插件
3.4.2 实战插件1:移除console.log
学习内容:
- 识别console.log调用:CallExpression + MemberExpression
- 判断方法名和对象名
- 删除语句而非表达式:parentPath处理
- 处理边界情况:console.log在各种位置
实践任务:
- [ ] 编写移除所有console.log的插件
- [ ] 扩展支持console.warn、console.error等
- [ ] 添加白名单功能(保留某些console调用)
- [ ] 测试插件在各种场景下的表现
代码示例:
module.exports = function({types: t}) {
return {
visitor: {
CallExpression(path) {
const callee = path.node.callee;
// 判断是否为console.*调用
if (
t.isMemberExpression(callee) &&
t.isIdentifier(callee.object, {name: 'console'})
) {
// 删除整个语句
const statement = path.findParent(p => p.isExpressionStatement());
if (statement) {
statement.remove();
}
}
}
}
};
};学习时间:1天
检验标准:插件能正确移除各种场景下的console.log
3.4.3 实战插件2:箭头函数转换
学习内容:
- ArrowFunctionExpression识别
- 转换为FunctionExpression
- this绑定处理:箭头函数不绑定this
- 处理简写形式:单行箭头函数需要添加return
实践任务:
- [ ] 将所有箭头函数转为普通函数
- [ ] 正确处理this引用
- [ ] 处理单行和多行箭头函数
- [ ] 测试嵌套箭头函数
代码示例:
module.exports = function({types: t}) {
return {
visitor: {
ArrowFunctionExpression(path) {
const { node } = path;
const { params, body } = node;
// 处理单行箭头函数
let blockBody = body;
if (!t.isBlockStatement(body)) {
blockBody = t.blockStatement([
t.returnStatement(body)
]);
}
// 创建普通函数
const func = t.functionExpression(
null,
params,
blockBody
);
path.replaceWith(func);
}
}
};
};学习时间:1.5天
检验标准:能正确转换各种箭头函数
3.4.4 实战插件3:自动严格模式
学习内容:
- Program节点:文件的根节点
- Directive节点:'use strict'等指令
- 检查是否已有严格模式声明
- 在文件开头插入指令
实践任务:
- [ ] 在每个文件开头添加'use strict'
- [ ] 检查避免重复添加
- [ ] 支持模块和脚本两种模式
- [ ] 测试插件的幂等性
代码示例:
module.exports = function({types: t}) {
return {
visitor: {
Program(path) {
const { directives } = path.node;
// 检查是否已有'use strict'
const hasStrict = directives.some(
d => d.value.value === 'use strict'
);
if (!hasStrict) {
const directive = t.directive(
t.directiveLiteral('use strict')
);
path.unshiftContainer('directives', directive);
}
}
}
};
};学习时间:0.5天
检验标准:能正确添加严格模式声明
3.4.5 插件调试与测试
学习内容:
- 调试技巧:console.log、断点调试
- 测试框架:Jest等
- 测试用例设计:正常情况、边界情况、错误情况
- 性能优化:避免不必要的遍历
实践任务:
- [ ] 为之前的插件编写测试用例
- [ ] 使用调试器调试插件
- [ ] 测试边界情况(空文件、特殊语法等)
- [ ] 优化插件性能
代码示例:
// 测试用例(Jest)
const babel = require('@babel/core');
const plugin = require('./my-plugin');
describe('my-plugin', () => {
it('should remove console.log', () => {
const input = 'console.log("test");';
const expected = '';
const result = babel.transformSync(input, {
plugins: [plugin]
});
expect(result.code.trim()).toBe(expected);
});
it('should handle multiple console.log', () => {
const input = 'console.log(1); console.log(2);';
const expected = '';
const result = babel.transformSync(input, {
plugins: [plugin]
});
expect(result.code.trim()).toBe(expected);
});
});学习时间:1天
检验标准:能编写测试并调试Babel插件
第四阶段:常见混淆技术分析(3-4周)
4.1 变量名混淆(2-3天)
4.1.1 混淆特征识别
学习内容:
- 短变量名:a、b、c、x、y、z等单字符变量
- 十六进制命名:_0x1234、_0xabcd等
- 下划线命名:_、__、___等
- 随机字符串:aB3dEf、xY9zW等
实践任务:
- [ ] 收集10个混淆后的代码样本
- [ ] 识别其中的变量命名模式
- [ ] 统计变量名长度分布
- [ ] 分析变量名生成规律
代码示例:
// 原始代码
function calculateTotal(price, quantity) {
const tax = 0.1;
return price * quantity * (1 + tax);
}
// 混淆后
function _0x1a2b(a, b) {
const c = 0.1;
return a * b * (1 + c);
}学习时间:0.5天
检验标准:能快速识别变量名混淆模式
4.1.2 变量重命名原理
学习内容:
- 作用域分析:识别变量的有效范围
- 名称冲突检测:避免重命名时产生冲突
- 语义化命名:根据用途推断合理的名称
- 绑定跟踪:追踪变量的所有引用
实践任务:
- [ ] 手动为混淆代码的变量重命名
- [ ] 理解变量作用域对重命名的影响
- [ ] 处理同名变量在不同作用域的情况
- [ ] 记录重命名映射表
代码示例:
// 混淆代码
function _0x1a2b(a, b) {
const c = 0.1;
return a * b * (1 + c);
}
// 作用域分析
{
函数作用域: {
参数: ['a', 'b'],
局部变量: ['c'],
引用: {
a: 2次,
b: 2次,
c: 2次
}
}
}学习时间:1天
检验标准:理解作用域分析的重要性
4.1.3 自动重命名插件实现
学习内容:
- 收集所有绑定:遍历作用域获取变量
- 生成新名称:根据规则生成有意义的名称
- 安全重命名:使用scope.rename()避免冲突
- 命名策略:按类型、用途、位置等命名
实践任务:
- [ ] 编写变量重命名Babel插件
- [ ] 实现按类型命名(param1, local1等)
- [ ] 实现按用途推断命名
- [ ] 处理嵌套作用域的重命名
代码示例:
module.exports = function({types: t}) {
return {
visitor: {
Scope(path) {
// 遍历当前作用域的所有绑定
Object.keys(path.scope.bindings).forEach((oldName, index) => {
// 生成新名称
const newName = `var_${index}`;
// 安全重命名
path.scope.rename(oldName, newName);
});
}
}
};
};学习时间:1天
检验标准:能编写自动重命名插件
4.2 字符串加密(3-4天)
4.2.1 字符串混淆模式识别
学习内容:
- 字符串数组:将所有字符串集中存储
- Base64编码:常见的字符串编码方式
- 十六进制编码:x61x62x63等形式
- Unicode编码:u0061u0062u0063等形式
- 解密函数:还原加密字符串的函数
实践任务:
- [ ] 识别字符串数组的位置和结构
- [ ] 找到解密函数的调用模式
- [ ] 分析解密函数的参数含义
- [ ] 手动执行解密函数验证
代码示例:
// 原始代码
console.log('Hello World');
console.log('Welcome');
// 混淆后
const _0x1234 = ['SGVsbG8gV29ybGQ=', 'V2VsY29tZQ=='];
function _0xdec(index) {
return atob(_0x1234[index]);
}
console.log(_0xdec(0)); // 'Hello World'
console.log(_0xdec(1)); // 'Welcome'学习时间:1天
检验标准:能识别各种字符串加密方式
4.2.2 字符串解密原理
学习内容:
- 静态执行:直接调用解密函数获取结果
- 沙箱执行:在隔离环境中执行不可信代码
- VM模块:使用Node.js的vm模块执行代码
- 参数提取:从调用表达式中提取参数
实践任务:
- [ ] 提取字符串数组和解密函数
- [ ] 在Node.js中执行解密函数
- [ ] 处理解密函数的依赖关系
- [ ] 建立索引到明文的映射表
代码示例:
const vm = require('vm');
// 提取的加密字符串数组和解密函数
const encryptedCode = `
const _0x1234 = ['SGVsbG8gV29ybGQ=', 'V2VsY29tZQ=='];
function _0xdec(index) {
return atob(_0x1234[index]);
}
`;
// 创建沙箱环境
const sandbox = {
atob: (str) => Buffer.from(str, 'base64').toString()
};
// 执行解密函数
vm.runInNewContext(encryptedCode, sandbox);
// 调用解密函数
const decrypt = vm.runInNewContext('_0xdec', sandbox);
console.log(decrypt(0)); // 'Hello World'学习时间:1.5天
检验标准:能提取并执行解密函数
4.2.3 字符串还原插件实现
学习内容:
- 识别解密调用:CallExpression模式匹配
- 执行解密函数:获取明文字符串
- 替换加密调用:用字符串字面量替换
- 清理无用代码:删除字符串数组和解密函数
实践任务:
- [ ] 编写字符串解密Babel插件
- [ ] 处理不同类型的解密函数
- [ ] 替换所有加密字符串调用
- [ ] 删除解密相关的代码
代码示例:
module.exports = function({types: t}) {
// 存储解密后的字符串
const decryptedStrings = {};
return {
visitor: {
// 第一遍:收集字符串数组和解密函数
VariableDeclarator(path) {
if (path.node.id.name === '_0x1234') {
// 提取字符串数组
const strings = path.node.init.elements.map(e => e.value);
// 执行解密
strings.forEach((str, index) => {
decryptedStrings[index] = Buffer.from(str, 'base64').toString();
});
}
},
// 第二遍:替换解密调用
CallExpression(path) {
if (path.node.callee.name === '_0xdec') {
const index = path.node.arguments[0].value;
const decrypted = decryptedStrings[index];
if (decrypted) {
path.replaceWith(t.stringLiteral(decrypted));
}
}
}
}
};
};学习时间:1.5天
检验标准:能编写完整的字符串解密插件
4.3 控制流平坦化(4-5天)
4.3.1 控制流混淆识别
学习内容:
- While + Switch结构:典型的控制流平坦化模式
- 调度器变量:控制执行顺序的变量
- 分支映射:case分支与原始代码的对应关系
- 执行流程:通过调度器跳转的执行路径
实践任务:
- [ ] 识别while循环和switch语句
- [ ] 找到调度器变量
- [ ] 分析case分支的内容
- [ ] 手动跟踪执行流程
代码示例:
// 原始代码
function greet(name) {
console.log('Hello');
console.log(name);
return 'Done';
}
// 控制流平坦化后
function greet(name) {
let _flow = '0|2|1';
const _parts = _flow.split('|');
let _index = 0;
while (true) {
switch (_parts[_index++]) {
case '0':
console.log('Hello');
continue;
case '1':
return 'Done';
case '2':
console.log(name);
continue;
}
break;
}
}学习时间:1天
检验标准:能识别控制流平坦化模式
4.3.2 控制流还原原理
学习内容:
- 调度器分析:确定分支执行顺序
- 分支提取:提取每个case的代码块
- 顺序重组:按正确顺序排列代码
- 跳转消除:去除while和switch结构
实践任务:
- [ ] 手动还原控制流混淆的代码
- [ ] 绘制控制流图
- [ ] 确定代码的真实执行顺序
- [ ] 重写为正常的顺序代码
代码示例:
// 分析调度器
const flow = '0|2|1'.split('|'); // ['0', '2', '1']
// 执行顺序:
// 1. case '0': console.log('Hello');
// 2. case '2': console.log(name);
// 3. case '1': return 'Done';
// 还原后的代码
function greet(name) {
console.log('Hello');
console.log(name);
return 'Done';
}学习时间:1.5天
检验标准:理解控制流还原的步骤
4.3.3 控制流还原插件实现
学习内容:
- While循环检测:识别控制流平坦化的while
- Switch分支提取:获取所有case分支
- 调度器求值:计算分支执行顺序
- 代码重组:按顺序合并代码块
实践任务:
- [ ] 编写控制流还原插件
- [ ] 处理简单的调度器(字符串split)
- [ ] 处理复杂的调度器(计算表达式)
- [ ] 测试插件的准确性
代码示例:
module.exports = function({types: t}) {
return {
visitor: {
WhileStatement(path) {
// 检查是否为控制流平坦化
const test = path.node.test;
if (!t.isBooleanLiteral(test, {value: true})) return;
const body = path.node.body;
if (!t.isSwitchStatement(body)) return;
// 提取调度器
const discriminant = body.discriminant;
// 这里需要分析调度器的值
// 提取所有case分支
const cases = body.cases;
// 根据调度器顺序重组代码
const newStatements = [];
// ... 重组逻辑
// 替换整个while语句
path.replaceWithMultiple(newStatements);
}
}
};
};学习时间:2天
检验标准:能编写控制流还原插件(基础版)
4.4 常量折叠(2-3天)
4.4.1 可计算表达式识别
学习内容:
- 算术运算:1 + 2、3 * 4等
- 字符串拼接:'a' + 'b'、'hello' + 'world'
- 逻辑运算:true && false、!true等
- 比较运算:1 > 2、'a' === 'a'等
实践任务:
- [ ] 列举所有可计算的表达式类型
- [ ] 识别混淆代码中的可计算表达式
- [ ] 手动计算这些表达式的值
- [ ] 统计常量折叠的优化效果
代码示例:
// 原始代码
const x = 10;
// 混淆后(插入可计算表达式)
const x = 5 + 5;
const y = 'hel' + 'lo';
const z = 100 - 50 + 10;
const flag = !false;学习时间:0.5天
检验标准:能识别各类可计算表达式
4.4.2 表达式求值原理
学习内容:
- 字面量识别:数字、字符串、布尔值
- 运算符优先级:正确处理复杂表达式
- 短路求值:&&和||的特殊处理
- 类型转换:隐式类型转换的处理
实践任务:
- [ ] 实现简单的表达式求值器
- [ ] 处理不同类型的字面量
- [ ] 正确计算嵌套表达式
- [ ] 处理边界情况(除零、NaN等)
代码示例:
// 简单求值器
function evaluate(expression) {
if (expression.type === 'NumericLiteral') {
return expression.value;
}
if (expression.type === 'BinaryExpression') {
const left = evaluate(expression.left);
const right = evaluate(expression.right);
const op = expression.operator;
switch (op) {
case '+': return left + right;
case '-': return left - right;
case '*': return left * right;
case '/': return left / right;
default: return undefined;
}
}
}学习时间:1天
检验标准:能实现表达式求值器
4.4.3 常量折叠插件实现
学习内容:
- path.evaluate():Babel内置的求值方法
- 表达式替换:用计算结果替换表达式
- 递归优化:处理嵌套表达式
- 副作用检测:避免折叠有副作用的表达式
实践任务:
- [ ] 使用path.evaluate()求值
- [ ] 编写常量折叠插件
- [ ] 处理多层嵌套的表达式
- [ ] 测试插件的准确性
代码示例:
module.exports = function({types: t}) {
return {
visitor: {
BinaryExpression(path) {
// 尝试求值
const result = path.evaluate();
if (result.confident) {
// 求值成功,替换为字面量
if (typeof result.value === 'number') {
path.replaceWith(t.numericLiteral(result.value));
} else if (typeof result.value === 'string') {
path.replaceWith(t.stringLiteral(result.value));
} else if (typeof result.value === 'boolean') {
path.replaceWith(t.booleanLiteral(result.value));
}
}
}
}
};
};学习时间:1天
检验标准:能编写常量折叠插件
4.5 死代码注入(2天)
4.5.1 死代码识别
学习内容:
- 不可达代码:return后的语句
- 恒假条件:if (false)的分支
- 空操作:空的if、while等
- 无用变量:声明但从未使用的变量
实践任务:
- [ ] 识别各种类型的死代码
- [ ] 分析死代码的注入目的
- [ ] 手动删除死代码
- [ ] 对比删除前后的代码大小
代码示例:
// 死代码示例
function example() {
const x = 10;
return x;
// 不可达代码
console.log('This will never run');
const y = 20;
}
// 恒假条件
if (false) {
console.log('Dead code');
}
// 无用变量
const unused = 'never used';学习时间:0.5天
检验标准:能识别各类死代码
4.5.2 死代码清理插件实现
学习内容:
- 可达性分析:判断代码是否可达
- 引用计数:统计变量的引用次数
- 条件求值:判断条件是否恒真/恒假
- 安全删除:避免误删有副作用的代码
实践任务:
- [ ] 编写死代码删除插件
- [ ] 删除return后的不可达代码
- [ ] 删除恒假条件的分支
- [ ] 删除未使用的变量声明
代码示例:
module.exports = function({types: t}) {
return {
visitor: {
// 删除return后的语句
ReturnStatement(path) {
const parent = path.parentPath;
if (parent.isBlockStatement()) {
const siblings = parent.node.body;
const index = siblings.indexOf(path.node);
// 删除return后的所有语句
siblings.splice(index + 1);
}
},
// 删除恒假条件
IfStatement(path) {
const test = path.get('test');
const result = test.evaluate();
if (result.confident) {
if (result.value) {
// 条件恒真,替换为consequent
path.replaceWith(path.node.consequent);
} else {
// 条件恒假,替换为alternate或删除
if (path.node.alternate) {
path.replaceWith(path.node.alternate);
} else {
path.remove();
}
}
}
},
// 删除未使用的变量
VariableDeclarator(path) {
const binding = path.scope.getBinding(path.node.id.name);
if (binding && binding.referenced === false) {
path.remove();
}
}
}
};
};学习时间:1.5天
检验标准:能编写死代码清理插件
4.6 数组乱序(2-3天)
4.6.1 数组乱序模式识别
学习内容:
- 乱序数组:元素被打乱的数组
- 还原函数:重新排列数组的函数
- 调用时机:还原函数的执行位置
- 索引映射:乱序前后的索引对应关系
实践任务:
- [ ] 识别被乱序的数组
- [ ] 找到数组还原函数
- [ ] 执行还原函数获取正确顺序
- [ ] 建立索引映射表
代码示例:
// 原始数组
const arr = ['apple', 'banana', 'cherry'];
// 乱序后
const _0x1234 = ['cherry', 'apple', 'banana'];
// 还原函数
(function(array, times) {
const restore = function(index) {
while (--times) {
array.push(array.shift());
}
};
restore(++times);
})(_0x1234, 0x2);
// 执行后 _0x1234 = ['apple', 'banana', 'cherry']学习时间:1天
检验标准:能识别数组乱序模式
4.6.2 数组还原插件实现
学习内容:
- 还原函数识别:IIFE模式匹配
- 函数执行:在沙箱中执行还原函数
- 数组更新:用还原后的数组替换乱序数组
- 清理代码:删除还原函数
实践任务:
- [ ] 编写数组还原插件
- [ ] 提取并执行还原函数
- [ ] 更新数组定义
- [ ] 删除还原相关代码
代码示例:
module.exports = function({types: t}) {
return {
visitor: {
Program(path) {
// 查找数组和还原函数
let targetArray = null;
let restoreFunction = null;
path.traverse({
VariableDeclarator(varPath) {
if (t.isArrayExpression(varPath.node.init)) {
targetArray = {
name: varPath.node.id.name,
value: varPath.node.init.elements.map(e => e.value),
path: varPath
};
}
},
CallExpression(callPath) {
// 识别IIFE还原函数
if (t.isFunctionExpression(callPath.node.callee)) {
restoreFunction = callPath;
}
}
});
if (targetArray && restoreFunction) {
// 执行还原函数
const vm = require('vm');
const code = generate(restoreFunction.node).code;
const sandbox = {array: targetArray.value};
vm.runInNewContext(code.replace(targetArray.name, 'array'), sandbox);
// 更新数组
const newArray = t.arrayExpression(
sandbox.array.map(v => t.stringLiteral(v))
);
targetArray.path.node.init = newArray;
// 删除还原函数
restoreFunction.remove();
}
}
}
};
};学习时间:2天
检验标准:能编写数组还原插件
4.7 对象属性混淆(2天)
4.7.1 属性访问混淆识别
学习内容:
- 点号转方括号:obj.name → obj['name']
- 计算属性名:obj[_0x123('0x0')]
- 属性名加密:配合字符串加密
- 动态属性:通过函数调用获取属性名
实践任务:
- [ ] 识别属性访问的混淆模式
- [ ] 找到属性名的解密方法
- [ ] 还原真实的属性名
- [ ] 统一属性访问方式
代码示例:
// 原始代码
const user = {name: 'Alice', age: 25};
console.log(user.name);
// 混淆后
const user = {name: 'Alice', age: 25};
console.log(user['name']);
// 更复杂的混淆
const _0x1 = ['name'];
console.log(user[_0x1[0]]);学习时间:0.5天
检验标准:能识别属性访问混淆
4.7.2 属性访问还原插件实现
学习内容:
- MemberExpression处理:识别属性访问
- computed属性:区分点号和方括号
- 属性名求值:计算动态属性名
- 访问方式统一:转换为点号访问
实践任务:
- [ ] 编写属性访问还原插件
- [ ] 将方括号转为点号(当适用时)
- [ ] 计算动态属性名
- [ ] 测试插件效果
代码示例:
module.exports = function({types: t}) {
return {
visitor: {
MemberExpression(path) {
// 处理 obj['name'] → obj.name
if (path.node.computed && t.isStringLiteral(path.node.property)) {
const propName = path.node.property.value;
// 检查是否为合法标识符
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(propName)) {
path.node.computed = false;
path.node.property = t.identifier(propName);
}
}
// 处理计算属性名
if (path.node.computed) {
const result = path.get('property').evaluate();
if (result.confident && typeof result.value === 'string') {
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(result.value)) {
path.node.computed = false;
path.node.property = t.identifier(result.value);
}
}
}
}
}
};
};学习时间:1.5天
检验标准:能编写属性访问还原插件
第五阶段:实战项目(4-6周)
5.1 通用解混淆工具开发(2-3周)
5.1.1 项目架构设计(2-3天)
学习内容:
- 插件系统设计:可扩展的插件架构
- 配置文件管理:支持自定义配置
- 管道模式:多个插件串联执行
- 错误处理机制:优雅处理各种错误
实践任务:
- [ ] 设计项目目录结构
- [ ] 定义插件接口规范
- [ ] 实现插件加载器
- [ ] 设计配置文件格式
项目结构:
ast-deobfuscator/
├── src/
│ ├── core/
│ │ ├── pipeline.js # 处理管道
│ │ ├── plugin.js # 插件基类
│ │ └── config.js # 配置管理
│ ├── plugins/
│ │ ├── rename.js # 变量重命名
│ │ ├── string.js # 字符串解密
│ │ ├── constant.js # 常量折叠
│ │ ├── deadcode.js # 死代码清理
│ │ └── flow.js # 控制流还原
│ ├── utils/
│ │ ├── logger.js # 日志工具
│ │ ├── vm.js # 沙箱执行
│ │ └── helper.js # 辅助函数
│ └── cli.js # 命令行入口
├── test/
├── config.json
└── package.json学习时间:1天
检验标准:完成项目基础架构
5.1.2 插件系统实现(3-4天)
学习内容:
- 插件生命周期:初始化、执行、清理
- 插件依赖管理:处理插件间的依赖
- 插件优先级:控制插件执行顺序
- 插件配置:支持插件级配置
实践任务:
- [ ] 实现插件基类
- [ ] 实现插件注册机制
- [ ] 实现插件执行引擎
- [ ] 支持插件配置
代码示例:
// plugin.js - 插件基类
class Plugin {
constructor(options = {}) {
this.options = options;
this.name = this.constructor.name;
}
// 插件初始化
init() {}
// 返回Babel插件
getVisitor(babel) {
throw new Error('Plugin must implement getVisitor()');
}
// 清理资源
cleanup() {}
}
// pipeline.js - 处理管道
class Pipeline {
constructor() {
this.plugins = [];
}
// 注册插件
use(plugin) {
this.plugins.push(plugin);
return this;
}
// 执行管道
async run(code) {
const babel = require('@babel/core');
let result = code;
for (const plugin of this.plugins) {
plugin.init();
const transformed = babel.transformSync(result, {
plugins: [plugin.getVisitor.bind(plugin)]
});
result = transformed.code;
plugin.cleanup();
}
return result;
}
}
module.exports = { Plugin, Pipeline };学习时间:2天
检验标准:完成插件系统核心功能
5.1.3 常量折叠模块(1天)
实践任务:
- [ ] 实现常量折叠插件类
- [ ] 支持多种表达式类型
- [ ] 添加配置选项
- [ ] 编写单元测试
代码示例:
const { Plugin } = require('../core/plugin');
class ConstantFoldingPlugin extends Plugin {
getVisitor(babel) {
const t = babel.types;
return {
visitor: {
BinaryExpression(path) {
const result = path.evaluate();
if (result.confident) {
if (typeof result.value === 'number') {
path.replaceWith(t.numericLiteral(result.value));
} else if (typeof result.value === 'string') {
path.replaceWith(t.stringLiteral(result.value));
}
}
}
}
};
}
}
module.exports = ConstantFoldingPlugin;学习时间:1天
检验标准:常量折叠模块正常工作
5.1.4 字符串解密模块(2天)
实践任务:
- [ ] 识别字符串数组和解密函数
- [ ] 提取并执行解密函数
- [ ] 替换所有加密调用
- [ ] 清理解密相关代码
学习时间:2天
检验标准:字符串解密模块正常工作
5.1.5 命令行工具封装(2天)
学习内容:
- Commander.js:命令行框架
- 参数解析:处理命令行参数
- 文件读写:处理输入输出文件
- 进度显示:显示处理进度
实践任务:
- [ ] 安装commander库
- [ ] 实现CLI入口
- [ ] 支持输入输出文件
- [ ] 添加进度显示
代码示例:
// cli.js
const { program } = require('commander');
const fs = require('fs');
const path = require('path');
const { Pipeline } = require('./core/pipeline');
const plugins = require('./plugins');
program
.version('1.0.0')
.option('-i, --input <file>', 'Input file')
.option('-o, --output <file>', 'Output file')
.option('-c, --config <file>', 'Config file')
.parse(process.argv);
const options = program.opts();
if (!options.input) {
console.error('Error: Input file is required');
process.exit(1);
}
// 读取输入文件
const code = fs.readFileSync(options.input, 'utf-8');
// 创建管道
const pipeline = new Pipeline();
// 加载插件
pipeline
.use(new plugins.ConstantFolding())
.use(new plugins.StringDecrypt())
.use(new plugins.DeadCodeRemoval());
// 执行
pipeline.run(code).then(result => {
if (options.output) {
fs.writeFileSync(options.output, result);
console.log(`Output written to ${options.output}`);
} else {
console.log(result);
}
});学习时间:2天
检验标准:CLI工具可用
5.2 分析特定混淆器(1-2周)
5.2.1 obfuscator.io深度分析(3-4天)
学习内容:
- 混淆选项:各种混淆配置的效果
- 混淆特征:识别obfuscator.io的特征
- 混淆等级:从低到高的混淆强度
- 针对性还原:专门处理该混淆器的策略
实践任务:
- [ ] 访问obfuscator.io生成样本
- [ ] 测试不同混淆选项的效果
- [ ] 分析各种混淆模式
- [ ] 编写针对性解混淆脚本
混淆选项分析:
1. 字符串数组(String Array)
- 将字符串提取到数组
- 使用索引访问
2. 字符串数组编码(String Array Encoding)
- base64编码
- rc4加密
3. 字符串数组旋转(String Array Rotate)
- 数组元素旋转
4. 控制流平坦化(Control Flow Flattening)
- while + switch结构
5. 死代码注入(Dead Code Injection)
- 插入永不执行的代码学习时间:2天
检验标准:了解obfuscator.io的所有混淆特性
5.2.2 编写专用解混淆器(2-3天)
实践任务:
- [ ] 实现针对性的解密插件
- [ ] 处理特殊的混淆模式
- [ ] 优化解混淆效果
- [ ] 测试不同混淆等级
学习时间:3天
检验标准:能解混淆obfuscator.io的代码
5.3 实战案例分析(1-2周)
5.3.1 选择目标网站(1天)
学习内容:
- 目标选择标准:合适难度、合法性
- 代码获取:从网页提取JS文件
- 初步分析:判断混淆类型和难度
- 伦理考虑:合法性和道德边界
实践任务:
- [ ] 选择1-2个目标网站
- [ ] 下载混淆的JS文件
- [ ] 初步分析混淆特征
- [ ] 评估解混淆难度
学习时间:1天
检验标准:找到合适的练习目标
5.3.2 完整解混淆流程(3-5天)
实践任务:
- [ ] 分析混淆模式
- [ ] 选择合适的插件组合
- [ ] 逐步解混淆
- [ ] 验证解混淆结果
- [ ] 编写分析报告
解混淆流程:
1. 第一遍:常量折叠
2. 第二遍:字符串解密
3. 第三遍:再次常量折叠
4. 第四遍:死代码清理
5. 第五遍:变量重命名
6. 第六遍:控制流还原
7. 第七遍:代码美化学习时间:5天
检验标准:完成真实案例的解混淆
第六阶段:高级技巧与进阶(持续学习)
6.1 高级混淆技术(2-3周)
6.1.1 虚拟机保护原理(3-4天)
学习内容:
- VM概念:自定义指令集和解释器
- 指令设计:自定义操作码
- 解释器实现:执行自定义指令
- 代码转换:将JS转为自定义指令
实践任务:
- [ ] 研究简单的VM实现
- [ ] 理解指令集设计
- [ ] 分析VM保护的代码
- [ ] 尝试还原VM指令
VM保护示例:
// 原始代码
function add(a, b) {
return a + b;
}
// VM保护后(简化)
const vm = {
instructions: [
['LOAD', 0], // 加载参数a
['LOAD', 1], // 加载参数b
['ADD'], // 执行加法
['RETURN'] // 返回结果
],
execute: function(args) {
const stack = [];
const instructions = this.instructions;
for (let i = 0; i < instructions.length; i++) {
const [op, arg] = instructions[i];
switch (op) {
case 'LOAD':
stack.push(args[arg]);
break;
case 'ADD':
const b = stack.pop();
const a = stack.pop();
stack.push(a + b);
break;
case 'RETURN':
return stack.pop();
}
}
}
};
function add(a, b) {
return vm.execute([a, b]);
}学习时间:3天
检验标准:理解VM保护的基本原理
6.1.2 反调试技术(2天)
学习内容:
- DevTools检测:检测开发者工具是否打开
- 调试器检测:检测debugger语句
- 时间检测:检测代码执行时间异常
- 对抗方法:绕过反调试检测
实践任务:
- [ ] 了解常见反调试技术
- [ ] 分析反调试代码
- [ ] 学习绕过方法
- [ ] 实践反调试对抗
反调试示例:
// DevTools检测
setInterval(() => {
const start = Date.now();
debugger;
const end = Date.now();
if (end - start > 100) {
console.log('DevTools detected!');
// 执行反调试操作
}
}, 1000);
// 窗口大小检测
setInterval(() => {
const widthThreshold = window.outerWidth - window.innerWidth > 160;
const heightThreshold = window.outerHeight - window.innerHeight > 160;
if (widthThreshold || heightThreshold) {
console.log('DevTools detected!');
}
}, 1000);学习时间:2天
检验标准:了解并能绕过基本的反调试
6.1.3 代码加壳技术(2天)
学习内容:
- 加壳原理:运行时解密执行
- Eval执行:动态执行代码
- 多层加壳:加壳套娃
- 脱壳方法:获取真实代码
实践任务:
- [ ] 了解代码加壳的原理
- [ ] 分析加壳的代码
- [ ] 学习脱壳技巧
- [ ] 实践多层脱壳
加壳示例:
// 原始代码
const secret = 'Hello World';
console.log(secret);
// 加壳后
const encrypted = 'Y29uc3Qgc2VjcmV0ID0gJ0hlbGxvIFdvcmxkJzsgY29uc29sZS5sb2coc2VjcmV0KTs=';
eval(atob(encrypted));
// 多层加壳
const layer2 = 'ZXZhbChhdG9iKCJZMjl1YzNRZ2MyVmpjbVYwSUQwZ0owaGxiR3h2SUZkdmNteGtKenNnWTI5dWMyOXNaUzVzYjJjb2MyVmpjbVYwS1RzPSIpKTs=';
eval(atob(layer2));学习时间:2天
检验标准:能脱壳并获取真实代码
6.2 动静态结合分析(1-2周)
6.2.1 动态Hook技术(3-4天)
学习内容:
- 函数Hook:拦截函数调用
- Object.defineProperty:劫持属性访问
- Proxy:代理对象操作
- 日志注入:记录函数调用
实践任务:
- [ ] 实现函数Hook
- [ ] Hook关键API(eval、Function等)
- [ ] 记录函数调用参数和返回值
- [ ] 分析Hook日志
Hook示例:
// Hook eval
const originalEval = window.eval;
window.eval = function(code) {
console.log('eval called with:', code);
return originalEval.call(this, code);
};
// Hook Function构造器
const OriginalFunction = window.Function;
window.Function = function(...args) {
console.log('Function called with:', args);
return OriginalFunction.apply(this, args);
};
// Hook对象属性
const obj = {value: 42};
const proxy = new Proxy(obj, {
get(target, prop) {
console.log(`Getting ${prop}: ${target[prop]}`);
return target[prop];
},
set(target, prop, value) {
console.log(`Setting ${prop} to ${value}`);
target[prop] = value;
return true;
}
});学习时间:3天
检验标准:能使用Hook技术辅助分析
6.2.2 断点调试技巧(2天)
学习内容:
- 条件断点:在特定条件下中断
- 日志断点:不中断但输出日志
- DOM断点:监控DOM变化
- 事件断点:监控特定事件
实践任务:
- [ ] 使用Chrome DevTools调试
- [ ] 设置各种类型的断点
- [ ] 追踪代码执行流程
- [ ] 分析变量值变化
学习时间:2天
检验标准:熟练使用调试工具
6.3 自动化工具开发(1-2周)
6.3.1 特征识别算法(3-4天)
学习内容:
- 模式匹配:识别特定的代码模式
- 启发式分析:基于经验的判断
- 统计分析:代码特征统计
- 机器学习:训练分类器
实践任务:
- [ ] 收集混淆代码样本
- [ ] 提取代码特征
- [ ] 设计识别规则
- [ ] 实现自动识别
特征提取:
function extractFeatures(code) {
const ast = parser.parse(code);
const features = {
// 基础特征
codeLength: code.length,
lineCount: code.split('\n').length,
// AST特征
nodeCount: 0,
identifierCount: 0,
stringLiteralCount: 0,
// 特殊模式
hasWhileSwitch: false,
hasStringArray: false,
hasEval: false,
// 变量名特征
avgIdentifierLength: 0,
hexIdentifiers: 0
};
traverse(ast, {
enter(path) {
features.nodeCount++;
if (path.isIdentifier()) {
features.identifierCount++;
if (/^_0x[0-9a-f]+$/.test(path.node.name)) {
features.hexIdentifiers++;
}
}
if (path.isStringLiteral()) {
features.stringLiteralCount++;
}
}
});
return features;
}学习时间:4天
检验标准:能自动识别混淆类型
6.3.2 批量处理能力(2天)
学习内容:
- 并发处理:同时处理多个文件
- 错误恢复:单个文件失败不影响其他
- 进度跟踪:显示整体进度
- 结果汇总:统计处理结果
实践任务:
- [ ] 实现文件批量处理
- [ ] 添加并发控制
- [ ] 实现错误处理
- [ ] 生成处理报告
学习时间:2天
检验标准:能批量处理多个文件
6.4 学习建议与进阶方向
6.4.1 持续学习资源
推荐资源:
- GitHub项目:关注优秀的解混淆项目
- 技术博客:阅读相关技术文章
- CTF比赛:参加安全竞赛
- 开源贡献:为开源项目贡献代码
6.4.2 进阶方向
方向建议:
- WebAssembly逆向:学习WASM分析
- 编译原理深入:学习编译器设计
- 二进制逆向:学习汇编和逆向工程
- 安全研究:深入Web安全领域
🎓 学习总结
完整学习路径
第一阶段(2-3周):JavaScript基础
- 掌握JS核心语法和特性
- 为AST学习打下基础
第二阶段(1-2周):AST理论
- 理解AST的概念和结构
- 熟悉各种节点类型
第三阶段(2-3周):Babel工具
- 掌握Babel核心库
- 能编写Babel插件
第四阶段(3-4周):混淆分析
- 识别常见混淆技术
- 编写解混淆插件
第五阶段(4-6周):实战项目
- 开发通用解混淆工具
- 分析真实混淆代码
第六阶段(持续):高级进阶
- 学习高级混淆技术
- 掌握动静态分析
- 开发自动化工具
💪 最后的话
坚持是关键:AST解混淆是一个需要耐心和毅力的领域。从零基础到高手,需要大量的实践和积累。
动手最重要:理论知识固然重要,但只有通过实际编码和调试,才能真正掌握解混淆技术。
保持好奇心:混淆技术在不断演进,保持学习的热情,关注最新的技术动态。
合法与道德:使用解混淆技术时,请遵守法律法规,尊重知识产权,仅用于学习和合法目的。
持续精进:完成这个学习计划后,不要停止学习。继续探索更深层次的技术,成为真正的AST解混淆专家!
🎯 现在就开始你的AST解混淆学习之旅吧!