Files
wechat_chatter/onebot/image.js
T
2026-01-08 21:01:33 +08:00

789 lines
33 KiB
JavaScript

// 1. 获取微信主模块的基地址
var baseAddr = Process.getModuleByName("WeChat").base;
if (!baseAddr) {
console.error("[!] 找不到 WeChat 模块基址,请检查进程名。");
}
console.log("[+] WeChat base address: " + baseAddr);
// // 触发函数地址,不同版本的地址看wechat_version 中的json文件复制过来
var sendMessageCallbackFunc = baseAddr.add(0xAA8C5B078);
var messageCallbackFunc1 = baseAddr.add(0x7fa0b18);
var messageCallbackFunc2 = baseAddr.add(0x7fa0b70);
var messageCallbackFunc3 = baseAddr.add(0x804ae78);
var messageCallbackFunc4 = baseAddr.add(0x803ac78);
// 这个必须是绝对位置
var triggerX1Payload = ptr(0x175ED6600);
var triggerFuncAddr = baseAddr.add(0x448A858);
var req2bufEnterAddr = baseAddr.add(0x34566C0);
var req2bufExitAddr = baseAddr.add(0x34577D8);
var protobufAddr = baseAddr.add(0x2275BFC);
var buf2RespAddr = baseAddr.add(0x347BD44);
// var patchProtobufAddr = baseAddr.add(0x2275BD8);
// var protobufDeleteAddr = baseAddr.add(0x2275C14);
// 触发函数X0参数地址
var globalMessagePtr = ptr(0);
// 消息体的一些指针地址
var cgiAddr = ptr(0);
var callBackFuncAddr = ptr(0);
var callBackFuncAddr2 = ptr(0);
var sendMessageAddr = ptr(0);
var messageAddr = ptr(0);
var messageContentAddr = 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;
// 消息的taskId
var taskIdGlobal = 0x20000090 // 最好比较大,不和原始的微信消息重复
var receiverGlobal = "wxid_"
var contentGlobal = "";
var lastSendTime = 0;
// 打印消息的地址,便于查询问题
function printAddr() {
console.log("[+] Addresses:");
console.log(" - cgiAddr: " + cgiAddr);
console.log(" - callBackFuncAddr: " + callBackFuncAddr);
console.log(" - sendMessageAddr: " + sendMessageAddr);
console.log(" - contentAddr: " + contentAddr);
console.log(" - globalMessagePtr: " + globalMessagePtr);
console.log(" - triggerX1Payload: " + triggerX1Payload);
}
// 辅助函数:写入 Hex 字符串
function patchHex(addr, hexStr) {
const bytes = hexStr.split(' ').map(h => parseInt(h, 16));
addr.writeByteArray(bytes);
addr.add(bytes.length).writeU8(0); // 终止符
}
// 初始化进行内存的分配
function setupSendMessageDynamic() {
console.log("[+] Starting Dynamic Message Patching...");
// 1. 动态分配内存块(按需分配大小)
// 分配原则:字符串给 64-128 字节,结构体按实际大小分配
cgiAddr = Memory.alloc(128);
callBackFuncAddr = Memory.alloc(16);
callBackFuncAddr2 = Memory.alloc(16);
sendMessageAddr = Memory.alloc(256);
messageAddr = Memory.alloc(512);
contentAddr = Memory.alloc(255);
receiverAddr = Memory.alloc(24);
htmlContentAddr = Memory.alloc(24);
// A. 写入字符串内容
patchHex(cgiAddr, "2f 63 67 69 2d 62 69 6e 2f 6d 69 63 72 6f 6d 73 67 2d 62 69 6e 2f 75 70 6c 6f 61 64 6d 73 67 69 6d 67");
patchHex(contentAddr, " ");
// B. 构建 SendMessage 结构体 (X24 基址位置)
sendMessageAddr.add(0x00).writeU64(0);
sendMessageAddr.add(0x08).writeU64(0);
sendMessageAddr.add(0x10).writePointer(sendMessageCallbackFunc); // 虚表地址通常仍需硬编码或从模块基址计算
sendMessageAddr.add(0x18).writeU64(1);
sendMessageAddr.add(0x20).writeU32(taskIdGlobal);
sendMessageAddr.add(0x28).writePointer(messageAddr); // 指向动态分配的 Message
console.log(" [+] sendMessageAddr Object: ", hexdump(sendMessageAddr, {
offset: 0,
length: 48,
header: true,
ansi: true
}));
// C. 构建 Message 结构体
messageAddr.add(0x00).writePointer(messageCallbackFunc1);
messageAddr.add(0x08).writeU32(taskIdGlobal);
messageAddr.add(0x0c).writeU32(0x6e);
messageAddr.add(0x10).writeU64(0x3);
messageAddr.add(0x18).writePointer(cgiAddr);
// 设置一些固定值
messageAddr.add(0x20).writeU64(uint64("0x20"));
messageAddr.add(0x28).writeU64(uint64("0x8000000000000030"));
messageAddr.add(0x30).writeU64(uint64("0x0000000001010100"));
messageAddr.add(0x58).writeU64(uint64("0x0101010100000001"));
// 处理回调地址
callBackFuncAddr.writePointer(messageCallbackFunc2);
messageAddr.add(0x98).writePointer(callBackFuncAddr);
// 设置内容指针
messageAddr.add(0xb8).writePointer(messageCallbackFunc3);
callBackFuncAddr2.writePointer(messageCallbackFunc4);
messageAddr.add(0xc0).writePointer(callBackFuncAddr2);
console.log(" [+] messageAddr Object: ", hexdump(messageAddr, {
offset: 0,
length: 64,
header: true,
ansi: true
}));
console.log(" [+] Dynamic Memory Setup Complete. - Message Object: " + messageAddr);
}
setImmediate(setupSendMessageDynamic);
// 辅助函数:写入 Hex 字符串
function patchHex(addr, hexStr) {
const bytes = hexStr.split(' ').map(h => parseInt(h, 16));
addr.writeByteArray(bytes);
addr.add(bytes.length).writeU8(0); // 终止符
}
// 初始化进行内存的分配
function setupSendMessageDynamic() {
console.log("[+] Starting Dynamic Message Patching...");
// 1. 动态分配内存块(按需分配大小)
// 分配原则:字符串给 64-128 字节,结构体按实际大小分配
cgiAddr = Memory.alloc(128);
callBackFuncAddr = Memory.alloc(16);
sendMessageAddr = Memory.alloc(256);
messageAddr = Memory.alloc(256);
contentAddr = Memory.alloc(255);
receiverAddr = Memory.alloc(24);
// A. 写入字符串内容
patchHex(cgiAddr, "2F 63 67 69 2D 62 69 6E 2F 6D 69 63 72 6F 6D 73 67 2D 62 69 6E 2F 6E 65 77 73 65 6E 64 6D 73 67");
patchHex(contentAddr, " ");
// B. 构建 SendMessage 结构体 (X24 基址位置)
sendMessageAddr.add(0x00).writeU64(0);
sendMessageAddr.add(0x08).writeU64(0);
sendMessageAddr.add(0x10).writePointer(sendMessageCallbackFunc);
sendMessageAddr.add(0x18).writeU64(1);
sendMessageAddr.add(0x20).writeU32(taskIdGlobal);
sendMessageAddr.add(0x28).writePointer(messageAddr); // 指向动态分配的 Message
console.log(" [+] sendMessageAddr Object: ", hexdump(sendMessageAddr, {
offset: 0,
length: 48,
header: true,
ansi: true
}));
// C. 构建 Message 结构体
messageAddr.add(0x00).writePointer(messageCallbackFunc1);
messageAddr.add(0x08).writeU32(taskIdGlobal);
messageAddr.add(0x0c).writeU32(0x20a);
messageAddr.add(0x10).writeU64(0x3);
messageAddr.add(0x18).writePointer(cgiAddr);
// 设置一些固定值
messageAddr.add(0x20).writeU64(uint64("0x20"));
messageAddr.add(0x28).writeU64(uint64("0x8000000000000030"));
messageAddr.add(0x30).writeU64(uint64("0x0000000001010100"));
messageAddr.add(0x58).writeU64(uint64("0x0101010100000001"));
// 处理回调地址
callBackFuncAddr.writePointer(messageCallbackFunc2);
messageAddr.add(0x98).writePointer(callBackFuncAddr);
// 设置内容指针
messageAddr.add(0xb8).writePointer(messageCallbackFunc3);
messageAddr.add(0xc0).writePointer(messageContentAddr);
messageAddr.add(0xc8).writeU64(uint64("0x0000000100000001"));
messageAddr.add(0xd0).writeU64(0x4);
messageAddr.add(0xd8).writeU64(0x1);
messageAddr.add(0xe0).writeU64(0x1);
messageAddr.add(0xe8).writePointer(messageCallbackFunc4);
console.log(" [+] messageAddr Object: ", hexdump(messageAddr, {
offset: 0,
length: 64,
header: true,
ansi: true
}));
console.log("[+] Dynamic Memory Setup Complete. - Message Object: " + messageAddr);
}
setImmediate(setupSendMessageDynamic);
function patchProtoBuf() {
// Memory.patchCode(patchProtobufAddr, 4, code => {
// const cw = new Arm64Writer(code, {pc: patchProtobufAddr});
// cw.putNop();
// cw.flush();
// });
//
// console.log("[+] Patching BL to NOP at " + patchProtobufAddr + " completed.");
//
// Memory.patchCode(protobufDeleteAddr, 4, code => {
// const cw = new Arm64Writer(code, {pc: protobufDeleteAddr});
// cw.putNop();
// cw.flush();
// });
//
// console.log("[+] Patching BL DELETE to NOP at " + protobufDeleteAddr + " completed.");
}
setImmediate(patchProtoBuf);
function manualTrigger(taskId, receiver, content) {
console.log("[+] Manual Trigger Started...");
if (!taskId || !receiver || !content) {
console.error("[!] taskId or Receiver or Content is empty!");
return "fail";
}
// 获取当前时间戳 (秒)
const timestamp = Math.floor(Date.now() / 1000);
// 全局变量不为空,并且上次发送时间小于1s,不给发送
if ((taskIdGlobal !== 0 || receiverGlobal !== "" || contentGlobal !== "") && lastSendTime + 1 > timestamp) {
console.error("[!] taskId or receiver or content is not empty!");
return "fail";
}
lastSendTime = timestamp
taskIdGlobal = taskId;
receiverGlobal = receiver;
contentGlobal = content;
messageAddr.add(0x08).writeU32(taskIdGlobal);
sendMessageAddr.add(0x20).writeU32(taskIdGlobal);
console.log("start init 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
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20
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
0xC0, 0x66, 0xED, 0x75, 0x01, 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
0x98, 0x67, 0xED, 0x75, 0x01, 0x00, 0x00, 0x00, // 0x190
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x198
];
// 从 0x175ED6604 开始写入 Payload
triggerX1Payload.writeU32(taskIdGlobal);
triggerX1Payload.add(0x04).writeByteArray(payloadData);
triggerX1Payload.add(0x18).writePointer(cgiAddr);
console.log("finished init payload")
const MMStartTask = new NativeFunction(triggerFuncAddr, 'int64', ['pointer']);
// 5. 调用函数
try {
// const arg1 = globalMessagePtr; // 第一个指针参数
const arg2 = triggerX1Payload; // 第二个参数 0x175ED6600
console.log(`[+] Calling MMStartTask at ${triggerFuncAddr} with args: (${arg2})`);
const result = MMStartTask(arg2);
console.log("[+] Execution MMStartTask Success. Return value: " + result);
return "ok";
} catch (e) {
console.error("[!] Error trigger function during execution: " + e);
return "fail";
}
}
// ReqBuf 进行拦截,替换入参数的消息指针
function attachReq2buf() {
console.log("[+] Target Req2buf enter Address: " + req2bufEnterAddr);
// 2. 开始拦截
Interceptor.attach(req2bufEnterAddr, {
onEnter: function (args) {
if (!this.context.x1.equals(taskIdGlobal)) {
return;
}
console.log("[+] 已命中目标Req2Buf地址:0x1033EE8E8 taskId:" + taskIdGlobal + "base:" + baseAddr);
// 3. 获取 X24 寄存器的值
const x24_base = this.context.x24;
insertMsgAddr = x24_base.add(0x60);
console.log("[+] 当前 Req2Buf X24 基址: " + x24_base);
if (typeof sendMessageAddr !== 'undefined') {
insertMsgAddr.writePointer(sendMessageAddr);
console.log("[+] 成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendMessageAddr +
"[+] Req2Buf 写入后内存预览: " + insertMsgAddr);
console.log(hexdump(insertMsgAddr, {
offset: 0,
length: 16,
header: true,
ansi: true
}))
console.log(hexdump(sendMessageAddr, {
offset: 0,
length: 48,
header: true,
ansi: true
}))
} else {
console.error("[!] 错误: 变量 sendMessageAddr 未定义,请确保已运行分配逻辑。");
}
}
});
// 在出口处拦截req2buf,把insertMsgAddr设置为0,避免被垃圾回收导致整个程序崩溃
console.log("[+] Target Req2buf leave Address: " + req2bufExitAddr);
Interceptor.attach(req2bufExitAddr, {
onEnter: function (args) {
if (!this.context.x25.equals(taskIdGlobal)) {
return;
}
insertMsgAddr.writeU64(0x0);
console.log("[+] 清空写入后内存预览: " + insertMsgAddr.readPointer());
taskIdGlobal = 0;
receiverGlobal = "";
contentGlobal = "";
send({
type: "finish",
})
}
});
}
setImmediate(attachReq2buf);
// 辅助函数:Protobuf Varint 编码 (对应 get_varint_timestamp_bytes)
function getVarintTimestampBytes() {
let ts = Math.floor(Date.now() / 1000);
let encodedBytes = [];
let tempTs = ts >>> 0; // 强制转为 32位 无符号整数
while (true) {
let byte = tempTs & 0x7F;
tempTs >>>= 7;
if (tempTs !== 0) {
encodedBytes.push(byte | 0x80);
} else {
encodedBytes.push(byte);
break;
}
}
return encodedBytes;
}
function stringToHexArray(str) {
var utf8Str = unescape(encodeURIComponent(str));
var arr = [];
for (var i = 0; i < utf8Str.length; i++) {
arr.push(utf8Str.charCodeAt(i)); // 获取字符的 ASCII 码 (即十六进制值)
}
return arr;
}
function generateRandom5ByteVarint() {
let res = [];
// 前 4 个字节:最高位(bit 7)必须是 1,低 7 位随机
for (let i = 0; i < 4; i++) {
let random7Bit = Math.floor(Math.random() * 128);
res.push(random7Bit | 0x80); // 强制设置最高位为 1
}
// 第 5 个字节:最高位必须是 0,为了确保不变成 4 字节,低 7 位不能全为 0
let lastByte = Math.floor(Math.random() * 127) + 1;
res.push(lastByte & 0x7F); // 确保最高位为 0
return res;
}
// 拦截 Protobuf 编码逻辑,注入自定义 Payload
function attachProto() {
console.log("[+] proto注入拦截目标地址: " + protobufAddr);
protoX1PayloadAddr = Memory.alloc(protoX1PayloadLen);
console.log("[+] Frida 分配的 Payload 地址: " + protoX1PayloadAddr);
Interceptor.attach(protobufAddr, {
onEnter: function (args) {
console.log("[+] Protobuf 拦截命中");
// var sp = this.context.sp;
// var firstValue = sp.readU32();
// if (firstValue !== taskIdGlobal) {
// console.log("[+] Protobuf 拦截未命中,跳过...");
// return;
// }
const type = [0x0A, 0x3F, 0x0A, 0x01, 0x00, 0x10]
const msgId = [0xc6, 0xbc, 0x90, 0xb9, 0x08]
const cpHeader = [0x1A, 0x10]
const cp = [0x6D, 0x33, 0x30, 0x63, 0x34, 0x36, 0x37, 0x34, 0x66, 0x35, 0x61, 0x30, 0x62, 0x39, 0x64, 0x30] // m30c4674f5a0b9d0
const randomId = [0x20, 0xAF, 0xAC, 0x90, 0x93, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01]
const sysHeader = [0x2A, 0x15]
// UnifiedPCMac 26 arm64
const sys = [0x55, 0x6E, 0x69, 0x66, 0x69, 0x65, 0x64, 0x50, 0x43, 0x4D, 0x61, 0x63, 0x20, 0x32, 0x36, 0x20, 0x61, 0x72, 0x6D, 0x36, 0x34]
const msgIdHeader = [0x30, 0x73, 0x12, 0x2E, 0x0A, 0x2C]
// 45872025384@chatroom_176787000_60_xwechat_1
const receiverMsgId = [0x34, 0x35, 0x38, 0x37, 0x32, 0x30, 0x32, 0x35, 0x33, 0x38, 0x34,
0x40, 0x63, 0x68, 0x61, 0x74, 0x72, 0x6F, 0x6F, 0x6D, 0x5F, 0x31, 0x37, 0x36, 0x37, 0x38, 0x37,
0x30, 0x30, 0x30, 0x30, 0x5F, 0x36, 0x30, 0x5F, 0x78, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x5F, 0x31,
]
const senderHeader = [0x1A, 0x15, 0x0A, 0x13];
const sender = [0x77, 0x78, 0x69, 0x64, 0x5F, 0x6C, 0x64, 0x66, 0x74, 0x75, 0x68, 0x65, 0x33, 0x36, 0x69, 0x7A, 0x67, 0x31, 0x39];
const receiverHeader = [0x22, 0x16, 0x0A, 0x14]
const receiver = [0x34, 0x35, 0x38, 0x37, 0x32, 0x30, 0x32, 0x35, 0x33, 0x38, 0x34, 0x40, 0x63, 0x68, 0x61, 0x74, 0x72, 0x6F, 0x6F, 0x6D]
const randomId1 = [0x28, 0xF4, 0x0B]
const type1 = [0x30, 0x00]
const randomId2 = [0x38, 0xF4, 0x0B]
const randomId3 = [0x42, 0x04, 0x08, 0x00, 0x12, 0x00]
const randomId4 = [0x48, 0x03]
const htmlHeader = [0x52, 0x83, 0x01];
// <msgsource>
// <img_file_name>cc357085-cbff-4968-b9fe-b4e4eab5b0ca.png</img_file_name>
// <alnode>
// <fr>1</fr>
// <cf>3</cf>
// </alnode>
// </msgsource>
const html = [0x3C, 0x6D, 0x73, 0x67, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x3E, 0x3C, 0x69,
0x6D, 0x67, 0x5F, 0x66, 0x69, 0x6C, 0x65, 0x5F, // 0xC0
0x6E, 0x61, 0x6D, 0x65, 0x3E, 0x63, 0x63, 0x33, // 0xC8
0x35, 0x37, 0x30, 0x38, 0x35, 0x2D, 0x63, 0x62, // 0xD0
0x66, 0x66, 0x2D, 0x34, 0x39, 0x36, 0x38, 0x2D, // 0xD8
0x62, 0x39, 0x66, 0x65, 0x2D, 0x62, 0x34, 0x65, // 0xE0
0x34, 0x65, 0x61, 0x62, 0x35, 0x62, 0x30, 0x63, // 0xE8
0x61, 0x2E, 0x70, 0x6E, 0x67, 0x3C, 0x2F, 0x69, // 0xF0
0x6D, 0x67, 0x5F, 0x66, 0x69, 0x6C, 0x65, 0x5F, // 0xF8
0x6E, 0x61, 0x6D, 0x65, 0x3E, 0x3C, 0x61, 0x6C, // 0x100
0x6E, 0x6F, 0x64, 0x65, 0x3E, 0x3C, 0x66, 0x72, // 0x108
0x3E, 0x31, 0x3C, 0x2F, 0x66, 0x72, 0x3E, 0x3C, // 0x110
0x63, 0x66, 0x3E, 0x33, 0x3C, 0x2F, 0x63, 0x66, // 0x118
0x3E, 0x3C, 0x2F, 0x61, 0x6C, 0x6E, 0x6F, 0x64, // 0x120
0x65, 0x3E, 0x3C, 0x2F, 0x6D, 0x73, 0x67, 0x73, // 0x128
0x6F, 0x75, 0x72, 0x63, 0x65, 0x3E,
]
const cdnHeader = [0x58, 0x01, 0x60, 0x02, 0x68, 0x05, 0x7A, 0xB2, 0x01]
// 3057 开头的cdn key
const cdn = [0x33, // 0x138
0x30, 0x35, 0x37, 0x30, 0x32, 0x30, 0x31, 0x30, // 0x140
0x30, 0x30, 0x34, 0x34, 0x62, 0x33, 0x30, 0x34, // 0x148
0x39, 0x30, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, // 0x150
0x32, 0x30, 0x34, 0x64, 0x30, 0x61, 0x37, 0x30, // 0x158
0x39, 0x35, 0x64, 0x30, 0x32, 0x30, 0x33, 0x32, // 0x160
0x66, 0x35, 0x61, 0x34, 0x35, 0x30, 0x32, 0x30, // 0x168
0x34, 0x65, 0x61, 0x38, 0x65, 0x30, 0x65, 0x37, // 0x170
0x39, 0x30, 0x32, 0x30, 0x34, 0x36, 0x39, 0x35, // 0x178
0x66, 0x38, 0x65, 0x33, 0x31, 0x30, 0x34, 0x32, // 0x180
0x34, 0x33, 0x37, 0x33, 0x39, 0x36, 0x35, 0x33, // 0x188
0x31, 0x33, 0x37, 0x33, 0x31, 0x33, 0x34, 0x33, // 0x190
0x35, 0x32, 0x64, 0x33, 0x34, 0x33, 0x34, 0x33, // 0x198
0x35, 0x33, 0x35, 0x32, 0x64, 0x33, 0x34, 0x33, // 0x1A0
0x33, 0x36, 0x33, 0x33, 0x32, 0x32, 0x64, 0x36, // 0x1A8
0x31, 0x33, 0x35, 0x33, 0x32, 0x33, 0x31, 0x32, // 0x1B0
0x64, 0x33, 0x39, 0x33, 0x33, 0x33, 0x32, 0x33, // 0x1B8
0x32, 0x36, 0x34, 0x36, 0x34, 0x36, 0x32, 0x36, // 0x1C0
0x31, 0x33, 0x37, 0x36, 0x36, 0x33, 0x32, 0x36, // 0x1C8
0x36, 0x30, 0x32, 0x30, 0x34, 0x30, 0x35, 0x32, // 0x1D0
0x34, 0x33, 0x38, 0x30, 0x31, 0x30, 0x32, 0x30, // 0x1D8
0x31, 0x30, 0x30, 0x30, 0x34, 0x30, 0x35, 0x30, // 0x1E0
0x30, 0x34, 0x63, 0x34, 0x65, 0x36, 0x31, 0x30, // 0x1E8
0x30,
];
const cdn2Header = [0x82, 0x01, 0xB2, 0x01]
const cdn2 = [
0x33, 0x30, 0x35, // 0x1F0
0x37, 0x30, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, // 0x1F8
0x34, 0x34, 0x62, 0x33, 0x30, 0x34, 0x39, 0x30, // 0x200
0x32, 0x30, 0x31, 0x30, 0x30, 0x30, 0x32, 0x30, // 0x208
0x34, 0x64, 0x30, 0x61, 0x37, 0x30, 0x39, 0x35, // 0x210
0x64, 0x30, 0x32, 0x30, 0x33, 0x32, 0x66, 0x35, // 0x218
0x61, 0x34, 0x35, 0x30, 0x32, 0x30, 0x34, 0x65, // 0x220
0x61, 0x38, 0x65, 0x30, 0x65, 0x37, 0x39, 0x30, // 0x228
0x32, 0x30, 0x34, 0x36, 0x39, 0x35, 0x66, 0x38, // 0x230
0x65, 0x33, 0x31, 0x30, 0x34, 0x32, 0x34, 0x33, // 0x238
0x37, 0x33, 0x39, 0x36, 0x35, 0x33, 0x31, 0x33, // 0x240
0x37, 0x33, 0x31, 0x33, 0x34, 0x33, 0x35, 0x32, // 0x248
0x64, 0x33, 0x34, 0x33, 0x34, 0x33, 0x35, 0x33, // 0x250
0x35, 0x32, 0x64, 0x33, 0x34, 0x33, 0x33, 0x36, // 0x258
0x33, 0x33, 0x32, 0x32, 0x64, 0x36, 0x31, 0x33, // 0x260
0x35, 0x33, 0x32, 0x33, 0x31, 0x32, 0x64, 0x33, // 0x268
0x39, 0x33, 0x33, 0x33, 0x32, 0x33, 0x32, 0x36, // 0x270
0x34, 0x36, 0x34, 0x36, 0x32, 0x36, 0x31, 0x33, // 0x278
0x37, 0x36, 0x36, 0x33, 0x32, 0x36, 0x36, 0x30, // 0x280
0x32, 0x30, 0x34, 0x30, 0x35, 0x32, 0x34, 0x33, // 0x288
0x38, 0x30, 0x31, 0x30, 0x32, 0x30, 0x31, 0x30, // 0x290
0x30, 0x30, 0x34, 0x30, 0x35, 0x30, 0x30, 0x34, // 0x298
0x63, 0x34, 0x65, 0x36, 0x31, 0x30, 0x30,
]
const aesKeyHeader = [0x8A, 0x01, 0x20]
const aesKey = [
0x38, 0x31, 0x66, 0x66, 0x37, 0x65, // 0x2A8
0x61, 0x37, 0x36, 0x37, 0x31, 0x34, 0x66, 0x33, // 0x2B0
0x30, 0x66, 0x36, 0x63, 0x32, 0x63, 0x33, 0x64, // 0x2B8
0x37, 0x37, 0x30, 0x64, 0x39, 0x38, 0x63, 0x35, // 0x2C0
0x64, 0x65,
]
const randomId5 = [0x90, 0x01, 0x01, 0x98, 0x01, 0xFF, // 0x2C8
0x13, 0xA0, 0x01, 0xFF, 0x13]
const cdn3Header = [0xAA, 0x01, 0xB2, 0x01]
const cdn3 = [
0x33, 0x30, 0x35, 0x37, 0x30, 0x32, 0x30, // 0x2D8
0x31, 0x30, 0x30, 0x30, 0x34, 0x34, 0x62, 0x33, // 0x2E0
0x30, 0x34, 0x39, 0x30, 0x32, 0x30, 0x31, 0x30, // 0x2E8
0x30, 0x30, 0x32, 0x30, 0x34, 0x64, 0x30, 0x61, // 0x2F0
0x37, 0x30, 0x39, 0x35, 0x64, 0x30, 0x32, 0x30, // 0x2F8
0x33, 0x32, 0x66, 0x35, 0x61, 0x34, 0x35, 0x30, // 0x300
0x32, 0x30, 0x34, 0x65, 0x61, 0x38, 0x65, 0x30, // 0x308
0x65, 0x37, 0x39, 0x30, 0x32, 0x30, 0x34, 0x36, // 0x310
0x39, 0x35, 0x66, 0x38, 0x65, 0x33, 0x31, 0x30, // 0x318
0x34, 0x32, 0x34, 0x33, 0x37, 0x33, 0x39, 0x36, // 0x320
0x35, 0x33, 0x31, 0x33, 0x37, 0x33, 0x31, 0x33, // 0x328
0x34, 0x33, 0x35, 0x32, 0x64, 0x33, 0x34, 0x33, // 0x330
0x34, 0x33, 0x35, 0x33, 0x35, 0x32, 0x64, 0x33, // 0x338
0x34, 0x33, 0x33, 0x36, 0x33, 0x33, 0x32, 0x32, // 0x340
0x64, 0x36, 0x31, 0x33, 0x35, 0x33, 0x32, 0x33, // 0x348
0x31, 0x32, 0x64, 0x33, 0x39, 0x33, 0x33, 0x33, // 0x350
0x32, 0x33, 0x32, 0x36, 0x34, 0x36, 0x34, 0x36, // 0x358
0x32, 0x36, 0x31, 0x33, 0x37, 0x36, 0x36, 0x33, // 0x360
0x32, 0x36, 0x36, 0x30, 0x32, 0x30, 0x34, 0x30, // 0x368
0x35, 0x32, 0x34, 0x33, 0x38, 0x30, 0x31, 0x30, // 0x370
0x32, 0x30, 0x31, 0x30, 0x30, 0x30, 0x34, 0x30, // 0x378
0x35, 0x30, 0x30, 0x34, 0x63, 0x34, 0x65, 0x36, // 0x380
0x31, 0x30, 0x30
]
const randomId6 = [0xB0, 0x01, 0xF4, 0x0B]
const randomId7 = [0xB8, 0x01, 0x68]
const randomId8 = [0xC0, 0x01, 0x3A]
const aesKey1Header = [0xCA, 0x01, 0x20]
const aesKey1 = [
0x38, 0x31, 0x66, 0x66, 0x37, 0x65, 0x61, 0x37, // 0x398
0x36, 0x37, 0x31, 0x34, 0x66, 0x33, 0x30, 0x66, // 0x3A0
0x36, 0x63, 0x32, 0x63, 0x33, 0x64, 0x37, 0x37, // 0x3A8
0x30, 0x64, 0x39, 0x38, 0x63, 0x35, 0x64, 0x65, // 0x3B0
]
const aesKey2Header = [0xDA, 0x01, 0x20]
const aesKey2 = [
0x36, 0x37, 0x36, 0x33, 0x32, // 0x3B8
0x35, 0x32, 0x30, 0x35, 0x36, 0x38, 0x35, 0x37, // 0x3C0
0x61, 0x64, 0x66, 0x65, 0x35, 0x36, 0x36, 0x31, // 0x3C8
0x30, 0x35, 0x37, 0x30, 0x63, 0x61, 0x34, 0x32, // 0x3D0
0x38, 0x34, 0x63,
]
const randomId9 = [0xE0, 0x01, 0xd9, 0xe7, 0xc7, 0xF3, 0x02]
var left0 = [
0xF0, 0x01, 0x00, 0xA0, 0x02, 0x00, // 0x3E0
0xC8, 0x02, 0x00, 0x00 // 0x3E8
]
const finalPayload = type.concat(msgId, cpHeader, cp, randomId, sysHeader, sys, msgIdHeader, receiverMsgId,
senderHeader, sender, receiverHeader, receiver, randomId1, type1, randomId2, randomId3, randomId4, htmlHeader, html, cdnHeader,
cdn,cdn2Header, cdn2,aesKeyHeader,aesKey, randomId5, cdn3Header, cdn3, randomId6, randomId7, randomId8, aesKey1Header, aesKey1,
aesKey2Header, aesKey2, randomId9, left0)
console.log("[+] Payload 准备写入");
protoX1PayloadAddr.writeByteArray(finalPayload);
console.log("[+] Payload 已写入,长度: " + finalPayload.length);
this.context.x1 = protoX1PayloadAddr;
this.context.x2 = ptr(finalPayload.length);
console.log("[+] 寄存器修改完成: X1=" + this.context.x1 + ", X2=" + this.context.x2, hexdump(protoX1PayloadAddr, {
offset: 0,
length: 1024,
header: true,
ansi: true
}));
},
});
}
function toVarint(n) {
let res = [];
while (n >= 128) {
res.push((n & 0x7F) | 0x80); // 取后7位,最高位置1
n = n >> 7; // 右移7位
}
res.push(n); // 最后一位最高位为0
return res;
}
setImmediate(attachProto);
function setReceiver() {
console.log("[+] buf2RespAddr WeChat Base: " + baseAddr + "[+] Attaching to: " + buf2RespAddr);
// 3. 开始拦截
Interceptor.attach(buf2RespAddr, {
onEnter: function (args) {
const currentPtr = this.context.x1;
let start = 0x1e;
let senderLen = currentPtr.add(start).readU8();
if (senderLen !== 0x14 && senderLen !== 0x13) {
start = 0x1d;
let senderLen = currentPtr.add(start).readU8();
if (senderLen !== 0x14 && senderLen !== 0x13) {
return
}
}
console.log("[+] Entered Receive Function: 0x1023B5348");
let senderPtr = currentPtr.add(start + 1);
let sender = senderPtr.readUtf8String(senderLen);
let receiverLenPtr = senderPtr.add(senderLen).add(3);
let receiverLen = receiverLenPtr.readU8();
let receiverStrPtr = receiverLenPtr.add(1);
let receiver = receiverStrPtr.readUtf8String(receiverLen);
let contentLenPtr = receiverStrPtr.add(receiverLen).add(6);
// 判断是否等于 0x77 ('w'), 如果是,则是短字段
if (contentLenPtr.readU8() === 0x77) {
contentLenPtr = contentLenPtr.add(-1);
}
const contentLenValue = readVarint(contentLenPtr)
let contentPtr = contentLenPtr.add(contentLenValue.byteLength);
var content = contentPtr.readUtf8String(contentLenValue.value);
var selfId = receiver
var msgType = "private"
var groupId = ""
var senderUser = sender
var messages = [];
messages.push({type: "text", data: {text: content}});
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();
content = content.substring(splitIndex + 2).trim();
messages = [];
const parts = content.split('\u2005');
for (let part of parts) {
part = part.trim();
if (!part.startsWith("@")) {
messages.push({type: "text", data: {text: part}});
}
}
const xmlPtr = contentPtr.add(contentLenValue.value).add(15);
const xmlLenValue = readVarint(xmlPtr)
const xml = xmlPtr.add(xmlLenValue.byteLength).readUtf8String(xmlLenValue.value);
const atUserMatch = xml.match(/<atuserlist>([\s\S]*?)<\/atuserlist>/);
const atUser = atUserMatch ? atUserMatch[1] : null;
if (atUser) {
messages.push({type: "at", data: {qq: atUser}});
}
}
send({
message_type: msgType,
user_id: senderUser, // 发送人的 ID
self_id: selfId, // 接收人的 ID
group_id: groupId, // 群 ID
message_id: taskIdGlobal,
type: "send",
raw: {peerUid: taskIdGlobal},
message: messages
})
},
});
}
// 使用 setImmediate 确保在模块加载后执行
setImmediate(setReceiver)
function readVarint(addr) {
let value = 0;
let shift = 0;
let count = 0;
while (true) {
let byte = addr.add(count).readU8();
// 取低7位进行累加
value |= (byte & 0x7f) << shift;
count++; // 消耗了一个字节
// 如果最高位是0,跳出循环
if ((byte & 0x80) === 0) break;
shift += 7;
if (count > 5) return -1; // 安全校验,防止死循环
}
return {
value: value, // 最终长度数值 (例如 251)
byteLength: count // 长度字段占用的字节数 (例如 2)
};
}
rpc.exports = {
manualTrigger: manualTrigger
};