var targetPath = "/Applications/WeChat.app/Contents/Resources/wechat.dylib"; var module = Process.enumerateModules().find(function(m) { return m.path === targetPath; }); const baseAddr = module.base if (!baseAddr) { console.error("[!] 找不到 WeChat 模块基址,请检查进程名。"); } console.log("[+] WeChat base address: " + baseAddr); var buf2RespAddr = baseAddr.add(0x39FAC00) var downloadImagAddr = baseAddr.add(0x4E6F32C) // image_download var downloadFileAddr = baseAddr.add(0x4E0E264) // c2c_download var downloadVideoAddr = baseAddr.add(0x4E28044) // hdvideo_streaming var startDownloadMedia = baseAddr.add(0x4d4c838) var downloadGlobalX0; var downloadFileX1 = ptr(0) var fileIdAddr = ptr(0) var fileMd5Addr = ptr(0) var downloadAesKeyAddr = ptr(0) var filePathAddr = ptr(0) var fileCdnUrlAddr = ptr(0) // -------------------------接收消息分区------------------------- function setupDownloadFileDynamic() { downloadFileX1 = Memory.alloc(1624) fileIdAddr = Memory.alloc(128) fileMd5Addr = Memory.alloc(128) downloadAesKeyAddr = Memory.alloc(128) filePathAddr = Memory.alloc(256) fileCdnUrlAddr = Memory.alloc(256) } setImmediate(setupDownloadFileDynamic) function setReceiver() { Interceptor.attach(buf2RespAddr, { onEnter: function (args) { const currentPtr = this.context.x20; if (currentPtr.add(0).readU8() !== 0x08) { return } const x2 = this.context.x0.toInt32(); const fields = getProtobufRawBytes(currentPtr, x2) const sender = fields[0] const receiver = fields[1] const content = fields[2] const mediaContent = fields[3] const xml = fields[4] const userContent = fields[5] const msgId = protobufVarintToNumberString(fields[6]) if (typeof sender !== "string" || sender === "" || typeof receiver !== "string" || receiver === "" || typeof content !== "string" || content === "" || typeof msgId !== "string" || msgId === "") { return; } console.log(" [+] currentPtr: ", hexdump(currentPtr, { offset: 0, length: x2, header: true, ansi: true })); var selfId = receiver var msgType = "private" var groupId = "" var senderUser = sender var senderNickname = "" var messages = getMessages(content, sender, mediaContent); if (sender.includes("@chatroom")) { msgType = "group" groupId = sender let splitIndex = content.indexOf(':') const sendUserStart = content.indexOf('wxid_') senderUser = content.substring(sendUserStart, splitIndex).trim(); const atUserMatch = xml.match(/([\s\S]*?)<\/atuserlist>/); const atUser = atUserMatch ? atUserMatch[1] : null; if (atUser) { atUser.split(',').forEach(atUser => { atUser = atUser.trim(); if (atUser) { messages.push({type: "at", data: {qq: atUser}}); } }); } // 处理用户的名称 splitIndex = userContent?.indexOf(':') if (splitIndex === -1) { splitIndex = userContent?.indexOf('在群聊中@了你') !== -1 ? userContent?.indexOf('在群聊中@了你') : userContent?.indexOf('在群聊中发了一段语') senderNickname = userContent?.substring(0, splitIndex).trim(); } else { senderNickname = userContent?.substring(0, splitIndex).trim(); } if (!senderNickname) { senderNickname = senderUser } } else { // 处理用户的名称 const splitIndex = userContent?.indexOf(':') senderNickname = userContent?.substring(0, splitIndex).trim(); if (!senderNickname) { senderNickname = senderUser } } send({ time: Date.now(), post_type: "message", message_type: msgType, user_id: senderUser, // 发送人的 ID self_id: selfId, // 接收人的 ID group_id: groupId, // 群 ID message_id: msgId, type: "send", raw: {peerUid: msgId}, message: messages, sender: {user_id: senderUser, nickname: senderNickname}, msgsource: xml, raw_message: content, show_content: userContent }) }, }); Interceptor.attach(startDownloadMedia, { onEnter: function (args) { downloadGlobalX0 = this.context.x0; var fileIDAddr = this.context.x1.add(0x40).readPointer(); var fileId = fileIDAddr?.readUtf8String(); const t = this.context.x1.add(0xA0).readU32() console.log(" [+] download file: ", fileId, " type", t); if (t === 3) { if (fileId.endsWith("_1")) { this.context.x1.add(0xA0).writeU32(0x02); } if (fileId.endsWith("_31")) { this.context.x1.add(0xA0).writeU32(0x04); } } } }) Interceptor.attach(downloadFileAddr, { onEnter: function (args) { var dataPtr = this.context.x22; var dataLen = this.context.x2.toInt32(); var fileId = this.context.x19.add(0x2E0).readPointer().readUtf8String(); var cdnUrl = this.context.x19.add(0x2F8).readPointer().readUtf8String(); if (dataLen > 0) { var buffer = dataPtr.readByteArray(dataLen); var uint8Array = new Uint8Array(buffer); console.log("file", uint8Array.length, fileId, cdnUrl) } } }); Interceptor.attach(downloadImagAddr, { onEnter: function (args) { var dataPtr = this.context.x22; var dataLen = this.context.x2.toInt32(); var fileId = this.context.x19.add(0x2E0).readPointer().readUtf8String(); var cdnUrl = this.context.x19.add(0x2F8).readPointer().readUtf8String(); if (dataLen > 0) { var buffer = dataPtr.readByteArray(dataLen); var uint8Array = new Uint8Array(buffer); console.log("image", uint8Array.length, fileId, cdnUrl) } } }); Interceptor.attach(downloadVideoAddr, { onEnter: function (args) { var dataPtr = this.context.x20.add(0x178).readPointer(); var dataLen = this.context.x23.toInt32(); var fileId = this.context.x19.add(0x2E0).readPointer().readUtf8String(); var cdnUrl = this.context.x19.add(0x2F8).readPointer().readUtf8String(); if (dataLen > 0) { var buffer = dataPtr.readByteArray(dataLen); var uint8Array = new Uint8Array(buffer); console.log("video", uint8Array.length, fileId, cdnUrl) // send({ // type: "download", // media: Array.from(uint8Array), // file_id: fileId, // cdn_url: cdnUrl, // }) } } }); } // 使用 setImmediate 确保在模块加载后执行 setImmediate(setReceiver) function getMessages(content, sender, mediaContent) { var messages = []; if (sender.includes("@chatroom")) { let splitIndex = content.indexOf(':') let pureContent = content.substring(splitIndex + 1).trim(); const parts = pureContent.split('\u2005'); for (let part of parts) { part = part.trim(); if (part.startsWith("(.*?)<\/type>/s; const match = part.match(regex); if (match.length > 1) { switch (match[1]) { case "5": messages.push({type: "share", data: {text: part}}); break case "6": messages.push({type: "file", data: {text: part}}); break } } } else if (part.startsWith("(.*?)<\/type>/s; const match = content.match(regex); if (match.length > 1) { switch (match[1]) { case "5": messages.push({type: "share", data: {text: content}}); break case "6": messages.push({type: "file", data: {text: content}}); break } } } else if (content.startsWith(" { let found = false; for (; i < uint8Array.length; i++) { if (uint8Array[i] === targetTag) { // 1. 解析 Varint 长度 (支持 1-5 字节长度标识) let length = 0; let shift = 0; let bytesReadForLen = 0; i = i + 1; let lenNum = 0; while (i < uint8Array.length) { let b = uint8Array[i]; length |= (b & 0x7F) << shift; bytesReadForLen++; i++; lenNum++; if (!(b & 0x80)) break; shift += 7; } // 2. 截取原始 Byte 数据 if (i + length <= uint8Array.length) { let addNum = 0 if (targetTag === 0x12 || targetTag === 0x1A || targetTag === 0x2A) { addNum = lenNum + 1; } let rawData = uint8Array.slice(i + addNum, i + length); if (targetTag === 0x42) { finalResults.push(rawData); } else { finalResults.push(getCleanString(rawData)); } i += length; } else { finalResults.push(null); // 长度越界 } found = true; break; // 找到第一个匹配的 Tag 就跳出 } } if (!found) finalResults.push(null); // 未找到该 Tag }); for (; i < uint8Array.length; i++) { if (uint8Array[i] === 0x60 && i + 10 <= uint8Array.length) { finalResults.push(uint8Array.slice(i + 1, i + 10)) } } return finalResults; } function getCleanString(uint8Array) { var out = ""; var i = 0; var len = uint8Array.length; while (i < len) { var c = uint8Array[i++]; // 1. 处理单字节 (ASCII: 0xxxxxxx) if (c < 0x80) { // 只保留可见字符 (Space 32 到 ~ 126) if (c >= 32 && c <= 126) { out += String.fromCharCode(c); } } // 2. 处理双字节 (110xxxxx 10xxxxxx) else if ((c & 0xE0) === 0xC0 && i < len) { var c2 = uint8Array[i++]; if ((c2 & 0xC0) === 0x80) { // 这种通常是特殊拉丁字母等,按需保留 var charCode = ((c & 0x1F) << 6) | (c2 & 0x3F); out += String.fromCharCode(charCode); } else { i--; } } // 3. 处理三字节 (1110xxxx 10xxxxxx 10xxxxxx) -> 绝大多数汉字在此 else if ((c & 0xF0) === 0xE0 && i + 1 < len) { var c2 = uint8Array[i++]; var c3 = uint8Array[i++]; if ((c2 & 0xC0) === 0x80 && (c3 & 0xC0) === 0x80) { var charCode = ((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F); if ( (charCode >= 0x4E00 && charCode <= 0x9FA5) || // 基本汉字 (charCode >= 0x3000 && charCode <= 0x303F) || // 常用中文标点 (。,、) (charCode >= 0xFF00 && charCode <= 0xFFEF) || // 全角符号/标点 (!:?) (charCode >= 0x2000 && charCode <= 0x206F) || // 常用标点扩展 (含 \u2005) (charCode >= 0x3400 && charCode <= 0x4DBF) // 扩展 A 区汉字 ) { out += String.fromCharCode(charCode); } } else { i -= 2; } } else if ((c & 0xF8) === 0xF0 && i + 2 < len) { var c2 = uint8Array[i++]; var c3 = uint8Array[i++]; var c4 = uint8Array[i++]; if ((c2 & 0xC0) === 0x80 && (c3 & 0xC0) === 0x80 && (c4 & 0xC0) === 0x80) { // 计算 Unicode 码点 var codePoint = ((c & 0x07) << 18) | ((c2 & 0x3F) << 12) | ((c3 & 0x3F) << 6) | (c4 & 0x3F); // Emoji 范围通常在 U+1F000 到 U+1F9FF 之间 if (codePoint >= 0x1F000 && codePoint <= 0x1FADF) { // 使用 fromCodePoint 处理 4 字节字符 out += String.fromCodePoint(codePoint); } } else { i -= 3; } } } return out; } function protobufVarintToNumberString(uint8Array) { let result = BigInt(0); let shift = BigInt(0); for (let i = 0; i < uint8Array?.length; i++) { const byte = uint8Array[i]; // 1. 取出低 7 位并累加到结果中 // (BigInt(byte & 0x7F) << shift) result += BigInt(byte & 0x7F) << shift; // 2. 检查最高位 (MSB)。如果为 0,说明这个数字结束了 if ((byte & 0x80) === 0) { return result.toString(); } // 3. 准备处理下一个 7 位 shift += BigInt(7); } return result.toString(); } 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); } // -----------------------辅助函数----------------------- // fileType: HdImage => 1,Image => 2, humbImage => 3, Video => 4, File => 5, function triggerDownload(receiver, cdnUrl, aesKey, filePath, fileType) { const downloadMediaPayload = [ 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, // 0x10 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x30 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xB6, 0x4C, 0xFC, 0x0A, 0x00, 0x00, 0x00, // 0x40 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x10, 0x4B, 0xFA, 0x0A, 0x00, 0x00, 0x00, // 0x58 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xF0, 0xB3, 0x4C, 0xFC, 0x0A, 0x00, 0x00, 0x00, // 0x70 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0xC4, 0x2D, 0xFE, 0x0A, 0x00, 0x00, 0x00, // 0x88 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x98 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // 0xa0 0x00, 0x00, 0x00, 0x00, 0x01, 0xAA, 0xAA, 0xAA, // 0xa8 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xb0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xc0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xd0 0x01, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xf0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x100 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, // 0x128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x148 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, 0x02, 0x00, 0x00, 0xAA, 0xAA, 0xAA, // 0x170 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, // 0x180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // 0x1a0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 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, 0x22, 0x1A, 0xFE, 0x0A, 0x00, 0x00, 0x00, // 0x1d0 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, // 0x1f0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x200 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, 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, 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, // 0x288 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x4F, 0x56, 0xFC, 0x0A, 0x00, 0x00, 0x00, // 0x2c0 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x300 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, // 0x318 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, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x340 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, 0x0A, 0x00, 0x00, 0x00, // 0x378 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0A, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, // 0x3e0 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, ]; patchString(fileIdAddr, receiver + "_" + String(Math.floor(Date.now() / 1000)) + "_" + Math.floor(Math.random() * 1001) + "_1"); patchString(fileCdnUrlAddr, cdnUrl) patchString(downloadAesKeyAddr, aesKey) patchString(filePathAddr, filePath); downloadFileX1.writeByteArray(downloadMediaPayload); downloadFileX1.add(0x40).writePointer(fileIdAddr); downloadFileX1.add(0x58).writePointer(fileCdnUrlAddr); downloadFileX1.add(0x70).writePointer(downloadAesKeyAddr); downloadFileX1.add(0x88).writePointer(filePathAddr); downloadFileX1.add(0xa0).writeU32(fileType); const startDwMedia = new NativeFunction(startDownloadMedia, 'int64', ['pointer', 'pointer']); const result = startDwMedia(downloadGlobalX0, downloadFileX1); console.log("下载调用结果: " + result); return result; } rpc.exports = { triggerDownload: triggerDownload, }; // -----------------------测试函数------------------------- function testGetProtobufRawBytes() { const rawMemoryData = []; const pBuffer = { // 模拟指针读取内存返回 ArrayBuffer readByteArray: function (size) { // 返回模拟数据的 ArrayBuffer 副本 const slice = rawMemoryData.slice(0, size); const ab = new ArrayBuffer(slice.length); const view = new Uint8Array(ab); for (let i = 0; i < slice.length; i++) view[i] = slice[i]; return ab; } }; const results = getProtobufRawBytes(pBuffer, rawMemoryData.length); console.log(results); }