某狗网歌曲接口逆向之加密算法刨析

逆向网址

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

调试测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/541401.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

天猫精灵要会员,不能听歌,还能用来干什么呢?榨干它的剩余价值

目录 起因&#xff1a;以听歌为主要功能的设备&#xff0c;却不能听歌了 1.蓝牙音箱 2.控制智能家电 3.万能遥控器&#xff0c;需要一个外接设备 4.倒计时/提醒&#xff0c;闹钟提醒&#xff0c;整点提醒&#xff08;这功能有人不喜欢&#xff0c;闲吵&#xff0c;还不能关…

职场证件照:不只是一张照片那么简单,这些细节请注意

随着毕业季的到来&#xff0c;许多应届生已经开始在各自的岗位上实习&#xff0c;准备迎接转正的挑战。在这个过程中&#xff0c;一张得体的职场证件照将成为你职业生涯中的一张重要名片。在职场中&#xff0c;证件照的应用场景多种多样&#xff0c;从窗口岗位的公示到工作牌上…

Pytorch Windows EOFError: Ran out of input when num_workers>0

关于深度学习的一些学习框架,我使用过pytorch,caffe,caffe2,openchatkit,oneflow等,最近我将长达几十万字的报错手册重新进行了整理,制作出一个新的专栏,主要记录这几种常见的开发框架在安装和使用过程中常见的报错,以及我是如何解决掉的,以此来帮助更多的深度学习开…

紫光展锐T610平台_4G安卓核心板方案定制开发

紫光展锐T610核心板配备Android 11操作系统&#xff0c;采用12nm制程工艺。该处理器CPU由2颗基于Cortex-A75架构的大核心和6颗基于Cortex-A55架构的小核心组成&#xff0c;最高主频为1.8GHz。GPU采用的是614.4MHz的Mali G52&#xff0c;可以流畅播放2400*1080分辨率视频&#x…

maven之pom中的build标签

1、build标签分类 1.1、全局配置&#xff08;project build&#xff09; 针对整个项目的所有情况都有效。 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation"htt…

知道智源开源最强语义向量模型BGE是什么吗?

Embedding模型作为大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;的一个重要辅助&#xff0c;是很多LLM应用必不可少的部分。但是&#xff0c;现实中开源的Emebdding模型却很少。北京智源人工智能研究院&#xff08;BAAI&#xff09;开源了BGE系列Emb…

Proxmox VE qm 方式恢复虚拟机

前言 使用qm 恢复Proxmox VE虚拟机&#xff0c;高效便捷。 登录Proxmox VE shell 执行恢复操作 假设备份好的文件在其它主机存储&#xff0c;我们可以下载到Proxmox VE本地目录下&#xff0c;如何执行虚拟化恢复操作--storage local-lvm&#xff08;恢复后存储到的位置&…

【linux】TCP编程{三次握手/四次挥手/API注意点/代码}

文章目录 1.API介绍1.1wc -l dirName1.2inet_pton1.3inet_aton1.4inet_ntop 2.三次握手与四次挥手1.三次握手2.四次挥手3.应用程序和TCP协议层如何交互总结 3.TCP 和 UDP 对比1.宏观2.详细 4.地址转换函数inet_ntoa 5.TCP编程代码Makefiletcp_client.cctcp_server.cctcp_server…

TypeScript学习--day1

一、介绍 TypeScript是JS的超集&#xff0c;为JS添加了类型支持。 1.1 为什么添加类型支持 JS代码的错误大部分是类型错误&#xff0c;增加改Bug时间&#xff0c;影响开发效率。 静态类型&#xff1a;编译期做类型检查 动态类型&#xff1a;执行期做类型检查 TS--静态类型编…

前端保留两位小数

一、保留两位小数&#xff08;四舍五入&#xff09; 解决方案&#xff1a;使用 toFixed(x) 方法可以对小数进行指定位数保留&#xff0c;其中x是要保留的位数用法&#xff1a;num.toFixed(x)&#xff0c;其中num为需要操作的数据&#xff0c;x为要保留的位数示例&#xff1a;1…

力扣LeetCode138. 复制带随机指针的链表 两种解法(C语言实现)

目录 题目链接 题目分析 题目定位&#xff1a; 解题思路 解题思路1&#xff08;粗暴但是复杂度高&#xff09; 解题思路2&#xff08;巧妙并且复杂度低&#xff09; 题目链接 138. 复制带随机指针的链表https://leetcode-cn.com/problems/copy-list-with-random-pointer/ …

双写一致性问题

双写一致性问题&#xff1a;同一份数据&#xff0c;需要写数据库、写缓存。数据库中的数据和缓存中的数据要一致 解决办法&#xff1a;延迟双删 当我们要进行更新操作时&#xff0c;先删除缓存&#xff0c;再更新数据库&#xff0c;延迟几百ms再删除一次redis的缓存数据。 示…

2023年蓝桥杯——日期统计

目录 题目链接&#xff1a;1.日期统计 - 蓝桥云课 (lanqiao.cn) 题目描述 思路 代码思路 定义数据结构&#xff1a; 处理每一个月&#xff1a; 检查日期序列在num100中是否存在&#xff1a; 计数匹配的日期数&#xff1a; 输出结果&#xff1a; 代码实现 总结 题目链…

【Python习题】某景区门票的优惠措施为:购买5张以内门票不打折,5到20张打九折,20张以上打八折。编写程序,根据购买的门票数量,输出总票价。

题干 某景区门票的优惠措施为:购买5张以内门票不打折&#xff0c;5到20张打九折&#xff0c;20张以上打八折。编写程序&#xff0c;根据购买的门票数量&#xff0c;输出总票价。 代码

介绍几个好用的电商(淘宝京东1688)API接口,可测试

以下是几个好用的电商&#xff08;淘宝、京东、1688&#xff09;API接口&#xff0c;这些接口都可以进行测试以确保其稳定性和可用性&#xff1a; taobao.item_get-获取淘宝商品数据接口返回值说明 1.请求方式&#xff1a;HTTP POST GET &#xff08;复制薇&#xff1a;Anzex…

2024.4.13 Python 爬虫复习day01

目录 day01_HTTP协议HTML页面web服务器 各类名词解释 URL统一资源定位符 HTTP协议 HTML页面 知识点: 第一个页面 标题标签和图片标签 注册页面 登录页面 WEB服务器 安装fastapi和uvicorn 原始命令方式 镜像源命令方式 工具方式 快速搭建web服务器 知识点: 示例…

CH254X 8051芯片手册介绍

1 8051CPU 8051是一种8位元的单芯片微控制器&#xff0c;属于MCS-51单芯片的一种&#xff0c;由英特尔(Intel)公司于1981年制造。Intel公司将MCS51的核心技术授权给了很多其它公司&#xff0c;所以有很多公司在做以8051为核心的单片机&#xff0c;如Atmel、飞利浦、深联华等公…

ARMv8-A架构下的外部debug模型之外部调试事件(external debug events)概述

外部调试器与处理器之间的握手与external debug events 一&#xff0c;External Debug的使能二&#xff0c;外部调试器和CPU之间的握手三&#xff0c;外部调试事件 External debug events1. External debug request event2. Halt instruction debug event3. Halting step debug…

是的,本科毕业八年,我考研了

今天&#xff0c;是一篇纯分享文。 是的&#xff0c;本科毕业八年&#xff0c;我考研了。 停更10个月&#xff0c;历时296天&#xff0c;我考研上岸了。 小伙伴们&#xff0c;好久不见。 一 发今年第一篇文章的时候刚处理完后续事宜&#xff0c;就简单说了句&#xff0c;后台…

Vue3 ts环境下的PropType

简介 在Typscript中&#xff0c;我们可以使用PropType进行类型的推断与验证。在日常的开发中我们常常会遇到下面这样的场景&#xff1a; 我们通过request请求从服务端获取了一条数据&#xff0c;数据是个Array的格式&#xff0c;Array中的每个元素又是一个对象&#xff0c;像下…