mirror of
https://git.um-react.app/um/cli.git
synced 2025-11-28 03:33:02 +00:00
165 lines
4.8 KiB
Go
165 lines
4.8 KiB
Go
package qmc
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.um-react.app/um/cli/algo/common"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func LoadMMKVOrDefault(path string, key string, logger *zap.Logger) (result common.QMCKeys, err error) {
|
|
key1, err := loadMacKeysV8(logger)
|
|
if err != nil {
|
|
key1 = nil
|
|
logger.Warn("LoadMMKVOrDefault: could not read QQMusic v8.8.0 keys", zap.Error(err))
|
|
}
|
|
|
|
key2, err := loadMacKeysV10(logger)
|
|
if err != nil {
|
|
key2 = nil
|
|
logger.Warn("LoadMMKVOrDefault: could not read QQMusic v10.x keys", zap.Error(err))
|
|
}
|
|
|
|
userKeys := make(common.QMCKeys)
|
|
if path != "" {
|
|
logger.Info("Using user mmkv")
|
|
userKeys, err = LoadMMKV(path, key, logger)
|
|
if err != nil {
|
|
userKeys = nil
|
|
logger.Warn("LoadMMKVOrDefault: could not read user keys", zap.Error(err))
|
|
}
|
|
}
|
|
|
|
allKeys := mergeMMKVKeys(key1, key2, userKeys)
|
|
|
|
logger.Debug("Keys loaded", zap.Any("keys", allKeys), zap.Int("len", len(allKeys)))
|
|
|
|
return allKeys, nil
|
|
}
|
|
|
|
func loadMacKeysV8(logger *zap.Logger) (keys common.QMCKeys, err error) {
|
|
homeDir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
logger.Warn("Failed to get home dir")
|
|
return nil, fmt.Errorf("loadMacKeysV8: failed to get home: %w", err)
|
|
}
|
|
p := filepath.Join(
|
|
homeDir,
|
|
"Library/Containers/com.tencent.QQMusicMac/Data",
|
|
"Library/Application Support/QQMusicMac/mmkv",
|
|
"MMKVStreamEncryptId",
|
|
)
|
|
if f, err := os.Stat(p); err == nil && !f.IsDir() {
|
|
logger.Info("Using QQMusic 8.x mmkv", zap.String("mmkv", p))
|
|
return LoadMMKV(p, "", logger)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
var _RE_UDID = regexp.MustCompile(`_\x10\(([0-9a-f]{40})_`)
|
|
|
|
func extractUdid(data []byte) (string, error) {
|
|
match := _RE_UDID.FindSubmatch(data)
|
|
if len(match) != 2 {
|
|
return "", fmt.Errorf("extractUdid: could not find udid")
|
|
}
|
|
return string(match[1]), nil
|
|
}
|
|
|
|
func caesar(text string, shift int) string {
|
|
var result strings.Builder
|
|
|
|
for _, char := range []byte(text) {
|
|
var transformed byte
|
|
if 'A' <= char && char <= 'Z' {
|
|
transformed = (char-'A'+byte(shift))%26 + 'A'
|
|
} else if 'a' <= char && char <= 'z' {
|
|
transformed = (char-'a'+byte(shift))%26 + 'a'
|
|
} else if '0' <= char && char <= '9' {
|
|
transformed = (char-'0'+byte(shift))%10 + '0'
|
|
} else {
|
|
transformed = char
|
|
}
|
|
result.WriteByte(transformed)
|
|
}
|
|
|
|
return result.String()
|
|
}
|
|
|
|
func getMmkv(udid string, id int) (name string, key string, err error) {
|
|
str1 := caesar(udid, id+3)
|
|
int1, err := strconv.ParseInt(udid[5:7], 16, 32)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("getMmkv: could not parse udid: %w", err)
|
|
}
|
|
int2 := 5 + (int(int1)+id)%4
|
|
name = str1[:int2]
|
|
|
|
int3 := id + 0xa546
|
|
str3 := fmt.Sprintf("%s%04x", udid, int3)
|
|
hash := md5.Sum([]byte(str3))
|
|
key = fmt.Sprintf("%x", hash)[:16]
|
|
|
|
return name, key, nil
|
|
}
|
|
|
|
func loadMacKeysV10(logger *zap.Logger) (common.QMCKeys, error) {
|
|
homeDir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
logger.Warn("Failed to get home dir")
|
|
return nil, fmt.Errorf("loadMacKeysV10: failed to get home: %w", err)
|
|
}
|
|
plist_path := filepath.Join(
|
|
homeDir,
|
|
"Library/Preferences/com.tencent.QQMusicMac.plist",
|
|
)
|
|
if f, err := os.Stat(plist_path); err != nil || f.IsDir() {
|
|
logger.Debug("loadMacKeysV10: plist not found", zap.String("plist", plist_path))
|
|
return nil, fmt.Errorf("loadMacKeysV10: plist not found")
|
|
}
|
|
plist_data, err := os.ReadFile(plist_path)
|
|
if err != nil {
|
|
logger.Warn("loadMacKeysV10: could not read plist", zap.String("plist", plist_path), zap.Error(err))
|
|
return nil, fmt.Errorf("loadMacKeysV10: could not read plist: %w", err)
|
|
}
|
|
udid, err := extractUdid(plist_data)
|
|
if err != nil {
|
|
logger.Warn("loadMacKeysV10: could not extract udid", zap.Error(err))
|
|
return nil, fmt.Errorf("loadMacKeysV10: could not extract udid: %w", err)
|
|
}
|
|
logger.Debug("loadMacKeysV10: read udid", zap.String("udid", udid))
|
|
|
|
mmkv_dir := filepath.Join(
|
|
homeDir,
|
|
"Library/Containers/com.tencent.QQMusicMac/Data/",
|
|
"Library/Application Support/QQMusicMac/iData",
|
|
)
|
|
if f, err := os.Stat(mmkv_dir); err != nil || !f.IsDir() {
|
|
logger.Debug("loadMacKeysV10: mmkv dir not found", zap.String("mmkv_dir", mmkv_dir))
|
|
return nil, nil
|
|
}
|
|
mmkv_name, mmkv_key, err := getMmkv(udid, 1)
|
|
if err != nil {
|
|
logger.Warn("loadMacKeysV10: could not get mmkv name/key", zap.Error(err))
|
|
return nil, fmt.Errorf("loadMacKeysV10: could not get mmkv name/key: %w", err)
|
|
}
|
|
mmkv_path := filepath.Join(mmkv_dir, mmkv_name)
|
|
if f, err := os.Stat(mmkv_path); err != nil || f.IsDir() {
|
|
logger.Debug("loadMacKeysV10: mmkv file not found", zap.String("mmkv", mmkv_path))
|
|
return nil, nil
|
|
}
|
|
logger.Info("Using QQMusic 10.x mmkv", zap.String("mmkv", mmkv_path))
|
|
keys, err := LoadMMKV(mmkv_path, mmkv_key, logger)
|
|
if err != nil {
|
|
logger.Warn("loadMacKeysV10: could not load mmkv", zap.String("mmkv", mmkv_path), zap.Error(err))
|
|
return nil, fmt.Errorf("loadMacKeysV10: could not load mmkv: %w", err)
|
|
}
|
|
return keys, nil
|
|
}
|