【AST 混淆】二、实现数组混淆 & 十六进制字符串

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

说明

【AST 混淆】一、常量 & 标识符的混淆 之后,虽然字符串已经加密了,但是依旧原来的位置数组混淆,就是要把这些 字符串 提取到数组中,然后 字符串原先的位置 改用数组下标方式访问数值成员,字符串还能提取到多个数组,不同的数组处于不同的作用域。

// 混淆前
atob("RGF0ZQ==")

// 混淆后
let arr = ["RGF0ZQ=="]
atob([0]])

使用例子

window[atob("RGF0ZQ==")][atob("cHJvdG90eXBl")][atob("Zm9ybWF0")] = function (formatSTr) {
  var str = formatSTr;
  var week = [atob("5pel"), atob("5LiA"), atob("5LqM"), atob("5LiJ"), atob("5Zub"), atob("5LqU"), atob("5YWt")];
  str = str[atob("cmVwbGFjZQ==")](/yyyy|YYYY/, this[atob("Z2V0RnVsbFllYXI=")]());
  str = str[atob("cmVwbGFjZQ==")](/MM/, this[atob("Z2V0TW9udGg=")]() + 1 > 9 ? (this[atob("Z2V0TW9udGg=")]() + 1)[atob("dG9TdHJpbmc=")]() : atob("MA==") + (this[atob("Z2V0TW9udGg=")]() + 1));
  str = str[atob("cmVwbGFjZQ==")](/dd|DD/, this[atob("Z2V0RGF0ZQ==")]() > 9 ? this[atob("Z2V0RGF0ZQ==")]()[atob("dG9TdHJpbmc=")]() : atob("MA==") + this[atob("Z2V0RGF0ZQ==")]());
  return str;
};

window[atob("Y29uc29sZQ==")][atob("bG9n")](new window[atob("RGF0ZQ==")]()[atob("Zm9ybWF0")](atob("eXl5eS1NTS1kZA==")));

数组混淆

    let bigArr = []; // 存字符串大数组
    traverse(ast, {
        StringLiteral(path) {
            let value = path.node.value;  // 获取值
            let bigArrIndex = bigArr.indexOf(value); // 判断 字符串 是否再大数据
            let index = bigArrIndex;
            if (bigArrIndex == -1) { // 添加字符串到大数据
                let length = bigArr.push(value);
                index = length - 1;
            }
            path.replaceWith( // 字符串 替换成 调用表达式
                type.memberExpression(
                    object = type.identifier('arr'),
                    property = type.numericLiteral(value = index),
                    computed = true
                ))
        }
    })
    // 把大数组组合成节点 添加到 代码头部
    bigArr = bigArr.map((v) => {
        return type.stringLiteral(v)
    })
    bigArr = type.variableDeclarator(
        id = type.identifier('arr'),
        init = type.arrayExpression(bigArr)
    )
    bigArr = type.variableDeclaration(
        kind = 'var',
        declarations = [bigArr]
    )
    // 大数组添加到头部
    ast.program.body.unshift(bigArr)

mark

实现数组乱序

上面实现的数组混淆,数组元素索引之间 是一一对应的,那如果 数组元素索引之间 还需要函数 进行 转换,运行时,才把大数据还原到正确顺序,那样难度就会再上升。

乱序函数 & 还原函数

// 乱序函数
((arr, nums) => {
        while (--nums) { // 简单的倒序
            arr.unshift(arr.pop()) 
        }
})(this.bigArr, this.bigArr.length)

// 还原函数 
((arr, nums) => {
    while (--nums) { // 再倒回来
        arr.push(arr.shift())
    }
})(arr, arr.length)

添加到代码头部

还原函数 要在 乱序数组之后

this.bigArr = type.variableDeclarator(
    id = type.identifier('arr'),
    init = type.arrayExpression(this.bigArr)
)
this.bigArr = type.variableDeclaration(
    kind = 'var',
    declarations = [this.bigArr]
)

this.codeAst && this.ast.program.body.unshift(this.codeAst) // 把 解密函数添加到头部
this.ast.program.body.unshift(this.bigArr) // 大数组添加到头部

完整代码

arrayEncrypt = function (ast) {
    // 数组混淆,把 字符串 提取到一个大数组
    let bigArr = []; // 存字符串大数组
    traverse(ast, {
        StringLiteral(path) {
            let value = path.node.value;  // 获取值
            let bigArrIndex = bigArr.indexOf(value); // 判断 字符串 是否再大数据
            let index = bigArrIndex;
            if (bigArrIndex == -1) { // 添加字符串到大数据
                let length = bigArr.push(value);
                index = length - 1;
            }
            path.replaceWith( // 字符串 替换成 调用表达式
                type.memberExpression(
                    object = type.identifier('arr'),
                    property = type.numericLiteral(value = index),
                    computed = true
                ))
        }
    })
    // 把大数组组合成节点 添加到 代码头部
    bigArr = bigArr.map((v) => {
        return type.stringLiteral(v)
    })
    this.bigArr = bigArr; // 为数组乱序做准备
    this.ast = ast; // 把数组 和 顺序还原函数 添加到头部做准备
    return this
}
arrayEncrypt.prototype.arrayShuffle = function () {
    // 打乱大数组函数
    ((arr, nums) => {
        while (--nums) {
            arr.unshift(arr.pop())
        }
    })(this.bigArr, this.bigArr.length)
    // 读取解密函数
    // let code = fs.readFileSync('./utils/confountArray.js', {encoding: "utf-8"}) // 解密函数太长 可以单独放在一个文件里
    let code = '((arr, nums) => {while (--nums) {arr.push(arr.shift())}})(arr, arr.length)'
    this.codeAst = parser.parse(code);
    return this
}

arrayEncrypt.prototype.unshiftArrayDeclaration = function () {
    // 合并 大数据 & 还原函数
    this.bigArr = type.variableDeclarator(
        id = type.identifier('arr'),
        init = type.arrayExpression(this.bigArr)
    )
    this.bigArr = type.variableDeclaration(
        kind = 'var',
        declarations = [this.bigArr]
    )

    this.codeAst && this.ast.program.body.unshift(this.codeAst) // 把 解密函数添加到头部
    this.ast.program.body.unshift(this.bigArr) // 大数组添加到头部
    return this
}

// 打乱数组
new arrayEncrypt(ast).arrayShuffle().unshiftArrayDeclaration()
// 不打乱数组
// new arrayEncrypt(ast).unshiftArrayDeclaration()

mark

实现十六进制字符串

上面的数组混淆,其中的 还原函数 的函数没有办法提取到大数组中,但其中的 push 、shift 这些方法都可以转换成字符串,简单的编码成 十六进制字符串

arrayEncrypt.prototype.stringToHex = function () {
    let hexEnc = function (code) {
        for (var hexStr = [], i = 0, s; i < code.length; i++) {
            s = code.charCodeAt(i).toString(16);
            hexStr += '\\x' + s; // bable 会自动处理 转义字符,所以生成时,反斜杠会多,最后生成代码时要 替换掉
        }
        return hexStr;
    }
    traverse(this.ast, {
        MemberExpression(path) {
            if (type.isIdentifier(path.node.property)) {
                let name = path.node.property.name;
                path.node.property = type.stringLiteral(hexEnc(name))
            }
            path.node.computed = true;
        }
    });
    return this
}

new arrayEncrypt(ast).arrayShuffle().unshiftArrayDeclaration().stringToHex()
// new arrayEncrypt(ast).unshiftArrayDeclaration().stringToHex()

// ast 转化为 代码
let code = generator(ast).code
code = code.replace(/\\\\x/g, '\\x') // 替换掉 十六进制 多余的转义字符

mark

发表评论 / Comment

用心评论~