From c4806c4c3b7a1990d301f6804556b92b8502c8c4 Mon Sep 17 00:00:00 2001 From: yincong Date: Thu, 15 Jan 2026 18:21:46 +0800 Subject: [PATCH] merge text and img --- README.md | 13 + onebot/image.js | 221 +++++------ onebot/receiver.js | 12 +- onebot/script.js | 893 ++++++++++++++++++++++++++++++++++----------- onebot/text.js | 409 +++++++++++++++++++++ 5 files changed, 1209 insertions(+), 339 deletions(-) create mode 100644 onebot/text.js diff --git a/README.md b/README.md index d3183db..9a7f1a9 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,16 @@ hook2是对Req2Buf这个函数进行消息体注入,因为hook1触发的时候 hook4是在Req2Buf,清除掉消息体的内容,因为后序在OnTaskEnd会回收内存,如果我这边消息体还在整个的指针上就会被清除,但是这个线程不认识这块内存,整个程序就会crash。 查看:./frida/succ.js + +## 图片消息 +``` +发送一张图片,为了让函数找到X0寄存器的数据。 + +mkdir -p "/Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_xxx/temp/xxx/2026-01/Img/" +cp /Users/yincong/Desktop/1.png /Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_xxx/temp/xxx/2026-01/Img/xxx.jpg + + +manualUpload("wxid_7wd1ece99f7i21", "8dd4755e12e052fa5647a883e6bf0783", "/Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_xxx/temp/xxx/2026-01/Img/xxx.jpg") +manualTrigger(0x20000199, "wxid_xxx","wxid_xxx") + +``` diff --git a/onebot/image.js b/onebot/image.js index c49500d..5a6c9d2 100644 --- a/onebot/image.js +++ b/onebot/image.js @@ -7,38 +7,26 @@ console.log("[+] WeChat base address: " + baseAddr); // 触发函数地址,不同版本的地址看wechat_version 中的json文件复制过来 -var sendMessageCallbackFunc = ptr(0x0); -var messageCallbackFunc1 = baseAddr.add(0x7fa0b18); +var sendImgMessageCallbackFunc = ptr(0x0); +var imgMessageCallbackFunc1 = baseAddr.add(0x7fa0b18); +var CndOnCompleteAddr = baseAddr.add(0x34154E0); // 这个必须是绝对位置 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 patchProtobufFunc1 = baseAddr.add(0x2275BB8) -var patchProtobufFunc2 = baseAddr.add(0x2275BD8); -var protobufDeleteAddr = baseAddr.add(0x2275C14); -var CndOnCompleteAddr = baseAddr.add(0x34154E0); - -var runReadWriteAddr = baseAddr.add(0x450F518) -var runReadWriteAddr1 = baseAddr.add(0x450F51C); - -// 触发函数X0参数地址 -var globalMessagePtr = ptr(0); +var imgProtobufAddr = baseAddr.add(0x2275BFC); +var patchImgProtobufFunc1 = baseAddr.add(0x2275BB8) +var patchImgProtobufFunc2 = baseAddr.add(0x2275BD8); +var imgProtobufDeleteAddr = baseAddr.add(0x2275C14); // 消息体的一些指针地址 -var cgiAddr = ptr(0); -var callBackFuncAddr = ptr(0); -var callBackFuncAddr2 = ptr(0); -var callBackFuncAddr3 = ptr(0); - -var sendMessageAddr = ptr(0); -var messageAddr = ptr(0); +var imgCgiAddr = ptr(0); +var sendImgMessageAddr = ptr(0); +var imgMessageAddr = ptr(0); var contentAddr = ptr(0); var insertMsgAddr = ptr(0); -var receiverAddr = ptr(0); -var htmlContentAddr = ptr(0); var protoX1PayloadAddr = ptr(0); // 消息的taskId @@ -46,51 +34,54 @@ var taskIdGlobal = 0x20000090 // 最好比较大,不和原始的微信消息 var receiverGlobal = "wxid_7wd1ece99f7i21" var senderGlobal = "wxid_ldftuhe36izg19" var lastSendTime = 0; + var globalImageCdnKey = ""; var globalAesKey1 = ""; var globalMd5Key = ""; +var uploadGlobalX0 = ptr(0) +var uploadFunc1Addr = ptr(0) +var uploadFunc2Addr = ptr(0) +var imageIdAddr = ptr(0) +var md5Addr = ptr(0) +var uploadAesKeyAddr = ptr(0) +var ImagePathAddr1 = ptr(0) +var uploadImagePayload = ptr(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); -} // 初始化进行内存的分配 -function setupSendMessageDynamic() { - console.log("[+] Starting Dynamic Message Patching..."); +function setupSendImgMessageDynamic() { + console.log("[+] Starting setupSendImgMessageDynamic Dynamic Message Patching..."); // 1. 动态分配内存块(按需分配大小) // 分配原则:字符串给 64-128 字节,结构体按实际大小分配 - cgiAddr = Memory.alloc(128); - callBackFuncAddr = Memory.alloc(16); - callBackFuncAddr2 = Memory.alloc(16); - callBackFuncAddr3 = Memory.alloc(16); - sendMessageAddr = Memory.alloc(256); - messageAddr = Memory.alloc(512); + imgCgiAddr = Memory.alloc(128); + sendImgMessageAddr = Memory.alloc(256); + imgMessageAddr = Memory.alloc(512); contentAddr = Memory.alloc(16); - receiverAddr = Memory.alloc(24); - htmlContentAddr = Memory.alloc(24); + + uploadFunc1Addr = Memory.alloc(16); + uploadFunc2Addr = Memory.alloc(16); + imageIdAddr = Memory.alloc(128); + md5Addr = Memory.alloc(128); + uploadAesKeyAddr = Memory.alloc(128); + ImagePathAddr1 = Memory.alloc(128); + uploadImagePayload = Memory.alloc(512); + // A. 写入字符串内容 - patchString(cgiAddr, "/cgi-bin/micromsg-bin/uploadmsgimg"); + patchString(imgCgiAddr, "/cgi-bin/micromsg-bin/uploadmsgimg"); patchString(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); + sendImgMessageAddr.add(0x00).writeU64(0); + sendImgMessageAddr.add(0x08).writeU64(0); + sendImgMessageAddr.add(0x10).writePointer(sendImgMessageCallbackFunc); + sendImgMessageAddr.add(0x18).writeU64(1); + sendImgMessageAddr.add(0x20).writeU32(taskIdGlobal); + sendImgMessageAddr.add(0x28).writePointer(imgMessageAddr); - console.log(" [+] sendMessageAddr Object: ", hexdump(sendMessageAddr, { + console.log(" [+] sendImgMessageAddr Object: ", hexdump(sendImgMessageAddr, { offset: 0, length: 48, header: true, @@ -98,52 +89,49 @@ function setupSendMessageDynamic() { })); // 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(0x22); - messageAddr.add(0x28).writeU64(uint64("0x8000000000000030")); - messageAddr.add(0x30).writeU64(uint64("0x0000000001010100")); + imgMessageAddr.add(0x00).writePointer(imgMessageCallbackFunc1); + imgMessageAddr.add(0x08).writeU32(taskIdGlobal); + imgMessageAddr.add(0x0c).writeU32(0x6e); + imgMessageAddr.add(0x10).writeU64(0x3); + imgMessageAddr.add(0x18).writePointer(imgCgiAddr); + imgMessageAddr.add(0x20).writeU64(0x22); + imgMessageAddr.add(0x28).writeU64(uint64("0x8000000000000030")); + imgMessageAddr.add(0x30).writeU64(uint64("0x0000000001010100")); - console.log(" [+] messageAddr Object: ", hexdump(messageAddr, { - offset: 0, - length: 64, - header: true, - ansi: true - })); + console.log(" [+] Dynamic Memory Setup Complete. - Message Object: " + imgMessageAddr); - console.log(" [+] Dynamic Memory Setup Complete. - Message Object: " + messageAddr); + + uploadFunc1Addr.writePointer(baseAddr.add(0x802b8b0)); + uploadFunc2Addr.writePointer(baseAddr.add(0x7fd5908)); } -setImmediate(setupSendMessageDynamic); +setImmediate(setupSendImgMessageDynamic); function patchProtoBuf() { - Memory.patchCode(patchProtobufFunc1, 4, code => { - const cw = new Arm64Writer(code, {pc: patchProtobufFunc1}); + Memory.patchCode(patchImgProtobufFunc1, 4, code => { + const cw = new Arm64Writer(code, {pc: patchImgProtobufFunc1}); cw.putNop(); cw.flush(); }); - console.log("[+] Patching BL to NOP at " + patchProtobufFunc1 + " completed."); + console.log("[+] Patching BL to NOP at " + patchImgProtobufFunc1 + " completed."); - Memory.patchCode(patchProtobufFunc2, 4, code => { - const cw = new Arm64Writer(code, {pc: patchProtobufFunc2}); + Memory.patchCode(patchImgProtobufFunc2, 4, code => { + const cw = new Arm64Writer(code, {pc: patchImgProtobufFunc2}); cw.putNop(); cw.flush(); }); - console.log("[+] Patching BL to NOP at " + patchProtobufFunc2 + " completed."); + console.log("[+] Patching BL to NOP at " + patchImgProtobufFunc2 + " completed."); - Memory.patchCode(protobufDeleteAddr, 4, code => { - const cw = new Arm64Writer(code, {pc: protobufDeleteAddr}); + Memory.patchCode(imgProtobufDeleteAddr, 4, code => { + const cw = new Arm64Writer(code, {pc: imgProtobufDeleteAddr}); cw.putNop(); cw.flush(); }); - console.log("[+] Patching BL DELETE to NOP at " + protobufDeleteAddr + " completed."); + console.log("[+] Patching BL DELETE to NOP at " + imgProtobufDeleteAddr + " completed."); } setImmediate(patchProtoBuf); @@ -162,8 +150,8 @@ function manualTrigger(taskId, sender, receiver) { receiverGlobal = receiver; senderGlobal = sender; - messageAddr.add(0x08).writeU32(taskIdGlobal); - sendMessageAddr.add(0x20).writeU32(taskIdGlobal); + imgMessageAddr.add(0x08).writeU32(taskIdGlobal); + sendImgMessageAddr.add(0x20).writeU32(taskIdGlobal); console.log("start init payload") @@ -225,7 +213,7 @@ function manualTrigger(taskId, sender, receiver) { // 从 0x175ED6604 开始写入 Payload triggerX1Payload.writeU32(taskIdGlobal); triggerX1Payload.add(0x04).writeByteArray(payloadData); - triggerX1Payload.add(0x18).writePointer(cgiAddr); + triggerX1Payload.add(0x18).writePointer(imgCgiAddr); console.log("finished init payload") @@ -233,7 +221,6 @@ function manualTrigger(taskId, sender, receiver) { // 5. 调用函数 try { - // const arg1 = globalMessagePtr; // 第一个指针参数 const arg2 = triggerX1Payload; // 第二个参数 0x175ED6600 console.log(`[+] Calling MMStartTask at ${triggerFuncAddr} with args: (${arg2})`); const result = MMStartTask(arg2); @@ -265,9 +252,9 @@ function attachReq2buf() { insertMsgAddr = x24_base.add(0x60); console.log("[+] 当前 Req2Buf X24 基址: " + x24_base); - if (typeof sendMessageAddr !== 'undefined') { - insertMsgAddr.writePointer(sendMessageAddr); - console.log("[+] 成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendMessageAddr + + if (typeof sendImgMessageAddr !== 'undefined') { + insertMsgAddr.writePointer(sendImgMessageAddr); + console.log("[+] 成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendImgMessageAddr + "[+] Req2Buf 写入后内存预览: " + insertMsgAddr); console.log(hexdump(insertMsgAddr, { offset: 0, @@ -275,7 +262,7 @@ function attachReq2buf() { header: true, ansi: true })) - console.log(hexdump(sendMessageAddr, { + console.log(hexdump(sendImgMessageAddr, { offset: 0, length: 48, header: true, @@ -319,11 +306,11 @@ function stringToHexArray(str) { // 拦截 Protobuf 编码逻辑,注入自定义 Payload function attachProto() { - console.log("[+] proto注入拦截目标地址: " + protobufAddr); + console.log("[+] proto注入拦截目标地址: " + protoX1PayloadAddr); protoX1PayloadAddr = Memory.alloc(1024); console.log("[+] Frida 分配的 Payload 地址: " + protoX1PayloadAddr); - Interceptor.attach(protobufAddr, { + Interceptor.attach(imgProtobufAddr, { onEnter: function (args) { console.log("[+] Protobuf 拦截命中"); @@ -450,50 +437,6 @@ function toVarint(n) { return res; } -function patchCdnOnComplete() { - Interceptor.attach(CndOnCompleteAddr, { - onEnter: function (args) { - console.log("[+] enter CndOnCompleteAddr"); - - try { - const x2 = this.context.x2; - globalImageCdnKey = x2.add(0x60).readPointer().readUtf8String(); - globalAesKey1 = x2.add(0x78).readPointer().readUtf8String(); - globalMd5Key = x2.add(0x90).readPointer().readUtf8String(); - console.log("[+] globalImageCdnKey: " + globalImageCdnKey + " globalAesKey1: " + globalAesKey1 + " globalAesKey2: " + globalMd5Key); - } catch (e) { - console.log("[-] Memory access error at onEnter: " + e); - } - } - }) -} - -setImmediate(patchCdnOnComplete) - -var uploadGlobalX0 = ptr(0) -var uploadFunc1Addr = ptr(0) -var uploadFunc2Addr = ptr(0) -var imageIdAddr = ptr(0) -var md5Addr = ptr(0) -var uploadAesKeyAddr = ptr(0) -var ImagePathAddr1 = ptr(0) -var uploadImagePayload = ptr(0); - -function initMemo() { - uploadFunc1Addr = Memory.alloc(24); - uploadFunc2Addr = Memory.alloc(24); - imageIdAddr = Memory.alloc(256); - md5Addr = Memory.alloc(256); - uploadAesKeyAddr = Memory.alloc(256); - ImagePathAddr1 = Memory.alloc(256); - uploadImagePayload = Memory.alloc(512); - - - uploadFunc1Addr.writePointer(baseAddr.add(0x802b8b0)); - uploadFunc2Addr.writePointer(baseAddr.add(0x7fd5908)); -} - -setImmediate(initMemo) function manualUpload(receiver, md5, imagePath) { @@ -642,6 +585,26 @@ function generateAESKey() { } +function patchCdnOnComplete() { + Interceptor.attach(CndOnCompleteAddr, { + onEnter: function (args) { + console.log("[+] enter CndOnCompleteAddr"); + + try { + const x2 = this.context.x2; + globalImageCdnKey = x2.add(0x60).readPointer().readUtf8String(); + globalAesKey1 = x2.add(0x78).readPointer().readUtf8String(); + globalMd5Key = x2.add(0x90).readPointer().readUtf8String(); + console.log("[+] globalImageCdnKey: " + globalImageCdnKey + " globalAesKey1: " + globalAesKey1 + " globalAesKey2: " + globalMd5Key); + } catch (e) { + console.log("[-] Memory access error at onEnter: " + e); + } + } + }) +} + +setImmediate(patchCdnOnComplete) + rpc.exports = { manualTrigger: manualTrigger, manualUpload: manualUpload diff --git a/onebot/receiver.js b/onebot/receiver.js index 668227a..35314f8 100644 --- a/onebot/receiver.js +++ b/onebot/receiver.js @@ -1,3 +1,9 @@ +var baseAddr = Process.getModuleByName("WeChat").base; +if (!baseAddr) { + console.error("[!] 找不到 WeChat 模块基址,请检查进程名。"); +} +console.log("[+] WeChat base address: " + baseAddr); + function setReceiver() { var buf2RespAddr = baseAddr.add(0x347BD44); console.log("[+] buf2RespAddr WeChat Base: " + baseAddr + "[+] Attaching to: " + buf2RespAddr); @@ -152,8 +158,4 @@ function generateAESKey() { key += chars.charAt(Math.floor(Math.random() * chars.length)); } return key; -} - -rpc.exports = { - manualTrigger: manualTrigger -}; \ No newline at end of file +} \ No newline at end of file diff --git a/onebot/script.js b/onebot/script.js index 012878e..d5f5c21 100644 --- a/onebot/script.js +++ b/onebot/script.js @@ -5,82 +5,172 @@ if (!baseAddr) { } console.log("[+] WeChat base address: " + baseAddr); -// 触发函数地址,不同版本的地址看wechat_version 中的json文件复制过来 +// -------------------------基础函数分区------------------------- +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; +} + + +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 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 patchString(addr, plainStr) { + const bytes = []; + for (let i = 0; i < plainStr.length; i++) { + bytes.push(plainStr.charCodeAt(i)); + } + + addr.writeByteArray(bytes); + addr.add(bytes.length).writeU8(0); +} + +function generateAESKey() { + const chars = 'abcdef0123456789'; + let key = ''; + for (let i = 0; i < 32; i++) { + key += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return key; +} + + +// -------------------------基础函数分区------------------------- + +// -------------------------全局变量分区------------------------- + +// 文本消息全局变量 +var sendTextFuncAddr = baseAddr.add(0x448A858); // 这个必须是绝对位置 +var protobufAddr = baseAddr.add(0x227EC70); +var patchTextProtobufAddr = baseAddr.add(0x227EC4C); +var PatchTextProtobufDeleteAddr = baseAddr.add(0x227EC88); +var textCgiAddr = ptr(0); +var sendTextMessageAddr = ptr(0); +var textMessageAddr = ptr(0); +var contentAddr = ptr(0); +var textProtoX1PayloadAddr = ptr(0); var sendMessageCallbackFunc = ptr(0); var messageCallbackFunc1 = baseAddr.add(0x7fa1050); -// 这个必须是绝对位置 + +// req2buf全局变量 var triggerX1Payload = ptr(0x175ED6600); -var triggerFuncAddr = baseAddr.add(0x448A858); var req2bufEnterAddr = baseAddr.add(0x34566C0); var req2bufExitAddr = baseAddr.add(0x34577D8); -var protobufAddr = baseAddr.add(0x227EC70); -var patchProtobufAddr = baseAddr.add(0x227EC4C); -var protobufDeleteAddr = baseAddr.add(0x227EC88); - -// 触发函数X0参数地址 -var globalMessagePtr = ptr(0); - -// 消息体的一些指针地址 -var cgiAddr = ptr(0); -var callBackFuncAddr = ptr(0); -var sendMessageAddr = ptr(0); -var messageAddr = ptr(0); - -var contentAddr = ptr(0); var insertMsgAddr = ptr(0); -var protoX1PayloadAddr = ptr(0); -var protoX1PayloadLen = 1024; +var sendMsgType = ""; -// 消息的taskId +// 图片消息全局变量 +var sendImgMessageCallbackFunc = ptr(0x0); +var imgMessageCallbackFunc1 = baseAddr.add(0x7fa0b18); +var imgProtobufAddr = baseAddr.add(0x2275BFC); +var patchImgProtobufFunc1 = baseAddr.add(0x2275BB8) +var patchImgProtobufFunc2 = baseAddr.add(0x2275BD8); +var imgProtobufDeleteAddr = baseAddr.add(0x2275C14); +var CndOnCompleteAddr = baseAddr.add(0x34154E0); + +var imgCgiAddr = ptr(0); +var sendImgMessageAddr = ptr(0); +var imgMessageAddr = ptr(0); +var imgProtoX1PayloadAddr = ptr(0); +var uploadGlobalX0 = ptr(0) +var uploadFunc1Addr = ptr(0) +var uploadFunc2Addr = ptr(0) +var imageIdAddr = ptr(0) +var md5Addr = ptr(0) +var uploadAesKeyAddr = ptr(0) +var ImagePathAddr1 = ptr(0) +var uploadImagePayload = ptr(0); + +var globalImageCdnKey = ""; +var globalAesKey1 = ""; +var globalMd5Key = ""; + +// 发送消息的全局变量 var taskIdGlobal = 0x20000090 // 最好比较大,不和原始的微信消息重复 var receiverGlobal = "wxid_" var contentGlobal = ""; +var senderGlobal = "wxid_" var lastSendTime = 0; -// 打印消息的地址,便于查询问题 -function printAddr() { - console.log("[+] Addresses:"); - console.log(" - cgiAddr: " + cgiAddr); - console.log(" - callBackFuncAddr: " + callBackFuncAddr); - console.log(" - sendMessageAddr: " + sendMessageAddr); - 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() { +function setupSendTextMessageDynamic() { 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); - + textCgiAddr = Memory.alloc(128); + sendTextMessageAddr = Memory.alloc(256); + textMessageAddr = Memory.alloc(256); + contentAddr = Memory.alloc(16); // 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, " "); + patchString(textCgiAddr, "/cgi-bin/micromsg-bin/newsendmsg"); + patchString(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 + // B. 构建 sendTextMessageAddr 结构体 (X24 基址位置) + sendTextMessageAddr.add(0x00).writeU64(0); + sendTextMessageAddr.add(0x08).writeU64(0); + sendTextMessageAddr.add(0x10).writePointer(sendMessageCallbackFunc); + sendTextMessageAddr.add(0x18).writeU64(1); + sendTextMessageAddr.add(0x20).writeU32(taskIdGlobal); + sendTextMessageAddr.add(0x28).writePointer(textMessageAddr); // 指向动态分配的 Message - console.log(" [+] sendMessageAddr Object: ", hexdump(sendMessageAddr, { + console.log(" [+] sendTextMessageAddr Object: ", hexdump(sendTextMessageAddr, { offset: 0, length: 48, header: true, @@ -88,47 +178,47 @@ function setupSendMessageDynamic() { })); // 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")); + textMessageAddr.add(0x00).writePointer(messageCallbackFunc1); + textMessageAddr.add(0x08).writeU32(taskIdGlobal); + textMessageAddr.add(0x0c).writeU32(0x20a); + textMessageAddr.add(0x10).writeU64(0x3); + textMessageAddr.add(0x18).writePointer(textCgiAddr); + textMessageAddr.add(0x20).writeU64(uint64("0x20")); - console.log(" [+] messageAddr Object: ", hexdump(messageAddr, { + console.log(" [+] textMessageAddr Object: ", hexdump(textMessageAddr, { offset: 0, length: 64, header: true, ansi: true })); - console.log("[+] Dynamic Memory Setup Complete. - Message Object: " + messageAddr); + console.log("[+] Dynamic Memory Setup Complete. - Message Object: " + textMessageAddr); } -setImmediate(setupSendMessageDynamic); +setImmediate(setupSendTextMessageDynamic); -function patchProtoBuf() { - Memory.patchCode(patchProtobufAddr, 4, code => { - const cw = new Arm64Writer(code, {pc: patchProtobufAddr}); +function patchTextProtoBuf() { + Memory.patchCode(patchTextProtobufAddr, 4, code => { + const cw = new Arm64Writer(code, {pc: patchTextProtobufAddr}); cw.putNop(); cw.flush(); }); - console.log("[+] Patching BL to NOP at " + patchProtobufAddr + " completed."); + console.log("[+] Patching BL to NOP at " + patchTextProtobufAddr + " completed."); - Memory.patchCode(protobufDeleteAddr, 4, code => { - const cw = new Arm64Writer(code, {pc: protobufDeleteAddr}); + Memory.patchCode(PatchTextProtobufDeleteAddr, 4, code => { + const cw = new Arm64Writer(code, {pc: PatchTextProtobufDeleteAddr}); cw.putNop(); cw.flush(); }); - console.log("[+] Patching BL DELETE to NOP at " + protobufDeleteAddr + " completed."); + console.log("[+] Patching BL DELETE to NOP at " + PatchTextProtobufDeleteAddr + " completed."); } -setImmediate(patchProtoBuf); +setImmediate(patchTextProtoBuf); -function manualTrigger(taskId, receiver, content) { +function triggerSendTextMessage(taskId, receiver, content) { console.log("[+] Manual Trigger Started..."); if (!taskId || !receiver || !content) { console.error("[!] taskId or Receiver or Content is empty!"); @@ -137,19 +227,13 @@ function manualTrigger(taskId, receiver, content) { // 获取当前时间戳 (秒) 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); + textMessageAddr.add(0x08).writeU32(taskIdGlobal); + sendTextMessageAddr.add(0x20).writeU32(taskIdGlobal); console.log("start init payload") @@ -207,21 +291,19 @@ function manualTrigger(taskId, receiver, content) { 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); + triggerX1Payload.add(0x18).writePointer(textCgiAddr); + sendMsgType = "text" console.log("finished init payload") - - const MMStartTask = new NativeFunction(triggerFuncAddr, 'int64', ['pointer']); + const MMStartTask = new NativeFunction(sendTextFuncAddr, 'int64', ['pointer']); // 5. 调用函数 try { // const arg1 = globalMessagePtr; // 第一个指针参数 const arg2 = triggerX1Payload; // 第二个参数 0x175ED6600 - console.log(`[+] Calling MMStartTask at ${triggerFuncAddr} with args: (${arg2})`); + console.log(`[+] Calling MMStartTask at ${sendTextFuncAddr} with args: (${arg2})`); const result = MMStartTask(arg2); console.log("[+] Execution MMStartTask Success. Return value: " + result); return "ok"; @@ -231,118 +313,11 @@ function manualTrigger(taskId, receiver, content) { } } - -// 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() { +// 拦截 SendTextProto 编码逻辑,注入自定义 Payload +function attachSendTextProto() { console.log("[+] proto注入拦截目标地址: " + protobufAddr); - protoX1PayloadAddr = Memory.alloc(protoX1PayloadLen); - console.log("[+] Frida 分配的 Payload 地址: " + protoX1PayloadAddr); + textProtoX1PayloadAddr = Memory.alloc(1024); + console.log("[+] Frida 分配的 Payload 地址: " + textProtoX1PayloadAddr); Interceptor.attach(protobufAddr, { onEnter: function (args) { @@ -384,13 +359,13 @@ function attachProto() { 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); + textProtoX1PayloadAddr.writeByteArray(finalPayload); console.log("[+] Payload 已写入,长度: " + finalPayload.length); - this.context.x1 = protoX1PayloadAddr; + this.context.x1 = textProtoX1PayloadAddr; this.context.x2 = ptr(finalPayload.length); - console.log("[+] 寄存器修改完成: X1=" + this.context.x1 + ", X2=" + this.context.x2, hexdump(protoX1PayloadAddr, { + console.log("[+] 寄存器修改完成: X1=" + this.context.x1 + ", X2=" + this.context.x2, hexdump(textProtoX1PayloadAddr, { offset: 0, length: 128, header: true, @@ -400,14 +375,522 @@ function attachProto() { }); } -function toVarint(n) { - let res = []; - while (n >= 128) { - res.push((n & 0x7F) | 0x80); // 取后7位,最高位置1 - n = n >> 7; // 右移7位 +setImmediate(attachSendTextProto); + + +rpc.exports = { + triggerSendTextMessage: triggerSendTextMessage +}; + +// -------------------------发送文本消息分区------------------------- + + + +// -------------------------Req2Buf公共部分分区------------------------- +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); + + + // todo 修改为判断是发送图片还是文本 + if (typeof sendTextMessageAddr !== 'undefined') { + if (sendMsgType === "text") { + insertMsgAddr.writePointer(sendTextMessageAddr); + console.log("[+] 发送文本消息成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendTextMessageAddr + + "[+] Req2Buf 写入后内存预览: " + insertMsgAddr); + } else if (sendMsgType === "img") { + insertMsgAddr.writePointer(sendImgMessageAddr); + console.log("[+] 发送图片消息成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendImgMessageAddr + + "[+] Req2Buf 写入后内存预览: " + insertMsgAddr); + } + } else { + console.error("[!] 错误: 变量 sendTextMessageAddr 未定义,请确保已运行分配逻辑。"); + } + } + }); + + // 在出口处拦截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 = ""; + senderGlobal = ""; + contentGlobal = ""; + send({ + type: "finish", + }) + } + }); +} + +setImmediate(attachReq2buf); + +// -------------------------Req2Buf公共部分分区------------------------- + +// -------------------------发送图片消息分区------------------------- + +// 初始化进行内存的分配 +function setupSendImgMessageDynamic() { + console.log("[+] Starting setupSendImgMessageDynamic Dynamic Message Patching..."); + + // 1. 动态分配内存块(按需分配大小) + // 分配原则:字符串给 64-128 字节,结构体按实际大小分配 + imgCgiAddr = Memory.alloc(128); + sendImgMessageAddr = Memory.alloc(256); + imgMessageAddr = Memory.alloc(512); + uploadFunc1Addr = Memory.alloc(24); + uploadFunc2Addr = Memory.alloc(24); + imageIdAddr = Memory.alloc(256); + md5Addr = Memory.alloc(256); + uploadAesKeyAddr = Memory.alloc(256); + ImagePathAddr1 = Memory.alloc(256); + uploadImagePayload = Memory.alloc(512); + + // A. 写入字符串内容 + patchString(imgCgiAddr, "/cgi-bin/micromsg-bin/uploadmsgimg"); + + // B. 构建 SendMessage 结构体 (X24 基址位置) + sendImgMessageAddr.add(0x00).writeU64(0); + sendImgMessageAddr.add(0x08).writeU64(0); + sendImgMessageAddr.add(0x10).writePointer(sendImgMessageCallbackFunc); + sendImgMessageAddr.add(0x18).writeU64(1); + sendImgMessageAddr.add(0x20).writeU32(taskIdGlobal); + sendImgMessageAddr.add(0x28).writePointer(imgMessageAddr); + + console.log(" [+] sendImgMessageAddr Object: ", hexdump(sendImgMessageAddr, { + offset: 0, + length: 48, + header: true, + ansi: true + })); + + // C. 构建 Message 结构体 + imgMessageAddr.add(0x00).writePointer(imgMessageCallbackFunc1); + imgMessageAddr.add(0x08).writeU32(taskIdGlobal); + imgMessageAddr.add(0x0c).writeU32(0x6e); + imgMessageAddr.add(0x10).writeU64(0x3); + imgMessageAddr.add(0x18).writePointer(imgCgiAddr); + imgMessageAddr.add(0x20).writeU64(0x22); + imgMessageAddr.add(0x28).writeU64(uint64("0x8000000000000030")); + imgMessageAddr.add(0x30).writeU64(uint64("0x0000000001010100")); + + console.log(" [+] Dynamic Memory Setup Complete. - Message Object: " + imgMessageAddr); + + + uploadFunc1Addr.writePointer(baseAddr.add(0x802b8b0)); + uploadFunc2Addr.writePointer(baseAddr.add(0x7fd5908)); +} + +setImmediate(setupSendImgMessageDynamic); + + +function patchProtoBuf() { + Memory.patchCode(patchImgProtobufFunc1, 4, code => { + const cw = new Arm64Writer(code, {pc: patchImgProtobufFunc1}); + cw.putNop(); + cw.flush(); + }); + + console.log("[+] Patching BL to NOP at " + patchImgProtobufFunc1 + " completed."); + + Memory.patchCode(patchImgProtobufFunc2, 4, code => { + const cw = new Arm64Writer(code, {pc: patchImgProtobufFunc2}); + cw.putNop(); + cw.flush(); + }); + + console.log("[+] Patching BL to NOP at " + patchImgProtobufFunc2 + " completed."); + + Memory.patchCode(imgProtobufDeleteAddr, 4, code => { + const cw = new Arm64Writer(code, {pc: imgProtobufDeleteAddr}); + cw.putNop(); + cw.flush(); + }); + + console.log("[+] Patching BL DELETE to NOP at " + imgProtobufDeleteAddr + " completed."); +} + +setImmediate(patchProtoBuf); + +function triggerSendImgMessage(taskId, sender, receiver) { + console.log("[+] Manual Trigger Started..."); + if (!taskId || !receiver) { + console.error("[!] taskId or Receiver or Content is empty!"); + return "fail"; } - res.push(n); // 最后一位最高位为0 - return res; + + // 获取当前时间戳 (秒) + const timestamp = Math.floor(Date.now() / 1000); + lastSendTime = timestamp + taskIdGlobal = taskId; + receiverGlobal = receiver; + senderGlobal = sender; + + imgMessageAddr.add(0x08).writeU32(taskIdGlobal); + sendImgMessageAddr.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 + 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 + 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 + ]; + triggerX1Payload.writeU32(taskIdGlobal); + triggerX1Payload.add(0x04).writeByteArray(payloadData); + triggerX1Payload.add(0x18).writePointer(imgCgiAddr); + sendMsgType = "img" + + console.log("finished init payload") + const MMStartTask = new NativeFunction(triggerFuncAddr, 'int64', ['pointer']); + + // 5. 调用函数 + try { + 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"; + } +} + + +// 拦截 Protobuf 编码逻辑,注入自定义 Payload +function attachProto() { + console.log("[+] proto注入拦截目标地址: " + imgProtoX1PayloadAddr); + imgProtoX1PayloadAddr = Memory.alloc(1024); + console.log("[+] Frida 分配的 Payload 地址: " + imgProtoX1PayloadAddr); + + Interceptor.attach(imgProtobufAddr, { + 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, 0x40, 0x0A, 0x01, 0x00] + const msgId = [0x10, 0xc6, 0xbc, 0x90, 0xb9, 0x08] // 时间戳 + const cpHeader = [0x1A, 0x10] + // m30c4674f5a0b9d + const cp = [0x6D, 0x33, 0x30, 0x63, 0x34, 0x36, 0x37, 0x34, 0x66, 0x35, 0x61, 0x30, 0x62, 0x39, 0x64, 0x30] + + 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, 0x30] + + + // 45872025384@chatroom_176787000_60_xwechat_1 只需要改这个时间戳就能重复发送 + const receiverMsgId = stringToHexArray(receiverGlobal).concat([0x5F]) + .concat(stringToHexArray(Math.floor(Date.now() / 1000).toString())) + .concat([0x5F, 0x31, 0x36, 0x30, 0x5F, 0x78, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x5F, 0x33]); + + // 0xb0, 0x02 是长度,需要看一下什么的长度 + const msgIdHeader = [0xb0, 0x02, 0x12, 0x2E, 0x0A, 0x2C] + + const senderHeader = [0x1A, senderGlobal.length + 2, 0x0A, senderGlobal.length]; + // wxid_xxxx 或者 chatroom + const sender = stringToHexArray(senderGlobal); + const receiverHeader = [0x22, receiverGlobal.length + 2, 0x0A, receiverGlobal.length] + // wxid_xxxx + const receiver = stringToHexArray(receiverGlobal) + 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, 0x32]; + + const html = [0x3C, + 0x6D, 0x73, 0x67, 0x73, 0x6F, 0x75, 0x72, // 0x30 msgsour + 0x63, 0x65, 0x3E, 0x3C, 0x61, 0x6C, 0x6E, 0x6F, // 0x38 ce>1 + 0x3C, 0x2F, 0x66, 0x72, 0x3E, 0x3C, 0x2F, 0x61, // 0x48 + ]; + + const cdnHeader = [0x58, 0x01, 0x60, 0x02, 0x68, 0x00, 0x7A, 0xB2, 0x01] + // 3057 开头的cdn key + const cdn = stringToHexArray(globalImageCdnKey); + + const cdn2Header = [0x82, 0x01, 0xB2, 0x01] + const cdn2 = stringToHexArray(globalImageCdnKey) + + const aesKeyHeader = [0x8A, 0x01, 0x20] + const aesKey = stringToHexArray(globalAesKey1) + + const randomId5 = [0x90, 0x01, 0x01, 0x98, 0x01, 0xFF, // 0x2C8 + 0x13, 0xA0, 0x01, 0xFF, 0x13] + + const cdn3Header = [0xAA, 0x01, 0xB2, 0x01] + const cdn3 = stringToHexArray(globalImageCdnKey) + + const randomId6 = [0xB0, 0x01, 0xF4, 0x0B] + const randomId7 = [0xB8, 0x01, 0x68] + const randomId8 = [0xC0, 0x01, 0x3A] + const aesKey1Header = [0xCA, 0x01, 0x20] + const aesKey1 = stringToHexArray(globalAesKey1) + const md5Header = [0xDA, 0x01, 0x20] + const me5Key = stringToHexArray(globalMd5Key) + + 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, md5Header, me5Key, randomId9, left0) + + console.log("[+] Payload 准备写入"); + imgProtoX1PayloadAddr.writeByteArray(finalPayload); + console.log("[+] Payload 已写入,长度: " + finalPayload.length); + + this.context.x1 = imgProtoX1PayloadAddr; + this.context.x2 = ptr(finalPayload.length); + + console.log("[+] 寄存器修改完成: X1=" + this.context.x1 + ", X2=" + this.context.x2, hexdump(imgProtoX1PayloadAddr, { + offset: 0, + length: 1024, + header: true, + ansi: true + })); + }, + }); } setImmediate(attachProto); + + + +function triggerUploadImg(receiver, md5, imagePath) { + const payload = [ + 0x20, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 函数 10802b8b0 的指针 + 0x00, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 函数 107fd5908 的指针 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x40 + 0xD0, 0x72, 0x20, 0x89, 0x0B, 0x00, 0x00, 0x00, // 图片id // 0x48 + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50 + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x78, 0x69, 0x64, 0x5F, 0x37, 0x77, 0x64, // 发送人 0x68 + 0x31, 0x65, 0x63, 0x65, 0x39, 0x39, 0x66, 0x37, + 0x69, 0x32, 0x31, 0x00, 0x00, 0x00, 0x00, 0x13, // 发送人id长度 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00, // 0x98 + 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // 0xa0 + 0xA0, 0xBE, 0x2D, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 某个aesid 7ea41d569f705357780968e9104284cf 0xa8 + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xb0 + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xb8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x55, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0xe0 图片地址 高清 /Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_ldftuhe36izg19_5e7d/temp/04ebaab7e3ea6050e26ff31d89cc121e/2026-01/Img/166_1768214492_hd.jpg + 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xe8 + 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 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 + 0x40, 0x54, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x110 图片地址 普清 /Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_ldftuhe36izg19_5e7d/temp/04ebaab7e3ea6050e26ff31d89cc121e/2026-01/Img/166_1768214492.jpg + 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x118 + 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x120 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x5D, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x140 图片地址 缩略图 /Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_ldftuhe36izg19_5e7d/temp/04ebaab7e3ea6050e26ff31d89cc121e/2026-01/Img/166_1768214492_thumb.jpg + 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x148 + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x150 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x158 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x160 + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x168 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x170 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x178 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x180 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x188 + 0x00, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00, // 0x190 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x198 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x1a0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1a8 + 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x0A, 0x0A, // 0x1b0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1b8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1c0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1c8 + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1d0 + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1d8 有个指针 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1e0 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1e8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1f0 + 0xD0, 0x78, 0x46, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0x1f8 某个key ecd57e9cf85f2e2087aee8c0fd1e445e + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x200 + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x208 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x210 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x218 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x220 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x228 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x230 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x238 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x240 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x248 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x250 + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x258 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x260 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x268 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x270 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x278 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x280 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0x288 + ] + + patchString(imageIdAddr, receiver + "_" + String(Math.floor(Date.now() / 1000)) + "_" + Math.floor(Math.random() * 1001) + "_1"); + patchString(md5Addr, md5) + patchString(uploadAesKeyAddr, generateAESKey()) + patchString(ImagePathAddr1, imagePath); + + uploadImagePayload.writeByteArray(payload); + uploadImagePayload.writePointer(uploadFunc1Addr); + uploadImagePayload.add(0x08).writePointer(uploadFunc2Addr); + uploadImagePayload.add(0x48).writePointer(imageIdAddr); + uploadImagePayload.add(0x68).writeUtf8String(receiver); + uploadImagePayload.add(0xa8).writePointer(md5Addr); + uploadImagePayload.add(0xe0).writePointer(ImagePathAddr1); + uploadImagePayload.add(0x110).writePointer(ImagePathAddr1); + uploadImagePayload.add(0x140).writePointer(ImagePathAddr1); + uploadImagePayload.add(0x1f8).writePointer(uploadAesKeyAddr); + + + const targetAddr = baseAddr.add(0x45DC834); + const startC2cUpload = new NativeFunction(targetAddr, 'int64', ['pointer', 'pointer']); + + console.log("开始手动触发 C2C 上传..."); + const result = startC2cUpload(uploadGlobalX0, uploadImagePayload); + console.log("调用结果: " + result); +} + +function attachUploadMedia() { + const targetAddr = baseAddr.add(0x45DC85C); + Interceptor.attach(targetAddr, { + onEnter: function (args) { + console.log("[+] enter UploadMedia"); + uploadGlobalX0 = this.context.x0; + console.log("UploadMedia x1: " + uploadGlobalX0); + } + }) +} + +setImmediate(attachUploadMedia); + +function patchCdnOnComplete() { + Interceptor.attach(CndOnCompleteAddr, { + onEnter: function (args) { + console.log("[+] enter CndOnCompleteAddr"); + + try { + const x2 = this.context.x2; + globalImageCdnKey = x2.add(0x60).readPointer().readUtf8String(); + globalAesKey1 = x2.add(0x78).readPointer().readUtf8String(); + globalMd5Key = x2.add(0x90).readPointer().readUtf8String(); + console.log("[+] globalImageCdnKey: " + globalImageCdnKey + " globalAesKey1: " + globalAesKey1 + " globalAesKey2: " + globalMd5Key); + } catch (e) { + console.log("[-] Memory access error at onEnter: " + e); + } + } + }) +} + +setImmediate(patchCdnOnComplete) + + +rpc.exports = { + triggerSendImgMessage: triggerSendImgMessage, + triggerUploadImg: triggerUploadImg +}; + +// -------------------------发送图片消息分区------------------------- diff --git a/onebot/text.js b/onebot/text.js new file mode 100644 index 0000000..8289941 --- /dev/null +++ b/onebot/text.js @@ -0,0 +1,409 @@ +// 1. 获取微信主模块的基地址 +var baseAddr = Process.getModuleByName("WeChat").base; +if (!baseAddr) { + console.error("[!] 找不到 WeChat 模块基址,请检查进程名。"); +} +console.log("[+] WeChat base address: " + baseAddr); + +// -------------------------基础函数分区------------------------- +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; +} + + +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 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 patchString(addr, plainStr) { + const bytes = []; + for (let i = 0; i < plainStr.length; i++) { + bytes.push(plainStr.charCodeAt(i)); + } + + addr.writeByteArray(bytes); + addr.add(bytes.length).writeU8(0); +} +// -------------------------基础函数分区------------------------- + +// -------------------------全局变量分区------------------------- + +// 文本消息全局变量 +var sendTextFuncAddr = baseAddr.add(0x448A858); // 这个必须是绝对位置 +var protobufAddr = baseAddr.add(0x227EC70); +var patchTextProtobufAddr = baseAddr.add(0x227EC4C); +var PatchTextProtobufDeleteAddr = baseAddr.add(0x227EC88); +var textCgiAddr = ptr(0); +var sendTextMessageAddr = ptr(0); +var textMessageAddr = ptr(0); +var contentAddr = ptr(0); +var insertTextMsgAddr = ptr(0); +var textProtoX1PayloadAddr = ptr(0); +var sendMessageCallbackFunc = ptr(0); +var messageCallbackFunc1 = baseAddr.add(0x7fa1050); + + +// req2buf全局变量 +var triggerX1Payload = ptr(0x175ED6600); +var req2bufEnterAddr = baseAddr.add(0x34566C0); +var req2bufExitAddr = baseAddr.add(0x34577D8); + +// 图片消息全局变量 + + +// 发送消息的全局变量 +var taskIdGlobal = 0x20000090 // 最好比较大,不和原始的微信消息重复 +var receiverGlobal = "wxid_" +var contentGlobal = ""; +var senderGlobal = "wxid_" +var lastSendTime = 0; + +// -------------------------全局变量分区------------------------- + + +// -------------------------发送文本消息分区------------------------- + + +// 初始化进行内存的分配 +function setupSendTextMessageDynamic() { + console.log("[+] Starting Dynamic Message Patching..."); + + // 1. 动态分配内存块(按需分配大小) + // 分配原则:字符串给 64-128 字节,结构体按实际大小分配 + textCgiAddr = Memory.alloc(128); + sendTextMessageAddr = Memory.alloc(256); + textMessageAddr = Memory.alloc(256); + contentAddr = Memory.alloc(16); + + // A. 写入字符串内容 + patchString(textCgiAddr, "/cgi-bin/micromsg-bin/newsendmsg"); + patchString(contentAddr, " "); + + // B. 构建 sendTextMessageAddr 结构体 (X24 基址位置) + sendTextMessageAddr.add(0x00).writeU64(0); + sendTextMessageAddr.add(0x08).writeU64(0); + sendTextMessageAddr.add(0x10).writePointer(sendMessageCallbackFunc); + sendTextMessageAddr.add(0x18).writeU64(1); + sendTextMessageAddr.add(0x20).writeU32(taskIdGlobal); + sendTextMessageAddr.add(0x28).writePointer(textMessageAddr); // 指向动态分配的 Message + + console.log(" [+] sendTextMessageAddr Object: ", hexdump(sendTextMessageAddr, { + offset: 0, + length: 48, + header: true, + ansi: true + })); + + // C. 构建 Message 结构体 + textMessageAddr.add(0x00).writePointer(messageCallbackFunc1); + textMessageAddr.add(0x08).writeU32(taskIdGlobal); + textMessageAddr.add(0x0c).writeU32(0x20a); + textMessageAddr.add(0x10).writeU64(0x3); + textMessageAddr.add(0x18).writePointer(textCgiAddr); + textMessageAddr.add(0x20).writeU64(uint64("0x20")); + + console.log(" [+] textMessageAddr Object: ", hexdump(textMessageAddr, { + offset: 0, + length: 64, + header: true, + ansi: true + })); + + console.log("[+] Dynamic Memory Setup Complete. - Message Object: " + textMessageAddr); +} + +setImmediate(setupSendTextMessageDynamic); + + +function patchTextProtoBuf() { + Memory.patchCode(patchTextProtobufAddr, 4, code => { + const cw = new Arm64Writer(code, {pc: patchTextProtobufAddr}); + cw.putNop(); + cw.flush(); + }); + + console.log("[+] Patching BL to NOP at " + patchTextProtobufAddr + " completed."); + + Memory.patchCode(PatchTextProtobufDeleteAddr, 4, code => { + const cw = new Arm64Writer(code, {pc: PatchTextProtobufDeleteAddr}); + cw.putNop(); + cw.flush(); + }); + + console.log("[+] Patching BL DELETE to NOP at " + PatchTextProtobufDeleteAddr + " completed."); +} + +setImmediate(patchTextProtoBuf); + +function triggerSendTextMessage(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); + lastSendTime = timestamp + taskIdGlobal = taskId; + receiverGlobal = receiver; + contentGlobal = content; + + 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 + 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 + 0x0A, 0x02, 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(textCgiAddr); + + console.log("finished init payload") + + const MMStartTask = new NativeFunction(sendTextFuncAddr, 'int64', ['pointer']); + + // 5. 调用函数 + try { + // const arg1 = globalMessagePtr; // 第一个指针参数 + const arg2 = triggerX1Payload; // 第二个参数 0x175ED6600 + console.log(`[+] Calling MMStartTask at ${sendTextFuncAddr} 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"; + } +} + +// 拦截 SendTextProto 编码逻辑,注入自定义 Payload +function attachSendTextProto() { + console.log("[+] proto注入拦截目标地址: " + protobufAddr); + textProtoX1PayloadAddr = Memory.alloc(1024); + console.log("[+] Frida 分配的 Payload 地址: " + textProtoX1PayloadAddr); + + 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; + } + console.log("[+] 正在注入 Protobuf Payload..."); + + const type = [0x08, 0x01, 0x12] + const receiverHeader = [0x0A, receiverGlobal.length + 2, 0x0A, receiverGlobal.length]; + const receiverProto = stringToHexArray(receiverGlobal); + const contentProto = stringToHexArray(contentGlobal); + const contentHeader = [0x12, ...toVarint(contentProto.length)]; + const tsHeader = [0x18, 0x01, 0x20]; + const tsBytes = getVarintTimestampBytes(); + 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>1 + 0x3C, 0x2F, 0x66, 0x72, 0x3E, 0x3C, 0x2F, 0x61, // 0x48 . + ]; + + const valueLen = toVarint(receiverHeader.length + receiverProto.length + contentHeader.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); + + console.log("[+] Payload 准备写入"); + textProtoX1PayloadAddr.writeByteArray(finalPayload); + console.log("[+] Payload 已写入,长度: " + finalPayload.length); + + this.context.x1 = textProtoX1PayloadAddr; + this.context.x2 = ptr(finalPayload.length); + + console.log("[+] 寄存器修改完成: X1=" + this.context.x1 + ", X2=" + this.context.x2, hexdump(textProtoX1PayloadAddr, { + offset: 0, + length: 128, + header: true, + ansi: true + })); + }, + }); +} + +setImmediate(attachSendTextProto); + + +rpc.exports = { + triggerSendTextMessage: triggerSendTextMessage +}; + +// -------------------------发送文本消息分区------------------------- + + + +// -------------------------Req2Buf公共部分分区------------------------- +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; + insertTextMsgAddr = x24_base.add(0x60); + console.log("[+] 当前 Req2Buf X24 基址: " + x24_base); + + + // todo 修改为判断是发送图片还是文本 + if (typeof sendTextMessageAddr !== 'undefined') { + insertTextMsgAddr.writePointer(sendTextMessageAddr); + console.log("[+] 成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendTextMessageAddr + + "[+] Req2Buf 写入后内存预览: " + insertTextMsgAddr); + } else { + console.error("[!] 错误: 变量 sendTextMessageAddr 未定义,请确保已运行分配逻辑。"); + } + } + }); + + // 在出口处拦截req2buf,把insertTextMsgAddr设置为0,避免被垃圾回收导致整个程序崩溃 + console.log("[+] Target Req2buf leave Address: " + req2bufExitAddr); + Interceptor.attach(req2bufExitAddr, { + onEnter: function (args) { + if (!this.context.x25.equals(taskIdGlobal)) { + return; + } + insertTextMsgAddr.writeU64(0x0); + console.log("[+] 清空写入后内存预览: " + insertTextMsgAddr.readPointer()); + taskIdGlobal = 0; + receiverGlobal = ""; + senderGlobal = ""; + contentGlobal = ""; + send({ + type: "finish", + }) + } + }); +} + +setImmediate(attachReq2buf); + +// -------------------------Req2Buf公共部分分区------------------------- +