From b6121ba5daf1b0085921045e0b13bdcd744f480e Mon Sep 17 00:00:00 2001 From: yincong Date: Wed, 8 Apr 2026 09:44:45 +0800 Subject: [PATCH] support multi wechat --- onebot/main.go | 21 +++++++-- onebot/param.go | 3 +- onebot/utils.go | 78 ++++++++++++++++---------------- wechat_version/4_1_7_57_mac.json | 2 +- 4 files changed, 59 insertions(+), 45 deletions(-) diff --git a/onebot/main.go b/onebot/main.go index cfd3e38..31d2fde 100644 --- a/onebot/main.go +++ b/onebot/main.go @@ -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) diff --git a/onebot/param.go b/onebot/param.go index d97f7cf..462c468 100644 --- a/onebot/param.go +++ b/onebot/param.go @@ -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"` } diff --git a/onebot/utils.go b/onebot/utils.go index 404c099..198983b 100644 --- a/onebot/utils.go +++ b/onebot/utils.go @@ -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 { diff --git a/wechat_version/4_1_7_57_mac.json b/wechat_version/4_1_7_57_mac.json index 6b730dd..62caaa4 100644 --- a/wechat_version/4_1_7_57_mac.json +++ b/wechat_version/4_1_7_57_mac.json @@ -19,4 +19,4 @@ "downloadVideoAddr": "0x4A2A268", "videoCallbackFuncAddr": "0x24C76C8", "videoMessageCallbackFunc": "0x8793B78" -} \ No newline at end of file +}