AST还原实战|某条系jsvmp 中 if 语句 转Switch语句

免责声明:

本篇文章仅作为学术研究和安全交流使用,切勿用于商业用途。如因违反规定产生任何法律纠纷,本人概不负责。如果本篇文章影响到官方利益,请添加文末的作者微信号告知,本人会在第一时间将文章删除。

一.前言

看了大佬的  JS逆向之字节系列某量算数jsvmp算法分析及深度还原   这篇文章后,也想照着学一波,看到这里时,发现他将 if 语句转换成了switch语句,再进行分析:

430b18a22b96dd47d02be65fbc3b68a0.png

群里问过大佬怎么弄的,他说是手动还原的,因为手动要比写还原插件的时间少很多。

碰巧群里有另外一个兄弟用AST进行了还原,学习了一波,思路已掌握。本文的大部分思路也来自他的代码,在此感谢。

二.分析

把网站上面的代码复制下来,并保存为 acrawler.js,逐个进行分析。

8f5acf8189120981e88c1dcd85934ade.png

语句①:

if ((A = x) > 11)
 throw S[R--];

没有括号,给它加上:

if ((A = x) > 11)
{
 throw S[R--];
}

语句②:

(A = x) > 10 ? S[++R] = void 0 : A > 1 ? (C = S[R--],S[R] = S[R] >= C) : A > -1 && (S[++R] = null);

条件表达式,转成 if 语句:

if ((A = x) > 10) {
    S[++R] = void 0;
} else {
    if (A > 1) {
        C = S[R--],
        S[R] = S[R] >= C;
    } else {
        if (A > -1) {
            S[++R] = null;
        }
    }
}

语句③:

A > 5 && (S[R] = h(S[R]))

逻辑表达式,转成 if 语句:

if (A > 5) {
    S[R] = h(S[R]);
}

经过这些还原步骤后,做成了统一的代码,就可以愉快的进行插桩了。

问提来了,为什么要插桩,哪些地方需要插?

三.插桩

经过上面的那些步骤后,我们来看代码

5a0e3c5dc44deef64cdf51807ff29db5.png

在插桩获取 j的值之前,我们要知道,哪些地方才需要获取j的值。

答案: 每个 if 语句里面参与加密计算的语句。

语句①:

if (x >>= 2, A < 1) {
          A = 3 & x;
          .....
}

这一层还在外层,无法确定 j的值,因此不需要插桩。

语句②:

f((A = x) > 10)
{
    S[++R] = void 0;
}

包含了加密相关的代码,S[++R] = void 0;,需要进行插桩,获取 j的值。

f((A = x) > 10)
{
    mxt_("mxt_5448_1", j);
    S[++R] = void 0;
}

添加的这一行代码,用于保存到 case_node_map 变量中:

var case_node_map = new Map();
window.case_node_map = case_node_map;
function mxt_(sign, step) {
    case_node_map.set(sign, step)
}

同理,语句③、④、⑥一样需要进行插桩获取j的值,语句⑤则不用。

经过这样一波分析后,找到了规律,类似于下面的if语句:

if ((A = x) > 10) {
    S[++R] = void 0;
} else {
    if (A > 1) {
        C = S[R--],
        S[R] = S[R] >= C;
    } else {
        if (A > -1) {
            S[++R] = null;
        }
    }
}

及其兄弟节点,都需要进行插桩:

if ((A = x) > 11) {
    throw S[R--];
}
//下面是兄弟节点:
if (A > 7) {
    for (C = S[R--],
    z = v(b, O),
    A = "",
    P = i.q[z][0]; P < i.q[z][1]; P++)
        A += String.fromCharCode(r ^ i.p[P]);


    O += 4,
    S[R--][A] = C;
} else {
    if (A > 5) {
        S[R] = h(S[R]);
    }
}

分析后写下 AST代码,生成插桩后的代码,部分截图如下:

0dfcd3ac3dd5f0c1d8d24115fa86d164.png

经过这一波插桩后,就可以使用reres插件替换网页上的js,从而获取case_node_map,及保存了 节点位置和j的值,便于还原分析。

四.获取j的值

直接使用Reres插件,将整个js文件进行映射,得到 case_node_map 变量的值:

4d2848f31b1ac232fd8904940c6ba848.png

这里说明一下,映射后,可能得到的size不一样,正常是 126 个 元素。

再把这个map转换为Array:

var arr = Array.from(case_node_map);
copy(arr);

把这个数组复制到ast文件中,然后进行比对,生成switch节点即可。部分代码:

if (case_node_map.has(tmp)) {
    let i = case_node_map.get(tmp);
    if (!types.isReturnStatement(consequent.body[consequent.body.length - 1])) {
        consequent.body.push(types.BreakStatement())
    }


    caseNodes[i] = types.SwitchCase(types.NumericLiteral(i), consequent.body);
}

完整代码我会放在星球里,需要的可以自取。

感谢阅读。

另外我建了个AST的交流群,想进群的朋友可以加我微信。

63f47675153169ec1d6a6117f0e9f22b.png

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值