// 下载文件上传函数 - 基于视频上传,0x98位置从0x04改为0x05 // 获取微信主模块的基地址 var moduleName = "wechat.dylib"; var baseAddr = Process.findModuleByName(moduleName).base; if (!baseAddr) { console.error("[!] 找不到 WeChat 模块基址,请检查进程名。"); } console.log("[+] WeChat base address: " + baseAddr); // 全局变量 var uploadFileX1 = ptr(0); var fileIdAddr = ptr(0); var md5Addr = ptr(0) var uploadAesKeyAddr = ptr(0); var filePathAddr1 = ptr(0); var uploadGlobalX0 = ptr(0); var uploadFunc1Addr = ptr(0); var uploadFunc2Addr = ptr(0); var uploadCallback = ptr(0); var uploadImageAddr = baseAddr.add(0x4ad95c0); var uploadGetCallbackWrapperAddr = baseAddr.add(0x4aa7084); var uploadGetCallbackWrapperFuncAddr = baseAddr.add(0x37C874C); var uploadOnCompleteAddr = baseAddr.add(0x4AA7680); var uploadOnCompleteFuncAddr = baseAddr.add(0x37C9930); var cndOnCompleteAddr = baseAddr.add(0x37c8f00); var fileCallbackFuncAddr = baseAddr.add(0x2544584); var fileProtobufAddr = fileCallbackFuncAddr.add(0x50); var patchFileProtobufFunc1 = fileCallbackFuncAddr.add(0x10); var patchFileProtobufFunc1Byte; var patchFileProtobufFunc2 = fileCallbackFuncAddr.add(0x30); var patchFileProtobufFunc2Byte; var fileProtobufDeleteAddr = fileCallbackFuncAddr.add(0x6c); var fileProtobufDeleteAddrByte; var fileMessageCallbackFunc1 = baseAddr.add(0x892DEF8) var sendFuncAddr = baseAddr.add(0x4992040); var req2bufEnterAddr = baseAddr.add(0x380b950); var req2bufExitAddr = baseAddr.add(0x380CA64); var fileCgiAddr2 = ptr(0); var sendFileMessageAddr = ptr(0); var fileMessageAddr = ptr(0); var fileProtoX1PayloadAddr = ptr(0); var triggerX1Payload; var triggerX0; var insertMsgAddr = ptr(0); var sendMsgType = ""; var taskIdGlobal = 0x0 var receiverGlobal = "wxid_" var contentGlobal = ""; var senderGlobal = "wxid_" var lastSendTime = 0; const fileCp = generateBytes(16) // 文件上传完成队列 var fileUploadQueue = []; function getFileUploadInfo() { if (fileUploadQueue.length > 0) { return fileUploadQueue.shift(); } return null; } function pushFileUploadInfo(info) { fileUploadQueue.push(info); console.log("[+] 文件上传信息已入队,当前队列长度:", fileUploadQueue.length); } function setupUploadFileDynamic() { fileIdAddr = Memory.alloc(256); filePathAddr1 = Memory.alloc(256); uploadFileX1 = Memory.alloc(1024); uploadFunc1Addr = Memory.alloc(24); uploadFunc2Addr = Memory.alloc(24); uploadCallback = Memory.alloc(128); md5Addr = Memory.alloc(128); uploadAesKeyAddr = Memory.alloc(128); // 发送文件消息相关内存分配 fileCgiAddr2 = Memory.alloc(128); sendFileMessageAddr = Memory.alloc(256); fileMessageAddr = Memory.alloc(256); fileProtoX1PayloadAddr = Memory.alloc(4096); triggerX1Payload = Memory.alloc(1024); patchString(fileCgiAddr2, "/cgi-bin/micromsg-bin/sendappmsg"); sendFileMessageAddr.add(0x00).writeU64(0); sendFileMessageAddr.add(0x08).writeU64(0); sendFileMessageAddr.add(0x10).writeU64(0); sendFileMessageAddr.add(0x18).writeU64(1); sendFileMessageAddr.add(0x20).writeU32(taskIdGlobal); sendFileMessageAddr.add(0x28).writePointer(fileMessageAddr); fileMessageAddr.add(0x00).writePointer(fileMessageCallbackFunc1); fileMessageAddr.add(0x08).writeU32(taskIdGlobal); fileMessageAddr.add(0x0c).writeU32(0x20a); fileMessageAddr.add(0x10).writeU64(0x3); fileMessageAddr.add(0x18).writePointer(fileCgiAddr2); fileMessageAddr.add(0x20).writeU64(uint64("0x20")); patchFileProtobufFunc1Byte = patchFileProtobufFunc1.readByteArray(4); patchFileProtobufFunc2Byte = patchFileProtobufFunc2.readByteArray(4); fileProtobufDeleteAddrByte = fileProtobufDeleteAddr.readByteArray(4); console.log("[+] setupUploadFileDynamic Complete."); } setImmediate(setupUploadFileDynamic); function triggerUploadFile(receiver, md5, filePath) { 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, 0x0B, 0x00, 0x00, 0x00, // 0x40 0xD0, 0x72, 0x20, 0x89, 0x0B, 0x00, 0x00, 0x00, // 文件id // 0x48 0x26, 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, 0x05, 0x00, 0x00, 0x00, // 0x98 ← 这里从0x04改成0x05 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // 0xa0 0xA0, 0xBE, 0x2D, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 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 文件地址 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 文件地址 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 文件地址 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, 0x04, 0x00, 0xE0, 0x03, // 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 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, 0x01, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x290 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x298 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2a0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2a8 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2b0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2b8 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2c0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2c8 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2d0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2d8 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2e0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0x2e8 ] patchString(fileIdAddr, receiver + "_" + String(Math.floor(Date.now() / 1000)) + "_" + Math.floor(Math.random() * 1001) + "_1"); patchString(md5Addr, md5) patchString(uploadAesKeyAddr, generateAESKey()) patchString(filePathAddr1, filePath); uploadFileX1.writeByteArray(payload); uploadFileX1.writePointer(uploadFunc1Addr); uploadFileX1.add(0x08).writePointer(uploadFunc2Addr); uploadFileX1.add(0x48).writePointer(fileIdAddr); uploadFileX1.add(0x68).writeUtf8String(receiver); uploadFileX1.add(0xa8).writePointer(md5Addr); uploadFileX1.add(0xe0).writePointer(filePathAddr1); uploadFileX1.add(0x110).writePointer(filePathAddr1); uploadFileX1.add(0x140).writePointer(filePathAddr1); uploadFileX1.add(0x1f8).writePointer(uploadAesKeyAddr); const startUploadMedia = new NativeFunction(uploadImageAddr, 'int64', ['pointer', 'pointer']); console.log("开始手动触发文件上传 X0 " + uploadGlobalX0 + ", X1: " + uploadFileX1 + hexdump(uploadFileX1, { offset: 0, length: 256, header: true, ansi: true })); const result = startUploadMedia(uploadGlobalX0, uploadFileX1); console.log("文件上传调用结果: " + result); } // -------------------------上传相关Attach逻辑------------------------- function attachUploadFileMedia() { Interceptor.attach(uploadImageAddr.add(0x10), { onEnter: function (args) { uploadGlobalX0 = this.context.x0; const selfId = this.context.x1.add(0x68).readUtf8String(); const filePath = this.context.x1.add(0xe0).readPointer().readUtf8String(); send({ type: "upload", self_id: selfId, }) console.log("UploadFileMedia x0: " + uploadGlobalX0 + " filePath: " + filePath + " selfId: " + selfId); } }) } setImmediate(attachUploadFileMedia); function patchFileCdnOnComplete() { Interceptor.attach(cndOnCompleteAddr, { onEnter: function (args) { try { const x2 = this.context.x2; const currentFileId = x2.add(0x20).readPointer().readUtf8String(); const fileId = fileIdAddr.readUtf8String(); if (currentFileId !== fileId) { console.log("[-] FileCdnOnComplete x2: " + x2 + " currentFileId: " + currentFileId + " fileId: " + fileId); return; } const cdnKey = x2.add(0x60).readPointer().readUtf8String(); const aesKey = x2.add(0x78).readPointer().readUtf8String(); const md5Key = x2.add(0x90).readPointer().readUtf8String(); const videoId = x2.add(0xf0).readPointer().readUtf8String(); const targetId = x2.add(0x40).readUtf8String(); console.log("File X2: " + x2 + "[+] cdnKey: " + cdnKey + " aesKey: " + aesKey + " md5Key: " + md5Key + " videoId:" + videoId); send({ type: "finish", }); if (cdnKey !== "" && cdnKey != null && aesKey !== "" && aesKey != null && md5Key !== "" && md5Key != null) { pushFileUploadInfo({ cdnKey: cdnKey, aesKey: aesKey, md5Key: md5Key, targetId: targetId }); send({ type: "upload_file_finish", target_id: targetId, cdn_key: cdnKey, aes_key: aesKey, md5_key: md5Key }); } else { console.error("cdnKey or aesKey or md5key 为空") } } catch (e) { console.log("[-] File Memory access error at onEnter: " + e); } } }) } setImmediate(patchFileCdnOnComplete); function attachFileGetCallbackFromWrapper() { Interceptor.attach(uploadGetCallbackWrapperAddr, { onEnter: function (args) { const tmpFileId = this.context.x1.readPointer().readUtf8String(); const fileId = fileIdAddr.readUtf8String(); if (tmpFileId !== fileId) { console.log("[+] File GetCallbackFromWrapper tmpFileId: " + tmpFileId + " fileId: " + fileId); return; } uploadCallback.add(0x10).writePointer(uploadGetCallbackWrapperFuncAddr); this.context.x8 = uploadCallback; console.log("[+] File GetCallbackFromWrapper x8: " + this.context.x8); } }) Interceptor.attach(uploadOnCompleteAddr, { onEnter: function (args) { const tmpFileId = this.context.x1.readPointer().readUtf8String(); const fileId = fileIdAddr.readUtf8String(); if (tmpFileId !== fileId) { console.log("[+] File OnComplete tmpFileId: " + tmpFileId + " fileId: " + fileId); return; } uploadCallback.add(0x30).writePointer(uploadOnCompleteFuncAddr); this.context.x8 = uploadCallback; console.log("[+] File OnComplete x8: " + this.context.x8); } }) } setImmediate(attachFileGetCallbackFromWrapper); // -------------------------上传相关Attach逻辑结束------------------------- // -------------------------Req2Buf公共部分分区------------------------- function attachReq2buf() { console.log("[+] Target Req2buf enter Address: " + req2bufEnterAddr); Interceptor.attach(req2bufEnterAddr, { onEnter: function (args) { if (!this.context.x1.equals(taskIdGlobal)) { return; } console.log("[+] 已命中目标Req2Buf taskId:" + taskIdGlobal + " base:" + baseAddr); const x24_base = this.context.x24; insertMsgAddr = x24_base.add(0x60); console.log("[+] 当前 Req2Buf X24 基址: " + x24_base); if (sendMsgType === "file") { insertMsgAddr.writePointer(sendFileMessageAddr); console.log("[+] 发送文件消息成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendFileMessageAddr + " Req2Buf 写入后内存预览: " + insertMsgAddr); } } }); 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); function AttachSendProto() { Interceptor.attach(sendFuncAddr, { onEnter: function (args) { if (triggerX1Payload) { return } triggerX0 = this.context.x0; triggerX1Payload = this.context.x1; console.log(`[+] 捕获到 StartTask 调用,X0地址:${triggerX0}, Payload 地址: ${triggerX1Payload}`); } }) } setImmediate(AttachSendProto); // -------------------------Req2Buf公共部分分区结束------------------------- // -------------------------PatchFileProtoBuf分区------------------------- function patchFileProtoBuf() { Interceptor.attach(fileCallbackFuncAddr, { onEnter: function (args) { var firstValue = this.context.sp.add(0x10).readU32(); console.log("[+] 捕获到 FileCallbackFunc 调用,firstValue:", firstValue, "taskIdGlobal:", taskIdGlobal); if (firstValue === taskIdGlobal) { if (patchFileProtobufFunc1.readU32() !== 3573751839) { Memory.patchCode(patchFileProtobufFunc1, 4, code => { const cw = new Arm64Writer(code, {pc: patchFileProtobufFunc1}); cw.putNop(); cw.flush(); }); Memory.patchCode(patchFileProtobufFunc2, 4, code => { const cw = new Arm64Writer(code, {pc: patchFileProtobufFunc2}); cw.putNop(); cw.flush(); }); Memory.patchCode(fileProtobufDeleteAddr, 4, code => { const cw = new Arm64Writer(code, {pc: fileProtobufDeleteAddr}); cw.putNop(); cw.flush(); }); } } else { if (patchFileProtobufFunc1.readU32() === 3573751839) { Memory.patchCode(patchFileProtobufFunc1, 4, code => { const cw = new Arm64Writer(code, {pc: patchFileProtobufFunc1}); cw.putBytes(new Uint8Array(patchFileProtobufFunc1Byte)); cw.flush(); }); Memory.patchCode(patchFileProtobufFunc2, 4, code => { const cw = new Arm64Writer(code, {pc: patchFileProtobufFunc2}); cw.putBytes(new Uint8Array(patchFileProtobufFunc2Byte)); cw.flush(); }); Memory.patchCode(fileProtobufDeleteAddr, 4, code => { const cw = new Arm64Writer(code, {pc: fileProtobufDeleteAddr}); cw.putBytes(new Uint8Array(fileProtobufDeleteAddrByte)); cw.flush(); }); } } } }) } setImmediate(patchFileProtoBuf); // -------------------------PatchFileProtoBuf分区结束------------------------- // -------------------------发送文件消息分区------------------------- function triggerSendFileMessage(taskId, sender, receiver) { console.log("[+] File Manual Trigger 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; fileMessageAddr.add(0x08).writeU32(taskIdGlobal); sendFileMessageAddr.add(0x20).writeU32(taskIdGlobal); const payloadData = [ 0x0A, 0x02, 0x00, 0x00, // 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08 0x03, 0x00, 0x00, 0x00, 0x01, 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 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(fileCgiAddr2); triggerX1Payload.add(0xb8).writePointer(triggerX1Payload.add(0xc0)); triggerX1Payload.add(0x190).writePointer(triggerX1Payload.add(0x198)); sendMsgType = "file" console.log("finished init file 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"; } } function attachFileProto() { Interceptor.attach(fileProtobufAddr, { onEnter: function (args) { var currTaskId = this.context.sp.add(0x30).readU32(); if (currTaskId !== taskIdGlobal) { console.log(`[+] 文件拦截到非目标 currTaskId: ${currTaskId} taskIdGlobal: ${taskIdGlobal}`); return; } // 从文件队列获取上传信息 const fileUploadInfo = getFileUploadInfo(); let cdnKey = ""; let aesKey = ""; let md5Key = ""; let targetId = ""; let fileName = ""; let fileSize = ""; let appId = ""; let fileExt = ""; let fileUploadToken = ""; if (fileUploadInfo) { cdnKey = fileUploadInfo.cdnKey; aesKey = fileUploadInfo.aesKey; md5Key = fileUploadInfo.md5Key; targetId = fileUploadInfo.targetId; fileName = fileUploadInfo.fileName || ""; fileSize = fileUploadInfo.fileSize || "0"; appId = fileUploadInfo.appId || "wx6618f1cfc6c132f8"; fileExt = fileUploadInfo.fileExt || ""; fileUploadToken = fileUploadInfo.fileUploadToken || ""; } else { console.error("[!] 无法获取文件上传信息"); return; } // --- 构建 protobuf --- // Field 1: Header (0x0A, len=0x40) const type = [0x0A, 0x40, 0x0A, 0x01, 0x00] const msgId = [0x10].concat(generateRandom5ByteVarint()) const cpHeader = [0x1A, 0x10] const randomId = [0x20, 0x9D, 0xB0, 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 sysEnd = [0x30, 0xF8, 0x01] // Field 2: Message body // sender (field 1) const senderBytes = stringToHexArray(senderGlobal); const senderHeader = [0x0A, senderBytes.length]; // appid (field 2) const appIdBytes = stringToHexArray(appId); const appIdHeader = [0x12, appIdBytes.length]; // field 3 = 0 const field3 = [0x18, 0x00]; // receiver (field 4) const receiverBytes = stringToHexArray(targetId); const receiverHeader = [0x22, receiverBytes.length]; // type = 6 (file) (field 5) const msgType = [0x28, 0x06]; // 构建 appmsg XML (field 6) const cdnAttachUrl = cdnKey.replace(/_[^_]*$/, ''); const appmsgXml = '' + '' + fileName + '' + '' + '' + '6' + '0' + '0' + '' + '' + '' + '' + '0' + '' + '' + '' + '' + '' + '' + '' + '' + '' + fileSize + '' + '' + cdnKey + '' + '' + '' + fileExt + '' + '' + cdnAttachUrl + '' + '' + aesKey + '' + '0' + '' + Math.floor(Date.now() * 1000000 + Math.random() * 1000000).toString() + '' + (fileUploadToken ? '' + fileUploadToken + '' : '') + '' + '' + '' + '' + '' + '' + md5Key + '' + '' + ''; const appmsgBytes = stringToHexArray(appmsgXml); // fromusername 紧跟在 appmsg XML 后面 const fromUsernameXml = '' + senderGlobal + ''; const fromUsernameBytes = stringToHexArray(fromUsernameXml); // 合并 appmsg content 和 fromusername const contentBytes = appmsgBytes.concat(fromUsernameBytes); const contentHeader = [0x32].concat(toVarint(contentBytes.length)); // timestamp (field 7) const tsHeader = [0x38]; const tsBytes = getVarintTimestampBytes(); // msgid string (field 8): wxid_xxx_timestamp_3_xwechat_9 const receiverMsgId = stringToHexArray(targetId).concat([0x5F]) .concat(stringToHexArray(Math.floor(Date.now() / 1000).toString())) .concat([0x5F, 0x33, 0x5F, 0x78, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x5F, 0x39]); // _3_xwechat_9 const msgIdHeader2 = [0x42, receiverMsgId.length]; // field 10: varint = 1 const field10 = [0x50, 0x01]; // field 12: msgsource XML const msgsourceXml = '12'; const msgsourceBytes = stringToHexArray(msgsourceXml); const msgsourceHeader = [0x62, msgsourceBytes.length]; // empty fields const field13 = [0x6A, 0x00]; const field14 = [0x72, 0x00]; const field15 = [0x7A, 0x00]; // 构建 field 2 内部数据 const field2Inner = senderHeader.concat(senderBytes, appIdHeader, appIdBytes, field3, receiverHeader, receiverBytes, msgType, contentHeader, contentBytes, tsHeader, tsBytes, msgIdHeader2, receiverMsgId, field10, msgsourceHeader, msgsourceBytes, field13, field14, field15); // 构建完整 field 2 const field2HeaderBytes = [0x12].concat(toVarint(field2Inner.length)); // md5 trailing field: 0x2A 0x20 + 32 bytes md5 const md5Trailing = [0x2A, 0x20].concat(stringToHexArray(md5Key)); // field 9: varint = 1 const field9 = [0x48, 0x01]; // field 10 (second): file size varint const fileSizeVarint = toVarint(parseInt(fileSize)); const field10b = [0x50].concat(fileSizeVarint); // field 11: varint = 2 const field11 = [0x58, 0x02]; // 构建最终 payload const finalPayload = type.concat(msgId, cpHeader, fileCp, randomId, sysHeader, sys, sysEnd, field2HeaderBytes, field2Inner, md5Trailing, field9, field10b, field11); fileProtoX1PayloadAddr.writeByteArray(finalPayload); console.log("[+] 文件Payload 已写入,长度: " + finalPayload.length); this.context.x1 = fileProtoX1PayloadAddr; this.context.x2 = ptr(finalPayload.length); console.log("[+] 文件寄存器修改完成: X1=" + this.context.x1 + ", X2=" + this.context.x2, hexdump(fileProtoX1PayloadAddr, { offset: 0, length: Math.min(finalPayload.length, 512), header: true, ansi: true })); }, }); } setImmediate(attachFileProto); // -------------------------发送文件消息分区结束------------------------- rpc.exports = { triggerUploadFile: triggerUploadFile, triggerSendFileMessage: triggerSendFileMessage, }; 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 toVarint(n) { let res = []; while (n >= 128) { res.push((n & 0x7F) | 0x80); n = n >> 7; } res.push(n); return res; } 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; } function generateBytes(n) { // 生成随机字符串 const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < n; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return stringToHexArray(result); }