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