Compare commits
3 Commits
v0.0.6
...
feature/ke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c15ad45d89 | ||
|
|
b64902ecb6 | ||
|
|
dc116c50bf |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,6 +27,5 @@ go.work.sum
|
|||||||
# syncthing files
|
# syncthing files
|
||||||
.stfolder
|
.stfolder
|
||||||
|
|
||||||
chatlog
|
|
||||||
chatlog.exe# Added by goreleaser init:
|
chatlog.exe# Added by goreleaser init:
|
||||||
dist/
|
dist/
|
||||||
|
|||||||
146
cmd/chatlog/cmd_dumpmemory.go
Normal file
146
cmd/chatlog/cmd_dumpmemory.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package chatlog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/sjzar/chatlog/internal/wechat"
|
||||||
|
"github.com/sjzar/chatlog/internal/wechat/key/darwin/glance"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(dumpmemoryCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dumpmemoryCmd = &cobra.Command{
|
||||||
|
Use: "dumpmemory",
|
||||||
|
Short: "dump memory",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if runtime.GOOS != "darwin" {
|
||||||
|
log.Info().Msg("dump memory only support macOS")
|
||||||
|
}
|
||||||
|
|
||||||
|
session := time.Now().Format("20060102150405")
|
||||||
|
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("get current directory failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info().Msgf("current directory: %s", dir)
|
||||||
|
|
||||||
|
// step 1. check pid
|
||||||
|
if err = wechat.Load(); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("load wechat failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
accounts := wechat.GetAccounts()
|
||||||
|
if len(accounts) == 0 {
|
||||||
|
log.Fatal().Msg("no wechat account found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Msgf("found %d wechat account", len(accounts))
|
||||||
|
for i, a := range accounts {
|
||||||
|
log.Info().Msgf("%d. %s %d %s", i, a.FullVersion, a.PID, a.DataDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// step 2. dump memory
|
||||||
|
account := accounts[0]
|
||||||
|
file := fmt.Sprintf("wechat_%s_%d_%s.bin", account.FullVersion, account.PID, session)
|
||||||
|
path := filepath.Join(dir, file)
|
||||||
|
log.Info().Msgf("dumping memory to %s", path)
|
||||||
|
|
||||||
|
g := glance.NewGlance(account.PID)
|
||||||
|
b, err := g.Read()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("read memory failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.WriteFile(path, b, 0644); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("write memory failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Msg("dump memory success")
|
||||||
|
|
||||||
|
// step 3. copy encrypted database file
|
||||||
|
dbFile := "db_storage/session/session.db"
|
||||||
|
if account.Version == 3 {
|
||||||
|
dbFile = "Session/session_new.db"
|
||||||
|
}
|
||||||
|
from := filepath.Join(account.DataDir, dbFile)
|
||||||
|
to := filepath.Join(dir, fmt.Sprintf("wechat_%s_%d_session.db", account.FullVersion, account.PID))
|
||||||
|
|
||||||
|
log.Info().Msgf("copying %s to %s", from, to)
|
||||||
|
b, err = os.ReadFile(from)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("read session.db failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = os.WriteFile(to, b, 0644); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("write session.db failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info().Msg("copy session.db success")
|
||||||
|
|
||||||
|
// step 4. package
|
||||||
|
zipFile := fmt.Sprintf("wechat_%s_%d_%s.zip", account.FullVersion, account.PID, session)
|
||||||
|
zipPath := filepath.Join(dir, zipFile)
|
||||||
|
log.Info().Msgf("packaging to %s", zipPath)
|
||||||
|
|
||||||
|
zf, err := os.Create(zipPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("create zip file failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer zf.Close()
|
||||||
|
|
||||||
|
zw := zip.NewWriter(zf)
|
||||||
|
|
||||||
|
for _, file := range []string{file, to} {
|
||||||
|
f, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("open file failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
info, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("get file info failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header, err := zip.FileInfoHeader(info)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("create zip file info header failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header.Name = filepath.Base(file)
|
||||||
|
header.Method = zip.Deflate
|
||||||
|
writer, err := zw.CreateHeader(header)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("create zip file header failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = io.Copy(writer, f); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("copy file to zip failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = zw.Close(); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("close zip writer failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Msgf("package success, please send %s to developer", zipPath)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -74,6 +74,16 @@ func (c *Context) SwitchHistory(account string) {
|
|||||||
c.WorkDir = history.WorkDir
|
c.WorkDir = history.WorkDir
|
||||||
c.HTTPEnabled = history.HTTPEnabled
|
c.HTTPEnabled = history.HTTPEnabled
|
||||||
c.HTTPAddr = history.HTTPAddr
|
c.HTTPAddr = history.HTTPAddr
|
||||||
|
} else {
|
||||||
|
c.Account = ""
|
||||||
|
c.Platform = ""
|
||||||
|
c.Version = 0
|
||||||
|
c.FullVersion = ""
|
||||||
|
c.DataKey = ""
|
||||||
|
c.DataDir = ""
|
||||||
|
c.WorkDir = ""
|
||||||
|
c.HTTPEnabled = false
|
||||||
|
c.HTTPAddr = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
V3ProcessName = "WeChat"
|
ProcessNameOfficial = "WeChat"
|
||||||
V4ProcessName = "Weixin"
|
ProcessNameBeta = "Weixin"
|
||||||
V3DBFile = "Message/msg_0.db"
|
V3DBFile = "Message/msg_0.db"
|
||||||
V4DBFile = "db_storage/message/message_0.db"
|
V4DBFile = "db_storage/session/session.db"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Detector 实现 macOS 平台的进程检测器
|
// Detector 实现 macOS 平台的进程检测器
|
||||||
@@ -40,7 +40,7 @@ func (d *Detector) FindProcesses() ([]*model.Process, error) {
|
|||||||
var result []*model.Process
|
var result []*model.Process
|
||||||
for _, p := range processes {
|
for _, p := range processes {
|
||||||
name, err := p.Name()
|
name, err := p.Name()
|
||||||
if err != nil || (name != V3ProcessName && name != V4ProcessName) {
|
if err != nil || (name != ProcessNameOfficial && name != ProcessNameBeta) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user