本文所有内容均以学习为目的,如有不良影响请联系我立即删除!
距离上一篇瑞数反调试的文章已经过了2个月,今天来聊一聊瑞数第一层js的反混淆,下面就来看看反混淆思路。
首先,拿到HTML中的js代码,简单阅读后发现,js是个自执行函数,首先声明了一个很大的二维数组:
![](http://maplestory.yueeronline.xyz/wp-content/uploads/2023/04/rs1-1024x104.png)
之后定义了一大堆方法后,出现了控制流平坦化,大量的if-else if-else语句,并且使用的控制流数组就是从开头定义的二维数组中取的
var _$Al, _$_e, _$9n, _$fv, _$Di, _$GJ, _$YD, _$Dm, _$vl, _$rX, _$8a, _$Wj, _$qK, _$C8, _$Gm, _$ZP, _$7G, _$mk,
_$0D;
var _$82, _$45, _$r_ = _$fJ, _$jy = _$LW[1];
我们可以先把变量还原成控制流数组,先把数组拿出来,然后通过AST修改代码:
_$LW = [...]
function step3(ast) {
traverse(ast, {
MemberExpression: handleList,
})
function handleList(path){
let node = path.node;
let parentNode = path.parent;
if (node.object.name === '_$LW' && t.isVariableDeclarator(parentNode)){
var p = node.property.value
var value = _$LW[p]
path.replaceInline(t.valueToNode(value))
}
}
}
这里需要注意的是,控制流数组在代码运行过程中,会被修改,所以大家要拿到修改之后的新数组进行替换,并修改起始下标,才没有问题。
第二步我们需要把所有的if-else if-else,全都替换成if-else的形式,方便我们后续做处理:
function step1(ast) {
traverse(ast, {
IfStatement: func
})
function func(path) {
let node = path.node;
let parentNode = path.parent;
if (t.isIfStatement(parentNode) &&
parentNode.alternate !== undefined &&
parentNode.alternate === node
){
path.replaceWith(t.blockStatement([node]))
}
}
}
这样所有的控制流都变成了if-else了,接下来就是重头戏,我们要把if-else控制流去除了。
大量的嵌套if-else其实都是没有实际作用的代码,每一个从控制流数组中遍历出来的值,最终经过大量的if-else都只会运行一句有用的代码。所以我们需要做的就是,将这个值带进if-else中计算,最终拿到这一句代码,同时去除掉所有的if-eise。
可以发现控制流在代码中不止出现了一次,其中有些存在于函数中,控制流数组下标是通过参数传进来的,但他们都是通过一个while(1)无限循环中执行的,同时while语句的前几句也都是固定的格式,我们可以通过这一特征,来定位控制流平坦化代码,定位到控制流平坦化代码之后,我们在前面提到,有些控制流平坦化代码在函数内部,这样的话我们需要知道这个函数被调用了多少次,传了哪些下标进来,并保存起来,组成一个Switch语句:
function handleWhile(path) {
var prevSiblingNodePath = path.getPrevSibling();
var prevSiblingNode = prevSiblingNodePath.node;
if (prevSiblingNode.declarations.length === 4){
var subscriptNameNode = prevSiblingNode.declarations[2];
var subscriptName = subscriptNameNode.id.name;
var parent_function = path.getFunctionParent().node
var node = path.node
var initNode = node.body
var arrNode = prevSiblingNode.declarations[3];
var arr = arrNode.init.elements;
if (parent_function.params.length !== 0 && parent_function.params[0].name !== undefined){
var param = parent_function.params[0].name
var function_name = parent_function.id.name
var case_list = []
var p_parent_function = path.getFunctionParent().getFunctionParent()
p_parent_function.traverse({
CallExpression(c_path){
var c_node = c_path.node
if (c_node.callee !== undefined &&
c_node.callee.name === function_name &&
c_node.arguments.length > 0 &&
t.isNumericLiteral(c_node.arguments[0]) &&
case_list.indexOf(c_node.arguments[0].value) === -1
){
case_list.push(c_node.arguments[0].value)
}
}
})
let ast_case_list = []
for (let cs of case_list){
let new_node = test(node.body, cs, 0)[0]
new_node.push(t.breakStatement())
ast_case_list.push(t.switchCase(t.numericLiteral(cs), new_node))
}
let a = t.switchStatement(t.identifier(param), ast_case_list)
path.replaceWith(a)
}
}
}
最后我们要实现的就是如上代码中的test方法,手动判断if-else中的逻辑表达式、二元表达式和一元表达式,需要注意的是,真正执行的代码中,可能存在修改控制流数组下标的语句,我们也需要相应的对下标进行修改
由于此处实现后的代码过长,因为篇幅原因此处只展示大概,可以参考这篇文章来实现 https://blog.csdn.net/qq_35491275/article/details/117969108
![](http://maplestory.yueeronline.xyz/wp-content/uploads/2023/04/rs_test-1024x702.png)
Comments | NOTHING