前言

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”类似,但更具针对性。

  • 核心代码: unblockSoundEffectsunblockLyricsEffects 函数。
  • 工作原理: 代理会拦截查询这些付费功能是否可用的API请求,然后修改响应中的关键布尔值,例如将 canUse 改为 true,或者将 cannotListenReason 改为 null,欺骗客户端让认为已经购买了这些功能。
  • 最终效果: 可以在客户端中免费使用鲸云音效、动态歌词特效等原本需要付费的功能。

操作失败“模拟” (如收藏/喜欢灰色歌曲)

这是一个非常巧妙的体验优化。

  • 核心代码: tryCollecttryLike 函数。
  • 工作原理: 当尝试“喜欢”或“收藏”一首灰色歌曲时,发往网易云的请求会因为歌曲无版权而失败(例如返回 401512 错误)。代理在拦截到这个失败响应后,并不会直接把返回给客户端。相反,会自己再发起一系列新的API请求(例如,先查询到“我喜欢的音乐”歌单ID,然后再调用添加歌曲到歌单的接口),强行将这首歌加入的歌单。操作成功后,会伪造一个 code: 200 的成功响应返回给客户端。
  • 最终效果: 在的客户端界面上,那个“红心”会立刻点亮,给一种操作成功的无缝体验,尽管背后的实现过程非常曲折。

替换流程

以安卓端clash代理为例,流程主要分为四个阶段:DNS劫持前置请求处理后置响应处理代理流式传输


阶段一:DNS劫持与连接建立 (Clash)

  1. DNS查询拦截:当网易云音乐客户端启动或需要与API服务器通信时,会向操作系统发起一个DNS查询,请求解析域名 interface.music.163.com
  2. DNS欺骗:Clash截获了这个DNS查询。根据配置的 DOMAIN-SUFFIX,163.com,🎶 网易音乐 规则,Clash并不会向上游DNS服务器查询真实IP,而是直接返回在 proxies 部分为 UnblockNeteaseMusic 代理节点配置的IP地址和端口。
  3. 建立TCP连接:客户端的操作系统收到这个“伪造”的IP地址后,便信任地与其建立TCP连接。因此,客户端实际上是直接与 UnblockNeteaseMusic 代理服务器建立了连接,尽管本身以为自己连接的是网易云官方服务器。

阶段二:前置请求处理 (hook.request.before)

  1. 接收HTTP请求:客户端通过已建立的TCP连接,发送一个加密的 HTTP POST 请求到 /eapi/song/enhance/player/url或者/eapi/song/enhance/player/url/v1
  2. 触发before钩子server.js 接收到此请求,并将其传递给 hook.request.before 函数进行处理。
  3. 请求体解密:函数首先读取加密的请求体(Request Body),并调用 crypto.js 中的解密函数 (eapi.decrypt) 将其还原为明文。
  4. 参数提取与净化
    • 从解密后的明文中,程序解析出真实的API路径 (/api/song/enhance/player/url) 和请求参数 (params 对象)。
    • 程序从 params 对象中提取出歌曲ID。通过 songId.toString().match(/\d+/) 正则表达式,将ID净化为纯数字形式(例如,从 1962165898_0 得到 1962165898),以兼容不同客户端的ID格式。
  5. 启动并行查询
    • 程序立即使用净化后的ID调用 match(sanitizedSongId) 函数。
    • 这个调用是异步的。程序不会等待 match 函数返回结果,而是将这个正在执行的 Promise 对象存入当前请求的上下文 ctx.alternativeSearchPromise 中。
    • 与此同时,hook.request.before 函数的执行流程继续
  6. 转发原始请求:由于我们已经移除了所有对请求的修改逻辑,hook.request.before 执行完毕后,代理会将客户端原始的、未经修改的请求,忠实地转发给网易云官方服务器。

阶段三:后置响应处理 (hook.request.after & tryMatch)

  1. 接收双边响应:在接下来的几百毫秒内,代理服务器会异步地等待两个结果:
    • 来自网易云官方服务器的响应(通常是一个表示“无版权”的失败响应)。
    • 之前在后台启动的 alternativeSearchPromise 的完成(即 match 函数从的自建解析服务获取到的结果)。
  2. 触发after钩子:一旦收到网易云的响应,hook.request.after 函数被触发。首先解密网易云的响应体。
  3. 调用tryMatch决策:程序进入 tryMatch 函数,并执行“强制替换”逻辑:
    • 函数通过 await.then() 等待之前存入 ctx.alternativeSearchPromise 的任务完成。由于此任务早已开始,这里的等待时间通常很短。
    • 函数检查 matchedSong(来自解析服务的结果)是否存在。
    • 只要 matchedSong 存在,程序就忽略网易云返回的数据。
  4. **响应体构建 (/package/ 代理流)**:
    • 程序提取 matchedSong 中的 urlbrsize 等核心数据。
    • realUrl (真实的第三方音源链接) 进行Base64编码。
    • 读取 global.endpoint(通过 -e 参数设置的代理自身地址),并构建一个指向代理自身的、永不过期的代理流URL,格式为 http://<endpoint>/package/<encodedUrl>/<songId>.flac。默认是music.163.com
    • 程序用这个代理流URL和从 matchedSong 获取的其他数据,彻底重写了从网易云返回的响应JSON对象中的 data 部分。
  5. “美化”与重加密:被重写后的JSON对象会经过 inject 函数进行最后的“美化”(如修正flagencodeType等),然后被重新加密。
  6. 返回最终响应:最终,这份包含了 /package/ 链接的、经过完美伪装的加密响应被发送回客户端。

阶段四:代理流式传输 (处理 /package/ 请求)

  1. 客户端发起流请求:客户端收到响应后,解析出 /package/ 链接。的播放器向这个链接发起一个 HTTP GET 请求以获取音频数据,此请求可能会包含 Range 头用于分块缓冲。
  2. 代理拦截自身请求:这个请求的目标地址就是代理服务器自身,因此被 hook.request.before 再次捕获。
  3. 处理package路由:函数检测到URL中包含 package,进入专门的处理逻辑:
    • 从URL中提取出Base64编码的部分,并将其解码,从而得到那个真实的、会过期的第三方音源链接。
    • 代理服务器作为客户端,向这个真实的第三方链接发起请求(如果原始请求有Range头,代理也会带上)。
    • 代理从第三方服务器接收到音频数据流。
  4. 数据转发:代理不等待文件下载完成,而是将接收到的音频数据实时地、流式地转发(pipe)给客户端。
  5. 持续服务:客户端的播放器会为歌曲的每一部分重复发起对 /package/ 链接的 Range 请求。代理则会为每一次请求重复步骤3和4,从而实现了对整个音频文件的无缝流式传输,完全屏蔽了真实链接的时效性问题。

项目使用

通过hysteria2加速

在部署UnblockNeteaseMusic的服务器上建立hysteria2节点,出站分流将网易云请求传给UnblockNeteaseMusic

客户端代理分流将网易云请求交给hysteria2节点处理。

规则配置

ios小火箭

1
2
3
4
5
USER-AGENT,NeteaseMusic*,服务器解锁
DOMAIN-SUFFIX,music.163.com,服务器解锁
DOMAIN-SUFFIX,mam.netease.com,服务器解锁
DOMAIN-SUFFIX,hz.netease.com,服务器解锁
DOMAIN-SUFFIX,music.126.net,DIRECT

clash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#安卓端termux部署项目需要在clash放行应用termux
#安卓端支持版本:8.6.20之前
proxies:
- {name: "服务端解锁", type: http, server: your-server-ip, port: }
- {name: "本地解锁", type: http, server: 127.0.0.1, port: }

proxy-groups:
- name: 🎶 网易音乐
type: select
proxies:
- DIRECT
- "服务端解锁"
- "本地解锁"

rules:
- DOMAIN,music.163.com,🎶 网易音乐
- DOMAIN,interface.music.163.com,🎶 网易音乐
- DOMAIN,interface3.music.163.com,🎶 网易音乐
- DOMAIN,apm.music.163.com,🎶 网易音乐
- DOMAIN,apm3.music.163.com,🎶 网易音乐
- DOMAIN,interface.music.163.com.163jiasu.com,🎶 网易音乐
- DOMAIN,interface3.music.163.com.163jiasu.com,🎶 网易音乐