/// <summary> /// Create a new process wrapper with an executable path and working directory. /// </summary> /// <param name="executablePath">Path to executable</param> /// <param name="workingDirectory">Starting directory for executable. May be left empty to inherit from parent</param> public ProcessHost(string executablePath, string workingDirectory) { if (!HostIsCompatible()) { throw new NotSupportedException("The host operating system is not compatible"); } _executablePath = executablePath; _workingDirectory = DefaultToCurrentIfEmpty(workingDirectory); StdIn = new Pipe(Pipe.Direction.In); StdErr = new Pipe(Pipe.Direction.Out); StdOut = new Pipe(Pipe.Direction.Out); _lastExitCode = 0; _si = new Kernel32.Startupinfo { wShowWindow = 0, dwFlags = Kernel32.StartfUsestdhandles | Kernel32.StartfUseshowwindow, hStdOutput = StdOut.WriteHandle, hStdError = StdErr.WriteHandle, hStdInput = StdIn.ReadHandle }; _si.cb = (uint)Marshal.SizeOf(_si); _pi = new Kernel32.ProcessInformation(); }
public static void Run(Settings settings, string clientDirectory, string bin, long ip, ushort port) { var binpath = Path.Combine(clientDirectory, bin); var startupInfo = new Kernel32.Startupinfo(); var processInfo = new Kernel32.ProcessInformation(); //TODO -- what to do if !success? var success = Kernel32.CreateProcess(binpath, string.Format("\"{0}\" {1} {2}", binpath.Trim(), ip, port), IntPtr.Zero, IntPtr.Zero, false, Kernel32.ProcessCreationFlags.CreateSuspended | Kernel32.ProcessCreationFlags.CreateDefaultErrorMode, IntPtr.Zero, null, ref startupInfo, out processInfo); var tHandle = processInfo.HThread; DllInjector.GetInstance.BInject(processInfo.DwProcessId, Path.Combine(clientDirectory, "login.dll")); Kernel32.ResumeThread(tHandle); System.Threading.Thread.Sleep(1000); // open the process after it is created so we can add the appropriate flags to write to the process var hndProc = Kernel32.OpenProcess((uint)(Kernel32.ProcessAccessFlags.CreateThread | Kernel32.ProcessAccessFlags.VirtualMemoryOperation | Kernel32.ProcessAccessFlags.VirtualMemoryRead | Kernel32.ProcessAccessFlags.VirtualMemoryWrite | Kernel32.ProcessAccessFlags.QueryInformation), 0, processInfo.DwProcessId); Kernel32.CloseHandle(tHandle); Kernel32.SuspendThread(hndProc); // Remove darkness if (settings.DisableDark) { Kernel32.WriteProcessMemory(hndProc, (IntPtr)0x0046690B, new byte[] { 0x90, 0xE9 }, 2, 0); } // Mob level highlight toggle if (settings.EnableMobColours) { Kernel32.WriteProcessMemory(hndProc, (IntPtr)0x0046786E, new byte[] { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }, 6, 0); } // Don't know if the constant suspend/resume is needed, but WinXP was being funnky and this works // Needed to get the lance master poly working properly var zelgoPak = File.ReadAllBytes(Path.Combine(clientDirectory, "zelgo.pak")); Kernel32.WriteProcessMemory(hndProc, (IntPtr)0x004B6CE0, new byte[] { 0xEB }, 1, 0); Kernel32.WriteProcessMemory(hndProc, (IntPtr)0x00504538, zelgoPak, (uint)zelgoPak.Length - 1, 0); Kernel32.WriteProcessMemory(hndProc, (IntPtr)0x006DA508, new byte[] { 0x0F, 0x27 }, 2, 0); Kernel32.ResumeThread(hndProc); Kernel32.CloseHandle(hndProc); hndProc = IntPtr.Zero; System.Threading.Thread.Sleep(1000); }
public static void Run(Settings settings, string bin, long ip, ushort port) { var binpath = Path.Combine(settings.ClientDirectory, bin); var startupInfo = new Kernel32.Startupinfo(); _processInfo = new Kernel32.ProcessInformation(); //TODO -- what to do if !success? var success = Kernel32.CreateProcess(binpath, string.Format("\"{0}\" {1} {2}", binpath.Trim(), ip, port), IntPtr.Zero, IntPtr.Zero, false, Kernel32.ProcessCreationFlags.CreateSuspended | Kernel32.ProcessCreationFlags.CreateDefaultErrorMode, IntPtr.Zero, null, ref startupInfo, out _processInfo); var tHandle = _processInfo.HThread; // TODO: need a better way to hook/suspend the client after themida unpack var tries = 10; byte[] patchWatchFor = { 0x75, 0x3B }; var patchWatchBuff = new byte[2]; Kernel32.ResumeThread(tHandle); System.Threading.Thread.Sleep(500); while (tries > 0) { Kernel32.SuspendThread(tHandle); Kernel32.ReadProcessMemory(_processInfo.HProcess, (IntPtr)0x0045CF2F, patchWatchBuff, (uint)patchWatchBuff.Length, 0); if (ByteArrayCompare(patchWatchBuff, patchWatchFor)) { // Fix Runtime Expired Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x0045CF2F, new byte[] { 0xEB }, 1, 0); // Fix GameGuard Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x0045E3AC, new byte[] { 0x90, 0xE9 }, 2, 0); Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x004DE45A, new byte[] { 0x90, 0x90 }, 2, 0); Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x0045BA71, new byte[] { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }, 6, 0); Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x0045EABA, new byte[] { 0xEB }, 1, 0); // Don't let Lin.bin install NPKCMSVC Windows Service Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x00474AC4, new byte[] { 0x90, 0x90, 0x90, 0x90, 0x90, 0x84, 0xC0, 0x5E, 0x5B, 0xEB }, 10, 0); // Remove darkness if (settings.DisableDark) { byte[] write7 = { 0x90, 0xE9 }; Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x0046690B, new byte[] { 0x90, 0xE9 }, 2, 0); } // TODO: A checkbox to enable/disable like darkness? // Mob name with color Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x0046786E, new byte[] { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }, 6, 0); // Load list.spr with lancemaster fix string zelgoPakPath = Path.Combine(settings.ClientDirectory, (string)"zelgo.pak"); if (File.Exists(zelgoPakPath)) { byte[] zelgoPak = File.ReadAllBytes(zelgoPakPath); Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x004B6CE0, new byte[] { 0xEB }, 1, 0); Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x00504538, zelgoPak, (uint)zelgoPak.Length, 0); Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x006DA508, new byte[] { 0x0F, 0x27 }, (uint)2, 0); } // Codepage?? Kernel32.WriteProcessMemory(_processInfo.HProcess, (IntPtr)0x00483B8E, new byte[] { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }, 6, 0); Kernel32.ResumeThread(tHandle); break; } Kernel32.ResumeThread(tHandle); System.Threading.Thread.Sleep(500); tries--; } Kernel32.ResumeThread(tHandle); }
public static int Main(string[] args) { #if DEBUG IntPtr hConsole = IntPtr.Zero; if (Kernel32.AllocConsole()) { hConsole = Kernel32.GetConsoleWindow(); User32.SetLayeredWindowAttributes(hConsole, 0u, 225, 2); } User32.ShowWindow(hConsole, 5); Console.ForegroundColor = ConsoleColor.Green; Tracer.TraceWrite += TraceWrite; #endif Console.CancelKeyPress += ConsoleCancel; Kernel32.Win32FindDataW findFileData = new Kernel32.Win32FindDataW(); IntPtr hSearch = Kernel32.FindFirstFileW("lotrsec.big", ref findFileData); if (hSearch == (IntPtr)(-1)) { StringBuilder fileName = new StringBuilder(Kernel32.MAX_PATH); Kernel32.GetModuleFileNameW(IntPtr.Zero, fileName, Kernel32.MAX_PATH); for (int idx = fileName.Length - 1; idx != 0; --idx) { if (fileName[idx] == '\\') { fileName[idx] = '\0'; break; } } Kernel32.SetCurrentDirectoryW(fileName.ToString()); } else { Kernel32.FindClose(hSearch); } string config = null; string modconifg = null; List <string> argList = new List <string>(args.Length); for (int idx = 0; idx < args.Length; ++idx) { if (string.Equals(args[idx], "-config")) { if (idx == args.Length - 1) { MessageBox.Show("Invalid config parameter. A path needs to be set."); return(-1); } config = args[idx++ + 1]; } if (string.Equals(args[idx], "-modconfig")) { if (idx == args.Length - 1) { MessageBox.Show("Invalid modconfig parameter. A path needs to be set."); return(-1); } modconifg = args[idx++ + 1]; } else { argList.Add(args[idx]); } } args = argList.ToArray(); Kernel32.StartupInfoW si = new Kernel32.StartupInfoW(true); Kernel32.ProcessInformation pi = new Kernel32.ProcessInformation(); int overallTries = 0; int tries = 0; Registry registry = new Registry(); string executablePath = System.IO.Path.Combine(registry.InstallPath, "data", "ra3_1.12.game"); ExecutableType executableType = ExecutableType.Unknown; using (System.IO.Stream stream = new System.IO.FileStream(executablePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read)) { byte[] buffer = new byte[stream.Length]; stream.Read(buffer, 0, buffer.Length); uint hash = Nanocore.Core.FastHash.GetHashCode(buffer); switch (hash) { case 0xCFAAD44Bu: case 0xE6D223E6u: case 0xCF5817CCu: executableType = ExecutableType.Steam; break; case 0xE7AF6A35u: case 0x2F121290u: executableType = ExecutableType.Origin; break; case 0xA05DEB39: // this has the 4gb thing applied, need to check the original executableType = ExecutableType.Retail; break; case 0xBFE68CAD: // should I care? this is mostly here because origins and reloaded exe have the same size executableType = ExecutableType.ReLOADeD; break; } } if (executableType == ExecutableType.Unknown) { MessageBox.Show("A version of the game is installed. Please get the game from an official source."); return(-1); } else if (executableType == ExecutableType.Retail) { MessageBox.Show("The retail or an old origin version is installed. If you are using Origin please update the game. Retail versions cannot be supported due to SecuROM."); return(-1); } while (!_isConsoleCancel) { ++tries; if (tries > 20) { if (MessageBox.Show("Windows caching interferred with injecting into the game, do you want to try again?", "Error", MessageBoxButton.YesNo) == MessageBoxResult.Yes) { overallTries += tries; tries = 1; } else { break; } } try { Kernel32.CreateProcessW(null, $"\"{executablePath}\" {string.Join(" ", args)} -config \"{config ?? System.IO.Path.Combine(registry.InstallPath, $"RA3_{registry.Language}_1.12.skudef")}\" {(modconifg is null ? string.Empty : $"-modconfig \"{modconifg}\"")}", IntPtr.Zero, IntPtr.Zero, true, 4, IntPtr.Zero, null, ref si, ref pi); EZHook.Inject(pi.DwProcessId, "Nanocore.dll", pi.DwThreadId, executableType); }
////////////////////////////////////////////////////////////////////////////////////////////////////////// // Start, Stop, Pause, Resume, etc // ////////////////////////////////////////////////////////////////////////////////////////////////////////// #region Start/Stop/Pause/Resume/etc /// <summary> /// Start the process. /// </summary> /// <exception cref="Win32Exception"></exception> internal void StartProcess(string username) { //CloseProcess old instances StopProcess(); var startupInfo = new Kernel32.StartupInfo(); var processInformation = new Kernel32.ProcessInformation(); IntPtr environment = IntPtr.Zero; //Flags that specify the priority and creation method of the process Kernel32.CreationFlags creationFlags = Kernel32.CreationFlags.CreateUnicodeEnvironment | //Windows 7+ always using the unicode environment Kernel32.CreationFlags.CreateNewConsole | _serviceDefinition.ProcessPriority.ConvertToCreationFlag(); //Set the startupinfo startupInfo.cb = (uint)Marshal.SizeOf(startupInfo); startupInfo.desktop = "winsta0\\default"; //Create command line arguments var cmdLine = ""; if (string.Equals(Path.GetExtension(_serviceDefinition.BinaryPath), ".bat", StringComparison.OrdinalIgnoreCase)) { cmdLine = "cmd.exe /c"; } cmdLine += BuildDoubleQuotedString(_serviceDefinition.BinaryPath); if (!string.IsNullOrWhiteSpace(_serviceDefinition.Arguments)) { cmdLine += " " + _serviceDefinition.Arguments; } try { if (string.IsNullOrWhiteSpace(username)) { //Set last start in user session _lastStartInUserSession = false; _lastSessionUsername = string.Empty; if (!Kernel32.CreateProcess( null, cmdLine, null, null, false, creationFlags, IntPtr.Zero, //TODO: Change Path.GetDirectoryName(_serviceDefinition.BinaryPath), ref startupInfo, out processInformation)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } } else { //Set last start in user session _lastStartInUserSession = true; _lastSessionUsername = username; //Get user token using (SafeAccessTokenHandle token = TokenHelper.GetPrimaryTokenByUsername(username)) { if (token == null) { throw new Exception("GetPrimaryTokenByUsername: No valid session found."); } //Create environment block if (!Userenv.CreateEnvironmentBlock(ref environment, token, false)) { throw new Exception("StartInUserSession: CreateEnvironmentBlock failed."); } WindowsIdentity.RunImpersonated(token, () => { if (!Advapi32.CreateProcessAsUser( token, null, cmdLine, null, null, false, creationFlags, environment, Path.GetDirectoryName(_serviceDefinition.BinaryPath), ref startupInfo, out processInformation)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } }); } } } finally { if (environment != IntPtr.Zero) { Userenv.DestroyEnvironmentBlock(environment); } if (processInformation.processHandle != IntPtr.Zero) { Kernel32.CloseHandle(processInformation.processHandle); } if (processInformation.threadHandle != IntPtr.Zero) { Kernel32.CloseHandle(processInformation.threadHandle); } } //Get process and enable raising events _process = Process.GetProcessById((int)processInformation.processId); _process.EnableRaisingEvents = true; _process.Exited += ProcessOnExited; //Assign process to job if (KillChildProcessJob.IsSupportedWindowsVersion) { _killChildProcessJob.AssignProcess(_process); } _lastRestartTime = DateTime.UtcNow; //Trigger event to update pid OnUpdateProcessPid(processInformation.processId); }