// -------------------------Reply消息全局变量分区-------------------------
// 模块信息 - 需要与file.js保持一致
var moduleName = "wechat.dylib";
var baseAddr = Process.findModuleByName(moduleName).base;
// 全局变量
var sendFuncAddr = baseAddr.add(0x4992040);
var req2bufEnterAddr = baseAddr.add(0x380b950);
var req2bufExitAddr = baseAddr.add(0x380CA64);
var taskIdGlobal = 0x0;
var lastSendTime = 0;
var receiverGlobal = "wxid_";
var senderGlobal = "wxid_";
var sendMsgType = "";
var triggerX0;
var triggerX1Payload;
var insertMsgAddr;
// 回复消息回调函数地址 (基于用户提供: 0x24BDE40)
var replyCallbackFuncAddr = baseAddr.add(0x260C444);
var replyProtobufAddr = replyCallbackFuncAddr.add(0x50);
var patchReplyProtobufFunc1 = replyCallbackFuncAddr.add(0x10);
var patchReplyProtobufFunc1Byte;
var patchReplyProtobufFunc2 = replyCallbackFuncAddr.add(0x30);
var patchReplyProtobufFunc2Byte;
var replyProtobufDeleteAddr = replyCallbackFuncAddr.add(0x6c);
var replyProtobufDeleteAddrByte;
var replyMessageCallbackFunc1 = baseAddr.add(0x8C29A08); // 复用图片的callback函数
// Reply消息相关地址 - 需要导出为全局变量供file.js使用
var sendReplyMessageAddr = ptr(0);
var replyMessageAddr = ptr(0);
var replyCgiAddr = ptr(0);
var replyProtobufBufAddr = ptr(0); // 用于存储protobuf数据的缓冲区
// Reply消息内容相关
var replyContent = ""; // 回复的文本内容
var replyReferMsgContent = ""; // 被引用消息的内容
var replyReferMsgType = 1; // 被引用消息的类型
var replyReferMsgSender = ""; // 被引用消息的发送者
var replyReferMsgId = ""; // 被引用消息的ID
var replyReferMsgCreateTime = ""; // 被引用消息的创建时间
var replyChatRoom = ""; // 是否是群聊
var replyFromUser = ""; // 回复者wxid
// -------------------------Reply消息全局变量分区-------------------------
// -------------------------发送Reply消息动态初始化-------------------------
function setupSendReplyMessageDynamic() {
console.log("[+] Starting setupSendReplyMessageDynamic Dynamic Message Patching...");
// 1. 动态分配内存块
sendReplyMessageAddr = Memory.alloc(256);
replyMessageAddr = Memory.alloc(256);
replyCgiAddr = Memory.alloc(128);
replyProtobufBufAddr = Memory.alloc(3069);
// 写入reply的cgi路径
patchString(replyCgiAddr, "/cgi-bin/micromsg-bin/sendappmsg");
// 初始化sendReplyMessageAddr结构
sendReplyMessageAddr.add(0x00).writeU64(0);
sendReplyMessageAddr.add(0x08).writeU64(0);
sendReplyMessageAddr.add(0x10).writeU64(0);
sendReplyMessageAddr.add(0x18).writeU64(1);
sendReplyMessageAddr.add(0x20).writeU32(taskIdGlobal);
sendReplyMessageAddr.add(0x28).writePointer(replyMessageAddr);
// 初始化replyMessageAddr结构
replyMessageAddr.add(0x00).writePointer(replyMessageCallbackFunc1);
replyMessageAddr.add(0x08).writeU32(taskIdGlobal);
replyMessageAddr.add(0x0c).writeU32(0x6e);
replyMessageAddr.add(0x10).writeU64(0x3);
replyMessageAddr.add(0x18).writePointer(replyCgiAddr); // 添加cgi地址
replyMessageAddr.add(0x20).writeU64(0x22);
replyMessageAddr.add(0x28).writeU64(uint64("0x8000000000000030"));
replyMessageAddr.add(0x30).writeU64(uint64("0x0000000001010100"));
// 保存原始字节用于恢复
patchReplyProtobufFunc1Byte = patchReplyProtobufFunc1.readByteArray(4);
patchReplyProtobufFunc2Byte = patchReplyProtobufFunc2.readByteArray(4);
replyProtobufDeleteAddrByte = replyProtobufDeleteAddr.readByteArray(4);
}
setImmediate(setupSendReplyMessageDynamic);
// -------------------------Patch Reply protobuf-------------------------
function patchReplyProtoBuf() {
// attach到replyProtobufAddr (replyCallbackFuncAddr + 0x50) 来修改protobuf
Interceptor.attach(replyCallbackFuncAddr, {
onEnter: function (args) {
var firstValue = this.context.sp.add(0x10).readU32();
console.log("[+] 捕获到 ReplyProtobufAddr 调用,firstValue:", firstValue, "taskIdGlobal:", taskIdGlobal);
if (firstValue === taskIdGlobal) {
// 不匹配时,patch掉这些函数(NOP替换)
console.log("[+] Reply taskId匹配,开始patch...");
if (patchReplyProtobufFunc1.readU32() !== 3573751839) {
Memory.patchCode(patchReplyProtobufFunc1, 4, code => {
const cw = new Arm64Writer(code, {pc: patchReplyProtobufFunc1});
cw.putNop();
cw.flush();
});
Memory.patchCode(patchReplyProtobufFunc2, 4, code => {
const cw = new Arm64Writer(code, {pc: patchReplyProtobufFunc2});
cw.putNop();
cw.flush();
});
Memory.patchCode(replyProtobufDeleteAddr, 4, code => {
const cw = new Arm64Writer(code, {pc: replyProtobufDeleteAddr});
cw.putNop();
cw.flush();
});
}
} else {
// 匹配时,恢复原来的字节
console.log("[+] Reply taskId不匹配,开始修改protobuf...");
if (patchReplyProtobufFunc1.readU32() === 3573751839) {
Memory.patchCode(patchReplyProtobufFunc1, 4, code => {
const cw = new Arm64Writer(code, {pc: patchReplyProtobufFunc1});
cw.putBytes(new Uint8Array(patchReplyProtobufFunc1Byte));
cw.flush();
});
Memory.patchCode(patchReplyProtobufFunc2, 4, code => {
const cw = new Arm64Writer(code, {pc: patchReplyProtobufFunc2});
cw.putBytes(new Uint8Array(patchReplyProtobufFunc2Byte));
cw.flush();
});
Memory.patchCode(replyProtobufDeleteAddr, 4, code => {
const cw = new Arm64Writer(code, {pc: replyProtobufDeleteAddr});
cw.putBytes(new Uint8Array(replyProtobufDeleteAddrByte));
cw.flush();
});
}
}
}
});
}
setImmediate(patchReplyProtoBuf);
function attachProto() {
Interceptor.attach(replyProtobufAddr, {
onEnter: function (args) {
var currTaskId = this.context.sp.add(0x30).readU32();
if (currTaskId !== taskIdGlobal) {
console.log(`[+] 拦截到非目标 currTaskId: ${currTaskId} taskIdGlobal: ${taskIdGlobal}` + hexdump(this.context.x1, {
offset: 0,
length: 3069,
header: true,
ansi: true
}));
return;
}
const replyProtobuf = buildReplyProtobuf();
// 写入protobuf数据到缓冲区
replyProtobufBufAddr.writeByteArray(replyProtobuf);
console.log("[+] Reply protobuf写入到缓冲区: ", replyProtobufBufAddr);
// 设置x1指向缓冲区地址,x2为长度
this.context.x1 = replyProtobufBufAddr;
this.context.x2 = ptr(replyProtobuf.length);
console.log("[+] Reply寄存器修改完成: X1=" + this.context.x1 + ", X2=" + this.context.x2, hexdump(replyProtobufBufAddr, {
offset: 0,
length: 3069,
header: true,
ansi: true
}));
}
});
}
setImmediate(attachProto);
// -------------------------触发发送Reply消息-------------------------
function triggerSendReplyMessage(taskId, sender, receiver, content, referMsgContent, referMsgType, referMsgSender, referMsgId, chatRoom, referMsgCreateTime) {
console.log("[+] Manual Trigger Reply Message Started...");
if (!taskId || !receiver || !sender) {
console.error("[!] taskId or receiver or sender is empty!");
return "fail";
}
// 更新全局变量
const timestamp = Math.floor(Date.now() / 1000);
lastSendTime = timestamp;
taskIdGlobal = taskId;
receiverGlobal = receiver;
senderGlobal = sender;
replyContent = content || "";
replyReferMsgContent = referMsgContent || "";
replyReferMsgType = referMsgType || 1;
replyReferMsgSender = referMsgSender || sender;
replyReferMsgId = referMsgId || generateRandomMsgId();
replyReferMsgCreateTime = referMsgCreateTime || Math.floor(Date.now() / 1000).toString();
replyChatRoom = chatRoom || "";
replyFromUser = sender;
// 更新消息地址的taskId
replyMessageAddr.add(0x08).writeU32(taskIdGlobal);
sendReplyMessageAddr.add(0x20).writeU32(taskIdGlobal);
console.log("start init reply payload");
// 使用与triggerSendImgMessage类似的payload结构
const payloadData = [
0x6e, 0x00, 0x00, 0x00, // 0x00
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08
0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // 0x10
0x40, 0xec, 0x0e, 0x12, 0x01, 0x00, 0x00, 0x00, // 0x18
0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 cgi的长度
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x28
0x00, 0x01, 0x01, 0x01, 0x00, 0xAA, 0xAA, 0xAA, // 0x30
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // 0x38
0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // 0x40
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xAA, 0xAA, 0xAA, // 0x48
0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0xAA, 0xAA, 0xAA, // 0x50
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x58
0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x60
0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x2D, // 0x68 default-
0x6C, 0x6F, 0x6E, 0x67, 0x6C, 0x69, 0x6E, 0x6B, // 0x70 longlink
0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x10, // 0x78
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x98
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA8
0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // 0xB0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xF0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xF8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x100
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x108
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x110
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x118
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x120
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x128
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x130
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x138
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x140
0x01, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // 0x148
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x150
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x158
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x160
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x168
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x170
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x178
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x180
0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // 0x188
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x190
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x198
];
triggerX1Payload.writeU32(taskIdGlobal);
triggerX1Payload.add(0x04).writeByteArray(payloadData);
triggerX1Payload.add(0x18).writePointer(replyCgiAddr); // 使用reply的cgi
triggerX1Payload.add(0xb8).writePointer(triggerX1Payload.add(0xc0));
triggerX1Payload.add(0x190).writePointer(triggerX1Payload.add(0x198));
sendMsgType = "reply";
console.log("finished init reply payload");
const MMStartTask = new NativeFunction(sendFuncAddr, 'int64', ['pointer', 'pointer']);
try {
const result = MMStartTask(triggerX0, triggerX1Payload);
console.log(`[+] Execution StartTask ${sendFuncAddr} with args: (${triggerX0}) (${triggerX1Payload}) Success. Return value: ` + result);
return "ok";
} catch (e) {
console.error(`[!] Error trigger StartTask ${sendFuncAddr} with args: (${triggerX0}) (${triggerX1Payload}), during execution: ` + e);
return "fail";
}
}
// -------------------------构建Reply消息Protobuf-------------------------
function buildReplyProtobuf() {
// 构建appmsg的XML内容
const appmsgXml = buildReplyAppmsgXml();
// 根据用户提供的protobuf格式构建
const payload = [];
// 先构建内部数据
const innerData = [];
// 字段1: 0x0A 0x01 0x00 (固定)
innerData.push(0x0A, 0x01, 0x00);
// 字段2: 0x10 + varint (时间戳)
innerData.push(0x10);
const timestampVarint = encodeVarint(Math.floor(Date.now() / 1000));
innerData.push(...timestampVarint);
// 字段3: 0x1A 0x10 + md5 (16字节)
innerData.push(0x1A, 0x10);
const md5Bytes = stringToUtf8Bytes(generateRandomMd5());
innerData.push(...md5Bytes);
// 字段4: 0x20 + varint (大整数 0x1FF... )
innerData.push(0x20, 0x9D, 0xB0, 0x90, 0x93, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01);
// 字段5: 0x2A 0x15 + "UnifiedPCMac 26 arm64"
innerData.push(0x2A, 0x15);
innerData.push(...stringToUtf8Bytes("UnifiedPCMac 26 arm64"));
const fromUsrBytes = stringToUtf8Bytes(senderGlobal);
innerData.push(0x30, 0xa8, 0x01, 0x12, 0xb9);
innerData.push(...encodeVarint(fromUsrBytes.length + 2));
innerData.push(0x0a)
innerData.push(...encodeVarint(fromUsrBytes.length));
innerData.push(...fromUsrBytes);
innerData.push(0x12, 0x00, 0x18, 0x00);
const receiverBytes = stringToUtf8Bytes(receiverGlobal)
innerData.push(0x22);
innerData.push(...encodeVarint(receiverBytes.length));
innerData.push(...receiverBytes);
// 结尾 varint
innerData.push(0x28, 0x39, 0x32);
const appmsgBytes = stringToUtf8Bytes(appmsgXml);
innerData.push(...encodeVarint(appmsgBytes.length))
innerData.push(...appmsgBytes)
innerData.push(0x38, 0xb2, 0xe4, 0x87, 0xce, 0x06, 0x42)
const receiverMsgId = receiverBytes.concat([0x5F])
.concat(stringToUtf8Bytes(Math.floor(Date.now() / 1000).toString()))
.concat([0x5F, 0x31, 0x36, 0x30, 0x5F, 0x78, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x5F, 0x33]);
innerData.push(...encodeVarint(receiverMsgId.length))
innerData.push(...receiverMsgId)
innerData.push(0x50, 0x01, 0x62, 0x32, 0x3c, 0x6d, 0x73, 0x67, 0x73, 0x6f, 0x75, 0x72,
0x63, 0x65, 0x3e, 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);
// 结尾字段 (根据成功版本)
// 字段12: 0x6A (field 6) + 0x00
innerData.push(0x6A, 0x00);
// 字段13: 0x72 (field 7) + 0x00
innerData.push(0x72, 0x00);
// 字段14: 0x7A (field 8) + 0x00
innerData.push(0x7A, 0x00);
// 字段15: 0x2A (field 9) + 0x00
innerData.push(0x2A, 0x00);
// 字段16: 0x48 (field 10) + 0x00
innerData.push(0x48, 0x00);
// 字段17: 0x50 (field 11) + 0x00
innerData.push(0x50, 0x00);
// 字段18: 0x58 (field 12) + 0x02 + 10个0
innerData.push(0x58);
innerData.push(0x02);
payload.push(0x0A, 0x40);
payload.push(...innerData);
return payload;
}
// 生成随机MD5格式字符串 (16字节)
function generateRandomMd5() {
let result = "md4";
for (let i = 0; i < 13; i++) {
result += Math.floor(Math.random() * 10).toString();
}
return result;
}
// 生成接收者消息ID
function generateReceiverMsgId() {
const timestamp = Math.floor(Date.now() / 1000);
const receiver = replyChatRoom || receiverGlobal;
return receiver + "_" + timestamp + "_" + Math.floor(Math.random() * 100) + "_xwechat_3";
}
// Varint编码
function encodeVarint(value) {
const result = [];
if (value === 0) {
result.push(0);
return result;
}
while (value > 0) {
let byte = value & 0x7F;
value >>= 7;
if (value > 0) {
byte |= 0x80;
}
result.push(byte);
}
return result;
}
// -------------------------构建Reply Appmsg XML-------------------------
function buildReplyAppmsgXml() {
// 构建appmsg XML内容
// type=57 是reply消息类型
let xml = "";
xml += "" + escapeXml(replyContent) + "";
xml += "";
xml += "";
xml += "57";
xml += "0";
xml += "0";
xml += "";
xml += "";
xml += "";
xml += "";
xml += "0";
xml += "";
xml += "";
xml += "";
xml += "";
xml += "";
xml += "";
xml += "";
xml += "0";
xml += "";
xml += "";
xml += "";
xml += "";
xml += "";
xml += "";
// 添加refermsg (引用消息)
if (replyReferMsgContent || replyReferMsgSender) {
xml += "";
if (replyReferMsgSender) {
xml += "" + escapeXml(replyReferMsgSender) + "";
}
xml += "" + replyReferMsgType + "";
xml += "" + replyReferMsgCreateTime + "";
xml += "" + escapeXml("1") + "";
xml += "";
xml += "" + escapeXml(replyReferMsgId || generateRandomMsgId()) + "";
xml += "" + escapeXml(replyReferMsgSender) + "";
xml += "" + escapeXml(replyReferMsgContent) + "";
xml += "";
}
xml += "";
// 添加fromusername
xml += "" + escapeXml(senderGlobal) + "";
return xml;
}
// 生成UUID
function generateUuid() {
return 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/x/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
}).replace(/-/g, '');
}
function AttachSendTextProto() {
Interceptor.attach(sendFuncAddr.add(0x08), {
onEnter: function (args) {
if (triggerX1Payload) {
return
}
triggerX0 = this.context.x0;
triggerX1Payload = this.context.x1;
console.log(`[+] 捕获到 StartTask 调用,X0地址:${triggerX0}, Payload 地址: ${triggerX1Payload}`);
}
})
}
setImmediate(AttachSendTextProto);
// -------------------------Reply消息Req2Buf拦截-------------------------
function attachReplyReq2buf() {
console.log("[+] Target Reply Req2buf enter Address: " + req2bufEnterAddr);
// 拦截入口
Interceptor.attach(req2bufEnterAddr, {
onEnter: function (args) {
if (!this.context.x1.equals(taskIdGlobal)) {
return;
}
console.log("[+] 已命中Reply目标Req2Buf地址 taskId:" + taskIdGlobal);
// 获取 X24 寄存器的值
const x24_base = this.context.x24;
insertMsgAddr = x24_base.add(0x60);
console.log("[+] 当前 Reply Req2Buf X24 基址: " + x24_base);
if (sendMsgType === "reply") {
insertMsgAddr.writePointer(sendReplyMessageAddr);
console.log("[+] 发送回复消息成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendReplyMessageAddr);
}
}
});
// 拦截出口
console.log("[+] Target Reply Req2buf leave Address: " + req2bufExitAddr);
Interceptor.attach(req2bufExitAddr, {
onEnter: function (args) {
if (!this.context.x25.equals(taskIdGlobal)) {
return;
}
insertMsgAddr.writeU64(0x0);
console.log("[+] Reply Req2Buf 清空完成");
taskIdGlobal = 0;
receiverGlobal = "wxid_";
senderGlobal = "wxid_";
}
});
}
setImmediate(attachReplyReq2buf);
// -------------------------辅助函数-------------------------
// 字符串转UTF8字节数组
function stringToUtf8Bytes(str) {
const utf8 = [];
for (let i = 0; i < str.length; i++) {
let charcode = str.charCodeAt(i);
if (charcode < 0x80) utf8.push(charcode);
else if (charcode < 0x800) {
utf8.push(0xc0 | (charcode >> 6),
0x80 | (charcode & 0x3f));
} else if (charcode < 0x10000) {
utf8.push(0xe0 | (charcode >> 12),
0x80 | ((charcode >> 6) & 0x3f),
0x80 | (charcode & 0x3f));
} else {
utf8.push(0xf0 | (charcode >> 18),
0x80 | ((charcode >> 12) & 0x3f),
0x80 | ((charcode >> 6) & 0x3f),
0x80 | (charcode & 0x3f));
}
}
return utf8;
}
// XML转义
function escapeXml(str) {
if (!str) return "";
return str.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// 生成随机消息ID
function generateRandomMsgId() {
let result = '';
for (let i = 0; i < 16; i++) {
result += Math.floor(Math.random() * 10).toString();
}
return result;
}
// 辅助函数: 将字符串写入内存
function patchString(addr, str) {
for (let i = 0; i < str.length; i++) {
addr.add(i).writeU8(str.charCodeAt(i));
}
addr.add(str.length).writeU8(0);
}
// -------------------------RPC导出函数-------------------------
// 发送Reply消息的RPC接口
// 参数: taskId, sender, receiver, content, referMsgContent, referMsgType, referMsgSender, referMsgId, chatRoom
rpc.exports = {
triggerSendReplyMessage: triggerSendReplyMessage
};