UnblockNeteaseMusic项目分析
前言
UnblockNeteaseMusic是用其他平台音源替换网易云无版权的歌曲,达到解锁灰色歌曲的目的
项目地址UnblockNeteaseMusic,在本地或服务器都可,安卓可在termux上部署
项目分析
权限“美化”与“伪造” (核心)
这是除了音源替换之外,最重要的一种解锁方式。像一个“化妆师”,负责将网易云返回的原始、带有各种限制的歌曲信息,美化成一份“完美无瑕”的数据再交给客户端。
- 核心代码:
hook.request.after函数中的inject辅助函数。 - 工作原理: 在代理拿到网易云的响应(无论是可播放还是不可播放)并准备返回给客户端之前,
inject函数会遍历这份JSON数据,并强制修改其中的关键权限字段:fee: 0: 将“费用”字段改为0,欺骗客户端这首歌是免费的,从而移除“VIP”或“付费”角标。cp: 1(或其他代表可播放的值): 将“版权”字段改为可播放状态,欺骗客户端这首歌有版权。这是让灰色歌曲的播放按钮“亮起来”的关键一步。flLevel: 'exhigh'等: 将音质等级标识强制改为最高,让客户端在界面上显示“无损”或“Hi-Res”的图标。pl/dl: 提升显示的播放码率和下载码率,让客户端认为这是一首高品质歌曲。
- 最终效果: 即使一首歌在网易云上是灰色的(
code: 404),经过这层“美化”后,在客户端的界面上也会显示为一首完全正常、高品质、可播放的歌曲。这为我们后续的“音源替换”铺平了道路,确保了播放按钮可以被点击。
“本地VIP”解锁
这个功能专门用于解锁客户端的VIP专属界面和功能,是一种纯粹的“视觉解锁”。
- 核心代码:
hook.request.after函数中处理ENABLE_LOCAL_VIP的逻辑块。 - 工作原理: 代理会专门拦截客户端查询用户VIP身份的API请求(
/api/music-vip-membership/client/vip/info)。然后,会彻底修改这份API的响应内容,伪造一个“尊贵的黑胶SVIP”身份信息(例如,修改redVipLevel,associator,musicPackage等字段),并设定一个非常久的过期时间。 - 最终效果: 客户端收到这份伪造的VIP信息后,会解锁并显示所有VIP专属的皮肤、主题、头像挂件、播放器样式等。请注意:这并不能让你真正免费听付费歌曲,付费歌曲的播放依然依赖于第一步的“音源替换”。
客户端功能解锁 (鲸云音效/歌词特效)
这与“本地VIP”类似,但更具针对性。
- 核心代码:
unblockSoundEffects和unblockLyricsEffects函数。 - 工作原理: 代理会拦截查询这些付费功能是否可用的API请求,然后修改响应中的关键布尔值,例如将
canUse改为true,或者将cannotListenReason改为null,欺骗客户端让认为已经购买了这些功能。 - 最终效果: 可以在客户端中免费使用鲸云音效、动态歌词特效等原本需要付费的功能。
操作失败“模拟” (如收藏/喜欢灰色歌曲)
这是一个非常巧妙的体验优化。
- 核心代码:
tryCollect和tryLike函数。 - 工作原理: 当尝试“喜欢”或“收藏”一首灰色歌曲时,发往网易云的请求会因为歌曲无版权而失败(例如返回
401或512错误)。代理在拦截到这个失败响应后,并不会直接把返回给客户端。相反,会自己再发起一系列新的API请求(例如,先查询到“我喜欢的音乐”歌单ID,然后再调用添加歌曲到歌单的接口),强行将这首歌加入的歌单。操作成功后,会伪造一个code: 200的成功响应返回给客户端。 - 最终效果: 在的客户端界面上,那个“红心”会立刻点亮,给一种操作成功的无缝体验,尽管背后的实现过程非常曲折。
替换流程
以安卓端clash代理为例,流程主要分为四个阶段:DNS劫持、前置请求处理、后置响应处理 与 代理流式传输。
阶段一:DNS劫持与连接建立 (Clash)
- DNS查询拦截:当网易云音乐客户端启动或需要与API服务器通信时,会向操作系统发起一个DNS查询,请求解析域名
interface.music.163.com。 - DNS欺骗:Clash截获了这个DNS查询。根据配置的
DOMAIN-SUFFIX,163.com,🎶 网易音乐规则,Clash并不会向上游DNS服务器查询真实IP,而是直接返回在proxies部分为UnblockNeteaseMusic代理节点配置的IP地址和端口。 - 建立TCP连接:客户端的操作系统收到这个“伪造”的IP地址后,便信任地与其建立TCP连接。因此,客户端实际上是直接与
UnblockNeteaseMusic代理服务器建立了连接,尽管本身以为自己连接的是网易云官方服务器。
阶段二:前置请求处理 (hook.request.before)
- 接收HTTP请求:客户端通过已建立的TCP连接,发送一个加密的
HTTP POST请求到/eapi/song/enhance/player/url或者/eapi/song/enhance/player/url/v1。 - 触发
before钩子:server.js接收到此请求,并将其传递给hook.request.before函数进行处理。 - 请求体解密:函数首先读取加密的请求体(Request Body),并调用
crypto.js中的解密函数 (eapi.decrypt) 将其还原为明文。 - 参数提取与净化:
- 从解密后的明文中,程序解析出真实的API路径 (
/api/song/enhance/player/url) 和请求参数 (params对象)。 - 程序从
params对象中提取出歌曲ID。通过songId.toString().match(/\d+/)正则表达式,将ID净化为纯数字形式(例如,从1962165898_0得到1962165898),以兼容不同客户端的ID格式。
- 从解密后的明文中,程序解析出真实的API路径 (
- 启动并行查询:
- 程序立即使用净化后的ID调用
match(sanitizedSongId)函数。 - 这个调用是异步的。程序不会等待
match函数返回结果,而是将这个正在执行的Promise对象存入当前请求的上下文ctx.alternativeSearchPromise中。 - 与此同时,
hook.request.before函数的执行流程继续。
- 程序立即使用净化后的ID调用
- 转发原始请求:由于我们已经移除了所有对请求的修改逻辑,
hook.request.before执行完毕后,代理会将客户端原始的、未经修改的请求,忠实地转发给网易云官方服务器。
阶段三:后置响应处理 (hook.request.after & tryMatch)
- 接收双边响应:在接下来的几百毫秒内,代理服务器会异步地等待两个结果:
- 来自网易云官方服务器的响应(通常是一个表示“无版权”的失败响应)。
- 之前在后台启动的
alternativeSearchPromise的完成(即match函数从的自建解析服务获取到的结果)。
- 触发
after钩子:一旦收到网易云的响应,hook.request.after函数被触发。首先解密网易云的响应体。 - 调用
tryMatch决策:程序进入tryMatch函数,并执行“强制替换”逻辑:- 函数通过
await或.then()等待之前存入ctx.alternativeSearchPromise的任务完成。由于此任务早已开始,这里的等待时间通常很短。 - 函数检查
matchedSong(来自解析服务的结果)是否存在。 - 只要
matchedSong存在,程序就忽略网易云返回的数据。
- 函数通过
- **响应体构建 (
/package/代理流)**:- 程序提取
matchedSong中的url、br、size等核心数据。 - 将
realUrl(真实的第三方音源链接) 进行Base64编码。 - 读取
global.endpoint(通过-e参数设置的代理自身地址),并构建一个指向代理自身的、永不过期的代理流URL,格式为http://<endpoint>/package/<encodedUrl>/<songId>.flac。默认是music.163.com。 - 程序用这个代理流URL和从
matchedSong获取的其他数据,彻底重写了从网易云返回的响应JSON对象中的data部分。
- 程序提取
- “美化”与重加密:被重写后的JSON对象会经过
inject函数进行最后的“美化”(如修正flag、encodeType等),然后被重新加密。 - 返回最终响应:最终,这份包含了
/package/链接的、经过完美伪装的加密响应被发送回客户端。
阶段四:代理流式传输 (处理 /package/ 请求)
- 客户端发起流请求:客户端收到响应后,解析出
/package/链接。的播放器向这个链接发起一个HTTP GET请求以获取音频数据,此请求可能会包含Range头用于分块缓冲。 - 代理拦截自身请求:这个请求的目标地址就是代理服务器自身,因此被
hook.request.before再次捕获。 - 处理
package路由:函数检测到URL中包含package,进入专门的处理逻辑:- 从URL中提取出Base64编码的部分,并将其解码,从而得到那个真实的、会过期的第三方音源链接。
- 代理服务器作为客户端,向这个真实的第三方链接发起请求(如果原始请求有
Range头,代理也会带上)。 - 代理从第三方服务器接收到音频数据流。
- 数据转发:代理不等待文件下载完成,而是将接收到的音频数据实时地、流式地转发(pipe)给客户端。
- 持续服务:客户端的播放器会为歌曲的每一部分重复发起对
/package/链接的Range请求。代理则会为每一次请求重复步骤3和4,从而实现了对整个音频文件的无缝流式传输,完全屏蔽了真实链接的时效性问题。
项目使用
通过hysteria2加速
在部署UnblockNeteaseMusic的服务器上建立hysteria2节点,出站分流将网易云请求传给UnblockNeteaseMusic。
客户端代理分流将网易云请求交给hysteria2节点处理。
规则配置
ios小火箭
1 | USER-AGENT,NeteaseMusic*,服务器解锁 |
clash
1 | #安卓端termux部署项目需要在clash放行应用termux |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Meng's blog!