diff --git a/PInvoke/NtDll/Winternl.cs b/PInvoke/NtDll/Winternl.cs index c17ee622..3610d28a 100644 --- a/PInvoke/NtDll/Winternl.cs +++ b/PInvoke/NtDll/Winternl.cs @@ -210,6 +210,12 @@ public static partial class NtDll /// Reserved. MaxSubsystemInformationType, } + + /// Set the debug object handle in the TEB. This function is UNDOCUMENTED. + /// Debug object handle. Retrieve from NtQueryInformationProcess + [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] + public static extern void DbgUiSetThreadDebugObject(IntPtr DebugObjectHandle); + /// Creates a process. This function is UNDOCUMENTED. /// The process handle. /// The desired access. @@ -524,7 +530,7 @@ public static partial class NtDll if (ret.Succeeded) return res; if (ret != NTStatus.STATUS_INFO_LENGTH_MISMATCH || qsz == 0) - throw ret.GetException(); + throw ret.GetException()!; res.Size = qsz; NtWow64QueryInformationProcess64(ProcessHandle, ProcessInformationClass, ((IntPtr)res).ToInt32(), res.Size, out _).ThrowIfFailed(); return res; @@ -538,7 +544,7 @@ public static partial class NtDll if (status.Succeeded) return mem; if (status != NTStatus.STATUS_INFO_LENGTH_MISMATCH || sz == 0) - throw status.GetException(); + throw status.GetException()!; mem.Size = sz; NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, mem, mem.Size, out _).ThrowIfFailed(); return mem; @@ -551,6 +557,20 @@ public static partial class NtDll /// if structures returned from NtQueryInformationProcess must be configured exclusively for 64-bit use. public static bool NtQueryInformationProcessRequiresWow64Structs(HPROCESS ProcessHandle) => IsWow64(Kernel32.GetCurrentProcess()) && !IsWow64(ProcessHandle); + /// Call the kernel to remove the debug object. This function is UNDOCUMENTED. + /// The process handle. + /// Debug object handle. Retrieve from NtQueryInformationProcess + /// + /// The function returns an NTSTATUS success or error code. + /// + /// The forms and significance of NTSTATUS error codes are listed in the Ntstatus.h header file available in the DDK, and are + /// described in the DDK documentation under Kernel-Mode Driver Architecture / Design Guide / Driver Programming Techniques / + /// Logging Errors. + /// + /// + [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] + public static extern NTStatus NtRemoveProcessDebug(HPROCESS ProcessHandle, IntPtr DebugObjectHandle); + /// /// /// [ NtQueryInformationProcess may be altered or unavailable in future versions of Windows. Applications should use the diff --git a/UnitTests/PInvoke/NtDll/WinternlTests.cs b/UnitTests/PInvoke/NtDll/WinternlTests.cs index c484021d..53aa45ac 100644 --- a/UnitTests/PInvoke/NtDll/WinternlTests.cs +++ b/UnitTests/PInvoke/NtDll/WinternlTests.cs @@ -68,4 +68,58 @@ public partial class WinternlTests Assert.That(() => ppb = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessPriorityBoost), Throws.Nothing); TestContext.WriteLine($"Priority boost: {ppb.Value}"); } + + [Test] + public void DbgUiSetThreadDebugObjectAndNtRemoveProcessDebugTest() + { + Kernel32.STARTUPINFO StartInfo = new() + { + dwFlags = Kernel32.STARTF.STARTF_USESHOWWINDOW, + wShowWindow = (ushort)ShowWindowCommand.SW_HIDE + }; + Assert.That(Kernel32.CreateProcess(@"C:\Program Files\Notepad++\notepad++.exe", dwCreationFlags: Kernel32.CREATE_PROCESS.DEBUG_PROCESS | Kernel32.CREATE_PROCESS.CREATE_UNICODE_ENVIRONMENT, lpStartupInfo: StartInfo, lpProcessInformation: out Kernel32.SafePROCESS_INFORMATION Information), ResultIs.Successful); + using (Information) + using (NtQueryResult DebugObjectHandleQueryResult = NtQueryInformationProcess(Information.hProcess, PROCESSINFOCLASS.ProcessDebugObjectHandle)) + { + Assert.That(DebugObjectHandleQueryResult, ResultIs.ValidHandle); + Assert.That(DebugObjectHandleQueryResult.Value, ResultIs.ValidHandle); + + try + { + DbgUiSetThreadDebugObject(DebugObjectHandleQueryResult.Value); + + try + { + Kernel32.SafeHPROCESS DebugProcessHandle = Kernel32.SafeHPROCESS.Null; + try + { + while (true) + { + Assert.That(Kernel32.WaitForDebugEvent(out Kernel32.DEBUG_EVENT Event, Kernel32.INFINITE), ResultIs.Successful); + if (Event.dwDebugEventCode == Kernel32.DEBUG_EVENT_CODE.CREATE_PROCESS_DEBUG_EVENT) + { + DebugProcessHandle = new Kernel32.SafeHPROCESS(Event.u.CreateProcessInfo.hProcess); + break; + } + Assert.That(Kernel32.ContinueDebugEvent(Event.dwProcessId, Event.dwThreadId, Kernel32.DEBUG_CONTINUE.DBG_CONTINUE), ResultIs.Successful); + } + Assert.False(DebugProcessHandle.IsNull); + } + finally + { + DebugProcessHandle.Dispose(); + } + } + finally + { + DbgUiSetThreadDebugObject(IntPtr.Zero); + } + } + finally + { + Assert.That(Kernel32.TerminateProcess(Information.hProcess, 0), ResultIs.Successful); + Assert.That(NtRemoveProcessDebug(Information.hProcess, DebugObjectHandleQueryResult.Value), ResultIs.Successful); + } + } + } } \ No newline at end of file