AST反混淆脚本

文章目录

  • AST反混淆脚本
    • [1. 基础概念](#1. 基础概念)
    • [2. 前置准备](#2. 前置准备)
    • [3. 反混淆脚本结构](#3. 反混淆脚本结构)
    • [4. 反混淆脚本](#4. 反混淆脚本)
      • [4.1 字面量还原](#4.1 字面量还原)
      • [4.2 字符串拼接](#4.2 字符串拼接)
      • [4.3 解密函数还原](#4.3 解密函数还原)

AST反混淆脚本

1. 基础概念

2. 前置准备

  • 安装必要的npm包:

    bash 复制代码
    npm install @babel/parser @babel/traverse @babel/generator @babel/types
  • 准备混淆的JS文件: 例如 14.js

  • AST在线转换为抽象语法树的网站: AST Explorer

3. 反混淆脚本结构

  • 脚本内容:

    javascript 复制代码
    /**
     * 反混淆脚本结构.js
    */
    // @babel/parser 用于将JavaScript代码转换为ast树
    const parser = require('@babel/parser');
    // @babel/traverse 用于遍历各个节点的函数
    const traverse = require('@babel/traverse').default;
    // @babel/generator 将处理完毕的AST转换成JavaScript源代码
    const generator = require('@babel/generator').default;
    // @babel/types 节点的类型判断及构造等操作
    const types = require('@babel/types');
    // fs模块 用于操作文件的读写
    const fs = require('fs');
    
    // 混淆的js代码文件
    const input_path = 'xx.js';
    // 反混淆的js代码文件
    const output_path = input_path.replace(/\.js$/, '_deobfuscated.js')
    // 读取混淆的 JS 文件
    const code = fs.readFileSync(input_path, 'utf-8');
    // 解析代码生成 AST
    const ast = parser.parse(code);
    
    // ------------------------- 中间插入反混淆的代码 ------------------------------
    
    // 解析AST生成 反混淆后的代码
    const output = generate(ast, {
        compact: false,              // 不压缩代码,保持格式化输出(换行和缩进)
        comments: true,              // 保留源代码中的注释
        jsescOption: {               // JavaScript 转义选项配置
            minimal: false,          // 最小化转义,只对必要的字符进行转义(Unicode 字面量)
            es6: true,               // 允许使用 ES6 语法
            quotes: 'single'         // 使用单引号作为字符串的引号类型(single/double)
        }
    });
    
    // 输出到文件或控制台
    fs.writeFileSync(output_path, output.code);
    console.log('\n✅ ---> 反混淆完成!输出文件:' + output_path);
  • 脚本功能: 读取混淆的JS文件, 解析为AST, 遍历AST, 对特定节点进行反混淆, 生成反混淆后的JS代码

  • 运行脚本:

    bash 复制代码
    node 反混淆脚本结构.js
  • 输出结果: 生成 xx_deobfuscated.js 文件, 内容为反混淆后的JS代码

4. 反混淆脚本

4.1 字面量还原

JavaScript 复制代码
traverse(ast, {
    "StringLiteral|NumericLiteral"(path) {
        // 关键点:直接删除 extra 属性显示为字面量
        path.node.extra && delete path.node.extra;
    }
});
console.log('✅ ---> 字面量还原完成');

4.2 字符串拼接

JavaScript 复制代码
traverse(ast, {
    BinaryExpression: {
        exit(path) {
            const node = path.node;
            if (node.operator !== '+') {
                return;
            }
            const left = node.left;
            const right = node.right;
            // 如果左右都是字符串字面量,则合并
            if (types.isStringLiteral(left) && types.isStringLiteral(right)) {
                const mergedValue = left.value + right.value;
                const mergedNode = types.stringLiteral(mergedValue);
                path.replaceWith(mergedNode);
            }
        }
    }
});
console.log('✅ ---> 字符串拼接完成');

4.3 解密函数还原

JavaScript 复制代码
// 加载解密函数
let bigArray_name, bigArray_index, index = 0;
let arrayMove_index, arrayMove_arg1, arrayMove_arg2;
let decrypt_func, decrypt_index, flag = false;
ast.program.body.some(v => {
    // 数组移位操作
    if (v.type === 'VariableDeclaration' && v.declarations[0].init && v.declarations[0].init.elements && v.declarations[0].init.elements.length > 200) {
        bigArray_name = v.declarations[0].id.name;
        bigArray_index = index;
    }
    if (index + 1 == ast.program.body.length && bigArray_name == undefined) throw '---------大数组获取失败---------';
    // 根据数组名字,匹配出数组位移的函数
    if (v.type == 'ExpressionStatement' && bigArray_name != undefined && v.expression && v.expression.arguments && v.expression.arguments[0].name === bigArray_name) {
        arrayMove_index = index;
        arrayMove_arg1 = v.expression.arguments[0].name;
        arrayMove_arg2 = v.expression.arguments[1].value;
    }
    if (index + 1 == ast.program.body.length && arrayMove_index == undefined) throw '---------数组位移函数获取失败---------';
    // 解密函数
    if (bigArray_index != undefined && arrayMove_index != undefined && arrayMove_index + 1 == index && !flag) {
        try {
            decrypt_func = v.declarations[0].id.name;
            decrypt_index = index;
            flag = true; // 解密函数标志
            return true;
        } catch { console.log('---------解密函数名获取失败---------'); }
    }
    index += 1;
})
console.log('✅ ---> 找到大数组', bigArray_name)
console.log('✅ ---> 找到移位函数,参数:', arrayMove_arg1, arrayMove_arg2)
console.log('✅ ---> 找到解密函数', decrypt_func)

let newAst = parser.parse('');
newAst.program.body.push(ast.program.body[bigArray_index]) // 大数组
newAst.program.body.push(ast.program.body[arrayMove_index]) // 数组位移
newAst.program.body.push(ast.program.body[arrayMove_index + 1]) // 解密函数(采用+1的方式可能会有问题)
let eval_js = generator(newAst, { compact: true }).code; // 将3部分转换成js代码
eval(eval_js) // 执行加载到node内存
console.log('✅ ---> 解密函数加载到内存中')

// 解密函数还原
traverse(ast, {
    VariableDeclaration(path) {
        let { scope, node } = path
        if (!(node.declarations[0].id.name == decrypt_func)) return;
        // 当变量名与解密函数名相同的时候,就执行相应的操作
        let binding = scope.getBinding(decrypt_func)
        // 判断初始值是否被更改
        if (!binding || !binding.constant) return
        for (let referencePath of binding.referencePaths) {
            if (referencePath.parentPath.node.type == 'CallExpression') {
                // 替换操作
                value = eval(referencePath.parentPath.toString())
                referencePath.parentPath.replaceInline(types.valueToNode(value))
            }
        }
    }
})


// 删除解密函数3部分
ast.program.body.splice(bigArray_index, 1);
ast.program.body.splice(arrayMove_index - 1, 1);
ast.program.body.splice(decrypt_index - 2, 1);
console.log('✅ ---> 解密函数解密完成');
相关推荐
早點睡3902 小时前
ReactNative项目OpenHarmony三方库集成实战:@react-native-community/slider
javascript·react native·react.js
早點睡3902 小时前
ReactNative项目OpenHarmony三方库集成实战:react-native-progress
javascript·react native·react.js
张元清2 小时前
React 滚动效果:告别第三方库
前端·javascript·面试
我是伪码农2 小时前
JS 复习
开发语言·前端·javascript
进击的尘埃2 小时前
前端异常监控:从 window.onerror 到完整的错误追踪方案
javascript
Data 实验室2 小时前
TaskPyro “小龙虾版本”专业爬虫管理平台来了:AI+分布式+IM 机器人,一套搞定企业级爬虫调度
人工智能·分布式·爬虫
漂移的电子2 小时前
【echarts 细节】
前端·javascript·echarts
当时只道寻常2 小时前
JavaScript 实现图片懒加载
javascript·性能优化
kyriewen2 小时前
事件流与事件委托:当点击按钮时,浏览器里发生了什么?
前端·javascript·面试