华为音乐的歌曲无法解密 #48

Open
opened 2023-09-20 02:34:24 +00:00 by Selene · 10 comments

该APP下载的歌曲无法在工单里上传,麻烦您访问onedrive:https://1drv.ms/f/s!AoIYwZ1ut_KhiHwhJc4faKije8_i?e=jwH2tT

该APP下载的歌曲无法在工单里上传,麻烦您访问onedrive:https://1drv.ms/f/s!AoIYwZ1ut_KhiHwhJc4faKije8_i?e=jwH2tT
Author

客户端为PC端,Windows11

客户端为PC端,Windows11
Owner

请提供app下载地址或一并上传到网盘

请提供app下载地址或一并上传到网盘
Author

请提供app下载地址或一并上传到网盘
apk上传至onedrive:https://1drv.ms/f/s!AoIYwZ1ut_KhiQHdZK8l0fgWn935?e=SjUHd7
(下载歌曲需安装HMS Core,我将该软件一起上传到了onedrive中)

> 请提供app下载地址或一并上传到网盘 apk上传至onedrive:https://1drv.ms/f/s!AoIYwZ1ut_KhiQHdZK8l0fgWn935?e=SjUHd7 (下载歌曲需安装HMS Core,我将该软件一起上传到了onedrive中)
lsr changed title from 华为音乐的歌曲无法破解 to 华为音乐的歌曲无法解密 2023-10-11 20:35:53 +00:00
lsr added the
help wanted
component-crypto
platform-android
labels 2023-11-02 18:49:38 +00:00

com.android.mediacenter.playback.player类的downLoadSource(PathBean pathBean, String str)方法看,貌似分两种加密格式(AES/OFB 和 HW)
OFB的解密逻辑貌似由 Lcom/android/common/components/security/FileMediaDataSource;类下的构造方法、readAt和d方法完成,
按照ai分析的结果用py实现了一个解密脚本,但输出文件无法正常播放,但HEX看到输出文件的文件头似乎正确? 播放器可以正确获取时长,孩子太菜了实在干不动了
附件,包含使用的华为音乐app本体、HMS、测试脚本、一个加密文件
调试过程偶然发现某一方法会获取到CDN未加密文件的直链,附件对应未加密歌曲文件

com.android.mediacenter.playback.player类的downLoadSource(PathBean pathBean, String str)方法看,貌似分两种加密格式(AES/OFB 和 HW) OFB的解密逻辑貌似由 Lcom/android/common/components/security/FileMediaDataSource;类下的构造方法、readAt和d方法完成, 按照ai分析的结果用py实现了一个解密脚本,但输出文件无法正常播放,但HEX看到输出文件的文件头似乎正确? 播放器可以正确获取时长,孩子太菜了实在干不动了 [附件](https://www.123684.com/s/pdiKTd-XhUC),包含使用的华为音乐app本体、HMS、测试脚本、一个加密文件 调试过程偶然发现某一方法会获取到CDN未加密文件的直链,[附件对应未加密歌曲文件](https://www.123684.com/s/pdiKTd-nhUC)
Owner

com.android.mediacenter.playback.player类的downLoadSource(PathBean pathBean, String str)方法看,貌似分两种加密格式(AES/OFB 和 HW)
OFB的解密逻辑貌似由 Lcom/android/common/components/security/FileMediaDataSource;类下的构造方法、readAt和d方法完成,
按照ai分析的结果用py实现了一个解密脚本,但输出文件无法正常播放,但HEX看到输出文件的文件头似乎正确? 播放器可以正确获取时长,孩子太菜了实在干不动了
附件,包含使用的华为音乐app本体、HMS、测试脚本、一个加密文件
调试过程偶然发现某一方法会获取到CDN未加密文件的直链,附件对应未加密歌曲文件

能否换成不需要登录就可以下载的网盘?
例如微软的 OneDrive、Google Drive、mega.nz 等。

或直接到 tg 讨论群组上传也可。

> com.android.mediacenter.playback.player类的downLoadSource(PathBean pathBean, String str)方法看,貌似分两种加密格式(AES/OFB 和 HW) > OFB的解密逻辑貌似由 Lcom/android/common/components/security/FileMediaDataSource;类下的构造方法、readAt和d方法完成, > 按照ai分析的结果用py实现了一个解密脚本,但输出文件无法正常播放,但HEX看到输出文件的文件头似乎正确? 播放器可以正确获取时长,孩子太菜了实在干不动了 > [附件](https://www.123684.com/s/pdiKTd-XhUC),包含使用的华为音乐app本体、HMS、测试脚本、一个加密文件 > 调试过程偶然发现某一方法会获取到CDN未加密文件的直链,[附件对应未加密歌曲文件](https://www.123684.com/s/pdiKTd-nhUC) 能否换成不需要登录就可以下载的网盘? 例如微软的 OneDrive、Google Drive、mega.nz 等。 或直接到 tg 讨论群组上传也可。

com.android.mediacenter.playback.player类的downLoadSource(PathBean pathBean, String str)方法看,貌似分两种加密格式(AES/OFB 和 HW)
OFB的解密逻辑貌似由 Lcom/android/common/components/security/FileMediaDataSource;类下的构造方法、readAt和d方法完成,
按照ai分析的结果用py实现了一个解密脚本,但输出文件无法正常播放,但HEX看到输出文件的文件头似乎正确? 播放器可以正确获取时长,孩子太菜了实在干不动了
附件,包含使用的华为音乐app本体、HMS、测试脚本、一个加密文件
调试过程偶然发现某一方法会获取到CDN未加密文件的直链,附件对应未加密歌曲文件

能否换成不需要登录就可以下载的网盘?
例如微软的 OneDrive、Google Drive、mega.nz 等。

或直接到 tg 讨论群组上传也可。

https://1drv.ms/u/c/d979081d97ec3bf9/EUucOJn2elhAuqgmrvG7VRQBUef9Dvcn3hQNUoK1AeaFaw?e=btAGji

> > com.android.mediacenter.playback.player类的downLoadSource(PathBean pathBean, String str)方法看,貌似分两种加密格式(AES/OFB 和 HW) > > OFB的解密逻辑貌似由 Lcom/android/common/components/security/FileMediaDataSource;类下的构造方法、readAt和d方法完成, > > 按照ai分析的结果用py实现了一个解密脚本,但输出文件无法正常播放,但HEX看到输出文件的文件头似乎正确? 播放器可以正确获取时长,孩子太菜了实在干不动了 > > [附件](https://www.123684.com/s/pdiKTd-XhUC),包含使用的华为音乐app本体、HMS、测试脚本、一个加密文件 > > 调试过程偶然发现某一方法会获取到CDN未加密文件的直链,[附件对应未加密歌曲文件](https://www.123684.com/s/pdiKTd-nhUC) > > 能否换成不需要登录就可以下载的网盘? > 例如微软的 OneDrive、Google Drive、mega.nz 等。 > > 或直接到 tg 讨论群组上传也可。 https://1drv.ms/u/c/d979081d97ec3bf9/EUucOJn2elhAuqgmrvG7VRQBUef9Dvcn3hQNUoK1AeaFaw?e=btAGji
Owner

com.android.mediacenter.playback.player类的downLoadSource(PathBean pathBean, String str)方法看,貌似分两种加密格式(AES/OFB 和 HW)
OFB的解密逻辑貌似由 Lcom/android/common/components/security/FileMediaDataSource;类下的构造方法、readAt和d方法完成,
按照ai分析的结果用py实现了一个解密脚本,但输出文件无法正常播放,但HEX看到输出文件的文件头似乎正确? 播放器可以正确获取时长,孩子太菜了实在干不动了
附件,包含使用的华为音乐app本体、HMS、测试脚本、一个加密文件
调试过程偶然发现某一方法会获取到CDN未加密文件的直链,附件对应未加密歌曲文件

因为 OFB 不适合流媒体(随机快进),对比两个文件发现在 0x800 处开始不一致。盲猜是每 0x800 为一段。

试了下果然如此,每处理 0x800 字节的时候重置 AES 实例(主要是 IV)即可。

from Crypto.Cipher import AES
import base64

# 示例:使用动态调试获得的密钥和初始化向量
key = base64.b64decode("islnah6dNam8+lVsafVz/g==")  # 16/24/32字节密钥,具体取决于AES模式
iv = base64.b64decode("xTIYxsCR17Uyi60RYt/Tzw==")  # 16字节IV,通常为AES的块大小

# 加载加密文件
encrypted_file_path = r"郭顶-凄美地(6016916_3)"
decrypted_file_path = r"output.flac"


def decrypt_chunk(chunk):
    cipher = AES.new(key, AES.MODE_OFB, iv=iv)
    return cipher.decrypt(chunk)


def decrypt_file(encrypted_file_path, decrypted_file_path, key, iv):
    with open(decrypted_file_path, "wb") as decrypted_file:
        with open(encrypted_file_path, "rb") as encrypted_file:
            while data_in := encrypted_file.read(0x800):
                decrypted_file.write(decrypt_chunk(data_in))

    print(f"File decrypted successfully: {decrypted_file_path}")


if __name__ == "__main__":
    decrypt_file(encrypted_file_path, decrypted_file_path, key, iv)

目前不清楚的是,Key/IV 是固定的,还是不同的(下载的时候预先加密,或是下载后利用用户信息的一部分加密)。

> > > com.android.mediacenter.playback.player类的downLoadSource(PathBean pathBean, String str)方法看,貌似分两种加密格式(AES/OFB 和 HW) > > > OFB的解密逻辑貌似由 Lcom/android/common/components/security/FileMediaDataSource;类下的构造方法、readAt和d方法完成, > > > 按照ai分析的结果用py实现了一个解密脚本,但输出文件无法正常播放,但HEX看到输出文件的文件头似乎正确? 播放器可以正确获取时长,孩子太菜了实在干不动了 > > > [附件](https://www.123684.com/s/pdiKTd-XhUC),包含使用的华为音乐app本体、HMS、测试脚本、一个加密文件 > > > 调试过程偶然发现某一方法会获取到CDN未加密文件的直链,[附件对应未加密歌曲文件](https://www.123684.com/s/pdiKTd-nhUC) 因为 OFB 不适合流媒体(随机快进),对比两个文件发现在 0x800 处开始不一致。盲猜是每 0x800 为一段。 试了下果然如此,每处理 0x800 字节的时候重置 AES 实例(主要是 IV)即可。 ```py from Crypto.Cipher import AES import base64 # 示例:使用动态调试获得的密钥和初始化向量 key = base64.b64decode("islnah6dNam8+lVsafVz/g==") # 16/24/32字节密钥,具体取决于AES模式 iv = base64.b64decode("xTIYxsCR17Uyi60RYt/Tzw==") # 16字节IV,通常为AES的块大小 # 加载加密文件 encrypted_file_path = r"郭顶-凄美地(6016916_3)" decrypted_file_path = r"output.flac" def decrypt_chunk(chunk): cipher = AES.new(key, AES.MODE_OFB, iv=iv) return cipher.decrypt(chunk) def decrypt_file(encrypted_file_path, decrypted_file_path, key, iv): with open(decrypted_file_path, "wb") as decrypted_file: with open(encrypted_file_path, "rb") as encrypted_file: while data_in := encrypted_file.read(0x800): decrypted_file.write(decrypt_chunk(data_in)) print(f"File decrypted successfully: {decrypted_file_path}") if __name__ == "__main__": decrypt_file(encrypted_file_path, decrypted_file_path, key, iv) ``` 目前不清楚的是,Key/IV 是固定的,还是不同的(下载的时候预先加密,或是下载后利用用户信息的一部分加密)。
Owner

跟了大半天的 setSecretKey / getSecretKey,终于看到它会从 SQLite 数据库拿数据。

CREATE TABLE IF NOT EXISTS "online_song_shiting_cache" ("id" INTEGER PRIMARY KEY AUTOINCREMENT ,"playlistid" TEXT,"onlineId" TEXT,"url" TEXT,"filePath" TEXT,"lastModified" TEXT,"totalLen" TEXT,"type" TEXT,"fileType" TEXT,"visitControl" TEXT,"quality" INTEGER NOT NULL ,"encrypt_type" TEXT,"encrypt_iv" TEXT,"copyrightType" TEXT,"secretKey" TEXT,"partialCache" TEXT,"piceCount" INTEGER NOT NULL ,"CACHED_PICE" TEXT,"INSTRUMENTS" TEXT,"CHANNEL" TEXT,"status" TEXT);

CREATE TABLE IF NOT EXISTS "run_cache_music" ("pk" INTEGER PRIMARY KEY AUTOINCREMENT ,"playlistID" TEXT NOT NULL ,"onlineID" TEXT NOT NULL ,"quality" TEXT,"songExInfo" TEXT,"songStyle" TEXT,"cacheState" INTEGER NOT NULL ,"cacheSize" INTEGER NOT NULL ,"encrypt_type" TEXT,"encrypt_iv" TEXT,"copyrightType" TEXT,"secretKey" TEXT);

CREATE TABLE IF NOT EXISTS "download_record" ("id" INTEGER PRIMARY KEY AUTOINCREMENT ,"online_id" TEXT,"song_name" TEXT,"singer" TEXT,"online_url" TEXT,"quality" TEXT,"album_pic_url" TEXT,"singer_pic_url" TEXT,"portal" TEXT,"audio_id" TEXT,"state" INTEGER NOT NULL ,"download_size" INTEGER NOT NULL ,"total_size" INTEGER NOT NULL ,"duration" INTEGER NOT NULL ,"completed_time" INTEGER NOT NULL ,"download_path" TEXT,"relative_path" TEXT,"error_code" INTEGER NOT NULL ,"encrypt_type" TEXT,"user_id" TEXT,"visitControl" TEXT,"albumID" TEXT,"artistCode" TEXT,"encrypt_iv" TEXT,"copyrightType" TEXT,"secretKey" TEXT,"exInfo" TEXT,"albumName" TEXT,"contentExInfo" TEXT,"contentType" TEXT,"columnExInfo" TEXT,"isHide" INTEGER,"pinYin" TEXT,"explicit" TEXT,"isLocal" INTEGER,"fileID" TEXT,"copyright_state" TEXT,"force_info" TEXT,"delHide" INTEGER,"songStatus" INTEGER,"country" TEXT,"categoryList" TEXT);

CREATE TABLE IF NOT EXISTS "download_record2" ("id" INTEGER PRIMARY KEY AUTOINCREMENT ,"online_id" TEXT,"song_name" TEXT,"singer" TEXT,"online_url" TEXT,"quality" TEXT,"album_pic_url" TEXT,"singer_pic_url" TEXT,"portal" TEXT,"audio_id" TEXT,"state" INTEGER NOT NULL ,"download_size" INTEGER NOT NULL ,"total_size" INTEGER NOT NULL ,"duration" INTEGER NOT NULL ,"completed_time" INTEGER NOT NULL ,"download_path" TEXT,"relative_path" TEXT,"error_code" INTEGER NOT NULL ,"encrypt_type" TEXT,"user_id" TEXT,"visitControl" TEXT,"albumID" TEXT,"artistCode" TEXT,"encrypt_iv" TEXT,"copyrightType" TEXT,"secretKey" TEXT,"exInfo" TEXT,"albumName" TEXT,"contentExInfo" TEXT,"contentType" TEXT,"columnExInfo" TEXT,"isHide" INTEGER,"pinYin" TEXT,"explicit" TEXT,"isLocal" INTEGER,"fileID" TEXT,"copyright_state" TEXT,"force_info" TEXT,"delHide" INTEGER,"songStatus" INTEGER,"country" TEXT,"categoryList" TEXT,"extendInfo" TEXT,"migrateStatus" INTEGER NOT NULL ,"migrateErrorCode" INTEGER NOT NULL ,"uriString" TEXT);

CREATE TABLE IF NOT EXISTS "online_pre_download_cache" ("id" INTEGER PRIMARY KEY AUTOINCREMENT ,"playlistid" TEXT,"onlineId" TEXT,"url" TEXT,"filePath" TEXT,"lastModified" TEXT,"listenCount" INTEGER NOT NULL ,"totalLen" INTEGER NOT NULL ,"curCachelLen" INTEGER NOT NULL ,"fileType" TEXT,"visitControl" TEXT,"quality" INTEGER NOT NULL ,"type" TEXT,"encrypt_type" TEXT,"encrypt_iv" TEXT,"copyrightType" TEXT,"secretKey" TEXT);

如果你不介意的话,可以将应用私有目录的这些数据库上传(不推荐,因为可能含有私人数据),或动手能力强的话,可以手动翻翻这些数据库看看哪些文件/表有这个 encrypt_ivsecretKey

项目需要的内容:

  • db 文件路径(方便写使用指南)
  • 所有可能存在密钥 / IV 的表

没有的话我应该也能从 APK 里面找到。

乐:华为把其中一个表叫做 “在线播放拉屎缓存” 💩

跟了大半天的 setSecretKey / getSecretKey,终于看到它会从 SQLite 数据库拿数据。 ```sql CREATE TABLE IF NOT EXISTS "online_song_shiting_cache" ("id" INTEGER PRIMARY KEY AUTOINCREMENT ,"playlistid" TEXT,"onlineId" TEXT,"url" TEXT,"filePath" TEXT,"lastModified" TEXT,"totalLen" TEXT,"type" TEXT,"fileType" TEXT,"visitControl" TEXT,"quality" INTEGER NOT NULL ,"encrypt_type" TEXT,"encrypt_iv" TEXT,"copyrightType" TEXT,"secretKey" TEXT,"partialCache" TEXT,"piceCount" INTEGER NOT NULL ,"CACHED_PICE" TEXT,"INSTRUMENTS" TEXT,"CHANNEL" TEXT,"status" TEXT); CREATE TABLE IF NOT EXISTS "run_cache_music" ("pk" INTEGER PRIMARY KEY AUTOINCREMENT ,"playlistID" TEXT NOT NULL ,"onlineID" TEXT NOT NULL ,"quality" TEXT,"songExInfo" TEXT,"songStyle" TEXT,"cacheState" INTEGER NOT NULL ,"cacheSize" INTEGER NOT NULL ,"encrypt_type" TEXT,"encrypt_iv" TEXT,"copyrightType" TEXT,"secretKey" TEXT); CREATE TABLE IF NOT EXISTS "download_record" ("id" INTEGER PRIMARY KEY AUTOINCREMENT ,"online_id" TEXT,"song_name" TEXT,"singer" TEXT,"online_url" TEXT,"quality" TEXT,"album_pic_url" TEXT,"singer_pic_url" TEXT,"portal" TEXT,"audio_id" TEXT,"state" INTEGER NOT NULL ,"download_size" INTEGER NOT NULL ,"total_size" INTEGER NOT NULL ,"duration" INTEGER NOT NULL ,"completed_time" INTEGER NOT NULL ,"download_path" TEXT,"relative_path" TEXT,"error_code" INTEGER NOT NULL ,"encrypt_type" TEXT,"user_id" TEXT,"visitControl" TEXT,"albumID" TEXT,"artistCode" TEXT,"encrypt_iv" TEXT,"copyrightType" TEXT,"secretKey" TEXT,"exInfo" TEXT,"albumName" TEXT,"contentExInfo" TEXT,"contentType" TEXT,"columnExInfo" TEXT,"isHide" INTEGER,"pinYin" TEXT,"explicit" TEXT,"isLocal" INTEGER,"fileID" TEXT,"copyright_state" TEXT,"force_info" TEXT,"delHide" INTEGER,"songStatus" INTEGER,"country" TEXT,"categoryList" TEXT); CREATE TABLE IF NOT EXISTS "download_record2" ("id" INTEGER PRIMARY KEY AUTOINCREMENT ,"online_id" TEXT,"song_name" TEXT,"singer" TEXT,"online_url" TEXT,"quality" TEXT,"album_pic_url" TEXT,"singer_pic_url" TEXT,"portal" TEXT,"audio_id" TEXT,"state" INTEGER NOT NULL ,"download_size" INTEGER NOT NULL ,"total_size" INTEGER NOT NULL ,"duration" INTEGER NOT NULL ,"completed_time" INTEGER NOT NULL ,"download_path" TEXT,"relative_path" TEXT,"error_code" INTEGER NOT NULL ,"encrypt_type" TEXT,"user_id" TEXT,"visitControl" TEXT,"albumID" TEXT,"artistCode" TEXT,"encrypt_iv" TEXT,"copyrightType" TEXT,"secretKey" TEXT,"exInfo" TEXT,"albumName" TEXT,"contentExInfo" TEXT,"contentType" TEXT,"columnExInfo" TEXT,"isHide" INTEGER,"pinYin" TEXT,"explicit" TEXT,"isLocal" INTEGER,"fileID" TEXT,"copyright_state" TEXT,"force_info" TEXT,"delHide" INTEGER,"songStatus" INTEGER,"country" TEXT,"categoryList" TEXT,"extendInfo" TEXT,"migrateStatus" INTEGER NOT NULL ,"migrateErrorCode" INTEGER NOT NULL ,"uriString" TEXT); CREATE TABLE IF NOT EXISTS "online_pre_download_cache" ("id" INTEGER PRIMARY KEY AUTOINCREMENT ,"playlistid" TEXT,"onlineId" TEXT,"url" TEXT,"filePath" TEXT,"lastModified" TEXT,"listenCount" INTEGER NOT NULL ,"totalLen" INTEGER NOT NULL ,"curCachelLen" INTEGER NOT NULL ,"fileType" TEXT,"visitControl" TEXT,"quality" INTEGER NOT NULL ,"type" TEXT,"encrypt_type" TEXT,"encrypt_iv" TEXT,"copyrightType" TEXT,"secretKey" TEXT); ``` 如果你不介意的话,可以将应用私有目录的这些数据库上传(不推荐,因为可能含有私人数据),或动手能力强的话,可以手动翻翻这些数据库看看哪些文件/表有这个 `encrypt_iv` 和 `secretKey`。 项目需要的内容: - **db 文件路径**(方便写使用指南) - 所有可能存在密钥 / IV 的表 没有的话我应该也能从 APK 里面找到。 乐:华为把其中一个表叫做 “在线播放拉屎缓存” 💩

挨个看过没有,不过有几个打不开,貌似用了 sqlcipher 加密数据库¿databases

挨个看过没有,不过有几个打不开,貌似用了 sqlcipher 加密数据库¿[databases](https://1drv.ms/u/c/d979081d97ec3bf9/EUI8IbjjcklLljgScJxOVQkBkqdKnEwBcua3KuoFs08MKw?e=xOat6G)

挨个看过没有,不过有几个打不开,貌似用了 sqlcipher 加密数据库¿databases

hook getWritableDatabase 方法发现两行神秘密钥(均为base64编码)但是电脑环境炸了没法验证((((
oVrICAyCV0hcCRAIr6WIh4yQeJEA8UxbIaa4s+l3V3U=
b1ZySUNBeUNWMGhjQ1JBSXI2V0loNHlRZUpFQThVeGJJYWE0cytsM1YzVT0K

> 挨个看过没有,不过有几个打不开,貌似用了 sqlcipher 加密数据库¿[databases](https://1drv.ms/u/c/d979081d97ec3bf9/EUI8IbjjcklLljgScJxOVQkBkqdKnEwBcua3KuoFs08MKw?e=xOat6G) hook getWritableDatabase 方法发现两行神秘密钥(均为base64编码)但是电脑环境炸了没法验证(((( oVrICAyCV0hcCRAIr6WIh4yQeJEA8UxbIaa4s+l3V3U= b1ZySUNBeUNWMGhjQ1JBSXI2V0loNHlRZUpFQThVeGJJYWE0cytsM1YzVT0K
Sign in to join this conversation.
No description provided.