关于巨量算数jsvmp简单还原signature思路

关于巨量算数jsvmp简单还原signature思路

也是头一次处理jsvmp,简单记录一下。因为不是补环境,所以介绍的会比较细碎,不然可能漏掉了,结果又会有问题

先说好,只是介绍思路哈,我只是个摸鱼的混子!

  • 一些前提条件
    • 关于jsvmp的大致思路(肝总给的原话,肝总yyds),概念就这样,具体需要调试的时候才能真正理解!

      • 按这个用心跟,找以下 3 个组件:
        • 操作码:块相互构建的基本指令
        • 存储:用于存储状态和指令的数组或列表
        • 解释器:一个步进器,将一个操作码链接到下一个,读取和写入内存
    • 推荐理解jsvmp:https://www.cnblogs.com/2014asm/p/13733623.html

    • 按照常规的,可以直接搜到或者hook到目的参数,对于jsvmp的话不太方便了,个人这个用的是插桩方法(其实就是打日志,看判断条件,有问题就断到上一个条件,接着分析)

    • 按照这位大佬的方法: https://mp.weixin.qq.com/s/YAL4iT3ECQeK7vZRsLRFaA


  • 分析步骤

    1. 抓包介绍

      • 中间会有很多的请求,关注url包含 https://trendinsight.oceanengine.com/api/open/index/get_keyword_valid_date的请求

        • 在这里插入图片描述
      • 断点一下xhr就能跟到crawler.js,熟悉的$jsvmprt初始化,构建运行js的vm, 头条系好像都是这个名

        • 在这里插入图片描述
      • 很显然G,K函数就是入口,接下来开始正式分析

    2. 介绍一下函数

      • 简单看一下,K函数长这样,从上往下看下来,可以推测这个函数就是在配置需要计算的各个参数,也就是初始化,仔细可以看到前面对K函数的调用。

        • 在这里插入图片描述

        • 在这里插入图片描述

      • 再简单看一下,G函数长这个样子,这就很长了,可以看到是在两个条件下的循环,很明显这就是个用来计算的函数

        • 在这里插入图片描述

        • 再仔细看下来,可以发现中间大多把参数放在S数组当中存放,再回到最开始说的前提那里,就会有点印象了。

    3. 分析一波

      1. 分析前提

        1. 最重要的是,加密操作肯定是拿我们提交的、动态的内容做操作,比如 cookie,headers, params, data之类的,要是用其他的,就感觉有点扯了,所以分析的时候把这些综合分析很有必要。
        2. 比较重要的是,像这种计算,大多都是取ascii码,然后运算再转,这个时候关于ascii相关的方法就需要重点关注
        3. 结果字符串是有哪些组成的:从头往后看一遍日志就很容易得出来了
        4. 既然G是参与运算的,那就在这里插桩。把S里的内容打出来瞧瞧都啥玩意。
        5. 既然G里有两个for循环那就分别看看,一堆测试之后发现只在一个条件里操作。
        6. 很显然O和j是判断的依据,下两个日志断点,一个看S内容,一个看条件的O和j(方便分析不动的时候调试到上一步,直接走流程)
        7. 把xhr发出之前的日志全打出来
          1. 中间应该是会比较卡,因为计算的次数是真的很多,既然会卡很久,那尽量先保存一个,另外再开一个用做分析
          2. 大致是这一个样子在这里插入图片描述
      2. 分析具体流程: 如果不确定推断,就按照条件断点到上一步,自己走流程

        1. 可以看到这个_02B4Z6WO就是我们的最终结果,一直往上走,会发现最开始参数是在时间戳,至于是怎么确定的话,可以分析一组操作需要用的参数,看他是从哪来的(全剧搜基本就行),细看发现前面那一点就是固定的版本号

          • 在这里插入图片描述
        2. 继续看接下来的步骤,可以看到时间戳/1000之后转字符取ascii码

          • 在这里插入图片描述

          • 目测一遍,发现基本是遍历字符串,每个运算基本都是: (hash ^ ts[i].charCodeAt()) * 65599 >>> 0。像这种基本都是有算法名字的,搜一下叫sdbm,原理是一样的,参数不一样。

          • 一轮操作下来,结果和页面计算算一样。但是如果不将这记录下来的话,后续分析的时候,可能就会漏掉,或者找不到之类的,毕竟算力这么大的不可能依次拿参数做操作。所以建议是自己在ide跟着走代码流程,再用别的记录分析流程!

          • 往下推的话,后续肯定很多也是这个运算,但不能保证所有都是用这个算法,毕竟大厂哪有这么小家子气

        3. 发现是对href取的主体,一波分析发现,还是上面那个算法,只是入参hash改成了时间戳的计算结果

          • 在这里插入图片描述
        4. 接着往下走,发现又有和之前算法不一样的别的运算,建议是同样把计算结果保存,从上往下计算,比从下往上追要方便很多,也不确定后续哪里需要它来做运算,所以先保存。

          • 在这里插入图片描述

          • 大致计算为:((href_result % 65521) * 65521) ^ ts_result

        5. 然后就是第四步结果转成二进制,转成之后发现有点对不上后面的,细看原来他在前面加了一串 8240二进制和0,前后走几步发现这确实是固定值

        6. 第五步二进制转成十进制

        7. 然后对十进制字符串进行sdbm_hash,入参为0

          1. 在这里插入图片描述
        8. 第七步之后发现有多行都没用的,细看可以发现这是在校验环境。

        9. 走到这里发现无依无靠的keyword,应该是下面会用到,先计算下来存着

          • 在这里插入图片描述

          • ,发现计算还不太一样,但是细推一下就能看出来: (hash * 65599 + str[i].charCodeAt()) >>> 0;

        10. 接着往下走还是一长串的环境校验

        11. 一堆的玛卡巴卡之后发现了canvas,经过多次发现画的这图没变,不同电脑画的应该会不一样。

          1. 在这里插入图片描述

          2. 看了一会,发现这个数没找到,搜一下发现是块死牛肉。确定这是固定值。

          3. 推测发现跟之前算的有优点区别,他并不是全部取,而是根据入参进行取模,但其实还是固定值

          4. ((num * 65599) + img_str[num % (img_str.length)].charCodeAt()) >>> 0

        12. 好了,整了这么多没用的数,终于看到我们想看的字符了。

          1. 反向走一波发现,源头是我们伟大的第六步结果。做了操作:(((num >> 2) >> 24) & 63) + 65
            • 65怎么来的: 做走几组发现这不是固定值,但出现的都是那几个,所以推测是一组数据,根据条件判断给的值。
              1. 在这里插入图片描述

              2. 总结发现:[0,26) + 65, [26, 52) + 71, ( 62 || 63) -17, 其他-4,取函数filter_special_char

            • 24怎么来的:一组字符串走完发现都不一样,但发现是按照[24, 18, 12, 6, 0]来偏移的。多走几次返现还真是,但后面出现六个字符的咋办?那后面再看,至少现在是没啥问题
            • 再想象一下,这么多字符串,不可能各走各的,应该是每一组都是一个函数出来的,只是入参不一样,所以写的时候尽量按组写函数
            • 这个时候再讨论 >> 2,发现一组数据完了,它只用了一次,那它就是这组字符外层的操作,不会影响这个函数。
            • 计算规律: String.fromCharCode(filter_special_char(num >> the_offset[i]) & 63));
            • 这样看是不满足六个字符,还是现阶段没问题,接着往下走,慢慢补!
          2. 仔细走完第一组,所以第一组外层是右移2,接着走第二组
            1. 在这里插入图片描述

            2. 看这里是28了,上下推一下发现是:(num << 28) ^ (8240) >> 4);,多看几组,发现右边是固定的,接着看字符,发现按照上面的进行计算,一毛一样。果然,一组字符用一个函数应该是没问题的。

          3. 接着到第三组字符,发现他有六个,但是按照之前函数计算前五个居然能对上。那就是第六个做了操作了。
            1. 在这里插入图片描述

            2. 发现hash值为第11步的canvas计算值,但是却没有偏移

              1. 可改为: String.fromCharCode(filter_special_char(the_numi < 5 ? ((num >> the_offset[i]) & 63): (six_num & 63)));,
              2. six_num = canvas_result ^ num;测完发现完全ojbk
          4. 完了发现这里只有三组
        13. 接着往下走发现了,ua,发现还是之前的sdbm_hash算法,只是hash值为第六步的值,一顿下来也没有问题。

        14. 中途发现一个小操作,这也记录下来,留着用。

          1. 在这里插入图片描述
        15. 然后再出现的就是body了,这一串中间的字符一看,这就是keywords的计算值呀,一同计算下来,发现也没啥问题

          1. 在这里插入图片描述
        16. 再又是一个小操作,仍然记录下来

          1. 在这里插入图片描述
        17. 然后就开始了第二轮字符串了。这里和之前又有不一样,看来原函数又得改了。测试发现,此时8240已经不是固定值,需要新传入值(todo_result)做判断使用,但是也就两个参数。以及第三组的入参成了todo_result。

          1. todo_result: 一轮下来,发现这个数是变的,需要追溯到最开始,这里就需要自己动手了哈
        18. 然后就是第二轮字符串下来,接下里又会看到一组字符。对比发现,在结果字符串里并没有它,所以别算这个了。

          1. 在这里插入图片描述
        19. 接着往下走,发现没啥看的了。就发现直接出现了一长串,仔细发现刷新删除cookie就会变,很显然是个根据cookie来的。所以这里也要自己推了哈

          1. 在这里插入图片描述
        20. 这里走完直接跟前面拼接上,还有版本号也拼上。中间过了老长一段。发现他居然拿拼接的字符串开始sdbm_hash,然后拿结果取16进制,截取6到8位做最后拼接的值。

          1. 在这里插入图片描述
        21. 至此就是:signature = _ + 版本号+第一轮字符串+第二轮字符串+根据cookie来的值+补的两个字符


  • 分析体会

    • 可以看到,其实vmp的操作,和开始介绍的已经很契合了。反正代码就那些,反反复复的在用

    • 操作码:也就是一个条件下来,中间用到的那一块操作,但其实可以看到,它大多都是一层一层条件后最后那一下才做真正操作

    • 存储:很明显就是那个S,那些计算参数名字都一样,计算完了一直在索引加加减减的往里塞。

    • 解释器:也就是需要在哪一段条件下,也就是O和j的值,需要执行这一段计算。


  • 个人体会:

    • 大部分的内容,都是在一个窗口是固定的,另外开一个窗口用来推理不顺进行调试用。
    • 都是从上往下推,中间可能会有很多的没有用到的参数,比如第三轮字符串,会造成不必要消耗
    • 当然逆向肯定是根据结果来推内容的来源,由于我也是第一次正面处理jsvmp,希望先了解更细一点再这么做。
    • 还有一点就是,从下往上推的话,肯定是一个小入口,慢慢补很多东西,在分析的时候,会让逻辑有点乱,不知道里面参数、步骤到底还需要哪些。
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值