Skip to content

Commit

Permalink
refactor: Decrypt according to the 'e_r' parameter (#1454)
Browse files Browse the repository at this point in the history
* feat: whether the eapi return value is needs to be encrypted is controlled by the param ‘e_r’ (refer to NeteaseCloudMusicApi)

* feat(crypto/hook): support 'api' crypto
  • Loading branch information
s12mmm3 authored Aug 10, 2024
1 parent 8073825 commit 11c1ed5
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 60 deletions.
9 changes: 9 additions & 0 deletions src/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ module.exports = {
};
},
},
api: {
encryptRequest: (url, object) => {
url = parse(url);
return {
url: url.href.replace(/\w*api/, 'api'),
body: bodyify(object),
};
},
},
linuxapi: {
encrypt: (buffer) => encrypt(buffer, linuxapiKey),
decrypt: (buffer) => decrypt(buffer, linuxapiKey),
Expand Down
172 changes: 112 additions & 60 deletions src/hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ const domainList = [
'iplay.163.com',
'look.163.com',
'y.163.com',
'interface.music.163.com',
'interface3.music.163.com',
];

hook.request.before = (ctx) => {
Expand All @@ -117,7 +119,7 @@ hook.request.before = (ctx) => {
: (req.socket.encrypted ? 'https:' : 'http:') +
'//' +
(domainList.some((domain) =>
(req.headers.host || '').endsWith(domain)
(req.headers.host || '').includes(domain)
)
? req.headers.host
: null)) + req.url;
Expand Down Expand Up @@ -146,7 +148,7 @@ hook.request.before = (ctx) => {
hook.target.host.has(host)
) &&
req.method === 'POST' &&
(url.path === '/api/linux/forward' || url.path.startsWith('/eapi/'))
(url.path.startsWith('/eapi/') || url.path.startsWith('/api/'))
) {
return request
.read(req)
Expand All @@ -162,41 +164,74 @@ hook.request.before = (ctx) => {
return; // look living/cloudupload eapi can not be decrypted
req.headers['Accept-Encoding'] = 'gzip, deflate'; // https://blog.csdn.net/u013022222/article/details/51707352
if (body) {
let data;
const netease = {};
netease.pad = (body.match(/%0+$/) || [''])[0];
netease.forward = url.path === '/api/linux/forward';
if (netease.forward) {
data = JSON.parse(
crypto.linuxapi
if (url.path === '/api/linux/forward') {
netease.crypto = 'linuxapi'
}
else if (url.path.startsWith('/eapi/')) {
netease.crypto = 'eapi'
}
else if (url.path.startsWith('/api/')) {
netease.crypto = 'api'
}
let data;
switch (netease.crypto) {
case 'linuxapi':
data = JSON.parse(
crypto.linuxapi
.decrypt(
Buffer.from(
body.slice(
8,
body.length - netease.pad.length
),
'hex'
)
)
.toString()
);
netease.path = parse(data.url).path;
netease.param = data.params;
break;
case 'eapi':
data = crypto.eapi
.decrypt(
Buffer.from(
body.slice(
8,
7,
body.length - netease.pad.length
),
'hex'
)
)
.toString()
);
netease.path = parse(data.url).path;
netease.param = data.params;
} else {
data = crypto.eapi
.decrypt(
Buffer.from(
body.slice(
7,
body.length - netease.pad.length
),
'hex'
)
)
.toString()
.split('-36cd479b6b5-');
netease.path = data[0];
netease.param = JSON.parse(data[1]);
.split('-36cd479b6b5-');
netease.path = data[0];
netease.param = JSON.parse(data[1]);
if (
netease.param.hasOwnProperty('e_r') &&
(netease.param.e_r == 'true' ||
netease.param.e_r == true)
) {
// eapi's e_r is true, needs to be encrypted
netease.e_r = true;
} else {
netease.e_r = false;
}
break;
case 'api':
data = {};
decodeURIComponent(body).split('&').forEach((pair) => {
let [key, value] = pair.split('=');
data[key] = value
});
netease.path = url.path
netease.param = data
break;
default:
// unsupported crypto
break;
}
netease.path = netease.path.replace(/\/\d*$/, '');
ctx.netease = netease;
Expand Down Expand Up @@ -292,15 +327,16 @@ hook.request.after = (ctx) => {
/([^\\]"\s*:\s*)(\d{16,})(\s*[}|,])/g,
'$1"$2L"$3'
); // for js precision
try {
netease.encrypted = false;
netease.jsonBody = JSON.parse(patch(buffer.toString()));
} catch (error) {
netease.encrypted = true;

if (netease.e_r) {
// eapi's e_r is true, needs to be encrypted
netease.jsonBody = JSON.parse(
patch(crypto.eapi.decrypt(buffer).toString())
);
}
else {
netease.jsonBody = JSON.parse(patch(buffer.toString()));
}

if (ENABLE_LOCAL_VIP) {
const vipPath = '/api/music-vip-membership/client/vip/info';
Expand Down Expand Up @@ -466,7 +502,7 @@ hook.request.after = (ctx) => {
/([^\\]"\s*:\s*)"(\d{16,})L"(\s*[}|,])/g,
'$1$2$3'
); // for js precision
proxyRes.body = netease.encrypted
proxyRes.body = netease.e_r // eapi's e_r is true, needs to be encrypted
? crypto.eapi.encrypt(Buffer.from(body))
: body;
})
Expand Down Expand Up @@ -526,14 +562,22 @@ const pretendPlay = (ctx) => {
const { req, netease } = ctx;
const turn = 'http://music.163.com/api/song/enhance/player/url';
let query;
if (netease.forward) {
const { id, br } = netease.param;
netease.param = { ids: `["${id}"]`, br };
query = crypto.linuxapi.encryptRequest(turn, netease.param);
} else {
const { id, br, e_r, header } = netease.param;
netease.param = { ids: `["${id}"]`, br, e_r, header };
query = crypto.eapi.encryptRequest(turn, netease.param);
const { id, br, e_r, header } = netease.param;
switch (netease.crypto) {
case 'linuxapi':
netease.param = { ids: `["${id}"]`, br };
query = crypto.linuxapi.encryptRequest(turn, netease.param);
break;
case 'eapi':
case 'api':
netease.param = { ids: `["${id}"]`, br, e_r, header };
if (netease.crypto == 'eapi')
query = crypto.eapi.encryptRequest(turn, netease.param);
else if (netease.crypto == 'api')
query = crypto.api.encryptRequest(turn, netease.param);
break;
default:
break;
}
req.url = query.url;
req.body = query.body + netease.pad;
Expand All @@ -543,26 +587,34 @@ const pretendPlayV1 = (ctx) => {
const { req, netease } = ctx;
const turn = 'http://music.163.com/api/song/enhance/player/url/v1';
let query;
if (netease.forward) {
const { id, level, immerseType } = netease.param;
netease.param = {
ids: `["${id}"]`,
level,
encodeType: 'flac',
immerseType,
};
query = crypto.linuxapi.encryptRequest(turn, netease.param);
} else {
const { id, level, immerseType, e_r, header } = netease.param;
netease.param = {
ids: `["${id}"]`,
level,
encodeType: 'flac',
immerseType,
e_r,
header,
};
query = crypto.eapi.encryptRequest(turn, netease.param);
const { id, level, immerseType, e_r, header } = netease.param;
switch (netease.crypto) {
case 'linuxapi':
netease.param = {
ids: `["${id}"]`,
level,
encodeType: 'flac',
immerseType,
};
query = crypto.linuxapi.encryptRequest(turn, netease.param);
break;
case 'eapi':
case 'api':
netease.param = {
ids: `["${id}"]`,
level,
encodeType: 'flac',
immerseType,
e_r,
header,
};
if (netease.crypto == 'eapi')
query = crypto.eapi.encryptRequest(turn, netease.param);
else if (netease.crypto == 'api')
query = crypto.api.encryptRequest(turn, netease.param);
break;
default:
break;
}
req.url = query.url;
req.body = query.body + netease.pad;
Expand Down

0 comments on commit 11c1ed5

Please sign in to comment.