/// <summary> /// Reads the MODULEINFO struct of a process. /// </summary> /// <param name="process">The process to read.</param> /// <returns>Returns a 32 bit representation of the MODULEINFO struct.</returns> public static PInvokeStuff.MODULEINFO GetKernel32ModuleInfo(Process process) { // GetModuleHandle returns NULL when failed IntPtr moduleHandle = PInvokeStuff.GetModuleHandle("kernel32.dll"); if (moduleHandle == null) { throw new Exceptions.PInvokeException(nameof(GetKernel32ModuleInfo), nameof(PInvokeStuff.GetModuleHandle), Marshal.GetLastWin32Error()); } PInvokeStuff.MODULEINFO mi = new PInvokeStuff.MODULEINFO(); // GetModuleInformation returns 0 when failed var result = PInvokeStuff.GetModuleInformation(process.Handle, moduleHandle, out mi, (uint)Marshal.SizeOf(mi)); if (!result) { throw new Exceptions.PInvokeException(nameof(GetKernel32ModuleInfo), nameof(PInvokeStuff.GetModuleInformation), Marshal.GetLastWin32Error()); } return(mi); }
/// <summary> /// Returns the address of the ThreadStack0 special symbol. /// </summary> /// <param name="process">The target process</param> /// <returns>The memory address of ThreadStack0</returns> public static uint GetThreadStack0(Process process) { if (!Is32BitProcess(process.Handle)) { throw new ArgumentException("Provided process is not 32 bit."); } const uint BytesToSample = 4096; // Arbitrary (?). 4096 should be enough for both x86 and x64; should be divisible by 4 (x86) PInvokeStuff.MODULEINFO mi = GetKernel32ModuleInfo(process); // MODULEINFO delivers the Kernel32 module's load address and size (note that load address (lpBaseOfDll) is the same as the module handle) PInvokeStuff.NT_TIB tib = GetTIB(process); // NT_TIB delivers the stack base address of the process' main thread // Read sample byte array from the base of the main thread stack byte[] StackBaseSample = ReadToByteArray(process.Handle, (tib.StackBase - BytesToSample), BytesToSample); int i = 0; // Keep scope bigger than loop // ThreadStack0 is the first pointer in the main thread's stack that points inside the Kernel32 module. // To find it, we iterate through each 32 bit (4 byte) value in the stack sample, and check if it is in the target range. for (i = (StackBaseSample.Length / 4) - 1; i >= 0; --i) { UInt32 valueAtPosition = BitConverter.ToUInt32(StackBaseSample, i * 4); if (valueAtPosition >= (uint)mi.lpBaseOfDll && valueAtPosition <= (uint)mi.lpBaseOfDll + mi.SizeOfImage) { break; } } if (i == 0) // If i reached zero, then iteration finished without finding a match { throw new Exceptions.LocalException(nameof(GetThreadStack0), "ThreadStack0 can't be found in the sampled " + BytesToSample + " bytes"); } // Finally, calculate and return the actual ThreadStack0 address from index i return((uint)(tib.StackBase - BytesToSample + i * 4)); }