diff --git a/frida/script.js b/frida/script.js index 7411bff..de18f16 100644 --- a/frida/script.js +++ b/frida/script.js @@ -5,12 +5,24 @@ if (!baseAddr) { } console.log("[*] WeChat base address: " + baseAddr); -// 假设 0x10444A99C 是相对于 0x100000000 的地址 -const triggerFuncAddr = baseAddr.add(0x444A99C); +// 触发函数地址,不同版本的地址看wechat_version 中的json文件复制过来 +var triggerFuncAddr = baseAddr.add(0x444A99C); +var sendMessageCallbackFunc = baseAddr.add(0xEDB4678); +var messageCallbackFunc1 = baseAddr.add(0x7f04f70); +var messageCallbackFunc2 = baseAddr.add(0x7f04fc8); +var messageCallbackFunc3 = baseAddr.add(0x7f96918); +var messageCallbackFunc4 = baseAddr.add(0x7f96a08); +var messageCallbackFunc5 = baseAddr.add(0x7f968a0); +// 这个必须是绝对位置 +var triggerX1Payload = ptr(0x175ED6600); +var req2bufEnterAddr = baseAddr.add(0x33EE8E8); +var req2bufExitAddr = baseAddr.add(0x33EFA00); +var protobufAddr = baseAddr.add(0x223EF58); -// 定义一个全局变量用于保存 X0 的指针 +// 触发函数X0参数地址 var globalMessagePtr = ptr(0); +// 消息体的一些指针地址 var cgiAddr = ptr(0); var callBackFuncAddr = ptr(0); var sendMessageAddr = ptr(0); @@ -18,11 +30,13 @@ var messageAddr = ptr(0); var messageContentAddr = ptr(0); var messageAddrAddr = ptr(0); var contentAddr = ptr(0); - var insertMsgAddr = ptr(0); - +// 消息的taskId var taskId = 0x20000090 +var receiver = "wxid_7wd1ece99f7i21" +var content = "hello world"; +// 打印消息的地址,便于查询问题 function printAddr() { console.log("[*] Addresses:"); console.log(" - cgiAddr: " + cgiAddr); @@ -33,6 +47,7 @@ function printAddr() { console.log(" - messageAddrAddr: " + messageAddrAddr); console.log(" - contentAddr: " + contentAddr); console.log(" - globalMessagePtr: " + globalMessagePtr); + console.log(" - triggerX1Payload: " + triggerX1Payload); } // 辅助函数:写入 Hex 字符串 @@ -42,6 +57,7 @@ function patchHex(addr, hexStr) { addr.add(bytes.length).writeU8(0); // 终止符 } +// 初始化进行内存的分配 function setupSendMessageDynamic() { console.log("[*] Starting Dynamic Message Patching..."); @@ -63,7 +79,7 @@ function setupSendMessageDynamic() { // B. 构建 SendMessage 结构体 (X24 基址位置) sendMessageAddr.add(0x00).writeU64(0); sendMessageAddr.add(0x08).writeU64(0); - sendMessageAddr.add(0x10).writePointer(baseAddr.add(0xEDB4678)); // 虚表地址通常仍需硬编码或从模块基址计算 + sendMessageAddr.add(0x10).writePointer(sendMessageCallbackFunc); // 虚表地址通常仍需硬编码或从模块基址计算 sendMessageAddr.add(0x18).writeU64(1); sendMessageAddr.add(0x20).writeU32(taskId); sendMessageAddr.add(0x28).writePointer(messageAddr); // 指向动态分配的 Message @@ -76,7 +92,7 @@ function setupSendMessageDynamic() { })); // C. 构建 Message 结构体 - messageAddr.add(0x00).writePointer(baseAddr.add(0x7f04f70)); + messageAddr.add(0x00).writePointer(messageCallbackFunc1); messageAddr.add(0x08).writeU32(taskId); messageAddr.add(0x0c).writeU32(0x20a); messageAddr.add(0x10).writeU64(0x3); @@ -89,26 +105,26 @@ function setupSendMessageDynamic() { messageAddr.add(0x58).writeU64(uint64("0x0101010100000001")); // 处理回调地址 - callBackFuncAddr.writePointer(baseAddr.add(0x7f04fc8)); + callBackFuncAddr.writePointer(messageCallbackFunc2); messageAddr.add(0x98).writePointer(callBackFuncAddr); // 设置内容指针 - messageAddr.add(0xb8).writePointer(baseAddr.add(0x7f96918)); + messageAddr.add(0xb8).writePointer(messageCallbackFunc3); messageAddr.add(0xc0).writePointer(messageContentAddr); messageAddr.add(0xc8).writeU64(uint64("0x0000000100000001")); messageAddr.add(0xd0).writeU64(0x4); messageAddr.add(0xd8).writeU64(0x1); messageAddr.add(0xe0).writeU64(0x1); - messageAddr.add(0xe8).writePointer(baseAddr.add(0x7f96a08)); + messageAddr.add(0xe8).writePointer(messageCallbackFunc4); messageContentAddr.writePointer(messageAddrAddr); - messageAddrAddr.writePointer(baseAddr.add(0x7f968a0)); + messageAddrAddr.writePointer(messageCallbackFunc5); messageAddrAddr.add(0x08).writePointer(contentAddr); console.log(" [+] messageAddr Object: ", hexdump(messageAddr, { offset: 0, - length: 200, + length: 128, header: true, ansi: true })); @@ -118,14 +134,12 @@ function setupSendMessageDynamic() { setImmediate(setupSendMessageDynamic); - +// 设置trigger函数的x0参数 function setTriggerAttach() { - var targetAddr = baseAddr.add(0x444A99C); - - console.log("[*] WeChat Base: " + baseAddr + "[*] Attaching to: " + targetAddr); + console.log("[*] WeChat Base: " + baseAddr + "[*] Attaching to: " + triggerFuncAddr); // 3. 开始拦截 - Interceptor.attach(targetAddr, { + Interceptor.attach(triggerFuncAddr, { onEnter: function (args) { console.log("[*] Entered Function: 0x10444A99C"); @@ -145,25 +159,21 @@ function setTriggerAttach() { setImmediate(setTriggerAttach); -/** - * 手动触发函数调用:模拟 IDA Appcall 逻辑 - */ function manualTrigger() { console.log("[*] Manual Trigger Started..."); - - const payloadBase = ptr("0x175ED6600"); + if (globalMessagePtr.isNull()) { + console.log("[!] globalMessagePtr is NULL, cannot trigger!"); + return; + } // 获取当前时间戳 (秒) const timestamp = Math.floor(Date.now() / 1000); - // 2. 执行基础的 Patch (Dword/Qword) messageAddr.add(0x08).writeU32(taskId); sendMessageAddr.add(0x20).writeU32(taskId); messageAddrAddr.add(0x18).writeU32(timestamp); - // 3. 构造并填充 Payload - // 注意:Frida 的 writeByteArray 不会自动处理长度,需确保数据完整 const payloadData = [ 0x0A, 0x02, 0x00, 0x00, // 0x00 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08 @@ -220,17 +230,16 @@ function manualTrigger() { ]; // 从 0x175ED6604 开始写入 Payload - payloadBase.writeU32(taskId); - payloadBase.add(0x04).writeByteArray(payloadData); - payloadBase.add(0x18).writePointer(cgiAddr); - console.log("[+] Payload trigger function written to memory."); + triggerX1Payload.writeU32(taskId); + triggerX1Payload.add(0x04).writeByteArray(payloadData); + triggerX1Payload.add(0x18).writePointer(cgiAddr); const sub_10444A99C = new NativeFunction(triggerFuncAddr, 'uint64', ['pointer', 'pointer']); // 5. 调用函数 try { const arg1 = globalMessagePtr; // 第一个指针参数 - const arg2 = payloadBase; // 第二个参数 0x175ED6600 + const arg2 = triggerX1Payload; // 第二个参数 0x175ED6600 console.log(`[*] Calling trigger function at ${triggerFuncAddr} with args: (${arg1}, ${arg2})`); @@ -243,18 +252,13 @@ function manualTrigger() { } -/** - * 拦截特定地址并重定向结构体指针 - */ - +// ReqBuf 进行拦截,替换入参数的消息指针 function attachReq2buf() { - // 1. 计算运行时地址 - // 假设 IDA 地址 1033EE8E8 对应的偏移是 0x33EE8E8 (基于基址 0x100000000) - const targetAddr = baseAddr.add(0x33EE8E8); - console.log("[*] Target Req2buf enter Address: " + targetAddr); + + console.log("[*] Target Req2buf enter Address: " + req2bufEnterAddr); // 2. 开始拦截 - Interceptor.attach(targetAddr, { + Interceptor.attach(req2bufEnterAddr, { onEnter: function(args) { if (!this.context.x1.equals(taskId)) { return; @@ -289,10 +293,9 @@ function attachReq2buf() { } }); - const returnAddr = baseAddr.add(0x33EFA00); - console.log("[*] Target Req2buf leave Address: " + targetAddr); - - Interceptor.attach(returnAddr, { + // 在出口处拦截req2buf,把insertMsgAddr设置为0,避免被垃圾回收导致整个程序崩溃 + console.log("[*] Target Req2buf leave Address: " + req2bufExitAddr); + Interceptor.attach(req2bufExitAddr, { onEnter: function(args) { if (!this.context.x25.equals(taskId)) { return; @@ -302,8 +305,6 @@ function attachReq2buf() { } }); } - -// 确保在初始化后执行 setImmediate(attachReq2buf); // 辅助函数:Protobuf Varint 编码 (对应 get_varint_timestamp_bytes) @@ -325,33 +326,53 @@ function getVarintTimestampBytes() { return encodedBytes; } -/** - * 模拟 run_patch_script 逻辑的 Frida 脚本 - * 当命中 10223EF58 时触发内存写入和寄存器修改 - */ +function stringToHexArray(str) { + var arr = []; + for (var i = 0; i < str.length; i++) { + arr.push(str.charCodeAt(i)); // 获取字符的 ASCII 码 (即十六进制值) + } + 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() { - const targetHookAddr = baseAddr.add(0x223EF58); - console.log("[*] proto注入拦截目标地址: " + targetHookAddr); - // 2. 预先分配一块持久内存用于存放 Payload (对应 x1_addr) - // Memory.alloc 会返回一个在脚本运行期间有效的地址 + + console.log("[*] proto注入拦截目标地址: " + protobufAddr); + const x1_custom_addr = Memory.alloc(256); console.log("[*] Frida 分配的 Payload 地址: " + x1_custom_addr); - Interceptor.attach(targetHookAddr, { + Interceptor.attach(protobufAddr, { onEnter: function(args) { - // --- 构造动态 Payload --- const prefix = [ 0x08, 0x01, 0x12, 0x5E, 0x0A, 0x15, 0x0A, 0x13, // 0x00 - 0x77, 0x78, 0x69, 0x64, 0x5F, 0x37, 0x77, 0x64, // 0x08 - 0x31, 0x65, 0x63, 0x65, 0x39, 0x39, 0x66, 0x37, // 0x10 - 0x69, 0x32, 0x31, 0x12, 0x03, 0x38, 0x38, 0x38, // 0x18 - 0x18, 0x01, 0x20 // 0x20 ]; + const receiverProto = stringToHexArray(receiver); + const contentHeader = [0x12, content.length]; + const contentProto = stringToHexArray(content); + const tsHeader = [0x18, 0x01, 0x20]; const tsBytes = getVarintTimestampBytes(); + const msgIdHeader = [0x28] + const msgId = generateRandom5ByteVarint() const suffix = [ - 0x28, 0xD1, 0xF7, 0xA6, 0xE6, 0x0C, // 某个id头部 0x32, 0x32, 0x3C, // 0x28 头部 0x6D, 0x73, 0x67, 0x73, 0x6F, 0x73, 0x75, 0x72, // 0x30 msgsour 0x63, 0x65, 0x3E, 0x3C, 0x61, 0x6C, 0x6E, 0x6F, // 0x38 ce>>> 0; // 强制转为 32位 无符号整数 -/** - * 模拟 run_patch_script 逻辑的 Frida 脚本 - * 当命中 10223EF58 时触发内存写入和寄存器修改 - */ + 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 arr = []; + for (var i = 0; i < str.length; i++) { + arr.push(str.charCodeAt(i)); // 获取字符的 ASCII 码 (即十六进制值) + } + 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() { - // 1. 计算运行时地址 (假设 IDA 基址 0x100000000) - const targetHookAddr = baseAddr.add(0x223EF58); + console.log("[*] proto注入拦截目标地址: " + protobufAddr); - // 2. 预先分配一块持久内存用于存放 Payload (对应 x1_addr) - // Memory.alloc 会返回一个在脚本运行期间有效的地址 const x1_custom_addr = Memory.alloc(256); console.log("[*] Frida 分配的 Payload 地址: " + x1_custom_addr); - // 辅助函数: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; - } - - // 3. 开始 Attach - Interceptor.attach(targetHookAddr, { + Interceptor.attach(protobufAddr, { onEnter: function(args) { - console.log("------------------------------------------"); - console.log("[+] 命中 Hook 地址: " + targetHookAddr); - - // --- 构造动态 Payload --- const prefix = [ 0x08, 0x01, 0x12, 0x5E, 0x0A, 0x15, 0x0A, 0x13, // 0x00 - 0x77, 0x78, 0x69, 0x64, 0x5F, 0x37, 0x77, 0x64, // 0x08 - 0x31, 0x65, 0x63, 0x65, 0x39, 0x39, 0x66, 0x37, // 0x10 - 0x69, 0x32, 0x31, 0x12, 0x03, 0x38, 0x38, 0x38, // 0x18 - 0x18, 0x01, 0x20 // 0x20 ]; + const receiverProto = stringToHexArray(receiver); + const contentHeader = [0x12, content.length]; + const contentProto = stringToHexArray(content); + const tsHeader = [0x18, 0x01, 0x20]; const tsBytes = getVarintTimestampBytes(); + const msgIdHeader = [0x28] + const msgId = generateRandom5ByteVarint() const suffix = [ - 0x28, 0xD1, 0xF7, 0xA6, 0xE6, 0x0C, // 某个id头部 0x32, 0x32, 0x3C, // 0x28 头部 0x6D, 0x73, 0x67, 0x73, 0x6F, 0x73, 0x75, 0x72, // 0x30 msgsour 0x63, 0x65, 0x3E, 0x3C, 0x61, 0x6C, 0x6E, 0x6F, // 0x38 ce>