@ChiChou wrote:
看了 http://iosre.com/t/mac/7014/6 一贴之后,消息防撤回才是更具备痛点的需求。
没有参考前人(后来才发现 Google 一下可以省掉我一些逆向的时间)的成果,直接 hopper 在 WeChat 可执行文件里找 recall,仅仅找到了
-[MessageService RecallMsgWithChatName:msgData:erroHandler:]:
,是触发撤回的动作。然后继续看这个 MessageService 类,找到了另一个方法-[MessageService onRevokeMsg:]:
。妹的,怎么又变成 revoke 了。代码就不全贴了:
void -[MessageService onRevokeMsg:](void * self, void * _cmd, void * arg2) { r12 = self; r15 = [arg2 retain]; if ([r15 length] == 0x0) goto loc_1007cfbae; goto loc_1007cf72c; loc_1007cfbae: rax = [r15 release]; return; loc_1007cf72c: r14 = @selector(alloc); rcx = [NSString alloc]; r13 = @selector(initWithFormat:); rbx = [rcx initWithFormat:@"INFO: Revoke msg: %@", r15]; [MMLogger logWithMMLogLevel:0x4 module:"Message" file:"MessageService+Internal.mm" line:0x18b func:"-[MessageService(Internal) onRevokeMsg:]" message:rbx]; [rbx release]; sub_1008833c4(var_48, 0x1); var_50 = r12; rbx = objc_retainAutorelease(r15); r12 = [rbx UTF8String]; rax = [rbx lengthOfBytesUsingEncoding:0x4]; sub_100883ae6(var_48, r12, LODWORD(rax)); rax = loc_100883d04(var_48, r12); if (rax == 0x0) goto loc_1007cf9e5; goto loc_1007cf813; loc_1007cf9e5: rcx = [NSString alloc]; rbx = [rcx initWithFormat:rdx]; [MMLogger logWithMMLogLevel:0x2 module:"Message" file:"MessageService+Internal.mm" line:0x192 func:"-[MessageService(Internal) onRevokeMsg:]" message:rbx];
这个方法就是微信在从服务器上获得一个撤回动作的时候执行的。经过分析这个方法完全可以安全地屏蔽掉。
下面来折腾 hook。
接下来的 hook 就是老生常谈了,完全可以写成一个 dylib 插件打包到应用可执行文件里。
为了偷懒,下面用 frida.re 做原型。这也是为什么我们的代码这么短的原因……
frida 是一个类似 cycript 的工具,可以用脚本执行一些 hook 逻辑的编写,除 iOS 之外还支持 Android、Windows 和 Linux,语法完全基于 javascript。具体可以参考 frida 的文档:
Mac 上自带 python,当然 brew 装一个更好用。默认读者机器有 python 环境。用包管理 pypi 安装 frida,一条命令就搞定了:
[sudo] pip install frida
这样就多了 frida, frida-ps 等命令,具体的用法还是继续参考官方文档啦。
然后就是使用 frida 附加到进程。需要注意的是,在较新的 OS X 上需要关闭 SIP。
frida WeChat
附加到微信的进程,这时候我们可以看到一个控制台:➜ GitHub frida WeChat ____ / _ | Frida 8.0.0 - A world-class dynamic instrumentation framework | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at http://www.frida.re/docs/home/ [Local::ProcName::WeChat]->
在这个控制台里可以输入任意的 javascript 并获得输出结果,类似 cycript 的操作。做函数 hook 的时候我们需要用到 Interceptor
Interceptor 提供了两个 hook 方法:attach 和 replace。这两者的区别是,attach 会在函数进入之前和返回之后增加两个回调函数,可以打印参数、堆栈、返回值等功能,但不能『阻止』这个函数的调用。replace 方法则是整个替换掉函数,也就是完全可以避免执行到函数里的功能。
首先我们需要得到这个方法……使用 ObjC 对象的 classes 成员可以枚举当前进程中所有的 ObjC 类:
[Local::ProcName::WeChat]-> ObjC.classes { "_NSViewAnimator_NSBrowserView": { "handle": "0x7fff9c27fc78" }, "_NSViewAnimator_NSClipView": { "handle": "0x7fff9c280c90" }, "_NSViewAnimator_NSClipView_NSClipView": { "handle": "0x600001054190" }, "_NSViewAnimator_NSCollectionView": { "handle": "0x7fff9c29dd40" }, ...
列表很长就不全列了。这是一个 js 的字典对象,每一个 key 对应一个 class 的名字。
[Local::ProcName::WeChat]-> ObjC.classes.MessageService { "handle": "0x10f6d5d28" }
这样便获得了 MessageService 类。此外使用 ObjC.classes.类名.$methods 可以列出一个类所有可用的方法。
定位到我们要的方法:
[Local::ProcName::WeChat]-> ObjC.classes.MessageService["- onRevokeMsg:"] function
在 Javascript 里甚至可以直接调用这个方法,当然需要有合适的参数。接着修改其
.implementation
属性即可替换函数实现。而在 frida 中将普通的 javascript 函数转换成 ObjectiveC 可以调用的函数,还需要使用 ObjC.implement。最后只要两行代码:
var method = ObjC.classes.MessageService["- onRevokeMsg:"]; method.implementation = ObjC.implement(method, function (self, sel) {})
2.2.0 测试可用。
Mac 上无法撤回
手机上已经撤回成功
当然,这样简单地处理连自己撤回的消息都会失败,有兴趣的话自己在函数里判断撤回消息的 fromUsrName,再进一步决定是否要拦截。
Posts: 2
Participants: 1