From 322a92acb6e54f3ba054f554eb35e23f813b7101 Mon Sep 17 00:00:00 2001 From: Vatsan Madhavan Date: Sat, 27 Jun 2020 13:43:36 -0700 Subject: [PATCH] Add tests for `CreateRemoteThread` and `CreateRemoteThreadEx` --- .../storebanned/Kernel32Facts.cs | 128 ++++++++++++++++-- 1 file changed, 116 insertions(+), 12 deletions(-) diff --git a/src/Kernel32.Tests/storebanned/Kernel32Facts.cs b/src/Kernel32.Tests/storebanned/Kernel32Facts.cs index 7fae28f1..dbb3df4e 100644 --- a/src/Kernel32.Tests/storebanned/Kernel32Facts.cs +++ b/src/Kernel32.Tests/storebanned/Kernel32Facts.cs @@ -957,28 +957,28 @@ public void SetHandleInformation_DoesNotThrow() [Fact] public unsafe void CreateThread_Test() { - Kernel32.SECURITY_ATTRIBUTES secAttrs = new Kernel32.SECURITY_ATTRIBUTES + var secAttrs = new Kernel32.SECURITY_ATTRIBUTES { bInheritHandle = 1, lpSecurityDescriptor = IntPtr.Zero, nLength = Marshal.SizeOf() }; - int dwThreadId = Kernel32.GetCurrentThreadId(); + var dwThreadId = Kernel32.GetCurrentThreadId(); - bool result = false; + var result = false; var gcHandle = GCHandle.Alloc(result); try { - int dwNewThreadId = 0; + var dwNewThreadId = 0; var hThread = Kernel32.CreateThread( - &secAttrs, - SIZE_T.Zero, - CreateThread_Test_ThreadMain, - GCHandle.ToIntPtr(gcHandle), - Kernel32.CreateProcessFlags.None, - &dwNewThreadId); + &secAttrs, + SIZE_T.Zero, + CreateThread_Test_ThreadMain, + GCHandle.ToIntPtr(gcHandle), + Kernel32.CreateProcessFlags.None, + &dwNewThreadId); Kernel32.WaitForSingleObject(hThread, -1); result = (bool)gcHandle.Target; @@ -992,10 +992,114 @@ public unsafe void CreateThread_Test() } /// - /// Helper for test. + /// Basic validation for + /// Note that this test DOES NOT create a true REMOTE thread in a foreign process; it just leverages this function to create a thread in the current (i.e, the test) procrss. + /// Nevertheless, this approach provides modest confidence that the P/Invoke definition is well-formed. + /// + /// Creates a thread by supplying as its ThreadProc/. + /// The ThreadProc updates a bool value (supplied by the thread that created it) from false -> true. This change + /// is observed by the calling thread as proof of successful thread-creation. + /// + /// Also validates that the (native) Thread-ID for the newly created Thread is different than the (native) Thread-ID + /// of that of the calling thread. + /// + [Fact] + public unsafe void CreateRemoteThread_PseudoTest() + { + var secAttrs = new Kernel32.SECURITY_ATTRIBUTES + { + bInheritHandle = 1, + lpSecurityDescriptor = IntPtr.Zero, + nLength = Marshal.SizeOf() + }; + + var dwThreadId = Kernel32.GetCurrentThreadId(); + using var hProcess = Kernel32.GetCurrentProcess(); + + var result = false; + var gcHandle = GCHandle.Alloc(result); + try + { + var dwNewThreadId = 0; + var hThread = + Kernel32.CreateRemoteThread( + hProcess.DangerousGetHandle(), + &secAttrs, + SIZE_T.Zero, + CreateThread_Test_ThreadMain, + GCHandle.ToIntPtr(gcHandle), + Kernel32.CreateProcessFlags.None, + &dwNewThreadId); + Kernel32.WaitForSingleObject(hThread, -1); + + result = (bool)gcHandle.Target; + Assert.True(result); + Assert.NotEqual(dwThreadId, dwNewThreadId); + } + finally + { + gcHandle.Free(); + } + } + + /// + /// Basic validation for + /// Note that this test DOES NOT create a true REMOTE thread in a foreign process; it just leverages this function to create a thread in the current (i.e, the test) procrss. + /// Nevertheless, this approach provides modest confidence that the P/Invoke definition is well-formed. + /// + /// Creates a thread by supplying as its ThreadProc/. + /// The ThreadProc updates a bool value (supplied by the thread that created it) from false -> true. This change + /// is observed by the calling thread as proof of successful thread-creation. + /// + /// Also validates that the (native) Thread-ID for the newly created Thread is different than the (native) Thread-ID + /// of that of the calling thread. + /// + [Fact] + public unsafe void CreateRemoteThreadEx_PseudoTest() + { + var secAttrs = new Kernel32.SECURITY_ATTRIBUTES + { + bInheritHandle = 1, + lpSecurityDescriptor = IntPtr.Zero, + nLength = Marshal.SizeOf() + }; + + var dwThreadId = Kernel32.GetCurrentThreadId(); + using var hProcess = Kernel32.GetCurrentProcess(); + + var result = false; + var gcHandle = GCHandle.Alloc(result); + try + { + var dwNewThreadId = 0; + var hThread = + Kernel32.CreateRemoteThreadEx( + hProcess.DangerousGetHandle(), + &secAttrs, + SIZE_T.Zero, + CreateThread_Test_ThreadMain, + GCHandle.ToIntPtr(gcHandle), + Kernel32.CreateProcessFlags.None, + null, + &dwNewThreadId); + Kernel32.WaitForSingleObject(hThread, -1); + + result = (bool)gcHandle.Target; + Assert.True(result); + Assert.NotEqual(dwThreadId, dwNewThreadId); + } + finally + { + gcHandle.Free(); + } + } + + /// + /// Helper for , and + /// tests. /// /// - /// Data passed by . This is a pinned to a + /// Data passed by the test. This is a pinned to a /// which will be updated to true in this method. /// ///