特别说明:本文 生成节点时,函数的使用对于 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)
实现数组乱序
上面实现的数组混淆,
数组元素
与索引
之间 是一一对应的
,那如果数组元素
与索引
之间 还需要函数 进行 转换
,运行时,才把大数据还原到正确顺序,那样难度就会再上升。
乱序函数 & 还原函数
// 乱序函数 ((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()
实现十六进制字符串
上面的数组混淆,其中的 还原函数 的函数没有办法提取到大数组中,但其中的 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') // 替换掉 十六进制 多余的转义字符
版权声明:《 【AST 混淆】二、实现数组混淆 & 十六进制字符串 》为明妃原创文章,转载请注明出处!
最后编辑:2022-4-13 10:04:26