├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── new_version.yml └── workflows │ └── build.yml ├── .gitignore ├── Images ├── give_a_star.png ├── logo.png ├── logo_old.jpg ├── revoke.jpg ├── screenshot.mi.png ├── screenshot.png ├── screenshot.v0.4.png ├── screenshot.v0.5.png ├── screenshot.v0.6.png ├── screenshot.v0.7.png ├── screenshot.v0.8.png ├── screenshot.v0.9.png ├── watch_release.png └── wiki │ ├── qq │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png │ └── wechat │ ├── 10_mutex.png │ ├── 11_push_ebp_to_ret.png │ ├── 12_push_ebp_to_ret.png │ ├── 13_push_ebp_to_ret.png │ ├── 14_patch_dll.png │ ├── 1_start.png │ ├── 2_click_attach.png │ ├── 3_attach_wechat_exe.png │ ├── 4_wechatwin_dll.png │ ├── 5_search_string.png │ ├── 6_revokemsg_1.png │ ├── 7_je_to_jmp.png │ ├── 8_je_to_jmp.png │ └── 9_je_to_jmp.png ├── LICENSE ├── README.md ├── RevokeMsgPatcher.Assistant ├── App.config ├── Data │ ├── 0.7 │ │ └── patch.json │ ├── 0.8 │ │ └── patch.json │ ├── 0.9 │ │ └── patch.json │ ├── 1.0 │ │ └── patch.json │ ├── 1.1 │ │ └── patch.json │ ├── 1.2 │ │ └── patch.json │ ├── 1.3 │ │ └── patch.json │ ├── 1.4 │ │ └── patch.json │ ├── 1.5 │ │ └── patch.json │ ├── 1.6 │ │ └── patch.json │ ├── 1.7 │ │ └── patch.json │ ├── 1.8 │ │ └── patch.json │ ├── 1.9 │ │ └── patch.json │ └── 2.0 │ │ └── patch.json ├── FormAssisant.Designer.cs ├── FormAssisant.cs ├── FormAssisant.resx ├── JsonData.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings └── RevokeMsgPatcher.Assistant.csproj ├── RevokeMsgPatcher.Launcher ├── App.config ├── FormMain.Designer.cs ├── FormMain.cs ├── FormMain.resx ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings └── RevokeMsgPatcher.Launcher.csproj ├── RevokeMsgPatcher.MultiInstance ├── App.config ├── FormMultiInstance.Designer.cs ├── FormMultiInstance.cs ├── FormMultiInstance.resx ├── ProcessUtil.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── README.md ├── RevokeMsgPatcher.MultiInstance.csproj ├── WechatProcess.cs └── app.manifest ├── RevokeMsgPatcher.sln ├── RevokeMsgPatcher ├── App.config ├── BusinessException.cs ├── FormMain.Designer.cs ├── FormMain.cs ├── FormMain.resx ├── Forms │ ├── FormLiteLoaderQQNT.Designer.cs │ ├── FormLiteLoaderQQNT.cs │ ├── FormLiteLoaderQQNT.resx │ ├── FormPatchInfo.Designer.cs │ ├── FormPatchInfo.cs │ ├── FormPatchInfo.resx │ └── UIController.cs ├── Matcher │ ├── BoyerMooreMatcher.cs │ ├── FuzzyMatcher.cs │ └── ModifyFinder.cs ├── Model │ ├── App.cs │ ├── Bag.cs │ ├── Change.cs │ ├── CommonModifyInfo.cs │ ├── Json │ │ ├── LiteLoaderPackage.cs │ │ ├── LiteLoaderPluginsManifest.cs │ │ ├── ReleaseApiRes.cs │ │ └── VersionJson.cs │ ├── LiteLoaderRowData.cs │ ├── ModifyInfo.cs │ ├── ReplacePattern.cs │ └── TargetInfo.cs ├── Modifier │ ├── AppModifier.cs │ ├── FileHexEditor.cs │ ├── QQLiteModifier.cs │ ├── QQModifier.cs │ ├── QQNTModifier.cs │ ├── TIMModifier.cs │ ├── WechatModifier.cs │ └── WeixinModifier.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── RevokeMsgPatcher.csproj ├── Utils │ ├── ByteUtil.cs │ ├── Device.cs │ ├── FileUtil.cs │ ├── GAHelper.cs │ ├── HttpUtil.cs │ ├── PathUtil.cs │ ├── ProxySpeedTester.cs │ └── VersionUtil.cs ├── app.manifest ├── icon.ico └── packages.config └── appveyor.yml /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/.github/ISSUE_TEMPLATE/config.yml -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new_version.yml: -------------------------------------------------------------------------------- 1 | name: 版本支持 2 | description: 发现有未支持的版本,且 Issue 列表中暂无人提出相同版本未支持的问题,可以通过创建这个 Issue 进行反馈 3 | title: "[未支持版本]: " 4 | assignees: 5 | - huiyadanli 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | 请按下方的要求填写完整的问题表单,方便作者进行更新。 11 | 12 | - type: checkboxes 13 | id: lastest 14 | attributes: 15 | label: 请先确认当前使用的 微信/QQ/TIM防撤回补丁 是最新版本! 16 | description: 提交此 Issue 前请先确认你使用的是[最新 Release 版本的软件](https://github.com/huiyadanli/RevokeMsgPatcher/releases) 17 | options: 18 | - label: 我确认当前使用的是最新版 19 | required: true 20 | 21 | - type: input 22 | id: version 23 | attributes: 24 | label: 微信/QQ 版本 25 | description: | 26 | 请填写完整版本号,并写清楚是正式版还是测试版。 27 | placeholder: 例:微信正式版 3.6.0.18 28 | validations: 29 | required: true 30 | 31 | - type: input 32 | id: url 33 | attributes: 34 | label: 对应版本的下载地址 35 | description: 如果是从某个链接下载的对应版本,请把链接附上。测试版本请务必附上下载链接! 36 | placeholder: 例:https://dldir1.qq.com/weixin/Windows/WeChatSetup.exe 37 | 38 | - type: textarea 39 | id: tips 40 | attributes: 41 | label: 软件提示了什么? 42 | description: 你的操作流程简述,和软件安装补丁失败后的提示,建议附上截图。 43 | validations: 44 | required: true 45 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: .Net Build 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | Reason: 7 | description: 'Reasons for temporary build' 8 | required: true 9 | default: 'No reason.Just do it.' 10 | push: 11 | branches: 12 | - master 13 | paths-ignore: 14 | - '**/*.md' 15 | - .gitignore 16 | - .editorconfig 17 | - appveyor.yml 18 | 19 | pull_request: 20 | branches: 21 | - master 22 | paths-ignore: 23 | - '**/*.md' 24 | - .gitignore 25 | - .editorconfig 26 | - appveyor.yml 27 | 28 | env: 29 | # Path to the solution file relative to the root of the project. 30 | SOLUTION_FILE_PATH: . 31 | 32 | # Configuration type to build. 33 | # You can convert this to a build matrix if you need coverage of multiple configuration types. 34 | # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 35 | #BUILD_CONFIGURATION: [Debug , Release] 36 | 37 | jobs: 38 | build: 39 | name: ${{matrix.BUILD_CONFIGURATION}} 40 | runs-on: windows-2019 41 | strategy: 42 | matrix: 43 | BUILD_CONFIGURATION: ['Debug', 'Release'] 44 | steps: 45 | - uses: actions/checkout@v2 46 | 47 | - name: Add MSBuild to PATH 48 | uses: microsoft/setup-msbuild@v1.0.2 49 | 50 | - name: Restore NuGet packages 51 | working-directory: ${{env.GITHUB_WORKSPACE}} 52 | run: nuget restore ${{env.SOLUTION_FILE_PATH}} 53 | 54 | - name: Build ${{matrix.BUILD_CONFIGURATION}} 55 | working-directory: ${{env.GITHUB_WORKSPACE}} 56 | # Add additional options to the MSBuild command line here (like platform or verbosity level). 57 | # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference 58 | run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}} 59 | 60 | - name: Upload Artifact 61 | uses: actions/upload-artifact@v4 62 | with: 63 | name: RevokeMsgPatcher-${{matrix.BUILD_CONFIGURATION}} 64 | path: | 65 | .\RevokeMsgPatcher\bin 66 | !.\RevokeMsgPatcher\bin\**\RevokeMsgPatcher.exe.config 67 | !.\RevokeMsgPatcher\bin\**\RevokeMsgPatcher.pdb 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | *.suo 3 | *.user 4 | *.userosscache 5 | *.sln.docstates 6 | .vs/ 7 | 8 | # User-specific files (MonoDevelop/Xamarin Studio) 9 | *.userprefs 10 | 11 | # Build results 12 | [Dd]ebug/ 13 | [Dd]ebugPublic/ 14 | [Rr]elease/ 15 | [Rr]eleases/ 16 | x64/ 17 | x86/ 18 | bld/ 19 | [Bb]in/ 20 | [Oo]bj/ 21 | [Ll]og/ 22 | 23 | # Mine 24 | Temp/ 25 | /packages/ 26 | -------------------------------------------------------------------------------- /Images/give_a_star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/give_a_star.png -------------------------------------------------------------------------------- /Images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/logo.png -------------------------------------------------------------------------------- /Images/logo_old.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/logo_old.jpg -------------------------------------------------------------------------------- /Images/revoke.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/revoke.jpg -------------------------------------------------------------------------------- /Images/screenshot.mi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/screenshot.mi.png -------------------------------------------------------------------------------- /Images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/screenshot.png -------------------------------------------------------------------------------- /Images/screenshot.v0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/screenshot.v0.4.png -------------------------------------------------------------------------------- /Images/screenshot.v0.5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/screenshot.v0.5.png -------------------------------------------------------------------------------- /Images/screenshot.v0.6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/screenshot.v0.6.png -------------------------------------------------------------------------------- /Images/screenshot.v0.7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/screenshot.v0.7.png -------------------------------------------------------------------------------- /Images/screenshot.v0.8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/screenshot.v0.8.png -------------------------------------------------------------------------------- /Images/screenshot.v0.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/screenshot.v0.9.png -------------------------------------------------------------------------------- /Images/watch_release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/watch_release.png -------------------------------------------------------------------------------- /Images/wiki/qq/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/1.png -------------------------------------------------------------------------------- /Images/wiki/qq/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/10.png -------------------------------------------------------------------------------- /Images/wiki/qq/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/11.png -------------------------------------------------------------------------------- /Images/wiki/qq/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/12.png -------------------------------------------------------------------------------- /Images/wiki/qq/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/13.png -------------------------------------------------------------------------------- /Images/wiki/qq/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/2.png -------------------------------------------------------------------------------- /Images/wiki/qq/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/3.png -------------------------------------------------------------------------------- /Images/wiki/qq/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/4.png -------------------------------------------------------------------------------- /Images/wiki/qq/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/5.png -------------------------------------------------------------------------------- /Images/wiki/qq/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/6.png -------------------------------------------------------------------------------- /Images/wiki/qq/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/7.png -------------------------------------------------------------------------------- /Images/wiki/qq/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/8.png -------------------------------------------------------------------------------- /Images/wiki/qq/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/qq/9.png -------------------------------------------------------------------------------- /Images/wiki/wechat/10_mutex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/10_mutex.png -------------------------------------------------------------------------------- /Images/wiki/wechat/11_push_ebp_to_ret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/11_push_ebp_to_ret.png -------------------------------------------------------------------------------- /Images/wiki/wechat/12_push_ebp_to_ret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/12_push_ebp_to_ret.png -------------------------------------------------------------------------------- /Images/wiki/wechat/13_push_ebp_to_ret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/13_push_ebp_to_ret.png -------------------------------------------------------------------------------- /Images/wiki/wechat/14_patch_dll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/14_patch_dll.png -------------------------------------------------------------------------------- /Images/wiki/wechat/1_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/1_start.png -------------------------------------------------------------------------------- /Images/wiki/wechat/2_click_attach.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/2_click_attach.png -------------------------------------------------------------------------------- /Images/wiki/wechat/3_attach_wechat_exe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/3_attach_wechat_exe.png -------------------------------------------------------------------------------- /Images/wiki/wechat/4_wechatwin_dll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/4_wechatwin_dll.png -------------------------------------------------------------------------------- /Images/wiki/wechat/5_search_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/5_search_string.png -------------------------------------------------------------------------------- /Images/wiki/wechat/6_revokemsg_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/6_revokemsg_1.png -------------------------------------------------------------------------------- /Images/wiki/wechat/7_je_to_jmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/7_je_to_jmp.png -------------------------------------------------------------------------------- /Images/wiki/wechat/8_je_to_jmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/8_je_to_jmp.png -------------------------------------------------------------------------------- /Images/wiki/wechat/9_je_to_jmp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/Images/wiki/wechat/9_je_to_jmp.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 |

5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

16 | 17 | # 👀微信/QQ/TIM防撤回补丁 18 | 适用于 Windows 下 PC 版微信/QQ/TIM的防撤回补丁。**支持最新版微信/QQ/TIM**,其中微信能够选择安装多开功能。 19 | 20 | 21 | 22 | 下载地址: 23 | **[⚡️点我下载最新版本](https://github.com/huiyadanli/RevokeMsgPatcher/releases/download/2.0/RevokeMsgPatcher.v2.0.zip)** | 24 | [☁备用下载-蓝奏云](https://wwmy.lanzouq.com/b0fot7dpe) 密码:coco| 25 | [☁备用下载-百度云](https://pan.baidu.com/s/15ilr78t8F1-VW8eUZSkr_Q?pwd=3rrj) 26 | 27 | 相关文档: 28 | **[✔支持哪些版本](https://github.com/huiyadanli/RevokeMsgPatcher/wiki/%E7%89%88%E6%9C%AC%E6%94%AF%E6%8C%81)** | 29 | [❓常见问题](https://github.com/huiyadanli/RevokeMsgPatcher/wiki#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98) | 30 | [📖查看完整文档](https://github.com/huiyadanli/RevokeMsgPatcher/wiki) 31 | 32 | 原理与方法: 33 | [📗微信](https://github.com/huiyadanli/RevokeMsgPatcher/wiki/%E5%BE%AE%E4%BF%A1%E9%98%B2%E6%92%A4%E5%9B%9E%E4%B8%8E%E5%A4%9A%E5%BC%80%E6%95%99%E7%A8%8B) | 34 | [📕QQ](https://github.com/huiyadanli/RevokeMsgPatcher/wiki/QQ%E6%88%96TIM%E9%98%B2%E6%92%A4%E5%9B%9E%E6%95%99%E7%A8%8B) | 35 | [📘TIM](https://github.com/huiyadanli/RevokeMsgPatcher/wiki/QQ%E6%88%96TIM%E9%98%B2%E6%92%A4%E5%9B%9E%E6%95%99%E7%A8%8B) 36 | **(本人不参与方法寻找,仅做特征搬运)** 37 | 38 | 附带产物:[一个通用的微信多开工具](https://github.com/huiyadanli/RevokeMsgPatcher/tree/master/RevokeMsgPatcher.MultiInstance) 39 | 40 | ## 📷截图 41 | ![Screenshot](https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/master/Images/screenshot.png) 42 | 43 | ## 🔨使用方法 44 | 45 | 1. 首先,你的系统需要满足以下条件: 46 | 47 | * Windows 7 或更高版本,**不支持XP**。 48 | * [.NET Framework 4.5.2](https://www.microsoft.com/en-us/download/details.aspx?id=42642) 或更高版本。**低于此版本在打开程序时可能无反应,或者直接报错**。 49 | 50 | 2. 使用本程序前,先关闭微信/QQ/TIM。 51 | 52 | 3. **以管理员身份运行本程序**,等待右下角获取最新的补丁信息。 53 | 54 | 4. 选择微信/QQ/TIM的安装路径。如果你用的安装版的微信/QQ/TIM,正常情况下本程序会自动从注册表中获取安装路径,绿色版需要手动选择路径。 55 | 56 | 5. 点击防撤回。界面可能会出现一段时间的无响应,请耐心等待。**由于修改了微信的 WeChatWin.dll 文件、QQ/TIM的 IM.dll 文件,杀毒软件可能会弹出警告,放行即可。** 57 | 58 | 注意:微信/QQ/TIM更新之后要重新安装补丁! 59 | 60 | ## 💡致谢 61 | 62 | 本项目早期内容源自 [wechat_anti_revoke](https://github.com/36huo/wechat_anti_revoke) 项目。 63 | 64 | QQNT 防撤回依赖于 [LiteLoaderQQNT](https://github.com/LiteLoaderQQNT/LiteLoaderQQNT),修补依赖于 [DLLHijackMethod](https://github.com/LiteLoaderQQNT/QQNTFileVerifyPatch/tree/DLLHijackMethod) 并集成了以下插件: 65 | 66 | * [插件列表查看 LL-plugin-list-viewer](https://github.com/ltxhhz/LL-plugin-list-viewer) 67 | * [防撤回 LiteLoaderQQNT-Anti-Recall](https://github.com/xh321/LiteLoaderQQNT-Anti-Recall) 68 | 69 | 微信4.0版本后的防撤回特征来自于 [BetterWX](https://github.com/zetaloop/BetterWX) 70 | 71 | ## ❤️投喂 72 | 73 | 觉的好用的话,可以支持作者哟ヾ(・ω・`。) 74 | * [⚡爱发电](https://afdian.com/@huiyadanli) 75 | * [🍚微信赞赏](https://github.com/huiyadanli/huiyadanli/blob/master/DONATE.md) 76 | 77 | ## 📄License 78 | [GPLv3](https://github.com/huiyadanli/RevokeMsgPatcher/blob/master/LICENSE) 79 | 80 | ![](https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/master/Images/give_a_star.png) 81 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Assistant/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Assistant/FormAssisant.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace RevokeMsgPatcher.Assistant 2 | { 3 | partial class FormAssisant 4 | { 5 | /// 6 | /// 必需的设计器变量。 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// 清理所有正在使用的资源。 12 | /// 13 | /// 如果应释放托管资源,为 true;否则为 false。 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows 窗体设计器生成的代码 24 | 25 | /// 26 | /// 设计器支持所需的方法 - 不要修改 27 | /// 使用代码编辑器修改此方法的内容。 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.txtInfo = new System.Windows.Forms.TextBox(); 32 | this.btnSearch = new System.Windows.Forms.Button(); 33 | this.btnGetVersion = new System.Windows.Forms.Button(); 34 | this.SuspendLayout(); 35 | // 36 | // txtInfo 37 | // 38 | this.txtInfo.Location = new System.Drawing.Point(12, 12); 39 | this.txtInfo.Multiline = true; 40 | this.txtInfo.Name = "txtInfo"; 41 | this.txtInfo.Size = new System.Drawing.Size(484, 182); 42 | this.txtInfo.TabIndex = 0; 43 | // 44 | // btnSearch 45 | // 46 | this.btnSearch.Location = new System.Drawing.Point(12, 211); 47 | this.btnSearch.Name = "btnSearch"; 48 | this.btnSearch.Size = new System.Drawing.Size(75, 23); 49 | this.btnSearch.TabIndex = 1; 50 | this.btnSearch.Text = "查找测试"; 51 | this.btnSearch.UseVisualStyleBackColor = true; 52 | this.btnSearch.Click += new System.EventHandler(this.btnSearch_Click); 53 | // 54 | // btnGetVersion 55 | // 56 | this.btnGetVersion.Location = new System.Drawing.Point(106, 211); 57 | this.btnGetVersion.Name = "btnGetVersion"; 58 | this.btnGetVersion.Size = new System.Drawing.Size(91, 23); 59 | this.btnGetVersion.TabIndex = 2; 60 | this.btnGetVersion.Text = "获取文件版本"; 61 | this.btnGetVersion.UseVisualStyleBackColor = true; 62 | this.btnGetVersion.Click += new System.EventHandler(this.btnGetVersion_Click); 63 | // 64 | // FormAssisant 65 | // 66 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 67 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 68 | this.ClientSize = new System.Drawing.Size(508, 252); 69 | this.Controls.Add(this.btnGetVersion); 70 | this.Controls.Add(this.btnSearch); 71 | this.Controls.Add(this.txtInfo); 72 | this.Name = "FormAssisant"; 73 | this.Text = "冷血无情的助手界面"; 74 | this.Load += new System.EventHandler(this.FormMain_Load); 75 | this.ResumeLayout(false); 76 | this.PerformLayout(); 77 | 78 | } 79 | 80 | #endregion 81 | 82 | private System.Windows.Forms.TextBox txtInfo; 83 | private System.Windows.Forms.Button btnSearch; 84 | private System.Windows.Forms.Button btnGetVersion; 85 | } 86 | } 87 | 88 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Assistant/FormAssisant.cs: -------------------------------------------------------------------------------- 1 | using RevokeMsgPatcher.Matcher; 2 | using RevokeMsgPatcher.Model; 3 | using RevokeMsgPatcher.Utils; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Windows.Forms; 9 | 10 | namespace RevokeMsgPatcher.Assistant 11 | { 12 | public partial class FormAssisant : Form 13 | { 14 | public FormAssisant() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | private void FormMain_Load(object sender, EventArgs e) 20 | { 21 | JsonData obj = new JsonData(); 22 | string json = obj.BagJson(); 23 | Console.WriteLine(json); 24 | 25 | DirectoryInfo directory = new DirectoryInfo("../../Data/" + obj.Bag().LatestVersion); 26 | if (!directory.Exists) 27 | { 28 | directory.Create(); 29 | } 30 | string path = Path.Combine(directory.FullName, "patch.json"); 31 | File.WriteAllText(path, json); 32 | 33 | txtInfo.AppendText("生成完毕!位置:" + path + Environment.NewLine); 34 | } 35 | 36 | private void btnSearch_Click(object sender, EventArgs e) 37 | { 38 | byte[] fileByteArray = File.ReadAllBytes(@""); 39 | byte[] searchBytes = ByteUtil.HexStringToByteArray("1C E9 9D 00 00 00 8B 45 E8 8D 55 EC 52 89 5D EC 68 3F 3F 3F 54 8B 08 50 FF 51 78 85 C0 79 2D 8D 45 0C C7 45 0C"); 40 | byte[] replaceBytes = ByteUtil.HexStringToByteArray("1C E9 9D 00 00 00 8B 45 E8 8D 55 EC 52 89 5D EC EB 09 90 90 90 8B 08 50 FF 51 78 85 C0 79 2D 8D 45 0C C7 45 0C"); 41 | //int[] indexs = FuzzyMatcher.MatchAll(fileByteArray, searchBytes); 42 | int[] indexs = FuzzyMatcher.MatchNotReplaced(fileByteArray, searchBytes, replaceBytes); 43 | txtInfo.AppendText("查找结果位置:" + string.Join(",", indexs) + Environment.NewLine); 44 | // 371130 45 | 46 | List changes = ComputChanges(indexs, searchBytes, replaceBytes); 47 | foreach (Change c in changes) 48 | { 49 | txtInfo.AppendText("替换位置:" + Convert.ToString(c.Position, 16) + " 替换内容:" + ByteUtil.ByteArrayToHexString(c.Content) + Environment.NewLine); 50 | } 51 | 52 | } 53 | 54 | public static List ComputChanges(int[] indexs, byte[] searchBytes, byte[] replaceBytes) 55 | { 56 | if (searchBytes.Length != replaceBytes.Length) 57 | { 58 | throw new Exception("查询串与替换串长度不同!"); 59 | } 60 | // 一个替换串存在多个替换点的情况 61 | List changeOffsets = new List(); // 查询串与替换串变化偏移 62 | List diff = null; 63 | for (int i = 0; i < searchBytes.Length; i++) 64 | { 65 | if (searchBytes[i] != replaceBytes[i]) 66 | { 67 | if (diff == null) 68 | { 69 | diff = new List(); 70 | Change offset = new Change 71 | { 72 | Position = i 73 | }; 74 | changeOffsets.Add(offset); 75 | } 76 | diff.Add(replaceBytes[i]); 77 | } 78 | else 79 | { 80 | if (diff != null) 81 | { 82 | changeOffsets.Last().Content = diff.ToArray(); 83 | diff = null; 84 | } 85 | } 86 | } 87 | // 最后一位也是要被替换的情况 88 | if (diff != null) 89 | { 90 | changeOffsets.Last().Content = diff.ToArray(); 91 | diff = null; 92 | } 93 | 94 | if (changeOffsets.Count == 0) 95 | { 96 | throw new Exception("查询串与替换串完全相同!请确认补丁信息的正确性。"); 97 | } 98 | 99 | List changes = new List(); 100 | foreach (int index in indexs) 101 | { 102 | foreach (Change offset in changeOffsets) 103 | { 104 | Change c = offset.Clone(); 105 | c.Position += index; 106 | changes.Add(c); 107 | } 108 | } 109 | return changes; 110 | } 111 | 112 | private void btnGetVersion_Click(object sender, EventArgs e) 113 | { 114 | string version = FileUtil.GetFileVersion(@""); 115 | txtInfo.AppendText("文件版本:" + version + Environment.NewLine); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Assistant/FormAssisant.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Assistant/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace RevokeMsgPatcher.Assistant 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// 应用程序的主入口点。 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new FormAssisant()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Assistant/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("RevokeMsgPatcher.Assistant")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RevokeMsgPatcher.Assistant")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("6992004f-17e6-45bf-8d72-180a31e9c23c")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Assistant/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace RevokeMsgPatcher.Assistant.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RevokeMsgPatcher.Assistant.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Assistant/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Assistant/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace RevokeMsgPatcher.Assistant.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.1.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Assistant/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Assistant/RevokeMsgPatcher.Assistant.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {6992004F-17E6-45BF-8D72-180A31E9C23C} 8 | WinExe 9 | RevokeMsgPatcher.Assistant 10 | RevokeMsgPatcher.Assistant 11 | v4.5.2 12 | 512 13 | true 14 | publish\ 15 | true 16 | Disk 17 | false 18 | Foreground 19 | 7 20 | Days 21 | false 22 | false 23 | true 24 | 0 25 | 1.0.0.%2a 26 | false 27 | false 28 | true 29 | 30 | 31 | 32 | AnyCPU 33 | true 34 | full 35 | false 36 | bin\Debug\ 37 | DEBUG;TRACE 38 | prompt 39 | 4 40 | 41 | 42 | AnyCPU 43 | pdbonly 44 | true 45 | bin\Release\ 46 | TRACE 47 | prompt 48 | 4 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Form 67 | 68 | 69 | FormAssisant.cs 70 | 71 | 72 | 73 | 74 | 75 | FormAssisant.cs 76 | 77 | 78 | ResXFileCodeGenerator 79 | Resources.Designer.cs 80 | Designer 81 | 82 | 83 | True 84 | Resources.resx 85 | True 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | SettingsSingleFileGenerator 103 | Settings.Designer.cs 104 | 105 | 106 | True 107 | Settings.settings 108 | True 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | False 117 | .NET Framework 3.5 SP1 118 | false 119 | 120 | 121 | 122 | 123 | {977bf781-ced8-4389-9404-0fa08fdf21df} 124 | RevokeMsgPatcher 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Launcher/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Launcher/FormMain.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace RevokeMsgPatcher.Launcher 2 | { 3 | partial class FormMain 4 | { 5 | /// 6 | /// 必需的设计器变量。 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// 清理所有正在使用的资源。 12 | /// 13 | /// 如果应释放托管资源,为 true;否则为 false。 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows 窗体设计器生成的代码 24 | 25 | /// 26 | /// 设计器支持所需的方法 - 不要修改 27 | /// 使用代码编辑器修改此方法的内容。 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormMain)); 32 | this.tabControl = new System.Windows.Forms.TabControl(); 33 | this.tabQQNT = new System.Windows.Forms.TabPage(); 34 | this.tabControl.SuspendLayout(); 35 | this.SuspendLayout(); 36 | // 37 | // tabControl 38 | // 39 | this.tabControl.Controls.Add(this.tabQQNT); 40 | this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill; 41 | this.tabControl.Location = new System.Drawing.Point(0, 0); 42 | this.tabControl.Name = "tabControl"; 43 | this.tabControl.SelectedIndex = 0; 44 | this.tabControl.Size = new System.Drawing.Size(411, 446); 45 | this.tabControl.TabIndex = 0; 46 | // 47 | // tabQQNT 48 | // 49 | this.tabQQNT.Location = new System.Drawing.Point(4, 22); 50 | this.tabQQNT.Name = "tabQQNT"; 51 | this.tabQQNT.Padding = new System.Windows.Forms.Padding(3); 52 | this.tabQQNT.Size = new System.Drawing.Size(403, 420); 53 | this.tabQQNT.TabIndex = 0; 54 | this.tabQQNT.Text = "QQNT"; 55 | this.tabQQNT.UseVisualStyleBackColor = true; 56 | // 57 | // FormMain 58 | // 59 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 60 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 61 | this.ClientSize = new System.Drawing.Size(411, 446); 62 | this.Controls.Add(this.tabControl); 63 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 64 | this.Name = "FormMain"; 65 | this.Text = "RevokeMsgPatcher 防撤回启动器"; 66 | this.tabControl.ResumeLayout(false); 67 | this.ResumeLayout(false); 68 | 69 | } 70 | 71 | #endregion 72 | 73 | private System.Windows.Forms.TabControl tabControl; 74 | private System.Windows.Forms.TabPage tabQQNT; 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Launcher/FormMain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace RevokeMsgPatcher.Launcher 12 | { 13 | public partial class FormMain : Form 14 | { 15 | public FormMain() 16 | { 17 | InitializeComponent(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Launcher/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace RevokeMsgPatcher.Launcher 8 | { 9 | internal static class Program 10 | { 11 | /// 12 | /// 应用程序的主入口点。 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new FormMain()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Launcher/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("RevokeMsgPatcher.Launcher")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RevokeMsgPatcher.Launcher")] 13 | [assembly: AssemblyCopyright("Copyright © 2024")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("b1d05208-e291-406b-a8b4-f673ec784b1c")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Launcher/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本: 4.0.30319.42000 5 | // 6 | // 对此文件的更改可能导致不正确的行为,如果 7 | // 重新生成代码,则所做更改将丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace RevokeMsgPatcher.Launcher.Properties 12 | { 13 | 14 | 15 | /// 16 | /// 强类型资源类,用于查找本地化字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// 返回此类使用的缓存 ResourceManager 实例。 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RevokeMsgPatcher.Launcher.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// 重写当前线程的 CurrentUICulture 属性,对 56 | /// 使用此强类型资源类的所有资源查找执行重写。 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Launcher/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Launcher/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace RevokeMsgPatcher.Launcher.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Launcher/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.Launcher/RevokeMsgPatcher.Launcher.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {B1D05208-E291-406B-A8B4-F673EC784B1C} 8 | WinExe 9 | RevokeMsgPatcher.Launcher 10 | RevokeMsgPatcher.Launcher 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Form 51 | 52 | 53 | FormMain.cs 54 | 55 | 56 | 57 | 58 | FormMain.cs 59 | 60 | 61 | ResXFileCodeGenerator 62 | Resources.Designer.cs 63 | Designer 64 | 65 | 66 | True 67 | Resources.resx 68 | 69 | 70 | SettingsSingleFileGenerator 71 | Settings.Designer.cs 72 | 73 | 74 | True 75 | Settings.settings 76 | True 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/FormMultiInstance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Windows.Forms; 5 | 6 | namespace RevokeMsgPatcher.MultiInstance 7 | { 8 | public partial class FormMultiInstance : Form 9 | { 10 | public FormMultiInstance() 11 | { 12 | InitializeComponent(); 13 | 14 | // 标题加上版本号 15 | string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); 16 | if (currentVersion.Length > 3) 17 | { 18 | currentVersion = " v" + currentVersion.Substring(0, 3); 19 | } 20 | this.Text += currentVersion; 21 | } 22 | 23 | private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 24 | { 25 | Process.Start("https://github.com/huiyadanli/RevokeMsgPatcher"); 26 | } 27 | 28 | private void btnStartTimer_Click(object sender, EventArgs e) 29 | { 30 | mutexHandleCloseTimer.Start(); 31 | btnStartTimer.Enabled = false; 32 | btnStopTimer.Enabled = true; 33 | } 34 | 35 | private void btnStopTimer_Click(object sender, EventArgs e) 36 | { 37 | mutexHandleCloseTimer.Stop(); 38 | btnStartTimer.Enabled = true; 39 | btnStopTimer.Enabled = false; 40 | } 41 | 42 | private List wechatProcesses = new List(); 43 | 44 | private void mutexHandleCloseTimer_Tick(object sender, EventArgs e) 45 | { 46 | Process[] processes = Process.GetProcessesByName("WeChat"); 47 | Console.WriteLine("WeChat进程数:" + processes.Length); 48 | // 添加新进程 49 | foreach (Process p in processes) 50 | { 51 | int i = 0; 52 | for (i = 0; i < wechatProcesses.Count; i++) 53 | { 54 | WechatProcess wechatProcess = wechatProcesses[i]; 55 | if (wechatProcess.Proc.Id == p.Id) 56 | { 57 | break; 58 | } 59 | } 60 | if (i == wechatProcesses.Count) 61 | { 62 | wechatProcesses.Add(new WechatProcess(p)); 63 | } 64 | } 65 | // 关闭所有存在互斥句柄的进程 66 | int num = 0; 67 | for (int i = wechatProcesses.Count - 1; i >= 0; i--) 68 | { 69 | WechatProcess wechatProcess = wechatProcesses[i]; 70 | if (!wechatProcess.MutexClosed) 71 | { 72 | wechatProcess.MutexClosed = ProcessUtil.CloseMutexHandle(wechatProcess.Proc); 73 | Console.WriteLine("进程:" + wechatProcess.Proc.Id + ",关闭互斥句柄:" + wechatProcess.MutexClosed); 74 | } 75 | else 76 | { 77 | if (wechatProcess.Proc.HasExited) 78 | { 79 | // 移除不存在的线程 80 | wechatProcesses.RemoveAt(i); 81 | } 82 | else 83 | { 84 | num++; 85 | } 86 | 87 | } 88 | } 89 | lblProcNum.Text = "当前微信数量:" + num.ToString(); 90 | } 91 | 92 | private void btnKillAll_Click(object sender, EventArgs e) 93 | { 94 | Process[] processes = Process.GetProcessesByName("WeChat"); 95 | if (processes.Length > 0) 96 | { 97 | foreach (Process p in processes) 98 | { 99 | p.Kill(); 100 | } 101 | MessageBox.Show("已经关闭所有微信进程,共" + processes.Length + "个", "提示"); 102 | } 103 | else 104 | { 105 | MessageBox.Show("当前无微信进程", "提示"); 106 | } 107 | } 108 | 109 | private void btnCloseAllMutex_Click(object sender, EventArgs e) 110 | { 111 | Process[] processes = Process.GetProcessesByName("WeChat"); 112 | ProcessUtil.CloseMutexHandle(processes); 113 | } 114 | 115 | private void lblHowToUse_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 116 | { 117 | Process.Start("https://github.com/huiyadanli/RevokeMsgPatcher/tree/master/RevokeMsgPatcher.MultiInstance"); 118 | } 119 | 120 | private void FormMultiInstance_FormClosed(object sender, FormClosedEventArgs e) 121 | { 122 | mutexHandleCloseTimer.Stop(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/FormMultiInstance.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace RevokeMsgPatcher.MultiInstance 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// 应用程序的主入口点。 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new FormMultiInstance()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("RevokeMsgPatcher.MultiInstance")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RevokeMsgPatcher.MultiInstance")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("73043ca8-af54-4591-9174-40fb6e0a3d36")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("0.1")] 36 | [assembly: AssemblyFileVersion("0.1")] 37 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace RevokeMsgPatcher.MultiInstance.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RevokeMsgPatcher.MultiInstance.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace RevokeMsgPatcher.MultiInstance.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.1.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/README.md: -------------------------------------------------------------------------------- 1 | ## 一个通用的微信多开工具 2 | 3 | 此工具可以无视微信版本进行多开。 4 | 5 | **⚠如果你曾使用过“PC版微信/QQ/TIM防撤回补丁”,并对微信安装了防撤回/多开的补丁,此时微信本身已经支持多开,请勿重复使用本工具!!!** 6 | 7 | ## 📷截图 8 | ![Screenshot](https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/master/Images/screenshot.mi.png) 9 | 10 | ## 🔨使用方法 11 | 12 | ⚔自动模式(一般用户使用这个模式即可): 13 | 14 | 点击【启动多开】之后,就可以启动多个微信了。 15 | 16 | 注意:启动多个微信频率太快时,可能会失败。 17 | 18 | 🏹手动模式: 19 | 20 | 关闭所有微信进程:功能就是关闭所有微信进程,微信在某种启动失败的情况下会残留进程(无界面),一般不会遇到这种情况。 21 | 22 | 清理所有微信互斥句柄:功能就是把所有微信判断是否多开的标志全部清理掉,实现多开。如果只使用这个按钮实现多开的话,每次开启一个微信之后都要点一下这个按钮。 23 | 24 | ## ❤Thanks 25 | 26 | [微信电脑端多开分析与原理](https://mp.weixin.qq.com/s/bb7XMxop7e8rd7YqQ88nyA) 27 | 28 | ## 📄License 29 | GPLv3 30 | 31 | ![](https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/master/Images/give_a_star.png) 32 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/RevokeMsgPatcher.MultiInstance.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {73043CA8-AF54-4591-9174-40FB6E0A3D36} 8 | WinExe 9 | RevokeMsgPatcher.MultiInstance 10 | RevokeMsgPatcher.MultiInstance 11 | v4.5.2 12 | 512 13 | true 14 | 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | app.manifest 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Form 54 | 55 | 56 | FormMultiInstance.cs 57 | 58 | 59 | 60 | 61 | 62 | 63 | FormMultiInstance.cs 64 | 65 | 66 | ResXFileCodeGenerator 67 | Resources.Designer.cs 68 | Designer 69 | 70 | 71 | True 72 | Resources.resx 73 | True 74 | 75 | 76 | 77 | SettingsSingleFileGenerator 78 | Settings.Designer.cs 79 | 80 | 81 | True 82 | Settings.settings 83 | True 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/WechatProcess.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace RevokeMsgPatcher.MultiInstance 4 | { 5 | public class WechatProcess 6 | { 7 | public Process Proc { get; set; } 8 | 9 | public bool MutexClosed { get; set; } 10 | 11 | public WechatProcess(Process p) 12 | { 13 | Proc = p; 14 | MutexClosed = false; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.MultiInstance/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | 54 | true 55 | 56 | 57 | 58 | 59 | 60 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /RevokeMsgPatcher.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.10.35013.160 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevokeMsgPatcher", "RevokeMsgPatcher\RevokeMsgPatcher.csproj", "{977BF781-CED8-4389-9404-0FA08FDF21DF}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevokeMsgPatcher.Assistant", "RevokeMsgPatcher.Assistant\RevokeMsgPatcher.Assistant.csproj", "{6992004F-17E6-45BF-8D72-180A31E9C23C}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevokeMsgPatcher.MultiInstance", "RevokeMsgPatcher.MultiInstance\RevokeMsgPatcher.MultiInstance.csproj", "{73043CA8-AF54-4591-9174-40FB6E0A3D36}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RevokeMsgPatcher.Launcher", "RevokeMsgPatcher.Launcher\RevokeMsgPatcher.Launcher.csproj", "{B1D05208-E291-406B-A8B4-F673EC784B1C}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {977BF781-CED8-4389-9404-0FA08FDF21DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {977BF781-CED8-4389-9404-0FA08FDF21DF}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {977BF781-CED8-4389-9404-0FA08FDF21DF}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {977BF781-CED8-4389-9404-0FA08FDF21DF}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {6992004F-17E6-45BF-8D72-180A31E9C23C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {6992004F-17E6-45BF-8D72-180A31E9C23C}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {6992004F-17E6-45BF-8D72-180A31E9C23C}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {6992004F-17E6-45BF-8D72-180A31E9C23C}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {73043CA8-AF54-4591-9174-40FB6E0A3D36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {73043CA8-AF54-4591-9174-40FB6E0A3D36}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {73043CA8-AF54-4591-9174-40FB6E0A3D36}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {73043CA8-AF54-4591-9174-40FB6E0A3D36}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {B1D05208-E291-406B-A8B4-F673EC784B1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {B1D05208-E291-406B-A8B4-F673EC784B1C}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {B1D05208-E291-406B-A8B4-F673EC784B1C}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {B1D05208-E291-406B-A8B4-F673EC784B1C}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {56DAEA8C-77F7-4E55-A7AF-CE9F23F2C1A6} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/BusinessException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RevokeMsgPatcher 4 | { 5 | class BusinessException : ApplicationException 6 | { 7 | public string ErrorCode { get; protected set; } 8 | 9 | public BusinessException(string errcode, string message) : base(message) 10 | { 11 | ErrorCode = errcode; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Forms/FormPatchInfo.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace RevokeMsgPatcher.Forms 2 | { 3 | partial class FormPatchInfo 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.rtbPatchInfo = new System.Windows.Forms.RichTextBox(); 32 | this.btnOK = new System.Windows.Forms.Button(); 33 | this.btnCancel = new System.Windows.Forms.Button(); 34 | this.label1 = new System.Windows.Forms.Label(); 35 | this.SuspendLayout(); 36 | // 37 | // rtbPatchInfo 38 | // 39 | this.rtbPatchInfo.Location = new System.Drawing.Point(12, 45); 40 | this.rtbPatchInfo.Name = "rtbPatchInfo"; 41 | this.rtbPatchInfo.Size = new System.Drawing.Size(489, 158); 42 | this.rtbPatchInfo.TabIndex = 0; 43 | this.rtbPatchInfo.Text = ""; 44 | // 45 | // btnOK 46 | // 47 | this.btnOK.Location = new System.Drawing.Point(345, 209); 48 | this.btnOK.Name = "btnOK"; 49 | this.btnOK.Size = new System.Drawing.Size(75, 23); 50 | this.btnOK.TabIndex = 1; 51 | this.btnOK.Text = "确定"; 52 | this.btnOK.UseVisualStyleBackColor = true; 53 | // 54 | // btnCancel 55 | // 56 | this.btnCancel.Location = new System.Drawing.Point(426, 209); 57 | this.btnCancel.Name = "btnCancel"; 58 | this.btnCancel.Size = new System.Drawing.Size(75, 23); 59 | this.btnCancel.TabIndex = 2; 60 | this.btnCancel.Text = "取消"; 61 | this.btnCancel.UseVisualStyleBackColor = true; 62 | // 63 | // label1 64 | // 65 | this.label1.Location = new System.Drawing.Point(12, 9); 66 | this.label1.Name = "label1"; 67 | this.label1.Size = new System.Drawing.Size(489, 33); 68 | this.label1.TabIndex = 3; 69 | this.label1.Text = "注意:请勿随意输入补丁信息,错误的补丁信息将导致本软件无法正常使用。在被恶意情况下可以修改/破坏系统任意位置文件"; 70 | // 71 | // FormPatchInfo 72 | // 73 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 74 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 75 | this.ClientSize = new System.Drawing.Size(513, 241); 76 | this.Controls.Add(this.label1); 77 | this.Controls.Add(this.btnCancel); 78 | this.Controls.Add(this.btnOK); 79 | this.Controls.Add(this.rtbPatchInfo); 80 | this.Name = "FormPatchInfo"; 81 | this.Text = "请输入补丁信息"; 82 | this.ResumeLayout(false); 83 | 84 | } 85 | 86 | #endregion 87 | 88 | private System.Windows.Forms.RichTextBox rtbPatchInfo; 89 | private System.Windows.Forms.Button btnOK; 90 | private System.Windows.Forms.Button btnCancel; 91 | private System.Windows.Forms.Label label1; 92 | } 93 | } -------------------------------------------------------------------------------- /RevokeMsgPatcher/Forms/FormPatchInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace RevokeMsgPatcher.Forms 12 | { 13 | public partial class FormPatchInfo : Form 14 | { 15 | public FormPatchInfo() 16 | { 17 | InitializeComponent(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Forms/FormPatchInfo.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Forms/UIController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Forms; 8 | 9 | namespace RevokeMsgPatcher.Forms 10 | { 11 | public class UIController 12 | { 13 | public static void AddCategoryCheckBoxToPanel(Panel panel, string[] categories, string[] installed) 14 | { 15 | if (categories != null && categories.Length != 0) 16 | { 17 | panel.Controls.Clear(); 18 | for (int i = 0; i < categories.Length; i++) 19 | { 20 | CheckBox chk = new CheckBox 21 | { 22 | Text = categories[i], 23 | Name = "chkCategoriesIndex" + i, 24 | Checked = true, 25 | AutoSize = true 26 | }; 27 | if (installed.Contains(categories[i])) 28 | { 29 | chk.Text = chk.Text + "(已安装)"; 30 | chk.Enabled = false; 31 | } 32 | panel.Controls.Add(chk); 33 | } 34 | } 35 | else 36 | { 37 | AddMsgToPanel(panel, "无功能选项"); 38 | } 39 | } 40 | 41 | public static void AddMsgToPanel(Panel panel, string msg) 42 | { 43 | panel.Controls.Clear(); 44 | Label label = new Label 45 | { 46 | Name = "lblCategoriesMsg", 47 | Text = msg, 48 | TextAlign = ContentAlignment.MiddleLeft, 49 | Size = new Size(panel.Width, panel.Height) 50 | }; 51 | panel.Controls.Add(label); 52 | } 53 | 54 | public static List GetCategoriesFromPanel(Panel panel) 55 | { 56 | List categories = new List(); 57 | foreach (Control ctrl in panel.Controls) 58 | { 59 | if (ctrl is CheckBox checkBox) 60 | { 61 | if (checkBox.Enabled && checkBox.Checked) 62 | { 63 | categories.Add(checkBox.Text); 64 | } 65 | } 66 | else if (ctrl is Label label) 67 | { 68 | return null; // 如果是标签, 说明是精准匹配, 直接返回null 69 | } 70 | } 71 | return categories; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Matcher/BoyerMooreMatcher.cs: -------------------------------------------------------------------------------- 1 | namespace RevokeMsgPatcher.Matcher 2 | { 3 | public class BoyerMooreMatcher 4 | { 5 | private static int AlphabetSize = 256; 6 | 7 | private static int Max(int a, int b) { return (a > b) ? a : b; } 8 | 9 | static int[] PreprocessToBuildBadCharactorHeuristic(byte[] pattern) 10 | { 11 | int m = pattern.Length; 12 | int[] badCharactorShifts = new int[AlphabetSize]; 13 | 14 | for (int i = 0; i < AlphabetSize; i++) 15 | { 16 | //badCharactorShifts[i] = -1; 17 | badCharactorShifts[i] = m; 18 | } 19 | 20 | // fill the actual value of last occurrence of a character 21 | for (int i = 0; i < m; i++) 22 | { 23 | //badCharactorShifts[(int)pattern[i]] = i; 24 | badCharactorShifts[(int)pattern[i]] = m - 1 - i; 25 | } 26 | 27 | return badCharactorShifts; 28 | } 29 | 30 | static int[] PreprocessToBuildGoodSuffixHeuristic(byte[] pattern) 31 | { 32 | int m = pattern.Length; 33 | int[] goodSuffixShifts = new int[m]; 34 | int[] suffixLengthArray = GetSuffixLengthArray(pattern); 35 | 36 | for (int i = 0; i < m; ++i) 37 | { 38 | goodSuffixShifts[i] = m; 39 | } 40 | 41 | int j = 0; 42 | for (int i = m - 1; i >= -1; --i) 43 | { 44 | if (i == -1 || suffixLengthArray[i] == i + 1) 45 | { 46 | for (; j < m - 1 - i; ++j) 47 | { 48 | if (goodSuffixShifts[j] == m) 49 | { 50 | goodSuffixShifts[j] = m - 1 - i; 51 | } 52 | } 53 | } 54 | } 55 | 56 | for (int i = 0; i < m - 1; ++i) 57 | { 58 | goodSuffixShifts[m - 1 - suffixLengthArray[i]] = m - 1 - i; 59 | } 60 | 61 | return goodSuffixShifts; 62 | } 63 | 64 | static int[] GetSuffixLengthArray(byte[] pattern) 65 | { 66 | int m = pattern.Length; 67 | int[] suffixLengthArray = new int[m]; 68 | 69 | int f = 0, g = 0, i = 0; 70 | 71 | suffixLengthArray[m - 1] = m; 72 | 73 | g = m - 1; 74 | for (i = m - 2; i >= 0; --i) 75 | { 76 | if (i > g && suffixLengthArray[i + m - 1 - f] < i - g) 77 | { 78 | suffixLengthArray[i] = suffixLengthArray[i + m - 1 - f]; 79 | } 80 | else 81 | { 82 | if (i < g) 83 | { 84 | g = i; 85 | } 86 | f = i; 87 | 88 | // find different preceded character suffix 89 | while (g >= 0 && pattern[g] == pattern[g + m - 1 - f]) 90 | { 91 | --g; 92 | } 93 | suffixLengthArray[i] = f - g; 94 | } 95 | } 96 | 97 | return suffixLengthArray; 98 | } 99 | 100 | public static bool TryMatch(byte[] text, byte[] pattern, out int firstShift) 101 | { 102 | firstShift = -1; 103 | int n = text.Length; 104 | int m = pattern.Length; 105 | int s = 0; // s is shift of the pattern with respect to text 106 | int j = 0; 107 | 108 | // fill the bad character and good suffix array by preprocessing 109 | int[] badCharShifts = PreprocessToBuildBadCharactorHeuristic(pattern); 110 | int[] goodSuffixShifts = PreprocessToBuildGoodSuffixHeuristic(pattern); 111 | 112 | while (s <= (n - m)) 113 | { 114 | // starts matching from the last character of the pattern 115 | j = m - 1; 116 | 117 | // keep reducing index j of pattern while characters of 118 | // pattern and text are matching at this shift s 119 | while (j >= 0 && pattern[j] == text[s + j]) 120 | { 121 | j--; 122 | } 123 | 124 | // if the pattern is present at current shift, then index j 125 | // will become -1 after the above loop 126 | if (j < 0) 127 | { 128 | firstShift = s; 129 | return true; 130 | } 131 | else 132 | { 133 | // shift the pattern so that the bad character in text 134 | // aligns with the last occurrence of it in pattern. the 135 | // max function is used to make sure that we get a positive 136 | // shift. We may get a negative shift if the last occurrence 137 | // of bad character in pattern is on the right side of the 138 | // current character. 139 | //s += Max(1, j - badCharShifts[(int)text[s + j]]); 140 | // now, compare bad char shift and good suffix shift to find best 141 | s += Max(goodSuffixShifts[j], badCharShifts[(int)text[s + j]] - (m - 1) + j); 142 | } 143 | } 144 | 145 | return false; 146 | } 147 | 148 | public static int[] MatchAll(byte[] text, byte[] pattern) 149 | { 150 | int n = text.Length; 151 | int m = pattern.Length; 152 | int s = 0; // s is shift of the pattern with respect to text 153 | int j = 0; 154 | int[] shiftIndexes = new int[n - m + 1]; 155 | int c = 0; 156 | 157 | // fill the bad character and good suffix array by preprocessing 158 | int[] badCharShifts = PreprocessToBuildBadCharactorHeuristic(pattern); 159 | int[] goodSuffixShifts = PreprocessToBuildGoodSuffixHeuristic(pattern); 160 | 161 | while (s <= (n - m)) 162 | { 163 | // starts matching from the last character of the pattern 164 | j = m - 1; 165 | 166 | // keep reducing index j of pattern while characters of 167 | // pattern and text are matching at this shift s 168 | while (j >= 0 && pattern[j] == text[s + j]) 169 | { 170 | j--; 171 | } 172 | 173 | // if the pattern is present at current shift, then index j 174 | // will become -1 after the above loop 175 | if (j < 0) 176 | { 177 | shiftIndexes[c] = s; 178 | c++; 179 | 180 | // shift the pattern so that the next character in text 181 | // aligns with the last occurrence of it in pattern. 182 | // the condition s+m < n is necessary for the case when 183 | // pattern occurs at the end of text 184 | //s += (s + m < n) ? m - badCharShifts[(int)text[s + m]] : 1; 185 | s += goodSuffixShifts[0]; 186 | } 187 | else 188 | { 189 | // shift the pattern so that the bad character in text 190 | // aligns with the last occurrence of it in pattern. the 191 | // max function is used to make sure that we get a positive 192 | // shift. We may get a negative shift if the last occurrence 193 | // of bad character in pattern is on the right side of the 194 | // current character. 195 | //s += Max(1, j - badCharShifts[(int)text[s + j]]); 196 | // now, compare bad char shift and good suffix shift to find best 197 | s += Max(goodSuffixShifts[j], badCharShifts[(int)text[s + j]] - (m - 1) + j); 198 | } 199 | } 200 | 201 | int[] shifts = new int[c]; 202 | for (int y = 0; y < c; y++) 203 | { 204 | shifts[y] = shiftIndexes[y]; 205 | } 206 | 207 | return shifts; 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Matcher/FuzzyMatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace RevokeMsgPatcher.Matcher 6 | { 7 | /// 8 | /// 对16进制数据进行通配符查找 9 | /// 10 | public class FuzzyMatcher 11 | { 12 | public const byte wildcard = 0x3F; // 通配符 13 | 14 | /// 15 | /// 通配符匹配所有符合查找串的位置 16 | /// 17 | /// 被查找对象 18 | /// 查找串 19 | /// 20 | public static int[] MatchAll(byte[] content, byte[] pattern) 21 | { 22 | byte[] head = GetHead(pattern); 23 | int[] indexs = BoyerMooreMatcher.MatchAll(content, head); 24 | // 头串和查找串相同则直接返回,不同则继续判断是否符合查询串 25 | if (head.Length == pattern.Length) 26 | { 27 | return indexs; 28 | } 29 | else 30 | { 31 | List res = new List(); 32 | foreach (int index in indexs) 33 | { 34 | if (IsEqual(content, index, pattern)) 35 | { 36 | res.Add(index); 37 | } 38 | } 39 | return res.ToArray(); 40 | } 41 | } 42 | 43 | /// 44 | /// 通配符匹配所有符合查找串的位置,并排除已经替换的情况 45 | /// 46 | /// 被查找对象 47 | /// 查找串 48 | /// 替换串 49 | /// 50 | public static int[] MatchNotReplaced(byte[] content, byte[] searchBytes, byte[] replaceBytes) 51 | { 52 | byte[] head = GetHead(searchBytes); 53 | int[] indexs = BoyerMooreMatcher.MatchAll(content, head); 54 | // 头串和查找串相同则直接返回,不同则继续判断是否符合查询串 55 | List res = new List(); 56 | if (head.Length != searchBytes.Length) 57 | { 58 | foreach (int index in indexs) 59 | { 60 | if (IsEqual(content, index, searchBytes)) 61 | { 62 | res.Add(index); 63 | } 64 | } 65 | indexs = res.ToArray(); 66 | } 67 | // 判断是否与替换串相同 68 | res = new List(); 69 | foreach (int index in indexs) 70 | { 71 | if (!IsEqual(content, index, replaceBytes)) 72 | { 73 | res.Add(index); 74 | } 75 | } 76 | return res.ToArray(); 77 | } 78 | 79 | /// 80 | /// 获取头串 81 | /// 82 | /// 完整查找串 83 | /// 84 | private static byte[] GetHead(byte[] whole) 85 | { 86 | int len = whole.Length; 87 | for (int i = 0; i < whole.Length; i++) 88 | { 89 | if (whole[i] == wildcard) 90 | { 91 | len = i; 92 | break; 93 | } 94 | } 95 | if (len == 0) 96 | { 97 | throw new Exception("不正确的通配符位置!"); 98 | } 99 | return whole.Take(len).ToArray(); 100 | } 101 | 102 | /// 103 | /// 确认整个查找串是否匹配 104 | /// 105 | /// 被查找对象 106 | /// 头串匹配位置 107 | /// 完整查找串 108 | /// 109 | public static bool IsEqual(byte[] content, int start, byte[] whole) 110 | { 111 | int i = 0; 112 | for (i = 0; i < whole.Length; i++) 113 | { 114 | if (whole[i] == wildcard) 115 | { 116 | continue; 117 | } 118 | if (content[start + i] != whole[i]) 119 | { 120 | break; 121 | } 122 | } 123 | if (i == whole.Length) 124 | { 125 | return true; 126 | } 127 | else 128 | { 129 | return false; 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Matcher/ModifyFinder.cs: -------------------------------------------------------------------------------- 1 | using RevokeMsgPatcher.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | 8 | namespace RevokeMsgPatcher.Matcher 9 | { 10 | public class ModifyFinder 11 | { 12 | // TODO 该逻辑需要优化! 13 | public static List FindChanges(string path, List replacePatterns) 14 | { 15 | Stopwatch sw = new Stopwatch(); 16 | sw.Start(); 17 | // 读取整个文件(dll) 18 | byte[] fileByteArray = File.ReadAllBytes(path); 19 | Console.WriteLine("读取文件耗时:{0}ms.", sw.Elapsed.TotalMilliseconds); 20 | 21 | List changes = new List(); // 匹配且需要替换的地方 22 | 23 | // 查找所有替换点 24 | int matchNum = 0; // 匹配数量 25 | foreach (ReplacePattern pattern in replacePatterns) 26 | { 27 | // 所有的匹配点位 28 | int[] matchIndexs = FuzzyMatcher.MatchAll(fileByteArray, pattern.Search); 29 | Console.WriteLine("匹配{0}耗时:{1}ms.", pattern.Category, sw.Elapsed.TotalMilliseconds); 30 | if (matchIndexs.Length >= 1) 31 | { 32 | for (int i = 0; i < matchIndexs.Length; i++) 33 | { 34 | matchNum++; 35 | // 与要替换的串不一样才需要替换(当前的特征肯定不一样) 36 | if (!FuzzyMatcher.IsEqual(fileByteArray, matchIndexs[i], pattern.Replace)) 37 | { 38 | changes.Add(new Change(matchIndexs[i], pattern.Replace)); 39 | } 40 | } 41 | } 42 | } 43 | 44 | // 匹配数和期望的匹配数不一致时报错(当前一个特征会出现多次) 45 | if (matchNum < replacePatterns.Count) 46 | { 47 | Tuple> res = IsAllReplaced(fileByteArray, replacePatterns); 48 | if (res.Item1) 49 | { 50 | throw new BusinessException("match_already_replace", "特征比对:当前应用已经安装了对应功能的补丁!"); 51 | } 52 | else 53 | { 54 | if (res.Item2.Count > 0) 55 | { 56 | throw new BusinessException("match_inconformity", $"特征比对:以下功能补丁已经安装,请取消勾选!\n已安装功能:【{string.Join("、", res.Item2)}】"); 57 | } 58 | else 59 | { 60 | throw new BusinessException("match_inconformity", $"特征比对:当前特征码匹配数[{matchNum}]和期望的匹配数[{replacePatterns.Count}]不一致。\n" + 61 | $"出现此种情况的一般有如下可能:\n" + 62 | $"1. 你可能已经安装了某个功能的补丁,请选择未安装功能进行安装。\n" + 63 | $"2. 如果当前版本为最新版本,特征码可能出现变化(可能性比较低),请联系作者处理。"); 64 | } 65 | } 66 | } 67 | else 68 | { 69 | // 匹配数和需要替换的数量不一致时,可能时部分/所有特征已经被替换 70 | if (matchNum != changes.Count) 71 | { 72 | // 此逻辑在当前特征配置下不会进入,因为查找串和替换串当前全部都是不相同的 73 | if (changes.Count == 0) 74 | { 75 | throw new BusinessException("match_already_replace", "特征比对:当前应用已经安装了所选功能补丁!"); 76 | } 77 | else 78 | { 79 | throw new BusinessException("match_part_replace", "特征比对:部分特征已经被替换,请确认是否有使用过其他防撤回/多开补丁!"); 80 | } 81 | 82 | } 83 | else 84 | { 85 | // 匹配数和需要替换的数量一致时才是正常状态 86 | return changes; 87 | } 88 | } 89 | } 90 | 91 | public static SortedSet FindReplacedFunction(string path, List replacePatterns) 92 | { 93 | Stopwatch sw = new Stopwatch(); 94 | sw.Start(); 95 | byte[] fileByteArray = File.ReadAllBytes(path); 96 | Console.WriteLine("读取文件耗时:{0}ms.", sw.Elapsed.TotalMilliseconds); 97 | Tuple> res = IsAllReplaced(fileByteArray, replacePatterns); 98 | Console.WriteLine("匹配耗时:{0}ms.", sw.Elapsed.TotalMilliseconds); 99 | return res.Item2; 100 | } 101 | 102 | private static Tuple> IsAllReplaced(byte[] partByteArray, List replacePatterns) 103 | { 104 | int matchNum = 0; 105 | SortedSet alreadyReplaced = new SortedSet(); // 已经被替换特征的功能 106 | foreach (ReplacePattern pattern in replacePatterns) 107 | { 108 | int[] searchMatchIndexs = FuzzyMatcher.MatchAll(partByteArray, pattern.Search); 109 | int[] replaceMatchIndexs = FuzzyMatcher.MatchAll(partByteArray, pattern.Replace); 110 | // 查找串没有,但是替换串存在,也就是说明这个功能已经完全完成替换 111 | if (searchMatchIndexs.Length == 0 && replaceMatchIndexs.Length > 0) 112 | { 113 | alreadyReplaced.Add(pattern.Category); 114 | } 115 | } 116 | return new Tuple>(matchNum >= replacePatterns.Count, alreadyReplaced); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Model/App.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RevokeMsgPatcher.Model 8 | { 9 | public class App 10 | { 11 | public string Name { get; set; } 12 | 13 | public Dictionary FileTargetInfos { get; set; } 14 | 15 | public Dictionary> FileModifyInfos { get; set; } 16 | 17 | /// 18 | /// 通用的修改特征 19 | /// 20 | public Dictionary> FileCommonModifyInfos { get; set; } 21 | 22 | public HashSet GetSupportVersions() 23 | { 24 | // 使用 HashSet 防重 25 | HashSet versions = new HashSet(); 26 | // 精准 27 | if (FileModifyInfos != null) 28 | { 29 | foreach (List modifyInfos in FileModifyInfos.Values) 30 | { 31 | foreach (ModifyInfo modifyInfo in modifyInfos) 32 | { 33 | versions.Add(modifyInfo.Version); 34 | } 35 | } 36 | } 37 | // 模糊 范围 38 | if (FileCommonModifyInfos != null) 39 | { 40 | foreach (List commonModifyInfos in FileCommonModifyInfos.Values) 41 | { 42 | foreach (CommonModifyInfo commonModifyInfo in commonModifyInfos) 43 | { 44 | string end = string.IsNullOrEmpty(commonModifyInfo.EndVersion) ? "最新版" : commonModifyInfo.EndVersion; 45 | versions.Add(commonModifyInfo.StartVersion + "~" + end); 46 | } 47 | } 48 | } 49 | return versions; 50 | } 51 | 52 | public string GetSupportVersionStr() 53 | { 54 | string str = ""; 55 | foreach (string v in GetSupportVersions()) 56 | { 57 | str += v + "、"; 58 | } 59 | if (str.Length > 1) 60 | { 61 | str = str.Substring(0, str.Length - 1); 62 | } 63 | return str; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Model/Bag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RevokeMsgPatcher.Model 8 | { 9 | public class Bag 10 | { 11 | public Dictionary Apps { get; set; } 12 | 13 | public string LatestVersion { get; set; } 14 | 15 | public string Notice { get; set; } 16 | 17 | public string NoticeUrl { get; set; } 18 | 19 | public int PatchVersion { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Model/Change.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RevokeMsgPatcher.Model 8 | { 9 | public class Change 10 | { 11 | public long Position { get; set; } 12 | 13 | public byte[] Content { get; set; } 14 | 15 | public Change() 16 | { 17 | 18 | } 19 | 20 | public Change(long position, byte[] content) 21 | { 22 | Position = position; 23 | Content = content; 24 | } 25 | 26 | public Change Clone() 27 | { 28 | Change o = new Change(); 29 | o.Position = Position; 30 | o.Content = Content; 31 | return o; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Model/CommonModifyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace RevokeMsgPatcher.Model 9 | { 10 | public class CommonModifyInfo 11 | { 12 | public string Name { get; set; } 13 | 14 | public string StartVersion { get; set; } 15 | 16 | public string EndVersion { get; set; } 17 | 18 | public List ReplacePatterns { get; set; } 19 | 20 | public CommonModifyInfo Clone() 21 | { 22 | CommonModifyInfo o = new CommonModifyInfo(); 23 | o.Name = Name; 24 | o.StartVersion = StartVersion; 25 | o.EndVersion = EndVersion; 26 | List cs = new List(); 27 | foreach (ReplacePattern c in ReplacePatterns) 28 | { 29 | cs.Add(c.Clone()); 30 | } 31 | o.ReplacePatterns = cs; 32 | return o; 33 | } 34 | 35 | public List GetCategories() 36 | { 37 | if (ReplacePatterns != null && ReplacePatterns.Count > 0) 38 | { 39 | return ReplacePatterns.Select(p => p.Category).ToList(); 40 | } 41 | else 42 | { 43 | return new List(); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Model/Json/LiteLoaderPackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RevokeMsgPatcher.Model.Json 8 | { 9 | internal class LiteLoaderPackage 10 | { 11 | public string Name { get; set; } 12 | public string Version { get; set; } 13 | public bool Private { get; set; } 14 | public string Description { get; set; } 15 | public string ProductName { get; set; } 16 | public string Homepage { get; set; } 17 | public bool SideEffects { get; set; } 18 | public string Main { get; set; } 19 | public string BuildVersion { get; set; } 20 | public bool IsPureShell { get; set; } 21 | public bool IsByteCodeShell { get; set; } 22 | public string Platform { get; set; } 23 | public string EleArch { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Model/Json/LiteLoaderPluginsManifest.cs: -------------------------------------------------------------------------------- 1 | namespace RevokeMsgPatcher.Model.Json 2 | { 3 | 4 | /// 5 | /// 只有部分信息,主要是拿版本号 6 | /// https://github.com/xh321/LiteLoaderQQNT-Anti-Recall/blob/master/manifest.json 7 | /// 8 | internal class LiteLoaderPluginsManifest 9 | { 10 | public string Type { get; set; } 11 | public string Name { get; set; } 12 | public string Slug { get; set; } 13 | public string Description { get; set; } 14 | public string Version { get; set; } 15 | public string Icon { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Model/Json/ReleaseApiRes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace RevokeMsgPatcher.Model.Json 5 | { 6 | internal class ReleaseApiRes 7 | { 8 | 9 | public string Url { get; set; } 10 | public string AssetsUrl { get; set; } 11 | public string UploadUrl { get; set; } 12 | public string HtmlUrl { get; set; } 13 | public int Id { get; set; } 14 | public string NodeId { get; set; } 15 | public string TagName { get; set; } 16 | public string TargetCommitish { get; set; } 17 | public string Name { get; set; } 18 | public bool Draft { get; set; } 19 | public bool Prerelease { get; set; } 20 | public DateTime CreatedAt { get; set; } 21 | public DateTime PublishedAt { get; set; } 22 | public List Assets { get; set; } 23 | public string TarballUrl { get; set; } 24 | public string ZipballUrl { get; set; } 25 | public string Body { get; set; } 26 | } 27 | 28 | public class Asset 29 | { 30 | public string Url { get; set; } 31 | public int Id { get; set; } 32 | public string NodeId { get; set; } 33 | public string Name { get; set; } 34 | public object Label { get; set; } 35 | public string ContentType { get; set; } 36 | public string State { get; set; } 37 | public int Size { get; set; } 38 | public int DownloadCount { get; set; } 39 | public string CreatedAt { get; set; } 40 | public string UpdatedAt { get; set; } 41 | public string BrowserDownloadUrl { get; set; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Model/Json/VersionJson.cs: -------------------------------------------------------------------------------- 1 | namespace RevokeMsgPatcher.Model.Json 2 | { 3 | internal class VersionJson 4 | { 5 | public string Name { get; set; } 6 | public string Version { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Model/ModifyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace RevokeMsgPatcher.Model 9 | { 10 | public class ModifyInfo 11 | { 12 | public string Name { get; set; } 13 | 14 | public string Version { get; set; } 15 | 16 | public string SHA1Before { get; set; } 17 | 18 | public string SHA1After { get; set; } 19 | 20 | public List Changes { get; set; } 21 | 22 | public ModifyInfo Clone() 23 | { 24 | ModifyInfo o = new ModifyInfo(); 25 | o.Name = Name; 26 | o.Version = Version; 27 | o.SHA1Before = SHA1Before; 28 | o.SHA1After = SHA1After; 29 | List cs = new List(); 30 | foreach(Change c in Changes) 31 | { 32 | cs.Add(c.Clone()); 33 | } 34 | o.Changes = cs; 35 | return o; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Model/ReplacePattern.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RevokeMsgPatcher.Model 4 | { 5 | public class ReplacePattern 6 | { 7 | public byte[] Search { get; set; } 8 | 9 | public byte[] Replace { get; set; } 10 | 11 | public string Category { get; set; } 12 | 13 | /// 14 | /// 悬浮气泡提示 15 | /// 16 | public string Tips { get; set; } 17 | 18 | /// 19 | /// 同类冲突标签 20 | /// 21 | public string SimilarCategories { get; set; } 22 | 23 | /// 24 | /// 选择同类冲突标签时的提示 25 | /// 26 | public string ChooseSimilarCategoriesMsg { get; set; } 27 | 28 | public ReplacePattern Clone() 29 | { 30 | ReplacePattern o = new ReplacePattern(); 31 | o.Search = Search; 32 | o.Replace = Replace; 33 | return o; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Model/TargetInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RevokeMsgPatcher.Model 8 | { 9 | public class TargetInfo 10 | { 11 | public string Name { get; set; } 12 | 13 | public string RelativePath { get; set; } 14 | 15 | public string Memo { get; set; } 16 | 17 | public string StartVersion { get; set; } 18 | 19 | public string EndVersion { get; set; } 20 | 21 | public TargetInfo Clone() 22 | { 23 | TargetInfo o = new TargetInfo(); 24 | o.Name = Name; 25 | o.RelativePath = RelativePath; 26 | o.Memo = Memo; 27 | return o; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Modifier/FileHexEditor.cs: -------------------------------------------------------------------------------- 1 | using RevokeMsgPatcher.Model; 2 | using RevokeMsgPatcher.Utils; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | namespace RevokeMsgPatcher.Modifier 7 | { 8 | public class FileHexEditor 9 | { 10 | public string FileName { get; set; } 11 | 12 | public string FilePath { get; set; } 13 | 14 | public string FileBakPath { get; set; } 15 | 16 | private string fileReplacedPath; 17 | 18 | private string version; 19 | public string FileVersion 20 | { 21 | get 22 | { 23 | if (version == null) 24 | { 25 | version = FileUtil.GetFileVersion(FilePath); 26 | } 27 | return version; 28 | } 29 | } 30 | 31 | public string BackupFileVersion 32 | { 33 | get 34 | { 35 | return FileUtil.GetFileVersion(FileBakPath); 36 | } 37 | } 38 | 39 | public string sha1; 40 | public string FileSHA1 41 | { 42 | get 43 | { 44 | if (sha1 == null) 45 | { 46 | sha1 = FileUtil.ComputeFileSHA1(FilePath); 47 | } 48 | return sha1; 49 | } 50 | } 51 | 52 | public TargetInfo FileTargetInfo { get; set; } 53 | 54 | /// 55 | /// 通过比对SHA1得到的特定版本的修改信息 56 | /// 57 | public ModifyInfo FileModifyInfo { get; set; } 58 | 59 | /// 60 | /// 通过比对版本范围得到的通用查找替换的修改信息(特征码替换信息) 61 | /// 62 | public CommonModifyInfo FileCommonModifyInfo { get; set; } 63 | 64 | /// 65 | /// 将要执行的修改 66 | /// 67 | public List TargetChanges { get; set; } 68 | 69 | public FileHexEditor(string installPath, TargetInfo target) 70 | { 71 | FileTargetInfo = target.Clone(); 72 | FileName = FileTargetInfo.Name; 73 | FilePath = Path.Combine(installPath, FileTargetInfo.RelativePath); 74 | FileBakPath = FilePath + ".h.bak"; 75 | fileReplacedPath = FilePath + ".h.process"; 76 | } 77 | 78 | /// 79 | /// 备份 80 | /// 81 | public void Backup() 82 | { 83 | // 不覆盖同版本的备份文件 84 | if (File.Exists(FileBakPath)) 85 | { 86 | if (FileVersion != BackupFileVersion) 87 | { 88 | File.Copy(FilePath, FileBakPath, true); 89 | } 90 | } 91 | else 92 | { 93 | File.Copy(FilePath, FileBakPath, true); 94 | } 95 | 96 | } 97 | 98 | /// 99 | /// 打补丁 100 | /// 101 | /// 102 | public bool Patch() 103 | { 104 | if (TargetChanges == null) 105 | { 106 | throw new BusinessException("change_null", "在安装补丁时,变更的内容为空!"); 107 | } 108 | 109 | FileUtil.EditMultiHex(FilePath, TargetChanges); 110 | return true; 111 | } 112 | 113 | /// 114 | /// 还原 115 | /// 116 | public void Restore() 117 | { 118 | File.Copy(FileBakPath, FilePath, true); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Modifier/QQLiteModifier.cs: -------------------------------------------------------------------------------- 1 | using RevokeMsgPatcher.Model; 2 | using RevokeMsgPatcher.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace RevokeMsgPatcher.Modifier 7 | { 8 | class QQLiteModifier : AppModifier 9 | { 10 | public QQLiteModifier(App config) 11 | { 12 | this.config = config; 13 | } 14 | 15 | public override void AfterPatchSuccess() 16 | { 17 | } 18 | 19 | public override void AfterPatchFail() 20 | { 21 | } 22 | 23 | /// 24 | /// 自动寻找获取微信安装路径 25 | /// 26 | /// 27 | public override string FindInstallPath() 28 | { 29 | try 30 | { 31 | string installPath = PathUtil.FindInstallPathFromRegistry("QQLite"); 32 | if (!IsAllFilesExist(installPath)) 33 | { 34 | List defaultPathList = PathUtil.GetDefaultInstallPaths(@"Tencent\QQLite"); 35 | foreach (string defaultPath in defaultPathList) 36 | { 37 | if (IsAllFilesExist(defaultPath)) 38 | { 39 | return defaultPath; 40 | } 41 | } 42 | } 43 | else 44 | { 45 | return installPath; 46 | } 47 | } 48 | catch (Exception e) 49 | { 50 | Console.WriteLine(e.Message); 51 | } 52 | return null; 53 | } 54 | 55 | /// 56 | /// 获取整个APP的当前版本 57 | /// 58 | /// 59 | public override string GetVersion() 60 | { 61 | if (editors != null && editors.Count > 0) 62 | { 63 | foreach (FileHexEditor editor in editors) 64 | { 65 | if (editor.FileName == "IM.dll") 66 | { 67 | return editor.FileVersion; 68 | } 69 | } 70 | } 71 | return ""; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Modifier/QQModifier.cs: -------------------------------------------------------------------------------- 1 | using RevokeMsgPatcher.Model; 2 | using RevokeMsgPatcher.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace RevokeMsgPatcher.Modifier 7 | { 8 | class QQModifier : AppModifier 9 | { 10 | public QQModifier(App config) 11 | { 12 | this.config = config; 13 | } 14 | 15 | public override void AfterPatchSuccess() 16 | { 17 | } 18 | 19 | public override void AfterPatchFail() 20 | { 21 | } 22 | 23 | /// 24 | /// 自动寻找获取微信安装路径 25 | /// 26 | /// 27 | public override string FindInstallPath() 28 | { 29 | try 30 | { 31 | string installPath = PathUtil.FindInstallPathFromRegistry("{052CFB79-9D62-42E3-8A15-DE66C2C97C3E}"); 32 | if (!IsAllFilesExist(installPath)) 33 | { 34 | List defaultPathList = PathUtil.GetDefaultInstallPaths(@"Tencent\QQ"); 35 | foreach (string defaultPath in defaultPathList) 36 | { 37 | if (IsAllFilesExist(defaultPath)) 38 | { 39 | return defaultPath; 40 | } 41 | } 42 | } 43 | else 44 | { 45 | return installPath; 46 | } 47 | } 48 | catch (Exception e) 49 | { 50 | Console.WriteLine(e.Message); 51 | } 52 | return null; 53 | } 54 | 55 | /// 56 | /// 获取整个APP的当前版本 57 | /// 58 | /// 59 | public override string GetVersion() 60 | { 61 | if (editors != null && editors.Count > 0) 62 | { 63 | foreach (FileHexEditor editor in editors) 64 | { 65 | if (editor.FileName == "IM.dll") 66 | { 67 | return editor.FileVersion; 68 | } 69 | } 70 | } 71 | return ""; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Modifier/QQNTModifier.cs: -------------------------------------------------------------------------------- 1 | using RevokeMsgPatcher.Model; 2 | using RevokeMsgPatcher.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Text.RegularExpressions; 8 | using System.Windows.Forms; 9 | 10 | namespace RevokeMsgPatcher.Modifier 11 | { 12 | class QQNTModifier : AppModifier 13 | { 14 | public QQNTModifier(App config) 15 | { 16 | this.config = config; 17 | } 18 | 19 | /// 20 | /// 自动寻找获取微信安装路径 21 | /// 22 | /// 23 | public override string FindInstallPath() 24 | { 25 | return "请在新弹出的窗口内进行 LiteLoaderQQNT 的安装与更新!"; 26 | // try 27 | // { 28 | // string installPath = PathUtil.FindInstallPathFromRegistryWOW6432Node("QQ"); 29 | // if (!string.IsNullOrEmpty(installPath)) 30 | // { 31 | // installPath = Path.GetDirectoryName(installPath); 32 | // if (IsAllFilesExist(installPath)) 33 | // { 34 | // return installPath; 35 | // } 36 | // } 37 | // 38 | // installPath = PathUtil.FindInstallPathFromRegistry("QQNT"); 39 | // if (!IsAllFilesExist(installPath)) 40 | // { 41 | // List defaultPathList = PathUtil.GetDefaultInstallPaths(@"Tencent\QQNT"); 42 | // foreach (string defaultPath in defaultPathList) 43 | // { 44 | // if (IsAllFilesExist(defaultPath)) 45 | // { 46 | // return defaultPath; 47 | // } 48 | // } 49 | // } 50 | // else 51 | // { 52 | // return installPath; 53 | // } 54 | // } 55 | // catch (Exception e) 56 | // { 57 | // Console.WriteLine(e.Message); 58 | // } 59 | // 60 | // return null; 61 | } 62 | 63 | /// 64 | /// 获取整个APP的当前版本 65 | /// 66 | /// 67 | public override string GetVersion() 68 | { 69 | if (editors != null && editors.Count > 0) 70 | { 71 | foreach (FileHexEditor editor in editors) 72 | { 73 | if (editor.FileName == "QQ.exe") 74 | { 75 | return editor.FileVersion; 76 | } 77 | } 78 | } 79 | 80 | return ""; 81 | } 82 | 83 | public string GetIndexJsPath() 84 | { 85 | if (string.IsNullOrEmpty(InstallPath)) 86 | { 87 | throw new Exception("未获取到QQNT安装路径或者QQNT安装路径不合法"); 88 | } 89 | 90 | string indexPath = Path.Combine(InstallPath, @"resources\app\app_launcher\index.js"); 91 | if (!File.Exists(indexPath)) 92 | { 93 | throw new Exception("未找到index.js文件"); 94 | } 95 | 96 | return indexPath; 97 | } 98 | 99 | public string GetLiteLoaderPath() 100 | { 101 | return Path.Combine(Application.StartupPath, @"LiteLoaderQQNT"); 102 | } 103 | 104 | public override void AfterPatchSuccess() 105 | { 106 | string indexPath = GetIndexJsPath(); 107 | string content = File.ReadAllText(indexPath); 108 | // 正则 require\(String.raw`.*`\); 109 | string pattern = @"require\(String.raw`.*`\);"; 110 | string liteLoaderPath = GetLiteLoaderPath(); 111 | if (!Directory.Exists(liteLoaderPath)) 112 | { 113 | MessageBox.Show("LiteLoaderQQNT文件夹不存在,仅安装QQNT去验证补丁", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning); 114 | return; 115 | } 116 | 117 | string replacement = $"require(String.raw`{liteLoaderPath}`);"; 118 | if (Regex.IsMatch(content, pattern)) 119 | { 120 | content = Regex.Replace(content, pattern, replacement); 121 | } 122 | else 123 | { 124 | content = replacement + "\n" + content; 125 | } 126 | 127 | File.WriteAllText(indexPath, content); 128 | } 129 | 130 | public override void AfterPatchFail() 131 | { 132 | try 133 | { 134 | string indexPath = GetIndexJsPath(); 135 | string content = File.ReadAllText(indexPath); 136 | string pattern = @"require\(String.raw`.*`\);\n"; 137 | if (Regex.IsMatch(content, pattern)) 138 | { 139 | content = Regex.Replace(content, pattern, ""); 140 | File.WriteAllText(indexPath, content); 141 | } 142 | } 143 | catch (Exception e) 144 | { 145 | Debug.WriteLine(e); 146 | } 147 | } 148 | 149 | public new bool Restore() 150 | { 151 | AfterPatchFail(); 152 | return base.Restore(); 153 | } 154 | } 155 | } -------------------------------------------------------------------------------- /RevokeMsgPatcher/Modifier/TIMModifier.cs: -------------------------------------------------------------------------------- 1 | using RevokeMsgPatcher.Model; 2 | using RevokeMsgPatcher.Utils; 3 | 4 | namespace RevokeMsgPatcher.Modifier 5 | { 6 | class TIMModifier : AppModifier 7 | { 8 | 9 | public TIMModifier(App config) 10 | { 11 | this.config = config; 12 | } 13 | 14 | public override void AfterPatchSuccess() 15 | { 16 | } 17 | 18 | public override void AfterPatchFail() 19 | { 20 | } 21 | 22 | /// 23 | /// 自动寻找获取微信安装路径 24 | /// 25 | /// 26 | public override string FindInstallPath() 27 | { 28 | string installPath = PathUtil.FindInstallPathFromRegistry("TIM"); 29 | if (!IsAllFilesExist(installPath)) 30 | { 31 | foreach (string defaultPath in PathUtil.GetDefaultInstallPaths(@"Tencent\TIM")) 32 | { 33 | if (IsAllFilesExist(defaultPath)) 34 | { 35 | return defaultPath; 36 | } 37 | } 38 | } 39 | else 40 | { 41 | return installPath; 42 | } 43 | return null; 44 | } 45 | 46 | /// 47 | /// 获取整个APP的当前版本 48 | /// 49 | /// 50 | public override string GetVersion() 51 | { 52 | if (editors != null && editors.Count > 0) 53 | { 54 | foreach (FileHexEditor editor in editors) 55 | { 56 | if (editor.FileName == "IM.dll") 57 | { 58 | return editor.FileVersion; 59 | } 60 | } 61 | } 62 | return ""; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Modifier/WechatModifier.cs: -------------------------------------------------------------------------------- 1 | using RevokeMsgPatcher.Model; 2 | using RevokeMsgPatcher.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | 7 | namespace RevokeMsgPatcher.Modifier 8 | { 9 | class WechatModifier : AppModifier 10 | { 11 | 12 | public WechatModifier(App config) 13 | { 14 | this.config = config; 15 | } 16 | 17 | public override void AfterPatchSuccess() 18 | { 19 | } 20 | 21 | public override void AfterPatchFail() 22 | { 23 | } 24 | 25 | /// 26 | /// 自动寻找获取微信安装路径 27 | /// 28 | /// 29 | public override string FindInstallPath() 30 | { 31 | try 32 | { 33 | string installPath = PathUtil.FindInstallPathFromRegistry("Wechat"); 34 | string realPath = GetRealInstallPath(installPath); 35 | if (string.IsNullOrEmpty(realPath)) 36 | { 37 | List defaultPathList = PathUtil.GetDefaultInstallPaths(@"Tencent\Wechat"); 38 | foreach (string defaultPath in defaultPathList) 39 | { 40 | realPath = GetRealInstallPath(defaultPath); 41 | if (!string.IsNullOrEmpty(realPath)) 42 | { 43 | return defaultPath; 44 | } 45 | } 46 | } 47 | else 48 | { 49 | return realPath; 50 | } 51 | } 52 | catch (Exception e) 53 | { 54 | Console.WriteLine(e.Message); 55 | } 56 | return null; 57 | } 58 | 59 | /// 60 | /// 微信 3.5.0.4 改变了目录结构 61 | /// 62 | /// 63 | /// 64 | private string GetRealInstallPath(string basePath) 65 | { 66 | if (basePath == null) 67 | { 68 | return null; 69 | } 70 | if (IsAllFilesExist(basePath)) 71 | { 72 | return basePath; 73 | } 74 | DirectoryInfo[] directories = new DirectoryInfo(basePath).GetDirectories(); 75 | PathUtil.SortByLastWriteTimeDesc(ref directories); // 按修改时间倒序 76 | foreach (DirectoryInfo folder in directories) 77 | { 78 | if (IsAllFilesExist(folder.FullName)) 79 | { 80 | return folder.FullName; 81 | } 82 | } 83 | return null; 84 | } 85 | 86 | 87 | /// 88 | /// 获取整个APP的当前版本 89 | /// 90 | /// 91 | public override string GetVersion() 92 | { 93 | if (editors != null && editors.Count > 0) 94 | { 95 | foreach (FileHexEditor editor in editors) 96 | { 97 | if (editor.FileName == "WeChatWin.dll") 98 | { 99 | return editor.FileVersion; 100 | } 101 | } 102 | } 103 | return ""; 104 | } 105 | 106 | //public override bool ValidateAndInitialize(string installPath) 107 | //{ 108 | // // 判断是否是安装路径 109 | // if (!IsAllBinaryFilesExist(installPath)) 110 | // { 111 | // return false; 112 | // } 113 | 114 | // // 初始化十六进制文件编辑器 115 | // // 并寻找与之配对的版本修改信息 116 | // InitEditors(installPath); 117 | 118 | // return true; 119 | //} 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Modifier/WeixinModifier.cs: -------------------------------------------------------------------------------- 1 | using RevokeMsgPatcher.Model; 2 | using RevokeMsgPatcher.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | 7 | namespace RevokeMsgPatcher.Modifier 8 | { 9 | class WeixinModifier : AppModifier 10 | { 11 | 12 | public WeixinModifier(App config) 13 | { 14 | this.config = config; 15 | } 16 | 17 | public override void AfterPatchSuccess() 18 | { 19 | } 20 | 21 | public override void AfterPatchFail() 22 | { 23 | } 24 | 25 | /// 26 | /// 自动寻找获取微信安装路径 27 | /// 28 | /// 29 | public override string FindInstallPath() 30 | { 31 | try 32 | { 33 | string installPath = PathUtil.FindInstallPathFromRegistryWOW6432Node("Weixin"); 34 | string realPath = null; 35 | if (!string.IsNullOrEmpty(installPath)) 36 | { 37 | installPath = Path.GetDirectoryName(installPath); 38 | realPath = GetRealInstallPath(installPath); 39 | } 40 | if (string.IsNullOrEmpty(realPath)) 41 | { 42 | List defaultPathList = PathUtil.GetDefaultInstallPaths(@"Tencent\Weixin"); 43 | foreach (string defaultPath in defaultPathList) 44 | { 45 | realPath = GetRealInstallPath(defaultPath); 46 | if (!string.IsNullOrEmpty(realPath)) 47 | { 48 | return defaultPath; 49 | } 50 | } 51 | } 52 | else 53 | { 54 | return realPath; 55 | } 56 | } 57 | catch (Exception e) 58 | { 59 | Console.WriteLine(e.Message); 60 | } 61 | return null; 62 | } 63 | 64 | /// 65 | /// 微信目录结构 66 | /// 67 | /// 68 | /// 69 | private string GetRealInstallPath(string basePath) 70 | { 71 | if (basePath == null) 72 | { 73 | return null; 74 | } 75 | DirectoryInfo[] directories = new DirectoryInfo(basePath).GetDirectories(); 76 | PathUtil.SortByLastWriteTimeDesc(ref directories); // 按修改时间倒序 77 | foreach (DirectoryInfo folder in directories) 78 | { 79 | if (IsAllFilesExist(folder.FullName)) 80 | { 81 | return folder.FullName; 82 | } 83 | } 84 | return null; 85 | } 86 | 87 | 88 | /// 89 | /// 获取整个APP的当前版本 90 | /// 91 | /// 92 | public override string GetVersion() 93 | { 94 | if (editors != null && editors.Count > 0) 95 | { 96 | foreach (FileHexEditor editor in editors) 97 | { 98 | if (editor.FileName == "Weixin.dll") 99 | { 100 | return editor.FileVersion; 101 | } 102 | } 103 | } 104 | return ""; 105 | } 106 | 107 | //public override bool ValidateAndInitialize(string installPath) 108 | //{ 109 | // // 判断是否是安装路径 110 | // if (!IsAllBinaryFilesExist(installPath)) 111 | // { 112 | // return false; 113 | // } 114 | 115 | // // 初始化十六进制文件编辑器 116 | // // 并寻找与之配对的版本修改信息 117 | // InitEditors(installPath); 118 | 119 | // return true; 120 | //} 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace RevokeMsgPatcher 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// 应用程序的主入口点。 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | 18 | 19 | #if DEBUG 20 | Application.EnableVisualStyles(); 21 | Application.SetCompatibleTextRenderingDefault(false); 22 | Application.Run(new FormMain()); 23 | #else 24 | try 25 | { 26 | Application.EnableVisualStyles(); 27 | Application.SetCompatibleTextRenderingDefault(false); 28 | 29 | //当前用户是管理员的时候,直接启动应用程序 30 | //如果不是管理员,则使用启动对象启动程序,以确保使用管理员身份运行 31 | //获得当前登录的Windows用户标示 32 | System.Security.Principal.WindowsIdentity identity = System.Security.Principal.WindowsIdentity.GetCurrent(); 33 | System.Security.Principal.WindowsPrincipal principal = new System.Security.Principal.WindowsPrincipal(identity); 34 | //判断当前登录用户是否为管理员 35 | if (principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator)) 36 | { 37 | //如果是管理员,则直接运行 38 | Application.Run(new FormMain()); 39 | } 40 | else 41 | { 42 | //创建启动对象 43 | System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(); 44 | startInfo.UseShellExecute = true; 45 | startInfo.WorkingDirectory = Environment.CurrentDirectory; 46 | startInfo.FileName = Application.ExecutablePath; 47 | //设置启动动作,确保以管理员身份运行 48 | startInfo.Verb = "runas"; 49 | try 50 | { 51 | System.Diagnostics.Process.Start(startInfo); 52 | } 53 | catch 54 | { 55 | return; 56 | } 57 | //退出 58 | Application.Exit(); 59 | } 60 | 61 | } 62 | catch (Exception ex) 63 | { 64 | MessageBox.Show(ex.Message + "\n" + ex.StackTrace.Trim(), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); 65 | } 66 | #endif 67 | } 68 | 69 | 70 | static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) 71 | { 72 | MessageBox.Show(e.Exception.Message + "\n" + e.Exception.StackTrace.Trim(), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); 73 | } 74 | 75 | static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 76 | { 77 | MessageBox.Show((e.ExceptionObject as Exception).Message + "\n" + (e.ExceptionObject as Exception).StackTrace.Trim(), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("RevokeMsgPatcher")] 9 | [assembly: AssemblyDescription("微信防撤回补丁")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("huiyadanli")] 12 | [assembly: AssemblyProduct("RevokeMsgPatcher")] 13 | [assembly: AssemblyCopyright("Copyright © 2019-2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("977bf781-ced8-4389-9404-0fa08fdf21df")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 33 | // 方法是按如下所示使用“*”: : 34 | //[assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("2.0")] 36 | [assembly: AssemblyFileVersion("2.0")] 37 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace RevokeMsgPatcher.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RevokeMsgPatcher.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// 查找类似 {"Apps":{"Wechat":{"Name":"WeChat","FileTargetInfos":{"WeChatWin.dll":{"Name":"WeChatWin.dll","RelativePath":"WeChatWin.dll","Memo":null,"StartVersion":"1.0.0.0","EndVersion":null},"WeChat.exe":{"Name":"WeChat.exe","RelativePath":"../WeChat.exe","Memo":null,"StartVersion":"3.7.0.0","EndVersion":"3.7.0.26"}},"FileModifyInfos":{"WeChat.exe":[],"WeChatWin.dll":[{"Name":"WeChatWin.dll","Version":"3.3.5.25","SHA1Before":"3e94753ccbc2799d98f3c741377e99bdae33b4cf","SHA1After":"ab98f83fc16674ac4911380882c79c3ca4c2f [字符串的其余部分被截断]"; 的本地化字符串。 65 | /// 66 | internal static string PatchJson { 67 | get { 68 | return ResourceManager.GetString("PatchJson", resourceCulture); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace RevokeMsgPatcher.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.1.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/RevokeMsgPatcher.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {977BF781-CED8-4389-9404-0FA08FDF21DF} 8 | WinExe 9 | RevokeMsgPatcher 10 | RevokeMsgPatcher 11 | v4.5.2 12 | 512 13 | true 14 | 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | icon.ico 37 | 38 | 39 | app.manifest 40 | 41 | 42 | 43 | ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Form 64 | 65 | 66 | FormMain.cs 67 | 68 | 69 | Form 70 | 71 | 72 | FormLiteLoaderQQNT.cs 73 | 74 | 75 | Form 76 | 77 | 78 | FormPatchInfo.cs 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | FormMain.cs 116 | 117 | 118 | FormLiteLoaderQQNT.cs 119 | 120 | 121 | FormPatchInfo.cs 122 | 123 | 124 | ResXFileCodeGenerator 125 | Resources.Designer.cs 126 | Designer 127 | 128 | 129 | True 130 | Resources.resx 131 | True 132 | 133 | 134 | 135 | 136 | SettingsSingleFileGenerator 137 | Settings.Designer.cs 138 | 139 | 140 | True 141 | Settings.settings 142 | True 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Utils/ByteUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RevokeMsgPatcher.Utils 8 | { 9 | public class ByteUtil 10 | { 11 | public static byte[] HexStringToByteArray(string hex) 12 | { 13 | hex = hex.Replace(" ", ""); 14 | return Enumerable.Range(0, hex.Length) 15 | .Where(x => x % 2 == 0) 16 | .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) 17 | .ToArray(); 18 | } 19 | 20 | 21 | public static string ByteArrayToHexString(byte[] data) 22 | { 23 | StringBuilder sb = new StringBuilder(data.Length * 3); 24 | foreach (byte b in data) 25 | { 26 | sb.Append(Convert.ToString(b, 16).PadLeft(2, '0').PadRight(3, ' ')); 27 | } 28 | return sb.ToString().ToUpper(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Utils/Device.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Management; 5 | using System.Net; 6 | using System.Security.Cryptography; 7 | using System.Text; 8 | 9 | namespace RevokeMsgPatcher.Utils 10 | { 11 | public class Device 12 | { 13 | private static string macID = null; 14 | private static string osVersion = null; 15 | 16 | private static string fingerPrint = null; 17 | 18 | #region PROP, get it only once 19 | 20 | public static string MacID 21 | { 22 | get 23 | { 24 | if (macID == null) 25 | { 26 | macID = ObtainMacID(); 27 | } 28 | return macID; 29 | } 30 | } 31 | 32 | public static string OSVersion 33 | { 34 | get 35 | { 36 | if (osVersion == null) 37 | { 38 | var name = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().Cast() 39 | select x.GetPropertyValue("Caption")).FirstOrDefault(); 40 | osVersion = name != null ? name.ToString() : "Unknown"; 41 | } 42 | return osVersion; 43 | } 44 | } 45 | #endregion 46 | 47 | /// 48 | /// Calculate GUID 49 | /// 50 | /// GUID 51 | public static string Value() 52 | { 53 | try 54 | { 55 | if (fingerPrint == null) 56 | { 57 | fingerPrint = GetHash( 58 | "MAC >> " + MacID 59 | ); 60 | } 61 | return fingerPrint; 62 | } 63 | catch 64 | { 65 | return Guid.NewGuid().ToString(); 66 | } 67 | 68 | } 69 | 70 | private static string GetHash(string s) 71 | { 72 | MD5 sec = new MD5CryptoServiceProvider(); 73 | ASCIIEncoding enc = new ASCIIEncoding(); 74 | byte[] bt = enc.GetBytes(s); 75 | return GetHexString(sec.ComputeHash(bt)); 76 | } 77 | 78 | private static string GetHexString(byte[] bt) 79 | { 80 | string s = string.Empty; 81 | for (int i = 0; i < bt.Length; i++) 82 | { 83 | byte b = bt[i]; 84 | int n, n1, n2; 85 | n = (int)b; 86 | n1 = n & 15; 87 | n2 = (n >> 4) & 15; 88 | if (n2 > 9) 89 | s += ((char)(n2 - 10 + (int)'A')).ToString(); 90 | else 91 | s += n2.ToString(); 92 | if (n1 > 9) 93 | s += ((char)(n1 - 10 + (int)'A')).ToString(); 94 | else 95 | s += n1.ToString(); 96 | if ((i + 1) != bt.Length && (i + 1) % 2 == 0) s += "-"; 97 | } 98 | return s; 99 | } 100 | 101 | 102 | #region Original Device ID Getting Code 103 | 104 | public static string ObtainMacID() 105 | { 106 | return Identifier("Win32_NetworkAdapterConfiguration", "MACAddress", "IPEnabled"); 107 | } 108 | 109 | private static string Identifier(string wmiClass, string wmiProperty, string wmiMustBeTrue) 110 | { 111 | string result = ""; 112 | try 113 | { 114 | ManagementClass mc = new ManagementClass(wmiClass); 115 | ManagementObjectCollection moc = mc.GetInstances(); 116 | foreach (ManagementObject mo in moc) 117 | { 118 | if (mo[wmiMustBeTrue].ToString() == "True") 119 | { 120 | //Only get the first one 121 | if (result == "") 122 | { 123 | result = mo[wmiProperty].ToString(); 124 | break; 125 | } 126 | } 127 | } 128 | } 129 | catch 130 | { 131 | } 132 | return result; 133 | } 134 | 135 | private static string Identifier(string wmiClass, string wmiProperty) 136 | { 137 | string result = ""; 138 | try 139 | { 140 | ManagementClass mc = new ManagementClass(wmiClass); 141 | ManagementObjectCollection moc = mc.GetInstances(); 142 | foreach (ManagementObject mo in moc) 143 | { 144 | //Only get the first one 145 | if (result == "") 146 | { 147 | result = mo[wmiProperty].ToString(); 148 | break; 149 | } 150 | } 151 | } 152 | catch 153 | { 154 | } 155 | return result; 156 | } 157 | #endregion 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Utils/FileUtil.cs: -------------------------------------------------------------------------------- 1 | using RevokeMsgPatcher.Model; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | 8 | namespace RevokeMsgPatcher.Utils 9 | { 10 | public class FileUtil 11 | { 12 | /// 13 | /// 获取文件版本 14 | /// 15 | /// 16 | /// 17 | public static string GetFileVersion(string path) 18 | { 19 | if (File.Exists(path)) 20 | { 21 | FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(path); 22 | return fileVersionInfo.FileVersion; 23 | } 24 | else 25 | { 26 | return null; 27 | } 28 | 29 | } 30 | 31 | /// 32 | /// 计算文件SHA1 33 | /// 34 | /// 文件路径 35 | /// 36 | public static string ComputeFileSHA1(string s) 37 | { 38 | File.SetAttributes(s, FileAttributes.Normal); 39 | FileStream file = new FileStream(s, FileMode.Open); 40 | SHA1 sha1 = new SHA1CryptoServiceProvider(); 41 | byte[] retval = sha1.ComputeHash(file); 42 | file.Close(); 43 | 44 | StringBuilder sc = new StringBuilder(); 45 | for (int i = 0; i < retval.Length; i++) 46 | { 47 | sc.Append(retval[i].ToString("x2")); 48 | } 49 | return sc.ToString(); 50 | } 51 | 52 | /// 53 | /// 修改文件指定位置的字节 54 | /// 55 | /// 文件对象的路径 56 | /// 偏移位置 57 | /// 修改后的值 58 | /// 59 | public static bool EditHex(string path, long position, byte after) 60 | { 61 | using (var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite)) 62 | { 63 | stream.Position = position; 64 | stream.WriteByte(after); 65 | } 66 | return true; 67 | 68 | } 69 | 70 | /// 71 | /// 修改文件多个指定位置的多个字节 72 | /// 73 | /// 文件对象的路径 74 | /// 需要修改的位置和内容 75 | public static void EditMultiHex(string path, List changes) 76 | { 77 | using (var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite)) 78 | { 79 | foreach (Change change in changes) 80 | { 81 | stream.Seek(change.Position, SeekOrigin.Begin); 82 | foreach(byte b in change.Content) 83 | { 84 | // 跳过通配符 85 | if(b == 0x3F) 86 | { 87 | stream.ReadByte(); 88 | } 89 | else 90 | { 91 | stream.WriteByte(b); 92 | } 93 | } 94 | } 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Utils/GAHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Web.Script.Serialization; 8 | using System.Windows.Forms; 9 | 10 | namespace RevokeMsgPatcher.Utils 11 | { 12 | /// 13 | /// 用于软件的 Google Analytics 实现 By huiyadanli 14 | /// 20230409 更新 GA4 的实现 15 | /// 相关文档: 16 | /// #GA指南(过时) https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide 17 | /// #GA参数(过时) https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters 18 | /// GA4教程 https://firebase.google.com/codelabs/firebase_mp 19 | /// 测试 https://ga-dev-tools.google/ga4/event-builder/ 20 | /// 21 | public class GAHelper 22 | { 23 | private static GAHelper instance = null; 24 | private static readonly object obj = new object(); 25 | 26 | public static GAHelper Instance 27 | { 28 | get 29 | { 30 | //lock (obj) 31 | //{ 32 | if (instance == null) 33 | { 34 | instance = new GAHelper(); 35 | } 36 | return instance; 37 | //} 38 | } 39 | } 40 | 41 | // 根据实际情况修改 42 | private static readonly HttpClient client = HttpUtil.Client; 43 | 44 | private const string GAUrl = "https://www.google-analytics.com/mp/collect?api_secret=urKlcc29TSy3OIkHr8yFSQ&measurement_id=G-BE6FRPZS1W"; 45 | 46 | private static readonly string cid = Device.Value(); // Anonymous Client ID. // Guid.NewGuid().ToString() 47 | 48 | 49 | public string UserAgent { get; set; } 50 | 51 | public GAHelper() 52 | { 53 | UserAgent = $"Hui Google Analytics Tracker/1.0 ({Environment.OSVersion.Platform.ToString()}; {Environment.OSVersion.Version.ToString()}; {Environment.OSVersion.VersionString})"; 54 | } 55 | 56 | public async Task RequestPageViewAsync(string page, string title = null) 57 | { 58 | try 59 | { 60 | if (page.StartsWith("/")) 61 | { 62 | page = page.Remove(0, 1); 63 | } 64 | page = page.Replace("/", "_").Replace(".", "_"); 65 | // 请求参数 66 | var values = new Dictionary 67 | { 68 | { "client_id",UserAgent}, 69 | { "user_id", cid }, 70 | { "non_personalized_ads", "false" }, 71 | { "events", new List>() 72 | { 73 | new Dictionary() 74 | { 75 | { "name",page }, 76 | { 77 | "params", 78 | new Dictionary() 79 | { 80 | { "engagement_time_msec", "1"}, 81 | } 82 | }, 83 | } 84 | } 85 | }, 86 | }; 87 | var serializer = new JavaScriptSerializer(); 88 | var json = serializer.Serialize(values); 89 | var content = new StringContent(json, Encoding.UTF8, "application/json"); 90 | var response = await client.PostAsync(GAUrl, content); 91 | // Console.WriteLine(response.ToString()); 92 | } 93 | catch (Exception ex) 94 | { 95 | 96 | Console.WriteLine("GAHelper:" + ex.Message); 97 | Console.WriteLine(ex.StackTrace); 98 | } 99 | } 100 | 101 | public void RequestPageView(string page, string title = null) 102 | { 103 | Task.Run(() => RequestPageViewAsync(page, title)); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Utils/HttpUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | 6 | namespace RevokeMsgPatcher.Utils 7 | { 8 | public class HttpUtil 9 | { 10 | public static HttpClient Client { get; } = new HttpClient(); 11 | 12 | static HttpUtil() 13 | { 14 | Client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"); 15 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; 16 | } 17 | 18 | /// 19 | /// 补丁路径 20 | /// 已经弃用的路径 21 | /// https://swordmaker-hauls-51508.netlify.com/i/revokemsg/05.json 22 | /// https://huiyadanli.github.io/i/revokemsg/05.json 23 | /// 会自动跳转,没用了 24 | /// https://gitee.com/huiyadanli/RevokeMsgPatcher/raw/master/RevokeMsgPatcher.Assistant/Data/1.2/patch.json 25 | /// 需要登录,没用了 26 | /// https://huiyadanli.coding.net/p/RevokeMsgPatcher/d/RevokeMsgPatcher/git/raw/master/RevokeMsgPatcher.Assistant/Data/1.6/patch.json 27 | /// 28 | 29 | public static string PatchVersion 30 | { 31 | get 32 | { 33 | string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); 34 | if (currentVersion.Length > 3) 35 | { 36 | return currentVersion.Substring(0, 3); 37 | } 38 | 39 | return "1.6"; 40 | } 41 | } 42 | 43 | private static readonly string[] urls = new string[] 44 | { 45 | $"https://hui-config.oss-cn-hangzhou.aliyuncs.com/{PatchVersion}/patch.json", 46 | $"https://mirror.ghproxy.com/https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/master/RevokeMsgPatcher.Assistant/Data/{PatchVersion}/patch.json", 47 | $"https://raw.gitmirror.com/huiyadanli/RevokeMsgPatcher/master/RevokeMsgPatcher.Assistant/Data/{PatchVersion}/patch.json", 48 | $"https://cdn.jsdelivr.net/gh/huiyadanli/RevokeMsgPatcher@master/RevokeMsgPatcher.Assistant/Data/{PatchVersion}/patch.json", 49 | $"https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/master/RevokeMsgPatcher.Assistant/Data/{PatchVersion}/patch.json", 50 | }; 51 | 52 | public static async Task GetPatchJsonAsync() 53 | { 54 | int i = 0; 55 | while (i < urls.Length) 56 | { 57 | try 58 | { 59 | string json = await Client.GetStringAsync(urls[i]); 60 | if (!string.IsNullOrEmpty(json) && json.Contains("LatestVersion")) 61 | { 62 | return json; 63 | } 64 | else 65 | { 66 | Console.WriteLine("第" + (i + 1) + "次请求获得的数据并非期望数据\nURL:" + urls[i]); 67 | GAHelper.Instance.RequestPageView($"/main/json/request_ex/{i + 1}/not_my_json", "第" + (i + 1) + "次请求获得的数据并非期望数据"); 68 | } 69 | } 70 | catch (Exception ex) 71 | { 72 | Console.WriteLine("第" + (i + 1) + "次请求异常:[" + ex.Message + "]\nURL:" + urls[i]); 73 | GAHelper.Instance.RequestPageView($"/main/json/request_ex/{i + 1}/{ex.Message}", "第" + (i + 1) + "次请求异常:[" + ex.Message + "]"); 74 | } 75 | i++; 76 | } 77 | return null; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/Utils/PathUtil.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace RevokeMsgPatcher.Utils 10 | { 11 | public class PathUtil 12 | { 13 | public static void DisplayAllProgram() 14 | { 15 | RegistryKey uninstallKey, programKey; 16 | uninstallKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"); 17 | string[] programKeys = uninstallKey.GetSubKeyNames(); 18 | foreach (string keyName in programKeys) 19 | { 20 | programKey = uninstallKey.OpenSubKey(keyName); 21 | Console.WriteLine(keyName + " , " + programKey.GetValue("DisplayName") + " , " + programKey.GetValue("InstallLocation")); 22 | programKey.Close(); 23 | } 24 | 25 | uninstallKey.Close(); 26 | } 27 | 28 | /// 29 | /// 从注册表中寻找安装路径 30 | /// 31 | /// 32 | /// 安装信息的注册表键名 33 | /// 微信:WeChat 34 | /// QQ:{052CFB79-9D62-42E3-8A15-DE66C2C97C3E} 35 | /// TIM:TIM 36 | /// 37 | /// 安装路径 38 | public static string FindInstallPathFromRegistry(string uninstallKeyName) 39 | { 40 | try 41 | { 42 | RegistryKey key = Registry.LocalMachine.OpenSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{uninstallKeyName}"); 43 | if (key == null) 44 | { 45 | return null; 46 | } 47 | 48 | object installLocation = key.GetValue("InstallLocation"); 49 | key.Close(); 50 | if (installLocation != null && !string.IsNullOrEmpty(installLocation.ToString())) 51 | { 52 | return installLocation.ToString(); 53 | } 54 | } 55 | catch (Exception e) 56 | { 57 | Console.WriteLine(e.Message); 58 | } 59 | 60 | return null; 61 | } 62 | 63 | 64 | public static string FindInstallPathFromRegistryWOW6432Node(string uninstallKeyName) 65 | { 66 | try 67 | { 68 | RegistryKey key = Registry.LocalMachine.OpenSubKey($@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{uninstallKeyName}"); 69 | if (key == null) 70 | { 71 | return null; 72 | } 73 | 74 | object installLocation = key.GetValue("UninstallString"); 75 | key.Close(); 76 | if (installLocation != null && !string.IsNullOrEmpty(installLocation.ToString())) 77 | { 78 | return installLocation.ToString().Replace("\"",""); 79 | } 80 | } 81 | catch (Exception e) 82 | { 83 | Console.WriteLine(e.Message); 84 | } 85 | 86 | return null; 87 | } 88 | 89 | /// 90 | /// 获取所有可能的默认安装路径 91 | /// 92 | /// Tencent\* 93 | /// 94 | public static List GetDefaultInstallPaths(string relativePath) 95 | { 96 | List list = new List(); 97 | // 从默认安装目录查找 98 | string[] drives = Environment.GetLogicalDrives(); //获取当前计算机逻辑磁盘名称列表 99 | foreach (string d in drives) 100 | { 101 | string path = Path.Combine(d, $@"Program Files (x86)\{relativePath}"); 102 | if (Directory.Exists(path)) 103 | { 104 | list.Add(path); 105 | } 106 | else 107 | { 108 | path = Path.Combine(d, $@"Program Files\{relativePath}"); 109 | if (Directory.Exists(path)) 110 | { 111 | list.Add(path); 112 | } 113 | } 114 | } 115 | 116 | return list; 117 | } 118 | 119 | /// 120 | /// 按文件夹修改时间倒序 121 | /// 122 | /// 123 | public static void SortByLastWriteTimeDesc(ref DirectoryInfo[] dirs) 124 | { 125 | Array.Sort(dirs, delegate(DirectoryInfo x, DirectoryInfo y) { return y.LastWriteTime.CompareTo(x.LastWriteTime); }); 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /RevokeMsgPatcher/Utils/ProxySpeedTester.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Net.Http; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace RevokeMsgPatcher.Utils 9 | { 10 | public class ProxySpeedTester 11 | { 12 | public static readonly string TargetUrl = "https://raw.githubusercontent.com/LiteLoaderQQNT/LiteLoaderQQNT/refs/heads/main/package.json"; 13 | 14 | private static readonly HttpClient _httpClient = new HttpClient() { Timeout = TimeSpan.FromSeconds(5) }; 15 | 16 | public static readonly List ProxyUrls = new List() 17 | { 18 | "{0}", 19 | "https://mirror.ghproxy.com/{0}", 20 | "https://hub.gitmirror.com/{0}", 21 | "https://ghproxy.cc/{0}", 22 | "https://www.ghproxy.cc/{0}", 23 | "https://ghproxy.cn/{0}", 24 | "https://ghproxy.net/{0}" 25 | }; 26 | 27 | /// 28 | /// 获得最快的代理地址 29 | /// 30 | /// 31 | /// 最快的代理地址,结果 32 | public static async Task> GetFastestProxyAsync(string target) 33 | { 34 | return await GetFastestProxyAsync(ProxyUrls, target); 35 | } 36 | 37 | public static async Task> GetFastestProxyAsync(List proxyAddresses, string target) 38 | { 39 | var tasks = new List>>(); // 修改为包含成功标志的元组 40 | var cts = new CancellationTokenSource(); 41 | 42 | foreach (var proxy in proxyAddresses) 43 | { 44 | // 如果目标地址为空且代理地址为默认地址,则跳过 45 | if (string.IsNullOrEmpty(target) && proxy == "{0}") 46 | { 47 | continue; 48 | } 49 | 50 | tasks.Add(TestProxyAsync(proxy, target, cts.Token)); 51 | } 52 | 53 | while (tasks.Count > 0) 54 | { 55 | var firstCompletedTask = await Task.WhenAny(tasks); 56 | tasks.Remove(firstCompletedTask); 57 | 58 | var result = await firstCompletedTask; 59 | if (result.Item3) // 检查是否成功 60 | { 61 | cts.Cancel(); // 取消所有其他请求 62 | return new Tuple(result.Item1, result.Item2); // 返回第一个成功的代理地址 63 | } 64 | } 65 | 66 | return new Tuple(string.Empty, string.Empty); // 如果没有成功的结果,返回空 67 | } 68 | 69 | private static async Task> TestProxyAsync(string proxyAddress, string target, CancellationToken cancellationToken) 70 | { 71 | try 72 | { 73 | // 模拟代理测试请求 74 | var response = await _httpClient.GetAsync(string.Format(proxyAddress, target), cancellationToken); 75 | response.EnsureSuccessStatusCode(); 76 | return new Tuple(proxyAddress.Replace("{0}", ""), await response.Content.ReadAsStringAsync(), true); 77 | } 78 | catch (Exception e) 79 | { 80 | return new Tuple(proxyAddress.Replace("{0}", ""), e.Message, false); 81 | } 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /RevokeMsgPatcher/Utils/VersionUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RevokeMsgPatcher.Utils 8 | { 9 | public class VersionUtil 10 | { 11 | /// 12 | /// 版本比较 13 | /// 0 相等 14 | /// 1 大与 15 | /// -1 小于 16 | /// 为空的版本最大 17 | /// 18 | /// 版本1 19 | /// 版本2 20 | /// 21 | public static int Compare(string v1, string v2) 22 | { 23 | if (string.IsNullOrEmpty(v1)) 24 | { 25 | return 1; 26 | } 27 | if (string.IsNullOrEmpty(v2)) 28 | { 29 | return -1; 30 | } 31 | 32 | string[] v1s = v1.Split('.'); 33 | string[] v2s = v2.Split('.'); 34 | int len = Math.Max(v1s.Length, v2s.Length); 35 | for (int i = 0; i < len; i++) 36 | { 37 | int i1 = 0, i2 = 0; 38 | if (i < v1s.Length) 39 | { 40 | i1 = Convert.ToInt32(v1s[i]); 41 | } 42 | if (i < v2s.Length) 43 | { 44 | i2 = Convert.ToInt32(v2s[i]); 45 | } 46 | if (i1 == i2) 47 | { 48 | continue; 49 | } 50 | else if (i1 > i2) 51 | { 52 | return 1; 53 | } 54 | else if (i1 < i2) 55 | { 56 | return -1; 57 | } 58 | } 59 | return 0; 60 | } 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | 54 | true 55 | 56 | 57 | 58 | 59 | 60 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /RevokeMsgPatcher/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huiyadanli/RevokeMsgPatcher/7f971f48c0423f0bda856f862182635df2d771a0/RevokeMsgPatcher/icon.ico -------------------------------------------------------------------------------- /RevokeMsgPatcher/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: 2 | - Visual Studio 2022 3 | 4 | configuration: Release 5 | 6 | artifacts: 7 | - path: bin\Release\ 8 | name: RevokeMsgPatcher 9 | --------------------------------------------------------------------------------