ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

QMCDecode 实战解析:QQ音乐 QMC 加密音频还原 FLAC/MP3 的完整指南

QMCDecode 实战解析:QQ音乐 QMC 加密音频还原 FLAC/MP3 的完整指南 QMCDecode 实战解析QQ音乐 QMC 加密音频还原 FLAC/MP3 的完整指南【免费下载链接】QMCDecodeQQ音乐QMC格式转换为普通格式(qmcflac转flacqmc0,qmc3转mp3, mflac,mflac0等转flac)仅支持macOS可自动识别到QQ音乐下载目录默认转换结果存储到~/Music/QMCConvertOutput,可自定义需要转换的文件和输出路径项目地址: https://gitcode.com/gh_mirrors/qm/QMCDecodeQMCDecode 是一款运行在 macOS 上的音频格式还原工具核心能力是把 QQ 音乐下载到本地的 QMC 加密文件qmcflac、qmc0、qmc3、mflac、mgg 等转换回 flac、mp3、ogg 等标准格式让用户真正拥有可播放、可迁移的音乐文件。对需要备份、跨设备或换播放器的音乐爱好者来说它是值得收藏的工具对想了解对称加密 密钥派生工程实践的开发者它也是一份不错的 Swift 源码教材。一、先聊聊那道锁为什么 QQ 音乐缓存文件换台设备就放不出来很多人遇到过这样的场景在 Mac 上开通了 QQ 音乐会员下载了几百首歌想把它们拷到手机或车载播放器里结果发现这些文件的后缀名是.qmcflac、.mflac这类生面孔主流播放器一律不认。为什么会这样因为下载到本地的并不是标准音频文件而是经过加密处理的原始音频数据。简单说播放器厂商会拿一串密钥对音频的每一个字节做异或运算之类的变换把数据彻底打乱同时把这串密钥塞进文件本身随文件一起下发。客户端在播放时再拿着钥匙把锁打开解码成正常音频播放。这种做法的直接后果就是钥匙只在 QQ 音乐客户端手里。普通播放器没有钥匙自然打不开文件。就算你把文件拷走也只是拷贝了一份乱码。那么问题来了有没有办法把钥匙偷出来自己开锁答案是可以而且钥匙就藏在文件里——这正是 QMCDecode 的切入点。理解了这一点后面整条技术链路就顺理成章了。二、十分钟上手把 QQ 音乐缓存目录变成普通音乐文件夹先别急着啃源码QMCDecode 是一个很轻的 macOS 原生小工具使用体验可以概括为三步打开应用、确认文件、点 Start。它的界面布局相当直白我们直接看图四个关键区域各司其职界面区域作用操作方式文件选择按钮打开文件/目录选择器点击后可以单选文件、多选文件或整个文件夹文件列表展示待转换文件路径 歌曲名自动填充也可以手动勾选输出路径设置转换结果存放位置默认为~/Music/QMCConvertOutput/可改Start 按钮 进度条触发批量转换并反馈进度点击后开始处理结束后弹窗汇总成功/失败数最贴心的一点是自动识别应用启动后会直接扫描 QQ 音乐 Mac 版的缓存目录沙盒容器内的Library/Application Support/QQMusicMac/iQmc/把里面所有能处理的加密文件一次性列出来几乎不用手动找路径。当然如果你把文件整理到了别处也可以用文件选择按钮手动指定。目前支持的格式映射关系如下加密输入还原输出qmcflac / qmflac / bkcflacflacqmc0 / qmc3 / bkcmp3mp3qmc2 / qmcogg / mgg / mgg1oggmflac / mflac0flactkmm4a666c6163 / 6d7033 / 6f6767 / 6d3461 / 776176flac / mp3 / ogg / m4a / wav最后一行是十六进制命名的扩展名本质是把目标格式的 ASCII 码直接当文件名后缀例如666c6163就是十六进制编码的flac。看到这里你应该已经感觉到这个项目的格式处理逻辑相当灵活。但点击 Start 之后后台到底发生了什么我们进入内核。三、拆开看内核钥匙怎么找、锁怎么开、流程怎么串QMCDecode 的代码规模不大核心文件只有四个QMCKeyDecoder.swift解钥匙、TeaCipher.swiftTEA 算法、QMCipher.swift三套开锁器、QMDecoder.swift总调度。整条链路由三道工序组成。工序一从文件尾部摸钥匙音频数据被加密后文件尾部会附加一段信息一部分是密钥一部分是格式标记。QMDecoder 打开文件后先读取最后 4 个字节判断钥匙的存放方式如果结尾是QTag字符串说明这是移动端下载的文件密钥长度以大端序写在倒数第 8~5 字节且密钥本身以英文逗号结尾否则按PC/macOS 端处理末尾 4 字节直接是密钥长度小端序密钥紧挨着它。这里有一个很关键的分支如果读出来的密钥长度大于等于0x300768 字节说明这个文件根本没有单独的密钥而是用一套内置固定密钥加密的——对应着最初几版 QQ 音乐客户端。这时候直接跳过密钥解码环节拿内置的privateKey256当钥匙用。把这段逻辑用伪代码表达长这样func searchKey() throws { let tail readLast4Bytes() if tail QTag { let keySize readUInt32(bigEndian: true) // 移动端大端序长度 let rawKey readBytes(at: fileLength - keySize - 8, count: keySize) let key rawKey.prefix(upTo: rawKey.firstIndex(of: 0x2C)!) // 逗号截断 cipher buildCipher(from: key) } else { let keySize tail.littleEndianValue if keySize 0x300 { cipher QMStaticCipher(key: builtinKey256) // 旧版固定密钥 } else { let rawKey readBytes(at: fileLength - keySize - 4, count: keySize) cipher buildCipher(from: rawKey) } } }这一步的关键在于密钥不是网络下发后被客户端用完即焚而是跟着文件一起落盘所以才给了本地还原的机会。工序二用 TEA 把钥匙的钥匙解开从文件里摸出来的原始密钥是经过二次加密的外层还有 Base64 和盐不能直接用来解音频。QMCDecode 用TEA 算法一种轻量分组密码对 64 位数据块做多轮混淆来还原真正的密钥。流程可以这样理解先取一段简单密钥把原始密钥的前 8 个字节与它交织拼成 16 字节的 TEA 密钥然后对剩余数据做 CBC 模式的 TEA 解密最后还要校验尾部若干字节必须为 0防止解出来一堆垃圾。func deriveKey(_ raw: [UInt8]) throws - [UInt8] { let base64 try decodeBase64(raw) // 去掉外层 Base64 guard base64.count 16 else { throw .keyTooShort } let simple simpleKey(seed: 106, length: 8) // 盐 三角函数生成 var teaKey UInt8 for i in 0..8 { teaKey[i*2] simple[i] teaKey[i*21] base64[i] // 与原文交织 } let body Array(base64[8...]) // 跳过前 8 字节 let decrypted try decryptTencentTea(body, key: teaKey) // 带盐校验的 TEA-CBC return Array(base64[0..8]) decrypted // 前 8 字节原样保留 }这里的盐salt和零校验zero check是腾讯系协议里特有的防御细节代码里通过saltLength 2、zeroLength 7两个常量控制。如果你自己实现解密这两处校验最容易出错——很多解出来是乱码的案例根源就在这里。工序三三套开锁器按需切换拿到真正的密钥后下一步是选解密算法。QMCDecode 通过一个协议统一了三种实现public protocol QMCipher { func qmDecrypt(data: Data, offset: Int) - Data init(originKey: [UInt8]) throws }三种实现的差异和适用场景如下实现类加密思路典型对应文件特点QMStaticCipher固定密钥 偏移量取模旧版 qmc0 / qmc3逻辑最简单掩码 key[(offset² 27) 0xFF]QMMapCipher密钥表 循环移位qmcflac / qmflac在静态基础上叠加了 8 方向位旋转抗破解性略高QMRC4Cipher类 RC4 流密码 分段处理mflac / mflac0 / mgg安全性最高按 128 字节首段 5120 字节分段滚动选型规则非常直观解密后的密钥超过 300 字节走 RC4 体系否则走映射体系。因为密钥越长通常代表越新的加密协议也越复杂。以 RC4 实现为例它的核心在于分段滚动前 128 字节用起始偏移直接异或之后按 5120 字节对齐分段每一段的密钥流都由上一段的状态继续生成而不是从头重来。这既保证了安全性也让流式处理大文件成为可能。public func qmDecrypt(data: Data, offset: Int) - Data { var out Array(data) // 阶段一首 128 字节使用分段密钥直接异或 // 阶段二对齐到 5120 边界后逐段调用 encodeAllSegment // 阶段三处理不足一整段的尾部残块 return Data(out) }三道工序串起来就是完整的解码链路摸钥匙 → 解钥匙 → 开锁。整个过程中QMDecoder 负责第 1 步和调度QMCKeyDecoder 负责第 2 步三个 Cipher 负责第 3 步。模块边界非常干净这也是我们后面讲二次开发时最大的底气。四、从源码到可执行文件最快的一键编译路线想动手改代码第一步是把它跑起来。项目结构不复杂编译路径很短按下面四步走基本不会踩坑获取源码git clone https://gitcode.com/gh_mirrors/qm/QMCDecode然后cd QMCDecode打开工程open QMCDecode.xcodeproj会自动唤起 Xcode选对 target确认左上角 Scheme 是QMCDecodemacOS 应用而不是测试 target签名与构建在 Signing Capabilities 里选择你自己的开发者账号或关闭签名跑本地调试然后Cmd B编译Cmd R运行需要说明的是项目依赖很少只有系统自带的 Cocoa 和 Foundation 框架不需要安装任何第三方包管理器也不涉及 Podfile 或 SPM。环境上建议使用 Xcode 11 及以上Swift 5 可编译系统版本 macOS 10.13 以上基本都能跑。编译完成后你会看到两个可交互的入口文件ViewController.swift负责界面交互与任务调度WindowController.swift负责窗口生命周期。想改界面就去 storyboard想改逻辑就去这几个 Swift 文件改动成本很低。五、让它跑得更快四项值得照抄的优化实践源码里藏了不少值得学习的性能处理手法这里挑四个最实用的讲。1. 按 CPU 核心数拆队列让每个核都吃饱批量转换是 IO CPU 混合任务串行做会浪费多核。项目在启动转换时读取ProcessInfo().processorCount按物理核心数创建等量的DispatchQueue再把文件按索引轮询分发let coreCount ProcessInfo().processorCount for index in 0..files.count { let queue queues[index % coreCount] // 轮询分发负载自然均衡 queue.async { try? QMDecoder(file: files[index]).decryptAndWriteToFile() } }每个队列使用utility优先级既保证吞吐又不至于把系统 UI 卡死。2. 只在主线程碰 UI进度更新统一回主队列解码线程完成任务后通过DispatchQueue.main.async回主线程更新进度条和计数。这避免了多线程同时写 UI 的竞态问题——代码里成功数、失败数都用主线程串行累加转换结束时一次性弹窗汇总。3. 原子写入防止输出文件写一半解密结果写出时使用了.atomic写入选项。系统会先写临时文件再替换目标文件即使中途断电或崩溃也不会留下半个残文件。这个习惯对任何生成文件型工具都值得借鉴。4. 输出目录懒加载 自动创建输出路径默认指向~/Music/QMCConvertOutput/首次访问时若目录不存在会自动创建并在 UI 上同步显示。这意味着用户打开应用就能直接开转省掉了先建文件夹的心智负担。六、高频故障自查手册三个最常被问的问题用这类工具问题往往集中在格式识别、转换失败和输出损坏上。下面三个场景覆盖了绝大多数反馈。问题一文件列表是空的缓存目录里明明有歌现象打开应用后左侧列表没有自动加载任何文件。排查步骤确认 QQ 音乐 Mac 客户端把文件下载到了本地在线缓存的临时文件不一定带 QMC 后缀检查扩展名是否在支持列表里——应用只认encryptExtDictionary中登记的扩展名冷门格式会被过滤用Choose File按钮手动指向文件所在的真实目录绕过自动扫描问题二转换完成了但输出文件播放不了现象进度条走满、提示成功结果文件打开是静音或报错。排查步骤先确认源文件本身完整没有被其他工具改过比如被改名过扩展名检查是不是移动端 QTag 尾巴的文件——这类文件的密钥里含逗号结束符解析要求严格老版本 QQ 音乐下载的文件更容易出现这种情况用十六进制工具看一眼输出文件头flac 应以fLaC开头mp3 应有 ID3 或帧同步字。头部不对说明密钥解错了而不是音频损坏用 kid3 这类标签工具批量修复元数据很多播放器显示异常其实只是 tag 字段乱码问题三转换中途卡住界面像死了一样现象大批量转换时进度条长时间不动。排查步骤确认是不是同时选了超大文件几百 MB 的 flac单文件耗时本身就不短看活动监视器里 CPU 是否被打满——项目设计就是尽量跑死 CPU卡顿在预期内等它跑完即可如果长时间完全无响应可能是某文件尾部解析异常导致死等建议把文件分批转换定位到具体出问题的那个文件七、把它改造成你的工具三个扩展方向与最终判断最后聊聊二次开发。前面说过这个项目的模块边界非常清晰做扩展基本是加表、加类、加分支的套路。方向一扩展新格式。QMC 协议家族一直在演化新后缀出现时你只需要在Constants.swift的映射表里登记输入扩展名 → 输出扩展名 加密版本再确保对应的 Cipher 能处理即可。版本号v1/v2字段就是为未来协议预留的开关。方向二给密钥解码补单元测试。QMCKeyDecoder的deriveKey是纯函数式逻辑非常适合用 XCTest 固定一批真实样本做回归测试。目前测试文件还是模板状态填上几个已知密钥的断言就能防止后续改动把解密链路改坏。方向三剥出命令行版本。QMDecoder本身不依赖任何 UI 代码完全可以把解码核心抽成独立库再套一层 CLI 入口做成qmdecode input.qmcflac -o output.flac这样的工具方便脚本化批量处理。至于值不值得用结论很明确推荐使用你使用 macOS且手上有自己下载的 QQ 音乐本地缓存文件希望备份、迁移或跨播放器使用或者你想研究文件尾部藏密钥 多算法切换这种真实工程案例。不建议你根本不用 QQ 音乐或者手里的文件来源不明、涉及版权争议——工具本身只做格式还原不负责解决授权问题另外它只支持 macOSWindows 和 Linux 用户需要另找方案。一句话总结QMCDecode 是那种小而美的典型——代码量不大却把密钥派生、多算法分派、多核调度这些概念完整串了起来。读懂它你收获的不只是一个好用的转换工具更是一套可以迁移到其他场景的加密文件还原方法论。【免费下载链接】QMCDecodeQQ音乐QMC格式转换为普通格式(qmcflac转flacqmc0,qmc3转mp3, mflac,mflac0等转flac)仅支持macOS可自动识别到QQ音乐下载目录默认转换结果存储到~/Music/QMCConvertOutput,可自定义需要转换的文件和输出路径项目地址: https://gitcode.com/gh_mirrors/qm/QMCDecode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表