Files
wechat_chatter/frida/receiver.js
T
2026-05-09 14:07:15 +08:00

635 lines
26 KiB
JavaScript

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(/<atuserlist>([\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("<?xml version=\"1.0\"?><msg><img")) {
messages.push({type: "image", data: {text: part}});
} else if (part.startsWith("<msg><voicemsg")) {
messages.push({type: "record", data: {text: part}});
} else if (part.startsWith("<?xml version=\"1.0\"?><msg><appmsg")) {
const regex = /<type>(.*?)<\/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("<msg><emoji")) {
messages.push({type: "face", data: {text: part}});
} else if (part.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
messages.push({type: "video", data: {text: part}});
} else if (part.startsWith("<sysmsg") || part.startsWith("<?xml version=\"1.0\"?><sysmsg")) {
messages.push({type: "sys", data: {text: part}});
} else {
messages.push({type: "text", data: {text: part}});
}
}
} else {
if (content.startsWith("<?xml version=\"1.0\"?><msg><img")) {
messages.push({type: "image", data: {text: content}});
} else if (content.startsWith("<msg><voicemsg")) {
const audioStart = mediaContent.indexOf(2);
if (audioStart !== -1) {
mediaContent = mediaContent.subarray(audioStart);
}
messages.push({type: "record", data: {text: content, media: Array.from(mediaContent)}});
} else if (content.startsWith("<?xml version=\"1.0\"?><msg><appmsg")) {
const regex = /<type>(.*?)<\/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("<msg><emoji")) {
messages.push({type: "face", data: {text: content}});
} else if (content.startsWith("<?xml version=\"1.0\"?><msg><videomsg")) {
messages.push({type: "video", data: {text: content}});
} else if (content.startsWith("<sysmsg") || content.startsWith("<?xml version=\"1.0\"?><sysmsg")) {
messages.push({type: "sys", data: {text: content}});
} else {
messages.push({type: "text", data: {text: content}});
}
}
return messages;
}
function getProtobufRawBytes(pBuffer, scanSize) {
const tags = [0x12, 0x1A, 0x2A, 0x42, 0x52, 0x5A];
let uint8Array;
try {
const mem = pBuffer.readByteArray(scanSize);
if (!mem) return [];
uint8Array = new Uint8Array(mem);
} catch (e) {
console.error("读取内存失败: " + e);
return [];
}
let finalResults = [];
let i = 0x1a;
tags.forEach(targetTag => {
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);
}