lsq665 发表于 2023-5-10 17:40

某数控制流改写

本帖最后由 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)


law.liu 发表于 2023-5-11 10:15

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

可以把还原前后的代码发上来嘛?

lsq665 发表于 2023-5-11 09:11

lsq665 发表于 2023-5-11 09:12

codebat 发表于 2023-5-10 21:05
控制流可以补环境解决的吗?

不太懂你的意思,补环境直接无视混淆就好了

lsq665 发表于 2023-5-11 09:13

悦来客栈的老板 发表于 2023-5-11 08:36
可以把还原前后的代码发上来嘛?

传了。蔡老板指点一下

weishi9527 发表于 2023-5-11 10:09

看一下这个行不行

Labraff 发表于 2023-5-11 10:29

建议直接copy给ChatGPT哈哈哈哈

darenxu123 发表于 2023-5-12 11:37

这个有点难啊感觉,不知道能不能学会
页: [1] 2
查看完整版本: 某数控制流改写