private static IntPtr GetBaseAddress(Win32.SafeProcessHandle hProcess, Win32.SafeThreadHandle hThread) { /* Entry point varies, so need to use GetThreadContext to find entrypoint */ Win32.CONTEXT cntx = new Win32.CONTEXT(); cntx.ContextFlags = (int)Win32.CONTEXT_FLAGS.CONTEXT_FULL; Win32.GetThreadContext(hThread.DangerousGetHandle(), ref cntx); /* Ebx+8 = pointer to entrypoint to be read with ReadProcessMemory. */ byte[] tmp = new byte[4]; Memory.Read(hProcess.DangerousGetHandle(), (IntPtr)(cntx.Ebx + 8), tmp, true); int baseAddress = tmp[3] << 24 | tmp[2] << 16 | tmp[1] << 8 | tmp[0]; return((IntPtr)baseAddress); }
internal bool ReadProcessMemory(Win32.SafeProcessHandle processHandle, out string commandLine, out string imagePath) { if (processHandle is null) { throw new ArgumentNullException(nameof(processHandle)); } commandLine = null; imagePath = null; if (processHandle.IsInvalid) { return(false); } // Gloves off... unsafe { int bytesRead = 0; long outResult = 0; var basicInfo = new Win32.ProcessBasicInformation { }; // Ask the OS for information about the process, this will include the address of the PEB or // Process Environment Block, which contains useful information (like the offset of the process' parameters). var hresult = Win32.Ntdll.QueryInformationProcess(processHandle: processHandle, processInformationClass: Win32.ProcessInformationClass.BasicInformation, processInformation: &basicInfo, processInformationLength: sizeof(Win32.ProcessBasicInformation), returnLength: &outResult); if (hresult != Win32.Hresult.Ok) { var error = Win32.Kernel32.GetLastError(); Trace.WriteLine($"failed to query process information [{error}]."); return(false); } var peb = new Win32.ProcessEnvironmentBlock { }; // Now that we know the offsets of the process' parameters, read it because // we want the offset to the image-path and the command-line strings. if (!Win32.Kernel32.ReadProcessMemory(processHandle: processHandle, baseAddress: basicInfo.ProcessEnvironmentBlock, buffer: &peb, bufferSize: sizeof(Win32.ProcessEnvironmentBlock), bytesRead: out bytesRead) || bytesRead < sizeof(Win32.ProcessEnvironmentBlock)) { var error = Win32.Kernel32.GetLastError(); Trace.WriteLine($"failed to read process environment block [{error}] ({bytesRead:n0} bytes read)."); return(false); } var processParameters = new Win32.PebProcessParameters { }; // Read the process parameters data structure to get the offsets to // the image-path and command-line strings. if (!Win32.Kernel32.ReadProcessMemory(processHandle: processHandle, baseAddress: peb.ProcessParameters, buffer: &processParameters, bufferSize: sizeof(Win32.PebProcessParameters), bytesRead: out bytesRead) || bytesRead < sizeof(Win32.PebProcessParameters)) { var error = Win32.Kernel32.GetLastError(); Trace.WriteLine($"failed to read process parameters [{error}] ({bytesRead:n0} bytes read)."); return(false); } byte *buffer = stackalloc byte[4096]; // Read the image-path string, then use it to produce a new string object. // Don't give up if the read fails, move on to the next value - have hope. if (!Win32.Kernel32.ReadProcessMemory(processHandle: processHandle, baseAddress: processParameters.ImagePathName.Buffer, buffer: buffer, bufferSize: processParameters.ImagePathName.MaximumSize, bytesRead: out bytesRead) || bytesRead != processParameters.ImagePathName.MaximumSize) { var error = Win32.Kernel32.GetLastError(); Trace.WriteLine($"failed to read process image path [{error}] ({bytesRead:n0} bytes read)."); } else { // Only allocate the string object if the read was successful. imagePath = new string((char *)buffer); } // Read the command-line string, then use it to produce a new string object. if (!Win32.Kernel32.ReadProcessMemory(processHandle: processHandle, baseAddress: processParameters.CommandLine.Buffer, buffer: buffer, bufferSize: processParameters.CommandLine.MaximumSize, bytesRead: out bytesRead) || bytesRead != processParameters.CommandLine.MaximumSize) { var error = Win32.Kernel32.GetLastError(); Trace.WriteLine($"failed to read process command line [{error}] ({bytesRead:n0} bytes read)."); } else { // Only allocate the string object if the read was successful. commandLine = new string((char *)buffer); } } return(true); }