网易云音乐ncm格式分析以及ncm与mp3格式转换 您所在的位置:网站首页 mp3是什么格式的音乐 网易云音乐ncm格式分析以及ncm与mp3格式转换

网易云音乐ncm格式分析以及ncm与mp3格式转换

2024-06-28 22:20| 来源: 网络整理| 查看: 265

目录NCM格式分析音频知识简介两种可能GitHub项目格式分析总体结构密钥问题代码分析main函数导入模块dump函数参考资料代码完整版转换工具ncmdumpncmdump-guincm-mp3NCM文件转换 昨天,我想将网易云上下载的歌曲拷到MP3里面,方便以后跑5公里的时候听,结果,突然发现不少歌都是ncm格式,不禁产生了好奇。

NCM格式分析 音频知识简介

特意读了一下《音视频开发进阶指南》,总结如下: 我们平常说的mp3格式、wav格式的音乐其实是说的压缩编码格式。 一首歌是怎么从歌手的喉咙里发出后变成一个文件的呢? 需要经过采样、量化和编码三个步骤。

采样 声音是连续的模拟信号,通过采样,将之转变为离散的数字信号,其中要遵循的是奈奎斯特定理:只要采样频率不低于声音信号最高频率的两倍,采样得到的数字信号就能保真地记录、还原声音。 人耳能够听到的范围是20Hz到20kHz,所以采样频率一般为44.1kHz,这样就可以保证采样声音达到20kHz也能被数字化,从而使得经过数字化处理之后,人耳听到的声音质量不会被降低。而所谓的44.1kHz就是代表1秒会采样44100次 量化 量化是指在幅度轴上对信号进行数字化,就是用多少位的数据来记录一个采样。比如用16比特的二进制信号来表示声音的一个采样,而16比特(一个short)所表示的范围是[-32768,32767],共有65536个可能取值,因此最终模拟的音频信号在幅度上也分为了65536层 编码 编码就是我们按一定的格式对采样和量化后的数字数据进行记录。直接存储的话,文件可能过大,像CD那样直接存储下来的没什么问题,但如果要在网络中在线传播,就必须进行压缩。 压缩的原理是压缩掉冗余信号,包括人耳感知不到的信号以及人耳掩蔽效应(指人耳只对最明显的声音反应敏感)掩蔽掉的信号。同时压缩算法包括有损压缩和无损压缩。无损压缩是指解压后的数据可以完全复原。有损压缩是指解压后的数据不能完全复原,会丢失一部分信息。 两种可能

第一种可能是网易独立进行了压缩编码算法的研究,创造出来的新的格式。 第二种是在现有格式的基础上,增加了一些冗余信息,相当于将一首MP3格式的歌放入密码箱中,付费者可开启。 不管是哪种,都必须了解格式的构成。

GitHub项目

我自知学艺不精,所以去万能的GitHub上寻求答案。 果然有先驱者,貌似是anonymous5l提供了最初的ncmdump版本,然后再由其他几位大佬进行重构和功能完善

anonymous5l(C++,MIT协议) 基于openssl库编写,所以速度非常快,而且又好。 nondanee(python,MIT协议) 依赖pycryptodome库、mutagen库,比较完善了。 lianglixin(python,MIT协议) fork的nondanee作者的源码,修改了依赖库依赖pycrypto库,会有一些安装和使用问题 yoki123(go,MIT协议) 依据anonymous51的工作,使用go语言实现 格式分析 总体结构

首先,我从yoki123那里找到了一张NCM结构图 由此可得知,NCM 实际上不是音频格式是容器格式,封装了对应格式的 Meta 以及封面等信息

密钥问题

另外,NCM使用了NCM使用了AES加密,但每个NCM加密的密钥是一样的,因此只要获取了AES的密钥KEY,就可以根据格式解开对应的资源。 AES我知道,一种对称加密算法嘛,这学期刚好学了网络密码。 AES是一种迭代型分组加密算法,分组长度为128bit,密钥长度为128、192或256bit,不同的密钥长度对应的迭代轮数不同,对应关系如下:

密钥长度 轮数 128 10 192 12 256 14 我最好奇的是AES的密钥是怎么搞到的。出于“不可能只有我一个人好奇”的信念,看了好几个项目的README.md以及issues 结果只有一个人在yoki123的项目中issues了这个问题, 大佬表示,他的密钥也是从annoymous51处获得的,但他推测是通过反编译播放器客户端得到的。 并给出了三条原因: 播放器也需要读取ncm格式,客户端就包含有解密逻辑 解密算法是AES,是对称加密 恰巧所有的文件都使用了相同的AES key,那么key在客户端播放器中就是一个常量

而作为第一个搞到密钥的大佬annoymous51,他的项目中竟然没有一个人问这个问题,我自己问了一下,看大佬会不会回复

代码分析

密钥的问题暂时不纠结了,接下来对照lianglixin的代码来钻研, lianglixin 可以看到项目中有两个文件 从提交说明来看,folder_dump.py实现的是批量的转换,虽说Python文件操作的部分不难,但是有人做了这个工作也省得我自己动手了。 在她的README.md中说明了需要安装依赖库pycrypto,使用pip install pycrypto安装,但如果使用了Anaconda,就不需要装了 代码地址为:https://github.com/lianglixin/ncmdump/blob/master/folder_dump.py 相比于C++版本和Go语言版本,Python实现出来相对比较好懂,结构十分明朗, 只有main函数和dump函数

main函数

main函数中用来进行文件操作,根据输入的参数中的文件夹,在此文件夹中的全部文件中进行筛选,找到.ncm格式的文件,执行dump函数 这个程序按理来说,运行的方法是在命令行中cd到此文件所在路径,然后输入python folder_dump.py ncm保存文件夹路径 但这种方式挺麻烦的,而且程序中竟然还有变量都没有定义,比如rootdir,因此无法运行成功, 于是我对她这一部分再次进行了修改,我将main函数改成如下所示的内容:

if __name__ == '__main__': file_path = input("请输入文件所在路径(例如:E:\\ncm_music)\n") list = os.listdir(file_path) # Get all files in folder. for i in range(0,len(list)): # path = os.path.join("E:\\ncm_music",list[i]) path = os.path.join(file_path, list[i]) print(path) if os.path.isfile(path): if os.path.isfile(path): if file_extension(path) == ".ncm": try: dump(path) except: pass

效果如下:

导入模块

然后看看导入的模块

import binascii import struct import base64 import json import os from Crypto.Cipher import AES binascii的主要作用是实现进制和字符串之间的转换。 Python提供了struct模块,它是一个类似C或C++的struct结构,配合其模块提供的方法可以将二进制数据与Python的数据结构互相转换。 Base64 是网络上最常见的用于传输 8Bit 字节码的编码方式之一,Base64 就是一种基于 64 个可打印字符来表示二进制数据的方法。可查看 RFC2045 ~ RFC2049,上面有 MIME 的详细规范。Base64 编码是从二进制到字符的过程,可用于在 HTTP 环境下传递较长的标识信息。比如使二进制数据可以作为电子邮件的内容正确地发送,用作 URL 的一部分,或者作为 HTTP POST 请求的一部分。 json模块提供了对JSON的支持,它既包含了将JSON字符串恢复成Python对象的函数,也提供了将Python对象转换成JSON字符串的函数。 os模块提供了多数操作系统的功能接口函数。当os模块被导入后,它会自适应于不同的操作系统平台,根据不同的平台进行相应的操作,在python编程时,经常和文件、目录打交道,所以离不开os模块。 Crypto是一个加密算法模块,Cipher是该模块下的对称加密算法对象。 dump函数

最后看看dump函数,这个才是重点

1. def dump(file_path): 2. core_key = binascii.a2b_hex("687A4852416D736F356B496E62617857") 3. meta_key = binascii.a2b_hex("2331346C6A6B5F215C5D2630553C2728") 4. unpad = lambda s : s[0:-(s[-1] if type(s[-1]) == int else ord(s[-1]))] 5. f = open(file_path,'rb') 6. header = f.read(8) 7. assert binascii.b2a_hex(header) == b'4354454e4644414d' 8. f.seek(2, 1) 9. key_length = f.read(4) 10. key_length = struct.unpack('= key_length: key_offset = 0 key_box[i] = key_box[c] key_box[c] = swap last_byte = c meta_length = f.read(4) meta_length = struct.unpack('


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有