splite file

This commit is contained in:
yincong
2026-01-15 13:51:53 +08:00
parent cb0e6bb708
commit 74c62b3ec1
4 changed files with 264 additions and 501 deletions
+58 -90
View File
@@ -1,103 +1,71 @@
// ====== 配置:你想 Hook 的 WeChat 函数偏移 ======
const WECHAT_OFFSETS = [
0x4565b2c,
0x4566f1c,
0x4564860,
0x4591ff8,
0x45d09cc,
0x45cebbc,
0x4591fa0,
0x4581834,
0x4581760,
0x4387d08,
0x4334e88,
0x4328ebc,
0x4384764,
0x43811e8
];
// ====== 统一安全打印函数 ======
function safePrintRegister(args) {
for (let i = 0; i < 8; i++) {
try {
console.log(`x${i}: ${args[i]}`);
} catch (e) {
console.log(`x${i}: <无法读取> (${e})`);
}
}
}
function safePrintBacktrace(context) {
function dumpMemoryToHex(ptr, size) {
try {
let bt = Thread.backtrace(
context,
Backtracer.FUZZY // 更稳定,遇到系统函数更不容易崩
).map(DebugSymbol.fromAddress)
.join("\n");
// 1. 读取内存数据
const buffer = ptr.readByteArray(size);
const data = new Uint8Array(buffer);
console.log("\n--- 调用堆栈 ---");
console.log(bt);
console.log("-----------------\n");
let output = "";
let line = "";
for (let i = 0; i < data.length; i++) {
// 格式化为 0x00 形式
const hex = "0x" + data[i].toString(16).padStart(2, '0').toUpperCase();
line += hex;
// 如果不是最后一个元素,添加逗号和空格
if (i < data.length - 1) {
line += ", ";
}
// 每 8 个字节输出一行
if ((i + 1) % 8 === 0 || i === data.length - 1) {
output += line + "\n";
line = "";
}
}
console.log("==================== MEMORY DUMP ====================");
console.log(output);
console.log("=====================================================");
} catch (e) {
console.log("无法获取堆栈:" + e);
console.log("[-] Dump 失败: " + e.message);
}
}
// ====== 主逻辑:Hook 偏移量函数 ======
function hook_wechat_internal_functions() {
const wechatModule = Process.findModuleByName("WeChat");
if (!wechatModule) {
console.error("❌ 找不到 WeChat 模块");
return;
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
}
const base = wechatModule.base;
console.log("📌 WeChat Base:", base);
// 第 5 个字节:最高位必须是 0,为了确保不变成 4 字节,低 7 位不能全为 0
let lastByte = Math.floor(Math.random() * 127) + 1;
res.push(lastByte & 0x7F); // 确保最高位为 0
WECHAT_OFFSETS.forEach(offset => {
const target = base.add(offset);
return res;
}
// 尝试符号化
let funcName = `WeChat!0x${offset.toString(16)}`;
try {
const sym = DebugSymbol.fromAddress(target);
if (sym && sym.name) funcName = sym.name;
} catch (_) {}
// 辅助函数:Protobuf Varint 编码 (对应 get_varint_timestamp_bytes)
function getVarintTimestampBytes() {
let ts = Math.floor(Date.now() / 1000);
let encodedBytes = [];
let tempTs = ts >>> 0; // 强制转为 32位 无符号整数
console.log(`\n🔧 准备 Hook: ${funcName} @ 0x${target}`);
try {
Interceptor.attach(target, {
onEnter(args) {
console.log("\n==============================================");
console.log(`🚀 进入函数: ${funcName}`);
console.log(`📍 地址: 0x${target}`);
console.log("\n--- 🧩 寄存器参数 x0-x7 ---");
safePrintRegister(args);
console.log("\n--- 🧵 调用堆栈 ---");
safePrintBacktrace(this.context);
console.log("==============================================\n");
},
onLeave(retval) {
// 如果需要打印返回值,可打开:
// console.log("返回值:", retval);
}
});
console.log(`✅ 已 Hook: ${funcName}`);
} catch (e) {
console.error(`❌ Hook 失败 @ 0x${target} ${e}`);
while (true) {
let byte = tempTs & 0x7F;
tempTs >>>= 7;
if (tempTs !== 0) {
encodedBytes.push(byte | 0x80);
} else {
encodedBytes.push(byte);
break;
}
});
}
// ====== 入口 ======
setImmediate(() => {
hook_wechat_internal_functions();
});
}
return encodedBytes;
}
+47 -257
View File
@@ -16,7 +16,6 @@ var triggerFuncAddr = baseAddr.add(0x448A858);
var req2bufEnterAddr = baseAddr.add(0x34566C0);
var req2bufExitAddr = baseAddr.add(0x34577D8);
var protobufAddr = baseAddr.add(0x2275BFC);
var buf2RespAddr = baseAddr.add(0x347BD44);
var patchProtobufFunc1 = baseAddr.add(0x2275BB8)
var patchProtobufFunc2 = baseAddr.add(0x2275BD8);
var protobufDeleteAddr = baseAddr.add(0x2275C14);
@@ -49,7 +48,7 @@ var senderGlobal = "wxid_ldftuhe36izg19"
var lastSendTime = 0;
var globalImageCdnKey = "";
var globalAesKey1 = "";
var globalAesKey2 = "";
var globalMd5Key = "";
// 打印消息的地址,便于查询问题
function printAddr() {
@@ -62,13 +61,6 @@ function printAddr() {
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() {
console.log("[+] Starting Dynamic Message Patching...");
@@ -87,8 +79,8 @@ function setupSendMessageDynamic() {
// 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 75 70 6c 6f 61 64 6d 73 67 69 6d 67");
patchHex(contentAddr, " ");
patchString(cgiAddr, "/cgi-bin/micromsg-bin/uploadmsgimg");
patchString(contentAddr, " ");
// B. 构建 SendMessage 结构体 (X24 基址位置)
sendMessageAddr.add(0x00).writeU64(0);
@@ -127,13 +119,6 @@ function setupSendMessageDynamic() {
setImmediate(setupSendMessageDynamic);
// 辅助函数:写入 Hex 字符串
function patchHex(addr, hexStr) {
const bytes = hexStr.split(' ').map(h => parseInt(h, 16));
addr.writeByteArray(bytes);
addr.add(bytes.length).writeU8(0); // 终止符
}
function patchProtoBuf() {
Memory.patchCode(patchProtobufFunc1, 4, code => {
@@ -174,8 +159,8 @@ function manualTrigger(taskId, sender, receiver) {
const timestamp = Math.floor(Date.now() / 1000);
lastSendTime = timestamp
taskIdGlobal = taskId;
// receiverGlobal = receiver;
// senderGlobal = sender;
receiverGlobal = receiver;
senderGlobal = sender;
messageAddr.add(0x08).writeU32(taskIdGlobal);
sendMessageAddr.add(0x20).writeU32(taskIdGlobal);
@@ -311,7 +296,8 @@ function attachReq2buf() {
}
insertMsgAddr.writeU64(0x0);
console.log("[+] 清空写入后内存预览: " + insertMsgAddr.readPointer());
// receiverGlobal = "";
receiverGlobal = "";
taskIdGlobal = 0;
send({
type: "finish",
})
@@ -321,24 +307,6 @@ function attachReq2buf() {
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));
@@ -349,22 +317,6 @@ function stringToHexArray(str) {
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() {
console.log("[+] proto注入拦截目标地址: " + protobufAddr);
@@ -446,12 +398,11 @@ function attachProto() {
const randomId8 = [0xC0, 0x01, 0x3A]
const aesKey1Header = [0xCA, 0x01, 0x20]
const aesKey1 = stringToHexArray(globalAesKey1)
const aesKey2Header = [0xDA, 0x01, 0x20]
const aesKey2 = stringToHexArray(globalAesKey2)
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
@@ -460,7 +411,7 @@ function attachProto() {
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, aesKey2Header, aesKey2, randomId9, left0)
aesKey1Header, aesKey1, md5Header, me5Key, randomId9, left0)
console.log("[+] Payload 准备写入");
// dumpMemoryToHex(this.context.x1, 1024)
@@ -499,183 +450,6 @@ function toVarint(n) {
return res;
}
function setReceiver() {
console.log("[+] buf2RespAddr WeChat Base: " + baseAddr + "[+] Attaching to: " + buf2RespAddr);
// 3. 开始拦截
Interceptor.attach(buf2RespAddr, {
onEnter: function (args) {
const currentPtr = this.context.x1;
let start = 0x1e;
let senderLen = currentPtr.add(start).readU8();
if (senderLen !== 0x14 && senderLen !== 0x13) {
start = 0x1d;
let senderLen = currentPtr.add(start).readU8();
if (senderLen !== 0x14 && senderLen !== 0x13) {
return
}
}
let senderPtr = currentPtr.add(start + 1);
let sender = senderPtr.readUtf8String(senderLen);
let receiverLenPtr = senderPtr.add(senderLen).add(3);
let receiverLen = receiverLenPtr.readU8();
let receiverStrPtr = receiverLenPtr.add(1);
let receiver = receiverStrPtr.readUtf8String(receiverLen);
let contentLenPtr = receiverStrPtr.add(receiverLen).add(6);
if (isPrintableOrChinese(contentLenPtr, 1)) {
contentLenPtr = contentLenPtr.add(-1);
}
const contentLenValue = readVarint(contentLenPtr)
let contentPtr = contentLenPtr.add(contentLenValue.byteLength);
var content = contentPtr.readUtf8String(contentLenValue.value);
var selfId = receiver
var msgType = "private"
var groupId = ""
var senderUser = sender
var messages = [];
messages.push({type: "text", data: {text: content}});
if (sender.includes("@chatroom")) {
msgType = "group"
groupId = sender
let splitIndex = -1;
for (let i = 0; i < content.length; i++) {
if (content[i] === ':') {
splitIndex = i;
break;
}
}
senderUser = content.substring(0, splitIndex).trim();
content = content.substring(splitIndex + 2).trim();
messages = [];
const parts = content.split('\u2005');
for (let part of parts) {
part = part.trim();
if (!part.startsWith("@")) {
messages.push({type: "text", data: {text: part}});
}
}
const xmlPtr = contentPtr.add(contentLenValue.value).add(15);
const xmlLenValue = readVarint(xmlPtr)
const xml = xmlPtr.add(xmlLenValue.byteLength).readUtf8String(xmlLenValue.value);
const atUserMatch = xml.match(/<atuserlist>([\s\S]*?)<\/atuserlist>/);
const atUser = atUserMatch ? atUserMatch[1] : null;
if (atUser) {
messages.push({type: "at", data: {qq: atUser}});
}
}
send({
message_type: msgType,
user_id: senderUser, // 发送人的 ID
self_id: selfId, // 接收人的 ID
group_id: groupId, // 群 ID
message_id: taskIdGlobal,
type: "send",
raw: {peerUid: taskIdGlobal},
message: messages
})
},
});
}
// 使用 setImmediate 确保在模块加载后执行
setImmediate(setReceiver)
function readVarint(addr) {
let value = 0;
let shift = 0;
let count = 0;
while (true) {
let byte = addr.add(count).readU8();
// 取低7位进行累加
value |= (byte & 0x7f) << shift;
count++; // 消耗了一个字节
// 如果最高位是0,跳出循环
if ((byte & 0x80) === 0) break;
shift += 7;
if (count > 5) return -1; // 安全校验,防止死循环
}
return {
value: value, // 最终长度数值 (例如 251)
byteLength: count // 长度字段占用的字节数 (例如 2)
};
}
function dumpMemoryToHex(ptr, size) {
try {
// 1. 读取内存数据
const buffer = ptr.readByteArray(size);
const data = new Uint8Array(buffer);
let output = "";
let line = "";
for (let i = 0; i < data.length; i++) {
// 格式化为 0x00 形式
const hex = "0x" + data[i].toString(16).padStart(2, '0').toUpperCase();
line += hex;
// 如果不是最后一个元素,添加逗号和空格
if (i < data.length - 1) {
line += ", ";
}
// 每 8 个字节输出一行
if ((i + 1) % 8 === 0 || i === data.length - 1) {
output += line + "\n";
line = "";
}
}
console.log("==================== MEMORY DUMP ====================");
console.log(output);
console.log("=====================================================");
} catch (e) {
console.log("[-] Dump 失败: " + e.message);
}
}
function isPrintableOrChinese(startPtr, maxScanLength) {
let offset = 0;
while (offset < maxScanLength) {
let b = startPtr.add(offset).readU8();
if (b === 0) {
// 扫描到 \0,且之前没有发现异常字节
return offset > 0; // 如果第一个就是 \0,视为非字符串(可能是空指针)
}
// 判定逻辑:
// 1. 可见 ASCII (32-126) 或 换行/制表符 (9, 10, 13)
let isAscii = (b >= 32 && b <= 126) || (b === 9 || b === 10 || b === 13);
// 2. 汉字 UTF-8 特征:第一个字节通常 >= 0x80 (128)
// 严谨点:UTF-8 汉字首字节通常在 0xE4-0xE9 之间,后续字节在 0x80-0xBF 之间
// 这里简化处理:如果是高位字符,我们暂时放行,由 readUtf8String 最终处理
let isHighBit = (b >= 0x80);
if (!isAscii && !isHighBit) {
// 发现既不是 ASCII 也不是高位字节(如 0x01-0x1F 的控制字符),判定为指针
return false;
}
offset++;
}
return true;
}
function patchCdnOnComplete() {
Interceptor.attach(CndOnCompleteAddr, {
onEnter: function (args) {
@@ -685,8 +459,8 @@ function patchCdnOnComplete() {
const x2 = this.context.x2;
globalImageCdnKey = x2.add(0x60).readPointer().readUtf8String();
globalAesKey1 = x2.add(0x78).readPointer().readUtf8String();
globalAesKey2 = x2.add(0x90).readPointer().readUtf8String();
console.log("[+] globalImageCdnKey: " + globalImageCdnKey + " globalAesKey1: " + globalAesKey1 + " globalAesKey2: " + globalAesKey2);
globalMd5Key = x2.add(0x90).readPointer().readUtf8String();
console.log("[+] globalImageCdnKey: " + globalImageCdnKey + " globalAesKey1: " + globalAesKey1 + " globalAesKey2: " + globalMd5Key);
} catch (e) {
console.log("[-] Memory access error at onEnter: " + e);
}
@@ -700,39 +474,29 @@ var uploadGlobalX0 = ptr(0)
var uploadFunc1Addr = ptr(0)
var uploadFunc2Addr = ptr(0)
var imageIdAddr = ptr(0)
var uploadAesKeyAddr1 = ptr(0)
var uploadAesKeyAddr2 = ptr(0)
var md5Addr = ptr(0)
var uploadAesKeyAddr = ptr(0)
var ImagePathAddr1 = ptr(0)
var ImagePathAddr2 = ptr(0)
var ImagePathAddr3 = ptr(0)
var uploadImagePayload = ptr(0);
function initMemo() {
uploadFunc1Addr = Memory.alloc(24);
uploadFunc2Addr = Memory.alloc(24);
imageIdAddr = Memory.alloc(256);
uploadAesKeyAddr1 = Memory.alloc(256);
uploadAesKeyAddr2 = Memory.alloc(256);
md5Addr = Memory.alloc(256);
uploadAesKeyAddr = Memory.alloc(256);
ImagePathAddr1 = Memory.alloc(256);
ImagePathAddr2 = Memory.alloc(256);
ImagePathAddr3 = Memory.alloc(256);
uploadImagePayload = Memory.alloc(512);
uploadFunc1Addr.writePointer(baseAddr.add(0x802b8b0));
uploadFunc2Addr.writePointer(baseAddr.add(0x7fd5908));
patchHex(imageIdAddr, "77 78 69 64 5F 37 77 64 31 65 63 65 39 39 66 37 69 32 31 5F 31 37 36 38 32 37 34 36 36 37 5F 32 33 32 5F 31");
patchHex(uploadAesKeyAddr1, "37 65 61 34 31 64 35 36 39 66 37 30 35 33 35 37 37 38 30 39 36 38 65 39 31 30 34 32 38 34 63 66")
patchHex(uploadAesKeyAddr2, "65 63 64 35 37 65 39 63 66 38 35 66 32 65 32 30 38 37 61 65 65 38 63 30 66 64 31 65 34 34 35 65")
patchHex(ImagePathAddr1, "2F 55 73 65 72 73 2F 79 69 6E 63 6F 6E 67 2F 4C 69 62 72 61 72 79 2F 43 6F 6E 74 61 69 6E 65 72 73 2F 63 6F 6D 2E 74 65 6E 63 65 6E 74 2E 78 69 6E 57 65 43 68 61 74 2F 44 61 74 61 2F 44 6F 63 75 6D 65 6E 74 73 2F 78 77 65 63 68 61 74 5F 66 69 6C 65 73 2F 77 78 69 64 5F 6C 64 66 74 75 68 65 33 36 69 7A 67 31 39 5F 35 65 37 64 2F 74 65 6D 70 2F 30 34 65 62 61 61 62 37 65 33 65 61 36 30 35 30 65 32 36 66 66 33 31 64 38 39 63 63 31 32 31 65 2F 32 30 32 36 2D 30 31 2F 49 6D 67 2F 32 33 32 5F 31 37 36 38 32 37 33 36 36 37 2E 6A 70 67");
patchHex(ImagePathAddr2, "2F 55 73 65 72 73 2F 79 69 6E 63 6F 6E 67 2F 4C 69 62 72 61 72 79 2F 43 6F 6E 74 61 69 6E 65 72 73 2F 63 6F 6D 2E 74 65 6E 63 65 6E 74 2E 78 69 6E 57 65 43 68 61 74 2F 44 61 74 61 2F 44 6F 63 75 6D 65 6E 74 73 2F 78 77 65 63 68 61 74 5F 66 69 6C 65 73 2F 77 78 69 64 5F 6C 64 66 74 75 68 65 33 36 69 7A 67 31 39 5F 35 65 37 64 2F 74 65 6D 70 2F 30 34 65 62 61 61 62 37 65 33 65 61 36 30 35 30 65 32 36 66 66 33 31 64 38 39 63 63 31 32 31 65 2F 32 30 32 36 2D 30 31 2F 49 6D 67 2F 32 33 32 5F 31 37 36 38 32 37 33 36 36 37 2E 6A 70 67");
patchHex(ImagePathAddr3, "2F 55 73 65 72 73 2F 79 69 6E 63 6F 6E 67 2F 4C 69 62 72 61 72 79 2F 43 6F 6E 74 61 69 6E 65 72 73 2F 63 6F 6D 2E 74 65 6E 63 65 6E 74 2E 78 69 6E 57 65 43 68 61 74 2F 44 61 74 61 2F 44 6F 63 75 6D 65 6E 74 73 2F 78 77 65 63 68 61 74 5F 66 69 6C 65 73 2F 77 78 69 64 5F 6C 64 66 74 75 68 65 33 36 69 7A 67 31 39 5F 35 65 37 64 2F 74 65 6D 70 2F 30 34 65 62 61 61 62 37 65 33 65 61 36 30 35 30 65 32 36 66 66 33 31 64 38 39 63 63 31 32 31 65 2F 32 30 32 36 2D 30 31 2F 49 6D 67 2F 32 33 32 5F 31 37 36 38 32 37 33 36 36 37 2E 6A 70 67");
}
setImmediate(initMemo)
function manualUpload() {
function manualUpload(receiver, md5, imagePath) {
const payload = [
0x20, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 函数 10802b8b0 的指针
@@ -819,15 +583,21 @@ function manualUpload() {
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(0xa8).writePointer(uploadAesKeyAddr1);
uploadImagePayload.add(0x68).writeUtf8String(receiver);
uploadImagePayload.add(0xa8).writePointer(md5Addr);
uploadImagePayload.add(0xe0).writePointer(ImagePathAddr1);
uploadImagePayload.add(0x110).writePointer(ImagePathAddr2);
uploadImagePayload.add(0x140).writePointer(ImagePathAddr3);
uploadImagePayload.add(0x1f8).writePointer(uploadAesKeyAddr2);
uploadImagePayload.add(0x110).writePointer(ImagePathAddr1);
uploadImagePayload.add(0x140).writePointer(ImagePathAddr1);
uploadImagePayload.add(0x1f8).writePointer(uploadAesKeyAddr);
const targetAddr = baseAddr.add(0x45DC834);
@@ -852,6 +622,26 @@ function attachUploadMedia() {
setImmediate(attachUploadMedia);
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;
}
rpc.exports = {
manualTrigger: manualTrigger,
manualUpload: manualUpload
+159
View File
@@ -0,0 +1,159 @@
function setReceiver() {
var buf2RespAddr = baseAddr.add(0x347BD44);
console.log("[+] buf2RespAddr WeChat Base: " + baseAddr + "[+] Attaching to: " + buf2RespAddr);
// 3. 开始拦截
Interceptor.attach(buf2RespAddr, {
onEnter: function (args) {
const currentPtr = this.context.x1;
let start = 0x1e;
let senderLen = currentPtr.add(start).readU8();
if (senderLen !== 0x14 && senderLen !== 0x13) {
start = 0x1d;
let senderLen = currentPtr.add(start).readU8();
if (senderLen !== 0x14 && senderLen !== 0x13) {
return
}
}
console.log("[+] Entered Receive Function: 0x1023B5348");
let senderPtr = currentPtr.add(start + 1);
let sender = senderPtr.readUtf8String(senderLen);
let receiverLenPtr = senderPtr.add(senderLen).add(3);
let receiverLen = receiverLenPtr.readU8();
let receiverStrPtr = receiverLenPtr.add(1);
let receiver = receiverStrPtr.readUtf8String(receiverLen);
let contentLenPtr = receiverStrPtr.add(receiverLen).add(6);
// 判断是否等于 0x77 ('w'), 如果是,则是短字段
if (isPrintableOrChinese(contentLenPtr, 1)) {
contentLenPtr = contentLenPtr.add(-1);
}
const contentLenValue = readVarint(contentLenPtr)
let contentPtr = contentLenPtr.add(contentLenValue.byteLength);
var content = contentPtr.readUtf8String(contentLenValue.value);
var selfId = receiver
var msgType = "private"
var groupId = ""
var senderUser = sender
var messages = [];
messages.push({type: "text", data: {text: content}});
if (sender.includes("@chatroom")) {
msgType = "group"
groupId = sender
let splitIndex = -1;
for (let i = 0; i < content.length; i++) {
if (content[i] === ':') {
splitIndex = i;
break;
}
}
senderUser = content.substring(0, splitIndex).trim();
content = content.substring(splitIndex + 2).trim();
messages = [];
const parts = content.split('\u2005');
for (let part of parts) {
part = part.trim();
if (!part.startsWith("@")) {
messages.push({type: "text", data: {text: part}});
}
}
const xmlPtr = contentPtr.add(contentLenValue.value).add(15);
const xmlLenValue = readVarint(xmlPtr)
const xml = xmlPtr.add(xmlLenValue.byteLength).readUtf8String(xmlLenValue.value);
const atUserMatch = xml.match(/<atuserlist>([\s\S]*?)<\/atuserlist>/);
const atUser = atUserMatch ? atUserMatch[1] : null;
if (atUser) {
messages.push({type: "at", data: {qq: atUser}});
}
}
send({
message_type: msgType,
user_id: senderUser, // 发送人的 ID
self_id: selfId, // 接收人的 ID
group_id: groupId, // 群 ID
message_id: generateAESKey(),
type: "send",
raw: {peerUid: generateAESKey()},
message: messages
})
},
});
}
// 使用 setImmediate 确保在模块加载后执行
setImmediate(setReceiver)
function readVarint(addr) {
let value = 0;
let shift = 0;
let count = 0;
while (true) {
let byte = addr.add(count).readU8();
// 取低7位进行累加
value |= (byte & 0x7f) << shift;
count++; // 消耗了一个字节
// 如果最高位是0,跳出循环
if ((byte & 0x80) === 0) break;
shift += 7;
if (count > 5) return -1; // 安全校验,防止死循环
}
return {
value: value, // 最终长度数值 (例如 251)
byteLength: count // 长度字段占用的字节数 (例如 2)
};
}
function isPrintableOrChinese(startPtr, maxScanLength) {
let offset = 0;
while (offset < maxScanLength) {
let b = startPtr.add(offset).readU8();
if (b === 0) {
// 扫描到 \0,且之前没有发现异常字节
return offset > 0; // 如果第一个就是 \0,视为非字符串(可能是空指针)
}
// 判定逻辑:
// 1. 可见 ASCII (32-126) 或 换行/制表符 (9, 10, 13)
let isAscii = (b >= 32 && b <= 126) || (b === 9 || b === 10 || b === 13);
// 2. 汉字 UTF-8 特征:第一个字节通常 >= 0x80 (128)
// 严谨点:UTF-8 汉字首字节通常在 0xE4-0xE9 之间,后续字节在 0x80-0xBF 之间
// 这里简化处理:如果是高位字符,我们暂时放行,由 readUtf8String 最终处理
let isHighBit = (b >= 0x80);
if (!isAscii && !isHighBit) {
// 发现既不是 ASCII 也不是高位字节(如 0x01-0x1F 的控制字符),判定为指针
return false;
}
offset++;
}
return true;
}
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;
}
rpc.exports = {
manualTrigger: manualTrigger
};
-154
View File
@@ -15,7 +15,6 @@ var triggerFuncAddr = baseAddr.add(0x448A858);
var req2bufEnterAddr = baseAddr.add(0x34566C0);
var req2bufExitAddr = baseAddr.add(0x34577D8);
var protobufAddr = baseAddr.add(0x227EC70);
var buf2RespAddr = baseAddr.add(0x347BD44);
var patchProtobufAddr = baseAddr.add(0x227EC4C);
var protobufDeleteAddr = baseAddr.add(0x227EC88);
@@ -30,7 +29,6 @@ var messageAddr = ptr(0);
var contentAddr = ptr(0);
var insertMsgAddr = ptr(0);
var receiverAddr = ptr(0)
var protoX1PayloadAddr = ptr(0);
var protoX1PayloadLen = 1024;
@@ -68,7 +66,6 @@ function setupSendMessageDynamic() {
sendMessageAddr = Memory.alloc(256);
messageAddr = Memory.alloc(256);
contentAddr = Memory.alloc(255);
receiverAddr = Memory.alloc(24);
// A. 写入字符串内容
@@ -414,154 +411,3 @@ function toVarint(n) {
}
setImmediate(attachProto);
function setReceiver() {
console.log("[+] buf2RespAddr WeChat Base: " + baseAddr + "[+] Attaching to: " + buf2RespAddr);
// 3. 开始拦截
Interceptor.attach(buf2RespAddr, {
onEnter: function (args) {
const currentPtr = this.context.x1;
let start = 0x1e;
let senderLen = currentPtr.add(start).readU8();
if (senderLen !== 0x14 && senderLen !== 0x13) {
start = 0x1d;
let senderLen = currentPtr.add(start).readU8();
if (senderLen !== 0x14 && senderLen !== 0x13) {
return
}
}
console.log("[+] Entered Receive Function: 0x1023B5348");
let senderPtr = currentPtr.add(start + 1);
let sender = senderPtr.readUtf8String(senderLen);
let receiverLenPtr = senderPtr.add(senderLen).add(3);
let receiverLen = receiverLenPtr.readU8();
let receiverStrPtr = receiverLenPtr.add(1);
let receiver = receiverStrPtr.readUtf8String(receiverLen);
let contentLenPtr = receiverStrPtr.add(receiverLen).add(6);
// 判断是否等于 0x77 ('w'), 如果是,则是短字段
if (isPrintableOrChinese(contentLenPtr, 1)) {
contentLenPtr = contentLenPtr.add(-1);
}
const contentLenValue = readVarint(contentLenPtr)
let contentPtr = contentLenPtr.add(contentLenValue.byteLength);
var content = contentPtr.readUtf8String(contentLenValue.value);
var selfId = receiver
var msgType = "private"
var groupId = ""
var senderUser = sender
var messages = [];
messages.push({type: "text", data: {text: content}});
if (sender.includes("@chatroom")) {
msgType = "group"
groupId = sender
let splitIndex = -1;
for (let i = 0; i < content.length; i++) {
if (content[i] === ':') {
splitIndex = i;
break;
}
}
senderUser = content.substring(0, splitIndex).trim();
content = content.substring(splitIndex + 2).trim();
messages = [];
const parts = content.split('\u2005');
for (let part of parts) {
part = part.trim();
if (!part.startsWith("@")) {
messages.push({type: "text", data: {text: part}});
}
}
const xmlPtr = contentPtr.add(contentLenValue.value).add(15);
const xmlLenValue = readVarint(xmlPtr)
const xml = xmlPtr.add(xmlLenValue.byteLength).readUtf8String(xmlLenValue.value);
const atUserMatch = xml.match(/<atuserlist>([\s\S]*?)<\/atuserlist>/);
const atUser = atUserMatch ? atUserMatch[1] : null;
if (atUser) {
messages.push({type: "at", data: {qq: atUser}});
}
}
send({
message_type: msgType,
user_id: senderUser, // 发送人的 ID
self_id: selfId, // 接收人的 ID
group_id: groupId, // 群 ID
message_id: taskIdGlobal,
type: "send",
raw: {peerUid: taskIdGlobal},
message: messages
})
},
});
}
// 使用 setImmediate 确保在模块加载后执行
setImmediate(setReceiver)
function readVarint(addr) {
let value = 0;
let shift = 0;
let count = 0;
while (true) {
let byte = addr.add(count).readU8();
// 取低7位进行累加
value |= (byte & 0x7f) << shift;
count++; // 消耗了一个字节
// 如果最高位是0,跳出循环
if ((byte & 0x80) === 0) break;
shift += 7;
if (count > 5) return -1; // 安全校验,防止死循环
}
return {
value: value, // 最终长度数值 (例如 251)
byteLength: count // 长度字段占用的字节数 (例如 2)
};
}
rpc.exports = {
manualTrigger: manualTrigger
};
function isPrintableOrChinese(startPtr, maxScanLength) {
let offset = 0;
while (offset < maxScanLength) {
let b = startPtr.add(offset).readU8();
if (b === 0) {
// 扫描到 \0,且之前没有发现异常字节
return offset > 0; // 如果第一个就是 \0,视为非字符串(可能是空指针)
}
// 判定逻辑:
// 1. 可见 ASCII (32-126) 或 换行/制表符 (9, 10, 13)
let isAscii = (b >= 32 && b <= 126) || (b === 9 || b === 10 || b === 13);
// 2. 汉字 UTF-8 特征:第一个字节通常 >= 0x80 (128)
// 严谨点:UTF-8 汉字首字节通常在 0xE4-0xE9 之间,后续字节在 0x80-0xBF 之间
// 这里简化处理:如果是高位字符,我们暂时放行,由 readUtf8String 最终处理
let isHighBit = (b >= 0x80);
if (!isAscii && !isHighBit) {
// 发现既不是 ASCII 也不是高位字节(如 0x01-0x1F 的控制字符),判定为指针
return false;
}
offset++;
}
return true;
}