Files
wechat_chatter/onebot/main.go
T
2026-02-27 10:14:47 +08:00

274 lines
7.1 KiB
Go

package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"text/template"
"github.com/frida/frida-go/frida"
)
// 全局变量,保持 Frida 脚本对象
var (
fridaScript *frida.Script
session *frida.Session
taskId = int64(0x20000000)
myWechatId = ""
msgChan = make(chan *SendMsg, 10)
finishChan = make(chan struct{})
config = &Config{}
userID2NicknameMap sync.Map
)
type WechatMessage struct {
GroupId string `json:"group_id"`
SelfID string `json:"self_id"`
UserID string `json:"user_id"`
Sender *Sender `json:"sender"`
}
type Sender struct {
UserID string `json:"user_id"`
Nickname string `json:"nickname"`
}
type SendMsg struct {
UserId string
GroupID string
Content string
Type string
AtUser string
}
// SendRequest 请求结构体
type SendRequest struct {
Message []*Message `json:"message"`
UserID string `json:"user_id"`
GroupID string `json:"group_id"`
}
type Message struct {
Type string `json:"type"`
Data *SendRequestData `json:"data"`
}
type SendRequestData struct {
Id string `json:"id"`
Text string `json:"text"`
File string `json:"file"`
QQ string `json:"qq"`
}
type Config struct {
FridaType string `json:"frida_type"`
SendURL string `json:"send_url"`
ReceiveHost string `json:"receive_host"`
FridaGadgetAddr string `json:"frida_gadget_addr"`
WechatPid int `json:"wechat_pid"`
OnebotToken string `json:"onebot_token"`
ImagePath string `json:"image_path"`
ConnType string `json:"conn_type"`
SendInterval int `json:"send_interval"`
WechatConf string `json:"wechat_conf"`
}
func main() {
initFlag()
if config.FridaType == "gadget" {
initFridaGadget()
} else {
initFrida()
}
go SendWorker()
http.HandleFunc("/send_private_msg", sendHandler)
http.HandleFunc("/send_group_msg", sendHandler)
http.HandleFunc("/ws", handleWebSocket)
http.HandleFunc("/test_ws", testWebSocket)
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
go func() {
<-stop
log.Fatalf("\n正在释放 Frida 资源并退出...")
}()
// 3. 启动服务
fmt.Printf("HTTP 服务启动在 %s", config.ReceiveHost)
if err := http.ListenAndServe(config.ReceiveHost, nil); err != nil {
log.Printf("服务启动失败: %v\n", err)
}
}
func initFlag() {
flag.StringVar(&config.FridaType, "type", "local", "frida 类型: local | gadget")
flag.StringVar(&config.SendURL, "send_url", "http://127.0.0.1:36060/onebot", "发送消息的 URL: http://127.0.0.1:36060/onebot")
flag.StringVar(&config.ReceiveHost, "receive_host", "127.0.0.1:58080", "接收消息的地址: 127.0.0.1:58080")
flag.StringVar(&config.FridaGadgetAddr, "gadget_addr", "127.0.0.1:27042", "Gadget 地址: 127.0.0.1:27042 仅当 type 为 gadget 时有效")
flag.IntVar(&config.WechatPid, "wechat_pid", 0, "微信进程 ID: 58183, 仅当 type 为 local 时有效")
flag.StringVar(&config.OnebotToken, "token", "MuseBot", "OneBot Token: MuseBot")
flag.StringVar(&config.ImagePath, "image_path", "", "图片路径: /Users/xxx/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/xxx/temp/xxx/2026-01/Img/")
flag.StringVar(&config.WechatConf, "wechat_conf", "../wechat_version/4_1_7_55_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.Parse()
fmt.Println("FridaType", config.FridaType)
fmt.Println("SendURL", config.SendURL)
fmt.Println("ReceiveHost", config.ReceiveHost)
fmt.Println("FridaGadgetAddr", config.FridaGadgetAddr)
fmt.Println("WechatPid", config.WechatPid)
fmt.Println("OnebotToken", config.OnebotToken)
fmt.Println("ImagePath", config.ImagePath)
fmt.Println("WechatConf", config.WechatConf)
}
func initFridaGadget() {
mgr := frida.NewDeviceManager()
// 连接到 Gadget 默认端口
device, err := mgr.AddRemoteDevice(config.FridaGadgetAddr, frida.NewRemoteDeviceOptions())
if err != nil {
log.Fatalf("❌ 无法连接 Gadget: %v\n", err)
}
session, err = device.Attach("Gadget", nil)
if err != nil {
log.Fatalf("❌ 附加失败: %v\n", err)
}
loadJs()
}
func initFrida() {
// 1. 获取本地设备管理器
mgr := frida.NewDeviceManager()
// 2. 枚举并获取本地设备 (TypeLocal)
device, err := mgr.DeviceByType(frida.DeviceTypeLocal)
if err != nil {
log.Fatalf("无法获取本地设备: %v", err)
}
fmt.Printf("正在尝试 Attach 到微信...")
session, err = device.Attach(config.WechatPid, nil)
if err != nil {
log.Fatalf("Attach 失败 (请检查 SIP 状态或权限): %v", err)
}
loadJs()
}
func loadJs() {
jsonData, err := os.ReadFile(config.WechatConf)
if err != nil {
log.Fatalf("读取文件失败: %v\n", err)
}
// 2. 将 JSON 解析为 Map
var wechatHookConf map[string]interface{}
if err := json.Unmarshal(jsonData, &wechatHookConf); err != nil {
log.Fatalf("解析 JSON 失败: %v\n", err)
}
codeTemplate, err := os.ReadFile("./script.js")
if err != nil {
log.Fatalf("读取脚本失败: %v\n", err)
}
tmpl, err := template.New("fridaScript").Parse(string(codeTemplate))
if err != nil {
fmt.Printf("解析模板失败: %v\n", err)
return
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, wechatHookConf)
if err != nil {
log.Fatalf("执行模板失败: %v\n", err)
}
script, err := session.CreateScript(buf.String())
if err != nil {
log.Fatalf("❌ 创建脚本失败: %v\n", err)
}
// 打印 JS 里的 console.log
script.On("message", func(rawMsg string) {
var msg map[string]interface{}
json.Unmarshal([]byte(rawMsg), &msg)
msgType := msg["type"].(string)
switch msgType {
case "send":
if p, ok := msg["payload"]; ok {
if pMap, ok := p.(map[string]interface{}); ok {
if t, ok := pMap["type"]; ok {
switch t.(string) {
case "send":
if config.ConnType == "http" {
go SendHttpReq(msg)
} else {
go SendWebSocketMsg(msg)
}
case "finish":
finishChan <- struct{}{}
case "upload":
if selfId, ok := pMap["self_id"]; ok && myWechatId == "" {
fmt.Printf("✅ 检测到微信登录,当前账号: %s\n", selfId.(string))
myWechatId = selfId.(string)
}
case "upload_finish":
m := &SendMsg{
Type: "send_image",
}
if targetIdInter, ok := pMap["target_id"]; ok {
targetIdStr := targetIdInter.(string)
if strings.Contains(targetIdStr, "wxid_") {
m.UserId = targetIdStr
} else {
m.GroupID = targetIdStr
}
}
msgChan <- m
}
}
}
}
case "log":
// 这里处理 console.log
log.Printf("[JS日志] %s\n", msg["payload"])
case "error":
// 这里处理 JS 脚本报错
log.Printf("[❌脚本报错] %s\n", msg["description"])
}
})
if err := script.Load(); err != nil {
log.Fatalf("❌ 加载脚本失败: %v\n", err)
}
fridaScript = script
fmt.Printf("✅ Frida 已就绪,微信控制通道已打通")
}