QQ音乐sign,jsvmp算法推导

有小半年没有去学习了,这是前几天突然看到的文章--qq音乐sign算法还原源码放送及jsvmp全流程分析,qq音乐的sign居然是个jsvmp,我之前弄得时候都没看,直接补了两行代码就可以了,不过本着学习的态度,而且我一直搞不懂jsvmp,虽然明白原理,但是在面对入栈出栈的一系列操作,依然还是很懵逼的状态。

今天就来拿这个简单点的jsvmp去实验,只讲我是怎么去推导的,当然这一切都是结合着大佬的文章来完成的。

废话不多说,进入今天的正题。

搞jsvmp首先就是打断点,这是大佬们经常说的,但是对于从来没有接触过的人来说,很难,因为不知道在哪里去打断点,我的理解是和webpack打包一样,都有一个中间的分发器,那我们找到这个地方,然后插桩,应该会有很多有用的信息打印出来(个人猜测,也不是很懂)。

我们直接在这里打上断点,运行看日志

 我这里打印出来3000多条日志,接下来,我们就一点一点的看这些日志。

 这里可以看到有一个base64的子码表,上面还有一个字典表。

 这里有一步  __sign_hash_20200305 函数,如果你看代码的话,会在后面看到有这个函数

大体扫一遍,就猜出了 这是一个md5加密,至于到底是不是,我们验证一下,

看到这个函数后面跟了一串字符,我们就大胆的去猜测,这就是入参 

果然,我猜的没错,那是不是最后几个字段就是方法和参数,这个还有待考究 ,继续往下看,看到对结果进行了转大写。

 这里可以看到对navigator.userAgent进行了判断,这个test是啥方法,往上面再看一点,就可以发现是 RegExp 的方法

 

这就是这几段日志的运行逻辑 ,最后把这个false赋予这个数组的第13位,我们可以把后面几行日志复制出来

 第13位赋值为false,继续往下看

这里看到了location.href,其实补环境也就补这两个就能出结果了。

 

这个列表应该是自身的代码,并没有找到生成方法

可以看到,即将对列表进行map操作

可以看到,"C496EB283AA82ECB999AD253A5464314" 的 第 21 位就是 2

那么大体的算法就是

 

 

接着 list join 拼接,一直没看结果,我们先看一下结果  zzb2ea49d61elu7bpkljqaeriwiaqkreg98694825

zzb 2ea49d61  elu7bpkljqaeriwiaqkreg98694825

这一段就在结果里,继续往下看

和上面一样 

直接看结果

 98694825 就是结果的最后几位,那现在还有 头部 3 个,以及中间几位,其实头部是写死的,也就剩下中间的。

这个list 和上面的一样,没有生成方法,那他应该也是有用的 ,其实接下来应该从后面去推的,但是这样有点别扭,我这边就从正面去推。

 看一下这一小段日志,依然和 "C496EB283AA82ECB999AD253A5464314" 这段md5有关系,

取第一位,为c,然后从字典表中找到 c所指向的数字,为 12,那么这个54,38从哪来,直接搜,发现后面都是,那应该就是写死的,54和38做什么操作能为16,这个我是试的,我也不是很懂这些操作,这样应该最简单,毕竟运算符是有限的,

12*16 = 192

接下来找到4对应的4 和 192进行 ^ 操作得到 196

然后用 196 ^ 212  push到一个新数组,重复上面的操作,直到遍历完 md5的字符串,那么现在可以根据这些操作大概的写出算法

往下翻就会看到结果是一样的,接下来就是操作这个新数组了,对他进行操作,来获取中间的数据

 

 先取了3个分别放入列表 22 23 24 的位置

 我们先往后面看看,需要这几个数字来推算结果,那这几个数字从哪来,就往前翻

看到 25 26 27 28 4位分别放着结果,那就是来看这四位上的数据怎么生成的

 

 先看25   拿出16 进行  16>>2 然后 25 这个就等于 4 ,这个是最简单的

 接着看 26  

首先 取 16 操作 这里 a=16 b=187 ,大概就是这个意思,我也不知道该怎么去说了,都是一些试的工作

27

 

28

 

 

 再往下翻依然是这样的,可以猜出for 循环那个 新数组 一次取3个参数,最后还要判断一下参数不足的情况,也很简单,最终这段算法如下,至于有的时候,0>>4 或者 0<<4 或者 0&4都是 0 这个结果的时候,先随便写一个,等出现别的参数的时候,会很容易判断到底是什么运算符

 最终代码,看上面大佬的文章说页面之前是不一样的,我就不清楚了,只是拿这个js练练手,熟悉一下jsvmp怎么破解,难得俺也不会。。。

let md5 = require('md5-node');

function middle(ls) {
    let resNum = []

    function test(a, b, c) {
        let r25 = a >> 2
        if (b !== undefined && c !== undefined) {
            let r26 = a & 3
            let r26_2 = r26 << 4
            let r26_3 = b >> 4
            let r26_4 = r26_2 | r26_3
            let r27 = b & 15
            let r27_2 = r27 << 2
            let r27_3 = r27_2 | (c >> 6)
            let r28 = c & 63
            resNum.push(r25)
            resNum.push(r26_4)
            resNum.push(r27_3)
            resNum.push(r28)
        } else {
            let r10 = a >> 2
            let r11 = a & 3
            let r11_2 = r11 << 4
            resNum.push(r10)
            resNum.push(r11_2)
        }
    }

    for (let i = 0; i < ls.length; i += 3) {
        if (ls[i] !== undefined && ls[i + 1] !== undefined && ls[i + 2] !== undefined) {
            test(ls[i], ls[i + 1], ls[i + 2])
        } else {
            test(ls[i], undefined, undefined)
        }
    }
    let res = []
    resNum.forEach((item) => {
        let zd = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
        res.push(zd[item])
    })
    res = res.join('')
    return res
}

function head(md5Str) {
    let res = [];
    [21, 4, 9, 26, 16, 20, 27, 30].map(x => {
        res.push(md5Str[x])
    })
    return res.join('')
}

function tail(md5Str) {
    let res = [];
    [18, 11, 3, 2, 1, 7, 6, 25].map(x => {
        res.push(md5Str[x])
    })
    return res.join('')
}

function getLs(md5Str) {
    let zd = {
        "0": 0,
        "1": 1,
        "2": 2,
        "3": 3,
        "4": 4,
        "5": 5,
        "6": 6,
        "7": 7,
        "8": 8,
        "9": 9,
        "A": 10,
        "B": 11,
        "C": 12,
        "D": 13,
        "E": 14,
        "F": 15
    }
    let ol = [212, 45, 80, 68, 195, 163, 163, 203, 157, 220, 254, 91, 204, 79, 104, 6]
    let res = []
    let j = 0
    for (let i = 0; i < md5Str.length; i += 2) {
        let one = zd[md5Str[i]]
        let two = zd[md5Str[i + 1]]
        let r = one * 16 ^ two
        res.push(r ^ ol[j])
        j += 1
    }
    return res
}

function sign(params) {
    let md5Str = md5(params).toUpperCase()
    let h = head(md5Str)
    let e = tail(md5Str)
    let ls = getLs(md5Str)
    let m = middle(ls)
    let res = ('zzb' + h + m + e).toLowerCase()
    let r = RegExp(/[\\/+]/g)
    res = res.replace(r, '')
    return res
}

console.log(sign('{"comm":{"cv":4747474,"ct":24,"format":"json","inCharset":"utf-8","outCharset":"utf-8","notice":0,"platform":"yqq.json","needNewCode":1,"uin":0,"g_tk_new_20200303":5381,"g_tk":5381},"req_1":{"module":"music.globalComment.CommentRead","method":"GetReplyCommentList","param":{"RootCmId":"1!beu.a6oPFn6eDtAiQIvWlaRWmVKREp0SoANGpzZ41b-v9TBHJJ5LX8Ak6TciMJgJ","LastCommentSeqNo":"","PageSize":10,"LastRankScore":"","RankType":1,"PicEnable":1}}}'))

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值