From a5583fd4f720def4f7f134cf69be4e91af665e51 Mon Sep 17 00:00:00 2001 From: kitbyte Date: Sun, 30 Nov 2025 19:01:19 +0200 Subject: [PATCH] bump version to 1.0.5.0, refactor patching logic, remove unused code --- WeModPatcher/Constants.cs | 6 +- .../Core/{StaticPatcher.cs => Patcher.cs} | 74 +---- WeModPatcher/Core/RuntimePatcher.cs | 144 ---------- WeModPatcher/Models/PatchConfig.cs | 87 ------ WeModPatcher/Models/Signature.cs | 35 ++- WeModPatcher/Program.cs | 31 +-- WeModPatcher/Properties/AssemblyInfo.cs | 4 +- WeModPatcher/Utils/MemoryUtils.cs | 178 ------------ WeModPatcher/Utils/Win32/Imports.cs | 260 ------------------ WeModPatcher/View/MainWindow/MainWindowVm.cs | 94 +++---- .../View/Popups/PatchVectorsPopup.xaml | 141 ++-------- .../View/Popups/PatchVectorsPopup.xaml.cs | 45 +-- WeModPatcher/View/Popups/UpdatePopup.xaml | 20 ++ WeModPatcher/View/Popups/UpdatePopup.xaml.cs | 22 ++ WeModPatcher/WeModPatcher.csproj | 26 +- 15 files changed, 190 insertions(+), 977 deletions(-) rename WeModPatcher/Core/{StaticPatcher.cs => Patcher.cs} (73%) delete mode 100644 WeModPatcher/Core/RuntimePatcher.cs delete mode 100644 WeModPatcher/Utils/MemoryUtils.cs delete mode 100644 WeModPatcher/Utils/Win32/Imports.cs create mode 100644 WeModPatcher/View/Popups/UpdatePopup.xaml create mode 100644 WeModPatcher/View/Popups/UpdatePopup.xaml.cs diff --git a/WeModPatcher/Constants.cs b/WeModPatcher/Constants.cs index 45d1553..4fdafb4 100644 --- a/WeModPatcher/Constants.cs +++ b/WeModPatcher/Constants.cs @@ -12,16 +12,18 @@ namespace WeModPatcher public static readonly string RepositoryUrl = $"https://github.com/{Owner}/{RepoName}"; public static readonly Version Version; public static readonly string[] WeModBrandNames = { "Wand", "WeMod" }; + + public const string ProxyDllResouceName = "proxydll"; // cmp dword ptr [rdx], 0 // jnz loc_XXXXXXXX // mov rsi, rdx - public static Signature ExePatchSignature = new Signature( + /*public static Signature ExePatchSignature = new Signature( "83 3A 00 0F ?? ?? 01 00 00 48 89 D6 48 B8", 4, new byte[]{ 0x84, 0x17 }, new byte[]{ 0x85, 0x22 } - ); + );*/ /*// ... // test eax, eax (0x85 for r/m16/32/64) diff --git a/WeModPatcher/Core/StaticPatcher.cs b/WeModPatcher/Core/Patcher.cs similarity index 73% rename from WeModPatcher/Core/StaticPatcher.cs rename to WeModPatcher/Core/Patcher.cs index 40b862d..3e1d374 100644 --- a/WeModPatcher/Core/StaticPatcher.cs +++ b/WeModPatcher/Core/Patcher.cs @@ -14,7 +14,7 @@ using Application = System.Windows.Application; namespace WeModPatcher.Core { - public class StaticPatcher + public class Patcher { private class PatchEntry { @@ -54,7 +54,7 @@ namespace WeModPatcher.Core private readonly string _unpackedPath; private int _sumOfPatches = 0; - public StaticPatcher(WeModConfig weModConfig, Action logger, PatchConfig config) + public Patcher(WeModConfig weModConfig, Action logger, PatchConfig config) { _weModConfig = weModConfig; _logger = logger; @@ -138,59 +138,22 @@ namespace WeModPatcher.Core } } - private void PatchPe() + private void AttachProxyDll() { - _logger("[PATCHER] Patching PE...", ELogType.Info); - var patchResult = MemoryUtils.PatchFile( - _weModConfig.ExecutablePath, - Constants.ExePatchSignature, - Constants.ExePatchSignature.PatchBytes - ); - if(patchResult == -1) + var assembly = Assembly.GetExecutingAssembly(); + var dll = assembly.GetManifestResourceStream(Constants.ProxyDllResouceName); + if (dll == null) { - _logger("[PATCHER] Failed to patch PE", ELogType.Error); - return; + throw new Exception("[PATCHER] Proxy DLL resource not found"); } - _logger(patchResult == 0 ? "[PATCHER] PE already patched!" : "[PATCHER] PE patched successfully!", ELogType.Success); + var destPath = Path.Combine(_weModConfig.RootDirectory, "version.dll"); + using (var fileStream = File.Create(destPath)) + { + dll.CopyTo(fileStream); + } + _logger("[PATCHER] Proxy DLL attached", ELogType.Info); } - - private void CreateShortcut() - { - // invoke file dialog save file - var fileDialog = new SaveFileDialog() - { - CheckPathExists = true, - AddExtension = true, - SupportMultiDottedExtensions = false, - FileName = _weModConfig.BrandName, - }; - - if(fileDialog.ShowDialog() != DialogResult.OK) - { - return; - } - - _config.Path = _weModConfig.RootDirectory; - var json = JsonConvert.SerializeObject(_config, new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore, - Formatting = Formatting.None - }); - - Utils.Win32.Shortcut.CreateShortcut( - fileName: fileDialog.FileName + ".lnk", - targetPath: Assembly.GetExecutingAssembly().Location, - arguments: Extensions.Base64Encode(json), - workingDirectory: Common.GetCurrentDir(), - description: null, - iconPath: _weModConfig.ExecutablePath - ); - - _logger("[PATCHER] The shortcut has been created, now you should only run WeMod through this shortcut", ELogType.Success); - } - public void Patch() { Common.TryKillProcess(_weModConfig.BrandName); @@ -232,15 +195,8 @@ namespace WeModPatcher.Core { throw new Exception($"[PATCHER] Failed to pack app.asar: {e.Message}"); } - - if (_config.PatchMethod == EPatchProcessMethod.Static) - { - PatchPe(); - } - else if(_config.PatchMethod == EPatchProcessMethod.Runtime) - { - Application.Current.Dispatcher.Invoke(CreateShortcut); - } + + AttachProxyDll(); _logger("[PATCHER] Done!", ELogType.Success); } diff --git a/WeModPatcher/Core/RuntimePatcher.cs b/WeModPatcher/Core/RuntimePatcher.cs deleted file mode 100644 index 29067f5..0000000 --- a/WeModPatcher/Core/RuntimePatcher.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using WeModPatcher.Models; -using WeModPatcher.Utils; -using WeModPatcher.Utils.Win32; -using WeModPatcher.View.MainWindow; - -namespace WeModPatcher.Core -{ - - public class RuntimePatcher - { - private readonly WeModConfig _config; - - public RuntimePatcher(WeModConfig config) - { - _config = config; - } - - - public void StartProcess() - { - if(string.IsNullOrEmpty(_config?.ExecutablePath)) - { - throw new Exception("Path is not specified"); - } - - Common.TryKillProcess(_config.BrandName); - var startupInfo = new Imports.StartupInfo { cb = Marshal.SizeOf(typeof(Imports.StartupInfo)) }; - if(!Imports.CreateProcessA(_config.ExecutablePath, - null, - IntPtr.Zero, - IntPtr.Zero, - false, Imports.DEBUG_PROCESS, IntPtr.Zero, - null, ref startupInfo, out var processInfo)) - { - throw new Exception("Failed to create process, error code: " + Marshal.GetLastWin32Error()); - } - - var debugEvent = new Imports.DEBUG_EVENT(); - var processIds = new Dictionary(); - while (Imports.WaitForDebugEvent(ref debugEvent, uint.MaxValue)) - { - uint continueStatus = Imports.DBG_CONTINUE; - var code = debugEvent.dwDebugEventCode; - // Console.WriteLine("Debug event code: " + code); - if (code == Imports.CREATE_PROCESS_DEBUG_EVENT) - { - // Console.WriteLine("Spawning process: " + debugEvent.dwProcessId); - processIds.Add(debugEvent.dwProcessId, false); - } - else if (code == Imports.EXIT_PROCESS_DEBUG_EVENT) - { - processIds.Remove(debugEvent.dwProcessId); - - if(processIds.Count == 0) - { - break; - } - } - else if (code == Imports.EXCEPTION_DEBUG_EVENT) - { - // pass the exception to the process - continueStatus = Imports.DBG_EXCEPTION_NOT_HANDLED; - - var exceptionInfo = Imports.MapUnmanagedStructure(debugEvent.Union); - // Console.WriteLine("Exception code: " + exceptionInfo.ExceptionRecord.ExceptionCode); - - if (exceptionInfo.ExceptionRecord.ExceptionCode == Imports.EXCEPTION_BREAKPOINT && - processIds.TryGetValue(debugEvent.dwProcessId, out var wasPatched) && !wasPatched) - { - var process = Process.GetProcessById((int)debugEvent.dwProcessId); - // Console.WriteLine("Scanning process: " + process.ProcessName + " " + process.Id); - var address = MemoryUtils.ScanVirtualMemory( - process.Handle, - process.Modules[0].BaseAddress, - process.Modules[0].ModuleMemorySize, - Constants.ExePatchSignature.Sequence, Constants.ExePatchSignature.Mask - ); - - if (address != IntPtr.Zero) - { - processIds[debugEvent.dwProcessId] = MemoryUtils.SafeWriteVirtualMemory( - process.Handle, - address + Constants.ExePatchSignature.Offset, - Constants.ExePatchSignature.PatchBytes - ); - - /*byte[] patchedBytes = new byte[32]; - if (Imports.ReadProcessMemory(process.Handle, address, patchedBytes, patchedBytes.Length, out int bytesRead)) - { - Console.WriteLine("Bytes after patching: "); - for (int i = 0; i < bytesRead; i++) - { - Console.Write($"{patchedBytes[i]:X2} "); - } - Console.WriteLine(); - }*/ - } - } - } - - Imports.ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, continueStatus); - } - - foreach (var entry in processIds) - { - Imports.DebugActiveProcessStop(entry.Key); - } - - Imports.CloseHandle(processInfo.hProcess); - } - - public static void Patch(PatchConfig config, Action logger) - { - if (config.AppProps == null) - { - throw new Exception("Path is not specified"); - } - - var parent = Directory.GetParent(config.AppProps.RootDirectory)?.FullName ?? config.AppProps.RootDirectory; - var latestWeModConfig = config.AutoApplyPatches ? Extensions.FindLatestWeMod(parent) ?? config.AppProps : config.AppProps; - - if (Extensions.CheckWeModPath(latestWeModConfig.RootDirectory) == null) - { - throw new Exception("Invalid WeMod path"); - } - - if(!File.Exists(Path.Combine(latestWeModConfig.RootDirectory, "resources", "app.asar.backup"))) - { - config.PatchMethod = EPatchProcessMethod.None; - new StaticPatcher(latestWeModConfig, logger, config).Patch(); - } - - new RuntimePatcher(latestWeModConfig) - .StartProcess(); - } - - - } -} \ No newline at end of file diff --git a/WeModPatcher/Models/PatchConfig.cs b/WeModPatcher/Models/PatchConfig.cs index dff4d8f..3a8c699 100644 --- a/WeModPatcher/Models/PatchConfig.cs +++ b/WeModPatcher/Models/PatchConfig.cs @@ -13,28 +13,12 @@ namespace WeModPatcher.Models DisableUpdates = 2, DisableTelemetry = 4 } - - public enum EPatchProcessMethod - { - None = 0, - Runtime = 1, - Static = 2 - } - - /*public sealed class PatchConfigOld - { - public HashSet PatchTypes { get; set; } - public EPatchProcessMethod PatchMethod { get; set; } - public string Path { get; set; } - }*/ public sealed class PatchConfig { private string _path; public HashSet PatchTypes { get; set; } - public EPatchProcessMethod PatchMethod { get; set; } - [JsonProperty("u")] public bool AutoApplyPatches { get; set; } [JsonIgnore] @@ -49,77 +33,6 @@ namespace WeModPatcher.Models AppProps = Extensions.CheckWeModPath(_path) ?? throw new Exception("Invalid WeMod path"); } } - - /*public static void PushConfig(PatchConfig config) - { - var hash = GetConfigHash(config); - var registry = _getRegistry(); - registry[hash] = config; - _stashRegistry(registry); - } - - public static void ActualizeRegistry() - { - var registry = _getRegistry(); - foreach (var entry in registry) - { - if(Extensions.CheckWeModPath(entry.Value.AppProps.RootDirectory) == null) - { - registry.Remove(entry.Key); - break; - } - } - _stashRegistry(registry); - } - - public static PatchConfig GetConfig(string hash) - { - var registry = _getRegistry(); - return registry.TryGetValue(hash, out var config) ? config : null; - } - - public static string GetConfigHash(PatchConfig config) - { - return Common.ComputeSha256Hash(config.AppProps.ExecutablePath); - } - - private static Dictionary _getRegistry() - { - var currentDir = Common.GetCurrentDir(); - var registryPath = Path.Combine(currentDir, Constants.PatchRegistryName); - try - { - return JsonConvert.DeserializeObject>( - File.ReadAllText(registryPath) - ); - } - catch - { - // ignored - } - - return new Dictionary(); - } - - private static void _stashRegistry(Dictionary registry) - { - - var currentDir = Common.GetCurrentDir(); - var registryPath = Path.Combine(currentDir, Constants.PatchRegistryName); - - if(registry.Count == 0) - { - if(File.Exists(registryPath)) - { - File.Delete(registryPath); - } - return; - } - - - var json = JsonConvert.SerializeObject(registry, Formatting.Indented); - File.WriteAllText(registryPath, json); - }*/ } } \ No newline at end of file diff --git a/WeModPatcher/Models/Signature.cs b/WeModPatcher/Models/Signature.cs index f22655b..4a8a715 100644 --- a/WeModPatcher/Models/Signature.cs +++ b/WeModPatcher/Models/Signature.cs @@ -1,4 +1,4 @@ -using WeModPatcher.Utils; +using System; namespace WeModPatcher.Models { @@ -9,17 +9,40 @@ namespace WeModPatcher.Models public readonly byte[] Sequence; public readonly byte[] Mask; public readonly int Offset; - + public int Length => Sequence.Length; - + public static implicit operator byte[](Signature signature) => signature.Sequence; - + public Signature(string signature, int offset, byte[] patchBytes, byte[] originalBytes) { - MemoryUtils.ParseSignature(signature, out Sequence, out Mask); + Parse(signature, out Sequence, out Mask); PatchBytes = patchBytes; OriginalBytes = originalBytes; Offset = offset; } + + private static void Parse(string signatureStr, out byte[] pattern, out byte[] mask) + { + var parts = signatureStr.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + var length = parts.Length; + + pattern = new byte[length]; + mask = new byte[length]; + + for (var i = 0; i < length; i++) + { + if (parts[i] == "??" || parts[i] == "?") + { + pattern[i] = 0; + // wildcard byte + mask[i] = 0; + continue; + } + + pattern[i] = Convert.ToByte(parts[i], 16); + mask[i] = 1; + } + } } -} \ No newline at end of file +} diff --git a/WeModPatcher/Program.cs b/WeModPatcher/Program.cs index 40277d9..4a442fe 100644 --- a/WeModPatcher/Program.cs +++ b/WeModPatcher/Program.cs @@ -1,15 +1,7 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.IO; -using System.Reflection; using System.Threading.Tasks; using System.Windows.Forms; -using Newtonsoft.Json; -using WeModPatcher.Core; -using WeModPatcher.Models; -using WeModPatcher.Utils; using WeModPatcher.View.MainWindow; namespace WeModPatcher @@ -25,28 +17,7 @@ namespace WeModPatcher List logEntries = new List(); if (args.Length > 0) { - try - { - var patchConfig = JsonConvert.DeserializeObject(Extensions.Base64Decode(args[0])); - RuntimePatcher.Patch(patchConfig, (message, type) => - { - logEntries.Add(new LogEntry - { - Message = message, - LogType = type - }); - }); - Environment.Exit(0); - } - catch (Exception e) - { - logEntries.Add(new LogEntry - { - Message = "Runtime patching failed: " + e.Message, - LogType = ELogType.Error - }); - } - + // TODO: Command line arguments handling } var application = new App(); diff --git a/WeModPatcher/Properties/AssemblyInfo.cs b/WeModPatcher/Properties/AssemblyInfo.cs index eef507e..103fff5 100644 --- a/WeModPatcher/Properties/AssemblyInfo.cs +++ b/WeModPatcher/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ using System.Windows; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.4.0")] -[assembly: AssemblyFileVersion("1.0.4.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.0.5.0")] +[assembly: AssemblyFileVersion("1.0.5.0")] \ No newline at end of file diff --git a/WeModPatcher/Utils/MemoryUtils.cs b/WeModPatcher/Utils/MemoryUtils.cs deleted file mode 100644 index 95032c1..0000000 --- a/WeModPatcher/Utils/MemoryUtils.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using WeModPatcher.Models; -using WeModPatcher.Utils.Win32; - -namespace WeModPatcher.Utils -{ -public class MemoryUtils -{ - public static int ScanMemoryBlock(byte[] buffer, int bufferLength, byte[] pattern, byte[] mask) - { - var patternLength = pattern.Length; - if (bufferLength < patternLength) - { - return -1; - } - - // Make a length of length outside the first cycle for optimization - var searchEnd = bufferLength - patternLength; - - // first pass - use the first non-empty byte of the mask for a quick check - var firstValidIndex = -1; - for (var i = 0; i < patternLength; i++) - { - if (mask[i] == 1) - { - firstValidIndex = i; - break; - } - } - - if (firstValidIndex == -1) - { - return 0; - } - - var firstByte = pattern[firstValidIndex]; - - for (var i = 0; i <= searchEnd; i++) - { - // quick check by the first byte before full comparison - if (buffer[i + firstValidIndex] != firstByte) - continue; - - var found = true; - - // check only those positions where mask = 1 - for (var j = 0; j < patternLength; j++) - { - if (mask[j] == 0 || buffer[i + j] == pattern[j]) - { - continue; - } - - found = false; - break; - } - - if (found) - { - return i; - } - } - - return -1; - } - - public static void ParseSignature(string signatureStr, out byte[] pattern, out byte[] mask) - { - var parts = signatureStr.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - var length = parts.Length; - - pattern = new byte[length]; - mask = new byte[length]; - - for (var i = 0; i < length; i++) - { - if (parts[i] == "??" || parts[i] == "?") - { - pattern[i] = 0; - // wildcard byte - mask[i] = 0; - continue; - } - - pattern[i] = Convert.ToByte(parts[i], 16); - mask[i] = 1; - } - } - - public static bool SafeWriteVirtualMemory(IntPtr hProcess, IntPtr address, byte[] bytes) - { - if (!Imports.VirtualProtectEx(hProcess, address, (IntPtr)1, 0x40, out uint oldProtect)) - { - return false; - } - - bool result = Imports.WriteProcessMemory(hProcess, address, bytes, bytes.Length, out _); - - // Restore the previous access rights - Imports.VirtualProtectEx(hProcess, address, (IntPtr)1, oldProtect, out _); - return result; - } - - public static IntPtr ScanVirtualMemory(IntPtr hProcess, IntPtr startAddress, int searchSize, byte[] signature, byte[] mask) - { - const int BUFFER_SIZE = 4096; - byte[] buffer = new byte[BUFFER_SIZE]; - - // We can't copy all the crap of the process into a byte array at once. Don't try this - for (long currentAddress = startAddress.ToInt64(); - currentAddress < startAddress.ToInt64() + searchSize; - currentAddress += BUFFER_SIZE - signature.Length) - { - if (!Imports.ReadProcessMemory(hProcess, new IntPtr(currentAddress), buffer, BUFFER_SIZE, out int bytesRead) || bytesRead == 0) - { - // Read error or end of memory, throw mb? - continue; - } - - var i = ScanMemoryBlock(buffer, bytesRead, signature, mask); - if (i != -1) - { - return new IntPtr(currentAddress + i); - } - } - - return IntPtr.Zero; - } - - public static int PatchFile(string filePath, Signature signature, byte[] patchBytes) - { - const int bufferSize = 8192; - var buffer = new byte[bufferSize + signature.Length - 1]; - - using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) - { - int filePosition = 0; - while (true) - { - int bytesRead = fileStream.Read(buffer, 0, bufferSize); - if (bytesRead == 0) break; - - int matchIndex = ScanMemoryBlock(buffer, bytesRead, signature, signature.Mask); - if (matchIndex != -1) - { - int functionStartPosition = filePosition + matchIndex; - - var checkBuffer = new byte[patchBytes.Length]; - fileStream.Seek(functionStartPosition + signature.Offset, SeekOrigin.Begin); - fileStream.Read(checkBuffer, 0, patchBytes.Length); - - if (checkBuffer.SequenceEqual(patchBytes)) - { - return 0; // Memory already patched - } - - // Go to patch position - fileStream.Seek(functionStartPosition + signature.Offset, SeekOrigin.Begin); - fileStream.Write(patchBytes, 0, patchBytes.Length); - - return functionStartPosition; // Return the address of the function start by signature - } - - filePosition += bytesRead; - Array.Copy(buffer, bufferSize, buffer, 0, signature.Length - 1); - } - } - - return -1; - } -} -} \ No newline at end of file diff --git a/WeModPatcher/Utils/Win32/Imports.cs b/WeModPatcher/Utils/Win32/Imports.cs deleted file mode 100644 index 94b89c1..0000000 --- a/WeModPatcher/Utils/Win32/Imports.cs +++ /dev/null @@ -1,260 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Text; - -namespace WeModPatcher.Utils.Win32 -{ - public static class Imports - { - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool ReadProcessMemory( - IntPtr hProcess, - IntPtr lpBaseAddress, - [Out] byte[] lpBuffer, - int dwSize, - out int lpNumberOfBytesRead); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool WriteProcessMemory( - IntPtr hProcess, - IntPtr lpBaseAddress, - byte[] lpBuffer, - int nSize, - out IntPtr lpNumberOfBytesWritten); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool VirtualProtectEx( - IntPtr hProcess, - IntPtr lpAddress, - IntPtr dwSize, - uint flNewProtect, - out uint lpflOldProtect); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool WaitForDebugEvent(ref DEBUG_EVENT lpDebugEvent, uint dwMilliseconds); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool CloseHandle(IntPtr hObject); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern uint ResumeThread(IntPtr hThread); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool DebugActiveProcessStop(uint dwProcessId); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool DebugActiveProcess(int dwProcessId); - - [DllImport("psapi.dll", SetLastError = true)] - public static extern bool EnumProcessModules( - IntPtr hProcess, - IntPtr lphModule, - uint cb, - out uint lpcbNeeded); - - [DllImport("psapi.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern int GetModuleFileNameEx( - IntPtr hProcess, - IntPtr hModule, - StringBuilder lpFilename, - int nSize); - - [DllImport("psapi.dll", SetLastError = true)] - public static extern bool GetModuleInformation(IntPtr hProcess, IntPtr hModule, out MODULEINFO lpmodinfo, uint cb); - - - [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] - public static extern bool CreateProcessA - ( - String lpApplicationName, - String lpCommandLine, - IntPtr lpProcessAttributes, - IntPtr lpThreadAttributes, - Boolean bInheritHandles, - uint dwCreationFlags, - IntPtr lpEnvironment, - String lpCurrentDirectory, - [In] ref StartupInfo lpStartupInfo, - out ProcessInformation lpProcessInformation - ); - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool ContinueDebugEvent(uint dwProcessId, uint dwThreadId, uint dwContinueStatus); - - [StructLayout(LayoutKind.Sequential)] - public struct StartupInfo - { - public Int32 cb ; - public IntPtr lpReserved ; - public IntPtr lpDesktop ; - public IntPtr lpTitle ; - public Int32 dwX ; - public Int32 dwY ; - public Int32 dwXSize ; - public Int32 dwYSize ; - public Int32 dwXCountChars ; - public Int32 dwYCountChars ; - public Int32 dwFillAttribute ; - public Int32 dwFlags ; - public Int16 wShowWindow ; - public Int16 cbReserved2 ; - public IntPtr lpReserved2 ; - public IntPtr hStdInput ; - public IntPtr hStdOutput ; - public IntPtr hStdError ; - } - - [StructLayout(LayoutKind.Sequential)] - public struct ProcessInformation - { - public IntPtr hProcess; - public IntPtr hThread; - public Int32 dwProcessId; - public Int32 dwThreadId; - } - - #region Debug event structures - - [StructLayout(LayoutKind.Explicit)] - public struct DEBUG_EVENT - { - [FieldOffset(0)] - public uint dwDebugEventCode; - [FieldOffset(4)] - public uint dwProcessId; - [FieldOffset(8)] - public uint dwThreadId; - [FieldOffset(16)] - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 160)] - public byte[] Union; - } - - [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct EXCEPTION_DEBUG_INFO - { - public EXCEPTION_RECORD ExceptionRecord; - public uint dwFirstChance; - } - - [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct EXCEPTION_RECORD - { - public uint ExceptionCode; - public uint ExceptionFlags; - public IntPtr pExceptionRecord; - public IntPtr ExceptionAddress; - public uint NumberParameters; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 15)] - public IntPtr[] ExceptionInformation; - } - - [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct CREATE_THREAD_DEBUG_INFO - { - public IntPtr hThread; - public IntPtr lpThreadLocalBase; - public IntPtr lpStartAddress; - } - - [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct CREATE_PROCESS_DEBUG_INFO - { - public IntPtr hFile; - public IntPtr hProcess; - public IntPtr hThread; - public IntPtr lpBaseOfImage; - public uint dwDebugInfoFileOffset; - public uint nDebugInfoSize; - public IntPtr lpThreadLocalBase; - public IntPtr lpStartAddress; - public IntPtr lpImageName; - public ushort fUnicode; - } - - [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct MODULEINFO - { - public IntPtr lpBaseOfDll; - public uint SizeOfImage; - public IntPtr EntryPoint; - } - - [StructLayout(LayoutKind.Sequential, Pack = 8)] - public struct EXIT_THREAD_DEBUG_INFO - { - public uint dwExitCode; - } - - [StructLayout(LayoutKind.Sequential)] - public struct EXIT_PROCESS_DEBUG_INFO - { - public uint dwExitCode; - } - - [StructLayout(LayoutKind.Sequential)] - public struct LOAD_DLL_DEBUG_INFO - { - public IntPtr hFile; - public IntPtr lpBaseOfDll; - public uint dwDebugInfoFileOffset; - public uint nDebugInfoSize; - public IntPtr lpImageName; - public ushort fUnicode; - } - - [StructLayout(LayoutKind.Sequential)] - public struct UNLOAD_DLL_DEBUG_INFO - { - public IntPtr lpBaseOfDll; - } - - [StructLayout(LayoutKind.Sequential)] - public struct OUTPUT_DEBUG_STRING_INFO - { - public IntPtr lpDebugStringData; - public ushort fUnicode; - public ushort nDebugStringLength; - } - - [StructLayout(LayoutKind.Sequential)] - public struct RIP_INFO - { - public uint dwError; - public uint dwType; - } - - - public static T MapUnmanagedStructure(byte[] debugInfo) - { - GCHandle handle = GCHandle.Alloc(debugInfo, GCHandleType.Pinned); - try - { - return Marshal.PtrToStructure(handle.AddrOfPinnedObject()); - } - finally - { - handle.Free(); - } - } - - #endregion - - // Determining constants for debugging - public const uint INFINITE = 0xFFFFFFFF; - public const uint DEBUG_PROCESS = 0x00000001; - public const uint DBG_CONTINUE = 0x00010002; - public const uint CREATE_PROCESS_DEBUG_EVENT = 3; - public const uint EXIT_PROCESS_DEBUG_EVENT = 5; - public const uint EXCEPTION_DEBUG_EVENT = 1; - public const uint LOAD_DLL_DEBUG_EVENT = 6; - public const uint OUTPUT_DEBUG_STRING_EVENT = 8; - public const uint EXCEPTION_BREAKPOINT = 0x80000003; - public const uint DBG_EXCEPTION_NOT_HANDLED = 0x80010001; - - // Constants for VirtualProtectex - public const uint PAGE_EXECUTE_READWRITE = 0x40; - } -} \ No newline at end of file diff --git a/WeModPatcher/View/MainWindow/MainWindowVm.cs b/WeModPatcher/View/MainWindow/MainWindowVm.cs index cfe55f9..1abaa84 100644 --- a/WeModPatcher/View/MainWindow/MainWindowVm.cs +++ b/WeModPatcher/View/MainWindow/MainWindowVm.cs @@ -1,6 +1,7 @@ using System; using System.Collections.ObjectModel; using System.IO; +using System.Reflection; using System.Threading.Tasks; using System.Windows.Forms; using WeModPatcher.Core; @@ -12,15 +13,14 @@ using Application = System.Windows.Application; namespace WeModPatcher.View.MainWindow { - public class MainWindowVm : ObservableObject { private readonly MainWindow _view; public ObservableCollection LogList { get; set; } = new ObservableCollection(); private static Updater _updater = new Updater(); - + private WeModConfig _weModConfig; - + public WeModConfig WeModInfo { get => _weModConfig; @@ -28,15 +28,17 @@ namespace WeModPatcher.View.MainWindow { SetProperty(ref _weModConfig, value); if (value == null) return; - + Log($"WeMod directory found at '{_weModConfig}' ({_weModConfig.ExecutableName})", ELogType.Success); if (File.Exists(Path.Combine(_weModConfig.RootDirectory, "resources", "app.asar.backup"))) { - Log("WeMod already patched. If you want to patch again, please restore the backup first.", ELogType.Warn); + Log("WeMod already patched. If you want to patch again, please restore the backup first.", + ELogType.Warn); IsPatchEnabled = false; AlreadyPatched = true; return; } + Log("Ready for patching.", ELogType.Info); IsPatchEnabled = true; } @@ -49,26 +51,28 @@ namespace WeModPatcher.View.MainWindow get => _isPatchEnabled; set => SetProperty(ref _isPatchEnabled, value); } - + private bool _alreadyPatched; + public bool AlreadyPatched { get => _alreadyPatched; set => SetProperty(ref _alreadyPatched, value); } - + private bool _isUpdateAvailable; + public bool IsUpdateAvailable { get => _isUpdateAvailable; set => SetProperty(ref _isUpdateAvailable, value); } - + public RelayCommand SetFolderPathCommand { get; } public RelayCommand ApplyPatchCommand { get; } public RelayCommand RestoreBackupCommand { get; } - public AsyncRelayCommand UpdateCommand { get; } - + public RelayCommand UpdateCommand { get; } + private void OnFolderPathSelection(object obj) { using (var dialog = new FolderBrowserDialog()) @@ -80,7 +84,7 @@ namespace WeModPatcher.View.MainWindow if (dialog.ShowDialog() != DialogResult.OK) return; string selectedPath = dialog.SelectedPath; string fileName = Path.GetFileName(selectedPath); - + var info = Extensions.CheckWeModPath(selectedPath); if (info != null) @@ -99,37 +103,25 @@ namespace WeModPatcher.View.MainWindow private void OnBackupRestoring(object param) { - var backupPath = Path.Combine(WeModInfo.RootDirectory, "resources", "app.asar.backup"); if (!File.Exists(backupPath)) { Log("Backup not found. Please dont delete it manually", ELogType.Error); return; } - + try { + // Try to lock the file to see if it's in use using (File.Open(backupPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { } - - // This shit doesn't look at the hash and verify() always returns true - //using X509Certificate2 cert = new X509Certificate2(X509Certificate.CreateFromSignedFile(filePath)); - var restoreExeResult = MemoryUtils.PatchFile( - WeModInfo.ExecutablePath, - Constants.ExePatchSignature, - Constants.ExePatchSignature.OriginalBytes - ); - if (restoreExeResult == -1) + var proxyDllPath = Path.Combine(WeModInfo.RootDirectory, "version.dll"); + + if(File.Exists(proxyDllPath)) { - Log("Failed to restore the backup. Please close the WeMod and try again.", ELogType.Error); - } - else - { - Log(restoreExeResult == 0 ? - "Signature exe is original, does not require restoration" - : $"{WeModInfo.ExecutableName} restored successfully", ELogType.Success); + File.Delete(proxyDllPath); } } catch @@ -137,7 +129,7 @@ namespace WeModPatcher.View.MainWindow Log("Backup file is locked. Please close the WeMod and try again.", ELogType.Error); return; } - + File.Copy(backupPath, Path.Combine(WeModInfo.RootDirectory, "resources", "app.asar"), true); File.Delete(backupPath); Log("Backup restored successfully.", ELogType.Success); @@ -152,8 +144,8 @@ namespace WeModPatcher.View.MainWindow Log("Can't be done. Please specify the directory first.", ELogType.Warn); return; } - - MainWindow.Instance.OpenPopup(new PatchVectorsPopup( async config => + + MainWindow.Instance.OpenPopup(new PatchVectorsPopup(async config => { MainWindow.Instance.ClosePopup(); IsPatchEnabled = false; @@ -161,7 +153,7 @@ namespace WeModPatcher.View.MainWindow { try { - new StaticPatcher(WeModInfo, Log, config).Patch(); + new Patcher(WeModInfo, Log, config).Patch(); AlreadyPatched = true; } catch (Exception e) @@ -170,7 +162,6 @@ namespace WeModPatcher.View.MainWindow IsPatchEnabled = true; } }); - }), "What are we gonna patch?"); } @@ -190,24 +181,27 @@ namespace WeModPatcher.View.MainWindow }); } - private async Task OnUpdate(object param) + private void OnUpdate(object param) { - await Task.Run(async () => + MainWindow.Instance.OpenPopup(new UpdatePopup(() => { - try + Task.Run(async () => { - await _updater.Update(); - } - catch (Exception e) - { - Log($"Failed to update: {e.Message}", ELogType.Error); - return; - } - - Log("WeModPatcher updated successfully. Restarting...", ELogType.Success); - }); + try + { + await _updater.Update(); + } + catch (Exception e) + { + Log($"Failed to update: {e.Message}", ELogType.Error); + return; + } + + Log("WeModPatcher updated successfully. Restarting...", ELogType.Success); + }); + }), "Update available!"); } - + public MainWindowVm(MainWindow view) { Task.Run(async () => IsUpdateAvailable = await _updater.CheckForUpdates()); @@ -215,8 +209,8 @@ namespace WeModPatcher.View.MainWindow SetFolderPathCommand = new RelayCommand(OnFolderPathSelection); ApplyPatchCommand = new RelayCommand(OnPatching); RestoreBackupCommand = new RelayCommand(OnBackupRestoring); - UpdateCommand = new AsyncRelayCommand(OnUpdate); - + UpdateCommand = new RelayCommand(OnUpdate); + WeModInfo = Extensions.FindWeMod(); if (WeModInfo == null) { diff --git a/WeModPatcher/View/Popups/PatchVectorsPopup.xaml b/WeModPatcher/View/Popups/PatchVectorsPopup.xaml index e045424..1058e1c 100644 --- a/WeModPatcher/View/Popups/PatchVectorsPopup.xaml +++ b/WeModPatcher/View/Popups/PatchVectorsPopup.xaml @@ -11,124 +11,37 @@ Foreground="{DynamicResource MutedForeground}" FontWeight="Medium" FontSize="13"> - -