吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3895|回复: 14
收起左侧

[Web逆向] 某数控制流改写

[复制链接]
lsq665 发表于 2023-5-10 17:40
本帖最后由 lsq665 于 2023-8-4 11:54 编辑

仅供学习,不得用于商业用途,侵删。

某数控制流有好多种,例如
Quicker_20230510_153811.png
Quicker_20230510_153637.png
虽然判断条件的形式不同,但整体结构都基本相同,所以可以对这种结构统一进行改写
,伪代码如下:
[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
向vm中的代码如果是这种形式的可以手动把索引大数字替换掉window.变量名 ;
如果索引数组是以自执行函数的参数的形式穿入的,也可以改成这段代码支持的形式,或者自己写解析逻辑。

某普的控制流改写如下:

某普控制流

某普控制流

某标控制流改写如下:

某标控制流

某标控制流




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)


控制流还原.zip

181.1 KB, 下载次数: 12, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 10威望 +2 吾爱币 +112 热心值 +8 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
onlywey + 1 + 1 用心讨论,共获提升!
alanhays + 2 + 1 用心讨论,共获提升!
涛之雨 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
悦来客栈的老板 + 1 用心讨论,共获提升!
三滑稽甲苯 + 1 + 1 用心讨论,共获提升!
peiwithhao + 3 + 1 很强,但是我不懂
mn126kk72 + 1 + 1 用心讨论,共获提升!
helian147 + 1 + 1 热心回复!
RickSanchez + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

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')[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

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 [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);


从代码分析来看,这段代码应该可以正常运行,但有一些注意事项:

确保已经安装了所有必要的依赖库。你可以使用 npm 或 yarn 来安装这些库:
[Asm] 纯文本查看 复制代码
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
1.zip (390.33 KB, 下载次数: 30)
 楼主| 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
这个有点难啊感觉,不知道能不能学会
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-22 12:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表