mirror of
https://github.com/yincongcyincong/wechat_chatter.git
synced 2026-07-15 10:26:52 +08:00
support multi wechat
This commit is contained in:
+17
-4
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/frida/frida-go/frida"
|
||||
)
|
||||
@@ -62,6 +63,7 @@ func initFlag() {
|
||||
flag.StringVar(&config.WechatConf, "wechat_conf", "../wechat_version/4_1_8_29_mac.json", "微信配置文件路径: ../wechat_version/4_1_6_12_mac.json")
|
||||
flag.StringVar(&config.ConnType, "conn_type", "http", "连接类型: http | websocket")
|
||||
flag.IntVar(&config.SendInterval, "send_interval", 1000, "发送间隔: ms")
|
||||
flag.IntVar(&config.WechatPid, "wechat_pid", 0, "微信进程 PID,不设置则自动查找")
|
||||
flag.StringVar(&logLevel, "log_level", "info", "log level")
|
||||
|
||||
flag.Parse()
|
||||
@@ -75,6 +77,7 @@ func initFlag() {
|
||||
fmt.Println("WechatConf", config.WechatConf)
|
||||
fmt.Println("ConnType", config.ConnType)
|
||||
fmt.Println("SendInterval", config.SendInterval)
|
||||
fmt.Println("WechatPid", config.WechatPid)
|
||||
fmt.Println("LogLevel", logLevel)
|
||||
}
|
||||
|
||||
@@ -107,11 +110,21 @@ func initFrida() {
|
||||
Fatal("无法获取本地设备", "err", err)
|
||||
}
|
||||
|
||||
pid, err := GetWeChatPID()
|
||||
if err != nil {
|
||||
Fatal("未发现正在运行的微信进程")
|
||||
var pid int
|
||||
if config.WechatPid > 0 {
|
||||
pid = config.WechatPid
|
||||
Info("使用指定的微信进程 PID", "PID", pid)
|
||||
} else {
|
||||
for {
|
||||
pid, err = GetWeChatPID()
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
Info("未发现正在运行的微信进程,5秒后重试...")
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
Info("自动发现微信进程 PID", "PID", pid)
|
||||
}
|
||||
Info("微信进程 PID", "PID", pid)
|
||||
MonitorProcessExit(pid)
|
||||
|
||||
session, err = device.Attach(pid, nil)
|
||||
|
||||
+2
-1
@@ -88,7 +88,8 @@ type Config struct {
|
||||
ImagePath string `json:"image_path"`
|
||||
ConnType string `json:"conn_type"`
|
||||
SendInterval int `json:"send_interval"`
|
||||
|
||||
WechatPid int `json:"wechat_pid"`
|
||||
|
||||
WechatConf string `json:"wechat_conf"`
|
||||
}
|
||||
|
||||
|
||||
+39
-39
@@ -30,14 +30,14 @@ func SaveBase64Image(base64Data string) (string, string, error) {
|
||||
} else if idx := strings.Index(base64Data, ","); idx != -1 {
|
||||
rawContents = base64Data[idx+1:]
|
||||
}
|
||||
|
||||
|
||||
data, err := base64.StdEncoding.DecodeString(rawContents)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("base64 decode failed: %v", err)
|
||||
}
|
||||
salt := []byte(fmt.Sprintf("\n#md5_salt_%d_%d#", time.Now().UnixNano(), rand.Intn(10000)))
|
||||
data = append(data, salt...)
|
||||
|
||||
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
randomNumber := r.Intn(1000) // 生成 0-999 的随机数
|
||||
timestamp := time.Now().Unix()
|
||||
@@ -47,17 +47,17 @@ func SaveBase64Image(base64Data string) (string, string, error) {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return "", "", fmt.Errorf("create directory failed: %v", err)
|
||||
}
|
||||
|
||||
|
||||
err = os.WriteFile(targetPath, data, 0644)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("write file failed: %v", err)
|
||||
}
|
||||
|
||||
|
||||
md5Str, err := GetFileMD5(targetPath)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("get file md5 failed: %v", err)
|
||||
}
|
||||
|
||||
|
||||
return targetPath, md5Str, nil
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func GetFileMD5(filePath string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
|
||||
hash := md5.New()
|
||||
if _, err := io.Copy(hash, file); err != nil {
|
||||
return "", err
|
||||
@@ -80,12 +80,12 @@ func SaveAudioFile(silkBytes []byte) (path string, err error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
randomNumber := r.Intn(1000)
|
||||
timestamp := time.Now().Unix()
|
||||
@@ -95,7 +95,7 @@ func SaveAudioFile(silkBytes []byte) (path string, err error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
return targetPath, nil
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ func SilkToMp3(silkBytes []byte) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
cmd := exec.Command("ffmpeg",
|
||||
"-f", "s16le",
|
||||
"-ar", "16000",
|
||||
@@ -116,16 +116,16 @@ func SilkToMp3(silkBytes []byte) ([]byte, error) {
|
||||
"pipe:1",
|
||||
)
|
||||
cmd.Stdin = bytes.NewReader(pcm)
|
||||
|
||||
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("ffmpeg error: %v, details: %s", err, stderr.String())
|
||||
}
|
||||
|
||||
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
@@ -136,12 +136,12 @@ func GetFilePath(data []byte, key []byte) (string, error) {
|
||||
for i := 0; i < len(data); i += bs {
|
||||
block.Decrypt(decrypted[i:i+bs], data[i:i+bs])
|
||||
}
|
||||
|
||||
|
||||
ext := DetectFileFormat(decrypted)
|
||||
if ext == "unknown" {
|
||||
return "", fmt.Errorf("无法解析的文件数据")
|
||||
}
|
||||
|
||||
|
||||
return SaveFileToFile(ext, decrypted)
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ func DetectFileFormat(data []byte) string {
|
||||
if len(data) < 8 {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
|
||||
switch {
|
||||
// 视频格式
|
||||
case bytes.HasPrefix(data, []byte{0x00, 0x00, 0x00}): // MP4/MOV 通常以 ftyp 开头,后面是具体类型
|
||||
@@ -168,7 +168,7 @@ func DetectFileFormat(data []byte) string {
|
||||
if len(data) > 8 && bytes.HasPrefix(data[8:], []byte{0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}) {
|
||||
return "wmv"
|
||||
}
|
||||
|
||||
|
||||
// 图片格式
|
||||
case bytes.HasPrefix(data, []byte{0xFF, 0xD8, 0xFF}):
|
||||
return "jpg"
|
||||
@@ -180,29 +180,29 @@ func DetectFileFormat(data []byte) string {
|
||||
return "bmp"
|
||||
case bytes.HasPrefix(data, []byte("RIFF")) && len(data) > 8 && bytes.HasPrefix(data[8:], []byte("WEBP")):
|
||||
return "webp"
|
||||
|
||||
|
||||
// 文档格式
|
||||
case bytes.HasPrefix(data, []byte("%PDF")):
|
||||
return "pdf"
|
||||
|
||||
|
||||
// Office 2007+ 格式 (docx, xlsx, pptx 都是 ZIP 格式)
|
||||
case bytes.HasPrefix(data, []byte{0x50, 0x4B, 0x03, 0x04}):
|
||||
return detectOfficeFormat(data)
|
||||
|
||||
|
||||
// Office 97-2003 格式 (OLE2 格式)
|
||||
case bytes.HasPrefix(data, []byte{0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1}):
|
||||
return detectLegacyOfficeFormat(data)
|
||||
|
||||
|
||||
// 压缩文件
|
||||
case bytes.HasPrefix(data, []byte("Rar!\x1a\x07")):
|
||||
return "rar"
|
||||
case bytes.HasPrefix(data, []byte("7z\xBC\xAF\x27\x1C")):
|
||||
return "7z"
|
||||
|
||||
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@@ -245,30 +245,30 @@ func SaveFileToFile(ext string, data []byte) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
randomNumber := r.Intn(1000)
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
|
||||
// 根据文件类型选择保存目录
|
||||
dir := "file"
|
||||
if ext == "jpg" || ext == "png" || ext == "gif" || ext == "bmp" || ext == "webp" {
|
||||
dir = "image"
|
||||
}
|
||||
|
||||
|
||||
fileName := fmt.Sprintf("%d_%d.%s", randomNumber, timestamp, ext)
|
||||
targetPath := filepath.Dir(exePath) + "/" + dir + "/" + fileName
|
||||
|
||||
|
||||
// 确保目录存在
|
||||
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
err = os.WriteFile(targetPath, data, 0644)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
return targetPath, nil
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ func SaveImageToFile(ext string, data []byte) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
randomNumber := r.Intn(1000)
|
||||
timestamp := time.Now().Unix()
|
||||
@@ -287,7 +287,7 @@ func SaveImageToFile(ext string, data []byte) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
return targetPath, nil
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@ func GetWeChatPID() (int, error) {
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("未发现正在运行的微信进程")
|
||||
}
|
||||
|
||||
|
||||
return strconv.Atoi(strings.TrimSpace(string(output)))
|
||||
}
|
||||
|
||||
@@ -305,13 +305,13 @@ func DownloadFile(urlStr string) ([]byte, error) {
|
||||
if urlStr == "" {
|
||||
return nil, errors.New("url is empty")
|
||||
}
|
||||
|
||||
|
||||
// 解析 URL 以判断协议
|
||||
parsedURL, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid URL format: " + err.Error())
|
||||
}
|
||||
|
||||
|
||||
// 处理 file:// 协议
|
||||
if parsedURL.Scheme == "file" {
|
||||
// 去除 "file://" 前缀,得到本地文件路径
|
||||
@@ -323,23 +323,23 @@ func DownloadFile(urlStr string) ([]byte, error) {
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Get(urlStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("failed to download file: " + resp.Status)
|
||||
}
|
||||
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ func DetectAndSaveImage(data []byte) (string, error) {
|
||||
// 如果进程退出,则发送 SIGTERM 停止当前进程
|
||||
func MonitorProcessExit(pid int) {
|
||||
go func() {
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
"downloadVideoAddr": "0x4A2A268",
|
||||
"videoCallbackFuncAddr": "0x24C76C8",
|
||||
"videoMessageCallbackFunc": "0x8793B78"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user