news 2026/8/23 16:49:21

WeChatICU-ForMac源码解析(二):3行代码Method Swizzling与isRevoke防撤回Hook实现原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WeChatICU-ForMac源码解析(二):3行代码Method Swizzling与isRevoke防撤回Hook实现原理

WeChatICU-ForMac源码解析(二):3行代码Method Swizzling与isRevoke防撤回Hook实现原理

【免费下载链接】WeChatICU-ForMacMac版企业微信消息防撤回, 聊天会话去水印项目地址: https://gitcode.com/gh_mirrors/we/WeChatICU-ForMac

WeChatICU-ForMac 是一个 Mac 企业微信消息防撤回、会话去水印的开源小工具。作为源码解析系列的第二篇,本文带你彻底看懂它最核心的实现:如何用3 行代码的 Method Swizzling,对isRevoke方法做一次 Hook,让消息"撤回"变成不可能。

一、项目回顾:WeChatICU-ForMac 到底能做什么?

这个项目的功能非常聚焦,主打两件事:

  1. 消息防撤回:同事撤回消息后,你的 Mac 企业微信上依然能看到原文,不再出现"XX撤回了一条消息"的提示;
  2. 会话去水印:聊天窗口背景不再显示你的姓名、企业名等信息。

感兴趣可以先克隆仓库体验一下:

git clone https://gitcode.com/gh_mirrors/we/WeChatICU-ForMac

💡 整个项目的核心源码只有一个不到 50 行的 Objective-C 文件,编译产物是一个.dylib动态库。麻雀虽小,五脏俱全——它把 Objective-C Runtime 的两大"绝活"用到了极致。

二、消息撤回的真相:HookisRevoke为什么就够了?

要理解防撤回,先要知道企业微信的"撤回"是怎么工作的。

⚠️ 关键认知:撤回一条消息,服务端并不是把消息内容删掉,而是给它打上一个"已撤回"的标记。

消息在客户端以"消息模型对象"的形式存在,对象上有一个表示撤回状态的属性,通常就是一个isRevoke(是否已撤回)这样的只读方法:

  • 消息正常 →isRevoke返回NO
  • 对方撤回 →isRevoke返回YES

聊天列表的 UI 层在渲染每一条消息时,会主动询问对象:"你被撤回了吗?"

  • 答案是"是" → 界面隐藏原文,显示"XX撤回了一条消息"
  • 答案是"否" → 界面照常显示原文

所以逻辑链条一目了然:

UI 渲染消息 → 调用 isRevoke() → 根据返回值决定显示方式

如果我们能让isRevoke永远回答"否",UI 层就永远认为消息没被撤回——原文照常显示,撤回提示自然消失。防撤回的"终极开关",其实只在这一个方法的返回值上。

这正是 WeChatICU-ForMac 的思路:不去破解协议、不去拦截网络请求,只在 UI 询问的那一刻"篡改答案"。简单、优雅、副作用极小。

三、Method Swizzling 是什么?新手友好的解释

isRevoke是厂商代码,我们不能改它的源码,那怎么让它"永远返回 NO"?

这里就轮到 Objective-C Runtime 的经典技术登场了:Method Swizzling(方法交换)

用一个生活化的比喻:

  • 消息对象的isRevoke方法,就像餐厅门口的招牌——别人喊的是招牌名字,但进去后吃到的,是招牌背后的厨房(实现)
  • Objective-C 的调用机制是"按名字查表找实现",而 Runtime 允许你在程序运行时修改这张"名字 → 实现"的映射表
  • Method Swizzling 就是:把isRevoke这块招牌背后指向的厨房,偷偷换成我们自己的小作坊。

从此以后,任何代码调用isRevoke,拿到的都是我们"定制"的结果,而调用方对此毫无察觉。📌

四、3行代码核心:替换isRevoke的实现

下面就是 WeChatICU-ForMac 防撤回功能的核心(示意还原,实际项目代码极其精简):

第一步,写一个"假实现":

// 不管消息处于什么状态,永远告诉 UI:"我没被撤回" BOOL fake_isRevoke(id self, SEL _cmd) { return NO; }

第二步,3 行代码完成 Swizzling:

// 第1行:找到消息模型类上的 isRevoke 方法 Method m = class_getInstanceMethod(msgClass, @selector(isRevoke)); // 第2行:准备"假实现"函数指针 IMP fake = (IMP)fake_isRevoke; // 第3行:把方法实现替换掉(核心就是这一行) method_setImplementation(m, fake);

三行代码各司其职,非常清晰:

代码作用通俗理解
class_getInstanceMethod按类名 + 方法名,从 Runtime 查到方法记录找到招牌
(IMP)fake_isRevoke把自己的函数取地址,转成方法实现类型准备好新厨房
method_setImplementation直接覆盖该方法的实现换掉招牌背后的厨房

📌一个常见疑问:教科书里 Swizzling 常用method_exchangeImplementations(交换两个方法的实现,便于原实现留作他用)。但防撤回场景根本不需要保留原逻辑——我们就是要"无条件返回 NO",所以项目用了更直白的method_setImplementation直接覆盖,一步到位。

小提示:msgClass是消息模型类,类名需要通过逆向手段(如 class-dump 或动态调试)在企业微信进程中确认。不同企业微信版本的类名可能变化,这也是这类工具需要"跟版本"的原因。

五、如何让 Hook 自动生效?构造器 + 注入的"组合拳"

改完代码还有个现实问题:企业微信是厂商的应用,我们怎么让自己的代码跑进它的进程?

WeChatICU-ForMac 用了一套 macOS 上的经典组合拳,分两步:

第一步:利用 dylib 构造器,加载即自动执行 Hook

// 这个函数会在动态库被加载的瞬间自动执行,无需任何调用 __attribute__((constructor)) static void WeChatICUInit(void) { // 在这里执行上面那"3行代码" install_isRevoke_hook(); }

__attribute__((constructor))是 Objective-C / C 的一个特性:被它标记的函数会在.dylib被加载进内存时自动调用。相当于给 Hook 装上了"开机自启"。

第二步:通过 DYLD_INSERT_LIBRARIES 把 dylib 注入企业微信进程

项目的启动脚本思路很直接(伪代码示意):

# 1. 退出正在运行的企业微信 killall WeCom # 2. 带上注入环境变量重新启动 DYLD_INSERT_LIBRARIES=/path/to/WeChatICU.dylib \ /Applications/WeCom.app/Contents/MacOS/WeCom &

DYLD_INSERT_LIBRARIES是动态链接器(dyld)提供的环境变量:程序启动加载动态库时,会优先把指定的 dylib 注入进程。我们的 Hook 库就这样"搭便车"进入企业微信,构造器随即触发,3 行 Swizzling 完成,防撤回静默生效。✅

去水印功能也是同一套技术:Hook 水印绘制相关的绘制方法,让它跳过姓名、企业名称的绘制,界面背景瞬间"干净"。

六、总结:小项目里的大智慧

用一张表回顾本篇的知识点:

技术点在项目中的角色
isRevoke方法撤回状态的"总开关",UI 渲染的依据
Method Swizzling运行时替换方法实现,实现无感 Hook
method_setImplementation3 行代码中的关键一击,直接覆盖实现
__attribute__((constructor))dylib 加载时自动执行 Hook,免手动调用
DYLD_INSERT_LIBRARIES把 Hook 库注入企业微信进程

💡 这个项目值得新手反复品味的地方在于:它没有炫技,只是精确找到了"撤回提示"这条链路的最短切入点——一个布尔方法——然后用 Runtime 最基础的能力去改它。理解了isRevoke这一处,整个 macOS 逆向 Hook 的套路(找切入点 → 写假实现 → Swizzling → 注入进程)也就通了。

最后提醒:作者已在项目说明中表示 WeChatICU 停止维护,精力转向 WeChatExtension;且此类 Hook 依赖企业微信的具体内部实现,版本大更新后可能失效,使用前请留意兼容性与自身账号安全。⚠️

【免费下载链接】WeChatICU-ForMacMac版企业微信消息防撤回, 聊天会话去水印项目地址: https://gitcode.com/gh_mirrors/we/WeChatICU-ForMac

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/23 16:48:55

HyperRogue的20+种几何投影全解析:从圆盘到鱼眼的渲染秘密

HyperRogue的20种几何投影全解析:从圆盘到鱼眼的渲染秘密 【免费下载链接】hyperrogue A SDL roguelike in a non-euclidean world 项目地址: https://gitcode.com/gh_mirrors/hy/hyperrogue HyperRogue 是一款运行在非欧几里得(双曲)…

作者头像 李华
网站建设 2026/8/23 16:37:13

帧缓存还是管道直传?timecut 两种帧传输模式详解与选型指南

帧缓存还是管道直传?timecut 两种帧传输模式详解与选型指南 【免费下载链接】timecut Node.js program to record smooth movies of web pages with JavaScript animations 项目地址: https://gitcode.com/gh_mirrors/ti/timecut timecut 是一个能把网页 Jav…

作者头像 李华
网站建设 2026/8/23 16:31:34

近红外光谱笔记

补充:OTSU算法 采用OTSU算法对灰度图像(玉米种子区域标记为“1或白色”背景区域标记为“0或黑色”)进行背景分割,得到最优阈值。接下来,对二值图像中的玉米种子(连通区域)进行编号。随后,将带有序列号的二值图像与每个波长的灰度图…

作者头像 李华