support multi wechat

This commit is contained in:
yincong
2026-04-08 09:44:45 +08:00
parent 95839403b8
commit b6121ba5da
4 changed files with 59 additions and 45 deletions
+17 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
+1 -1
View File
@@ -19,4 +19,4 @@
"downloadVideoAddr": "0x4A2A268",
"videoCallbackFuncAddr": "0x24C76C8",
"videoMessageCallbackFunc": "0x8793B78"
}
}