mirror of
https://github.com/yincongcyincong/wechat_chatter.git
synced 2026-07-15 10:26:52 +08:00
add at info
This commit is contained in:
+33
-14
@@ -30,6 +30,7 @@ function setReceiver() {
|
||||
const receiver = fields[1]
|
||||
const content = fields[2]
|
||||
const xml = fields[3]
|
||||
const userContent = fields[4]
|
||||
|
||||
if (sender === "" || receiver === "" || content === "" || xml === "") {
|
||||
console.log("字段缺失,无法解析 sender:" + sender + " receiver:" + receiver + hexdump(currentPtr, {
|
||||
@@ -45,23 +46,17 @@ function setReceiver() {
|
||||
var groupId = ""
|
||||
var senderUser = sender
|
||||
var messages = [];
|
||||
messages.push({type: "text", data: {text: content}});
|
||||
var senderNickname = ""
|
||||
|
||||
let splitIndex = content.indexOf(':')
|
||||
let pureContent = content.substring(splitIndex + 1).trim();
|
||||
|
||||
if (sender.includes("@chatroom")) {
|
||||
msgType = "group"
|
||||
groupId = sender
|
||||
let splitIndex = -1;
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
if (content[i] === ':') {
|
||||
splitIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
senderUser = content.substring(0, splitIndex).trim();
|
||||
|
||||
messages = [];
|
||||
const parts = content.split('\u2005');
|
||||
const parts = pureContent.split('\u2005');
|
||||
for (let part of parts) {
|
||||
part = part.trim();
|
||||
if (!part.startsWith("@")) {
|
||||
@@ -69,11 +64,34 @@ function setReceiver() {
|
||||
}
|
||||
}
|
||||
|
||||
const sendUserStart = content.indexOf('wxid_')
|
||||
senderUser = content.substring(sendUserStart, splitIndex).trim();
|
||||
|
||||
const atUserMatch = xml.match(/<atuserlist>([\s\S]*?)<\/atuserlist>/);
|
||||
const atUser = atUserMatch ? atUserMatch[1] : null;
|
||||
if (atUser) {
|
||||
messages.push({type: "at", data: {qq: atUser}});
|
||||
atUser.split(',').forEach(atUser => {
|
||||
atUser = atUser.trim();
|
||||
if (atUser) {
|
||||
messages.push({type: "at", data: {qq: atUser}});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理用户的名称
|
||||
splitIndex = userContent.indexOf(':')
|
||||
if (splitIndex === -1) {
|
||||
splitIndex = userContent.indexOf('在群聊中@了你')
|
||||
senderNickname = userContent.substring(0, splitIndex).trim();
|
||||
} else {
|
||||
senderNickname = userContent.substring(0, splitIndex).trim();
|
||||
}
|
||||
|
||||
} else {
|
||||
// 处理用户的名称
|
||||
const splitIndex = userContent.indexOf(':')
|
||||
senderNickname = userContent.substring(0, splitIndex).trim();
|
||||
messages.push({type: "text", data: {text: pureContent}});
|
||||
}
|
||||
|
||||
const msgId = generateAESKey()
|
||||
@@ -85,7 +103,8 @@ function setReceiver() {
|
||||
message_id: msgId,
|
||||
type: "send",
|
||||
raw: {peerUid: msgId},
|
||||
message: messages
|
||||
message: messages,
|
||||
sender: {user_id: senderUser, nickname: senderNickname},
|
||||
})
|
||||
},
|
||||
});
|
||||
@@ -105,7 +124,7 @@ function generateAESKey() {
|
||||
|
||||
|
||||
function getProtobufRawBytes(pBuffer, scanSize) {
|
||||
const tags = [0x12, 0x1A, 0x2A, 0x52];
|
||||
const tags = [0x12, 0x1A, 0x2A, 0x52, 0x5A];
|
||||
let uint8Array;
|
||||
|
||||
try {
|
||||
|
||||
+41
-10
@@ -19,6 +19,7 @@ import (
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"text/template"
|
||||
@@ -38,12 +39,20 @@ var (
|
||||
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"`
|
||||
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 {
|
||||
@@ -51,6 +60,7 @@ type SendMsg struct {
|
||||
GroupID string
|
||||
Content string
|
||||
Type string
|
||||
AtUser string
|
||||
}
|
||||
|
||||
// SendRequest 请求结构体
|
||||
@@ -69,6 +79,7 @@ type SendRequestData struct {
|
||||
Id string `json:"id"`
|
||||
Text string `json:"text"`
|
||||
File string `json:"file"`
|
||||
QQ string `json:"qq"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@@ -251,14 +262,19 @@ func sendHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
sendContent := ""
|
||||
atUserID := ""
|
||||
for _, v := range req.Message {
|
||||
if v.Type == "text" {
|
||||
msgChan <- &SendMsg{
|
||||
UserId: req.UserID,
|
||||
GroupID: req.GroupID,
|
||||
Content: v.Data.Text,
|
||||
Type: v.Type,
|
||||
sendContent += v.Data.Text
|
||||
} else if v.Type == "at" {
|
||||
if req.GroupID != "" {
|
||||
if nicknameInter, ok := userID2NicknameMap.Load(req.GroupID + "_" + v.Data.QQ); ok {
|
||||
sendContent += fmt.Sprintf("@%s\u2005", nicknameInter.(string))
|
||||
atUserID += v.Data.QQ + ","
|
||||
}
|
||||
}
|
||||
|
||||
} else if v.Type == "image" {
|
||||
msgChan <- &SendMsg{
|
||||
UserId: req.UserID,
|
||||
@@ -269,6 +285,16 @@ func sendHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
if sendContent != "" {
|
||||
msgChan <- &SendMsg{
|
||||
UserId: req.UserID,
|
||||
GroupID: req.GroupID,
|
||||
Content: sendContent,
|
||||
Type: "text",
|
||||
AtUser: strings.TrimRight(atUserID, ","),
|
||||
}
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(map[string]any{
|
||||
"status": "ok",
|
||||
})
|
||||
@@ -310,8 +336,9 @@ func SendWechatMsg(m *SendMsg) {
|
||||
|
||||
switch m.Type {
|
||||
case "text":
|
||||
result := fridaScript.ExportsCall("triggerSendTextMessage", currTaskId, targetId, m.Content)
|
||||
log.Printf("📩 发送文本任务执行结果:%s\n", result)
|
||||
result := fridaScript.ExportsCall("triggerSendTextMessage", currTaskId, targetId, m.Content, m.AtUser)
|
||||
log.Printf("📩 发送文本任务执行结果:%s, 参数:currTaskId: %d, targetId: %s, content: %s, atUser: %s\n",
|
||||
result, currTaskId, targetId, m.Content, m.AtUser)
|
||||
case "image":
|
||||
targetPath, md5Str, err := SaveBase64Image(m.Content)
|
||||
if err != nil {
|
||||
@@ -386,6 +413,10 @@ func SendHttpReq(msg map[string]interface{}) {
|
||||
return
|
||||
}
|
||||
myWechatId = m.SelfID
|
||||
|
||||
if m.GroupId != "" {
|
||||
userID2NicknameMap.Store(m.GroupId+"_"+m.UserID, m.Sender.Nickname)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 创建 POST 请求
|
||||
|
||||
+60
-46
@@ -83,7 +83,7 @@ function generateAESKey() {
|
||||
}
|
||||
|
||||
function getProtobufRawBytes(pBuffer, scanSize) {
|
||||
const tags = [0x12, 0x1A, 0x2A, 0x52];
|
||||
const tags = [0x12, 0x1A, 0x2A, 0x52, 0x5A];
|
||||
let uint8Array;
|
||||
|
||||
try {
|
||||
@@ -176,8 +176,7 @@ function getCleanString(uint8Array) {
|
||||
out += String.fromCharCode(charCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((c & 0xF8) === 0xF0 && i + 2 < len) {
|
||||
} else if ((c & 0xF8) === 0xF0 && i + 2 < len) {
|
||||
var c2 = uint8Array[i++];
|
||||
var c3 = uint8Array[i++];
|
||||
var c4 = uint8Array[i++];
|
||||
@@ -265,6 +264,8 @@ var receiverGlobal = "wxid_"
|
||||
var contentGlobal = "";
|
||||
var senderGlobal = "wxid_"
|
||||
var lastSendTime = 0;
|
||||
var atUserGlobal = "";
|
||||
|
||||
const imageCp = generateBytes(16) // m30c4674f5a0b9d
|
||||
|
||||
// -------------------------全局变量分区-------------------------
|
||||
@@ -343,7 +344,7 @@ setTimeout(function () {
|
||||
patchTextProtoBuf();
|
||||
}, 3000);
|
||||
|
||||
function triggerSendTextMessage(taskId, receiver, content) {
|
||||
function triggerSendTextMessage(taskId, receiver, content, atUser) {
|
||||
console.log("[+] Manual Trigger Started...");
|
||||
if (!taskId || !receiver || !content) {
|
||||
console.error("[!] taskId or Receiver or Content is empty!");
|
||||
@@ -356,12 +357,12 @@ function triggerSendTextMessage(taskId, receiver, content) {
|
||||
taskIdGlobal = taskId;
|
||||
receiverGlobal = receiver;
|
||||
contentGlobal = content;
|
||||
atUserGlobal = atUser
|
||||
console.log("taskIdGlobal: " + taskIdGlobal + ", receiverGlobal: " + receiverGlobal + ", contentGlobal: " + contentGlobal + ", atUserGlobal: " + atUserGlobal) ;
|
||||
|
||||
textMessageAddr.add(0x08).writeU32(taskIdGlobal);
|
||||
sendTextMessageAddr.add(0x20).writeU32(taskIdGlobal);
|
||||
|
||||
console.log("start init payload")
|
||||
|
||||
const payloadData = [
|
||||
0x0A, 0x02, 0x00, 0x00, // 0x00
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08
|
||||
@@ -428,9 +429,8 @@ function triggerSendTextMessage(taskId, receiver, content) {
|
||||
try {
|
||||
// const arg1 = globalMessagePtr; // 第一个指针参数
|
||||
const arg2 = triggerX1Payload; // 第二个参数 0x175ED6600
|
||||
console.log(`[+] Calling MMStartTask at ${sendFuncAddr} with args: (${arg2})`);
|
||||
const result = MMStartTask(arg2);
|
||||
console.log("[+] Execution MMStartTask Success. Return value: " + result);
|
||||
console.log(`[+] Execution MMStartTask ${sendFuncAddr} with args: (${arg2}) Success. Return value: ` + result);
|
||||
return "ok";
|
||||
} catch (e) {
|
||||
console.error("[!] Error trigger function during execution: " + e);
|
||||
@@ -454,7 +454,7 @@ function attachSendTextProto() {
|
||||
console.log("[+] Protobuf 拦截未命中,跳过...");
|
||||
return;
|
||||
}
|
||||
console.log("[+] 正在注入 Protobuf Payload...");
|
||||
console.log(`[+] 正在注入 Protobuf Payload content: ${contentGlobal}, receiver: ${receiverGlobal}, atUser: ${atUserGlobal}`);
|
||||
|
||||
const type = [0x08, 0x01, 0x12]
|
||||
const receiverHeader = [0x0A, receiverGlobal.length + 2, 0x0A, receiverGlobal.length];
|
||||
@@ -466,27 +466,32 @@ function attachSendTextProto() {
|
||||
const msgIdHeader = [0x28]
|
||||
const msgId = generateRandom5ByteVarint()
|
||||
|
||||
const suffix = [
|
||||
0x32, 0x32, 0x3C, // 0x28 头部
|
||||
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, 0x75, 0x72, // 0x58 msgsour
|
||||
0x63, 0x65, 0x3E, 0x00 // 0x60 ce>.
|
||||
];
|
||||
const htmlUpperPart = [0x3C, 0x6D, 0x73, 0x67, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x3E]
|
||||
let atUserHeader = []
|
||||
if (atUserGlobal) {
|
||||
atUserHeader = atUserHeader.concat([0x3C, 0x61, 0x74, 0x75, 0x73, 0x65, 0x72, 0x6c, 0x69, 0x73, 0x74, 0x3e]).
|
||||
concat(stringToHexArray(atUserGlobal)).concat([0x3C, 0x2F, 0x61, 0x74, 0x75, 0x73, 0x65, 0x72, 0x6C, 0x69, 0x73, 0x74, 0x3E])
|
||||
}
|
||||
const htmlLowerPart = [0x3C, 0x61, 0x6C, 0x6E, 0x6F,
|
||||
0x64, 0x65, 0x3E, 0x3C, 0x66, 0x72, 0x3E, 0x31,
|
||||
0x3C, 0x2F, 0x66, 0x72, 0x3E, 0x3C, 0x2F, 0x61,
|
||||
0x6C, 0x6E, 0x6F, 0x64, 0x65, 0x3E, 0x3C, 0x2F,
|
||||
0x6D, 0x73, 0x67, 0x73, 0x6F, 0x75, 0x72,
|
||||
0x63, 0x65, 0x3E, 0x00]
|
||||
|
||||
const htmlHeader = [0x32, htmlUpperPart.length + atUserHeader.length + htmlLowerPart.length]
|
||||
|
||||
|
||||
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 + htmlHeader.length +
|
||||
htmlUpperPart.length + atUserHeader.length + htmlLowerPart.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(htmlHeader).concat(htmlUpperPart).
|
||||
concat(atUserHeader).concat(htmlLowerPart);
|
||||
|
||||
console.log("[+] Payload 准备写入");
|
||||
textProtoX1PayloadAddr.writeByteArray(finalPayload);
|
||||
console.log("[+] Payload 已写入,长度: " + finalPayload.length);
|
||||
|
||||
this.context.x1 = textProtoX1PayloadAddr;
|
||||
this.context.x2 = ptr(finalPayload.length);
|
||||
|
||||
@@ -554,6 +559,7 @@ function attachReq2buf() {
|
||||
receiverGlobal = "";
|
||||
senderGlobal = "";
|
||||
contentGlobal = "";
|
||||
atUserGlobal = "";
|
||||
send({
|
||||
type: "finish",
|
||||
})
|
||||
@@ -1053,10 +1059,11 @@ function setReceiver() {
|
||||
const receiver = fields[1]
|
||||
const content = fields[2]
|
||||
const xml = fields[3]
|
||||
const userContent = fields[4]
|
||||
|
||||
if (sender === "" || receiver === "" || content === "" || xml === "") {
|
||||
console.log("字段缺失,无法解析 sender:" + sender + " receiver:" + receiver + hexdump(currentPtr, {
|
||||
length: 128,
|
||||
length: x2,
|
||||
header: true,
|
||||
ansi: true,
|
||||
}))
|
||||
@@ -1068,36 +1075,27 @@ function setReceiver() {
|
||||
var groupId = ""
|
||||
var senderUser = sender
|
||||
var messages = [];
|
||||
messages.push({type: "text", data: {text: content}});
|
||||
var senderNickname = ""
|
||||
|
||||
let splitIndex = content.indexOf(':')
|
||||
let pureContent = content.substring(splitIndex + 1).trim();
|
||||
|
||||
if (sender.includes("@chatroom")) {
|
||||
msgType = "group"
|
||||
groupId = sender
|
||||
let splitIndex = -1;
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
if (content[i] === ':') {
|
||||
splitIndex = i;
|
||||
break;
|
||||
|
||||
senderUser = content.substring(0, splitIndex).trim();
|
||||
const parts = pureContent.split('\u2005');
|
||||
for (let part of parts) {
|
||||
part = part.trim();
|
||||
if (!part.startsWith("@")) {
|
||||
messages.push({type: "text", data: {text: part}});
|
||||
}
|
||||
}
|
||||
|
||||
const sendUserStart = content.indexOf('wxid_')
|
||||
senderUser = content.substring(sendUserStart, splitIndex).trim();
|
||||
|
||||
messages = [];
|
||||
const parts = content.split('\u2005');
|
||||
for (let part of parts) {
|
||||
part = part.trim();
|
||||
if (!part.startsWith("@")) {
|
||||
if (part.indexOf(":") !== -1) {
|
||||
messages.push({type: "text", data: {text: part.substring(part.indexOf(':') + 1)}});
|
||||
} else {
|
||||
messages.push({type: "text", data: {text: part}});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const atUserMatch = xml.match(/<atuserlist>([\s\S]*?)<\/atuserlist>/);
|
||||
const atUser = atUserMatch ? atUserMatch[1] : null;
|
||||
if (atUser) {
|
||||
@@ -1108,6 +1106,21 @@ function setReceiver() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理用户的名称
|
||||
splitIndex = userContent.indexOf(':')
|
||||
if (splitIndex === -1) {
|
||||
splitIndex = userContent.indexOf('在群聊中@了你')
|
||||
senderNickname = userContent.substring(0, splitIndex).trim();
|
||||
} else {
|
||||
senderNickname = userContent.substring(0, splitIndex).trim();
|
||||
}
|
||||
|
||||
} else {
|
||||
// 处理用户的名称
|
||||
const splitIndex = userContent.indexOf(':')
|
||||
senderNickname = userContent.substring(0, splitIndex).trim();
|
||||
messages.push({type: "text", data: {text: pureContent}});
|
||||
}
|
||||
|
||||
const msgId = generateAESKey()
|
||||
@@ -1119,7 +1132,8 @@ function setReceiver() {
|
||||
message_id: msgId,
|
||||
type: "send",
|
||||
raw: {peerUid: msgId},
|
||||
message: messages
|
||||
message: messages,
|
||||
sender: {user_id: senderUser, nickname: senderNickname},
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user