ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

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

WeChatICU-ForMac源码解析(二):3行代码Method Swizzling与isRevoke防撤回Hook实现原理 WeChatICU-ForMac源码解析二3行代码Method Swizzling与isRevoke防撤回Hook实现原理【免费下载链接】WeChatICU-ForMacMac版企业微信消息防撤回, 聊天会话去水印项目地址: https://gitcode.com/gh_mirrors/we/WeChatICU-ForMacWeChatICU-ForMac 是一个 Mac 企业微信消息防撤回、会话去水印的开源小工具。作为源码解析系列的第二篇本文带你彻底看懂它最核心的实现如何用3 行代码的 Method Swizzling对isRevoke方法做一次 Hook让消息撤回变成不可能。一、项目回顾WeChatICU-ForMac 到底能做什么这个项目的功能非常聚焦主打两件事消息防撤回同事撤回消息后你的 Mac 企业微信上依然能看到原文不再出现XX撤回了一条消息的提示会话去水印聊天窗口背景不再显示你的姓名、企业名等信息。感兴趣可以先克隆仓库体验一下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运行时替换方法实现实现无感 Hookmethod_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),仅供参考
返回列表