代码改变世界

对网易云评论 JS加密 进行破解爬取评论

2019-07-17 14:50  Xiao_Chen  阅读(1156)  评论(0编辑  收藏  举报

爬过网易云评论的都知道,网易云的歌曲都是通过 <iframe> 便签进行层层迭代,而且数据都是异步加载的,就算是使用Selenium也要进行框架的转换,非常麻烦,且效率低下。

 

 

 

 

通过Chrome抓包得到评论的URL

 

可以看到评论的URL使用POST传进了两个参数,这一看就是加密过的,直接访问肯定是进不去的,猜测应该是JS加密,要破解只能去查看JS源码了。

在Initiator中可以看到 JS 目录的位置,在

 

 

格式化后放到本地,查找参数关键字以找到相关的JS代码,比如查找 “encSecKey”

 

运气不错一下就找到了,可以看到参数的值分别是bYc1x.encText,bYc1x.encSecKey,而bYc1x是由asrsea()方法传进4个参数构建的,再查找asrsea,

得到一长串混淆的JS代码,根本没法解析变量的意思,但是似乎找到了加密的方式 CryptoJS.mode.CBC。

 

 

要是想完全解密经过多层混淆的代码估计JS功底得非常深厚,所以这里取巧直接在本地输出参数看下参数的值,然后进行反加密。

这里我使用的是 Fiddler 去替换原来的JS代码,在控制台输出参数的值。因为服务器的资源是先放到浏览器上,浏览器再展示给我们,所以可以替换掉服务器的代码,输出参数的值。

打开 Fiddler,点击AutoResponser, 把Enable rules , Unmatched requests passthrough和 Enable Latency全部勾上。

然后刷新页面,在下方输入 Select Script 

 

 

找到我们需要更改的JS源码,拖到右侧AutoResponser界面。

其中左边是你需要替换掉的JS文件,右边是你想要应用的文件路径。

 

 

我们想要输出未经加密的参数,所以在源码中添加

console.log(i3x);console.log(bkY2x(["流泪", "强"]));console.log(bkY2x(VM8E.md));console.log(bkY2x(["爱心", "女孩", "惊恐", "大笑"]));
View Code

PS:我是在没有格式化JS代码的时候就要添加了,格式化后会报错变量未定义什么的,所以是直接在源码适当的地方直接插入。报错的可以试着换个位置插入。

 

 

然后在AutoResponse中点击 save ,刷新网易云的页面,点击控制台,会看到:

 

 

多次输出检验可以后3个参数是定值,而第一个参数就包含了歌曲信息:

rid:就是歌曲的 id信息。

offset:用作评论的分页,比如offset=100,就是从第101条评论开始。

limit:限制的评论数,上限为100,超过会变成20,限制输出的评论数。 

其他参数暂时还没有发现其用处。

 

部分源码分析:提取的源码如下:

function a(a) {
        var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
        c = "";
        for (d = 0; a > d; d += 1) e = Math.random() * b.length,
        e = Math.floor(e),
        c += b.charAt(e);
        return c
    }
    function b(a, b) {
        var c = CryptoJS.enc.Utf8.parse(b),
        d = CryptoJS.enc.Utf8.parse("0102030405060708"),
        e = CryptoJS.enc.Utf8.parse(a),
        f = CryptoJS.AES.encrypt(e, c, {
            iv: d,
            mode: CryptoJS.mode.CBC
        });
        return f.toString()
    }
    function c(a, b, c) {
        var d, e;
        return setMaxDigits(131),
        d = new RSAKeyPair(b, "", c),
        e = encryptedString(d, a)
    }
    function d(d, e, f, g) {
        var h = {},
        i = a(16);
        return h.encText = b(d, g),
        h.encText = b(h.encText, i),
        h.encSecKey = c(i, e, f),
        h
    }
    function e(a, b, d, e) {
        var f = {};
        return f.encText = c(a + e, b, d),
        f
    }
    window.asrsea = d,
View Code

函数 asrsea() 传进了4个参数,然后asrsea 调用了 函数 a() ,阅读a()的代码,可以知道返回的是一个长度为 16 的随机字符串。那么我们可以给它一个定值。

再看对象 h,易知 h.encSecKey = c(i, e, f)是一个定值,最后就是带有歌曲参数的属性 h.encText,这个属性是通过两次加密后得到的。

研究加密算法后,知道 “0102030405060708” 是 vi偏移量,加密模式是 CryptoJS.mode.CBC。

 

 

 然后回到反加密的过程,这里使用python编写解密过程。参考了网上的教程和该加密算法的加解密过程,代码如下:

PS:需要 Crypto 包 

 

from Crypto.Cipher import AES
import base64
import json
# limit里面可以得到更多的评论,rid后加歌曲id
# 下面参数通过控制台可以得到大部分,都是定值
# offset可以
first_param = '{rid: "R_SO_4_167844", offset: "120", total: "false", limit: "100", csrf_token: ""}'
second_param = '010001'
third_param = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
fourth_param = '0CoJUm6Qyw8W8jud'

def get_params():
    #根据该加密算法做出解密
    iv = '0102030405060708'
    first_key = fourth_param
    second_key = 16 * 'F'
    h_encText = AES_encrypt(first_param, first_key, iv)
    h_encText = AES_encrypt(h_encText, second_key, iv)
    return h_encText

def get_encSeckey():
    #这个也是定值
    encSecKey = "257348aecb5e556c066de214e531faadd1c55d814f9be95fd06d6bff9f4c7a41f831f6394d5a3fd2e3881736d94a02ca919d952872e7d0a50ebfa1769a7a62d512f5f1ca21aec60bc3819a9c3ffca5eca9a0dba6d6f7249b06f5965ecfff3695b54e1c28f3f624750ed39e7de08fc8493242e26dbc4484a01c76f739e135637c"
    return encSecKey

def AES_encrypt(text, key, iv):
    if type(text) == type(b'123'):
        #这是判断当前变量的类型是bytes还是字符串,因为pycryptodome要        
        #求参数要是字节类型
        text = text.decode('utf-8')

    pad = 16 - len(text) % 16
    text = text + pad*chr(pad)

    iv = iv.encode('utf-8')
    key = key.encode('utf-8')
    encryptor = AES.new(key, AES.MODE_CBC, iv)
    text = text.encode('utf-8')
    encrypt_text = encryptor.encrypt(text)
    encrypt_text = base64.b64encode(encrypt_text)
    return encrypt_text

if __name__ == '__main__':
    params = get_params()
    encSecKey = get_encSeckey()
    print(params.decode('utf-8'))
    print(encSecKey)
View Code

 

 

参考文章:

  知乎:https://www.zhihu.com/question/36081767/answer/386606315

  csdn:https://blog.csdn.net/weixin_40444270/article/details/81260638