【AST 混淆】一、常量 & 标识符的混淆

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

使用例子

Date.prototype.format = function (formatSTr) {
    var str = formatSTr;
    str = str.replace(/yyyy|YYYY/, this.getFullYear())
    str = str.replace(/MM/, (this.getMonth() + 1) > 9 ? (this.getMonth() + 1).toString() : '0' + (this.getMonth() + 1));
    str = str.replace(/dd|DD/, this.getDate() > 9 ? this.getDate().toString() : '0' + this.getDate())
    return str
}
console.log(new Date().format('yyyy-MM-dd'))

实现数值常量的加密

NumberLiteral节点:value -> BinaryExpression节点:cipherNum ^ key

代码中的 数值常量 可以遍历 NumberLiteral ,获取其中的 value 属性得到,然后随机生成 一个 Key把 Key 和 Value 进行异或,得到 加密后的 cipherNum ,即 cipherNum = value ^ key ,这样就可以用 BinaryExpression 节点 等价的替换 NumberLiteral 节点

/*
    加密数值常量:1 --> 343333 ^ 343332
        cipherNum = value ^ key
        value = cipherNum ^ key
     */
traverse(ast, {
    NumericLiteral(path) {
        let value = path.node.value;
        let key = parseInt(Math.random() * (999999 - 100000), 10)
        let cipherNum = value ^ key;
        path.replaceWith(
            type.binaryExpression(
                '^',
                left = type.numericLiteral(value = cipherNum),
                right = type.numericLiteral(value = key)))
        // 替换节点里 也有 NumericLiteral 节点,会造成死循环,因此需要加入 path.skip()
        path.skip()
    }
})

mark

实现字符串常量的加密

字符串常量的加密,是使用一个加密函数,对字符串进行加密,在使用时,又解密原始字符串

先遍历 StringLiteral 节点,获取 value 属性,然后对 value 进行加密,把 StringLiteral 节点 替换CallExpression 节点(调用表达式)

例子

这里的例子,需要改变一下,把 调用方法使用之前的文章,改成 字符串调用的方式

window["Date"]["prototype"]["format"] = function (formatSTr) {
  var str = formatSTr;
  str = str["replace"](/yyyy|YYYY/, this["getFullYear"]());
  str = str["replace"](/MM/, this["getMonth"]() + 1 > 9 ? (this["getMonth"]() + 1)["toString"]() : '0' + (this["getMonth"]() + 1));
  str = str["replace"](/dd|DD/, this["getDate"]() > 9 ? this["getDate"]()["toString"]() : '0' + this["getDate"]());
  return str;
};

window["console"]["log"](new window["Date"]()["format"]('yyyy-MM-dd'));

加密

对 字符串进行 base64 编码,然后 浏览器运行时,调用atob 再解码

encFun = (str) => {
    return Buffer.from(str, 'utf-8').toString('base64');
}

traverse(ast, {
    StringLiteral(path) {
        let str = path.node.value;
        let encStr = encFun(path.node.value);
        path.replaceWith(
            type.callExpression(
                callee = type.identifier(value = 'atob'), // atob() 不支持中文解码
                _arguments = [
                    type.stringLiteral(value = encStr)
                ])
        )
        // 替换节点里 也有 stringLiteral 节点,会造成死循环,因此需要加入 path.skip()
        path.skip();
    }
})

mark

mark

发表评论 / Comment

用心评论~