解密加密音频格式

如有商用自负法律责任!

支持格式

理论支持所有格式,但仅适配了 mflac 和 mgg 格式。

使用方法

  1. 开启 QQ 音乐,下载音乐到默认文件夹(C:/Users/你自己的用户名/Music/VipSongsDownload)。
  2. 编辑保存hook_qq_music.jshook_qq_music.py到随意位置
  3. 启动 hook_qq_music.py 脚本,自动从C:/Users/你自己的用户名/Music/VipSongsDownload读取音乐文件,解密后保存到output文件夹。

hook_qq_music.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
const TARGET_DLL = "QQMusicCommon.dll";

var EncAndDesMediaFileConstructorAddr = Module.findExportByName(
TARGET_DLL,
"??0EncAndDesMediaFile@@QAE@XZ"
);

var EncAndDesMediaFileDestructorAddr = Module.findExportByName(
TARGET_DLL,
"??1EncAndDesMediaFile@@QAE@XZ"
);

var EncAndDesMediaFileOpenAddr = Module.findExportByName(
TARGET_DLL,
"?Open@EncAndDesMediaFile@@QAE_NPB_W_N1@Z"
);

var EncAndDesMediaFileGetSizeAddr = Module.findExportByName(
TARGET_DLL,
"?GetSize@EncAndDesMediaFile@@QAEKXZ"
);

var EncAndDesMediaFileReadAddr = Module.findExportByName(
TARGET_DLL,
"?Read@EncAndDesMediaFile@@QAEKPAEK_J@Z"
);

var EncAndDesMediaFileConstructor = new NativeFunction(
EncAndDesMediaFileConstructorAddr,
"pointer",
["pointer"],
"thiscall"
);

var EncAndDesMediaFileDestructor = new NativeFunction(
EncAndDesMediaFileDestructorAddr,
"void",
["pointer"],
"thiscall"
);

var EncAndDesMediaFileOpen = new NativeFunction(
EncAndDesMediaFileOpenAddr,
"bool",
["pointer", "pointer", "bool", "bool"],
"thiscall"
);

var EncAndDesMediaFileGetSize = new NativeFunction(
EncAndDesMediaFileGetSizeAddr,
"uint32",
["pointer"],
"thiscall"
);

var EncAndDesMediaFileRead = new NativeFunction(
EncAndDesMediaFileReadAddr,
"uint",
["pointer", "pointer", "uint32", "uint64"],
"thiscall"
);

rpc.exports = {
decrypt: function (srcFileName) {
var EncAndDesMediaFileObject = Memory.alloc(0x28);
EncAndDesMediaFileConstructor(EncAndDesMediaFileObject);

var fileNameUtf16 = Memory.allocUtf16String(srcFileName);
EncAndDesMediaFileOpen(EncAndDesMediaFileObject, fileNameUtf16, 1, 0);

var fileSize = EncAndDesMediaFileGetSize(EncAndDesMediaFileObject);

var buffer = Memory.alloc(fileSize);
EncAndDesMediaFileRead(EncAndDesMediaFileObject, buffer, fileSize, 0);

var data = buffer.readByteArray(fileSize);
EncAndDesMediaFileDestructor(EncAndDesMediaFileObject);
return data;
},
};

hook_qq_music.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import frida
import os
import shutil
from pathlib import Path

# 挂钩 QQ 音乐进程
session = frida.attach("QQMusic.exe")

# 加载并执行 JavaScript 脚本
script = session.create_script(open("hook_qq_music.js", "r", encoding="utf-8").read())
script.load()

# 创建输出目录
output_dir = "output"
if not os.path.exists(output_dir):
os.makedirs(output_dir)

# 获取用户音乐目录路径
home = str(Path.home()) + "\\Music\\VipSongsDownload"
home = os.path.abspath(home)
# 解密转换后的路径
decode_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), output_dir)
print("用户音乐路径为:", home)
print("存储后的音乐路径为:", decode_dir)

# 遍历目录下的所有文件
for root, dirs, files in os.walk(home):
for file in files:
# 分离出扩展名
file_path = os.path.splitext(file)
# print(file_path[0].split('-'))
# 只处理 .mflac 和 .mgg 文件
if file_path[-1] in [".mflac", ".mgg"]:

# 修改文件扩展名
file_path = list(file_path)
file_path[-1] = file_path[-1].replace("mflac", "flac").replace("mgg", "ogg")
file_path_str = "".join(file_path)

# 检查解密文件是否已经存在
output_file_path = os.path.join(output_dir, file_path_str)
if os.path.exists(output_file_path):
print(f"File {output_file_path} 已存在,跳过.")
continue
else:
print("解密:", file)

# 调用脚本中的 decrypt 方法解密文件
data = script.exports_sync.decrypt(os.path.join(root, file))

# 将解密后的数据写入新的文件
with open(output_file_path, "wb") as f:
f.write(data)
# 如果是歌词文件直接复制
elif file_path[-1] in [".lrc"]:
shutil.copy(os.path.join(home, file), os.path.join(decode_dir, file))
else:
print("后缀不对跳过:", file)
# 分离会话
session.detach()