diff --git a/README.md b/README.md index e2dec85..b4bb08d 100644 --- a/README.md +++ b/README.md @@ -1,224 +1,12 @@ -# weixin-macos -使用frida进行逆向 -frida -f /Applications/WeChat.app/Contents/MacOS/WeChat -l script.js -删除__handler__文件夹格外重要 +# WeChat 4.0 Message hook +![image](https://github.com/user-attachments/assets/401de4b8-5d10-48d9-8dcf-eecc8ae8682a) -### 第一次尝试,根据关键字失败 -报错:Failed to attach: unable to access process with pid 43649 from the current user account -重启,长按开关进入recovery模式,然后关闭安全模式 +hook1是触发函数,和用户回车行为一样,触发startTask。 -frida-trace -p 进程号 -i "*Message*" --decorate 比较好用 -``` -frida-trace -p 10677 -i '*send*' -x '*objc_msgSend_noarg*' -x '*objc_msgSend_debug*' -x '*objc_msgSend*' -x "*_HIDisableSuddenTerminationForSendEvent*" -x "*_HIEnableSuddenTerminationForSendEvent*" -x "*SendEventToEventTarget*" -x "*s10RTCUtility10XPCMessageV4dictAA16RTCXPCDictionaryVvg*" -x "*MTLMessageContextEnd*" -x "*ictAA16RTCXPCDictionaryVvg*" -x "*_MTLMessageContextBegin_*" -x "*CFMachMessageCheckForAndDestroyUnsentMessag*" -x "*SLEventCopyAuthenticationMessage*" -x "*SendTextInputEvent_WithCompletionHandler*" -x '*mach_msg_send*' -x "*dispatch_mach_send_with_result_and_async_reply_4libxpc*" -x "*dispatch_mach_send_with_result_and_async_reply_4libxpc*" -x "*dispatch_mach_send_with_result*" --decorate --ui-port 60000 -``` +hook2是对Req2Buf这个函数进行消息体注入,因为hook1触发的时候我其实没有给消息体,但是我注入的这个消息体,在protobuf过程中一直失败,全是指针,根本看不懂。 -加密函数: -``` -nixiang % frida-trace -p 62349 -i '*encrypt*' -i "*Encrypt*" -x '*objc_msgSend_noarg*' -x '*objc_msgSend_debug*' -x '*objc_msgSend*' -x "*_HIDisableSuddenTerminationForSendEvent*" -x "*_HIEnableSuddenTerminationForSendEvent*" -x "*SendEventToEventTarget*" -x "*s10RTCUtility10XPCMessageV4dictAA16RTCXPCDictionaryVvg*" -x "*MTLMessageContextEnd*" -x "*ictAA16RTCXPCDictionaryVvg*" -x "*_MTLMessageContextBegin_*" -x "*CFMachMessageCheckForAndDestroyUnsentMessag*" -x "*SLEventCopyAuthenticationMessage*" -x "*SendTextInputEvent_WithCompletionHandler*" -x '*mach_msg_send*' -x "*dispatch_mach_send_with_result_and_async_reply_4libxpc*" -x "*dispatch_mach_send_with_result_and_async_reply_4libxpc*" -x "*dispatch_mach_send_with_result*" --decorate --ui-port 60000 -``` +所以在hook3处我直接注入protobuf的内容,然后进行发送。 -打印上游调用 -``` -defineHandler({ - onEnter(log, args, state) { - const connectionPtr = args[0]; - const messagePtr = args[1]; - const targetqPtr = args[2]; - const handlerPtr = args[3]; +hook4是在Req2Buf,清除掉消息体的内容,因为后序在OnTaskEnd会回收内存,如果我这边消息体还在整个的指针上就会被清除,但是这个线程不认识这块内存,整个程序就会crash。 - // --- 1. 打印函数调用信息 --- - log(`\n======================================================`); - log(`[HOOKED] xpc_connection_send_message_with_reply 被调用`); - - // --- 2. 打印调用栈(最重要的一步,用于定位上层应用函数)--- - const backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE) - .map(DebugSymbol.fromAddress).join('\n'); - log("调用栈 (寻找上游应用函数):"); - log(backtrace); - log("------------------------------------------------------"); - - // --- 3. 打印指针参数 --- - log(`[Arg 1] Connection (xpc_connection_t): ${connectionPtr}`); - log(`[Arg 2] Message (xpc_object_t): ${messagePtr}`); - log(`[Arg 3] Target Queue (dispatch_queue_t): ${targetqPtr}`); - log(`[Arg 4] Reply Handler: ${handlerPtr}`); - log("------------------------------------------------------"); - - // --- 4. 打印 XPC 消息的 HexDump 预览 --- - if (messagePtr.isNull() === false) { - log(`[Arg 2] Message 原始数据预览 (32 Bytes):`); - - // 注意:这里我们使用 hexdump(),然后将结果作为一个字符串打印到 log() 中 - const hexDumpOutput = hexdump(messagePtr, { length: 32 }); - log(hexDumpOutput); - } - - log(`======================================================`); - }, - - onLeave(log, retval, state) { - // 留空,或在这里打印返回值,例如: - // log(`xpc_connection_send_message_with_reply 返回: ${retval}`); - } -}); -``` - -会有一个http页面,进去之后,会有一些微信的代码,这些代码中能分析出微信是怎么发送消息的 - -### 第二次尝试,根据mac的系统函数 -注意权限问题,找一个文件夹有权限的 - -``` -cd go/src/github.com/yincongcyincong/nixiang -frida-trace -p 17649 -i "*_send*" -i "*_sendto*" -i "*_write*" -x "*xpc_connection_send_message*" -x '*objc_msgSend_noarg*' -x '*objc_msgSend_debug*' -x '*objc_msgSend*' -x "*_HIDisableSuddenTerminationForSendEvent*" -x "*_HIEnableSuddenTerminationForSendEvent*" -x "*SendEventToEventTarget*" -x "*s10RTCUtility10XPCMessageV4dictAA16RTCXPCDictionaryVvg*" -x "*MTLMessageContextEnd*" -x "*ictAA16RTCXPCDictionaryVvg*" -x "*_MTLMessageContextBegin_*" -x "*CFMachMessageCheckForAndDestroyUnsentMessag*" -x "*SLEventCopyAuthenticationMessage*" -x "*SendTextInputEvent_WithCompletionHandler*" -x '*mach_msg_send*' -x "*dispatch_mach_send_with_result_and_async_reply_4libxpc*" -x "*dispatch_mach_send_with_result_and_async_reply_4libxpc*" -x "*dispatch_mach_send_with_result*" -x "xpc_*" --decorate --ui-port 60000 -``` -``` -defineHandler({ - onEnter(log, args, state) { - // 1. 打印函数调用和时间 - log('__write_nocancel() [libsystem_kernel.dylib] 被调用'); - - // 2. 关键步骤:打印调用栈 - log("调用栈 (寻找上游应用函数):"); - - // 使用 Thread.backtrace() 捕获当前线程的堆栈 - // 然后用 DebugSymbol.fromAddress() 将地址转换为符号(函数名+偏移量) - const backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE) - .map(DebugSymbol.fromAddress).join('\n'); - - log(backtrace); - log("-----------------------------------------"); - - // 3. (可选) 打印写入的数据参数 - const fd = args[0].toInt32(); // 文件描述符 (File Descriptor) - const bufferPtr = args[1]; // 数据缓冲区地址 - const length = args[2].toUInt32(); // 写入的数据长度 - - log(`文件描述符 (FD): ${fd}, 长度: ${length} 字节`); - - if (length > 0) { - log("原始写入数据预览 (HexDump):"); - // 打印前 64 字节 - log(hexdump(bufferPtr, { length: Math.min(length, 64) })); - } - }, - - onLeave(log, retval, state) { - // 可选:打印返回值,即实际写入的字节数 - // log(`__write_nocancel() 返回: ${retval}`); - } -}); -``` - -通过这里成功定位到 -``` -__write_nocancel() [libsystem_kernel.dylib] 被调用 - -调用栈 (寻找上游应用函数): - -0x18b2e2764 libsystem_c.dylib!__swrite -0x18b2c4734 libsystem_c.dylib!_swrite -0x18b2c28bc libsystem_c.dylib!__sflush -0x18b2d0da0 libsystem_c.dylib!fclose -0x1068a307c WeChat!0x462307c (0x10462307c) -0x1068a1b90 WeChat!0x4621b90 (0x104621b90) -0x106864b3c WeChat!0x45e4b3c (0x1045e4b3c) -``` - -明显看到代码被混淆,下一步使用 Interceptor.attach() 对内存进行hook -``` -cd go/src/github.com/yincongcyincong/nixiang -frida -p 79464 -l ./script.js -``` - -能打印 send -``` -const wechat = Process.getModuleByName("WeChat"); -Interceptor.attach(wechat.findExportByName("recv"), { - onEnter(args) { - this.buf = args[1]; - this.len = args[2].toInt32(); - }, - onLeave(retval) { - if (retval.toInt32() > 0) { - console.log("=== Recv Data ==="); - console.log(hexdump(this.buf, { length: retval.toInt32() })); - } - } -}); - - -Interceptor.attach(wechat.findExportByName("read"), { - onEnter(args) { - this.buf = args[1]; - this.len = args[2].toInt32(); - }, - onLeave(retval) { - if (retval.toInt32() > 0) { - console.log("=== Read Data ==="); - console.log(hexdump(this.buf, { length: retval.toInt32() })); - } - } -}); - - -Interceptor.attach(wechat.findExportByName("send"), { - onEnter(args) { - console.log("=== Send Data ==="); - console.log(hexdump(args[1], { length: args[2].toInt32() })); - }, - onLeave(retval) { - if (retval.toInt32() > 0) { - console.log("=== Send Data ==="); - console.log(hexdump(this.buf, { length: retval.toInt32() })); - } - } -}); - -Interceptor.attach(wechat.findExportByName("write"), { - onEnter(args) { - let fd = args[0].toInt32(); - this.buf = args[1]; - this.len = args[2].toInt32(); - dump("WRITE", this.buf, this.len); - } -}); - - -``` - -拦截发送库 -``` -const libc = Process.getModuleByName("libSystem.B.dylib"); - -Interceptor.attach(libc.findExportByName("read"), { - onEnter(args) { - this.buf = args[1]; - this.len = args[2].toInt32(); - }, - onLeave(retval) { - if (retval.toInt32() > 0) { - console.log("=== Read Data ==="); - console.log(hexdump(this.buf, { length: retval.toInt32() })); - console.log(Thread.backtrace(this.context, Backtracer.FUZZY) - .map(DebugSymbol.fromAddress).join("\n")); - } - } -}); - -Interceptor.attach(libc.findExportByName("write"), { - onEnter(args) { - this.buf = args[1]; - this.len = args[2].toInt32(); - }, - onLeave(retval) { - if (retval.toInt32() > 0) { - console.log("=== Write Data ==="); - console.log(hexdump(this.buf, { length: retval.toInt32() })); - console.log(Thread.backtrace(this.context, Backtracer.FUZZY) - .map(DebugSymbol.fromAddress).join("\n")); - } - } -}); - -``` +查看:./frida/succ.js