【AST 混淆】四、代码块的混淆

特别说明:本文 生成节点时,函数的使用对于 Js 来说是错误的(不能指定参数给值),这样写是方便 看节点属性

二项式 转 函数花指令

花指令用来更好的隐藏 源代码的真实意图,还能增加代码量,增加分析难度

类型一

// 源代码
var num = a + b;

// 变成
function xxx(c, d){
    return c + d;
}
var num = xxx(a, b)

类型二

// 源代码
var num = add(a);

// 变成
function xxx(c, v){
    return c(v);
}
var num = xxx(add, v)

类型一 思路

获取 二项式符号左右部分,然后 生成 函数节点,把函数节点添加到当前代码块最前面,然后把原始二项式 替换为 调用表达式

// 二项式转花指令
traverse(ast, {
    BinaryExpression(path) {
        let operator = path.node.operator;
        let left_ = path.node.left; // 加个下滑线 防止 冲突
        let right_ = path.node.right;

        // 构造函数节点
        let funName = path.scope.generateUidIdentifier('xxx');
        let a = type.identifier(name = 'a');
        let b = type.identifier(name = 'b');
        let func = type.functionDeclaration(
            id = funName,
            params = [a, b],
            body = type.blockStatement(
                body = [type.returnStatement(
                    argument = type.binaryExpression(
                        operator = operator,
                        left=a,
                        right = b
                    )
                )]
            )
        )

        // 把生成的函数节点添加到当前代码的最前面
        let blockStatement = path.findParent(function (p) {
            return p.isBlockStatement()
        })
        blockStatement.node.body.unshift(func);

        // 替换原节点 为 调用表达式
        path.replaceWith(type.callExpression(
            callee = funName, _arguments = [left_, oright_]
        ))
    }
})

mark

类型二 思路

获取 调用表达式函数参数,然后 生成 函数节点,把函数节点添加到当前代码块最前面,然后把原始调用表达式 替换为 新的调用表达式,只处理 一层的 fun('aa') 不处理 fun.fun('aa')

例子

function add(a, b) {
    return a + b;
}

console.log(add(2, 3))

代码

 // 调用表达式 转 花指令
traverse(ast, {
    CallExpression(path) {
        let callee_ = path.node.callee;
        let arguments_ = path.node.arguments;
        if(!type.isIdentifier(callee_)){ // 只处理 一层的 `fun('aa')` 不处理 `fun.fun('aa')`
            return
        }

        // 构造函数节点
        let funName = path.scope.generateUidIdentifier('fff');
        let f = type.identifier(name = 'f') // 第一个参数,是 函数
        let params_ = []; // 参数列表
        for (let arg in arguments_) {
            params_.push(name = path.scope.generateUidIdentifier('arg'))
        }
        let func = type.functionDeclaration(
            id = funName,
            params = [f].concat(params_),
            body = type.blockStatement(
                body = [type.returnStatement(
                    type.callExpression(
                        callee = f, _arguments = params_
                    )
                )]
            )
        )

        // 把生成的函数节点添加到当前代码的最前面
        let blockStatement = path.findParent(function (p) {
            return p.isBlockStatement() || p.isProgram() // 兼容处理 如何函数需要添加到 最外面层
        })
        blockStatement.node.body.unshift(func);

        // 替换原节点 为 调用表达式
        arguments_.unshift(callee_)
        path.replaceWith(type.callExpression(
            callee = funName, _arguments = arguments_
        ))
        path.skip()
    }
})

mark

发表评论 / Comment

用心评论~