逆向网址
aHR0cHM6Ly93d3cua3Vnb3UuY29t
逆向链接
aHR0cHM6Ly93d3cua3Vnb3UuY29tL21peHNvbmcvN2dxcGVzNjguaHRtbA==
逆向接口
aHR0cHM6Ly93d3dhcGkua3Vnb3UuY29tL3BsYXkvc29uZ2luZm8=
逆向过程
请求方式:GET
逆向参数
signature:1898d8f157837fadc9751fdacf1398f9
过程分析
根据XHR断点方式可快速进入发包内容
再次跟栈找到位置打条件断点
可以看到此处加密参数【signature】 已经生成,继续跟栈.....
发现如下位置关键词 signature 打上断点开始调试
添加条件断点:
l.encode_album_audio_id == '7gqpes68'
l
s
那么就可以发现加密函数【d】
signature === d(s.join(""))
加密调试
调试加密字符串长度可知:32, 猜测 md5
d( '1' )
由此就可知 signature 加密方式为 MD5
参数分析
那么由上面分析可知,我们需要构建数据 【s】,那么需要知道来源,将方法全部拿出来进行分析
function c() {var t, n = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : "", o = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {}, i = !1, c = !1, a = "json", l = r({}, n), u = s.isInClient();"function" == typeof o ? t = o : (t = o.callback,i = o.useH5 || !1,a = o.postType || "json",c = o.isCDN || !1),e && ("[object Object]" != Object.prototype.toString.call(e) ? u = !1 : "urlencoded" == a && (u = !1));var f = function() {var n = (new Date).getTime(), i = [], s = [], u = "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt", f = {srcappid: "2919",clientver: "20000",clienttime: n,mid: n,uuid: n,dfid: "-"};c && (delete f.clienttime,delete f.mid,delete f.uuid,delete f.dfid),l = r({}, f, {}, l);for (var g in l)i.push(g);if (i.sort(),i.forEach(function(t) {s.push(t + "=" + l[t])}),e)if ("[object Object]" == Object.prototype.toString.call(e))if ("json" == a)s.push(JSON.stringify(e));else {var b = [];for (var g in e)b.push(g + "=" + e[g]);s.push(b.join("&"))}elses.push(e);s.unshift(u),s.push(u),l.signature = d(s.join("")),o.log && (console.log("H5签名前参数", s),console.log("H5签名后返回", l)),e ? t && t(l, "[object Object]" == Object.prototype.toString.call(e) && "json" == a ? JSON.stringify(e) : e) : t && t(l)};if (u && !i) {var g = !1;s.mobileCall(764, {get: l,post: e}, function(n) {return !g && (g = !0,n && n.status ? (delete n.status,o.log && (console.log("客户端签名前参数", {get: l,post: e}),console.log("客户端签名后返回", r({}, l, {}, n))),l = r({}, l, {}, n),e ? t && t(l, "[object Object]" == Object.prototype.toString.call(e) && "json" == a ? JSON.stringify(e) : e) : t && t(l),!1) : (u = !1,void f()))})} elseu = !1,f()
}
加密参数
["NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt","appid=1014","clienttime=1712727965638","clientver=20000","dfid=3exIvy0NDCiI1x9u9X0MmaUX","encode_album_audio_id=7gqpes68","mid=df8eb959431e3f2696fc23d514fd9bf4","platid=4","srcappid=2919","token=","userid=0","uuid=df8eb959431e3f2696fc23d514fd9bf4","NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt"
]
请求参数
{'srcappid': '2919','clientver': '20000','clienttime': '1712720179947','mid': 'df8eb959431e3f2696fc23d514fd9bf4','uuid': 'df8eb959431e3f2696fc23d514fd9bf4','dfid': '3exIvy0NDCiI1x9u9X0MmaUX','appid': '1014','platid': '4','encode_album_audio_id': '7gqpes68','token': '','userid': '0',
}
类比加密参数与请求参数下,结合加密函数
l = r({}, n), u = s.isInClient();
var n = (new Date).getTime()
s = [],
u = "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt",
f = {srcappid: "2919",clientver: "20000",clienttime: n,mid: n,uuid: n,dfid: "-"
};
l = r({}, f, {}, l);
for (var g in l)i.push(g);
if (i.sort(),
i.forEach(function(t) {s.push(t + "=" + l[t])
}),
e)
可知:
- 先将变量【f】压入变量 【l】中
- 将变量【l】中的key拿出进行排序
- 构建变量【s】利用新的排序方式以 key=val 形式构建字符串
- 将变量【u】分别加入【s】始末
逆向参数
参数分析
dfid: 3exIvy0NDCiI1x9u9X0MmaUX
mid: df8eb959431e3f2696fc23d514fd9bf4
uuid: df8eb959431e3f2696fc23d514fd9bf4
在打条件断点位置 向上跟栈....
可知由变量【bInfo】 而来,那么需要进入 【window.getBaseInfo】 内部查看
return function(t, i) {var r = {getUserInfo: 101,getVersion: 122,getMobileInfo: 124}, o = {appid: null,mid: null,uuid: null,plat: null,dfid: null,userid: null,userpic: null,userNickName: null,token: null,clientver: null};if (n.isInClient() && !n.isFXAP)n.mobileCall(r.getUserInfo, null, function(e) {1 == e.status ? (o.userid = e.kugouID,o.token = e.token,o.userpic = e.photo,o.userNickName = e.nickName,o.isVIP = e.isVIP || e.isVip) : (o.userid = null,o.token = null,o.userpic = null,o.userNickName = null),o.appid = e.appid,n.mobileCall(r.getVersion, null, function(e) {o.clientver = e.version,n.mobileCall(r.getMobileInfo, null, function(e) {o.mid = e.mid_v2 ? e.mid_v2 : e.mid,o.dfid = e.dfid ? e.dfid : "-",o.uuid = e.uuid ? e.uuid : o.mid,o.osVersion = e.osVersion ? e.osVersion : "",n.isIOS ? o.plat = 2 : o.plat = 1,i && i(o)})})});else if (n.isInClient() && n.isFXAP)n.mobileCall(625, null, function(t) {var r = JSON.parse(t.jsonStr);o.clientver = r.version,o.mid = r.mid,o.uuid = r.uuid ? r.uuid : o.mid,o.dfid = r.dfid ? r.dfid : "-",o.appid = r.appId,e.isiOS() ? o.plat = 2 : o.plat = 1,n.mobileCall(410, null, function(e) {1 == JSON.parse(e.jsonStr).status ? n.mobileCall(411, {}, function(e) {var n = JSON.parse(e.jsonStr).jsonStr ? JSON.parse(e.jsonStr).jsonStr : JSON.parse(e.jsonStr);o.userid = n.kugouId,o.token = n.token,o.pic = n.userLogo,o.nickName = n.nickName,i && i(o)}) : (o.userid = null,o.token = null,o.userpic = null,o.userNickName = null,i && i(o))})});else {o.appid = t || null,o.mid = e.getKgMid(),o.uuid = o.mid,o.plat = 4,o.dfid = e.Cookie.read("kg_dfid") || "-",o.userid = e.Cookie.read("KuGoo", "KugooID"),o.userpic = e.Cookie.read("KuGoo", "Pic"),o.userNickName = e.Cookie.read("KuGoo", "NickName"),o.token = e.Cookie.read("KuGoo", "t");var a = e.Cookie.read("KuGoo", "a_id");a && a != o.appid && o.appid && (o.userid = null,o.token = null,o.userpic = null,o.userNickName = null),o.clientver = 1e3,o.userNickName = o.userNickName ? unescape(o.userNickName) : "",i && i(o)}
}
挖掘数据
o.mid = e.getKgMid(),
o.uuid = o.mid,
o.dfid = e.Cookie.read("kg_dfid") || "-",
o.userid = e.Cookie.read("KuGoo", "KugooID"),
o.token = e.Cookie.read("KuGoo", "t");
o.clientver = 1e3,
打断点进行调试
dfid == cookie中的 kg_dfid 【3exIvy0NDCiI1x9u9X0MmaUX】
mid == uuid == e.getKgMid()
getKgMid: function() {var n = e.Cookie.read("kg_mid");if (navigator.cookieEnabled) {if (e.IsEmpty(n)) {var t = e.Guid();n = e.Md5(t);try {e.Cookie.write("kg_mid", e.Md5(t), 864e6, "/", "kugou.com")} catch (e) {}}} else {var i = navigator.userAgent, r = function() {var e = navigator.plugins, n = "";if (e.length > 0) {for (var t = [], i = 0, r = e.length; i < r; i++) {var o = e[i].name;t.push(o)}n = t.toString()}return n}(), o = screen.width + "x" + screen.height, a = screen.colorDepth ? screen.colorDepth : "", l = screen.pixelDepth ? screen.pixelDepth : "", s = function() {var n = ["canvas"];try {var t = document.createElement("canvas");if (t.getContext && t.getContext("2d")) {t.width = 200,t.height = 200,t.style.display = "inline";var i = t.getContext("2d");i.rect(0, 0, 10, 10),i.rect(2, 2, 6, 6),n.push("canvas winding:" + (!1 === i.isPointInPath(5, 5, "evenodd") ? "yes" : "no")),i.textBaseline = "alphabetic",i.fillStyle = "#f60",i.fillRect(125, 1, 62, 20),i.fillStyle = "#069",i.font = "14px 'Arial'",i.fillText("hello kugou", 2, 15),i.fillStyle = "rgba(102, 204, 0, 0.2)",i.font = "18pt Arial",i.fillText("hello kugou", 4, 45),i.globalCompositeOperation = "multiply",i.fillStyle = "rgb(255,0,255)",i.beginPath(),i.arc(50, 50, 50, 0, 2 * Math.PI, !0),i.closePath(),i.fill(),i.fillStyle = "rgb(0,255,255)",i.beginPath(),i.arc(100, 50, 50, 0, 2 * Math.PI, !0),i.closePath(),i.fill(),i.fillStyle = "rgb(255,255,0)",i.beginPath(),i.arc(75, 100, 50, 0, 2 * Math.PI, !0),i.closePath(),i.fill(),i.fillStyle = "rgb(255,0,255)",i.arc(75, 75, 75, 0, 2 * Math.PI, !0),i.arc(75, 75, 25, 0, 2 * Math.PI, !0),i.fill("evenodd"),t.toDataURL && n.push("canvas fp:" + t.toDataURL())}} catch (e) {}return e.Md5(n.toString())}();n = e.Md5(i + r + o + a + l + s)}return n
},
代码分析
// 首先取cookie kg_mid
var n = e.Cookie.read("kg_mid");// 判定 navigator中的 cookieEnabled 值
if (navigator.cookieEnabled) {// 开启}else{//关闭}
开启条件情况
//判定 n 是否为空
// 如果是空 则重新生成写入到 cookie中if (e.IsEmpty(n)) {var t = e.Guid();n = e.Md5(t);try {e.Cookie.write("kg_mid", e.Md5(t), 864e6, "/", "kugou.com")} catch (e) {}
}/// e.Guid()
Guid: function() {function e() {return (65536 * (1 + Math.random()) | 0).toString(16).substring(1)}return e() + e() + "-" + e() + "-" + e() + "-" + e() + "-" + e() + e() + e()
},
关闭条件情况
n = e.Md5(i + r + o + a + l + s)var i = navigator.userAgent
var r = navigator.plugins >>> 循环取 name 进行拼接成字符串
var o = screen.width + "x" + screen.height
var a = a = screen.colorDepth ? screen.colorDepth : ""
var l = screen.pixelDepth ? screen.pixelDepth : ""
var s = e.Md5(n.toString())n
var n = ["canvas"];
n.push("canvas fp:" + t.toDataURL())//这里的 t 为DOM画布 -> document.createElement("canvas");
Python代码
构建参数
### 构建参数
### encode_album_audio_id 歌曲短链 可由歌曲详情页链接后缀拿到
params = {'srcappid': '2919','clientver': '20000','clienttime': str( round(time.time()*1000) ),'mid': cookie['kg_mid'],'uuid': cookie['kg_mid'],'dfid': cookie['kg_dfid'],'appid': '1014','platid': '4','encode_album_audio_id': 'a9ton4f5','token': '','userid': '0',
}
提取KEY并排序
### 提取 params中的key
arrKeys = []
for key in params:arrKeys.append( key )//排序 -升序
arrKeys.sort()
构建加密字符串
### 构建加密字符串u = 'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt'
strRegParams = u
for key in arrKeys:strRegParams += f"{key}={params[key]}"strEnc = strRegParams + u
完整代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/4/10 16:11
# @Author : Carey
# @File : music.py
# @Descriptionimport requests
import time
import hashlibheaders = {'accept': '*/*','accept-language': 'en,zh-CN;q=0.9,zh;q=0.8,ja;q=0.7','origin': '{origin}','referer': '{referer}','user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
}cookie = {"kg_mid": "df8eb959431e3f2696fc23d514fd9bf4","kg_dfid": "3exIvy0NDCiI1x9u9X0MmaUX",
}### 构建参数
### encode_album_audio_id 歌曲短链 可由歌曲详情页链接后缀拿到
params = {'srcappid': '2919','clientver': '20000','clienttime': str( round(time.time()*1000) ),'mid': cookie['kg_mid'],'uuid': cookie['kg_mid'],'dfid': cookie['kg_dfid'],'appid': '1014','platid': '4','encode_album_audio_id': 'a9ton4f5','token': '','userid': '0',
}arrKeys = []
for key in params:arrKeys.append( key )arrKeys.sort()u = 'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt'
strRegParams = u
for key in arrKeys:strRegParams += f"{key}={params[key]}"strEnc = strRegParams + u
signature = hashlib.md5( strEnc.encode(encoding='UTF-8')).hexdigest()
params[ 'signature' ] = signatureresponse = requests.get('{api}', params=params, headers=headers)
print( response )
print( response.json() )