某数控制流改写

查看 49|回复 1
作者:lsq665   
仅供学习,不得用于商业用途,侵删。
某数控制流有好多种,例如


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)

语句, 递归

codebat   

控制流可以补环境解决的吗?
您需要登录后才可以回帖 登录 | 立即注册

返回顶部