feat: mmkv 加密数据库解析
This commit is contained in:
parent
e504048a30
commit
b7b1240603
@ -1,17 +1,20 @@
|
|||||||
package qmc
|
package qmc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"git.unlock-music.dev/awalol/go-mmkv"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
"golang.org/x/text/unicode/norm"
|
"golang.org/x/text/unicode/norm"
|
||||||
"unlock-music.dev/mmkv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var streamKeyVault mmkv.Vault
|
var streamKeyVault mmkv.Vault
|
||||||
@ -80,6 +83,45 @@ func readKeyFromMMKV(file string, logger *zap.Logger) ([]byte, error) {
|
|||||||
return deriveKey(buf)
|
return deriveKey(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readKeyFromMMKVCustom(d *Decoder) ([]byte, error) {
|
||||||
|
logger := d.logger
|
||||||
|
filePath, fileName := filepath.Split(VaultPath)
|
||||||
|
|
||||||
|
if streamKeyVault == nil {
|
||||||
|
mgr, err := mmkv.NewManager(filepath.Dir(filePath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("init mmkv manager: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
streamKeyVault, err = mgr.OpenVaultCrypto(fileName, VaultKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("open mmkv vault: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug("mmkv vault opened", zap.Strings("keys", streamKeyVault.Keys()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取mid即数据库键值
|
||||||
|
_, err := d.raw.Seek(-128, io.SeekEnd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get mid error: %w", err)
|
||||||
|
}
|
||||||
|
mid, err := io.ReadAll(io.LimitReader(d.raw, 64)) // 取64字节确保完全取完
|
||||||
|
mid = bytes.ReplaceAll(mid, []byte{0x00}, []byte{}) // clean NUL
|
||||||
|
mid = bytes.Trim(mid, "\0000") // maybe a little stupid
|
||||||
|
|
||||||
|
// 从数据库获取eKey
|
||||||
|
eKey, err := streamKeyVault.GetBytes(string(mid))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get eKey error: %w", err)
|
||||||
|
}
|
||||||
|
n, err := base64.StdEncoding.Decode(eKey, eKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("base64 error: %w", err)
|
||||||
|
}
|
||||||
|
return deriveKeyV1(eKey[:n])
|
||||||
|
}
|
||||||
|
|
||||||
func getRelativeMMKVDir(file string) (string, error) {
|
func getRelativeMMKVDir(file string) (string, error) {
|
||||||
mmkvDir := filepath.Join(filepath.Dir(file), "../mmkv")
|
mmkvDir := filepath.Join(filepath.Dir(file), "../mmkv")
|
||||||
if _, err := os.Stat(mmkvDir); err != nil {
|
if _, err := os.Stat(mmkvDir); err != nil {
|
||||||
|
@ -16,6 +16,9 @@ import (
|
|||||||
"unlock-music.dev/cli/internal/sniff"
|
"unlock-music.dev/cli/internal/sniff"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var VaultPath = ""
|
||||||
|
var VaultKey = ""
|
||||||
|
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
raw io.ReadSeeker // raw is the original file reader
|
raw io.ReadSeeker // raw is the original file reader
|
||||||
params *common.DecoderParams
|
params *common.DecoderParams
|
||||||
@ -140,11 +143,32 @@ func (d *Decoder) searchKey() (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch string(suffixBuf) {
|
switch string(bytes.ReplaceAll(suffixBuf, []byte{0x00}, []byte{})) {
|
||||||
case "QTag":
|
case "QTag":
|
||||||
return d.readRawMetaQTag()
|
return d.readRawMetaQTag()
|
||||||
case "STag":
|
case "STag":
|
||||||
return errors.New("qmc: file with 'STag' suffix doesn't contains media key")
|
return errors.New("qmc: file with 'STag' suffix doesn't contains media key")
|
||||||
|
case "cex":
|
||||||
|
d.decodedKey, err = readKeyFromMMKVCustom(d)
|
||||||
|
if err == nil {
|
||||||
|
suffix := []byte{0x63, 0x65, 0x78, 0x00} // cex
|
||||||
|
for i := 0; i <= 3; i++ {
|
||||||
|
// 末尾的信息数据每192字节出现一次,故只要循环判断末尾不为musicex时即为歌曲数据
|
||||||
|
musicexLen, err := d.raw.Seek(int64(-(192*i)-4), io.SeekEnd)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get musicexLen error: %w", err)
|
||||||
|
}
|
||||||
|
buf, err := io.ReadAll(io.LimitReader(d.raw, 4))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get musicex error: %w", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf, suffix) {
|
||||||
|
d.audioLen = int(musicexLen) + 4
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
default:
|
default:
|
||||||
size := binary.LittleEndian.Uint32(suffixBuf)
|
size := binary.LittleEndian.Uint32(suffixBuf)
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"go.uber.org/zap"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@ -14,10 +17,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unlock-music.dev/cli/algo/qmc"
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
|
|
||||||
"unlock-music.dev/cli/algo/common"
|
"unlock-music.dev/cli/algo/common"
|
||||||
_ "unlock-music.dev/cli/algo/kgm"
|
_ "unlock-music.dev/cli/algo/kgm"
|
||||||
@ -50,6 +50,8 @@ func main() {
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{Name: "input", Aliases: []string{"i"}, Usage: "path to input file or dir", Required: false},
|
&cli.StringFlag{Name: "input", Aliases: []string{"i"}, Usage: "path to input file or dir", Required: false},
|
||||||
&cli.StringFlag{Name: "output", Aliases: []string{"o"}, Usage: "path to output dir", Required: false},
|
&cli.StringFlag{Name: "output", Aliases: []string{"o"}, Usage: "path to output dir", Required: false},
|
||||||
|
&cli.StringFlag{Name: "vault-path", Aliases: []string{"db"}, Usage: "数据库文件位置 (请确保crc文件在同目录下)", Required: false},
|
||||||
|
&cli.StringFlag{Name: "vault-key", Aliases: []string{"key"}, Usage: "数据库密钥", Required: false},
|
||||||
&cli.BoolFlag{Name: "remove-source", Aliases: []string{"rs"}, Usage: "remove source file", Required: false, Value: false},
|
&cli.BoolFlag{Name: "remove-source", Aliases: []string{"rs"}, Usage: "remove source file", Required: false, Value: false},
|
||||||
&cli.BoolFlag{Name: "skip-noop", Aliases: []string{"n"}, Usage: "skip noop decoder", Required: false, Value: true},
|
&cli.BoolFlag{Name: "skip-noop", Aliases: []string{"n"}, Usage: "skip noop decoder", Required: false, Value: true},
|
||||||
&cli.BoolFlag{Name: "update-metadata", Usage: "update metadata & album art from network", Required: false, Value: false},
|
&cli.BoolFlag{Name: "update-metadata", Usage: "update metadata & album art from network", Required: false, Value: false},
|
||||||
@ -86,6 +88,8 @@ func appMain(c *cli.Context) (err error) {
|
|||||||
printSupportedExtensions()
|
printSupportedExtensions()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
qmc.VaultPath = c.String("vault-path") // TODO: 更改参数传递方式
|
||||||
|
qmc.VaultKey = c.String("vault-key")
|
||||||
input := c.String("input")
|
input := c.String("input")
|
||||||
if input == "" {
|
if input == "" {
|
||||||
switch c.Args().Len() {
|
switch c.Args().Len() {
|
||||||
|
9
go.mod
9
go.mod
@ -11,18 +11,19 @@ require (
|
|||||||
github.com/urfave/cli/v2 v2.23.6
|
github.com/urfave/cli/v2 v2.23.6
|
||||||
go.uber.org/zap v1.24.0
|
go.uber.org/zap v1.24.0
|
||||||
golang.org/x/crypto v0.3.0
|
golang.org/x/crypto v0.3.0
|
||||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db
|
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
|
||||||
golang.org/x/text v0.5.0
|
golang.org/x/text v0.5.0
|
||||||
unlock-music.dev/mmkv v0.0.0-20221204231432-41a75bd29939
|
//unlock-music.dev/mmkv v0.0.0-20221204231432-41a75bd29939
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
git.unlock-music.dev/awalol/go-mmkv v0.1.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
go.uber.org/atomic v1.10.0 // indirect
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
go.uber.org/multierr v1.8.0 // indirect
|
go.uber.org/multierr v1.8.0 // indirect
|
||||||
golang.org/x/sys v0.2.0 // indirect
|
golang.org/x/sys v0.2.0 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.32.0 // indirect
|
||||||
)
|
)
|
||||||
|
8
go.sum
8
go.sum
@ -1,3 +1,5 @@
|
|||||||
|
git.unlock-music.dev/awalol/go-mmkv v0.1.0 h1:Ev4buS12RNOUYd99wtE0P7CUIForghsBt0zRpaCnaSU=
|
||||||
|
git.unlock-music.dev/awalol/go-mmkv v0.1.0/go.mod h1:aM3nwVQyWkCH424GWxwU5w9JeRLUWNz7HgPxw0lWWOY=
|
||||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
@ -17,6 +19,8 @@ github.com/go-flac/go-flac v0.3.1/go.mod h1:jG9IumOfAXr+7J40x0AiQIbJzXf9Y7+Zs/2C
|
|||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||||
@ -48,6 +52,8 @@ golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
|||||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||||
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
|
||||||
|
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -58,6 +64,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
|||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||||
|
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
Loading…
Reference in New Issue
Block a user