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('\x69\x6e\x64\u0435\x78\x4f\x66'));
`
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) => {});
posted @ 2020-06-26 22:44  公众号python学习开发  阅读(1446)  评论(0编辑  收藏  举报