Refactor key extraction: drop V3 support, parallelize V4

Removed all code and logic related to WeChat V3 key extraction, including darwin and windows V3 extractors. Updated extractor selection to return an error for V3. Refactored Windows DLL extractor to run DLL and native (Dart-style) memory scan in parallel for V4, reporting keys as soon as found. Improved image key acquisition flow in the TUI and updated documentation to reflect the new focus on V4, parallel key extraction, and the deprecation of V3 support.
This commit is contained in:
teest114514
2025-12-16 00:15:59 +08:00
parent 91ae4a0886
commit cbf5201216
14 changed files with 631 additions and 897 deletions

View File

@@ -6,6 +6,8 @@ import (
"os"
"path/filepath"
"strings"
"github.com/rs/zerolog/log"
)
type AesKeyValidator struct {
@@ -18,7 +20,47 @@ func NewImgKeyValidator(path string) *AesKeyValidator {
Path: path,
}
// Walk the directory to find *.dat files (excluding *_t.dat files)
log.Info().Msgf("开始在 %s 查找验证样本文件", path)
// 1. First try to find *_t.dat files (Dart implementation priority)
foundTemplate := false
filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
// Look for *_t.dat files
if !strings.HasSuffix(info.Name(), "_t.dat") {
return nil
}
data, err := os.ReadFile(filePath)
if err != nil {
return nil
}
// Header check: 07 08 56 32 08 07 (V4Format2)
if len(data) >= 0x1F && bytes.Equal(data[:6], V4Format2.Header) {
// Extract 16 bytes starting at offset 0xF (15)
validator.EncryptedData = make([]byte, aes.BlockSize)
copy(validator.EncryptedData, data[0xF:0xF+aes.BlockSize])
foundTemplate = true
log.Info().Msgf("找到模板文件: %s", filePath)
return filepath.SkipAll
}
return nil
})
if foundTemplate {
return validator
}
// 2. Fallback: Walk the directory to find *.dat files (excluding *_t.dat files)
filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {
if err != nil {
return err
@@ -42,22 +84,30 @@ func NewImgKeyValidator(path string) *AesKeyValidator {
// Check if header matches V4Format2.Header
// Get aes.BlockSize (16) bytes starting from position 15
if len(data) >= 15+aes.BlockSize && bytes.Equal(data[:4], V4Format2.Header) {
if len(data) >= 15+aes.BlockSize && bytes.Equal(data[:4], V4Format2.Header[:4]) {
validator.EncryptedData = make([]byte, aes.BlockSize)
copy(validator.EncryptedData, data[15:15+aes.BlockSize])
log.Info().Msgf("找到备用模板文件: %s", filePath)
return filepath.SkipAll // Found what we need, stop walking
}
return nil
})
if len(validator.EncryptedData) == 0 {
log.Warn().Msg("未找到任何可用的验证样本文件")
}
return validator
}
func (v *AesKeyValidator) Validate(key []byte) bool {
// 16 bytes for AES-128, 32 bytes for AES-256 (but we only use first 16 bytes for V4 image key)
if len(key) < 16 {
return false
}
// Dart implementation explicitly uses first 16 bytes
aesKey := key[:16]
cipher, err := aes.NewCipher(aesKey)
@@ -65,8 +115,18 @@ func (v *AesKeyValidator) Validate(key []byte) bool {
return false
}
if len(v.EncryptedData) < aes.BlockSize {
return false
}
decrypted := make([]byte, len(v.EncryptedData))
cipher.Decrypt(decrypted, v.EncryptedData)
// Dart implementation checks for JPG header: FF D8 FF
if bytes.HasPrefix(decrypted, []byte{0xFF, 0xD8, 0xFF}) {
return true
}
// Keep existing checks for backward compatibility
return bytes.HasPrefix(decrypted, JPG.Header) || bytes.HasPrefix(decrypted, WXGF.Header)
}