网易云音乐NCM格式转化为mp3

网易云音乐NCM格式转化为mp3

月光魔力鸭

2021-06-09 00:45 阅读 1322 喜欢 7 ncm转mp3 nodejs 转 ncm

前段时间帮朋友下歌放在车上听..结果好多都是ncm格式,伤心 ,搜索了下发现基本上这格式解密有好多昂,可惜UI我都不太想要..决定抄一下,自己做一个。 这里先记录下核心代码,回头补充个UI 做个小程序。

ncm是啥

ncm 是网易云音乐的音乐容器格式,并非是音乐格式,文件内带有meta cover audio 等信息,所以我们需要根据对应的加密算法,解密回来转成mp3 内容进行播放。

这个解密很多人都已经实现了,可惜要么是python 要么是c 要么是go ,有一个js实现的结果是浏览器的,当然浏览器跟nodejs 也没啥差别,不过写代码的意义就在于折腾嘛,准备做个nodejs版本的,顺便再加个electron的界面。

ncm 格式

借用下大神的图,现有的格式内容

ncm解密

由于所有的ncm的密钥都是相同的,所以这里就直接写死就可以了,如果不同..那就没办法了,可能更新了或有了其他的处理。出现的话就只能等待大神给密钥了。

直接上代码,注释都有记录的

const filePath = 'd:/nodejs/toy/网易云NCM2MP3/2.ncm';
const fs = require('fs');
const crypto = require('crypto');

let content = fs.readFileSync(filePath);

(async function () {
  let start = 0;
  let buff = Buffer.from(content);
  //1.读取8字节,获取 magic header
  let temp = buff.slice(start, start+8);
  start += 10;//空余 2 字节
  let header = Buffer.from([0x43, 0x54, 0x45, 0x4E, 0x46, 0x44, 0x41, 0x4D]);
  if (!header.equals(temp)) {
    throw new Error('文件头已损坏');
  }
  //读取 32 位 4字节的密钥长度
  let keyLength = buff.readUInt32LE(start);
  start += 4;
  //根据密钥长度读取密钥内容
  temp = buff.slice(start, start + keyLength);
  let cipherText = temp.map(t => {
    return t ^ 0x64;
  })

  start += keyLength;

  //解密的key
  let key = Buffer.from('687a4852416d736f356b496e62617857', 'hex');
  //aes-128-ecb 解密
  let decipher = crypto.createDecipheriv('aes-128-ecb', key, '');
  let decodeText = decipher.update(cipherText);
  decodeText += decipher.final();
  //得到 keyData
  let keyData = decodeText.substr(17).trim();
  let key2Len = keyData.length;
  keyData = Buffer.from(keyData);


  //ttt
  //将keyData 做 RC4-KSA 算法解密 
  let keyBox = Buffer.alloc(256);
  for (let i = 0; i < keyBox.length; i++){
    keyBox[i] = i;
  }
  let j = 0;
  for (let i = 0; i < 256; i++) {
    j = (keyBox[i] + j + keyData[i % key2Len]) & 0xff;
    [keyBox[i], keyBox[j]] = [keyBox[j], keyBox[i]];
  }

  //读取4字节获取meta 长度
  let metaLen = buff.readUInt32LE(start);
  start += 4;
  //读取meta内容
  let metaContent = buff.slice(start, start + metaLen);
  metaContent = metaContent.map(t => t ^ 0x63)
  //去掉 22位 163 key(Don't modify):
  metaContent = metaContent.toString();
  metaContent = metaContent.substr(22);
  metaContent = Buffer.from(metaContent, 'base64');

  //解密meta 内容
  const metaKey = Buffer.from("2331346C6A6B5F215C5D2630553C2728",'hex');
  let decipher2 = crypto.createDecipheriv('aes-128-ecb', metaKey, '');
  let meta = decipher2.update(metaContent);
  meta += decipher2.final('utf8');

  meta = meta.substr(6);
  meta = JSON.parse(meta);
  start += metaLen;

  //5字节空白
  start += 5;
  //读取 4字节 crc32校验码
  let crc32 = buff.readUInt32LE(start);
  start += 4;
  

  //读取4字节图片大小
  let imgLen = buff.readUInt32LE(start);
  start += 4;

  //写入图片数据
  fs.writeFileSync('./1.jpg', buff.slice(start, start + imgLen))
  start += imgLen;

  let audioData = buff.slice(start);
  let m = 0;
  for (let i = 1; i < audioData.length + 1; i++){
    m = i & 0xff;
    audioData[i-1] ^= keyBox[(keyBox[m] + keyBox[(keyBox[m] + m) & 0xff]) & 0xff]
  }
  
  
  fs.writeFileSync('d:/nodejs/toy/网易云NCM2MP3/2.mp3', audioData);




})();

其实也就是照着大神的代码抄一遍... 还是等我补UI吧,尽量做个好看易用的..

转载请注明出处: https://chrunlee.cn/article/netnase-ncm-2-mp3.html


感谢支持!

赞赏支持
提交评论
评论信息 (请文明评论)
暂无评论,快来快来写想法...
推荐
通过node-xlsx模块读取excel和写入
前几天给朋友帮忙,想要一个一模一样的网站...自告奋勇去帮忙.. 结果发现之前一直没处理过类似的情况,虽然也写过爬虫,不过看了下网站,也不算麻烦。于是简单实现了这个自动抓站的功能,最终整理成为一个自动抓站的工具,能省很多的事情。
从豆瓣转到网易云后,发现了不少好听的歌曲,然鹅..当我想把这些歌拿下来扔车上听的时候发现竟然不允许下载..能听不能下?这不科学,作为一名程序猿,必然要迎难而上啊.
通过pm2来实现nodejs应用的集群,不过我之前没做session共享,导致.. 登录不上啊 啊啊啊,无奈,又重新对redis进行了集成。
通过nodejs来进行爬取页面的内容,这里简单试试做个小任务..
产品版本更新的时候经常会有一些数据库的差异,如果版本管理好的话,一步一步升级即可.. 但是如果好久没更新的话,还是有很多不确定的,只能挨着比对表和字段。比对了一次就烦了,写了这么一个工具,查询差异表和字段并给出sql语句。
在开发项目过程中,经常需要将开发的项目部署到服务器上,但是每个环境都有每个环境的配置等等,如果每次打包的时候都要去调整(可能删除、替换等),那就很烦人了,这里分享下自己实现的几个简单的小工具(当然这个工具可能只对我自己有用),希望能够帮到你。
经常会遇到需要系统重启后自动执行的一些任务,在windows 上可以将对应的程序打包成service 然后自启动即可