ARTICLE DETAIL

资讯详情

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

从“特征码匹配失败“到逆向定位:剖析RevokeMsgPatcher的防撤回补丁查找机制

从“特征码匹配失败“到逆向定位:剖析RevokeMsgPatcher的防撤回补丁查找机制 从特征码匹配失败到逆向定位剖析RevokeMsgPatcher的防撤回补丁查找机制【免费下载链接】RevokeMsgPatcher:trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁我已经看到了撤回也没用了项目地址: https://gitcode.com/GitHub_Trending/re/RevokeMsgPatcher某个深夜微信自动更新到了新版本你重新打开RevokeMsgPatcher准备打上防撤回补丁弹出的却是这样一段提示特征比对当前特征码匹配数[0]和期望的匹配数[2]不一致。翻译成人话就是程序没有在微信的DLL里找到它要找的那串二进制特征补丁打不上了。RevokeMsgPatcher是一款针对PC端微信/QQ/TIM的防撤回补丁工具它通过直接修改目标程序DLL中的机器码绕过消息撤回逻辑。这听起来简单但真正的难点在于腾讯频繁更新版本、重排代码固定偏移量式的暴力修改很快失效。这篇文章不介绍怎么点按钮而是把项目RevokeMsgPatcher/Matcher/目录下的查找引擎拆开讲清楚特征码匹配失败背后的原因以及这套系统如何做到在数十个版本之间保持可用。一、问题根源为什么不能用固定偏移打补丁先看一个历史事实。项目早期的补丁方式是精准定位在配置中记录某个版本的DLL的SHA1哈希和一组绝对文件偏移打补丁时直接往这些偏移处写入新字节。在RevokeMsgPatcher.Assistant/Data/目录下的patch.json中这种信息至今仍保留着。{ Name: WeChatWin.dll, Version: 3.3.5.25, SHA1Before: 3e94753ccbc2799d98f3c741377e99bdae33b4cf, Changes: [ { Position: 3413977, Content: [235] }, { Position: 12159591, Content: [235] } ] }注意这里Content: [235]十进制235对应的十六进制是EB即x86指令集中的JMP短跳转。反撤回的核心手法就是把某条JE条件相等跳转改成JMP无条件跳转让撤回这条路径永远走不通——这也是wiki中那张7_je_to_jmp.png所演示的操作。这种方案的缺陷一目了然每一个新版本都要手工在调试器里重新定位偏移、重新计算SHA1劳动密集且脆弱。只要微信编译时插入或删除几条指令所有偏移全部失效。于是项目引入了第二套机制基于特征码的模糊匹配。二、核心机制一通配符驱动的特征码匹配特征码匹配的思路是不再记第3413977字节要改而是记这段机器码长什么样。配置里每条规则是一个ReplacePattern包含Search查找串和Replace替换串两组字节数组0x3F即?作为通配符代表这个位置的值随版本变化忽略不比较。以微信4.0.x的防撤回特征为例Search: 117 33 72 184 114 101 118 111 107 101 109 115 ... 63 63 63 63 ... Replace: 235 33 72 184 114 101 118 111 107 101 109 115 ... 63 63 63 63 ...117是JNZ235是JMP中间72 184 114 101 118 111 107 101 109 115正是ASCII字符串Revokemsg。整段特征既锁定了撤回功能入口的代码形态又通过通配符容忍了地址字段的差异。为什么通配符必须用0x3F而不是别的值因为?在ASCII中就是0x3F而这段特征码本身不会出现在合法指令中——0x3F对应的AAS指令在正常编译代码里几乎不出现冲突概率极低。这是FuzzyMatcher类里public const byte wildcard 0x3F;这一行的设计考量。三、核心机制二Boyer-Moore算法如何让百兆DLL秒级定位有了特征码接下来是搜索效率问题。微信的WeChatWin.dll动辄上百MB用朴素逐字节比较法在最坏情况下要比较几十亿次。项目在Matcher/BoyerMooreMatcher.cs中实现了经典的Boyer-Moore算法。这个算法的核心思想是从后往前匹配 失败时尽可能多跳。它通过预处理模式串构建两个启发式表坏字符表匹配失败时根据文本中出错的那个字节在模式串里最后一次出现的位置决定可以安全跳过多少字节。好后缀表当模式串后缀已经部分匹配时寻找这截后缀是否能在模式串更早的位置重新对齐。// Matcher/BoyerMooreMatcher.cs static int[] PreprocessToBuildBadCharactorHeuristic(byte[] pattern) { int m pattern.Length; int[] badCharactorShifts new int[AlphabetSize]; for (int i 0; i AlphabetSize; i) badCharactorShifts[i] m; // 默认可跳整个模式串长度 for (int i 0; i m; i) badCharactorShifts[pattern[i]] m - 1 - i; // 记录每个字节最后出现的位置 return badCharactorShifts; } public static bool TryMatch(byte[] text, byte[] pattern, out int firstShift) { firstShift -1; int n text.Length, m pattern.Length, s 0; 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; } // 两条启发式规则取较大者作为跳跃距离 s Max(goodSuffixShifts[j], badCharShifts[text[s j]] - (m - 1) j); } return false; }工程上还提供了MatchAll变体返回所有匹配位置——这一点很关键因为同一个特征码可能在DLL里出现多次微信的撤回校验在多个消息类型上各有一处。四、核心机制三两阶段模糊匹配把通配符开销降到最低通配符不能直接喂给Boyer-Moore——算法要求精确字节比较。FuzzyMatcher的处理方式是两阶段匹配用GetHead从特征码里截取第一个通配符之前的连续字节作为头串交给Boyer-Moore精确定位候选位置对每个候选位置调用IsEqual做全模式验证跳过通配符位置逐字节确认剩余部分。// Matcher/FuzzyMatcher.cs public 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(); } 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; }这套设计把带通配符的搜索转化成了普通精确搜索 少量逐字节确认让Boyer-Moore的高效跳跃能力得以保留。有一个边界值得注意头串不能以通配符开头否则GetHead会直接抛出不正确的通配符位置异常——特征码设计规范里应把首个通配符尽量后置既能保证头串长度、加快定位也能降低误命中率。五、实战落地ModifyFinder如何把匹配结果变成补丁动作匹配引擎之上是编排层Modifier/AppModifier.cs与Matcher/ModifyFinder.cs。补丁决策的完整链路是读取目标DLL通过文件版本号在FileCommonModifyInfos中按StartVersion~EndVersion区间选择适用的特征码规则组如3.9.11.0 版本 4.0.3.0使用防撤回(老)与多开两组特征用户勾选需要的功能类别防撤回/多开按Category过滤出ReplacePattern列表调用ModifyFinder.FindChanges得到所有需要写入的位置与字节先备份DLL为*.h.bak再执行写入失败则自动还原。FindChanges里最容易被忽略的是它的自检逻辑它统计匹配总数matchNum如果小于期望的规则数replacePatterns.Count并不直接失败而是进入IsAllReplaced做一次反向扫描——查找串一个都搜不到但替换串却能搜到说明该功能已经被打过了此时报错信息会区分全部已安装和部分已安装请取消勾选两种情况。// Matcher/ModifyFinder.cs 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( matchNum replacePatterns.Count, alreadyReplaced); }这套双向匹配是防止重复打补丁损坏文件的关键防线。下图展示了从定位字符串到修改跳转指令的完整逆向过程特征码正是从这类调试会话中提炼出来的六、三个最常见的匹配问题与排查思路1. 版本更新后特征码匹配数不一致这是本文开头的场景。原因通常是新版本里目标函数的指令序列变了。排查步骤用x64dbg附加进程搜索revokemsg等关键字符串定位到新版本对应的函数入口观察分支指令是否从JE变成了JNZ、TEST等形态对比新旧特征如果只是某个操作数变了把该字节改为0x3F通配符即可如果是指令整体重排需要重写整段特征项目策略是按版本区间维护多组特征新版本发布后由维护者追加一组StartVersion/EndVersion范围所以遇到此报错时第一时间应确认使用的补丁配置是否已覆盖当前版本。2. 你已经安装过此补丁但明明没有打过通常是残留的.h.bak备份或上一次失败操作导致的。可以手工校验删除目标DLL同目录下的.h.bak文件重新运行补丁程序让它重新备份若仍报错用原版安装包覆盖DLL后重试。不要直接手工改DLL字节SHA1校验会立刻识破。3. 通配符位置设计不当导致误匹配通配符太多或太靠前会让特征码退化成只匹配几个固定字节在百兆DLL里产生大量假阳性候选拖慢匹配甚至写错位置。经验规则是头串第一个通配符前的字节至少保留8~16字节通配符只用于明显的地址/立即数区域指令操作码部分必须精确。七、局限与展望这套系统已经经受住了微信从2.7到4.x、QQ从9.0到QQNT、以及TIM多个产品线的长期验证其精确SHA1定位与模糊特征码双轨并行的设计本质上是用配置驱动替代代码驱动让新版本适配成本大幅下降。但它也有固有局限特征码仍然依赖人工逆向提炼维护者需要持续跟进每一个版本File.ReadAllBytes将整个DLL读入内存对超过100MB的文件仍有优化空间源码中FindChanges的TODO 该逻辑需要优化注释也证实了这一点。如果想要参与这个项目最实际的入口就是从RevokeMsgPatcher.Assistant/Data/下的patch.json入手读懂一段特征码的语义、为某个新版本补充一组特征就是对这个工具最直接的贡献。【免费下载链接】RevokeMsgPatcher:trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁我已经看到了撤回也没用了项目地址: https://gitcode.com/GitHub_Trending/re/RevokeMsgPatcher创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表