diff --git a/onebot/main.go b/onebot/main.go index c872196..936e4a1 100644 --- a/onebot/main.go +++ b/onebot/main.go @@ -2,11 +2,11 @@ package main import ( "bytes" + "context" "crypto/hmac" "crypto/sha1" "encoding/hex" "encoding/json" - "fmt" "io" "log" "net/http" @@ -24,8 +24,16 @@ var ( fridaScript *frida.Script session *frida.Session taskId = int64(0x20000000) + + msgChan = make(chan *SendMsg, 10) + finishChan = make(chan struct{}) ) +type SendMsg struct { + UserId string + Content string +} + // SendRequest 请求结构体 type SendRequest struct { Message []*Message `json:"message"` @@ -47,13 +55,13 @@ func initFridaGadget() { // 连接到 Gadget 默认端口 device, err := mgr.AddRemoteDevice("127.0.0.1:27042", frida.NewRemoteDeviceOptions()) if err != nil { - fmt.Printf("❌ 无法连接 Gadget: %v\n", err) + log.Printf("❌ 无法连接 Gadget: %v\n", err) os.Exit(1) } session, err = device.Attach("Gadget", nil) if err != nil { - fmt.Printf("❌ 附加失败: %v\n", err) + log.Printf("❌ 附加失败: %v\n", err) os.Exit(1) } @@ -71,7 +79,7 @@ func initFrida() { log.Fatalf("无法获取本地设备: %v", err) } - fmt.Println("正在尝试 Attach 到微信...") + log.Println("正在尝试 Attach 到微信...") session, err = device.Attach(47516, nil) if err != nil { log.Fatalf("Attach 失败 (请检查 SIP 状态或权限): %v", err) @@ -84,7 +92,7 @@ func loadJs() { js, _ := os.ReadFile("./script.js") script, err := session.CreateScript(string(js)) if err != nil { - fmt.Printf("❌ 创建脚本失败: %v\n", err) + log.Printf("❌ 创建脚本失败: %v\n", err) os.Exit(1) } @@ -97,23 +105,33 @@ func loadJs() { switch msgType { case "send": - go SendHttpReq(msg) + if p, ok := msg["payload"]; ok { + if pMap, ok := p.(map[string]interface{}); ok { + if t, ok := pMap["type"]; ok { + if t.(string) == "send" { + go SendHttpReq(msg) + } else if t.(string) == "finish" { + finishChan <- struct{}{} + } + } + } + } case "log": // 这里处理 console.log - fmt.Printf("[JS日志] %s\n", msg["payload"]) + log.Printf("[JS日志] %s\n", msg["payload"]) case "error": // 这里处理 JS 脚本报错 - fmt.Printf("[❌脚本报错] %s\n", msg["description"]) + log.Printf("[❌脚本报错] %s\n", msg["description"]) } }) if err := script.Load(); err != nil { - fmt.Printf("❌ 加载脚本失败: %v\n", err) + log.Printf("❌ 加载脚本失败: %v\n", err) os.Exit(1) } fridaScript = script - fmt.Println("✅ Frida 已就绪,微信控制通道已打通") + log.Println("✅ Frida 已就绪,微信控制通道已打通") } func sendHandler(w http.ResponseWriter, r *http.Request) { @@ -141,40 +159,69 @@ func sendHandler(w http.ResponseWriter, r *http.Request) { } } - // 调用 Frida RPC - atomic.AddInt64(&taskId, 1) - fmt.Printf("📩 收到 HTTP 请求,任务: %d\n", taskId) + msgChan <- &SendMsg{ + UserId: req.UserID, + Content: text, + } - // 注意:这里的名称 "manualtrigger" 必须和 JS 侧 rpc.exports 里的键名完全一致 - result := fridaScript.ExportsCall("manualTrigger", taskId, req.UserID, text) - // 返回结果 json.NewEncoder(w).Encode(map[string]any{ - "status": result, + "status": "ok", }) } +func SendWorker() { + for m := range msgChan { + currTaskId := atomic.AddInt64(&taskId, 1) + log.Printf("📩 收到任务: %d\n", currTaskId) + + // 1. 创建一个 1 秒超时的上下文 + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + + // 必须在处理完后释放 context 资源 + defer cancel() + + // 2. 使用 channel 接收 Frida 返回结果 + resChan := make(chan interface{}, 1) + + go func() { + // 在子协程中执行阻塞的 Frida 调用 + result := fridaScript.ExportsCall("manualTrigger", currTaskId, m.UserId, m.Content) + resChan <- result + }() + + // 3. 核心:通过 select 监听“完成”或“超时” + select { + case result := <-resChan: + log.Printf("✅ 任务 %d 执行成功: %v\n", currTaskId, result) + case <-ctx.Done(): + // 此时已经过了 1 秒,resChan 还没收到数据 + log.Printf("⏰ 任务 %d 执行超时!\n", currTaskId) + case <-finishChan: + log.Printf("🛑 收到完成信号,任务 %d 完成\n", currTaskId) + } + } +} + func main() { - // 1. 初始化 Frida initFrida() + go SendWorker() - // 2. 注册路由 http.HandleFunc("/send_private_msg", sendHandler) stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) - // 启动一个 goroutine 处理退出逻辑 go func() { <-stop - fmt.Println("\n正在释放 Frida 资源并退出...") - os.Exit(0) // 强制结束进程 + log.Println("\n正在释放 Frida 资源并退出...") + os.Exit(0) }() // 3. 启动服务 port := ":58080" - fmt.Printf("🌐 HTTP 服务启动在 http://127.0.0.1%s\n", port) + log.Printf("🌐 HTTP 服务启动在 http://127.0.0.1%s\n", port) if err := http.ListenAndServe(port, nil); err != nil { - fmt.Printf("❌ 服务启动失败: %v\n", err) + log.Printf("❌ 服务启动失败: %v\n", err) } } @@ -182,7 +229,7 @@ func main() { func SendHttpReq(msg map[string]interface{}) { defer func() { if r := recover(); r != nil { - fmt.Printf("panic: %v\n", r) + log.Printf("panic: %v\n", r) } }() @@ -190,16 +237,16 @@ func SendHttpReq(msg map[string]interface{}) { // 这里处理你的 X1 数据 jsonData, err := json.Marshal(msg["payload"]) if err != nil { - fmt.Printf("JSON 序列化失败: %v\n", err) + log.Printf("JSON 序列化失败: %v\n", err) return } - fmt.Printf("发送数据: %s\n", string(jsonData)) + log.Printf("发送数据: %s\n", string(jsonData)) // 4. 创建 POST 请求 req, err := http.NewRequest("POST", "http://127.0.0.1:36060/onebot", bytes.NewBuffer(jsonData)) if err != nil { - fmt.Printf("创建请求失败: %v\n", err) + log.Printf("创建请求失败: %v\n", err) return } @@ -215,7 +262,7 @@ func SendHttpReq(msg map[string]interface{}) { // 6. 执行请求 resp, err := client.Do(req) if err != nil { - fmt.Printf("请求执行失败: %v\n", err) + log.Printf("请求执行失败: %v\n", err) return } defer resp.Body.Close() @@ -223,9 +270,9 @@ func SendHttpReq(msg map[string]interface{}) { // 7. 读取返回结果 body, err := io.ReadAll(resp.Body) if err != nil { - fmt.Printf("读取响应失败: %v\n", err) + log.Printf("读取响应失败: %v\n", err) return } - fmt.Printf("状态码: %d 返回内容: %s\n", resp.StatusCode, string(body)) + log.Printf("状态码: %d 返回内容: %s\n", resp.StatusCode, string(body)) } diff --git a/onebot/script.js b/onebot/script.js index dc07fe2..f0af8ec 100644 --- a/onebot/script.js +++ b/onebot/script.js @@ -13,6 +13,8 @@ var messageCallbackFunc2 = baseAddr.add(0x7f04fc8); var messageCallbackFunc3 = baseAddr.add(0x7f96918); var messageCallbackFunc4 = baseAddr.add(0x7f96a08); var messageCallbackFunc5 = baseAddr.add(0x7f968a0); +var messageCallbackFunc6 = baseAddr.add(0x7f9dfe0); + // 这个必须是绝对位置 var triggerX1Payload = ptr(0x175ED6600); var req2bufEnterAddr = baseAddr.add(0x33EE8E8); @@ -32,6 +34,8 @@ var messageContentAddr = ptr(0); var messageAddrAddr = ptr(0); var contentAddr = ptr(0); var insertMsgAddr = ptr(0); +var receiverAddr = ptr(0); +var htmlContentAddr = ptr(0); var protoX1PayloadAddr = ptr(0); var protoX1PayloadLen = 1024; @@ -74,8 +78,10 @@ function setupSendMessageDynamic() { sendMessageAddr = Memory.alloc(256); messageAddr = Memory.alloc(512); messageContentAddr = Memory.alloc(32); - messageAddrAddr = Memory.alloc(32); + messageAddrAddr = Memory.alloc(64); contentAddr = Memory.alloc(255); + receiverAddr = Memory.alloc(24); + htmlContentAddr = Memory.alloc(24); // A. 写入字符串内容 @@ -126,7 +132,14 @@ function setupSendMessageDynamic() { messageContentAddr.writePointer(messageAddrAddr); messageAddrAddr.writePointer(messageCallbackFunc5); - messageAddrAddr.add(0x08).writePointer(contentAddr); + receiverAddr.writePointer(messageCallbackFunc6); + receiverAddr.add(0x08).writePointer(contentAddr); + messageAddrAddr.add(0x08).writePointer(receiverAddr); + messageAddrAddr.add(0x10).writePointer(contentAddr); + messageAddrAddr.add(0x18).writeU32(1); + messageAddrAddr.add(0x20).writeU32(Math.floor(Date.now() / 1000)); + htmlContentAddr.writePointer(contentAddr); + messageAddrAddr.add(0x28).writePointer(htmlContentAddr); console.log(" [+] messageAddr Object: ", hexdump(messageAddr, { offset: 0, @@ -399,6 +412,7 @@ function attachProto() { ansi: true })); + var firstValue = sp.readU32(); if (firstValue !== taskIdGlobal) { console.log("[+] Protobuf 拦截未命中,跳过..."); @@ -407,7 +421,7 @@ function attachProto() { console.log("[+] 正在注入 Protobuf Payload..."); const type = [0x08, 0x01, 0x12] - const receiverHeader = [0x0A, 0x15, 0x0A, 0x13]; + const receiverHeader = [0x0A, receiverGlobal.length + 2, 0x0A, receiverGlobal.length]; const receiverProto = stringToHexArray(receiverGlobal); const contentProto = stringToHexArray(contentGlobal); const contentHeader = [0x12, ...toVarint(contentProto.length)]; @@ -418,21 +432,20 @@ function attachProto() { const suffix = [ 0x32, 0x32, 0x3C, // 0x28 头部 - 0x6D, 0x73, 0x67, 0x73, 0x6F, 0x73, 0x75, 0x72, // 0x30 msgsour + 0x6D, 0x73, 0x67, 0x73, 0x6F, 0x75, 0x72, // 0x30 msgsour 0x63, 0x65, 0x3E, 0x3C, 0x61, 0x6C, 0x6E, 0x6F, // 0x38 ce>1 0x3C, 0x2F, 0x66, 0x72, 0x3E, 0x3C, 0x2F, 0x61, // 0x48 . ]; const valueLen = toVarint(receiverHeader.length + receiverProto.length + contentHeader.length + - contentProto.length + tsHeader.length + tsBytes.length + msgIdHeader.length + msgId.length + suffix.length) + contentProto.length + tsHeader.length + tsBytes.length + msgIdHeader.length + msgId.length + suffix.length) // 合并数组 - const finalPayload = type.concat(valueLen).concat(receiverHeader).concat(receiverProto).concat(contentHeader). - concat(contentProto).concat(tsHeader).concat(tsBytes).concat(msgIdHeader).concat(msgId).concat(suffix); + const finalPayload = type.concat(valueLen).concat(receiverHeader).concat(receiverProto).concat(contentHeader).concat(contentProto).concat(tsHeader).concat(tsBytes).concat(msgIdHeader).concat(msgId).concat(suffix); console.log("[+] Payload 准备写入"); protoX1PayloadAddr.writeByteArray(finalPayload);