AST反混淆实战-低级难度

Ast实战:反混淆解析低级难度

ob混淆网站
https://obfuscator.io/

一、混淆demo生成
在这里插入图片描述
二、混淆demo说明
在这里插入图片描述
三、混淆demo整理
在这里插入图片描述

demo.js
//TODO  这里对混淆demo进行了手动调整顺序操作,为了便于阅读,进行了格式化处理操作

function _0x49fa() {
    var _0x516a2d = ['{}.constructor(\x22return\x20this\x22)(\x20)', 'apply', 'log', 'constructor', '2SqklKs', 'bind', '16884qOndvr', '1165464jGkqOf', 'Hello\x20World!', 'trace', '(((.+)+)+)+$', 'table', '1335024wPJxRa', '1222485Bckiuw', '__proto__', '199500lvEvzx', 'error', 'info', '7220SPjIao', 'console', 'length', '5607vTyVEp', '181888FaBBpo', 'toString', 'return\x20(function()\x20', 'prototype', 'search', '2056AnReBT', 'exception'];
    _0x49fa = function () {
        return _0x516a2d;
    };
    return _0x49fa();
}
(function (_0x125bfe, _0x500fb6) {
    var _0x1329da = _0x4518, _0x3a893b = _0x125bfe();
    while (!![]) {
        try {
            var _0x5a9bd0 = parseInt(_0x1329da(0xf0)) / 0x1 * (-parseInt(_0x1329da(0xfb)) / 0x2) + parseInt(_0x1329da(0xe9)) / 0x3 + -parseInt(_0x1329da(0xe6)) / 0x4 + -parseInt(_0x1329da(0xe7)) / 0x5 + -parseInt(_0x1329da(0xfe)) / 0x6 + -parseInt(_0x1329da(0xef)) / 0x7 * (parseInt(_0x1329da(0xf5)) / 0x8) + -parseInt(_0x1329da(0xfd)) / 0x9 * (-parseInt(_0x1329da(0xec)) / 0xa);
            if (_0x5a9bd0 === _0x500fb6) break; else _0x3a893b['push'](_0x3a893b['shift']());
        } catch (_0x462d56) {
            _0x3a893b['push'](_0x3a893b['shift']());
        }
    }
}(_0x49fa, 0x3fa7a));
function _0x4518(_0x4980dd, _0x49bf25) {
    var _0x41ef62 = _0x49fa();
    return _0x4518 = function (_0x2e0a85, _0xe588d) {
        _0x2e0a85 = _0x2e0a85 - 0xe6;
        var _0x46a5ea = _0x41ef62[_0x2e0a85];
        return _0x46a5ea;
    }, _0x4518(_0x4980dd, _0x49bf25);
}

function hi() {
    var _0x40b3ee = _0x4518, _0x42eed9 = function () {
        var _0x14d6ea = !![];
        return function (_0x4d0fda, _0xb74864) {
            var _0x5cf757 = _0x14d6ea ? function () {
                var _0x3e3424 = _0x4518;
                if (_0xb74864) {
                    var _0x17acd4 = _0xb74864[_0x3e3424(0xf8)](_0x4d0fda, arguments);
                    return _0xb74864 = null, _0x17acd4;
                }
            } : function () {
            };
            return _0x14d6ea = ![], _0x5cf757;
        };
    }(), _0x29f72a = _0x42eed9(this, function () {
        var _0x332c5c = _0x4518;
        return _0x29f72a[_0x332c5c(0xf1)]()[_0x332c5c(0xf4)](_0x332c5c(0x101))[_0x332c5c(0xf1)]()[_0x332c5c(0xfa)](_0x29f72a)[_0x332c5c(0xf4)](_0x332c5c(0x101));
    });
    _0x29f72a();
    var _0x4a8060 = function () {
        var _0x2d3452 = !![];
        return function (_0x576843, _0x17ac8b) {
            var _0x7723ab = _0x2d3452 ? function () {
                if (_0x17ac8b) {
                    var _0x12504d = _0x17ac8b['apply'](_0x576843, arguments);
                    return _0x17ac8b = null, _0x12504d;
                }
            } : function () {
            };
            return _0x2d3452 = ![], _0x7723ab;
        };
    }(), _0x19a1a3 = _0x4a8060(this, function () {
        var _0x1118aa = _0x4518, _0x1812be;
        try {
            var _0x5b1f89 = Function(_0x1118aa(0xf2) + _0x1118aa(0xf7) + ');');
            _0x1812be = _0x5b1f89();
        } catch (_0x5bb1f8) {
            _0x1812be = window;
        }
        var _0x591858 = _0x1812be[_0x1118aa(0xed)] = _0x1812be[_0x1118aa(0xed)] || {},
            _0x5e6a8b = [_0x1118aa(0xf9), 'warn', _0x1118aa(0xeb), _0x1118aa(0xea), _0x1118aa(0xf6), _0x1118aa(0x102), _0x1118aa(0x100)];
        for (var _0x1170fb = 0x0; _0x1170fb < _0x5e6a8b[_0x1118aa(0xee)]; _0x1170fb++) {
            var _0x504458 = _0x4a8060[_0x1118aa(0xfa)][_0x1118aa(0xf3)][_0x1118aa(0xfc)](_0x4a8060),
                _0x2f50a0 = _0x5e6a8b[_0x1170fb], _0x116675 = _0x591858[_0x2f50a0] || _0x504458;
            _0x504458[_0x1118aa(0xe8)] = _0x4a8060[_0x1118aa(0xfc)](_0x4a8060), _0x504458['toString'] = _0x116675[_0x1118aa(0xf1)][_0x1118aa(0xfc)](_0x116675), _0x591858[_0x2f50a0] = _0x504458;
        }
    });
    _0x19a1a3(), console[_0x40b3ee(0xf9)](_0x40b3ee(0xff));
}
hi();

四、存在难点
在这里插入图片描述

难点解决:
AST反混淆进阶-标识符重复赋值
https://jia666666.blog.csdn.net/article/details/120352005

在这里插入图片描述

难点解决:
AST反混淆进阶-大数组解密
https://jia666666.blog.csdn.net/article/details/120304802
注意:由于这里demo不同于大数组解密样例,需要进行部分改写,才能使用大数组解密模块

在这里插入图片描述

这一节是:
基于AST反混淆实战-默认难度解析源码
https://jia666666.blog.csdn.net/article/details/120368597
利用AST反混淆实战-默认难度解析源码解析后,如图,可以明显看到console禁用输入功能

难点解决
AST反混淆进阶-禁用console输出功能删减
https://jia666666.blog.csdn.net/article/details/120354257

五、解混淆

在AST反混淆实战-默认难度解析源码中增加禁用console输出功能删减即可
dec_main.js

const fs = require("fs");//文件读写
const parse = require("@babel/parser"); //解析为ast
const traverse = require('@babel/traverse').default;//遍历节点
const t = require('@babel/types');//类型
const generator = require('@babel/generator').default;//ast解析为代码


//读取js文件
const jscode = fs.readFileSync(
    './read.js', {
        encoding: 'utf-8'
    }
);
let ast = parse.parse(jscode);//js转ast

try {
    //TODO 1 标识符-重复赋值
    traverse(ast, {VariableDeclarator: {exit: [ReIdent]},});
    console.log('第一步:标识符-重复赋值已完成')

    //TODO 2 大数组解密
    ast = decrypt_arr(ast)//大数组还原
    console.log('第二步:大数组解密已完成')

    //TODO 3 禁用console删减
    ast = parse.parse(generator(ast).code)//刷新ast
    traverse(ast, {VariableDeclarator: {exit: [DelConsole_one]},});
    ast = parse.parse(generator(ast).code)//刷新ast
    traverse(ast, {VariableDeclarator: {exit: [DelConsole_two]},});

    console.log('第三步:禁用console删减已完成')

    //TODO  拆分对象合并
    //TODO  对象表达式字符串合并
    //TODO  花指令函数处理
    //TODO  反控制流平坦化
    //TODO  自执行实参替换形参
    //TODO  替换空参数的自执行方法为顺序语句-慎用!可能涉及到作用域的问题。
    //TODO  删减定时器
    //TODO  return函数简化
    //TODO  数组函数简化
    //TODO  常量计算
    //TODO  未修改常量替换
    //TODO  删除if语句块中假的部分-依赖常量计算
    //TODO  正则检测替换
    //TODO  删减未引用的标识符
    //TODO  内存爆破检测

} catch (e) {
    console.log(e);
} finally {
    //TODO Finally ast还原js
    code = generator(ast, opts = {jsescOption: {"minimal": true}}).code// 处理中文Unicode
//文件保存
    fs.writeFile('./demoNew.js', code, (err) => {
    });
}


function decrypt_arr(ast) {
    //TODO 1 解密三部分的代码执行
    let end = 3;//切片需要处理的代码块
    let newAst = parse.parse('');//新建ast
    let decrypt_code = ast.program.body.slice(0, end);//切片
    newAst.program.body = decrypt_code// 将前3个节点替换进新建ast
    let stringDecryptFunc = generator(newAst, {compact: true},).code;//转为js,由于存在格式化检测,需要指定选项,来压缩代码// 自动转义
    eval(stringDecryptFunc);//执行三部分的代码


    //TODO 2 准备工作及对解密三部分节点删除
    let stringDecryptFuncAst = ast.program.body[end - 1];// 拿到解密函数所在的节点

    let DecryptFuncName = stringDecryptFuncAst.id.name;//拿到解密函数的名字
    var rest_code = ast.program.body.slice(end); // 剩下的节点
    ast.program.body = rest_code;//剩下的节点替换


    //TODO 3 加密数组还原
    traverse(ast, {
        CallExpression(path) {//回调表达式匹配--替换加密数组为对应的值
            if (t.isIdentifier(path.node.callee, {name: DecryptFuncName})) {       //当变量名与解密函数名相同时,就执行相应操作
                path.replaceWith(t.valueToNode(eval(path.toString())));      // 值替换节点
            }
        },
    });
    return ast;

}


function ReIdent(path) {
    // 标识符简化
    let node = path.node;//获取路径节点
    if (!t.isIdentifier(node.id) || !t.isIdentifier(node.init)) return;

    let leftName = node.id.name;//函数名称
    let rightName = node.init.name;//函数名称

    let scope = path.scope;//获取路径的作用域
    let binding = scope.getBinding(leftName);//获取绑定
    if (!binding || binding.constantViolations.length > 0) {//检查该变量的值是否被修改--一致性检测
        return;
    }
    let paths = binding.referencePaths;//绑定引用的路径
    let paths_sums = 0;
    paths.map(function (refer_path) {
        refer_path.node.name = rightName;//标识符重命名
        paths_sums += 1;//路径+1
    });
    if (paths_sums == paths.length) {//若绑定的每个路径都已处理 ,则移除当前路径
        path.remove();//删除路径
    }
}

function DelConsole_one(path) {
    // 删除console
    let node = path.node;//获取路径节点
    if (!t.isCallExpression(node.init)) return;//不是回调表达式,退出
    if (node.init.arguments.length !== 2) return;//形参不等于2个
    if (!t.isThisExpression(node.init.arguments[0])) return;//this表达式
    let thisname = node.id.name;//节点名称

    let scope = path.scope;//获取路径的作用域
    let binding = scope.getBinding(thisname);//获取绑定
    if (!binding || binding.constantViolations.length > 0) {//检查该变量的值是否被修改--一致性检测
        return;
    }
    let paths = binding.referencePaths;//绑定引用的路径
    paths.map(function (refer_path) {
        let bindpath = refer_path.parentPath;//父路径
        if (!t.isCallExpression(bindpath)) return;//回调表达式判断
        if (!t.isIdentifier(bindpath.node.callee)) return;//标识符判定
        bindpath.remove();//删除路径
    });
    path.remove();//删除路径
}

function DelConsole_two(path) {
    // 删除console遗留下列未使用的定义变量
    let node = path.node;//获取路径节点
    if (!t.isCallExpression(node.init)) return;//不是回调表达式,退出
    if (node.init.arguments.length !== 0) return;//形参不等于0个
    if (!t.isFunctionExpression(node.init.callee)) return;//this表达式
    let thisname = node.id.name;//节点名称

    let scope = path.scope;//获取路径的作用域
    let binding = scope.getBinding(thisname);//获取绑定
    if (!binding || binding.constantViolations.length > 0) {//检查该变量的值是否被修改--一致性检测
        return;
    }
    let paths = binding.referencePaths;//绑定引用的路径
    if (paths.length !== 0) return;

    path.remove();//删除路径
}

六、解混淆完成

demoNew.js

function hi() {
  console["log"]("Hello World!");
}

hi();
js逆向AST混淆是一种通过析和修改JavaScript的抽象语法树(AST)来还原混淆代码的过程。首先,我们需要获取到混淆代码的AST表示形式。然后,根据特定的混淆算法,对AST进行遍历和修改,以还原原始代码的结构和逻辑。在这个过程中,我们可以使用不同的技术和工具来帮助我们完成混淆任务。 在提供的引用中,涉及了一些对AST进行遍历和修改的代码片段。例如,在引用中,使用了traverse函数来遍历AST,然后通过修改AST节点来进行替换和替换。在引用中,通过迭代和遍历AST,找到变量名和取值方法名,然后将它们替换或删除。在引用中,使用了traverse函数和eval函数来移除赋值表达式和成员表达式。 以上是一些常见的技术和方法,用于js逆向AST混淆。具体的混淆过程可能因代码结构和混淆方式而有所不同。为了成功混淆代码,可能需要更多的详细信息和专业知识。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [【JavaScript 逆向】AST 混淆](https://blog.csdn.net/pyzzd/article/details/130613135)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jia666666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值