ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

撤回的消息到底藏哪了?防撤回补丁如何在百MB的DLL里精准定位一行字节

撤回的消息到底藏哪了?防撤回补丁如何在百MB的DLL里精准定位一行字节 撤回的消息到底藏哪了防撤回补丁如何在百MB的DLL里精准定位一行字节【免费下载链接】RevokeMsgPatcher:trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁我已经看到了撤回也没用了项目地址: https://gitcode.com/GitHub_Trending/re/RevokeMsgPatcher深夜的微信群里有人发错了消息又秒撤回你反复刷新聊天记录只有一句冷冰冰的某某撤回了一条消息。防撤回补丁的原理说来并不神秘——它直接改写微信的 DLL 文件把撤回这条指令的开关从开改成关。但真正让开发者头疼的是特征码匹配这道题DLL 有上百 MB里面密密麻麻全是机器字节你怎么知道该改哪几个字节改错了微信直接崩溃怎么办本文就带你把这套在二进制世界里大海捞针的技术拆开揉碎看个明白。先认识敌人为什么不能靠固定位置打补丁如果你是第一次接触二进制补丁可能会想既然要改微信的某个功能那把那个位置记下来每次直接改不就行了想法很美好现实很骨感。微信的WeChatWin.dll动辄上百 MB而且几乎每个版本都在变。微信团队每次编译代码结构、函数位置、指令顺序都会微调。你今天记住的偏移量3413977下个版本就变成一坨完全无关的数据直接改写只会让程序崩溃。打个比方这就像你要在一本不断再版重排的字典里找某个生僻字页码每次都在变你只能靠这个字长什么样来定位而不是靠它在第几页。所以 RevokeMsgPatcher 的做法是通过特征码一串固定的字节片段来认人只要这串字节在文件里出现就能反推出这就是我要改的地方。匹配模块就集中在 Matcher/ 目录下。特征码长什么样一串带问号的字节打开项目的数据目录 RevokeMsgPatcher.Assistant/Data/里面按版本号分文件夹存放着patch.json微信每个版本的补丁规则都在里面。每条规则是一个搜索串 替换串对Search: [15, 31, 68, 0, 0, 73, 139, 80, 8, 72, 133, 210, 116, 63, 72, 199, 193] Replace: [15, 31, 68, 0, 0, 73, 139, 80, 8, 72, 133, 210, 117, 63, 72, 199, 193]等等这一串数字是什么鬼别慌这是把二进制文件按字节翻译成数字0-255 之间每个数字代表一个字节也就是 8 个比特。用十六进制看更直观——116等于0x74117等于0x75。前者是 x86 指令JE相等则跳转撤回时跳走后者是JNE不相等才跳。你看整个补丁的本质就是把JE换成JNE让撤回这个动作永远走不到生效分支。项目里用 x32dbg 调试器定位撤回逻辑的过程也印证了这个思路注意上面 Search 串里有个63它其实是0x3F也就是 ASCII 里的?。在项目里0x3F被约定为通配符——这个位置的字节可以随便变匹配时直接跳过。为什么要这样因为微信不同版本里同一条指令周围的某个操作数比如一个跳转偏移地址往往会变而核心指令不变。把容易变的位置打成问号一条特征码就能覆盖多个版本这就是模糊匹配的由来。朴素匹配为什么不行从一行行找到跳着找假设我们已经拿到了一串特征码怎么在百 MB 的字节流里找到它最笨的办法是逐字节对齐、逐个比较从文件头开始先比 0 号位置是不是特征码不是就挪到 1 号再比……直到找到。这在英文里叫 Brute Force暴力搜索优点是简单缺点是慢得离谱——文件越大越要命。RevokeMsgPatcher 选了更聪明的Boyer-Moore 算法BM 算法核心思想只有一句话匹配失败时不要只挪一格尽量多跳几步。它靠两个跳远规则实现坏字符规则从后往前比遇到对不上的字节时看看这个字节在特征码里最后一次出现在哪直接挪到让两者对齐的位置好后缀规则已经比过的后半段如果在特征码里还有更靠前的一次出现就直接挪过去对齐。这两条规则在匹配前先对特征码做一次预处理把每个字节该跳多远算成一张表匹配时查表就行。核心代码在 BoyerMooreMatcher.csTryMatch方法展示了主循环的跳跃逻辑public static bool TryMatch(byte[] text, byte[] pattern, out int firstShift) { firstShift -1; int n text.Length; int m pattern.Length; int s 0; // s 是模式串特征码相对文本的偏移量 // 预处理构建坏字符和好后缀两个跳跃表 int[] badCharShifts PreprocessToBuildBadCharactorHeuristic(pattern); int[] goodSuffixShifts PreprocessToBuildGoodSuffixHeuristic(pattern); while (s (n - m)) { int j m - 1; // 从特征码最后一个字节开始从后往前比 while (j 0 pattern[j] text[s j]) { j--; } if (j 0) // 全部对上找到 { firstShift s; return true; } else // 失败两条规则谁跳得远听谁的 { s Max(goodSuffixShifts[j], badCharShifts[(int)text[s j]] - (m - 1) j); } } return false; }一句话总结普通找法是一格一格爬BM 算法是踩着弹簧跳跳过的地方越多跑完整个文件就越快。对微信这种体积的文件性能差距是几十倍的。 小提示BM 算法最适合特征码长 文件大的场景。特征码越短跳跃收益越不明显所以设计特征码时尽量取足够长的固定片段。模糊匹配的两段式套路先粗筛再精验光有精确匹配还不够。前面说了特征码里带0x3F通配符BM 算法可比不了问号。怎么办项目在 FuzzyMatcher.cs 里玩了个漂亮的两段式粗筛把特征码里第一个通配符前面的头串抠出来这段是固定的、不含通配符的交给 BM 算法快速找出所有候选位置精验对每个候选位置用完整特征码含通配符逐字节验证通配符位置直接跳过。先粗筛后精验既保留了 BM 的速度又拿到通配符的灵活性。看核心代码IsEqual它负责验证整个特征码是否匹配public static bool IsEqual(byte[] content, int start, byte[] whole) { int i 0; for (i 0; i whole.Length; i) { if (whole[i] wildcard) // 0x3F通配符位置跳过比较 { continue; } if (content[start i] ! whole[i]) // 非通配符必须严格相等 { break; } } return i whole.Length; // 全部比完都没跳出说明匹配成功 }而MatchAll就是那个指挥官先GetHead抠头串交给BoyerMooreMatcher.MatchAll拿候选位置再逐个IsEqual过滤最后返回所有真正命中特征码的位置。整个过程在 FuzzyMatcher.cs 里只有二十来行思路非常清爽。总指挥 ModifyFinder匹配对了还要防呆找到了匹配位置是不是就能直接开改了还没完。这里藏着一个工程上的大坑如果用户已经打过一次补丁再打一次会怎样你想补丁的本质是把Search串改成Replace串。如果已经改过了文件里存的就不再是Search而是Replace。这时候再拿Search去找自然一个都找不到程序就会一脸懵地报特征码不匹配。ModifyFinder.cs 的FindChanges就是来处理这些意外情况的。它的思路非常工程化读入整个 DLL 字节数组对每个替换规则用模糊匹配找出所有命中Search的位置记入匹配总数数量校验匹配数小于期望值不直接报错先调用IsAllReplaced反向检查——用Replace串再去搜一遍文件如果Search 搜不到、Replace 搜得到说明这个补丁已经打过了抛出已安装的友好提示而不是一句冷冰冰的匹配失败。private static Tuplebool, SortedSetstring IsAllReplaced( byte[] partByteArray, ListReplacePattern replacePatterns) { SortedSetstring alreadyReplaced new SortedSetstring(); foreach (ReplacePattern pattern in replacePatterns) { int[] searchMatchIndexs FuzzyMatcher.MatchAll(partByteArray, pattern.Search); int[] replaceMatchIndexs FuzzyMatcher.MatchAll(partByteArray, pattern.Replace); // 查找串找不到但替换串找得到 → 这个功能已经被补过了 if (searchMatchIndexs.Length 0 replaceMatchIndexs.Length 0) { alreadyReplaced.Add(pattern.Category); } } return new Tuplebool, SortedSetstring(false, alreadyReplaced); }看到pattern.Category了吗这正是防撤回补丁的一个贴心设计——每个功能防撤回老防撤回带提示新多开都有独立的特征码组和类别名。当用户勾选了某个功能却发现它已经装过时程序能精确告诉你是哪几个功能撞车了而不是甩给你一段天书。双保险精确位置 模糊特征码还要有后悔药读到这里你可能还有个疑问既然特征码匹配这么强为什么patch.json里还保存了一堆固定位置和 SHA1 值看 FileHexEditor.cs 就知道项目其实走的是双轨制版本精确制FileModifyInfos先计算 DLL 的 SHA1 值相当于文件的指纹和库里记录的某个版本的 SHA1 对上就直接用该版本记录好的固定偏移量去改稳妥可靠版本范围制FileCommonModifyInfosSHA1 对不上比如太新的版本就退回到版本范围 特征码模糊匹配的路线用 Search/Replace 串去识别。一个精确、一个灵活两者互为兜底。新版微信一发布旧特征码失效是大概率事件这时靠 SHA1 匹配不到就要靠社区及时补充新的特征码。作者在 README 里也坦诚本人不参与方法寻找仅做特征搬运——特征码的逆向分析由社区高手完成项目负责把规则工程化、自动化。另外FileHexEditor.cs 的Backup()方法会在打补丁前把原始 DLL 复制成*.h.bak备份文件。一旦微信更新后出问题Restore()一键还原这就是后悔药。补丁工具敢改系统文件底气全在备份机制上——改坏了也能无损恢复。一次完整的防撤回之旅把前面所有零件拼起来一次打补丁的完整流程是这样的程序读取微信安装路径找到WeChatWin.dll先算 SHA1 确认版本根据版本加载对应的特征码规则组BM 算法 通配符模糊匹配定位所有需要修改的字节位置数量校验 已打补丁检测给用户清晰的中文提示确认无误后备份原始文件执行字节替换微信启动时加载修改后的 DLL撤回消息从此有去有回。微信每次更新后SHA1 和特征码都可能失效所以 README 里反复强调更新后要重新安装补丁。这就是特征码匹配技术的宿命——你永远在追着版本跑。还能怎么玩进阶方向与参与入口掌握了这套匹配技术值得探索的方向还有不少特征码自学习目前每版特征码靠人工逆向提取未来可以尝试用版本间 diff 对比半自动生成新特征码把人工成本降下来内存占用优化现在FindChanges是一次性把整个 DLL 读进内存对更大体积的目标文件可以考虑分块读取 流式匹配更多指令级技巧通配符目前只支持单字节0x3F能否扩展成连续 N 字节任意甚至范围匹配值得一试把玩法搬去别的软件这套特征码匹配 冲突检测 备份还原的套路对任何二进制补丁工具比如游戏汉化、软件去广告都通用。如果你在某个新版微信/QQ 上发现特征码失效或者想贡献一套新特征码可以到项目的 Issues 区提交反馈说明软件版本号和报错截图即可。作者也明确欢迎社区的 PR 来补充各版本的patch.json数据——毕竟特征码匹配的核心竞争力从来不只是算法更是那份不断有人维护的版本数据库。想亲手研究源码克隆仓库后重点看三个文件BoyerMooreMatcher.cs跳着找、FuzzyMatcher.cs带问号找、ModifyFinder.cs总指挥。看懂这三个文件你就掌握了二进制补丁最核心的命脉。【免费下载链接】RevokeMsgPatcher:trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁我已经看到了撤回也没用了项目地址: https://gitcode.com/GitHub_Trending/re/RevokeMsgPatcher创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表