This commit is contained in:
yincong
2025-12-31 17:27:26 +08:00
parent 999c89f785
commit 2745a1e8d8
2 changed files with 99 additions and 39 deletions
+78 -31
View File
@@ -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))
}
+21 -8
View File
@@ -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><alno
0x64, 0x65, 0x3E, 0x3C, 0x66, 0x72, 0x3E, 0x31, // 0x40 de><fr>1
0x3C, 0x2F, 0x66, 0x72, 0x3E, 0x3C, 0x2F, 0x61, // 0x48 </fr></a
0x6C, 0x6E, 0x6F, 0x64, 0x65, 0x3E, 0x3C, 0x2F, // 0x50 lnode></
0x6D, 0x73, 0x67, 0x73, 0x6F, 0x73, 0x75, 0x72, // 0x58 msgsour
0x6D, 0x73, 0x67, 0x73, 0x6F, 0x75, 0x72, // 0x58 msgsour
0x63, 0x65, 0x3E, 0x00 // 0x60 ce>.
];
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);