逆向网址
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("&"))
}
else
s.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()))
})
} else
u = !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
# @Description
import requests
import time
import hashlib
headers = {
'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' ] = signature
response = requests.get('{api}', params=params, headers=headers)
print( response )
print( response.json() )