MENU

记一次爬虫之网抑云js解密

January 18, 2022 • Read: 448 • CTFer

记一次爬虫之网抑云js解密

爬取目标:网易云热评

工具:浏览器开发者工具

浏览器开发者工具简单介绍

2022-01-16-10-07-46.png

  • 元素(Elements):用于查看或修改HTML元素的属性、CSS属性、监听事件、断点等。css可以即时修改,即时显示。大大方便了开发者调试页面
  • 控制台(Console):控制台一般用于执行一次性代码,查看JavaScript对象,查看调试日志信息或异常信息。还可以当作Javascript API查看用。例如我想查看console都有哪些方法和属性,我可以直接在Console中输入"console"并执行~
  • 源代码(Sources):该页面用于查看页面的HTML文件源代码、JavaScript源代码、CSS源代码,此外最重要的是可以调试JavaScript源代码,可以给JS代码添加断点等。
  • 网络(Network):网络页面主要用于查看header等与网络连接相关的信息。

这次调试主要用到network模块,这里再具体介绍一下这个模块

2022-01-16-10-11-25.png

  • Preserve log记录所有的请求事件,即当刷新后上次的请求不会消失。
  • XHR
    XHR全称XMLHttpRequest

    XMLHTTP是一组API函数集,可被JavaScript、JScript、VBScript以及其它web浏览器内嵌的脚本语言调用,通过HTTP在浏览器和web服务器之间收发XML或其它数据。XMLHTTP最大的好处在于可以动态地更新网页,它无需重新从服务器读取整个网页,也不需要安装额外的插件。该技术被许多网站使用,以实现快速响应的动态网页应用。
    

上手实操

  1. 通过网页源代码查看不到热评的任何信息,然后通过抓包一条条分析请求信息。然后可以看到了一个请求的返回包中包含了热评的JSON信息
    2022-01-16-10-23-43.png
  2. 查看请求头信息,这是爬虫的关键信息
    请求的URL:

2022-01-16-10-26-08.png
post请求参数:
2022-01-16-10-27-07.png

  1. 查看请求时的调用栈,Initiator:发送请求的对象,主要包含Parser和Script
    2022-01-16-10-30-29.png

这个就是所有的请求过程,是从下往上进行的。即最上面的是最后请求的调用。

  1. 点击进入最后一个调用
    2022-01-16-10-34-15.png

将代码格式化好后,查看到敏感的一条一句,send(data),点击1处将其设置一个断点,然后展开右边的scope临时变量中的request,查看请求的URL是否为需要的URL,若不是,则点击1处,继续运行到URL为获取评论的URL处即可。
2022-01-16-10-58-22.png

  1. 继续查看对应URL处的调用过程。然后观察右边的变量过程,直到发现了非加密的参数为止
    2022-01-16-11-01-08.png

如图,我们就可以发现参数已经没有加密了,至于这些参数为什么是这些就要去猜测分析了。那便可以肯定的是加密参数的过程就在t2x这个调用中。仔细去分析这个调用过程即可。

  1. 很明显,可以看到两个变量一个是加密的,一个未加密。更加验证了加密是在这个请求的想法。
    2022-01-16-11-07-47.png
  2. 搜索其中一个加密参数,查看出现在程序的那个地方
    2022-01-16-11-26-01.png

可以看到加密过程就是这个window...,再次搜索这个
可以得到最终的加密函数

!function() {
    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,
    window.ecnonasr = e
}();

分析加密过程及相关函数

  1. 参数分析
var bVj1x = window.asrsea(JSON.stringify(i2x), bsP6J(["流泪", "强"]), bsP6J(Xk0x.md), bsP6J(["爱心", "女孩", "惊恐", "大笑"]));

window.asrsea = d,

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
    }
//name就是看这个d函数的执行过程,
//先看传递的参数,
d=i2x={
csrf_token: ""
cursor: "-1"
offset: "0"
orderType: "1"
pageNo: "1"
pageSize: "20"
rid: "R_SO_4_1891469546"
threadId: "R_SO_4_189146"
}

//其他参数就是直接将该函数在控制台运行得到结果即可
e='010001'
f='00e0...很长'
g='0CoJUm6Qyw8W8jud'

//

2022-01-17-20-31-54.png

  1. i = a(16);这里执行了a函数
function a(a) {  //a=16
        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 //返回一个随机的16位字符串仅含字母和数字
    }

那么就可以使用断点工具,直接使用其中一次的随机字符串即可,可以得到i="4lw8juWopBOnGy23"
2022-01-17-20-44-58.png

  1. encText使用b函数加密了两次
    第一次:encText=b(d, g)

第二次:b(encText,i)

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()

//第一个参数就是JSON数据data。第二个参数也是已知的。

这是个AES加密,第一个参数是e,就是明文数据即data。
第二个参数是c,是秘钥。即b函数的第二个参数,然后有个偏移量d,加密模式是CBC

  1. encSecKey使用c函数加密了一次
    encSecKey = c(i, e, f)
function c(a, b, c) { //i是随机字符串已知且固定,e ,f也已知且固定。
        var d, e;
        return setMaxDigits(131),
        d = new RSAKeyPair(b,"",c),
        e = encryptedString(d, a)
    }

因此这里如果把i固定,那么得到的key也是一定的。
那么便要断点调试,在得到i的时候继续运行以查看key。这个i必须是和key对应的,因为这个i也用于作为data加密的秘钥

python中还原加密过程,使其对应的URL能够接收到正确的参数。

  1. 先确定encSecKey。由于只要i固定,则key固定。则可以利用浏览器进行断点调试并且单步跳过综合得出i和key
    2022-01-18-20-16-32.png

如图,在i后加上断点,接着能在右边的临时变量中得到i值,然后单步跳过得到key值。

  1. encText的话就是使用python中Crypto.Cipher的AES模块,先创建一个加密器,里面设置好上文所提及的相应秘钥,偏移量,加密方式等。
def enc_params(data, key):
    iv = "偏移量"
    data = to_16(data)
    aes = AES.new(
        key=key.encode("utf-8"), IV=iv.encode("utf-8"), mode=AES.MODE_CBC
    )  # 创建加密器
    bs = aes.encrypt(data.encode("utf-8"))  # 加密, 加密的内容的长度必须是16的倍数
    return str(b64encode(bs), "utf-8")  # 转化成字符串返回,

全文到这里差不多就结束了,先挖个坑。听说网易的加密方式都是一个套路,后续打算再看看别的接口,如歌词,甚至歌曲等,敬请看下回分析。

Leave a Comment

本站总访问量 32668 次