void PatchClient(ProcessInformation process, ClientVersion version, out ClientLoadResult result) { var patchMultipleInstances = UserSettingsManager.Instance.Settings.AllowMultipleInstances; var patchIntroVideo = UserSettingsManager.Instance.Settings.SkipIntroVideo; var patchNoWalls = UserSettingsManager.Instance.Settings.NoWalls; // Patch Process ProcessMemoryAccessor accessor = null; Stream patchStream = null; BinaryReader reader = null; BinaryWriter writer = null; try { accessor = new ProcessMemoryAccessor(process.ProcessId, ProcessAccess.ReadWrite); patchStream = accessor.GetStream(); reader = new BinaryReader(patchStream); writer = new BinaryWriter(patchStream); if (patchMultipleInstances && version.MultipleInstanceAddress > 0) { patchStream.Position = version.MultipleInstanceAddress; writer.Write((byte)0x31); // XOR writer.Write((byte)0xC0); // EAX, EAX writer.Write((byte)0x90); // NOP writer.Write((byte)0x90); // NOP writer.Write((byte)0x90); // NOP writer.Write((byte)0x90); // NOP } if (patchIntroVideo && version.IntroVideoAddress > 0) { patchStream.Position = version.IntroVideoAddress; writer.Write((byte)0x83); // CMP writer.Write((byte)0xFA); // EDX writer.Write((byte)0x00); // 0 writer.Write((byte)0x90); // NOP writer.Write((byte)0x90); // NOP writer.Write((byte)0x90); // NOP } if (patchNoWalls && version.NoWallAddress > 0) { patchStream.Position = version.NoWallAddress; writer.Write((byte)0xEB); // JMP SHORT writer.Write((byte)0x17); // +0x17 writer.Write((byte)0x90); // NOP } result = ClientLoadResult.Success; } catch { result = ClientLoadResult.PatchingFailed; } finally { reader?.Dispose(); writer?.Dispose(); accessor?.Dispose(); patchStream?.Dispose(); // Resume and close handles. NativeMethods.ResumeThread(process.ThreadHandle); NativeMethods.CloseHandle(process.ThreadHandle); NativeMethods.CloseHandle(process.ProcessHandle); } }
ClientVersion DetectClientVersion(string clientPath, out ClientLoadResult result) { ClientVersion clientVersion = null; var clientHash = string.Empty; var clientKey = UserSettingsManager.Instance.Settings.SelectedVersion; result = ClientLoadResult.Success; Stream inputStream = null; // Get MD5 Hash and Detect Version try { if (string.Equals("Auto-Detect", clientKey, StringComparison.OrdinalIgnoreCase)) { inputStream = File.Open(clientPath, FileMode.Open, FileAccess.Read, FileShare.Read); using (var md5 = new MD5CryptoServiceProvider()) { md5.ComputeHash(inputStream); inputStream.Close(); clientHash = BitConverter.ToString(md5.Hash).Replace("-", string.Empty); } clientKey = ClientVersionManager.Instance.DetectVersion(clientHash); if (clientKey == null) { result = ClientLoadResult.AutoDetectFailed; return clientVersion; } } } catch { result = ClientLoadResult.HashError; return clientVersion; } finally { inputStream?.Dispose(); } // Get Version from Manager clientVersion = ClientVersionManager.Instance.GetVersion(clientKey); if (clientVersion == null) result = ClientLoadResult.BadVersion; return clientVersion; }
void HandleClientLoadResult(ClientLoadResult result) { // Client Path Invalid if (result == ClientLoadResult.ClientPathInvalid) { var showClientPathSetting = this.ShowMessageBox("Invalid Client Path", "The client path specified in the settings does not exist.\nDo you wish to set it now?", "New clients cannot be started until this value is set to a valid path.", MessageBoxButton.YesNo, 460, 260); if (showClientPathSetting.Value) ShowSettingsWindow(SettingsWindow.GameClientTabIndex); } // Hash IO Error if (result == ClientLoadResult.HashError) { this.ShowMessageBox("IO Error", "An I/O error occured when trying to read the client executable.", "You must have read permissions for the file.", MessageBoxButton.OK, 460, 240); } // Auto-Detect Error if (result == ClientLoadResult.AutoDetectFailed) { var showClientVersionSetting = this.ShowMessageBox("Auto-Detect Failed", "The client version could not be detected from the file.\nDo you want to set it manually?", "New clients cannot be started unless version detection is successful.\nYou may manually select a client version instead.", MessageBoxButton.YesNo, 460, 260); if (showClientVersionSetting.Value) ShowSettingsWindow(SettingsWindow.GameClientTabIndex); } // Bad- Version Error if (result == ClientLoadResult.BadVersion) { var showClientVersionSetting = this.ShowMessageBox("Invalid Client Version", "The client version selected is invalid.\nWould you like to select another one?", "New clients cannot be started until a valid client version is selected.\nAuto-detection may also be selected.", MessageBoxButton.YesNo, 460, 240); if (showClientVersionSetting.Value) ShowSettingsWindow(SettingsWindow.GameClientTabIndex); } // Bad- Version Error if (result == ClientLoadResult.BadVersion) { var showClientVersionSetting = this.ShowMessageBox("Invalid Client Version", "The client version selected is invalid.\nWould you like to select another one?", "New clients cannot be started until a valid client version is selected.\nAuto-detection may also be selected.", MessageBoxButton.YesNo, 460, 240); if (showClientVersionSetting.Value) ShowSettingsWindow(SettingsWindow.GameClientTabIndex); } // Create Process if (result == ClientLoadResult.CreateProcessFailed) { var showClientVersionSetting = this.ShowMessageBox("Failed to Launch Client", "An error occured trying to launch the game client.\nDo you want to check the client settings?", "Check that the client path is correct.\nAnti-virus or other security software may be preventing this.", MessageBoxButton.YesNo, 420, 240); if (showClientVersionSetting.Value) ShowSettingsWindow(SettingsWindow.GameClientTabIndex); } // Patching if (result == ClientLoadResult.PatchingFailed) { this.ShowMessageBox("Patching Error", "An error occured trying to patch the game client.", "The client should continue to work but features such as multiple instances or skipping the intro video may not be patched properly.\n\nIn some cases the client may crash immediately.", MessageBoxButton.OK, 460, 260); } }
ProcessInformation StartClientProcess(string clientPath, out ClientLoadResult result) { result = ClientLoadResult.Success; // Create Process ProcessInformation processInformation; var startupInfo = new StartupInfo { Size = Marshal.SizeOf(typeof(StartupInfo)) }; var processSecurity = new SecurityAttributes(); var threadSecurity = new SecurityAttributes(); processSecurity.Size = Marshal.SizeOf(processSecurity); threadSecurity.Size = Marshal.SizeOf(threadSecurity); bool wasCreated = NativeMethods.CreateProcess(clientPath, null, ref processSecurity, ref threadSecurity, false, ProcessCreationFlags.Suspended, IntPtr.Zero, null, ref startupInfo, out processInformation); // Check Process if (!wasCreated || processInformation.ProcessId == 0) result = ClientLoadResult.CreateProcessFailed; return processInformation; }