This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
酷我格式研究
- 来源:酷我音乐官网的
10.3.8.1
APK - 加密文件样本:https://t.me/unlock_music_chat/121782
别怕变老(Live)-艾热_王以太-226743561.mflac SHA-256 7FDE8344B54EEA989AAE103982743EC2F5D7E6F93F7D6793BF5C95F34E5DBCD1
酷我音乐.10.3.8.1.apk SHA-256 4B4595A3A42244A833BA777DDB222C487AD70C560E866D01EE6B1ADBFCFC4C15
※ 该格式研究来自静态分析,并没有进行验证。可能会有不准确的地方。
格式说明
文件头有很多信息:
0000h 79 65 65 6C 69 6F 6E 2D 6B 75 77 6F 2D 74 6D 65 yeelion-kuwo-tme
0010h 02 00 00 00 00 00 00 00 09 D5 83 0D 00 00 00 00 .........Õƒ.....
0020h 88 54 0D 57 00 00 00 00 AC 6E 8C B8 00 00 00 00 ˆT.W....¬nŒ¸....
0030h 32 30 39 30 30 6B 6D 66 6C 61 63 00 76 00 00 00 20900kmflac.v...
0040h 10 E7 03 85 76 00 00 00 01 00 00 00 08 00 00 00 .ç.…v...........
0050h 20 E7 03 85 76 00 00 00 00 00 00 00 20 00 00 00 ç.…v....... ...
0060h 84 69 10 24 87 01 00 00 0D 00 00 00 00 00 00 00 „i.$‡...........
0070h 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
格式描述为 偏移: 类型 描述
:
0x00
:char[16]
固定文件头。0x10
:u32,le
加密版本。目前mflac
的这个值是2
,之前版本是1
。
内部名称为KwPocoVersion
或EncryptVersion
。
(内部) 加密版本为3
时,当作未加密文件处理。猜测下个加密版本可能会使用4
。0x18
:u32,le
资源 ID,同之前版本。写出时实际写出的是u64
数值。0x30
:char[?]
文件格式,读入字符到一个类型为std::string
的变量内。
在该样本的情况会读入11
字节并储存。0x68
:u8
VIP 资源类型,见下表;
其它:和解密没有多大关系,没有细看。
VIP 资源类型
样本的 VIP 资源类型为 0D
,转换为二进制为 1101
。
分别对应下述标记:
isTingshuVipPay
isSongPay
isVipPay
public static void a(Music music, int vip_type) {
boolean is_vip_pay = false;
music.isTingshuVipPay = (vip_type & 1) == 1;
music.isAlbumPay = (vip_type >> 1 & 1) == 1;
music.isSongPay = (vip_type >> 2 & 1) == 1;
if((vip_type >> 3 & 1) == 1) {
is_vip_pay = true;
}
music.isVipPay = is_vip_pay;
}
该值置零也许不能绕过非会员播放,因为文件未内嵌 ekey
密钥。
解密流程
- 读取文件头,校验加密版本是否为
2
。 - 使用读入的
ekey
进行解密,解密时的offset
为file_offset - 1024
。 - 使用 QQ 音乐同款
QMC2::MAP
或QMC2::RC4
进行解密 (取决于密钥长度)。
ekey 缓存
该逻辑在安卓的 Java 端:
public static String get_cached_file_ekey(long res_id, String format) {
return MMKV.ReadMMKVString("sec_ekey", res_id + "-" + format.toLowerCase(), "");
}
其中 format
为文件格式,该样本的值为 "20900kmflac"
;res_id
则是资源 ID,为 226743561
。