微信悄悄更新后,防撤回补丁为什么还能认出旧代码

📅 2026/8/14 9:44:29
微信悄悄更新后,防撤回补丁为什么还能认出旧代码
微信悄悄更新后防撤回补丁为什么还能认出旧代码【免费下载链接】RevokeMsgPatcher:trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁我已经看到了撤回也没用了项目地址: https://gitcode.com/GitHub_Trending/re/RevokeMsgPatcher凌晨一点微信弹出了版本更新完成。你心头一紧——果然那个用了半年的防撤回补丁失效了。你打开 RevokeMsgPatcher 想重新打一遍却发现它不但认出了新版本的微信还自动帮你找好了该改哪个字节。你也许会好奇一个连源代码都没有的二进制文件它是怎么做到认人的RevokeMsgPatcher 是 PC 端微信/QQ/TIM 的防撤回补丁工具核心思路是直接修改安装目录里的 DLL 文件把是否允许撤回的判断指令改掉。而这件事最难的环节恰恰是如何在一份几十上百 MB 的二进制文件里又快又准地找到那个该改的字节。第一版思路记住门牌号别管里面住了谁在最朴素的方案里补丁根本不用找什么。开发者在调试器里把微信的 WeChatWin.dll 翻个底朝天锁定某个je跳转指令所在的绝对偏移位置比如第 3413977 个字节然后把它的操作码从0x74条件跳转改成0xEB无条件跳转。在老的patch.json里你还能看到这种打法的痕迹——每条记录都带着版本号、SHA1 前后校验值和固定坐标Version: 3.3.5.25 SHA1Before: 3e94753c... Changes: [Position: 3413977, Content: [235]], [Position: 12159591, Content: [235]]这套方案像什么呢像你给朋友留地址直接写小区 3 栋 502 室。好处是干脆利落一次定位、直接改完还能用 SHA1 验证文件有没有被动过。但它有个致命弱点只要开发商重新编译一次哪怕只加了一行代码整个 DLL 的偏移量全部错位地址就作废了。微信的更新频率有多勤维护成本就有多高——每出一个版本就要重新逆向一次、重新记一次门牌号。这条路显然走不通。换个思路不记地址改记长相偏移量会漂移但机器码的特征不太会变。比如判断某次调用是否成功通常长这样先test一下寄存器再跟一个je条件跳转。只要逻辑没改这段字节的长相就还是那副样子。于是 RevokeMsgPatcher 换了个策略把要改的那条指令 周围一段固定上下文整体提取出来作为特征码去文件里找。找到之后替换掉其中关键的跳转字节即可。看这条微信旧版防撤回特征Search00 85 C0 74 32 B9 3F 3F 3F 11 8AReplace00 85 C0 EB 32 B9 3F 3F 3F 11 8A发现没只有第 4 个字节从74je 条件跳转变成了EBjmp 无条件跳转其余不动。因为程序逻辑变了无所谓只要这段长相还在我们就能按图索骥。但问题来了这些特征码里出现的3F是什么总不能要求每一个字节都严格一致吧。通配符0x3F给特征码留出随便填的空位二进制特征码最烦人的地方在于不是每个字节都那么稳定。像指令里的立即数、内存偏移在不同构建版本里说变就变。如果你把整条特征写死稍微一更新又失效了。RevokeMsgPatcher 的解决办法相当取巧约定0x3F作为通配符这个位置的字节爱是什么是什么跳过不比较。为什么要选0x3F因为它的 ASCII 码正好是问号?天然自带此处随意的气质而且它在 x86 机器码里极少出现在关键位置误伤概率低。匹配时的逻辑写在 RevokeMsgPatcher/Matcher/FuzzyMatcher.cs 里核心就是逐个字节比对见到通配符就跳过public static bool IsEqual(byte[] content, int start, byte[] whole) { int i 0; for (i 0; i whole.Length; i) { if (whole[i] wildcard) // 通配符跳过比较 { continue; } if (content[start i] ! whole[i]) // 非通配符位置必须严格相等 { break; } } return i whole.Length; // 全部非通配符位置都对上才算命中 }你可能会问这不就是普通的字符串匹配吗逐字节比下去不就行了问题是不行——我们面对的不是几 KB 的文本而是动辄上百 MB 的 WeChatWin.dll。如果每个候选位置都从头比到尾那是个天文数字。两段式匹配先用确定的头快跑再用模糊的身体验身这里藏着整个设计最聪明的部分。观察一下上面的特征码通配符几乎都出现在中段或后段而开头往往是连续、稳定的几个字节。比如微信 4.x 那条75 21 48 B8 ...前 4 个字节全是实打实的。FuzzyMatcher 因此采用了头串精确 全串模糊的两段式策略代码在 RevokeMsgPatcher/Matcher/FuzzyMatcher.cspublic static int[] MatchAll(byte[] content, byte[] pattern) { byte[] head GetHead(pattern); // 截取第一个通配符之前的所有字节 int[] indexs BoyerMooreMatcher.MatchAll(content, head); // 先用精确算法快速定位候选点 if (head.Length pattern.Length) { return indexs; // 没有通配符直接用结果 } Listint res new Listint(); foreach (int index in indexs) { if (IsEqual(content, index, pattern)) // 逐个候选点做全模式模糊校验 { res.Add(index); } } return res.ToArray(); }第一步把特征码里第一个3F之前的字节切出来当头串用精确匹配算法在文件里飞快扫一遍——这一轮只认确定性的字节跑得特别快第二步拿扫出来的少数几个候选位置做完整的、带通配符的逐字节验证。用确定的部分换速度用模糊的部分换兼容性两头都占。顺带一提那个精确匹配用的可不是朴素的双层循环而是Boyer-Moore 算法实现在 RevokeMsgPatcher/Matcher/BoyerMooreMatcher.cs。它最大的特点是匹配从模式串末尾开始失败时靠坏字符规则和好后缀规则一次性跳过一大段而不是老老实实只挪一格。你可以把它想象成查字典时不会从第一个字开始死磕而是直接翻到看起来差不多的那页——在大文件里这个跳跃带来的收益是数量级的。匹配上了然后呢数数对不对定位只是第一步。真正决定这个补丁敢不敢下手的是匹配完之后那一大段校验逻辑全部写在 RevokeMsgPatcher/Matcher/ModifyFinder.cs 里。核心思想就一句话数个数必须和预期完全一致多一个少一个都不行。foreach (ReplacePattern pattern in replacePatterns) { int[] matchIndexs FuzzyMatcher.MatchAll(fileByteArray, pattern.Search); if (matchIndexs.Length 1) { for (int i 0; i matchIndexs.Length; i) { matchNum; // 这个位置已经和替换串一样了说明补丁早打过了不再重复改 if (!FuzzyMatcher.IsEqual(fileByteArray, matchIndexs[i], pattern.Replace)) { changes.Add(new Change(matchIndexs[i], pattern.Replace)); } } } } if (matchNum replacePatterns.Count) { // 数量对不上要么已经打过补丁要么特征码失效了 throw new BusinessException(match_inconformity, ...特征码匹配数和期望的匹配数不一致...); }为什么这么较真因为**找不到特征码和已经打过补丁是两回事**但表面症状相同——都表现为匹配数量不足。工具在这里进一步做了个反向探测既然搜索串没了那就再搜一下替换串Replace。如果替换串能搜到说明是补丁已装如果替换串也搜不到那才是真的特征码失效。这两种情况一个提示请取消勾选一个提示请联系作者处理路径完全不同。这套双向匹配的严谨程度说实话远超一个个人工具的必要水平但正是它保证了打补丁失败时绝不会留下一个改到一半的 DLL。踩坑实录版本区间是怎么被特征码稀释的现在你大概理解了为什么工具界面上会显示3.5.0.28 ~ 3.6.0.0支持特征防撤回这样的提示。每个版本区间的特征码配置都独立维护在 RevokeMsgPatcher/Model/CommonModifyInfo.cs 对应的数据里一个区间可能同时配多条特征比如防撤回和多开各一条每条又允许带通配符。实际维护中最让人头疼的是这两件事通配符不能乱放。代码里GetHead明确抛异常如果第一个通配符出现在第 0 位说明特征码开头就没法确定头串为空两段式匹配直接失效。特征是拿来认人的脸可以模糊但额头必须清楚。特征码会撞车。你搜74 32可能在文件里命中好几处。这时matchNum会比预期多工具宁可报错也不乱改——宁可不打不可打错毕竟改坏一个字节微信可能直接崩溃。权衡与取舍快、准、稳只能保两样回看这套方案处处是取舍性能 vs 灵活性Boyer-Moore 用头串精确换速度通配符用模糊验证换兼容代价是每次匹配要多一次全模式复查。好在这个复查只发生在少数候选点上成本可控。维护成本 vs 适配范围按版本区间维护特征码比按单个版本维护绝对偏移省力得多——微信更新十次特征可能只需微调一次。代价是特征码的编写极度依赖逆向经验一个区间一条特征写错整片用户全挂。强校验 vs 用户体验宁可抛出特征码不匹配劝退用户也不猜着改。牺牲了一点便利换来了打补丁从不出错的口碑。这套精确定位 模糊验证 数量强校验的组合本质上是一个很朴素的思想机器码的世界里没有魔法只有把稳定和可变分开处理的老实功夫。下回你再看工具弹出的那一行特征比对失败提示大概就能读懂它背后的潜台词了——它不是在抱怨而是在说这个版本的微信我还没摸清长相别急等等作者更新特征库。如果你也恰好是那个愿意在 x64dbg 里蹲上几小时的人不妨翻翻 RevokeMsgPatcher/Matcher/ 里的源码想想还能怎么优化这套匹配流程。【免费下载链接】RevokeMsgPatcher:trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁我已经看到了撤回也没用了项目地址: https://gitcode.com/GitHub_Trending/re/RevokeMsgPatcher创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考