adjust errors and logger (#10)

This commit is contained in:
Sarv
2025-04-01 19:41:40 +08:00
committed by GitHub
parent f31953c42b
commit 167a9ca873
49 changed files with 553 additions and 981 deletions

View File

@@ -32,13 +32,13 @@ type DBFile struct {
func OpenDBFile(dbPath string, pageSize int) (*DBFile, error) {
fp, err := os.Open(dbPath)
if err != nil {
return nil, errors.DecryptOpenFileFailed(dbPath, err)
return nil, errors.OpenFileFailed(dbPath, err)
}
defer fp.Close()
fileInfo, err := fp.Stat()
if err != nil {
return nil, errors.WeChatDecryptFailed(err)
return nil, errors.StatFileFailed(dbPath, err)
}
fileSize := fileInfo.Size()
@@ -50,10 +50,10 @@ func OpenDBFile(dbPath string, pageSize int) (*DBFile, error) {
buffer := make([]byte, pageSize)
n, err := io.ReadFull(fp, buffer)
if err != nil {
return nil, errors.DecryptReadFileFailed(dbPath, err)
return nil, errors.ReadFileFailed(dbPath, err)
}
if n != pageSize {
return nil, errors.DecryptIncompleteRead(fmt.Errorf("read %d bytes, expected %d", n, pageSize))
return nil, errors.IncompleteRead(fmt.Errorf("read %d bytes, expected %d", n, pageSize))
}
if bytes.Equal(buffer[:len(SQLiteHeader)-1], []byte(SQLiteHeader[:len(SQLiteHeader)-1])) {

View File

@@ -10,6 +10,7 @@ import (
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt/common"
"golang.org/x/crypto/pbkdf2"
)
@@ -75,7 +76,7 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 解码密钥
key, err := hex.DecodeString(hexKey)
if err != nil {
return errors.DecryptDecodeKeyFailed(err)
return errors.DecodeKeyFailed(err)
}
// 打开数据库文件并读取基本信息
@@ -95,14 +96,14 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 打开数据库文件
dbFile, err := os.Open(dbfile)
if err != nil {
return errors.DecryptOpenFileFailed(dbfile, err)
return errors.OpenFileFailed(dbfile, err)
}
defer dbFile.Close()
// 写入 SQLite 头
_, err = output.Write([]byte(common.SQLiteHeader))
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
// 处理每一页
@@ -112,7 +113,7 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 检查是否取消
select {
case <-ctx.Done():
return errors.DecryptOperationCanceled()
return errors.ErrDecryptOperationCanceled
default:
// 继续处理
}
@@ -126,7 +127,7 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
break
}
}
return errors.DecryptReadFileFailed(dbfile, err)
return errors.ReadFileFailed(dbfile, err)
}
// 检查页面是否全为零
@@ -142,7 +143,7 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 写入零页面
_, err = output.Write(pageBuf)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
continue
}
@@ -156,7 +157,7 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 写入解密后的页面
_, err = output.Write(decryptedData)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
}

View File

@@ -80,7 +80,7 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 解码密钥
key, err := hex.DecodeString(hexKey)
if err != nil {
return errors.DecryptDecodeKeyFailed(err)
return errors.DecodeKeyFailed(err)
}
// 打开数据库文件并读取基本信息
@@ -100,14 +100,14 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 打开数据库文件
dbFile, err := os.Open(dbfile)
if err != nil {
return errors.DecryptOpenFileFailed(dbfile, err)
return errors.OpenFileFailed(dbfile, err)
}
defer dbFile.Close()
// 写入SQLite头
_, err = output.Write([]byte(common.SQLiteHeader))
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
// 处理每一页
@@ -117,7 +117,7 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 检查是否取消
select {
case <-ctx.Done():
return errors.DecryptOperationCanceled()
return errors.ErrDecryptOperationCanceled
default:
// 继续处理
}
@@ -131,7 +131,7 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
break
}
}
return errors.DecryptReadFileFailed(dbfile, err)
return errors.ReadFileFailed(dbfile, err)
}
// 检查页面是否全为零
@@ -147,7 +147,7 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 写入零页面
_, err = output.Write(pageBuf)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
continue
}
@@ -161,7 +161,7 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 写入解密后的页面
_, err = output.Write(decryptedData)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
}

View File

@@ -2,7 +2,6 @@ package decrypt
import (
"context"
"fmt"
"io"
"github.com/sjzar/chatlog/internal/errors"
@@ -10,12 +9,6 @@ import (
"github.com/sjzar/chatlog/internal/wechat/decrypt/windows"
)
// 错误定义
var (
ErrInvalidVersion = fmt.Errorf("invalid version, must be 3 or 4")
ErrUnsupportedPlatform = fmt.Errorf("unsupported platform")
)
// Decryptor 定义数据库解密的接口
type Decryptor interface {
// Decrypt 解密数据库

View File

@@ -78,7 +78,7 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 解码密钥
key, err := hex.DecodeString(hexKey)
if err != nil {
return errors.DecryptDecodeKeyFailed(err)
return errors.DecodeKeyFailed(err)
}
// 打开数据库文件并读取基本信息
@@ -98,14 +98,14 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 打开数据库文件
dbFile, err := os.Open(dbfile)
if err != nil {
return errors.DecryptOpenFileFailed(dbfile, err)
return errors.OpenFileFailed(dbfile, err)
}
defer dbFile.Close()
// 写入SQLite头
_, err = output.Write([]byte(common.SQLiteHeader))
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
// 处理每一页
@@ -115,7 +115,7 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 检查是否取消
select {
case <-ctx.Done():
return errors.DecryptOperationCanceled()
return errors.ErrDecryptOperationCanceled
default:
// 继续处理
}
@@ -129,7 +129,7 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
break
}
}
return errors.DecryptReadFileFailed(dbfile, err)
return errors.ReadFileFailed(dbfile, err)
}
// 检查页面是否全为零
@@ -145,7 +145,7 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 写入零页面
_, err = output.Write(pageBuf)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
continue
}
@@ -159,7 +159,7 @@ func (d *V3Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 写入解密后的页面
_, err = output.Write(decryptedData)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt/common"
"golang.org/x/crypto/pbkdf2"
)
@@ -76,7 +77,7 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 解码密钥
key, err := hex.DecodeString(hexKey)
if err != nil {
return errors.DecryptDecodeKeyFailed(err)
return errors.DecodeKeyFailed(err)
}
// 打开数据库文件并读取基本信息
@@ -96,14 +97,14 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 打开数据库文件
dbFile, err := os.Open(dbfile)
if err != nil {
return errors.DecryptOpenFileFailed(dbfile, err)
return errors.OpenFileFailed(dbfile, err)
}
defer dbFile.Close()
// 写入SQLite头
_, err = output.Write([]byte(common.SQLiteHeader))
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
// 处理每一页
@@ -113,7 +114,7 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 检查是否取消
select {
case <-ctx.Done():
return errors.DecryptOperationCanceled()
return errors.ErrDecryptOperationCanceled
default:
// 继续处理
}
@@ -127,7 +128,7 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
break
}
}
return errors.DecryptReadFileFailed(dbfile, err)
return errors.ReadFileFailed(dbfile, err)
}
// 检查页面是否全为零
@@ -143,7 +144,7 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 写入零页面
_, err = output.Write(pageBuf)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
continue
}
@@ -157,7 +158,7 @@ func (d *V4Decryptor) Decrypt(ctx context.Context, dbfile string, hexKey string,
// 写入解密后的页面
_, err = output.Write(decryptedData)
if err != nil {
return errors.DecryptWriteOutputFailed(err)
return errors.WriteOutputFailed(err)
}
}

View File

@@ -8,6 +8,9 @@ import (
"os/exec"
"path/filepath"
"time"
"github.com/rs/zerolog/log"
"github.com/sjzar/chatlog/internal/errors"
)
// FIXME 按照 region 读取效率较低512MB 内存读取耗时约 18s
@@ -38,14 +41,14 @@ func (g *Glance) Read() ([]byte, error) {
g.MemRegions = MemRegionsFilter(regions)
if len(g.MemRegions) == 0 {
return nil, fmt.Errorf("no memory regions found")
return nil, errors.ErrNoMemoryRegionsFound
}
region := g.MemRegions[0]
// 1. Create pipe file
if err := exec.Command("mkfifo", g.pipePath).Run(); err != nil {
return nil, fmt.Errorf("failed to create pipe file: %w", err)
return nil, errors.CreatePipeFileFailed(err)
}
defer os.Remove(g.pipePath)
@@ -56,7 +59,7 @@ func (g *Glance) Read() ([]byte, error) {
// Open pipe for reading
file, err := os.OpenFile(g.pipePath, os.O_RDONLY, 0600)
if err != nil {
errCh <- fmt.Errorf("failed to open pipe for reading: %w", err)
errCh <- errors.OpenPipeFileFailed(err)
return
}
defer file.Close()
@@ -64,7 +67,7 @@ func (g *Glance) Read() ([]byte, error) {
// Read all data from pipe
data, err := io.ReadAll(file)
if err != nil {
errCh <- fmt.Errorf("failed to read from pipe: %w", err)
errCh <- errors.ReadPipeFileFailed(err)
return
}
dataCh <- data
@@ -80,12 +83,12 @@ func (g *Glance) Read() ([]byte, error) {
// Set up stdout pipe for monitoring (optional)
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("failed to create stdout pipe: %w", err)
return nil, err
}
// Start the command
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("failed to start lldb: %w", err)
return nil, errors.RunCmdFailed(err)
}
// Monitor lldb output (optional)
@@ -102,16 +105,16 @@ func (g *Glance) Read() ([]byte, error) {
case data := <-dataCh:
g.data = data
case err := <-errCh:
return nil, fmt.Errorf("failed to read memory: %w", err)
return nil, errors.ReadMemoryFailed(err)
case <-time.After(30 * time.Second):
cmd.Process.Kill()
return nil, fmt.Errorf("timeout waiting for memory data")
return nil, errors.ErrReadMemoryTimeout
}
// Wait for the command to finish
if err := cmd.Wait(); err != nil {
// We already have the data, so just log the error
fmt.Printf("Warning: lldb process exited with error: %v\n", err)
log.Err(err).Msg("lldb process exited with error")
}
return g.data, nil

View File

@@ -7,6 +7,8 @@ import (
"regexp"
"strconv"
"strings"
"github.com/sjzar/chatlog/internal/errors"
)
const (
@@ -31,7 +33,7 @@ func GetVmmap(pid uint32) ([]MemRegion, error) {
cmd := exec.Command(CommandVmmap, "-wide", fmt.Sprintf("%d", pid))
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("error executing vmmap command: %w", err)
return nil, errors.RunCmdFailed(err)
}
// Parse the output using the existing LoadVmmap function

View File

@@ -4,12 +4,12 @@ import (
"bytes"
"context"
"encoding/hex"
"fmt"
"runtime"
"sync"
"github.com/sirupsen/logrus"
"github.com/rs/zerolog/log"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt"
"github.com/sjzar/chatlog/internal/wechat/key/darwin/glance"
"github.com/sjzar/chatlog/internal/wechat/model"
@@ -29,16 +29,16 @@ func NewV3Extractor() *V3Extractor {
func (e *V3Extractor) Extract(ctx context.Context, proc *model.Process) (string, error) {
if proc.Status == model.StatusOffline {
return "", fmt.Errorf("WeChat is offline")
return "", errors.ErrWeChatOffline
}
// Check if SIP is disabled, as it's required for memory reading on macOS
if !glance.IsSIPDisabled() {
return "", fmt.Errorf("System Integrity Protection (SIP) is enabled, cannot read process memory")
return "", errors.ErrSIPEnabled
}
if e.validator == nil {
return "", fmt.Errorf("validator not set")
return "", errors.ErrValidatorNotSet
}
// Create context to control all goroutines
@@ -57,7 +57,7 @@ func (e *V3Extractor) Extract(ctx context.Context, proc *model.Process) (string,
if workerCount > MaxWorkersV3 {
workerCount = MaxWorkersV3
}
logrus.Debug("Starting ", workerCount, " workers for V3 key search")
log.Debug().Msgf("Starting %d workers for V3 key search", workerCount)
// Start consumer goroutines
var workerWaitGroup sync.WaitGroup
@@ -77,7 +77,7 @@ func (e *V3Extractor) Extract(ctx context.Context, proc *model.Process) (string,
defer close(memoryChannel) // Close channel when producer is done
err := e.findMemory(searchCtx, uint32(proc.PID), memoryChannel)
if err != nil {
logrus.Error(err)
log.Err(err).Msg("Failed to read memory")
}
}()
@@ -98,7 +98,7 @@ func (e *V3Extractor) Extract(ctx context.Context, proc *model.Process) (string,
}
}
return "", fmt.Errorf("no valid key found")
return "", errors.ErrNoValidKey
}
// findMemory searches for memory regions using Glance
@@ -109,15 +109,15 @@ func (e *V3Extractor) findMemory(ctx context.Context, pid uint32, memoryChannel
// Read memory data
memory, err := g.Read()
if err != nil {
return fmt.Errorf("failed to read process memory: %w", err)
return err
}
logrus.Debug("Read memory region, size: ", len(memory), " bytes")
log.Debug().Msgf("Read memory region, size: %d bytes", len(memory))
// Send memory data to channel for processing
select {
case memoryChannel <- memory:
logrus.Debug("Sent memory region for analysis")
log.Debug().Msg("Memory region sent for analysis")
case <-ctx.Done():
return ctx.Err()
}
@@ -146,7 +146,7 @@ func (e *V3Extractor) worker(ctx context.Context, memoryChannel <-chan []byte, r
default:
}
logrus.Debugf("Searching for V3 key in memory region, size: %d bytes", len(memory))
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)
@@ -154,7 +154,7 @@ func (e *V3Extractor) worker(ctx context.Context, memoryChannel <-chan []byte, r
break // No more matches found
}
logrus.Debugf("Found potential V3 key pattern in memory region, index: %d", index)
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) {
@@ -170,7 +170,7 @@ func (e *V3Extractor) worker(ctx context.Context, memoryChannel <-chan []byte, r
if e.validator.Validate(keyData) {
select {
case resultChannel <- hex.EncodeToString(keyData):
logrus.Debug("Valid key found for V3 database")
log.Debug().Msg("Key found: " + hex.EncodeToString(keyData))
return
default:
}

View File

@@ -4,12 +4,12 @@ import (
"bytes"
"context"
"encoding/hex"
"fmt"
"runtime"
"sync"
"github.com/sirupsen/logrus"
"github.com/rs/zerolog/log"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt"
"github.com/sjzar/chatlog/internal/wechat/key/darwin/glance"
"github.com/sjzar/chatlog/internal/wechat/model"
@@ -29,16 +29,16 @@ func NewV4Extractor() *V4Extractor {
func (e *V4Extractor) Extract(ctx context.Context, proc *model.Process) (string, error) {
if proc.Status == model.StatusOffline {
return "", fmt.Errorf("WeChat is offline")
return "", errors.ErrWeChatOffline
}
// Check if SIP is disabled, as it's required for memory reading on macOS
if !glance.IsSIPDisabled() {
return "", fmt.Errorf("System Integrity Protection (SIP) is enabled, cannot read process memory")
return "", errors.ErrSIPEnabled
}
if e.validator == nil {
return "", fmt.Errorf("validator not set")
return "", errors.ErrValidatorNotSet
}
// Create context to control all goroutines
@@ -57,7 +57,7 @@ func (e *V4Extractor) Extract(ctx context.Context, proc *model.Process) (string,
if workerCount > MaxWorkers {
workerCount = MaxWorkers
}
logrus.Debug("Starting ", workerCount, " workers for V4 key search")
log.Debug().Msgf("Starting %d workers for V4 key search", workerCount)
// Start consumer goroutines
var workerWaitGroup sync.WaitGroup
@@ -77,7 +77,7 @@ func (e *V4Extractor) Extract(ctx context.Context, proc *model.Process) (string,
defer close(memoryChannel) // Close channel when producer is done
err := e.findMemory(searchCtx, uint32(proc.PID), memoryChannel)
if err != nil {
logrus.Error(err)
log.Err(err).Msg("Failed to read memory")
}
}()
@@ -98,7 +98,7 @@ func (e *V4Extractor) Extract(ctx context.Context, proc *model.Process) (string,
}
}
return "", fmt.Errorf("no valid key found")
return "", errors.ErrNoValidKey
}
// findMemory searches for memory regions using Glance
@@ -109,15 +109,15 @@ func (e *V4Extractor) findMemory(ctx context.Context, pid uint32, memoryChannel
// Read memory data
memory, err := g.Read()
if err != nil {
return fmt.Errorf("failed to read process memory: %w", err)
return err
}
logrus.Debug("Read memory region, size: ", len(memory), " bytes")
log.Debug().Msgf("Read memory region, size: %d bytes", len(memory))
// Send memory data to channel for processing
select {
case memoryChannel <- memory:
logrus.Debug("Sent memory region for analysis")
log.Debug().Msg("Memory region sent for analysis")
case <-ctx.Done():
return ctx.Err()
}
@@ -167,7 +167,7 @@ func (e *V4Extractor) worker(ctx context.Context, memoryChannel <-chan []byte, r
if e.validator.Validate(keyData) {
select {
case resultChannel <- hex.EncodeToString(keyData):
logrus.Debug("Valid key found for V4 database")
log.Debug().Msg("Key found: " + hex.EncodeToString(keyData))
return
default:
}

View File

@@ -2,20 +2,14 @@ package key
import (
"context"
"fmt"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt"
"github.com/sjzar/chatlog/internal/wechat/key/darwin"
"github.com/sjzar/chatlog/internal/wechat/key/windows"
"github.com/sjzar/chatlog/internal/wechat/model"
)
// 错误定义
var (
ErrInvalidVersion = fmt.Errorf("invalid version, must be 3 or 4")
ErrUnsupportedPlatform = fmt.Errorf("unsupported platform")
)
// Extractor 定义密钥提取器接口
type Extractor interface {
// Extract 从进程中提取密钥
@@ -36,6 +30,6 @@ func NewExtractor(platform string, version int) (Extractor, error) {
case platform == "darwin" && version == 4:
return darwin.NewV4Extractor(), nil
default:
return nil, fmt.Errorf("%w: %s v%d", ErrUnsupportedPlatform, platform, version)
return nil, errors.PlatformUnsupported(platform, version)
}
}

View File

@@ -1,20 +1,9 @@
package windows
import (
"errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt"
)
// Common error definitions
var (
ErrWeChatOffline = errors.New("wechat is not logged in")
ErrOpenProcess = errors.New("failed to open process")
ErrCheckProcessBits = errors.New("failed to check process architecture")
ErrFindWeChatDLL = errors.New("WeChatWin.dll module not found")
ErrNoValidKey = errors.New("no valid key found")
)
type V3Extractor struct {
validator *decrypt.Validator
}

View File

@@ -10,9 +10,10 @@ import (
"sync"
"unsafe"
"github.com/sirupsen/logrus"
"github.com/rs/zerolog/log"
"golang.org/x/sys/windows"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/model"
"github.com/sjzar/chatlog/pkg/util"
)
@@ -24,20 +25,20 @@ const (
func (e *V3Extractor) Extract(ctx context.Context, proc *model.Process) (string, error) {
if proc.Status == model.StatusOffline {
return "", ErrWeChatOffline
return "", errors.ErrWeChatOffline
}
// Open WeChat process
handle, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION|windows.PROCESS_VM_READ, false, proc.PID)
if err != nil {
return "", fmt.Errorf("%w: %v", ErrOpenProcess, err)
return "", errors.OpenProcessFailed(err)
}
defer windows.CloseHandle(handle)
// Check process architecture
is64Bit, err := util.Is64Bit(handle)
if err != nil {
return "", fmt.Errorf("%w: %v", ErrCheckProcessBits, err)
return "", err
}
// Create context to control all goroutines
@@ -56,7 +57,7 @@ func (e *V3Extractor) Extract(ctx context.Context, proc *model.Process) (string,
if workerCount > MaxWorkers {
workerCount = MaxWorkers
}
logrus.Debug("Starting ", workerCount, " workers for V3 key search")
log.Debug().Msgf("Starting %d workers for V3 key search", workerCount)
// Start consumer goroutines
var workerWaitGroup sync.WaitGroup
@@ -76,7 +77,7 @@ func (e *V3Extractor) Extract(ctx context.Context, proc *model.Process) (string,
defer close(memoryChannel) // Close channel when producer is done
err := e.findMemory(searchCtx, handle, proc.PID, memoryChannel)
if err != nil {
logrus.Error(err)
log.Err(err).Msg("Failed to find memory regions")
}
}()
@@ -97,7 +98,7 @@ func (e *V3Extractor) Extract(ctx context.Context, proc *model.Process) (string,
}
}
return "", ErrNoValidKey
return "", errors.ErrNoValidKey
}
// findMemoryV3 searches for writable memory regions in WeChatWin.dll for V3 version
@@ -105,9 +106,9 @@ func (e *V3Extractor) findMemory(ctx context.Context, handle windows.Handle, pid
// Find WeChatWin.dll module
module, isFound := FindModule(pid, V3ModuleName)
if !isFound {
return ErrFindWeChatDLL
return errors.ErrWeChatDLLNotFound
}
logrus.Debug("Found WeChatWin.dll module at base address: 0x", fmt.Sprintf("%X", module.ModBaseAddr))
log.Debug().Msg("Found WeChatWin.dll module at base address: 0x" + fmt.Sprintf("%X", module.ModBaseAddr))
// Read writable memory regions
baseAddr := uintptr(module.ModBaseAddr)
@@ -141,7 +142,7 @@ func (e *V3Extractor) findMemory(ctx context.Context, handle windows.Handle, pid
if err = windows.ReadProcessMemory(handle, currentAddr, &memory[0], regionSize, nil); err == nil {
select {
case memoryChannel <- memory:
logrus.Debug("Sent memory region for analysis, size: ", regionSize, " bytes")
log.Debug().Msgf("Memory region: 0x%X - 0x%X, size: %d bytes", currentAddr, currentAddr+regionSize, regionSize)
case <-ctx.Done():
return nil
}
@@ -198,7 +199,7 @@ func (e *V3Extractor) worker(ctx context.Context, handle windows.Handle, is64Bit
if key := e.validateKey(handle, ptrValue); key != "" {
select {
case resultChannel <- key:
logrus.Debug("Valid key found for V3 database")
log.Debug().Msg("Valid key found: " + key)
return
default:
}
@@ -230,7 +231,7 @@ func FindModule(pid uint32, name string) (module windows.ModuleEntry32, isFound
// Create module snapshot
snapshot, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPMODULE|windows.TH32CS_SNAPMODULE32, pid)
if err != nil {
logrus.Debug("Failed to create module snapshot: ", err)
log.Debug().Msgf("Failed to create module snapshot for PID %d: %v", pid, err)
return module, false
}
defer windows.CloseHandle(snapshot)
@@ -240,7 +241,7 @@ func FindModule(pid uint32, name string) (module windows.ModuleEntry32, isFound
// Get the first module
if err := windows.Module32First(snapshot, &module); err != nil {
logrus.Debug("Failed to get first module: ", err)
log.Debug().Msgf("Module32First failed for PID %d: %v", pid, err)
return module, false
}

View File

@@ -5,14 +5,14 @@ import (
"context"
"encoding/binary"
"encoding/hex"
"fmt"
"runtime"
"sync"
"unsafe"
"github.com/sirupsen/logrus"
"github.com/rs/zerolog/log"
"golang.org/x/sys/windows"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/model"
)
@@ -22,13 +22,13 @@ const (
func (e *V4Extractor) Extract(ctx context.Context, proc *model.Process) (string, error) {
if proc.Status == model.StatusOffline {
return "", ErrWeChatOffline
return "", errors.ErrWeChatOffline
}
// Open process handle
handle, err := windows.OpenProcess(windows.PROCESS_VM_READ|windows.PROCESS_QUERY_INFORMATION, false, proc.PID)
if err != nil {
return "", fmt.Errorf("%w: %v", ErrOpenProcess, err)
return "", errors.OpenProcessFailed(err)
}
defer windows.CloseHandle(handle)
@@ -48,7 +48,7 @@ func (e *V4Extractor) Extract(ctx context.Context, proc *model.Process) (string,
if workerCount > MaxWorkers {
workerCount = MaxWorkers
}
logrus.Debug("Starting ", workerCount, " workers for V4 key search")
log.Debug().Msgf("Starting %d workers for V4 key search", workerCount)
// Start consumer goroutines
var workerWaitGroup sync.WaitGroup
@@ -68,7 +68,7 @@ func (e *V4Extractor) Extract(ctx context.Context, proc *model.Process) (string,
defer close(memoryChannel) // Close channel when producer is done
err := e.findMemory(searchCtx, handle, memoryChannel)
if err != nil {
logrus.Error(err)
log.Err(err).Msg("Failed to find memory regions")
}
}()
@@ -89,7 +89,7 @@ func (e *V4Extractor) Extract(ctx context.Context, proc *model.Process) (string,
}
}
return "", ErrNoValidKey
return "", errors.ErrNoValidKey
}
// findMemoryV4 searches for writable memory regions for V4 version
@@ -101,7 +101,7 @@ func (e *V4Extractor) findMemory(ctx context.Context, handle windows.Handle, mem
if runtime.GOARCH == "amd64" {
maxAddr = uintptr(0x7FFFFFFFFFFF) // 64-bit process space limit
}
logrus.Debug("Scanning memory regions from 0x", fmt.Sprintf("%X", minAddr), " to 0x", fmt.Sprintf("%X", maxAddr))
log.Debug().Msgf("Scanning memory regions from 0x%X to 0x%X", minAddr, maxAddr)
currentAddr := minAddr
@@ -131,7 +131,7 @@ func (e *V4Extractor) findMemory(ctx context.Context, handle windows.Handle, mem
if err = windows.ReadProcessMemory(handle, currentAddr, &memory[0], regionSize, nil); err == nil {
select {
case memoryChannel <- memory:
logrus.Debug("Sent memory region for analysis, size: ", regionSize, " bytes")
log.Debug().Msgf("Memory region for analysis: 0x%X - 0x%X, size: %d bytes", currentAddr, currentAddr+regionSize, regionSize)
case <-ctx.Done():
return nil
}
@@ -185,7 +185,7 @@ func (e *V4Extractor) worker(ctx context.Context, handle windows.Handle, memoryC
if key := e.validateKey(handle, ptrValue); key != "" {
select {
case resultChannel <- key:
logrus.Debug("Valid key found for V4 database")
log.Debug().Msg("Valid key found: " + key)
return
default:
}

View File

@@ -2,9 +2,9 @@ package wechat
import (
"context"
"fmt"
"runtime"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/model"
"github.com/sjzar/chatlog/internal/wechat/process"
)
@@ -87,7 +87,7 @@ func (m *Manager) GetAccount(name string) (*Account, error) {
func (m *Manager) GetProcess(name string) (*model.Process, error) {
p, ok := m.processMap[name]
if !ok {
return nil, fmt.Errorf("account not found: %s", name)
return nil, errors.WeChatAccountNotFound(name)
}
return p, nil
}

View File

@@ -1,15 +1,15 @@
package darwin
import (
"fmt"
"os/exec"
"path/filepath"
"strconv"
"strings"
"github.com/rs/zerolog/log"
"github.com/shirou/gopsutil/v4/process"
log "github.com/sirupsen/logrus"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/model"
"github.com/sjzar/chatlog/pkg/appver"
)
@@ -33,7 +33,7 @@ func NewDetector() *Detector {
func (d *Detector) FindProcesses() ([]*model.Process, error) {
processes, err := process.Processes()
if err != nil {
log.Errorf("获取进程列表失败: %v", err)
log.Err(err).Msg("获取进程列表失败")
return nil, err
}
@@ -47,7 +47,7 @@ func (d *Detector) FindProcesses() ([]*model.Process, error) {
// 获取进程信息
procInfo, err := d.getProcessInfo(p)
if err != nil {
log.Errorf("获取进程 %d 的信息失败: %v", p.Pid, err)
log.Err(err).Msgf("获取进程 %d 的信息失败", p.Pid)
continue
}
@@ -68,7 +68,7 @@ func (d *Detector) getProcessInfo(p *process.Process) (*model.Process, error) {
// 获取可执行文件路径
exePath, err := p.Exe()
if err != nil {
log.Error(err)
log.Err(err).Msg("获取可执行文件路径失败")
return nil, err
}
procInfo.ExePath = exePath
@@ -77,7 +77,7 @@ func (d *Detector) getProcessInfo(p *process.Process) (*model.Process, error) {
// 注意macOS 的版本获取方式可能与 Windows 不同
versionInfo, err := appver.New(exePath)
if err != nil {
log.Error(err)
log.Err(err).Msg("获取版本信息失败")
procInfo.Version = 3
procInfo.FullVersion = "3.0.0"
} else {
@@ -87,7 +87,7 @@ func (d *Detector) getProcessInfo(p *process.Process) (*model.Process, error) {
// 初始化附加信息(数据目录、账户名)
if err := d.initializeProcessInfo(p, procInfo); err != nil {
log.Errorf("初始化进程信息失败: %v", err)
log.Err(err).Msg("初始化进程信息失败")
// 即使初始化失败也返回部分信息
}
@@ -99,7 +99,7 @@ func (d *Detector) initializeProcessInfo(p *process.Process, info *model.Process
// 使用 lsof 命令获取进程打开的文件
files, err := d.getOpenFiles(int(p.Pid))
if err != nil {
log.Error("获取打开文件列表失败: ", err)
log.Err(err).Msg("获取打开文件失败")
return err
}
@@ -112,7 +112,7 @@ func (d *Detector) initializeProcessInfo(p *process.Process, info *model.Process
if strings.Contains(filePath, dbPath) {
parts := strings.Split(filePath, string(filepath.Separator))
if len(parts) < 4 {
log.Debug("无效的文件路径格式: " + filePath)
log.Debug().Msg("无效的文件路径格式: " + filePath)
continue
}
@@ -142,7 +142,7 @@ func (d *Detector) getOpenFiles(pid int) ([]string, error) {
cmd := exec.Command("lsof", "-p", strconv.Itoa(pid), "-F", "n")
output, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("执行 lsof 命令失败: %v", err)
return nil, errors.RunCmdFailed(err)
}
// 解析 lsof -F n 输出

View File

@@ -3,8 +3,8 @@ package windows
import (
"strings"
"github.com/rs/zerolog/log"
"github.com/shirou/gopsutil/v4/process"
log "github.com/sirupsen/logrus"
"github.com/sjzar/chatlog/internal/wechat/model"
"github.com/sjzar/chatlog/pkg/appver"
@@ -29,7 +29,7 @@ func NewDetector() *Detector {
func (d *Detector) FindProcesses() ([]*model.Process, error) {
processes, err := process.Processes()
if err != nil {
log.Errorf("获取进程列表失败: %v", err)
log.Err(err).Msg("获取进程列表失败")
return nil, err
}
@@ -45,7 +45,7 @@ func (d *Detector) FindProcesses() ([]*model.Process, error) {
if name == V4ProcessName {
cmdline, err := p.Cmdline()
if err != nil {
log.Error(err)
log.Err(err).Msg("获取进程命令行失败")
continue
}
if strings.Contains(cmdline, "--") {
@@ -56,7 +56,7 @@ func (d *Detector) FindProcesses() ([]*model.Process, error) {
// 获取进程信息
procInfo, err := d.getProcessInfo(p)
if err != nil {
log.Errorf("获取进程 %d 的信息失败: %v", p.Pid, err)
log.Err(err).Msgf("获取进程 %d 的信息失败", p.Pid)
continue
}
@@ -77,7 +77,7 @@ func (d *Detector) getProcessInfo(p *process.Process) (*model.Process, error) {
// 获取可执行文件路径
exePath, err := p.Exe()
if err != nil {
log.Error(err)
log.Err(err).Msg("获取可执行文件路径失败")
return nil, err
}
procInfo.ExePath = exePath
@@ -85,7 +85,7 @@ func (d *Detector) getProcessInfo(p *process.Process) (*model.Process, error) {
// 获取版本信息
versionInfo, err := appver.New(exePath)
if err != nil {
log.Error(err)
log.Err(err).Msg("获取版本信息失败")
return nil, err
}
procInfo.Version = versionInfo.Version
@@ -93,7 +93,7 @@ func (d *Detector) getProcessInfo(p *process.Process) (*model.Process, error) {
// 初始化附加信息(数据目录、账户名)
if err := initializeProcessInfo(p, procInfo); err != nil {
log.Errorf("初始化进程信息失败: %v", err)
log.Err(err).Msg("初始化进程信息失败")
// 即使初始化失败也返回部分信息
}

View File

@@ -4,8 +4,8 @@ import (
"path/filepath"
"strings"
"github.com/rs/zerolog/log"
"github.com/shirou/gopsutil/v4/process"
log "github.com/sirupsen/logrus"
"github.com/sjzar/chatlog/internal/wechat/model"
)
@@ -14,7 +14,7 @@ import (
func initializeProcessInfo(p *process.Process, info *model.Process) error {
files, err := p.OpenFiles()
if err != nil {
log.Error("获取打开文件列表失败: ", err)
log.Err(err).Msgf("获取进程 %d 的打开文件失败", p.Pid)
return err
}
@@ -28,7 +28,7 @@ func initializeProcessInfo(p *process.Process, info *model.Process) error {
filePath := f.Path[4:] // 移除 "\\?\" 前缀
parts := strings.Split(filePath, string(filepath.Separator))
if len(parts) < 4 {
log.Debug("无效的文件路径格式: " + filePath)
log.Debug().Msg("无效的文件路径: " + filePath)
continue
}

View File

@@ -2,9 +2,9 @@ package wechat
import (
"context"
"fmt"
"os"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/wechat/decrypt"
"github.com/sjzar/chatlog/internal/wechat/key"
"github.com/sjzar/chatlog/internal/wechat/model"
@@ -71,28 +71,28 @@ func (a *Account) GetKey(ctx context.Context) (string, error) {
// 刷新进程状态
if err := a.RefreshStatus(); err != nil {
return "", fmt.Errorf("failed to refresh process status: %w", err)
return "", errors.RefreshProcessStatusFailed(err)
}
// 检查账号状态
if a.Status != model.StatusOnline {
return "", fmt.Errorf("account %s is not online", a.Name)
return "", errors.WeChatAccountNotOnline(a.Name)
}
// 创建密钥提取器 - 使用新的接口,传入平台和版本信息
extractor, err := key.NewExtractor(a.Platform, a.Version)
if err != nil {
return "", fmt.Errorf("failed to create key extractor: %w", err)
return "", err
}
process, err := GetProcess(a.Name)
if err != nil {
return "", fmt.Errorf("failed to get process: %w", err)
return "", err
}
validator, err := decrypt.NewValidator(process.DataDir, process.Platform, process.Version)
if err != nil {
return "", fmt.Errorf("failed to create validator: %w", err)
return "", err
}
extractor.SetValidate(validator)