关注它,不迷路。
本文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,否则后果自负,如有侵权,请联系作者立即删除!
1.控制流代码
在还原datadome的字符串以后,发现了一下控制流,大部分类似如下:
所有需要处理的循环都是这种结构,即循环体只有两个节点,一个是switch节点,另外一个是break节点,和ob混淆的控制流有点类似。
2.预处理
想要还原控制流,需要获取每次switch语句discriminant的值,而它的初始值是首先需要获取的,因此这里最好把for循环的init给提取到前面来,方便我们获取初始值。插件代码如下:
const aheadInitNodeOfFor = {
/** @param {NodePath} path */ //每个插件前都要加哈。
ForStatement(path) {
let { init, test, update, body } = path.node;
if (!types.isBooleanLiteral(test, { "value": true }) ||
update != null || body.body.length != 2) {
return;
}
let firstNode = body.body[0];
let secondNode = body.body[1];
if (!types.isSwitchStatement(firstNode) || !types.isBreakStatement(secondNode)) {
return;
}
if (types.isAssignmentExpression(init)) {
path.insertBefore(types.ExpressionStatement(init));
}
else if (types.isVariableDeclaration(init)) {
path.insertBefore(init);
}
else {
return;
}
path.node.init = null;
},
}
traverse(ast, aheadInitNodeOfFor);
处理后,还需要将变量定义给分离出来,方便后面的查找:
const DeclaratorToDeclaration =
{
VariableDeclaration(path) {
let { parentPath, node } = path;
if (!parentPath.isBlock()) {
return;
}
let { declarations, kind } = node;
if (declarations.length == 1) {
return;
}
let newNodes = [];
for (const varNode of declarations) {
let newDeclartionNode = types.VariableDeclaration(kind, [varNode]);
newNodes.push(newDeclartionNode);
}
path.replaceWithMultiple(newNodes);
},
}
traverse(ast, DeclaratorToDeclaration);
继续分析,看到这样的代码:
case语句里面是空的,因为它在最后一个case,其实就是退出了switch语句,因此加个break节点就好:
const addBreakNodeOfemptyCase =
{
SwitchCase(path) {
let { consequent } = path.node;
if (consequent.length == 0) {
console.log(path.toString());
path.node.consequent.push(types.BreakStatement());
}
},
}
traverse(ast, addBreakNodeOfemptyCase);
处理后的样子:
接下来,就可以愉快的去控制流了。
3.去控制流思路
获取到初始值,可以写个方法与插件分离出来
匹配这个初始值,获取该 SwitchCase 的 consequent子节点的内容,并根据最后一个节点类型 更新 初始值;
如果最后一个节点类型是break语句或者return语句,则直接退出循环;
4.反混淆源码
本文的demo及代码,放在星球里,需要的请自取。
https://t.zsxq.com/e23xV
今天的分享就到这里,感谢阅读。
欢迎加入知识星球,学习更多AST和爬虫技巧。