AST 技术进行 JavaScript 反混淆实战

一、AST 技术核心原理

抽象语法树(AST) 是代码的“骨架”,它把代码拆解成一个个节点,就像把一棵大树拆成树枝、树叶一样。通过分析和修改这些节点,我们可以精准地还原代码的逻辑。

二、实战案例 1:还原字符串编码

混淆代码特征

混淆代码会把字符串拆成数组,然后通过索引访问,比如:

const _0x5c0d = ["Hello", "World", "log", "split", "join"];
console[_0x5c0d[2]](_0x5c0d[0][_0x5c0d[3]]("")[_0x5c0d[4]]("-"));
// 输出 "H-e-l-l-o"
反混淆步骤
  1. 安装依赖

    npm install @babel/parser @babel/traverse @babel/generator
  2. 解析和还原代码

    • 用 Babel 把代码解析成 AST。

    • 遍历 AST,找到字符串数组的定义。

    • 替换所有数组索引访问为实际的字符串值。

const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;

const code = `
const _0x5c0d = ["Hello", "World", "log", "split", "join"];
console[_0x5c0d[2]](_0x5c0d[0][_0x5c0d[3]]("")[_0x5c0d[4]]("-"));
`;

// 解析为 AST
const ast = parser.parse(code);

// 遍历 AST 节点
traverse(ast, {
  VariableDeclarator(path) {
    // 找到字符串数组
    if (path.node.id.name === "_0x5c0d") {
      const stringArray = path.node.init.elements.map(e => e.value);
      
      // 替换所有数组引用
      path.scope.traverse(path.scope.block, {
        MemberExpression(subPath) {
          const node = subPath.node;
          if (node.object.name === "_0x5c0d" && node.property.type === "NumericLiteral") {
            const value = stringArray[node.property.value];
            subPath.replaceWith({ type: "StringLiteral", value });
          }
        }
      });
    }
  }
});

// 生成还原后的代码
const output = generator(ast).code;
console.log(output);

输出结果

console["log"]("Hello"["split"]("")["join"]("-"));
// 进一步简化为:console.log("H-e-l-l-o");

三、实战案例 2:修复控制流扁平化

混淆代码特征

混淆代码会把逻辑打乱成一个状态机,比如:

function getSum(n) {
  let state = 0, total = 0, i = 0;
  while (true) {
    switch (state) {
      case 0: state = 1; break;
      case 1: if (i < n) state = 2; else state = 3; break;
      case 2: total += i; i++; state = 1; break;
      case 3: return total;
    }
  }
}
反混淆步骤
  1. 识别状态机模式

    • 找到 switch-case 结构。

    • 分析 state 变量的跳转逻辑。

  2. 重构逻辑

    • 把状态机逻辑还原成正常的循环结构。

const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");

const code = `...上述控制流代码...`;
const ast = parser.parse(code);

traverse(ast, {
  FunctionDeclaration(path) {
    const body = path.get("body.body");
    const switchStmt = body.find(p => p.isSwitchStatement());
    
    // 提取状态转移逻辑
    const stateMap = new Map();
    switchStmt.get("cases").forEach(casePath => {
      const test = casePath.node.test.value;
      const consequent = casePath.get("consequent");
      stateMap.set(test, consequent);
    });

    // 重构为 for 循环
    const newBody = [
      t.variableDeclaration("let", [t.variableDeclarator(t.identifier("total"), t.numericLiteral(0))]),
      t.forStatement(
        t.variableDeclaration("let", [t.variableDeclarator(t.identifier("i"), t.numericLiteral(0))]),
        t.binaryExpression("<", t.identifier("i"), t.identifier("n")),
        t.updateExpression("++", t.identifier("i")),
        t.blockStatement([t.expressionStatement(
          t.assignmentExpression("+=", t.identifier("total"), t.identifier("i"))
        )])
      ),
      t.returnStatement(t.identifier("total"))
    ];

    path.get("body").replaceWith(t.blockStatement(newBody));
  }
});

// 输出还原后代码
console.log(generator(ast).code);

输出结果

function getSum(n) {
  let total = 0;
  for (let i = 0; i < n; i++) {
    total += i;
  }
  return total;
}

四、对抗混淆的高级技巧

  1. 处理多层嵌套

    • 混淆代码可能把数组多层嵌套,需要递归展开所有层级。

  2. 动态解密函数处理

    • 若字符串解密通过函数实现,AST 需模拟执行函数逻辑。

  3. 符号执行优化

    • 对条件进行数学推导,消除永远无法执行的分支。

五、工具链推荐

工具用途示例命令
@babel/parser代码解析为 ASTparser.parse(code)
@babel/traverse节点遍历与修改traverse(ast, { ... })
@babel/generator从 AST 生成代码generator(ast).code

六、总结

AST 反混淆的核心步骤

  1. 解析:把代码转换为结构化的 AST。

  2. 识别模式:找到字符串数组、控制流状态机等特征。

  3. 语义还原:通过节点替换或逻辑重构恢复原始逻辑。

  4. 验证:确保重构后的代码功能与原混淆代码一致。

注意事项

  • 混淆工具可能插入虚假节点(如无用循环),需结合数据流分析过滤。

  • 遇到 evalFunction 动态代码时,需结合动态执行。

  • 推荐结合 Prettier 格式化还原后的代码,提升可读性。

通过 AST 技术,我们可以从“代码加密 → 结构解析 → 逻辑还原”完成全链路逆向工程,是应对现代混淆技术的核心手段!

Esprima是一个强大的JavaScript解析器,它可以帮助开发者理解和操作JavaScript代码。在项目实战中,可以借助Esprima来检测代码是否经过混淆处理,并执行基础的反混淆操作。 参考资源链接:[javascript混淆检测与反混淆策略研究](https://wenku.youkuaiyun.com/doc/8bs0i8iwz7) 首先,了解Esprima的基本使用方法是关键。Esprima可以解析JavaScript代码,并生成一个抽象语法树(AST),这是一种以树状结构表示源代码的方法。通过分析AST,我们可以获取关于代码结构的详细信息,从而判断代码是否经过混淆。 使用Esprima进行混淆检测的步骤包括: 1. 将待检测的JavaScript代码作为输入传递给Esprima。 2. Esprima将代码解析为AST。 3. 分析AST的结构,查看代码中变量命名、函数声明、循环结构等是否出现不规则或不自然的模式,这些可能是混淆操作的结果。 如果发现代码被混淆,可以尝试进行反混淆。但请注意,反混淆是一个复杂的过程,往往依赖于具体混淆技术的细节。这里提供一个简单的反混淆策略: 1. 使用Esprima解析混淆的代码,获取AST。 2. 遍历AST,寻找可能的混淆点,比如不必要的运算符插入、变量替换等。 3. 根据混淆的类型,尝试恢复代码的原始意义。例如,如果发现变量名被替换为无意义的字符序列,可以尝试将它们改回有意义的名字。 4. 重新构造代码,使用Esprima输出反混淆后的代码。 需要注意的是,由于混淆手段的多样性和复杂性,使用Esprima进行反混淆可能无法完美恢复所有混淆代码的原始逻辑,特别是对于高度复杂和定制化的混淆算法。因此,对于商业级别的应用,建议采用更为专业的反混淆工具或服务。 为了更深入地了解JavaScript混淆检测和反混淆的理论与实践,推荐阅读《javascript混淆检测与反混淆策略研究》。这篇研究论文不仅介绍了JavaScript混淆的基本概念,还详细分析了不同混淆类型,并提出了一系列检测和反混淆的策略。通过这篇文章,你可以了解到更全面的检测方法和反混淆技术,为解决实际问题提供科学的指导和参考。 参考资源链接:[javascript混淆检测与反混淆策略研究](https://wenku.youkuaiyun.com/doc/8bs0i8iwz7)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值