// 页数

网易云参数解析(多图)

网易云api接口js逆向

工具:python3.6.6、火狐 、chrome、node.js

此教程为定向爬虫,非通用向,就是会改动😑,此教程忽略部分基础知识,如有不适请留下评论,我会补充。

api定位

基础知识(难度:☆)
首先在web应用中定位需要抓取的api接口,确定请求携带的参数及大概猜测加密方式(猜不到或者猜错也没有关系,只是为了方便快速定位),下图是查询评论api(其他原理一样)。

js定位

难点在于这里(敲黑板,难度:☆☆)

如何确定加密的参数出处?这里罗列几个步骤,大致可以按照这样来

  1. 首先第一步在js文件中全局搜索关键字(关键字可以是参数名称,URL路径,URL不需要全部,可以搜关键的部分,如果还有其他特征也可以搜索)
  2. 第一步基本可以解决大部分web应用api,如果搜不出来那就从调用栈进行查询(在浏览器网路监控中的发起程序有调用栈,如下图)

在这里插入图片描述

js解读

找到js的基本入口之后怎么利用?(持续难度:☆☆)

通用写个大致流程步骤(并不能通用哈)

  1. 一般定位到的js都是被混肴后压缩后的代码,所以需要先进行格式化(格式化点击浏览器左下角的花括号)。
  2. 光是定位到方法的位置还不够,还需要定位到参数的合成位置(需要一定的阅读基础)

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源码编译运行,通过调试断点看一下传输的格式是什么修改一下,再进行加密也可以得到答案。

免责声明:以上代码及示例,仅用于个人学习使用,不允许用于商业或者危害其他个人或者团体利益相关的活动中。

posted @ 2018-09-16 13:09  黄大胆  阅读(37)  评论(0)    收藏  举报  来源