| 
1 |  | -[TOC]  | 
2 |  | -# 震惊!!! macOS版微信竟可以这样消息防撤回  | 
3 |  | -## 一、 前言  | 
4 |  | -前一阵子入了 iOS 逆向的坑,整了个微信机器人,不过由于是用自己的证书打包,因此只能用7天,之后还得重新打包,实在麻烦。于是就拿macOS动刀了。     | 
5 | 1 | 
 
  | 
6 |  | -本篇主要制作 mac OS 版微信的插件,实现<a>消息防撤回与自动回复</a>的功能,从而熟悉 mac OS 插件制作,由于 ~~(lan ai)~~ mac OS 逆向分析与 iOS 类似,且不像 iOS 有那么多的工具,因此花费的时间较多,这里暂不阐述。~~之后有时间再整理 iOS 逆向分析过程。~~  | 
7 |  | - | 
8 |  | -* 基本原理:与 iOS 注入动态库类似,通过 app 启动时调用我们注入的动态库,从而进行 hook。  | 
9 |  | -* 插件 GitHub 地址: [WeChatPlugin](https://github.com/tusiji7/WeChatPlugin)  | 
10 |  | -* Demo 演示  | 
 | 2 | +本插件主要实现 macOS 版微信的<a>消息防撤回与自动回复</a>的功能,详细内容,请参考[我的博客](http://www.jianshu.com/p/7f65287a2e7a)  | 
 | 3 | +### Demo 演示  | 
11 | 4 | 
 
  | 
12 | 5 | 消息防撤回     | 
13 | 6 |   | 
14 | 7 | 
 
  | 
15 | 8 | 自动回复  | 
16 | 9 |   | 
17 | 10 | 
 
  | 
18 |  | -## 二、安装与使用  | 
19 |  | -* 下载 WeChatPlugin, 先进行 build (`command + B`),之后 run (`command + R`)即可启动微信,此时插件注入完成。~~(若出现 error 请往下看 **3.5 注意** 部分)~~  | 
20 |  | - | 
21 |  | -* 登录微信,可在**菜单栏-帮助**中看到消息防撤回与自动回复。  | 
 | 11 | +### 安装  | 
 | 12 | +* 下载 WeChatPlugin, 先进行 Build (`command + B`),之后 Run (`command + R`)即可启动微信,此时插件注入完成。  | 
 | 13 | +   | 
 | 14 | +* 若 Error,提示无权限,请对 WeChat 赋予权限。  | 
 | 15 | +`sudo chmod -R 777 /Applications/WeChat.app`  | 
 | 16 | +* 若 Error,提示找不到 Framework,先进行 Build。  | 
22 | 17 | 
 
  | 
 | 18 | +* 登录微信,在**菜单栏-帮助**中看到消息防撤回与自动回复即安装成功。  | 
23 | 19 |   | 
24 | 20 | 
 
  | 
 | 21 | +### 使用  | 
 | 22 | + | 
25 | 23 | * 消息防撤回:点击`开启消息防撤回`或者快捷键`command + t`,即可开启、关闭。  | 
26 | 24 | * 自动回复:点击`开启自动回复`或者快捷键`conmand + k`,将弹出自动回复设置的窗口,在窗口中输入关键字与回复内容,点击保存即可。~~(若无关键字保存,则所有消息都会自动回复)~~  | 
27 | 25 | 
 
  | 
28 | 26 |   | 
29 | 27 | 
 
  | 
30 |  | -* 卸载  | 
31 |  | - | 
32 |  | -    在 `/Applications/WeChat.app/Contents/MacOS` 目录中,删除 `WeChat` 与 `WeChatPlugin.framework`,将`WeChat_backup` 重命名为 `WeChat` 即可。  | 
33 |  | - | 
34 |  | -## 三、plugin 制作  | 
35 |  | -### 3.1 创建Framework  | 
36 |  | -使用 Xcode 创建 macOS 的 Cocoa Framework.  | 
37 |  | - | 
38 |  | - | 
39 |  | -  | 
40 |  | - | 
41 |  | -### 3.2 Edit Scheme…  | 
42 |  | -编辑 scheme,在 debug 模式下启动 WeChat。  | 
43 |  | -  | 
44 |  | - | 
45 |  | -  | 
46 |  | - | 
47 |  | -###3.3 添加Run Script  | 
48 |  | -在 Build Phases 中添加 run script  | 
49 |  | - | 
50 |  | -  | 
51 |  | - | 
52 |  | -script 内容如下  | 
53 |  | - | 
54 |  | -``` bash  | 
55 |  | -#!/bin/bash  | 
56 |  | -# 要注入的的app  | 
57 |  | -app_name="WeChat"  | 
58 |  | -# 此framework名字  | 
59 |  | -framework_name="WeChatPlugin"  | 
60 |  | -app_bundle_path="/Applications/${app_name}.app/Contents/MacOS"  | 
61 |  | -app_executable_path="${app_bundle_path}/${app_name}"  | 
62 |  | -app_executable_backup_path="${app_executable_path}_backup"  | 
63 |  | -framework_path="${app_bundle_path}/${framework_name}.framework"  | 
64 |  | -# 备份WeChat原始可执行文件  | 
65 |  | -if [ ! -f "$app_executable_backup_path" ]  | 
66 |  | -then  | 
67 |  | -cp "$app_executable_path" "$app_executable_backup_path"  | 
68 |  | -fi  | 
69 |  | -cp -r "${BUILT_PRODUCTS_DIR}/${framework_name}.framework" ${app_bundle_path}  | 
70 |  | -# 注入动态库  | 
71 |  | -./insert_dylib --all-yes "${framework_path}/${framework_name}" "$app_executable_backup_path" "$app_executable_path"  | 
72 |  | -```  | 
73 |  | - | 
74 |  | -**其中insert_dylib来源于[github](https://github.com/Tyilo/insert_dylib)(~~与iOS的insert_dylib不同~~)**  | 
75 |  | - | 
76 |  | -### 3.4 创建 main.mm  | 
77 |  | - | 
78 |  | -创建 main.mm 文件,添加构造方法。  | 
79 |  | - | 
80 |  | -  | 
81 |  | - | 
82 |  | -此时,一运行,即可执行`initalize`中的方法,并启动微信。  | 
83 |  | - | 
84 |  | -<a>因此,我们就可以在这里愉快的进行hook!!!</a>  | 
85 |  | - | 
86 |  | -### 3.5 **注意**  | 
87 |  | - | 
88 |  | -* 若 error,提示无权限,请对 WeChat 赋予权限。  | 
89 |  | -`sudo chmod -R 777 /Applications/WeChat.app`  | 
90 |  | -* 若 error,提示找不到 framework,先进行 build。  | 
91 |  | - | 
92 |  | -## 四、愉快的 hook (以撤回消息为例)  | 
93 |  | -### 4.1 创建 NSObject 分类  | 
94 |  | -新建 NSObject 分类,加入类方法`+(void)hookWeChat;`并在 main.mm 中执行该方法。之后所有的hook都可以在该类方法中进行。  | 
95 |  | - | 
96 |  | -```  | 
97 |  | -#import "WeChat+hook.h"  | 
98 |  | -
  | 
99 |  | -static void __attribute__((constructor)) initialize(void) {  | 
100 |  | -    NSLog(@"++++++++ WeChatPlugin loaded ++++++++");  | 
101 |  | -    [NSObject hook_WeChat];  | 
102 |  | -}  | 
103 |  | -```  | 
104 |  | - | 
105 |  | -### 4.2 寻找注入点  | 
106 |  | -首先使用`class-dump`,dump 出微信的头文件信息。~~(如何使用请左转[iOS 逆向 - 微信 helloWorld](http://www.jianshu.com/p/04495a429324))~~  | 
107 |  | -因为在 iOS 中,微信撤回的函数为`- (void)onRevokeMsg:(id)arg1;`因此,我们在微信的头文件中搜索该方法,最终在`MessageService.h`中找到。  | 
108 |  | - | 
109 |  | -### 4.3 runtime 登场  | 
110 |  | -到这里就要开始进行 hook 了,在`+(void)hookWeChat;`中进行`methodExchange`。  | 
111 |  | -将`MessageService`的`- (void)onRevokeMsg:(id)arg1;`方法实现替换成`NSObject`的`- (void)hook_onRevokeMsg:(id)msg`方法。  | 
112 |  | - | 
113 |  | -```  | 
114 |  | -+ (void)hookWeChat {  | 
115 |  | -    //      微信撤回消息  | 
116 |  | -    Method originalMethod = class_getInstanceMethod(objc_getClass("MessageService"), @selector(onRevokeMsg:));  | 
117 |  | -    Method swizzledMethod = class_getInstanceMethod([self class], @selector(hook_onRevokeMsg:));  | 
118 |  | -    if(originalMethod && swizzledMethod) {  | 
119 |  | -        method_exchangeImplementations(originalMethod, swizzledMethod);  | 
120 |  | -    }  | 
121 |  | -}  | 
122 |  | -
  | 
123 |  | -- (void)hook_onRevokeMsg:(id)msg {  | 
124 |  | -    NSLog(@"=== TK-LOG-msg = %@===",msg);  | 
125 |  | -    [self hook_onRevokeMsg:msg];  | 
126 |  | -}  | 
127 |  | -```  | 
128 |  | - | 
129 |  | -### 4.4 验证  | 
130 |  | -由于是使用 Xcode,就不用像 iOS 逆向那样只能用 lldb 调试了。可以在`- (void)hook_onRevokeMsg:(id)msg`中打个断点,然后撤回消息看是否会触发。结果证明该方法确实是微信消息撤回的处理方法。  | 
131 |  | - | 
132 |  | -### 4.5 使用 Hopper Disassembler  | 
133 |  | -接着我们在`- (void)hook_onRevokeMsg:(id)msg`中直接`return`就可以了。  | 
134 |  | -然而这时候看不到到底是撤回了哪一条信息。我们可以在用户撤回的时候将下面的内容改成"拦截 xx 的一条撤回消息:xxxx"。  | 
135 |  | -   | 
136 |  | -  | 
137 |  | - | 
138 |  | -这时候就要使用神器 `Hopper Disassembler`.用`hopper Disassembler` 进行分析,分析`- (void)onRevokeMsg:(id)arg1;`的实现。~~(分析过程与iOS类似,这里暂不阐述)~~  | 
139 |  | -最终得到了主要的代码实现。~~(完整代码在工程中)~~  | 
140 |  | - | 
141 |  | -```  | 
142 |  | -MessageService *msgService = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MessageService")];  | 
143 |  | -MessageData *revokeMsgData = [msgService GetMsgData:session svrId:[newmsgid integerValue]];  | 
144 |  | -MessageData *newMsgData = ({  | 
145 |  | -        MessageData *msg = [[objc_getClass("MessageData") alloc] initWithMsgType:0x2710];  | 
146 |  | -        [msg setFromUsrName:revokeMsgData.toUsrName];  | 
147 |  | -        [msg setToUsrName:revokeMsgData.fromUsrName];  | 
148 |  | -        [msg setMsgStatus:4];  | 
149 |  | -        [msg setMsgContent:newMsgContent];  | 
150 |  | -        [msg setMsgCreateTime:[revokeMsgData msgCreateTime]];  | 
151 |  | -        [msg setMesLocalID:[revokeMsgData mesLocalID]];  | 
152 |  | -          | 
153 |  | -        msg;  | 
154 |  | -    });  | 
155 |  | -      | 
156 |  | -[msgService AddLocalMsg:session msgData:newMsgData];  | 
157 |  | -```  | 
158 |  | - | 
159 |  | -## 五、效果  | 
160 |  | -点击 `菜单栏-帮助-开启消息防撤回`,当好友撤回消息是可以看到提示。  | 
161 |  | -  | 
162 |  | - | 
163 |  | -## 六、小结  | 
164 |  | -最终我们得到了拥有消息防撤回与自动回复的 macOS 版微信,虽然整个过程挺简单的,但主要目标是为了熟悉了如何制作 macOS 插件的过程,这样以后就可以给 macOS 上的 app 增加点小功能了。  | 
165 |  | - | 
166 |  | -由于本人还只是个逆向新手,难免会有所疏漏,还请大牛们指正。  | 
167 |  | -本项目仅供学习参考。  | 
168 |  | - | 
169 |  | -## 七、参考  | 
 | 28 | +### 卸载  | 
170 | 29 | 
 
  | 
171 |  | -[如何愉快地在Mac上刷朋友圈](http://www.iosre.com/t/mac/7014/2)  | 
 | 30 | +在 `/Applications/WeChat.app/Contents/MacOS` 目录中,删除 `WeChat` 与 `WeChatPlugin.framework`,将`WeChat_backup` 重命名为 `WeChat` 即可。  | 
172 | 31 | 
 
  | 
0 commit comments