Skip to content

一个完整的AST(抽象语法树)解混淆学习路线,适合零基础学习者从入门到精通。每个知识点都细分为小知识点,配有详细的学习计划。

📚 学习路线概览

本学习计划分为6个阶段,每个阶段都有明确的学习目标、实践项目和进阶路径。预计完整学习周期为3-6个月


第一阶段:JavaScript基础巩固(2-3周)

1.1 变量与数据类型(3-4天)

1.1.1 变量声明方式

学习内容

  • var的特性:函数作用域、变量提升、可重复声明
  • let的特性:块级作用域、暂时性死区、不可重复声明
  • const的特性:块级作用域、声明时必须初始化、引用不可变

实践任务

  • [ ] 编写代码验证var的变量提升行为
  • [ ] 测试let的暂时性死区(TDZ)
  • [ ] 理解const对象属性可修改但引用不可变
  • [ ] 对比三种声明方式在循环中的表现

代码示例

jsx
// 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 ===的区别

代码示例

jsx
// 类型检测
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属性可读可写
  • [ ] 验证函数也是对象(可以添加属性)
  • [ ] 通过代码理解引用传递的本质

代码示例

jsx
// 对象属性访问
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):动态创建函数

实践任务

  • [ ] 验证函数声明的提升行为
  • [ ] 对比函数表达式与函数声明的执行时机
  • [ ] 编写箭头函数的各种形式(单参数、多参数、单行、多行)
  • [ ] 理解箭头函数不能作为构造函数

代码示例

jsx
// 函数声明(会提升)
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对象与数组的区别

代码示例

jsx
// 默认参数
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实现

实践任务

  • [ ] 编写有返回值和无返回值的函数
  • [ ] 通过解构接收多个返回值
  • [ ] 实现简单的链式调用

代码示例

jsx
// 返回多个值
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 闭包

学习内容

  • 闭包定义:函数能够访问其词法作用域外的变量
  • 闭包原理:作用域链、垃圾回收机制
  • 闭包应用:数据私有化、函数工厂、模块化
  • 闭包陷阱:内存泄漏、循环中的闭包

实践任务

  • [ ] 创建一个计数器闭包
  • [ ] 实现私有变量(模拟类的私有属性)
  • [ ] 理解经典的循环闭包问题
  • [ ] 使用闭包创建函数工厂

代码示例

jsx
// 计数器闭包
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遍历对象

代码示例

jsx
// 对象字面量
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过滤原型链属性

代码示例

jsx
// 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转换类数组对象
  • [ ] 测试数组的稀疏性(空槽)

代码示例

jsx
// 数组创建
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实现复杂计算

代码示例

jsx
// 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 解构赋值

学习内容

  • 数组解构:基本用法、默认值、剩余元素
  • 对象解构:基本用法、重命名、默认值
  • 嵌套解构:多层结构的解构
  • 函数参数解构:简化参数传递

实践任务

  • [ ] 交换变量值(不用临时变量)
  • [ ] 从函数返回多个值并解构接收
  • [ ] 解构嵌套对象
  • [ ] 在函数参数中使用解构

代码示例

jsx
// 数组解构
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)
  • [ ] 使用??(空值合并运算符)处理默认值

代码示例

jsx
// 三元运算符
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控制循环

代码示例

jsx
// 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的执行时机(总是执行)
  • [ ] 抛出自定义错误信息
  • [ ] 创建自定义错误类

代码示例

jsx
// 基本异常处理
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 模板字符串

学习内容

  • 模板字面量:反引号语法
  • 插值表达式:$
  • 多行字符串:无需转义换行符
  • 标签模板:自定义模板处理

实践任务

  • [ ] 使用模板字符串拼接字符串
  • [ ] 在模板中嵌入表达式和函数调用
  • [ ] 创建多行字符串
  • [ ] 理解标签模板的用途

代码示例

jsx
// 基本使用
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的区别

代码示例

jsx
// 数组展开
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的错误传播机制

代码示例

jsx
// 创建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函数中使用

代码示例

jsx
// 基本使用
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存储键值对(键可以是对象)
  • [ ] 用可选链安全访问嵌套属性

代码示例

jsx
// 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的转换过程

代码示例

jsx
// 示例代码
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树

代码示例

jsx
// 代码
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结构

代码示例

jsx
// 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等属性
  • [ ] 对比声明语句和表达式语句

代码示例

jsx
// 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数组
  • [ ] 分析类声明的结构
  • [ ] 理解模块声明的用途

代码示例

jsx
// 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
  • [ ] 对比字面量和其他节点类型

代码示例

jsx
// 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节点定位到代码位置

学习资源

学习时间: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节点类型速查表

练习代码

jsx
// 练习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等)
  • [ ] 捕获并处理解析错误

代码示例

jsx
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的执行顺序
  • [ ] 统计代码中的函数数量

代码示例

jsx
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系列方法判断节点类型
  • [ ] 克隆节点并修改属性
  • [ ] 对比两个节点是否等价

代码示例

jsx
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
  • [ ] 生成压缩代码

代码示例

jsx
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的优劣

代码示例

jsx
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

代码示例

jsx
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的变化

代码示例

jsx
// 将所有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语句
  • [ ] 理解删除节点的副作用

代码示例

jsx
// 删除所有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'
  • [ ] 理解不同插入方法的使用场景

代码示例

jsx
// 在每个函数开头插入日志
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的查找机制

代码示例

jsx
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():安全地重命名变量

实践任务

  • [ ] 获取所有局部变量
  • [ ] 检测变量是否已定义
  • [ ] 安全地重命名变量(避免冲突)
  • [ ] 分析变量的引用次数

代码示例

jsx
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简化节点操作
  • [ ] 为插件添加配置选项

代码示例

jsx
// 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调用)
  • [ ] 测试插件在各种场景下的表现

代码示例

jsx
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引用
  • [ ] 处理单行和多行箭头函数
  • [ ] 测试嵌套箭头函数

代码示例

jsx
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'
  • [ ] 检查避免重复添加
  • [ ] 支持模块和脚本两种模式
  • [ ] 测试插件的幂等性

代码示例

jsx
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等
  • 测试用例设计:正常情况、边界情况、错误情况
  • 性能优化:避免不必要的遍历

实践任务

  • [ ] 为之前的插件编写测试用例
  • [ ] 使用调试器调试插件
  • [ ] 测试边界情况(空文件、特殊语法等)
  • [ ] 优化插件性能

代码示例

jsx
// 测试用例(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个混淆后的代码样本
  • [ ] 识别其中的变量命名模式
  • [ ] 统计变量名长度分布
  • [ ] 分析变量名生成规律

代码示例

jsx
// 原始代码
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 变量重命名原理

学习内容

  • 作用域分析:识别变量的有效范围
  • 名称冲突检测:避免重命名时产生冲突
  • 语义化命名:根据用途推断合理的名称
  • 绑定跟踪:追踪变量的所有引用

实践任务

  • [ ] 手动为混淆代码的变量重命名
  • [ ] 理解变量作用域对重命名的影响
  • [ ] 处理同名变量在不同作用域的情况
  • [ ] 记录重命名映射表

代码示例

jsx
// 混淆代码
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等)
  • [ ] 实现按用途推断命名
  • [ ] 处理嵌套作用域的重命名

代码示例

jsx
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等形式
  • 解密函数:还原加密字符串的函数

实践任务

  • [ ] 识别字符串数组的位置和结构
  • [ ] 找到解密函数的调用模式
  • [ ] 分析解密函数的参数含义
  • [ ] 手动执行解密函数验证

代码示例

jsx
// 原始代码
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中执行解密函数
  • [ ] 处理解密函数的依赖关系
  • [ ] 建立索引到明文的映射表

代码示例

jsx
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插件
  • [ ] 处理不同类型的解密函数
  • [ ] 替换所有加密字符串调用
  • [ ] 删除解密相关的代码

代码示例

jsx
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分支的内容
  • [ ] 手动跟踪执行流程

代码示例

jsx
// 原始代码
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结构

实践任务

  • [ ] 手动还原控制流混淆的代码
  • [ ] 绘制控制流图
  • [ ] 确定代码的真实执行顺序
  • [ ] 重写为正常的顺序代码

代码示例

jsx
// 分析调度器
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)
  • [ ] 处理复杂的调度器(计算表达式)
  • [ ] 测试插件的准确性

代码示例

jsx
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'等

实践任务

  • [ ] 列举所有可计算的表达式类型
  • [ ] 识别混淆代码中的可计算表达式
  • [ ] 手动计算这些表达式的值
  • [ ] 统计常量折叠的优化效果

代码示例

jsx
// 原始代码
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等)

代码示例

jsx
// 简单求值器
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()求值
  • [ ] 编写常量折叠插件
  • [ ] 处理多层嵌套的表达式
  • [ ] 测试插件的准确性

代码示例

jsx
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等
  • 无用变量:声明但从未使用的变量

实践任务

  • [ ] 识别各种类型的死代码
  • [ ] 分析死代码的注入目的
  • [ ] 手动删除死代码
  • [ ] 对比删除前后的代码大小

代码示例

jsx
// 死代码示例
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后的不可达代码
  • [ ] 删除恒假条件的分支
  • [ ] 删除未使用的变量声明

代码示例

jsx
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 数组乱序模式识别

学习内容

  • 乱序数组:元素被打乱的数组
  • 还原函数:重新排列数组的函数
  • 调用时机:还原函数的执行位置
  • 索引映射:乱序前后的索引对应关系

实践任务

  • [ ] 识别被乱序的数组
  • [ ] 找到数组还原函数
  • [ ] 执行还原函数获取正确顺序
  • [ ] 建立索引映射表

代码示例

jsx
// 原始数组
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模式匹配
  • 函数执行:在沙箱中执行还原函数
  • 数组更新:用还原后的数组替换乱序数组
  • 清理代码:删除还原函数

实践任务

  • [ ] 编写数组还原插件
  • [ ] 提取并执行还原函数
  • [ ] 更新数组定义
  • [ ] 删除还原相关代码

代码示例

jsx
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')]
  • 属性名加密:配合字符串加密
  • 动态属性:通过函数调用获取属性名

实践任务

  • [ ] 识别属性访问的混淆模式
  • [ ] 找到属性名的解密方法
  • [ ] 还原真实的属性名
  • [ ] 统一属性访问方式

代码示例

jsx
// 原始代码
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属性:区分点号和方括号
  • 属性名求值:计算动态属性名
  • 访问方式统一:转换为点号访问

实践任务

  • [ ] 编写属性访问还原插件
  • [ ] 将方括号转为点号(当适用时)
  • [ ] 计算动态属性名
  • [ ] 测试插件效果

代码示例

jsx
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天)

学习内容

  • 插件生命周期:初始化、执行、清理
  • 插件依赖管理:处理插件间的依赖
  • 插件优先级:控制插件执行顺序
  • 插件配置:支持插件级配置

实践任务

  • [ ] 实现插件基类
  • [ ] 实现插件注册机制
  • [ ] 实现插件执行引擎
  • [ ] 支持插件配置

代码示例

jsx
// 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天)

实践任务

  • [ ] 实现常量折叠插件类
  • [ ] 支持多种表达式类型
  • [ ] 添加配置选项
  • [ ] 编写单元测试

代码示例

jsx
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入口
  • [ ] 支持输入输出文件
  • [ ] 添加进度显示

代码示例

jsx
// 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保护示例

jsx
// 原始代码
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语句
  • 时间检测:检测代码执行时间异常
  • 对抗方法:绕过反调试检测

实践任务

  • [ ] 了解常见反调试技术
  • [ ] 分析反调试代码
  • [ ] 学习绕过方法
  • [ ] 实践反调试对抗

反调试示例

jsx
// 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执行:动态执行代码
  • 多层加壳:加壳套娃
  • 脱壳方法:获取真实代码

实践任务

  • [ ] 了解代码加壳的原理
  • [ ] 分析加壳的代码
  • [ ] 学习脱壳技巧
  • [ ] 实践多层脱壳

加壳示例

jsx
// 原始代码
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示例

jsx
// 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天)

学习内容

  • 模式匹配:识别特定的代码模式
  • 启发式分析:基于经验的判断
  • 统计分析:代码特征统计
  • 机器学习:训练分类器

实践任务

  • [ ] 收集混淆代码样本
  • [ ] 提取代码特征
  • [ ] 设计识别规则
  • [ ] 实现自动识别

特征提取

jsx
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解混淆学习之旅吧!

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.7.1