Compare commits
1 Commits
v0.0.15
...
feature/da
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67eff715f6 |
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/
|
||||||
|
|||||||
148
cmd/chatlog/cmd_dumpmemory.go
Normal file
148
cmd/chatlog/cmd_dumpmemory.go
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -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,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user