diff --git a/frida/receiver.js b/frida/receiver.js index 30553d6..200fa68 100644 --- a/frida/receiver.js +++ b/frida/receiver.js @@ -23,15 +23,23 @@ function setReceiver() { } const x2 = this.context.x2.toInt32(); + console.log(" [+] currentPtr: ", hexdump(currentPtr, { + offset: 0, + length: x2, + header: true, + ansi: true + })); const fields = getProtobufRawBytes(currentPtr, x2) const sender = fields[0] const receiver = fields[1] const content = fields[2] - const xml = fields[3] - const userContent = fields[4] + const mediaContent = fields[3] + const xml = fields[4] + const userContent = fields[5] + const msgId = protobufVarintToNumberString(fields[6]) - if (sender === "" || receiver === "" || content === "" || xml === "") { + if (sender === "" || receiver === "" || content === "") { console.log("字段缺失,无法解析 sender:" + sender + " receiver:" + receiver + hexdump(currentPtr, { length: x2, header: true, @@ -76,23 +84,30 @@ function setReceiver() { } // 处理用户的名称 - splitIndex = userContent.indexOf(':') + splitIndex = userContent?.indexOf(':') if (splitIndex === -1) { - splitIndex = userContent.indexOf('在群聊中@了你') - senderNickname = userContent.substring(0, splitIndex).trim(); + splitIndex = userContent?.indexOf('在群聊中@了你') + senderNickname = userContent?.substring(0, splitIndex).trim(); } else { - senderNickname = userContent.substring(0, splitIndex).trim(); + senderNickname = userContent?.substring(0, splitIndex).trim(); + } + if (!senderNickname) { + senderNickname = sender } } else { // 处理用户的名称 const splitIndex = userContent?.indexOf(':') - senderNickname = userContent.substring(0, splitIndex).trim(); + senderNickname = userContent?.substring(0, splitIndex).trim(); + if (!senderNickname) { + senderNickname = sender + } messages.push({type: "text", data: {text: content}}); } - const msgId = generateAESKey() send({ + time: Date.now(), + post_type: "message", message_type: msgType, user_id: senderUser, // 发送人的 ID self_id: selfId, // 接收人的 ID @@ -102,17 +117,22 @@ function setReceiver() { raw: {peerUid: msgId}, message: messages, sender: {user_id: senderUser, nickname: senderNickname}, + msgsource: xml, + raw_message: content, + // media: mediaContent, + show_content:userContent }) }, }); } + // 使用 setImmediate 确保在模块加载后执行 setImmediate(setReceiver) function getProtobufRawBytes(pBuffer, scanSize) { - const tags = [0x12, 0x1A, 0x2A, 0x52, 0x5A]; + const tags = [0x12, 0x1A, 0x2A, 0x42, 0x52, 0x5A]; let uint8Array; try { @@ -149,7 +169,11 @@ function getProtobufRawBytes(pBuffer, scanSize) { // 2. 截取原始 Byte 数据 if (i + length <= uint8Array.length) { let rawData = uint8Array.slice(i, i + length); - finalResults.push(getCleanString(rawData)); + if (targetTag === 0x42) { + finalResults.push(rawData); + } else { + finalResults.push(getCleanString(rawData)); + } i += length; } else { finalResults.push(null); // 长度越界 @@ -162,6 +186,13 @@ function getProtobufRawBytes(pBuffer, scanSize) { 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; } @@ -187,6 +218,8 @@ function getCleanString(uint8Array) { // 这种通常是特殊拉丁字母等,按需保留 var charCode = ((c & 0x1F) << 6) | (c2 & 0x3F); out += String.fromCharCode(charCode); + } else { + i--; } } // 3. 处理三字节 (1110xxxx 10xxxxxx 10xxxxxx) -> 绝大多数汉字在此 @@ -204,6 +237,8 @@ function getCleanString(uint8Array) { ) { out += String.fromCharCode(charCode); } + } else { + i -= 2; } } else if ((c & 0xF8) === 0xF0 && i + 2 < len) { var c2 = uint8Array[i++]; @@ -218,47 +253,84 @@ function getCleanString(uint8Array) { // 使用 fromCodePoint 处理 4 字节字符 out += String.fromCodePoint(codePoint); } + } else { + i -= 3; } } } return out; } -function generateAESKey() { - const chars = 'abcdef0123456789'; - let key = ''; - for (let i = 0; i < 32; i++) { - key += chars.charAt(Math.floor(Math.random() * chars.length)); +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 key; + + return result.toString(); } + + // -----------------------测试函数------------------------- function testGetProtobufRawBytes() { const rawMemoryData = [ - 0x08, 0x00, 0x12, 0xb6, 0x02, 0x08, 0x01, 0x12, 0xb1, 0x02, 0x08, 0x05, 0x12, 0xac, 0x02, 0x08, - 0xa6, 0x02, 0x12, 0xa6, 0x02, 0x08, 0xec, 0xfc, 0xb9, 0x96, 0x03, 0x12, 0x15, 0x0a, 0x13, 0x77, - 0x78, 0x69, 0x64, 0x5f, 0x35, 0x79, 0x72, 0x74, 0x6c, 0x79, 0xf6, 0x33, 0x32, 0x68, 0x39, 0x75, - 0x31, 0x32, 0x1a, 0x08, 0x0a, 0x06, 0x6b, 0x74, 0x73, 0x6b, 0x74, 0x73, 0x20, 0x01, 0x2a, 0x0d, - 0x0a, 0x0b, 0x6a, 0x64, 0x6a, 0x78, 0x68, 0x64, 0x62, 0x62, 0x64, 0x64, 0x20, 0x30, 0x03, 0x38, - 0x01, 0x42, 0x02, 0x08, 0x00, 0x48, 0xaa, 0xdb, 0xff, 0xcc, 0x06, 0x52, 0xc7, 0x01, 0x3c, 0x6d, - 0x73, 0x67, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3e, 0x0a, 0x09, 0x3c, 0x62, 0x69, 0x7a, 0x66, - 0x6c, 0x61, 0x67, 0x3e, 0x30, 0x3c, 0x2f, 0x62, 0x69, 0x7a, 0x66, 0x6c, 0x61, 0x67, 0x3e, 0x0a, - 0x09, 0x3c, 0x70, 0x75, 0x61, 0x3e, 0x31, 0x3c, 0x2f, 0x70, 0x75, 0x61, 0x3e, 0x0a, 0x09, 0x3c, - 0x65, 0x67, 0x67, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x3e, 0x31, 0x3c, 0x2f, 0x65, - 0x67, 0x67, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x3e, 0x0a, 0x09, 0x3c, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x3e, 0x4e, 0x30, 0x5f, 0x56, 0x31, 0x5f, 0x78, 0x6e, - 0x68, 0x6b, 0x4e, 0x65, 0x68, 0x4b, 0x7c, 0x76, 0x31, 0x5f, 0x42, 0x4b, 0x71, 0x2b, 0x62, 0x45, - 0x36, 0x31, 0x3c, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x3e, 0x0a, 0x09, - 0x3c, 0x74, 0x6d, 0x70, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x3e, 0x0a, 0x09, 0x09, 0x3c, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x69, 0x64, 0x3e, 0x3c, 0x2f, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x69, 0x64, 0x3e, 0x3c, 0x2f, 0x70, 0x75, 0x62, 0x6c, + 0x08, 0x00, 0x12, 0xdc, 0x0f, 0x08, 0x01, 0x12, 0xd7, 0x0f, 0x08, 0x05, 0x12, 0xd2, 0x0f, 0x08, + 0xcc, 0x0f, 0x12, 0xcc, 0x0f, 0x08, 0x85, 0xa0, 0xd5, 0xe8, 0x04, 0x12, 0x15, 0x0a, 0x13, 0x77, + 0x78, 0x69, 0x64, 0x5f, 0x37, 0x77, 0x64, 0x31, 0x65, 0x63, 0x65, 0x39, 0x39, 0x66, 0x37, 0x69, + 0x32, 0x31, 0x1a, 0x15, 0x0a, 0x13, 0x77, 0x78, 0x69, 0x64, 0x5f, 0x6c, 0x64, 0x66, 0x74, 0x75, + 0x68, 0x65, 0x33, 0x36, 0x69, 0x7a, 0x67, 0x31, 0x39, 0x20, 0x22, 0x2a, 0xe6, 0x03, 0x0a, 0xe3, + 0x03, 0x3c, 0x6d, 0x73, 0x67, 0x3e, 0x3c, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x6d, 0x73, 0x67, 0x20, + 0x65, 0x6e, 0x64, 0x66, 0x6c, 0x61, 0x67, 0x3d, 0x22, 0x31, 0x22, 0x20, 0x63, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x66, 0x6c, 0x61, 0x67, 0x3d, 0x22, 0x30, 0x22, 0x20, 0x66, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x66, 0x6c, 0x61, 0x67, 0x3d, 0x22, 0x30, 0x22, 0x20, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x3d, 0x22, 0x34, 0x22, 0x20, 0x76, 0x6f, 0x69, 0x63, 0x65, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x31, 0x30, 0x30, 0x22, 0x20, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x32, 0x35, 0x32, 0x22, 0x20, 0x62, 0x75, 0x66, 0x69, + 0x64, 0x3d, 0x22, 0x30, 0x22, 0x20, 0x61, 0x65, 0x73, 0x6b, 0x65, 0x79, 0x3d, 0x22, 0x37, 0x30, + 0x66, 0x34, 0x31, 0x36, 0x37, 0x36, 0x63, 0x38, 0x31, 0x31, 0x34, 0x33, 0x62, 0x66, 0x33, 0x61, + 0x38, 0x38, 0x36, 0x62, 0x33, 0x33, 0x38, 0x32, 0x33, 0x30, 0x62, 0x37, 0x38, 0x37, 0x22, 0x20, + 0x76, 0x6f, 0x69, 0x63, 0x65, 0x75, 0x72, 0x6c, 0x3d, 0x22, 0x33, 0x30, 0x35, 0x32, 0x30, 0x32, + 0x30, 0x31, 0x30, 0x30, 0x30, 0x34, 0x34, 0x62, 0x33, 0x30, 0x34, 0x39, 0x30, 0x32, 0x30, 0x31, + 0x30, 0x30, 0x30, 0x32, 0x30, 0x34, 0x66, 0x36, 0x35, 0x63, 0x39, 0x63, 0x65, 0x30, 0x30, 0x32, + 0x30, 0x33, 0x32, 0x66, 0x38, 0x30, 0x32, 0x39, 0x30, 0x32, 0x30, 0x34, 0x32, 0x35, 0x66, 0x38, + 0x33, 0x64, 0x62, 0x37, 0x30, 0x32, 0x30, 0x34, 0x36, 0x39, 0x61, 0x31, 0x33, 0x38, 0x65, 0x30, + 0x30, 0x34, 0x32, 0x34, 0x33, 0x38, 0x36, 0x31, 0x36, 0x34, 0x36, 0x33, 0x33, 0x35, 0x33, 0x36, + 0x36, 0x36, 0x33, 0x33, 0x32, 0x64, 0x33, 0x37, 0x33, 0x30, 0x33, 0x33, 0x36, 0x36, 0x32, 0x64, + 0x33, 0x34, 0x36, 0x36, 0x36, 0x36, 0x33, 0x30, 0x32, 0x64, 0x33, 0x39, 0x33, 0x38, 0x33, 0x36, + 0x36, 0x34, 0x32, 0x64, 0x33, 0x30, 0x36, 0x32, 0x33, 0x33, 0x36, 0x32, 0x36, 0x32, 0x36, 0x32, + 0x36, 0x32, 0x33, 0x31, 0x33, 0x32, 0x33, 0x38, 0x33, 0x38, 0x33, 0x37, 0x30, 0x32, 0x30, 0x34, + 0x30, 0x31, 0x31, 0x38, 0x30, 0x30, 0x30, 0x66, 0x30, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, 0x34, + 0x30, 0x30, 0x39, 0x61, 0x38, 0x35, 0x33, 0x65, 0x64, 0x61, 0x22, 0x20, 0x76, 0x6f, 0x69, 0x63, + 0x65, 0x6d, 0x64, 0x35, 0x3d, 0x22, 0x22, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x6d, 0x73, + 0x67, 0x69, 0x64, 0x3d, 0x22, 0x34, 0x39, 0x37, 0x35, 0x30, 0x66, 0x31, 0x61, 0x35, 0x31, 0x35, + 0x61, 0x37, 0x64, 0x30, 0x35, 0x35, 0x63, 0x35, 0x66, 0x31, 0x38, 0x34, 0x32, 0x64, 0x32, 0x66, + 0x66, 0x39, 0x37, 0x34, 0x30, 0x77, 0x78, 0x69, 0x64, 0x5f, 0x6c, 0x64, 0x66, 0x74, 0x75, 0x68, + 0x65, 0x33, 0x36, 0x69, 0x7a, 0x67, 0x31, 0x39, 0x5f, 0x32, 0x33, 0x36, 0x5f, 0x31, 0x37, 0x37, + 0x32, 0x31, 0x37, 0x33, 0x35, 0x33, 0x35, 0x22, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x75, 0x73, 0x65, + 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x22, 0x77, 0x78, 0x69, 0x64, 0x5f, 0x37, 0x77, 0x64, 0x31, + 0x65, 0x63, 0x65, 0x39, 0x39, 0x66, 0x37, 0x69, 0x32, 0x31, 0x22, 0x20, 0x2f, 0x3e, 0x3c, 0x2f, + 0x6d, 0x73, 0x67, 0x3e, 0x30, 0x03, 0x38, 0x01, 0x42, 0xea, 0x09, 0x08, 0xe4, 0x09, 0x12, 0xe4 ]; const pBuffer = { // 模拟指针读取内存返回 ArrayBuffer - readByteArray: function(size) { + readByteArray: function (size) { // 返回模拟数据的 ArrayBuffer 副本 const slice = rawMemoryData.slice(0, size); const ab = new ArrayBuffer(slice.length); diff --git a/go_al/go.mod b/go_al/go.mod deleted file mode 100644 index 2e465a2..0000000 --- a/go_al/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/yincongcyincong/weixin-macos/go_al - -go 1.25.0 diff --git a/onebot/script.js b/onebot/script.js index 4921ddb..b42ce61 100644 --- a/onebot/script.js +++ b/onebot/script.js @@ -83,7 +83,7 @@ function generateAESKey() { } function getProtobufRawBytes(pBuffer, scanSize) { - const tags = [0x12, 0x1A, 0x2A, 0x52, 0x5A]; + const tags = [0x12, 0x1A, 0x2A, 0x42, 0x52, 0x5A]; let uint8Array; try { @@ -120,7 +120,11 @@ function getProtobufRawBytes(pBuffer, scanSize) { // 2. 截取原始 Byte 数据 if (i + length <= uint8Array.length) { let rawData = uint8Array.slice(i, i + length); - finalResults.push(getCleanString(rawData)); + if (targetTag === 0x42) { + finalResults.push(rawData); + } else { + finalResults.push(getCleanString(rawData)); + } i += length; } else { finalResults.push(null); // 长度越界 @@ -158,6 +162,8 @@ function getCleanString(uint8Array) { // 这种通常是特殊拉丁字母等,按需保留 var charCode = ((c & 0x1F) << 6) | (c2 & 0x3F); out += String.fromCharCode(charCode); + } else { + i--; } } // 3. 处理三字节 (1110xxxx 10xxxxxx 10xxxxxx) -> 绝大多数汉字在此 @@ -175,6 +181,8 @@ function getCleanString(uint8Array) { ) { out += String.fromCharCode(charCode); } + } else { + i -= 2; } } else if ((c & 0xF8) === 0xF0 && i + 2 < len) { var c2 = uint8Array[i++]; @@ -189,6 +197,8 @@ function getCleanString(uint8Array) { // 使用 fromCodePoint 处理 4 字节字符 out += String.fromCodePoint(codePoint); } + } else { + i -= 3; } } } @@ -1070,13 +1080,6 @@ function setReceiver() { Interceptor.attach(buf2RespAddr, { onEnter: function (args) { const currentPtr = this.context.x1; - // console.log(" [+] currentPtr: ", hexdump(currentPtr, { - // offset: 0, - // length: 512, - // header: true, - // ansi: true - // })); - let start = 0x1e; let senderLen = currentPtr.add(start).readU8(); if (senderLen !== 0x14 && senderLen !== 0x13) { @@ -1088,15 +1091,22 @@ function setReceiver() { } const x2 = this.context.x2.toInt32(); + // console.log(" [+] currentPtr: ", hexdump(currentPtr, { + // offset: 0, + // length: x2, + // header: true, + // ansi: true + // })); const fields = getProtobufRawBytes(currentPtr, x2) const sender = fields[0] const receiver = fields[1] const content = fields[2] - const xml = fields[3] - const userContent = fields[4] + const mediaContent = fields[3] + const xml = fields[4] + const userContent = fields[5] - if (sender === "" || receiver === "" || content === "" || xml === "") { + if (sender === "" || receiver === "" || content === "") { console.log("字段缺失,无法解析 sender:" + sender + " receiver:" + receiver + hexdump(currentPtr, { length: x2, header: true, @@ -1116,8 +1126,8 @@ function setReceiver() { msgType = "group" groupId = sender - let splitIndex = content?.indexOf(':') - let pureContent = content?.substring(splitIndex + 1).trim(); + let splitIndex = content.indexOf(':') + let pureContent = content.substring(splitIndex + 1).trim(); const parts = pureContent.split('\u2005'); for (let part of parts) { part = part.trim(); @@ -1175,7 +1185,10 @@ function setReceiver() { raw: {peerUid: msgId}, message: messages, sender: {user_id: senderUser, nickname: senderNickname}, - raw_message: "" + msgsource: xml, + raw_message: content, + // media: mediaContent, + show_content:userContent }) }, });