网易云参数解析(多图)
网易云api接口js逆向
工具:python3.6.6、火狐 、chrome、node.js
此教程为定向爬虫,非通用向,就是会改动😑,此教程忽略部分基础知识,如有不适请留下评论,我会补充。
api定位
基础知识(难度:☆)
首先在web应用中定位需要抓取的api接口,确定请求携带的参数及大概猜测加密方式(猜不到或者猜错也没有关系,只是为了方便快速定位),下图是查询评论api(其他原理一样)。
js定位
难点在于这里(敲黑板,难度:☆☆)
如何确定加密的参数出处?这里罗列几个步骤,大致可以按照这样来
- 首先第一步在js文件中全局搜索关键字(关键字可以是参数名称,URL路径,URL不需要全部,可以搜关键的部分,如果还有其他特征也可以搜索)
- 第一步基本可以解决大部分web应用api,如果搜不出来那就从调用栈进行查询(在浏览器网路监控中的发起程序有调用栈,如下图)

js解读
找到js的基本入口之后怎么利用?(持续难度:☆☆)
通用写个大致流程步骤(并不能通用哈)
- 一般定位到的js都是被混肴后压缩后的代码,所以需要先进行格式化(格式化点击浏览器左下角的花括号)。
- 光是定位到方法的位置还不够,还需要定位到参数的合成位置(需要一定的阅读基础)
js追踪
到了这一步一般就是比较繁琐了,一般考究个人悟性及阅读水平,实在没有什么好的方法可以解决(也可以不追踪,直接编译整个js然后调用对应的function,但是也需要一定的阅读能力)。
说道此处,基本有了大概的处理思路了,就是不断断点调试,然后追踪参数的出处及方式,最后重现,下图是网易云的一个追踪过程图。
根据函数定位向上寻找函数d,并添加断点进行调试,可以发现d、e、f、g四个参数只有d是变化的,"{“s”:“s”,“limit”:“8”,“csrf_token”:""}"(这是照抄我的程序上面的,我发送的数据是s,要求返回的是8,这也可以换成下载音乐的查询格式,或者是评论歌词都可以)还有下面的encSecKey的几个参数除了(e、f)也是固定值。
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
}
继续跟踪a函数可以定位到一个生成16位的随机数, 用到参数 i 的就是params跟encSecKey的验证,只要固定死了了 i 那么,encSecKey也就是固定值了。
AES加密
parm追踪到 b 函数进行两次运算,重新看一下参数,d是发送的数据,g是一常量,看看b函数
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()
}
看到这里先不管前面几个运算是做什么,但是看到.mode.CBC 会不会有种熟悉的感觉?没错就是大名鼎鼎的AES的CBC,惊不惊喜意不意外,那么前面应该就是补位函数了,看函数的名字也越来越像了(所以说为什么要猜测算法加密类型)。
ps:关于crypto库的安装,略,也可以在浏览器的console窗口进行调试代码
from Crypto.Cipher import AES
import base64
#补全16的整数倍,要求bytes
def contentEndo(content):
content=content.encode('utf-8')
while len(content) % 16 != 0:
content += '\0'.encode('utf-8')#以0补全
return (content)
#进行AES加密
def AESencrypt(key, content):
key = contentEndo(key)
iv = b'0102030405060708'
encryptor = AES.new(key, AES.MODE_CBC, iv)
encrypt = encryptor.encrypt(content)
encrypt = base64.b64encode(encrypt)
return bytes.decode(encrypt)
content ="{\"s\":\"s\",\"limit\":\"8\",\"csrf_token\":\"\"}"
key = "0CoJUm6Qyw8W8jud"
content = contentEndo(content)
en =AESencrypt(key,content)
print(en)
得到的是,好像跟我们调试的有一点点不一样
f5Lcudct4ZaW6sHL3WNKrqGGzqLedMMO1DN2CDPnLFC55pGM7XlcfLr5RfVCzCai
不过至少成功了一半吧,将标准的加密串进行解密
def decrypt(text):
key = "0CoJUm6Qyw8W8jud".encode('utf-8')
cryptor = AES.new(key, AES.MODE_CBC, b'0102030405060708')
text = base64.decodestring(text.encode('utf-8'))
plain_text = cryptor.decrypt(text)
print (plain_text)
b'{"s":"s","limit":"8","csrf_token":""}\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'
可以得知其补位字符,把\0换成\x0b,就得到正确的运算结果,只要在结合随机数i进行第二次加密就可以得到真正的param ,但是同样的还是会出现加密串最后的有些不一样,同样通过解密得到补位的字符,加密后都是64位,用字符串拼接再进行加密
en =AESencrypt(key,content)+'\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10'
还有一点,就是最后的需要进行URL转换
from Crypto.Cipher import AES
import urllib.parse
import base64
#补全16的整数倍,要求bytes
def contentEndo(content):
content=content.encode('utf-8')
while len(content) % 16 != 0:
content += '\x0b'.encode('utf-8')#配合解密函数寻找补位字符
return (content)
#进行AES加密
def AESencrypt(key, content):
key = contentEndo(key)
iv = b'0102030405060708'
encryptor = AES.new(key, AES.MODE_CBC, iv)
encrypt = encryptor.encrypt(content)
encrypt = base64.b64encode(encrypt)
return bytes.decode(encrypt)
def makeData():
content ="{\"s\":\"s\",\"limit\":\"8\",\"csrf_token\":\"\"}"
key = "0CoJUm6Qyw8W8jud"
i="tCBHRuGjK2gndtne" #固定死,对应的encSecKey就是常量
encSecKey = "2e3d379b236dffdf32409f066f6cc2f076ab0d713546ed0da7372c357755b0c364009ce41c093864788cb487f2b854d56b1ce06c2b62d79a718206b9e4b42122a82322f8b28c857f3cc99cb39de06fe5882cea5f49fed9684afcc1d5e22ea500ea18781f586b533e0bde357ac2ea87d4bc8c99f65d7fc0756d099f4bb2ce8d84"
content = contentEndo(content)
en =AESencrypt(key,content)+'\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10'
param = AESencrypt(i,en.encode('utf-8'))
param = urllib.parse.quote(param)
param=param.replace("/","%2F")
data = "params={}&encSecKey={}".format(param,encSecKey)
return data
print (makeData())
content = "{\"s\":\"s\",\"limit\":\"8\",\"csrf_token\":\"\"}"
运行程序
重发
把结果拷贝下来打开火狐浏览器的开发者模式,选择编辑重发,将参数填进去,看是否可以正常响应。
把postdata粘贴进去,把请求头里面的一些参数删除Content-Length: XXX,看是不是正确的响应
结尾:
一个定向的api爬虫就写完了,其实大部分都是可以按照这样一个流程走一遍,如果这个不能通过的就需要一部分的js源码编译运行,通过调试断点看一下传输的格式是什么修改一下,再进行加密也可以得到答案。
免责声明:以上代码及示例,仅用于个人学习使用,不允许用于商业或者危害其他个人或者团体利益相关的活动中。

浙公网安备 33010602011771号