某数控制流改写
本帖最后由 lsq665 于 2023-8-4 11:54 编辑仅供学习,不得用于商业用途,侵删。
某数控制流有好多种,例如
虽然判断条件的形式不同,但整体结构都基本相同,所以可以对这种结构统一进行改写
,伪代码如下:
_$OR = ;
while (1) {
_$r4 = _$OR;
if (test1) {
statement1
} else if (test2) {
statement2
} else if (test3) {
statement3
} else {
statement4
}
}
上面的代码可以改写为另一种形式,伪代码如下:
_$OR = ;
while (1) {
_$r4 = _$OR;
if (test1) statement1;
if (!test1 && test2) statement2;
if (!test1 && !test2 && test3) statement3;
if (!test1 && !test2 && !test3) statement4;
由于这些if条件是互斥的,一个索引数组的值只会进入到一个if语句中,所以如果将这些if的条件收集起来进行遍历,就可以得到每个索引对应的语句,伪代码如下:
let cases = [];
let arr = [['test1',statement1],['!test1 && test2',statement2],['!test1 && !test2 && test3',statement3],['!test1 && !test2 && !test3',statement4]];
for (let index of ){
for (let of arr){
if (condition){
cases.push()
}
}
}
/*
=> [
,
,
,
]
*/
最终会生成一个索引与语句对应的数组,可以用来生成最终的switch case语句。
所有最终的改写过程是先将索引数组写到最终调用位置,然后通过递归if语句获取条件和对应的语句,然后通过索引数组进行遍历生成索引于语句对应的数组,最后转化为switch语句;
实现代码如下:
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;
const t = require('@babel/types');
const fs = require('fs');
const start = Date.now();
const jscode = fs.readFileSync("./input.js", {
encoding: "utf-8"
});
let code;
let ast = parser.parse(jscode);
// ---------------------------还原数组 如果第一句是数组
`
(function() {
var _$rD = 0
, _$zG = $_ts.scj
, _$cj = [[],[],[],[],[]];
.........
})()
`
traverse(ast,{
Program(path){
if (!path.get('body.0')?.get('expression')?.get('callee')?.get('body')?.get('body.0')?.isVariableDeclaration()) return;
let func_statement_path_1 = path.get('body').get('expression.callee.body.body');
func_statement_path_1.get('declarations').forEach(function (v) {
if (v.get('init').isArrayExpression()){
let binding = func_statement_path_1.scope.getBinding(v.node.id.name);
let arr = v.node.init.elements;
binding && binding.constantViolations.length == 0 && binding.referencePaths.forEach(function (vv) {
if (vv.parentPath.isMemberExpression()
&& vv.parent.object.name == v.node.id.name
&& vv.parentPath.get('property').isNumericLiteral()
){
vv.parentPath.replaceWith(arr);
}
})
}
});
}
});
// ---------------------------还原控制流
traverse(ast, {// 给if语句把 {} 都加上
IfStatement(path) {
let {consequent, alternate} = path.node;
if (consequent && !t.isBlockStatement(consequent)) {
path.get('consequent').replaceWith(t.BlockStatement());
}
if (alternate && !t.isBlockStatement(alternate)) {
path.get('alternate').replaceWith(t.BlockStatement());
}
}
});
function judge_include_identifier(path, idt) {
// path:if语句的test节点的path对象,用于判断 “判断条件中”是否有不是指定的标识符,避免遍历非控制流的if语句
// idt 标识符
let flag = true;
// 如果test节点有控制流的标识符,返回true,否则返回false
path.traverse({
Identifier(path_) {
if (path_.node.name != idt) {
flag = false
}
}
})
return flag
}
function parseIfStatement(path, test_li, idt, li) {
// path: if语句的path对象, test_li: 用来当前存储条件,idt:控制流中条件中的标识符 ,li:存储结果
let {consequent, alternate, test} = path.node;
// -----------------------------递归 test==true--------------------------------------------
// if成立时的结果存在 且为{}语句 且{}中是if语句 且{}中if语句的条件包含指定标识符【递归条件】
if (consequent && t.isBlockStatement(consequent)
&& t.isIfStatement(consequent.body)
&& judge_include_identifier(path.get('consequent.body').get('test'), idt)
) {
// 符合条件就进入子if语句中,将当前的条件加入到数组【是if==true的语句,直接加】
test_li.push(test);
parseIfStatement(path.get('consequent.body'), test_li, idt, li);
test_li.pop(); // 子语句遍历结束后弹出条件,保持状态。
} else {
// 没有控制流,递归结束
test_li.push(test);
// 根据数组构造判断条件的字符串
let condition = test_li.map(function (v) {
return generator(v).code
}).join(' && ');
test_li.pop();
let case1 = path.node.consequent.body;
li = case1;
}
// ----------------------递归 test==false 即 else中的语句---------------------------------------
// else 语句存在 且 是{}语句 且{}中是if语句 且{}中if语句的条件包含指定标识符【递归条件】
if (alternate && t.isBlockStatement(alternate)
&& t.isIfStatement(alternate.body)
&& judge_include_identifier(path.get('alternate.body').get('test'), idt)
) {
// else中条件对应 !test,所以添加的条件要取反
test_li.push(t.UnaryExpression('!', test));
parseIfStatement(path.get('alternate.body'), test_li, idt, li);
test_li.pop();
} else {
// 在else中添加的条件要取反
test_li.push(t.UnaryExpression('!', test));
let condition = test_li.map(function (v) {
return generator(v).code
}).join(' && ');
test_li.pop();
let case1 = path.node.alternate.body;
li = case1;
}
}
traverse(ast, {
WhileStatement(path) {
let while_body = path?.get('body')?.get('body');
if (t.isNumericLiteral(path.node.test, {value: 1})
&& while_body && while_body.length == 2
&& while_body.isExpressionStatement()
&& while_body.isIfStatement()
) { // 判断是否符合控制流特征
let condition_identifier = while_body.node.expression.left.name; // 获取标识符名
let if_statement = while_body;
let li = {};
parseIfStatement(if_statement, [], condition_identifier, li);// 递归遍历if
let cases = [];
// 获取索引数组
let var_statement_path = path.getSibling(path.key-1);
// 根据索引数组还原
// 判断是否包含数组
if (var_statement_path.isVariableDeclaration()
&& var_statement_path.get('declarations').at(-1)?.get('init')?.isArrayExpression()) {
let index_arr = eval(var_statement_path.get('declarations').at(-1).get('init')+'');
index_arr = Array.from(new Set(index_arr)).sort(function (a,b){return a-b});
for (let i of index_arr) {
let flag = false;
for (var of Object.entries(li)) {
eval(`
let ${condition_identifier} = i;
if (${test}) {
res_statement = statement;
flag = true;
}
`);
if (flag) break;
}
if (!flag) {
console.error('没有匹配的值:', i);
throw Error('没有匹配条件');
}
res_statement = Array.from(res_statement);// 浅copy一下,避免多个index进入同一个控制流时会添加多个break,比较难看
res_statement.push(t.BreakStatement());
cases.push(t.SwitchCase(t.valueToNode(parseInt(i)), res_statement));
}
}
else {
console.log('没有获取到索引数组,结束!!!!!')
}
let sw = t.SwitchStatement(while_body.node.expression.left, cases);
if_statement.replaceWith(sw);
}
}
});
code = generator(ast).code;
fs.writeFileSync('./output.js', code,{encoding:'utf-8'});
console.log('耗时=》', Date.now() - start);
以上代码只实现了索引大数组在最外层自执行函数第一句的情况,
向vm中的代码如果是这种形式的可以手动把索引大数字替换掉window.变量名 ;
如果索引数组是以自执行函数的参数的形式穿入的,也可以改成这段代码支持的形式,或者自己写解析逻辑。
某普的控制流改写如下:
某标控制流改写如下:
ps: 改写思路应该是没什么问题?如果改写结果不对可以自己改改看代码{:1_918:}
改写了其实也没什么用,也就少按几次f11,哈哈:lol
原本到上面是已经结束了的,但写文章的时候又想到另一种解法,而且还更简单,速度更快,所以也一起写了
开始依旧是要还原索引数组的;
还是开始的伪代码:
_$OR = ;
while (1) {
_$r4 = _$OR;
if (test1) {
statement1
} else if (test2) {
statement2
} else if (test3) {
statement3
} else {
statement4
}
}
上面是通过递归获取每个语句的完整条件,这里依旧是递归遍历if语句,将代码改写。改写为将每个语句赋予一个唯一的id,然后将id和对应的语句存起来,将if控制流改写为索引与id对应的形式
生成伪代码如下:
if (test1) {
if (test2) {
index_obj = 0;
} else {
if (test3) {
index_obj = 1;
} else {
if (test4) {
index_obj = 2;
} else {
index_obj = 3;
}
}
}
} else {
index_obj = 4;
}
/*
var li = {
0:statement1,
1:statement2,
2:statement3,
...
}
*/
然后这个if语句变成了可以执行的形式,然后就可以通过索引数组遍历,获取索引与id的对应关系;
伪代码如下:
var index_obj = {};
for (let condition of ){
if (test1) {
if (test2) {
index_obj = 0;
} else {
if (test3) {
index_obj = 1;
} else {
if (test4) {
index_obj = 2;
} else {
index_obj = 3;
}
}
}
} else {
index_obj = 4;
}
}
/*
=>{
a:0,
b:1,
c:2,
d:3,
e:4
}
*/
现在有了索引与id,id与语句的对应关系,就可以获取到id与语句的对应关系生成对应的switch case 语句了。
实现代码用法和上面相同,代码可能有bug,如果用可以自己再看看。思路应该是可行的
实现代码如下:
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;
const t = require('@babel/types');
const fs = require('fs');
const start = Date.now();
const jscode = fs.readFileSync("./input.js", {
encoding: "utf-8"
});
let code;
let ast = parser.parse(jscode);
// ---------------------------还原数组 如果第一句是数组
traverse(ast,{
Program(path){
if (!path.get('body.0')?.get('expression')?.get('callee')?.get('body')?.get('body.0')?.isVariableDeclaration()) return;
let func_statement_path_1 = path.get('body').get('expression.callee.body.body');
func_statement_path_1.get('declarations').forEach(function (v) {
if (v.get('init').isArrayExpression()){
let binding = func_statement_path_1.scope.getBinding(v.node.id.name);
let arr = v.node.init.elements;
binding && binding.constantViolations.length == 0 && binding.referencePaths.forEach(function (vv) {
if (vv.parentPath.isMemberExpression()
&& vv.parent.object.name == v.node.id.name
&& vv.parentPath.get('property').isNumericLiteral()
){
vv.parentPath.replaceWith(arr);
}
})
}
});
}
})
// ---------------还原控制流
traverse(ast, {
IfStatement(path) {
let {consequent, alternate} = path.node;
if (consequent && !t.isBlockStatement(consequent)) {
path.get('consequent').replaceWith(t.BlockStatement());
}
if (alternate && !t.isBlockStatement(alternate)) {
path.get('alternate').replaceWith(t.BlockStatement());
}
}
})
function judge_include_identifier(path, idt) {
// path:if语句的test节点的path对象,用于判断 “判断条件中”是否有不是指定的标识符,避免遍历非控制流的if语句
// idt 标识符
let flag = true;
// 如果test节点有控制流的标识符,返回true,否则返回false
path.traverse({
Identifier(path_) {
if (path_.node.name != idt) {
flag = false
}
}
})
return flag
}
function parseIfStatement(path, id_arr, idt, li) {
// path: if语句的path对象, test_li: 用来当前存储条件,idt:控制流中条件中的标识符
let {consequent, alternate, test} = path.node;
// -----------------------------递归 test==true--------------------------------------------
// if成立时的结果存在 且为{}语句 且{}中是if语句 且{}中if语句的条件包含指定标识符【递归条件】
if (consequent && t.isBlockStatement(consequent)
&& t.isIfStatement(consequent.body)
&& judge_include_identifier(path.get('consequent.body').get('test'), idt)
) {
parseIfStatement(path.get('consequent.body'), id_arr, idt, li);
} else {
// 没有控制流,递归结束
let case1 = path.node.consequent.body;
let id_ = id_arr++;
li = case1;
// index_obj = id_;
let replace_statement = t.BlockStatement([
t.ExpressionStatement(t.AssignmentExpression('=', t.MemberExpression(t.Identifier('index_obj'),t.Identifier(idt), true),t.NumericLiteral(id_)))
]);
path.get('consequent').replaceWith(replace_statement);
}
// ----------------------递归 test==false 即 else中的语句---------------------------------------
// else 语句存在 且 是{}语句 且{}中是if语句 且{}中if语句的条件包含指定标识符【递归条件】
if (alternate && t.isBlockStatement(alternate)
&& t.isIfStatement(alternate.body)
&& judge_include_identifier(path.get('alternate.body').get('test'), idt)
) {
parseIfStatement(path.get('alternate.body'), id_arr, idt, li);
} else {
let case1 = path.node.alternate.body;
let id_ = id_arr++;
li = case1;
// index_obj = id_; 以index作为键,会有多个index进入到同一个语句中
let replace_statement = t.BlockStatement([
t.ExpressionStatement(t.AssignmentExpression('=', t.MemberExpression(t.Identifier('index_obj'),t.Identifier(idt), true),t.NumericLiteral(id_)))
]);
path.get('alternate').replaceWith(replace_statement);
}
}
traverse(ast, {
WhileStatement(path) {
let while_body = path?.get('body')?.get('body');
if (t.isNumericLiteral(path.node.test, {value: 1})
&& while_body && while_body.length == 2
&& while_body.isExpressionStatement()
&& while_body.isIfStatement()
) {
let condition_identifier = while_body.node.expression.left.name;
let if_statement = while_body;
let li = {};
let id_arr = ;
parseIfStatement(if_statement, id_arr, condition_identifier, li);
let cases = [];
let index_obj = {};
let var_statement_path = path.getSibling(path.key-1);
if (var_statement_path.isVariableDeclaration() // 使用索引数组遍历
&& var_statement_path.get('declarations').at(-1)?.get('init')?.isArrayExpression()) {
let index_arr = eval(var_statement_path.get('declarations').at(-1).get('init') + '');
index_arr = Array.from(new Set(index_arr)).sort(function (a, b) {
return a - b
}); // 去重 排序
eval(`
for (let ${condition_identifier} of index_arr) {
${if_statement + ''}
}
`);
}else {
console.log('没有获取到索引数组,结束!!!!!');
}
for (let of Object.entries(index_obj).sort(function (a,b){return a-b})){
let res_statement = [...li];
res_statement.push(t.BreakStatement());
cases.push(t.SwitchCase(t.valueToNode(parseInt(index)), res_statement));
}
let sw = t.SwitchStatement(while_body.node.expression.left, cases);
if_statement.replaceWith(sw);
}
}
})
code = generator(ast).code;
fs.writeFileSync('./output.js', code, {encoding:'utf-8'});
console.log('耗时=》', Date.now() - start)
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;
const t = require('@babel/types');
const fs = require('fs');
const start = Date.now();
const jscode = fs.readFileSync("./input.js", {
encoding: "utf-8"
});
let code;
let ast = parser.parse(jscode);
// ---------------------------还原数组 如果第一句是数组
(function() { var _$rD = 0 , _$zG = $_ts.scj , _$cj = [[],[],[],[],[]]; ......... })()
traverse(ast,{
Program(path){
if (!path.get('body.0')?.get('expression')?.get('callee')?.get('body')?.get('body.0')?.isVariableDeclaration()) return;
let func_statement_path_1 = path.get('body').get('expression.callee.body.body');
func_statement_path_1.get('declarations').forEach(function (v) {
if (v.get('init').isArrayExpression()){
let binding = func_statement_path_1.scope.getBinding(v.node.id.name);
let arr = v.node.init.elements;
binding && binding.constantViolations.length == 0 && binding.referencePaths.forEach(function (vv) {
if (vv.parentPath.isMemberExpression()
&& vv.parent.object.name == v.node.id.name
&& vv.parentPath.get('property').isNumericLiteral()
){
vv.parentPath.replaceWith(arr);
}
})
}
});
}
});
// ---------------------------还原控制流
traverse(ast, { // 给if语句把 {} 都加上
IfStatement(path) {
let {consequent, alternate} = path.node;
if (consequent && !t.isBlockStatement(consequent)) {
path.get('consequent').replaceWith(t.BlockStatement());
}
if (alternate && !t.isBlockStatement(alternate)) {
path.get('alternate').replaceWith(t.BlockStatement());
}
}
});
function judge_include_identifier(path, idt) {
// path:if语句的test节点的path对象,用于判断 “判断条件中”是否有不是指定的标识符,避免遍历非控制流的if语句
// idt 标识符
let flag = true;
// 如果test节点有控制流的标识符,返回true,否则返回false
path.traverse({
Identifier(path_) {
if (path_.node.name != idt) {
flag = false
}
}
})
return flag
}
function parseIfStatement(path, test_li, idt, li) {
// path: if语句的path对象, test_li: 用来当前存储条件,idt:控制流中条件中的标识符 ,li:存储结果
let {consequent, alternate, test} = path.node;
// -----------------------------递归 test==true--------------------------------------------
// if成立时的结果存在 且为{}语句 且{}中是if语句 且{}中if语句的条件包含指定标识符【递归条件】
if (consequent && t.isBlockStatement(consequent)
&& t.isIfStatement(consequent.body)
&& judge_include_identifier(path.get('consequent.body').get('test'), idt)
) {
// 符合条件就进入子if语句中,将当前的条件加入到数组【是if==true的语句,直接加】
test_li.push(test);
parseIfStatement(path.get('consequent.body'), test_li, idt, li);
test_li.pop(); // 子语句遍历结束后弹出条件,保持状态。
} else {
// 没有控制流,递归结束
test_li.push(test);
// 根据数组构造判断条件的字符串
let condition = test_li.map(function (v) {
return generator(v).code
}).join(' && ');
test_li.pop();
let case1 = path.node.consequent.body;
li = case1;
}
// ----------------------递归 test==false 即 else中的语句---------------------------------------
// else 语句存在 且 是{}语句 且{}中是if语句 且{}中if语句的条件包含指定标识符【递归条件】
if (alternate && t.isBlockStatement(alternate)
&& t.isIfStatement(alternate.body)
&& judge_include_identifier(path.get('alternate.body').get('test'), idt)
) {
// else中条件对应 !test,所以添加的条件要取反
test_li.push(t.UnaryExpression('!', test));
parseIfStatement(path.get('alternate.body'), test_li, idt, li);
test_li.pop();
} else {
// 在else中添加的条件要取反
test_li.push(t.UnaryExpression('!', test));
let condition = test_li.map(function (v) {
return generator(v).code
}).join(' && ');
test_li.pop();
let case1 = path.node.alternate.body;
li = case1;
}
}
traverse(ast, {
WhileStatement(path) {
let while_body = path?.get('body')?.get('body');
if (t.isNumericLiteral(path.node.test, {value: 1})
&& while_body && while_body.length == 2
&& while_body.isExpressionStatement()
&& while_body.isIfStatement()
) { // 判断是否符合控制流特征
let condition_identifier = while_body.node.expression.left.name; // 获取标识符名
let if_statement = while_body;
let li = {};
parseIfStatement(if_statement, [], condition_identifier, li); // 递归遍历if
scheme
Copy
let cases = [];
// 获取索引数组
let var_statement_path = path.getSibling(path.key-1);
// 根据索引数组还原
// 判断是否包含数组
if (var_statement_path.isVariableDeclaration()
&& var_statement_path.get('declarations').at(-1)?.get('init')?.isArrayExpression()) {
let index_arr = eval(var_statement_path.get('declarations').at(-1).get('init')+'');
index_arr = Array.from(new Set(index_arr)).sort(function (a,b){return a-b});
for (let i of index_arr) {
let flag = false;
for (var of Object.entries(li)) {
eval(`
let ${condition_identifier} = i;
if (${test}) {
res_statement = statement;
flag = true;
}
`);
if (flag) break;
}
if (!flag) {
console.error('没有匹配的值:', i);
throw Error('没有匹配条件');
}
res_statement = Array.from(res_statement);// 浅copy一下,避免多个index进入同一个控制流时会添加多个break,比较难看
res_statement.push(t.BreakStatement());
cases.push(t.SwitchCase(t.valueToNode(parseInt(i)), res_statement));
}
}
else {
console.log('没有获取到索引数组,结束!!!!!')
}
let sw = t.SwitchStatement(while_body.node.expression.left, cases);
if_statement.replaceWith(sw);
}
}
});
code = generator(ast).code;
fs.writeFileSync('./output.js', code,{encoding:'utf-8'});
console.log('耗时=》', Date.now() - start);
从代码分析来看,这段代码应该可以正常运行,但有一些注意事项:
确保已经安装了所有必要的依赖库。你可以使用 npm 或 yarn 来安装这些库:
npm install @babel/parser @babel/traverse @babel/generator @babel/types
```
确保在与此脚本相同的目录下存在一个名为 input.js 的文件,因为这段代码将从该文件中读取 JavaScript 代码。
在运行此脚本之前,请确保你了解它的功能,因为它将读取 input.js 文件并生成一个新的名为 output.js 的文件。如果你已经有一个名为 output.js 的文件,它将被覆盖。此外,代码中的 eval() 函数可能会导致安全问题,特别是当处理不受信任的输入时。请确保你理解 eval() 的潜在风险。
如果你已经满足了上述条件,那么这段代码应该可以正常运行。请记住,这段代码的作用是根据 input.js 文件中的代码,对其进行解析、还原和优化,然后将结果输出到 output.js 文件中。
控制流可以补环境解决的吗? 可以把还原前后的代码发上来嘛? codebat 发表于 2023-5-10 21:05
控制流可以补环境解决的吗?
不太懂你的意思,补环境直接无视混淆就好了 悦来客栈的老板 发表于 2023-5-11 08:36
可以把还原前后的代码发上来嘛?
传了。蔡老板指点一下
看一下这个行不行 建议直接copy给ChatGPT哈哈哈哈 这个有点难啊感觉,不知道能不能学会
页:
[1]
2