【JavaScript 逆向】AST 反混淆

6 篇文章 1 订阅
6 篇文章 0 订阅

本期目标网站为:

aHR0cHMlM0EvL2R1bi4xNjMuY29tL3RyaWFsL2ppZ3Nhdw==

市面上有许多好用的解混淆的工具,如v神的插件、蔡老板的一键还原等,效果都非常不错。

本文是对易盾加密代码的简单解混淆,主要是对 AST 进行一个初步的学习与了解。

AST 解混淆需要使用到 babel 库,关于babel库的使用可以看官方文档。这里不介绍库的用法,只对解混淆思路做一个简单分析。本案例代码在最下方。

大数组还原

对于易盾这类混淆来说,基本特征都是一样的。在开头或某个地方定义一个大数组,用于存储变量的值,然后定义一个方法,根据传入的参数来从大数组中取值。稍微复杂一点就如易盾一样,还会通过一个自执行函数来对数组进行偏移。所以如果我们需要将大数组进行还原,需要先要对大数组进行偏移,然后通过eval将所有引用了大数组的地方的值计算出来。

本案例中:

大数组变量名为:_0x5ea4

取值方法名为:_0x4139

取大数组取值方法名
//大数组取值方法名
const largeArray = ast1.program.body[0].declarations[0].id.name
// _0x4139
执行偏移代码

得到大数组变量名后再取大数组与偏移代码,观察代码结构,可以发现我们需要的为 body 下的 VariableDeclaration、FunctionDeclaration 与 ExpressionStatement下的第一个 CallExpression。

VariableDeclaration :大数组

FunctionDeclaration :大数组取值方法

CallExpression:大数组偏移代码

拿到这三个结构后我们就可以执行了,这里我们可以直接删除掉 AssignmentExpression 结构,剩余的就是我们要执行的结构。

在这里插入图片描述

traverse(ast, {
    enter(path) {
        if (path.node.type === 'AssignmentExpression' && path.node.left.type === 'MemberExpression') {
            path.remove()
        }
    }
})

offsetCode = generator(ast, {compact: true}).code
eval(offsetCode)
console.log(_0x4139(0xb34))
// toString

为了防止误删其他代码,这里多做了一层判断。为了验证代码是否正确执行,可以调用一下大数组取值方法,看看是否能够正确输出结果。(_0x4139 是本案例代码中大数组取值的方法名)

替换

能够正确执行偏移代码后,就可以将所有引用大数组的代码替换成真实的值了。如

    var _0x530fea = _0x4139
      , _0x1bbc44 = {}[_0x530fea(0xb34)]

我们可以直接将它替换为

_0x1bbc44 = {}["toString"]

想要实现这一步,我们首先需要找到哪些地方通过 _0x4139 引用了大数组。并且,有些代码是嵌套调用的,如:

var _0x530fea = _0x4139
var _0xba4eba = _0x530fea
_0xba4eba(0x9d2)

因此,需要找到这条引用链上的所有变量名。

我们通过第一步中取到的大数组取值方法名来进行遍历。

let quoteArray = new Set([largeArray]);
let current = [largeArray];
while (true) {
    let floor = [];
    current.forEach(function (valName) {
        traverse(ast1, {
            VariableDeclarator(path) {
                if (path.node.init != null && path.node.init.type === "Identifier" && path.node.init.name === valName) {
                    floor.push(path.node.id.name)
                    path.remove()
                }
            }
        });
    })
    if (floor.length === 0) {
        break
    } else {
        quoteArray = new Set([...quoteArray].concat(floor));
        current = floor;
    }
}

console.log(quoteArray)
console.log(quoteArray.size)

鉴于博主 JS 方面不是很在行,所以这里逻辑有点混乱,运行速度稍微较慢,因为需要大量的循环与判断。

拿到所有变量名后就可以进行替换了。

traverse(ast1, {
    CallExpression(path) {
        if (path.node.arguments.length === 1 && quoteArray.has(path.node.callee.name)) {
            path.node.callee.name = largeArray
            path.replaceInline(types.stringLiteral(eval(path.toString())))
        }

    }
});

这时候我们可以将处理后的代码保存观察一下,是否符合我们的预期。

const gcode = generator(ast1.program.body[2].expression.expressions[1])

fs.writeFileSync("code1.js", gcode.code)

大对象还原

除了大数组,我们还会看到一些大对象,这些对象里面声明了许多方法,这些也是可以直接还原的。

原理与大数组一样。

这里只做简单示例,想要完整的还原也需要分析对象的引用关系,添加 try catch 是因为判断条件不够,可以根据代码结构添加一些判断:

let largeObjects = [];
traverse(ast1, {
    VariableDeclarator(path) {
        if (path.node.init != null && path.node.init.type === "ObjectExpression" && path.node.init.properties.length >= 1) {
            try {
                largeObjects.push(path.node.id.name)
                eval(path.toString())
            } catch (e) {

            }

        }
    }
})
traverse(ast1, {
    MemberExpression(path) {
        if (path.node.object.type === 'Identifier' && largeObjects.indexOf(path.node.object.name) > -1) {
            try {
                var path_value = eval(path.toString());
                path.replaceInline(types.stringLiteral(path_value));
            } catch (e) {
            }
        }
    }
})

进制转换

traverse(ast1, {
    "StringLiteral|NumericLiteral"(path) {
        delete path.node.extra;
    }
})

无用结构删除

traverse(ast1, {
    IfStatement(path) {
        if (path.node.test.type === "BooleanLiteral") {
            if (path.node.test.value) {
                path.replaceInline(path.node.consequent.body)
            } else {
                path.replaceInline(path.node.alternate.body)
            }
        }
    }
})

表达式还原

traverse(ast1, {
    BinaryExpression(path) {
        let {confident, value} = path.evaluate()
        if (value !== undefined) {
            path.replaceInline(types.valueToNode(value))
        }
    }
})

易盾的混淆中并未涉及到平坦流,想要了解的可以看看k哥的文章。

AST 能做的还有许多,这里只列举以上的基本操作。

案例代码

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
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
发出的红包

打赏作者

BuerCC

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

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

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

打赏作者

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

抵扣说明:

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

余额充值