support multi key pattern matching (#25)

This commit is contained in:
Sarv
2025-04-10 14:53:17 +08:00
committed by GitHub
parent b64902ecb6
commit 4983d27054
8 changed files with 152 additions and 81 deletions

View File

@@ -22,6 +22,8 @@ func initLog(cmd *cobra.Command, args []string) {
if Debug { if Debug {
zerolog.SetGlobalLevel(zerolog.DebugLevel) zerolog.SetGlobalLevel(zerolog.DebugLevel)
} }
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339})
} }
func initTuiLog(cmd *cobra.Command, args []string) { func initTuiLog(cmd *cobra.Command, args []string) {

View File

@@ -15,13 +15,17 @@ type Validator struct {
} }
// NewValidator 创建一个仅用于验证的验证器 // NewValidator 创建一个仅用于验证的验证器
func NewValidator(dataDir string, platform string, version int) (*Validator, error) { func NewValidator(platform string, version int, dataDir string) (*Validator, error) {
dbFile := GetSimpleDBFile(platform, version)
dbPath := filepath.Join(dataDir + "/" + dbFile)
return NewValidatorWithFile(platform, version, dbPath)
}
func NewValidatorWithFile(platform string, version int, dbPath string) (*Validator, error) {
decryptor, err := NewDecryptor(platform, version) decryptor, err := NewDecryptor(platform, version)
if err != nil { if err != nil {
return nil, err return nil, err
} }
dbFile := GetSimpleDBFile(platform, version)
dbPath := filepath.Join(dataDir + "/" + dbFile)
d, err := common.OpenDBFile(dbPath, decryptor.GetPageSize()) d, err := common.OpenDBFile(dbPath, decryptor.GetPageSize())
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -19,12 +19,22 @@ const (
MaxWorkersV3 = 8 MaxWorkersV3 = 8
) )
var V3KeyPatterns = []KeyPatternInfo{
{
Pattern: []byte{0x72, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x33, 0x32},
Offset: 24,
},
}
type V3Extractor struct { type V3Extractor struct {
validator *decrypt.Validator validator *decrypt.Validator
keyPatterns []KeyPatternInfo
} }
func NewV3Extractor() *V3Extractor { func NewV3Extractor() *V3Extractor {
return &V3Extractor{} return &V3Extractor{
keyPatterns: V3KeyPatterns,
}
} }
func (e *V3Extractor) Extract(ctx context.Context, proc *model.Process) (string, error) { func (e *V3Extractor) Extract(ctx context.Context, proc *model.Process) (string, error) {
@@ -127,7 +137,6 @@ func (e *V3Extractor) findMemory(ctx context.Context, pid uint32, memoryChannel
// worker processes memory regions to find V3 version key // worker processes memory regions to find V3 version key
func (e *V3Extractor) worker(ctx context.Context, memoryChannel <-chan []byte, resultChannel chan<- string) { func (e *V3Extractor) worker(ctx context.Context, memoryChannel <-chan []byte, resultChannel chan<- string) {
keyPattern := []byte{0x72, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x33, 0x32}
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
@@ -137,51 +146,60 @@ func (e *V3Extractor) worker(ctx context.Context, memoryChannel <-chan []byte, r
return return
} }
index := len(memory) if key, ok := e.SearchKey(ctx, memory); ok {
for {
select { select {
case <-ctx.Done(): case resultChannel <- key:
return // Exit if context cancelled
default: default:
} }
log.Debug().Msgf("Searching for V3 key in memory region, size: %d bytes", len(memory))
// Find pattern from end to beginning
index = bytes.LastIndex(memory[:index], keyPattern)
if index == -1 {
break // No more matches found
}
log.Debug().Msgf("Found potential V3 key pattern in memory region, index: %d", index)
// For V3, the key is 32 bytes and starts right after the pattern
if index+24+32 > len(memory) {
index -= 1
continue
}
// Extract the key data, which is right after the pattern and 32 bytes long
keyOffset := index + 24
keyData := memory[keyOffset : keyOffset+32]
// Validate key against database header
if e.validator.Validate(keyData) {
select {
case resultChannel <- hex.EncodeToString(keyData):
log.Debug().Msg("Key found: " + hex.EncodeToString(keyData))
return
default:
}
}
index -= 1
} }
} }
} }
} }
func (e *V3Extractor) SearchKey(ctx context.Context, memory []byte) (string, bool) {
for _, keyPattern := range e.keyPatterns {
index := len(memory)
for {
select {
case <-ctx.Done():
return "", false
default:
}
// Find pattern from end to beginning
index = bytes.LastIndex(memory[:index], keyPattern.Pattern)
if index == -1 {
break // No more matches found
}
// Check if we have enough space for the key
keyOffset := index + keyPattern.Offset
if keyOffset < 0 || keyOffset+32 > len(memory) {
index -= 1
continue
}
// Extract the key data, which is 32 bytes long
keyData := memory[keyOffset : keyOffset+32]
// Validate key against database header
if e.validator.Validate(keyData) {
log.Debug().
Str("pattern", hex.EncodeToString(keyPattern.Pattern)).
Int("offset", keyPattern.Offset).
Str("key", hex.EncodeToString(keyData)).
Msg("Key found")
return hex.EncodeToString(keyData), true
}
index -= 1
}
}
return "", false
}
func (e *V3Extractor) SetValidate(validator *decrypt.Validator) { func (e *V3Extractor) SetValidate(validator *decrypt.Validator) {
e.validator = validator e.validator = validator
} }

View File

@@ -19,12 +19,26 @@ const (
MaxWorkers = 8 MaxWorkers = 8
) )
var V4KeyPatterns = []KeyPatternInfo{
{
Pattern: []byte{0x20, 0x66, 0x74, 0x73, 0x35, 0x28, 0x25, 0x00},
Offset: 16,
},
{
Pattern: []byte{0x20, 0x66, 0x74, 0x73, 0x35, 0x28, 0x25, 0x00},
Offset: -80,
},
}
type V4Extractor struct { type V4Extractor struct {
validator *decrypt.Validator validator *decrypt.Validator
keyPatterns []KeyPatternInfo
} }
func NewV4Extractor() *V4Extractor { func NewV4Extractor() *V4Extractor {
return &V4Extractor{} return &V4Extractor{
keyPatterns: V4KeyPatterns,
}
} }
func (e *V4Extractor) Extract(ctx context.Context, proc *model.Process) (string, error) { func (e *V4Extractor) Extract(ctx context.Context, proc *model.Process) (string, error) {
@@ -127,8 +141,6 @@ func (e *V4Extractor) findMemory(ctx context.Context, pid uint32, memoryChannel
// worker processes memory regions to find V4 version key // worker processes memory regions to find V4 version key
func (e *V4Extractor) worker(ctx context.Context, memoryChannel <-chan []byte, resultChannel chan<- string) { func (e *V4Extractor) worker(ctx context.Context, memoryChannel <-chan []byte, resultChannel chan<- string) {
keyPattern := []byte{0x20, 0x66, 0x74, 0x73, 0x35, 0x28, 0x25, 0x00}
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
@@ -138,47 +150,65 @@ func (e *V4Extractor) worker(ctx context.Context, memoryChannel <-chan []byte, r
return return
} }
index := len(memory) if key, ok := e.SearchKey(ctx, memory); ok {
for {
select { select {
case <-ctx.Done(): case resultChannel <- key:
return // Exit if context cancelled
default: default:
} }
// Find pattern from end to beginning
index = bytes.LastIndex(memory[:index], keyPattern)
if index == -1 {
break // No more matches found
}
// Check if we have enough space for the key
if index+16+32 > len(memory) {
index -= 1
continue
}
// Extract the key data, which is 16 bytes after the pattern and 32 bytes long
keyOffset := index + 16
keyData := memory[keyOffset : keyOffset+32]
// Validate key against database header
if e.validator.Validate(keyData) {
select {
case resultChannel <- hex.EncodeToString(keyData):
log.Debug().Msg("Key found: " + hex.EncodeToString(keyData))
return
default:
}
}
index -= 1
} }
} }
} }
} }
func (e *V4Extractor) SearchKey(ctx context.Context, memory []byte) (string, bool) {
for _, keyPattern := range e.keyPatterns {
index := len(memory)
for {
select {
case <-ctx.Done():
return "", false
default:
}
// Find pattern from end to beginning
index = bytes.LastIndex(memory[:index], keyPattern.Pattern)
if index == -1 {
break // No more matches found
}
// Check if we have enough space for the key
keyOffset := index + keyPattern.Offset
if keyOffset < 0 || keyOffset+32 > len(memory) {
index -= 1
continue
}
// Extract the key data, which is 16 bytes after the pattern and 32 bytes long
keyData := memory[keyOffset : keyOffset+32]
// Validate key against database header
if e.validator.Validate(keyData) {
log.Debug().
Str("pattern", hex.EncodeToString(keyPattern.Pattern)).
Int("offset", keyPattern.Offset).
Str("key", hex.EncodeToString(keyData)).
Msg("Key found")
return hex.EncodeToString(keyData), true
}
index -= 1
}
}
return "", false
}
func (e *V4Extractor) SetValidate(validator *decrypt.Validator) { func (e *V4Extractor) SetValidate(validator *decrypt.Validator) {
e.validator = validator e.validator = validator
} }
type KeyPatternInfo struct {
Pattern []byte
Offset int
}

View File

@@ -15,6 +15,9 @@ type Extractor interface {
// Extract 从进程中提取密钥 // Extract 从进程中提取密钥
Extract(ctx context.Context, proc *model.Process) (string, error) Extract(ctx context.Context, proc *model.Process) (string, error)
// SearchKey 在内存中搜索密钥
SearchKey(ctx context.Context, memory []byte) (string, bool)
SetValidate(validator *decrypt.Validator) SetValidate(validator *decrypt.Validator)
} }

View File

@@ -1,6 +1,8 @@
package windows package windows
import ( import (
"context"
"github.com/sjzar/chatlog/internal/wechat/decrypt" "github.com/sjzar/chatlog/internal/wechat/decrypt"
) )
@@ -12,6 +14,11 @@ func NewV3Extractor() *V3Extractor {
return &V3Extractor{} return &V3Extractor{}
} }
func (e *V3Extractor) SearchKey(ctx context.Context, memory []byte) (string, bool) {
// TODO : Implement the key search logic for V3
return "", false
}
func (e *V3Extractor) SetValidate(validator *decrypt.Validator) { func (e *V3Extractor) SetValidate(validator *decrypt.Validator) {
e.validator = validator e.validator = validator
} }

View File

@@ -1,6 +1,8 @@
package windows package windows
import ( import (
"context"
"github.com/sjzar/chatlog/internal/wechat/decrypt" "github.com/sjzar/chatlog/internal/wechat/decrypt"
) )
@@ -12,6 +14,11 @@ func NewV4Extractor() *V4Extractor {
return &V4Extractor{} return &V4Extractor{}
} }
func (e *V4Extractor) SearchKey(ctx context.Context, memory []byte) (string, bool) {
// TODO : Implement the key search logic for V4
return "", false
}
func (e *V4Extractor) SetValidate(validator *decrypt.Validator) { func (e *V4Extractor) SetValidate(validator *decrypt.Validator) {
e.validator = validator e.validator = validator
} }

View File

@@ -90,7 +90,7 @@ func (a *Account) GetKey(ctx context.Context) (string, error) {
return "", err return "", err
} }
validator, err := decrypt.NewValidator(process.DataDir, process.Platform, process.Version) validator, err := decrypt.NewValidator(process.Platform, process.Version, process.DataDir)
if err != nil { if err != nil {
return "", err return "", err
} }