zoukankan      html  css  js  c++  java
  • js代码反混淆之ast的使用

    代码

    const parser = require("@babel/parser");
    const traverse = require("@babel/traverse").default;
    const t = require("@babel/types");
    const generator = require("@babel/generator").default;
    
    let origin_code = `
    function  _0x506bbd(v){
       const a= !![]
       console.log(a);
       return v.length
    }
     console.log(_0x506bbd('x69x6ex64u0435x78x4fx66'));
    `
    let ast = parser.parse(origin_code);
    pre_ast = ast.program.body.slice(0, 1)
    const gen = generator(ast, {
        // 禁止自动格式化(针对反调试)
        compact: true
    })
    eval(gen.code)
    
    function toSource(_ast) {
        let {code} = generator(_ast, {
            // 是否格式化
            compact: false,
            jsescOption: {
                // 自动转义
                minimal: true,
            }
        });
        return code.replace(/!![]/g, 'true').replace(/![]/g, 'false')
    }
    
    function getvalue(path) {
        const node = path.node
        if (t.isStringLiteral(node)) {
            if (node && node.exact) {
                delete path.node.exact
            }
        } else if (t.isCallExpression(node)) {
            const {callee} = path.node;
            if (t.isMemberExpression(callee) &&
                callee.object.name === "console" &&
                callee.property.name === "log") {
                node.arguments.map(item => {
                    if (t.isCallExpression(item)) {
                        let _value = item.arguments
                        let _key = item.callee.name
                        const func = eval(_key)
                        let new_value = func(..._value.map(v => v.value))
                        let value_type = Object.prototype.toString.call(new_value)
                        let new_node;
                        if (value_type === "[object Number]") {
                            new_node = t.NumericLiteral(new_value)
                        } else if (value_type === "[object String]") {
                            new_node = t.stringLiteral(new_value)
                        }
                        path.node.arguments[0] = new_node
                    }
                })
            }
        }
    
    
    }
    
    function traverse_ast(ast, opts) {
        traverse(ast, opts);
        return ast
    }
    
    let step1_ast = traverse_ast(ast, {StringLiteral: getvalue})
    
    let step2_ast = traverse_ast(step1_ast, {CallExpression: getvalue})
    
    let new_code = toSource(step2_ast)
    console.log("before: =============")
    console.log(origin_code)
    console.log("after: =============")
    console.log(new_code)
    
    

    目标结果:

    true
    7
    true
    before: =============
    
    function  _0x506bbd(v){
       const a= !![]
       console.log(a);
       return v.length
    }
     console.log(_0x506bbd('indеxOf'));
    
    after: =============
    function _0x506bbd(v) {
      const a = true;
      console.log(a);
      return v.length;
    }
    
    

    下面是一个完整的示例

    //解密替换字符串 --> 解耦 object --> 去控制流。
    const parser = require("@babel/parser");
    const traverse = require("@babel/traverse").default;
    const t = require("@babel/types");
    const generator = require("@babel/generator").default;
      
    const fs = require('fs');
      
    function replace_ugly_code(path) {
        let arr_path = path.get('body.0');
        let code = arr_path.toString();
     
        let shift_path = path.get('body.1');
        let callee_path = shift_path.get('expression.callee');
        let second_arg_node = callee_path.get('params.1').node;
          
        let first_body = callee_path.get('body.body.0');
        let call_fun = first_body.node.declarations[0].id
          
        var all_next_siblings = first_body.getAllNextSiblings();
        all_next_siblings.forEach(next_sibling => {
            next_sibling.remove();
        });
          
        first_body.insertBefore(t.ExpressionStatement(t.UpdateExpression("++", second_arg_node)));
        first_body.insertAfter(t.ExpressionStatement(t.callExpression(call_fun, [second_arg_node])));
          
        code += '!' + shift_path.toString();
         
        let call_path = path.get('body.2');
        let call_name = call_path.node.declarations[0].id.name;
        call_path.traverse({
            AssignmentExpression(_path) {
                let left = _path.get('left');
                let left_code = left.toString();
                 
                let right = _path.get('right');
                let right_code = right.toString();
                 
                if (right_code.indexOf(call_name) === -1 ||
                    right_code.indexOf(left_code) === -1 ) 
                {
                    return;
                }
                 
                const if_parent_path = _path.findParent(p => {
                    return p.isIfStatement();
                });
                if_parent_path && if_parent_path.replaceWith(_path.node);
            },
        })
         
        code += call_path.toString();
        return {call_name,code};
    }
     
    const delete_extra = 
    {
        StringLiteral:function(path)
        {
          delete path.node.extra;
        },
    }
     
     
    function replace_simple_code(path) {
         
        traverse(path.node,delete_extra)//防止所有字符串以十六进制形式展现导致查找失败。
           
        let source_code = path.toString();
        if(source_code.indexOf('removeCookie') !== -1) 
        {
            var {call_name,code} = replace_ugly_code(path);
        } 
        else
        {
            let arr_path = path.get('body.0');
            var code = arr_path.toString();
              
            let shift_path = path.get('body.1');
            code += '!' + shift_path.toString();
              
            let call_path = path.get('body.2');
            var call_name = call_path.get('declarations.0.id').toString();
            code += call_path.toString();
        }
          
        eval(code);
        
        
        let can_be_delete = true;
          
        path.traverse({
            CallExpression: function(_path) {
                let callee = _path.get('callee');
                if(callee.toString() !== call_name)
                    return;
                try
                {
                    let value = eval(_path.toString());
                    //console.log(value);
                    value !== undefined && _path.replaceWith(t.valueToNode(value))
                }
                catch(e)
                {
                    can_be_delete = false;
                }
            },
        });
         
        for (let i=0 ;can_be_delete && i<3; i++)
        {
            path.get('body.0').remove();
        }   
    }
    
    
    //解密字符串
    const decode_str = {
        "Program"(path)
        {
            replace_simple_code(path)
        },
         
    };
    
    
    
    //还原object
    const decode_object = {
    	VariableDeclarator(path)
    	{
    		const {id,init} = path.node;
    		if (!t.isObjectExpression(init) || init.properties.length == 0) return;
    
    		let name = id.name;
    		let scope = path.scope;
    		
    		for (const property of init.properties)
    		{
    			let key   = property.key.value;
    			if (key.length !== 5)
    			{
    				return;
    			} 
    			let value = property.value;
    			
    			if (t.isLiteral(value))
    			{
    				scope.traverse(scope.block,{
    					MemberExpression(_path)
    					{
    						let _node = _path.node;
    						if (!t.isIdentifier(_node.object,{name:name})) return;
    						if (!t.isLiteral(_node.property, {value:key})) return;
    						_path.replaceWith(value);
    					},
    				})
    			}
    			else if (t.isFunctionExpression(value))
    			{
    				let ret_state = value.body.body[0];
    				if(!t.isReturnStatement(ret_state)) continue;
    				scope.traverse(scope.block,{
    					CallExpression: function(_path) {
    						let {callee,arguments} = _path.node;
    						if (!t.isMemberExpression(callee)) return;
    						
    						if (!t.isIdentifier(callee.object,{name:name})) return;
    						if (!t.isLiteral(callee.property, {value:key})) return;
    						
    						if (t.isCallExpression(ret_state.argument) && arguments.length > 0) {
    							_path.replaceWith(t.CallExpression(arguments[0], arguments.slice(1)));
                }
    						else if (t.isBinaryExpression(ret_state.argument) && arguments.length === 2) 
                {
                	let replace_node = t.BinaryExpression(ret_state.argument.operator, arguments[0], arguments[1]);
                	_path.replaceWith(replace_node);
                }		
    						else if (t.isLogicalExpression(ret_state.argument) && arguments.length === 2) 
                {
                	let replace_node = t.LogicalExpression(ret_state.argument.operator, arguments[0], arguments[1]);
                	_path.replaceWith(replace_node);
                }
              }
            })
          }
        }
        
        path.remove();//慎重
      },
    }
    
    
    
    //去控制流
    const decode_while = {
    	
    	WhileStatement(path)
    	{
    
    		const {test,body} = path.node;
    		
    		
    		//特征语句判断,body.body[0] 必须是 SwitchStatement 节点,
    		//注意一定要先判断长度,避免index出错
    		if (!t.isUnaryExpression(test) || body.body.length === 0  || !t.isSwitchStatement(body.body[0])) return;
    		
    		let switch_state = body.body[0];
    		
    		//获取discriminant及cases节点
    		let {discriminant,cases} = switch_state;
    		
    		//特征语句判断,经过此判断后,基本可以确定是需要还原的while节点了。
    		//如果出错了,可以继续增加判断,直到不出错即可
    		if (!t.isMemberExpression(discriminant) || !t.isUpdateExpression(discriminant.property)) return;
    		
    		//获取数组名,用于查找该数组。
    		let arr_name = discriminant.object.name;
    		let arr = [];
    		
    		//在这里再加一个特征语句的判断:WhileStatement 节点前面有一个节点
    		let all_pre_siblings = path.getAllPrevSiblings();
    		if (all_pre_siblings.length !== 1) return;
    		
    		all_pre_siblings.forEach(pre_path =>
    		{//虽然知道是第0个节点,但这里还是做下判断取arr
    			const {declarations} = pre_path.node;
    			let {id,init} = declarations[0];
    			if (arr_name == id.name)
    			{//如果是定义arr的节点,拿到该arr的值
    				arr = init.callee.object.value.split('|');
    			}
    			//没啥用的语句可以直接删除
    			pre_path.remove();
    		})
    		
    		//新建一个 数组变量,用于存放 case 节点
    		let ret_body = [];
    		
    		arr.forEach(index =>
    		{//遍历数组,去case节点
    			let case_body = cases[index].consequent;
    			if (t.isContinueStatement(case_body[case_body.length-1]))
    			{//删除 continue语句
    				case_body.pop();
    			}
    			//存放于数组变量中
    			ret_body = ret_body.concat(case_body);
    		})
    		
    		//替换
    		path.replaceInline(ret_body);
    	},
    }
    
    
    
    
    var jscode = fs.readFileSync("./encode_ob.js", {
        encoding: "utf-8"
    });
      
    
     
    let ast = parser.parse(jscode);
     
    traverse(ast, decode_str);
    traverse(ast, decode_object);
    traverse(ast, decode_while);
    
    let {code} = generator(ast);
    
    fs.writeFile('decode_ob.js', code, (err) => {});
    
  • 相关阅读:
    IOS遍历未知对象属性、函数
    [Unity3D]Unity3D游戏开发之Logo渐入渐出效果的实现
    面向画布(Canvas)的JavaScript库
    将canvas画布内容转化为图片(toDataURL(),创建url)
    canvas上的像素操作(图像复制,细调)
    【bzoj1251】序列终结者(伸展树)
    延时标记
    曼哈顿距离(坐标投影距离之和)d(i,j)=|X1-X2|+|Y1-Y2|.
    曼哈顿距离最小生成树与莫队算法(总结)
    莫队算法(区间处理)
  • 原文地址:https://www.cnblogs.com/c-x-a/p/13196704.html
Copyright © 2011-2022 走看看