mirror of
https://github.com/yincongcyincong/wechat_chatter.git
synced 2026-07-15 10:26:52 +08:00
163 lines
6.4 KiB
Go
163 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"math/rand"
|
|
"time"
|
|
|
|
wxproto "github.com/yincongcyincong/weixin-macos/onebot/proto"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// BuildTextMsgProto 构建发送文本消息的protobuf并返回hex编码的字符串
|
|
func BuildTextMsgProto(receiver, content, atUser string) (string, error) {
|
|
// 构建 msgsource XML
|
|
xmlStr := "<msgsource>"
|
|
if atUser != "" {
|
|
xmlStr += "<atuserlist>" + atUser + "</atuserlist>"
|
|
}
|
|
xmlStr += "<alnode><fr>1</fr></alnode></msgsource>"
|
|
|
|
// 生成随机消息ID
|
|
msgId := rand.Int63n(1<<34) | (1 << 34)
|
|
|
|
msg := &wxproto.WxSendTextMsg{
|
|
Type: 1,
|
|
Body: &wxproto.WxSendTextBody{
|
|
Receiver: &wxproto.WxString{Value: receiver},
|
|
Content: []byte(content),
|
|
Flag: 1,
|
|
Timestamp: time.Now().Unix(),
|
|
MsgId: msgId,
|
|
Xml: []byte(xmlStr),
|
|
},
|
|
}
|
|
|
|
data, err := proto.Marshal(msg)
|
|
if err != nil {
|
|
return "", fmt.Errorf("marshal text proto failed: %w", err)
|
|
}
|
|
|
|
//fmt.Printf("[text-proto] final protobuf hex dump:\n%s\n", HexDump(data, 0))
|
|
|
|
return hex.EncodeToString(data), nil
|
|
}
|
|
|
|
// BuildSendPayload 构建MMStartTask的完整payload (0x1A0字节),返回hex编码字符串
|
|
// msgType: "text", "img", "video"
|
|
// 布局: [taskId(4字节)] + [payloadData(412字节)],总长固定0x1A0字节
|
|
// JS中的writePointer使用绝对偏移(0x18, 0xb8, 0x190)进行patch
|
|
func BuildSendPayload(taskId int64, msgType string) string {
|
|
// 公共模板 (412字节,与JS原始img/video payloadData完全一致)
|
|
// 差异字节通过下方switch设置: [0],[1],[16],[28],[92],[93]
|
|
payloadData := []byte{
|
|
0x00, 0x00, 0x00, 0x00, // [0]-[3] 由msgType决定 abs 0x04
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x08
|
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // [16] abs 0x10
|
|
0x40, 0xEC, 0x0E, 0x12, 0x01, 0x00, 0x00, 0x00, // abs 0x18 (被cgiAddr覆盖)
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // [28] abs 0x20
|
|
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // abs 0x28
|
|
0x00, 0x01, 0x01, 0x01, 0x00, 0xAA, 0xAA, 0xAA, // abs 0x30
|
|
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // abs 0x38
|
|
0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // abs 0x40
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xAA, 0xAA, 0xAA, // abs 0x48
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0xAA, 0xAA, 0xAA, // abs 0x50
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x58
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // [92] abs 0x60
|
|
0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x2D, // abs 0x68 "default-"
|
|
0x6C, 0x6F, 0x6E, 0x67, 0x6C, 0x69, 0x6E, 0x6B, // abs 0x70 "longlink"
|
|
0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x10, // abs 0x78
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x80
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x88
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x90
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x98
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0xA0
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0xA8
|
|
0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // abs 0xB0
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0xB8 (被指针patch覆盖)
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0xC0
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0xC8
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0xD0
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0xD8
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0xE0
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0xE8
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0xF0
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0xF8
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x100
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x108
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x110
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x118
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x120
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x128
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x130
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x138
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x140
|
|
0x01, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // abs 0x148
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x150
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x158
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x160
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x168
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x170
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x178
|
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x180
|
|
0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // abs 0x188
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x190 (被指针patch覆盖)
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // abs 0x198
|
|
}
|
|
|
|
// 根据msgType设置差异字节
|
|
switch msgType {
|
|
case "text":
|
|
payloadData[0] = 0x0A
|
|
payloadData[1] = 0x02
|
|
payloadData[16] = 0x01
|
|
payloadData[28] = 0x20
|
|
payloadData[92] = 0x0A
|
|
payloadData[93] = 0x02
|
|
case "img":
|
|
payloadData[0] = 0x6E
|
|
payloadData[16] = 0x10
|
|
payloadData[28] = 0x22
|
|
payloadData[92] = 0x6E
|
|
case "video":
|
|
payloadData[0] = 0x6E
|
|
payloadData[16] = 0x10
|
|
payloadData[28] = 0x21
|
|
payloadData[92] = 0x6E
|
|
case "reply":
|
|
payloadData[0] = 0x6E
|
|
payloadData[16] = 0x10
|
|
payloadData[28] = 0x20
|
|
payloadData[92] = 0x6E
|
|
case "file":
|
|
payloadData[0] = 0x6E
|
|
payloadData[16] = 0x10
|
|
payloadData[28] = 0x20
|
|
payloadData[92] = 0x6E
|
|
case "fileupload":
|
|
payloadData[0] = 0x6E
|
|
payloadData[16] = 0x10
|
|
payloadData[28] = 0x27
|
|
payloadData[92] = 0x6E
|
|
case "appattach":
|
|
payloadData[0] = 0x6E
|
|
payloadData[16] = 0x10
|
|
payloadData[28] = 0x25
|
|
payloadData[92] = 0x6E
|
|
case "voice":
|
|
payloadData[0] = 0x6E
|
|
payloadData[16] = 0x10
|
|
payloadData[28] = 0x21
|
|
payloadData[92] = 0x6E
|
|
}
|
|
|
|
// 构建完整buffer: taskId(4字节) + payloadData(412字节) = 0x1A0字节
|
|
payload := make([]byte, 0x1A0)
|
|
binary.LittleEndian.PutUint32(payload[0:4], uint32(taskId))
|
|
copy(payload[4:], payloadData)
|
|
|
|
return hex.EncodeToString(payload)
|
|
}
|