147 lines
3.5 KiB
Go
147 lines
3.5 KiB
Go
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)
|
|
},
|
|
}
|