某数控制流有好多种,例如
Quicker_20230510_153811.png (25.93 KB, 下载次数: 0)
下载附件
2023-5-10 15:39 上传
Quicker_20230510_153637.png (23.94 KB, 下载次数: 0)
下载附件
2023-5-10 15:39 上传
虽然判断条件的形式不同,但整体结构都基本相同,所以可以对这种结构统一进行改写
,伪代码如下:
[JavaScript] 纯文本查看 复制代码
_$OR = [1,2,3,4];
while (1) {
_$r4 = _$OR[_$NE++];
if (test1) {
statement1
} else if (test2) {
statement2
} else if (test3) {
statement3
} else {
statement4
}
}
上面的代码可以改写为另一种形式,伪代码如下:
[JavaScript] 纯文本查看 复制代码
_$OR = [1,2,3,4];
while (1) {
_$r4 = _$OR[_$NE++];
if (test1) statement1;
if (!test1 && test2) statement2;
if (!test1 && !test2 && test3) statement3;
if (!test1 && !test2 && !test3) statement4;
由于这些if条件是互斥的,一个索引数组的值只会进入到一个if语句中,所以如果将这些if的条件收集起来进行遍历,就可以得到每个索引对应的语句,伪代码如下:
[JavaScript] 纯文本查看 复制代码
let cases = [];
let arr = [['test1',statement1],['!test1 && test2',statement2],['!test1 && !test2 && test3',statement3],['!test1 && !test2 && !test3',statement4]];
for (let index of [1,2,3,4]){
for (let [condition,statement] of arr){
if (condition){
cases.push([index,statement])
}
}
}
/*
=> [
[1,statement1],
[2,statement2],
[3,statement3],
[4,statement4]
]
*/
最终会生成一个索引与语句对应的数组,可以用来生成最终的switch case语句。
所有最终的改写过程是先将索引数组写到最终调用位置,然后通过递归if语句获取条件和对应的语句,然后通过索引数组进行遍历生成索引于语句对应的数组,最后转化为switch语句;
实现代码如下:
[JavaScript] 纯文本查看 复制代码
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')[0].get('expression.callee.body.body')[0];
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[vv.parent.property.value]);
}
})
}
});
}
});
// ---------------------------还原控制流
traverse(ast, { // 给if语句把 {} 都加上
IfStatement(path) {
let {consequent, alternate} = path.node;
if (consequent && !t.isBlockStatement(consequent)) {
path.get('consequent').replaceWith(t.BlockStatement([consequent]));
}
if (alternate && !t.isBlockStatement(alternate)) {
path.get('alternate').replaceWith(t.BlockStatement([alternate]));
}
}
});
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[0])
&& judge_include_identifier(path.get('consequent.body')[0].get('test'), idt)
) {
// 符合条件就进入子if语句中,将当前的条件加入到数组【是if==true的语句,直接加】
test_li.push(test);
parseIfStatement(path.get('consequent.body')[0], 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[condition] = case1;
}
// ----------------------递归 test==false 即 else中的语句---------------------------------------
// else 语句存在 且 是{}语句 且{}中是if语句 且{}中if语句的条件包含指定标识符【递归条件】
if (alternate && t.isBlockStatement(alternate)
&& t.isIfStatement(alternate.body[0])
&& judge_include_identifier(path.get('alternate.body')[0].get('test'), idt)
) {
// else中条件对应 !test,所以添加的条件要取反
test_li.push(t.UnaryExpression('!', test));
parseIfStatement(path.get('alternate.body')[0], 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[condition] = 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[0].isExpressionStatement()
&& while_body[1].isIfStatement()
) { // 判断是否符合控制流特征
let condition_identifier = while_body[0].node.expression.left.name; // 获取标识符名
let if_statement = while_body[1];
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 [test, statement] 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[0].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);
以上代码只实现了索引大数组在最外层自执行函数第一句的情况,
Quicker_20230510_165233.png (15.18 KB, 下载次数: 0)
下载附件
2023-5-10 16:53 上传
向vm中的代码如果是这种形式的可以手动把索引大数字替换掉window.变量名 ;
如果索引数组是以自执行函数的参数的形式穿入的,也可以改成这段代码支持的形式,或者自己写解析逻辑。
某普的控制流改写如下:
Quicker_20230510_170123.png (66.67 KB, 下载次数: 0)
下载附件
某普控制流
2023-5-10 17:05 上传
某标控制流改写如下:
Quicker_20230510_170448.png (65.63 KB, 下载次数: 0)
下载附件
某标控制流
2023-5-10 17:05 上传
ps: 改写思路应该是没什么问题?如果改写结果不对可以自己改改看代码{:1_918:}
改写了其实也没什么用,也就少按几次f11,哈哈:lol
原本到上面是已经结束了的,但写文章的时候又想到另一种解法,而且还更简单,速度更快,所以也一起写了
开始依旧是要还原索引数组的;
还是开始的伪代码:
[JavaScript] 纯文本查看 复制代码
_$OR = [1,2,3,4];
while (1) {
_$r4 = _$OR[_$NE++];
if (test1) {
statement1
} else if (test2) {
statement2
} else if (test3) {
statement3
} else {
statement4
}
}
上面是通过递归获取每个语句的完整条件,这里依旧是递归遍历if语句,将代码改写。改写为将每个语句赋予一个唯一的id,然后将id和对应的语句存起来,将if控制流改写为索引与id对应的形式
生成伪代码如下:
[JavaScript] 纯文本查看 复制代码
if (test1) {
if (test2) {
index_obj[_$_o] = 0;
} else {
if (test3) {
index_obj[_$_o] = 1;
} else {
if (test4) {
index_obj[_$_o] = 2;
} else {
index_obj[_$_o] = 3;
}
}
}
} else {
index_obj[_$_o] = 4;
}
/*
var li = {
0:statement1,
1:statement2,
2:statement3,
...
}
*/
然后这个if语句变成了可以执行的形式,然后就可以通过索引数组遍历,获取索引与id的对应关系;
伪代码如下:
[JavaScript] 纯文本查看 复制代码
var index_obj = {};
for (let condition of [a,b,c,d,e]){
if (test1) {
if (test2) {
index_obj[_$_o] = 0;
} else {
if (test3) {
index_obj[_$_o] = 1;
} else {
if (test4) {
index_obj[_$_o] = 2;
} else {
index_obj[_$_o] = 3;
}
}
}
} else {
index_obj[_$_o] = 4;
}
}
/*
=>{
a:0,
b:1,
c:2,
d:3,
e:4
}
*/
现在有了索引与id,id与语句的对应关系,就可以获取到id与语句的对应关系生成对应的switch case 语句了。
实现代码用法和上面相同,代码可能有bug,如果用可以自己再看看。思路应该是可行的
实现代码如下:
[JavaScript] 纯文本查看 复制代码
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')[0].get('expression.callee.body.body')[0];
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[vv.parent.property.value]);
}
})
}
});
}
})
// ---------------还原控制流
traverse(ast, {
IfStatement(path) {
let {consequent, alternate} = path.node;
if (consequent && !t.isBlockStatement(consequent)) {
path.get('consequent').replaceWith(t.BlockStatement([consequent]));
}
if (alternate && !t.isBlockStatement(alternate)) {
path.get('alternate').replaceWith(t.BlockStatement([alternate]));
}
}
})
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[0])
&& judge_include_identifier(path.get('consequent.body')[0].get('test'), idt)
) {
parseIfStatement(path.get('consequent.body')[0], id_arr, idt, li);
} else {
// 没有控制流,递归结束
let case1 = path.node.consequent.body;
let id_ = id_arr[0]++;
li[id_] = case1;
// index_obj[index] = 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[0])
&& judge_include_identifier(path.get('alternate.body')[0].get('test'), idt)
) {
parseIfStatement(path.get('alternate.body')[0], id_arr, idt, li);
} else {
let case1 = path.node.alternate.body;
let id_ = id_arr[0]++;
li[id_] = case1;
// index_obj[index] = 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[0].isExpressionStatement()
&& while_body[1].isIfStatement()
) {
let condition_identifier = while_body[0].node.expression.left.name;
let if_statement = while_body[1];
let li = {};
let id_arr = [0];
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 [index,id_] of Object.entries(index_obj).sort(function (a,b){return a[0]-b[0]})){
let res_statement = [...li[id_]];
res_statement.push(t.BreakStatement());
cases.push(t.SwitchCase(t.valueToNode(parseInt(index)), res_statement));
}
let sw = t.SwitchStatement(while_body[0].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)