From 4ec0fb4f85b380048b8f1ea21a486be9953056d7 Mon Sep 17 00:00:00 2001 From: huiyadanli Date: Thu, 19 Sep 2019 02:46:57 +0800 Subject: [PATCH] =?UTF-8?q?[+]=20=E5=AE=8C=E6=88=90=E6=A0=B8=E5=BF=83?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=9A=84=E7=BC=96=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RevokeMsgPatcher/FormMain.cs | 94 +++++------ RevokeMsgPatcher/Model/App.cs | 4 +- RevokeMsgPatcher/Model/BinaryFile.cs | 24 --- RevokeMsgPatcher/Model/Change.cs | 10 +- RevokeMsgPatcher/Model/ModifyInfo.cs | 40 +++++ RevokeMsgPatcher/Model/TargetInfo.cs | 26 +++ RevokeMsgPatcher/Model/Version.cs | 2 +- RevokeMsgPatcher/Modifier/AppModifier.cs | 178 +++++++++++++++++--- RevokeMsgPatcher/Modifier/FileHexEditor.cs | 63 ++++++- RevokeMsgPatcher/Modifier/WechatModifier.cs | 45 ++++- RevokeMsgPatcher/Program.cs | 13 +- RevokeMsgPatcher/RevokeMsgPatcher.csproj | 4 +- RevokeMsgPatcher/Utils/FileUtil.cs | 59 +++++++ 13 files changed, 447 insertions(+), 115 deletions(-) delete mode 100644 RevokeMsgPatcher/Model/BinaryFile.cs create mode 100644 RevokeMsgPatcher/Model/ModifyInfo.cs create mode 100644 RevokeMsgPatcher/Model/TargetInfo.cs create mode 100644 RevokeMsgPatcher/Utils/FileUtil.cs diff --git a/RevokeMsgPatcher/FormMain.cs b/RevokeMsgPatcher/FormMain.cs index aba9a64..4637eac 100644 --- a/RevokeMsgPatcher/FormMain.cs +++ b/RevokeMsgPatcher/FormMain.cs @@ -1,4 +1,5 @@ -using RevokeMsgPatcher.Utils; +using RevokeMsgPatcher.Modifier; +using RevokeMsgPatcher.Utils; using System; using System.IO; using System.Net; @@ -9,18 +10,12 @@ namespace RevokeMsgPatcher { public partial class FormMain : Form { - Patcher patcher = null; + WechatModifier wechatModifier = new WechatModifier(); public FormMain() { InitializeComponent(); - patcher = new Patcher(); - txtPath.Text = Util.AutoFindInstallPath(); - if (!string.IsNullOrEmpty(txtPath.Text)) - { - patcher.IntallPath = txtPath.Text; - btnRestore.Enabled = File.Exists(patcher.BakPath); - } + // 标题加上版本号 string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); if (currentVersion.Length > 3) @@ -28,44 +23,41 @@ namespace RevokeMsgPatcher currentVersion = " v" + currentVersion.Substring(0, 3); } this.Text += currentVersion; + + // 自动获取应用安装路径 + txtPath.Text = wechatModifier.FindInstallPath(); + // 显示是否能够备份还原 + if (!string.IsNullOrEmpty(txtPath.Text)) + { + wechatModifier.InitEditors(txtPath.Text); + btnRestore.Enabled = wechatModifier.BackupExists(); + } + } private void btnPatch_Click(object sender, EventArgs e) { - if (string.IsNullOrEmpty(txtPath.Text) || !Util.IsWechatInstallPath(txtPath.Text)) + if (string.IsNullOrEmpty(txtPath.Text) || !wechatModifier.IsAllFilesExist(txtPath.Text)) { MessageBox.Show("请选择微信安装路径!"); return; } - patcher.IntallPath = txtPath.Text; - + // a.重新初始化编辑器 + wechatModifier.InitEditors(txtPath.Text); + btnPatch.Enabled = false; + // b.计算SHA1,验证文件完整性,寻找对应的补丁信息 try { - btnPatch.Enabled = false; - string version = patcher.JudgeVersion(); - if (!string.IsNullOrEmpty(version)) - { - if (version == "done") - { - MessageBox.Show("已经安装过防撤回补丁了"); - btnPatch.Enabled = true; - return; - } - - if (patcher.Patch()) - { - MessageBox.Show("成功安装防撤回补丁!原 WeChatWin.dll 文件已经备份到 " + patcher.BakPath + " ,如果有问题可以手动覆盖恢复"); - } - else - { - MessageBox.Show("防撤回补丁安装失败!"); - } - btnRestore.Enabled = File.Exists(patcher.BakPath); - } - else - { - MessageBox.Show("当前微信版本不被支持:" + Util.GetFileVersion(patcher.DllPath)); - } + wechatModifier.ValidateAndFindModifyInfo(); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + // c.打补丁 + try + { + wechatModifier.Patch(); } catch (Exception ex) { @@ -79,8 +71,8 @@ namespace RevokeMsgPatcher { if (!string.IsNullOrEmpty(txtPath.Text)) { - patcher.IntallPath = txtPath.Text; - btnRestore.Enabled = File.Exists(patcher.BakPath); + wechatModifier.InitEditors(txtPath.Text); + btnRestore.Enabled = wechatModifier.BackupExists(); } } @@ -90,7 +82,7 @@ namespace RevokeMsgPatcher dialog.Description = "请选择微信安装路径"; if (dialog.ShowDialog() == DialogResult.OK) { - if (string.IsNullOrEmpty(dialog.SelectedPath) || !Util.IsWechatInstallPath(dialog.SelectedPath)) + if (string.IsNullOrEmpty(dialog.SelectedPath) || !wechatModifier.IsAllFilesExist(dialog.SelectedPath)) { MessageBox.Show("无法找到微信关键文件,请选择正确的微信安装路径!"); } @@ -106,22 +98,14 @@ namespace RevokeMsgPatcher btnRestore.Enabled = false; try { - if (File.Exists(patcher.BakPath)) - { - File.Copy(patcher.BakPath, patcher.DllPath, true); - MessageBox.Show("还原成功"); - } - else - { - MessageBox.Show("备份文件不存在"); - } + wechatModifier.Restore(); } catch (Exception ex) { Console.WriteLine(ex.Message); MessageBox.Show(ex.Message); } - btnRestore.Enabled = File.Exists(patcher.BakPath); + btnRestore.Enabled = wechatModifier.BackupExists(); } private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) @@ -144,7 +128,7 @@ namespace RevokeMsgPatcher } else { - patcher.SetNewPatchJson(json); + //patcher.SetNewPatchJson(json); lblUpdatePachJson.Text = "获取成功"; } @@ -153,10 +137,10 @@ namespace RevokeMsgPatcher private void lblUpdatePachJson_Click(object sender, EventArgs e) { string versions = ""; - patcher.TargetFiles.ForEach(t => - { - versions += t.Version + Environment.NewLine; - }); + //patcher.TargetFiles.ForEach(t => + //{ + // versions += t.Version + Environment.NewLine; + //}); MessageBox.Show("当前所支持的微信版本:" + Environment.NewLine + versions); } diff --git a/RevokeMsgPatcher/Model/App.cs b/RevokeMsgPatcher/Model/App.cs index 29f6014..0b05e91 100644 --- a/RevokeMsgPatcher/Model/App.cs +++ b/RevokeMsgPatcher/Model/App.cs @@ -10,8 +10,8 @@ namespace RevokeMsgPatcher.Model { public string Name { get; set; } - public string[] ModifyFilePaths { get; set; } + public Dictionary FileTargetInfos { get; set; } - public List ModifyFileInfos { get; set; } + public Dictionary> FileModifyInfos { get; set; } } } diff --git a/RevokeMsgPatcher/Model/BinaryFile.cs b/RevokeMsgPatcher/Model/BinaryFile.cs deleted file mode 100644 index 6243494..0000000 --- a/RevokeMsgPatcher/Model/BinaryFile.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace RevokeMsgPatcher.Model -{ - public class BinaryFile - { - //public string Name { get {return Path.GetFileName(RelativePath); } } - - public string RelativePath { get; set; } - - public string Version { get; set; } - - public string SHA1Before { get; set; } - - public string SHA1After { get; set; } - - public List Changes { get; set; } - } -} diff --git a/RevokeMsgPatcher/Model/Change.cs b/RevokeMsgPatcher/Model/Change.cs index 48f5354..df3c99f 100644 --- a/RevokeMsgPatcher/Model/Change.cs +++ b/RevokeMsgPatcher/Model/Change.cs @@ -10,6 +10,14 @@ namespace RevokeMsgPatcher.Model { public long Position { get; set; } - public byte Content { get; set; } + public byte[] Content { get; set; } + + public Change Clone() + { + Change o = new Change(); + o.Position = Position; + o.Content = Content; + return o; + } } } diff --git a/RevokeMsgPatcher/Model/ModifyInfo.cs b/RevokeMsgPatcher/Model/ModifyInfo.cs new file mode 100644 index 0000000..c83a39b --- /dev/null +++ b/RevokeMsgPatcher/Model/ModifyInfo.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Model +{ + public class ModifyInfo + { + public string Name { get; set; } + + //public string RelativePath { get; set; } + + public string Version { get; set; } + + public string SHA1Before { get; set; } + + public string SHA1After { get; set; } + + public List Changes { get; set; } + + public ModifyInfo Clone() + { + ModifyInfo o = new ModifyInfo(); + o.Name = Name; + o.Version = Version; + o.SHA1Before = SHA1Before; + o.SHA1After = SHA1After; + List cs = new List(); + foreach(Change c in Changes) + { + cs.Add(c.Clone()); + } + o.Changes = cs; + return o; + } + } +} diff --git a/RevokeMsgPatcher/Model/TargetInfo.cs b/RevokeMsgPatcher/Model/TargetInfo.cs new file mode 100644 index 0000000..28a1dd2 --- /dev/null +++ b/RevokeMsgPatcher/Model/TargetInfo.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RevokeMsgPatcher.Model +{ + public class TargetInfo + { + public string Name { get; set; } + + public string RelativePath { get; set; } + + public string Memo { get; set; } + + public TargetInfo Clone() + { + TargetInfo o = new TargetInfo(); + o.Name = Name; + o.RelativePath = RelativePath; + o.Memo = Memo; + return o; + } + } +} diff --git a/RevokeMsgPatcher/Model/Version.cs b/RevokeMsgPatcher/Model/Version.cs index 096d33e..d14d55c 100644 --- a/RevokeMsgPatcher/Model/Version.cs +++ b/RevokeMsgPatcher/Model/Version.cs @@ -10,6 +10,6 @@ namespace RevokeMsgPatcher.Model { public string AppVersion { get; set; } - public List Files { get; set; } + public List Files { get; set; } } } diff --git a/RevokeMsgPatcher/Modifier/AppModifier.cs b/RevokeMsgPatcher/Modifier/AppModifier.cs index fa589cc..90bd629 100644 --- a/RevokeMsgPatcher/Modifier/AppModifier.cs +++ b/RevokeMsgPatcher/Modifier/AppModifier.cs @@ -8,41 +8,52 @@ using System.Threading.Tasks; namespace RevokeMsgPatcher.Modifier { + /// + /// 1. 自动获取安装目录(已验证) 或者手动填写安装目录 + /// 2. 验证安装目录 验证要修改的 dll 是否存在 + /// 3. 判断所有 dll 是否符合防撤回要求 + /// 4. 备份所有 dll // *.h.bak + /// 5. 根据每个 dll 匹配的 修改信息 循环修改 + /// public abstract class AppModifier { - private App config; + protected App config; - private List editors; + protected List editors; - // 1. 获取安装目录 - // 2. 验证安装目录 通过 BinaryFiles - // 3. 判断所有 BinaryFiles 是否符合防撤回要求 - // 4. 备份所有 BinaryFiles // *.h.bak - // 5. 对每个 BinaryFile 中的 Changes 循环修改(修改前要测试它的读写性质,是否被程序占用等) - - // 获取版本号 - // 通过SHA1判断是否可以进行16进制编辑 先判断版本号 再判断SHA1 根据不同结果返回不同的提示 - - // ?多文件的备份回退 // 暂定有一个备份文件就点亮按钮 + public string InstallPath { get; set; } public abstract string FindInstallPath(); - //public abstract bool ValidateInstallPath(); + //public abstract bool ValidateAndInitialize(string installPath); - public abstract bool GetVersion(); + /// + /// 获取版本号 + /// + /// + public abstract string GetVersion(); - public bool IsAllBinaryFilesExist(string installPath) + /// + /// 判断APP安装路径内是否都存在要修改的文件 + /// + /// APP安装路径 + /// + public bool IsAllFilesExist(string installPath) { - int success = 0; - foreach(string relativePath in config.ModifyFilePaths) + if(string.IsNullOrEmpty(installPath)) { - string filePath = Path.Combine(installPath, relativePath); - if(File.Exists(filePath)) + return false; + } + int success = 0; + foreach (TargetInfo info in config.FileTargetInfos.Values) + { + string filePath = Path.Combine(installPath, info.RelativePath); + if (File.Exists(filePath)) { success++; } } - if(success == config.ModifyFilePaths.Length) + if (success == config.FileTargetInfos.Count) { return true; } @@ -52,5 +63,132 @@ namespace RevokeMsgPatcher.Modifier } } + /// + /// a.初始化修改器 + /// + /// APP安装路径 + public void InitEditors(string installPath) + { + // 初始化文件修改器 + editors = new List(); + foreach (TargetInfo info in config.FileTargetInfos.Values) + { + FileHexEditor editor = new FileHexEditor(installPath, info); + editors.Add(editor); + } + + } + + /// + /// b.验证文件完整性,寻找对应的补丁信息 + /// + public void ValidateAndFindModifyInfo() + { + // 寻找对应文件版本与SHA1的修改信息 + foreach (FileHexEditor editor in editors) // 多种文件 + { + // 通过SHA1和文件版本判断是否可以打补丁 根据不同结果返回不同的提示 + ModifyInfo matchingSHA1Before = null, matchingSHA1After = null, matchingVersion = null; + foreach (ModifyInfo modifyInfo in config.FileModifyInfos[editor.FileName]) // 多个版本信息 + { + if (modifyInfo.Name == editor.FileName) // 保险用的无用判断 + { + if (editor.FileSHA1 == modifyInfo.SHA1After) + { + matchingSHA1After = modifyInfo; + } + else if (editor.FileSHA1 == modifyInfo.SHA1Before) + { + matchingSHA1Before = modifyInfo; + } + + if (editor.FileVersion == modifyInfo.Version) + { + matchingVersion = modifyInfo; + } + } + // 补丁前SHA1匹配上,肯定是正确的dll + if (matchingSHA1Before != null) + { + editor.FileModifyInfo = modifyInfo.Clone(); + continue; + } + // 补丁后SHA1匹配上,肯定已经打过补丁 + if (matchingSHA1After != null) + { + throw new Exception($"你已经安装过此补丁,文件路径:{editor.FilePath}"); + } + // 全部不匹配,说明不支持 + if (matchingSHA1Before == null && matchingSHA1After == null && matchingVersion == null) + { + throw new Exception($"不支持此版本:{editor.FileVersion},文件路径:{editor.FilePath}"); + } + // SHA1不匹配,版本匹配,可能dll已经被其他补丁程序修改过 + if ((matchingSHA1Before == null && matchingSHA1After == null) && matchingVersion != null) + { + throw new Exception($"程序支持此版本:{editor.FileVersion}。但是文件校验不通过,请确认是否使用过其他补丁程序。文件路径:{editor.FilePath}"); + } + } + } + } + + /// + /// c.根据补丁信息,安装补丁 + /// + /// + public bool Patch() + { + // 首先验证文件修改器是否没问题 + foreach (FileHexEditor editor in editors) + { + if (editor.FileModifyInfo == null) + { + throw new Exception("补丁安装失败,原因:文件修改器初始化失败!"); + } + } + // 再备份所有文件 + foreach (FileHexEditor editor in editors) + { + editor.Backup(); + } + // 打补丁! + foreach (FileHexEditor editor in editors) + { + bool success = editor.Patch(); + if (!success) + { + editor.Restore(); + } + } + return true; + } + + public bool BackupExists() + { + foreach (FileHexEditor editor in editors) + { + if(!File.Exists(editor.FileBakPath)) + { + return false; + } + } + return true; + } + + public bool Restore() + { + if (BackupExists()) + { + foreach (FileHexEditor editor in editors) + { + editor.Restore(); + } + return true; + } + else + { + throw new Exception("备份文件不存在,还原失败!"); + } + } } } diff --git a/RevokeMsgPatcher/Modifier/FileHexEditor.cs b/RevokeMsgPatcher/Modifier/FileHexEditor.cs index c3f4055..82cc995 100644 --- a/RevokeMsgPatcher/Modifier/FileHexEditor.cs +++ b/RevokeMsgPatcher/Modifier/FileHexEditor.cs @@ -1,5 +1,8 @@ -using System; +using RevokeMsgPatcher.Model; +using RevokeMsgPatcher.Utils; +using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -8,5 +11,63 @@ namespace RevokeMsgPatcher.Modifier { public class FileHexEditor { + public string FileName { get; set; } + + public string FilePath { get; set; } + + public string FileBakPath { get; set; } + + private string version; + public string FileVersion + { + get + { + if (version == null) + { + version = FileUtil.GetFileVersion(FilePath); + } + return version; + } + } + + public string sha1; + public string FileSHA1 + { + get + { + if (sha1 == null) + { + sha1 = FileUtil.ComputeFileSHA1(FilePath); + } + return sha1; + } + } + + public TargetInfo FileTargetInfo { get; set; } + + public ModifyInfo FileModifyInfo { get; set; } + + public FileHexEditor(string installPath, TargetInfo target) + { + FileTargetInfo = target.Clone(); + FileName = FileTargetInfo.Name; + FilePath = Path.Combine(installPath, FileTargetInfo.RelativePath); + FileBakPath = FilePath + ".h.bak"; + } + + public void Backup() + { + File.Copy(FilePath, FileBakPath, true); + } + + public bool Patch() + { + return true; + } + + public void Restore() + { + File.Copy(FileBakPath, FilePath, true); + } } } diff --git a/RevokeMsgPatcher/Modifier/WechatModifier.cs b/RevokeMsgPatcher/Modifier/WechatModifier.cs index f5ee869..eb9d278 100644 --- a/RevokeMsgPatcher/Modifier/WechatModifier.cs +++ b/RevokeMsgPatcher/Modifier/WechatModifier.cs @@ -9,14 +9,18 @@ namespace RevokeMsgPatcher.Modifier { class WechatModifier : AppModifier { + /// + /// 自动寻找获取微信安装路径 + /// + /// public override string FindInstallPath() { - string installPath = PathUtil.FindInstallPathFromRegistry("Wecaht"); - if(!IsAllBinaryFilesExist(installPath)) + string installPath = PathUtil.FindInstallPathFromRegistry("Wechat"); + if (!IsAllFilesExist(installPath)) { - foreach(string defaultPath in PathUtil.GetDefaultInstallPaths(@"Tencent\Wechat")) + foreach (string defaultPath in PathUtil.GetDefaultInstallPaths(@"Tencent\Wechat")) { - if(IsAllBinaryFilesExist(defaultPath)) + if (IsAllFilesExist(defaultPath)) { return defaultPath; } @@ -29,9 +33,38 @@ namespace RevokeMsgPatcher.Modifier return null; } - public override bool GetVersion() + /// + /// 获取整个APP的当前版本 + /// + /// + public override string GetVersion() { - throw new NotImplementedException(); + if (editors != null && editors.Count > 0) + { + foreach (FileHexEditor editor in editors) + { + if (editor.FileName == "WeChatWin.dll") + { + return editor.FileVersion; + } + } + } + return ""; } + + //public override bool ValidateAndInitialize(string installPath) + //{ + // // 判断是否是安装路径 + // if (!IsAllBinaryFilesExist(installPath)) + // { + // return false; + // } + + // // 初始化十六进制文件编辑器 + // // 并寻找与之配对的版本修改信息 + // InitEditors(installPath); + + // return true; + //} } } diff --git a/RevokeMsgPatcher/Program.cs b/RevokeMsgPatcher/Program.cs index 7c19d17..5046c55 100644 --- a/RevokeMsgPatcher/Program.cs +++ b/RevokeMsgPatcher/Program.cs @@ -14,14 +14,18 @@ namespace RevokeMsgPatcher [STAThread] static void Main() { + + +#if DEBUG + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new FormMain()); +#else try { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); -#if DEBUG - Application.Run(new FormMain()); -#else //当前用户是管理员的时候,直接启动应用程序 //如果不是管理员,则使用启动对象启动程序,以确保使用管理员身份运行 //获得当前登录的Windows用户标示 @@ -53,12 +57,13 @@ namespace RevokeMsgPatcher //退出 Application.Exit(); } -#endif + } catch (Exception ex) { MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } +#endif } diff --git a/RevokeMsgPatcher/RevokeMsgPatcher.csproj b/RevokeMsgPatcher/RevokeMsgPatcher.csproj index 01435c2..1129655 100644 --- a/RevokeMsgPatcher/RevokeMsgPatcher.csproj +++ b/RevokeMsgPatcher/RevokeMsgPatcher.csproj @@ -57,8 +57,9 @@ - + + @@ -66,6 +67,7 @@ + diff --git a/RevokeMsgPatcher/Utils/FileUtil.cs b/RevokeMsgPatcher/Utils/FileUtil.cs new file mode 100644 index 0000000..e844c59 --- /dev/null +++ b/RevokeMsgPatcher/Utils/FileUtil.cs @@ -0,0 +1,59 @@ +using System.Diagnostics; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace RevokeMsgPatcher.Utils +{ + public class FileUtil + { + /// + /// 获取文件版本 + /// + /// + /// + public static string GetFileVersion(string path) + { + FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(path); + return fileVersionInfo.FileVersion; + } + + /// + /// 计算文件SHA1 + /// + /// 文件路径 + /// + public static string ComputeFileSHA1(string s) + { + FileStream file = new FileStream(s, FileMode.Open); + SHA1 sha1 = new SHA1CryptoServiceProvider(); + byte[] retval = sha1.ComputeHash(file); + file.Close(); + + StringBuilder sc = new StringBuilder(); + for (int i = 0; i < retval.Length; i++) + { + sc.Append(retval[i].ToString("x2")); + } + return sc.ToString(); + } + + /// + /// 修改文件指定位置的字节 + /// + /// WeChatWin.dll 的路径 + /// 偏移位置 + /// 修改后的值 + /// + public static bool EditHex(string path, long position, byte after) + { + using (var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite)) + { + stream.Position = position; + stream.WriteByte(after); + } + return true; + + } + } +}