[PATCH] ntdll: Support THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER thread creation flag.

Paul Gofman pgofman at codeweavers.com
Fri Dec 3 11:28:34 CST 2021


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/ntdll/tests/Makefile.in |   1 +
 dlls/ntdll/tests/thread.c    | 126 +++++++++++++++++++++++++++++++++++
 dlls/ntdll/unix/process.c    |  12 +++-
 dlls/ntdll/unix/thread.c     |   6 +-
 server/protocol.def          |   2 +-
 server/thread.c              |   3 +-
 6 files changed, 146 insertions(+), 4 deletions(-)
 create mode 100644 dlls/ntdll/tests/thread.c

diff --git a/dlls/ntdll/tests/Makefile.in b/dlls/ntdll/tests/Makefile.in
index 8c6a86fb965..90deb5865f8 100644
--- a/dlls/ntdll/tests/Makefile.in
+++ b/dlls/ntdll/tests/Makefile.in
@@ -22,6 +22,7 @@ C_SRCS = \
 	rtlstr.c \
 	string.c \
 	sync.c \
+	thread.c \
 	threadpool.c \
 	time.c \
 	virtual.c \
diff --git a/dlls/ntdll/tests/thread.c b/dlls/ntdll/tests/thread.c
new file mode 100644
index 00000000000..09b9bcc8497
--- /dev/null
+++ b/dlls/ntdll/tests/thread.c
@@ -0,0 +1,126 @@
+/*
+ * Unit test suite for ntdll thread functions
+ *
+ * Copyright 2021 Paul Gofman for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ */
+
+#include "ntdll_test.h"
+
+static NTSTATUS (WINAPI *pNtCreateThreadEx)( HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *,
+                                             HANDLE, PRTL_THREAD_START_ROUTINE, void *,
+                                             ULONG, ULONG_PTR, SIZE_T, SIZE_T, PS_ATTRIBUTE_LIST * );
+
+static void init_function_pointers(void)
+{
+    HMODULE hntdll = GetModuleHandleA( "ntdll.dll" );
+#define GET_FUNC(name) p##name = (void *)GetProcAddress( hntdll, #name );
+    GET_FUNC( NtCreateThreadEx );
+#undef GET_FUNC
+}
+
+static void CALLBACK test_NtCreateThreadEx_proc(void *param)
+{
+}
+
+static void test_dbg_hidden_thread_creation(void)
+{
+    RTL_USER_PROCESS_PARAMETERS *params;
+    PS_CREATE_INFO create_info;
+    PS_ATTRIBUTE_LIST ps_attr;
+    WCHAR path[MAX_PATH + 4];
+    HANDLE process, thread;
+    UNICODE_STRING imageW;
+    BOOLEAN dbg_hidden;
+    NTSTATUS status;
+
+    if (!pNtCreateThreadEx)
+    {
+        win_skip( "NtCreateThreadEx is not available.\n" );
+        return;
+    }
+
+    status = pNtCreateThreadEx( &thread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), test_NtCreateThreadEx_proc,
+                                NULL, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, 0, 0, 0, NULL );
+    ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
+
+    dbg_hidden = 0xcc;
+    status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dbg_hidden, sizeof(dbg_hidden), NULL );
+    ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
+    ok( !dbg_hidden, "Got unexpected dbg_hidden %#x.\n", dbg_hidden );
+
+    status = NtResumeThread( thread, NULL );
+    ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
+    WaitForSingleObject( thread, INFINITE );
+    CloseHandle( thread );
+
+    status = pNtCreateThreadEx( &thread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), test_NtCreateThreadEx_proc,
+                                NULL, THREAD_CREATE_FLAGS_CREATE_SUSPENDED | THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER,
+                                0, 0, 0, NULL );
+    ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
+
+    dbg_hidden = 0xcc;
+    status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dbg_hidden, sizeof(dbg_hidden), NULL );
+    ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
+    ok( dbg_hidden == 1, "Got unexpected dbg_hidden %#x.\n", dbg_hidden );
+
+    status = NtResumeThread( thread, NULL );
+    ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
+    WaitForSingleObject( thread, INFINITE );
+    CloseHandle( thread );
+
+    lstrcpyW( path, L"\\??\\" );
+    GetModuleFileNameW( NULL, path + 4, MAX_PATH );
+
+    RtlInitUnicodeString( &imageW, path );
+
+    memset( &ps_attr, 0, sizeof(ps_attr) );
+    ps_attr.Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME;
+    ps_attr.Attributes[0].Size = lstrlenW(path) * sizeof(WCHAR);
+    ps_attr.Attributes[0].ValuePtr = path;
+    ps_attr.TotalLength = sizeof(ps_attr);
+
+    status = RtlCreateProcessParametersEx( &params, &imageW, NULL, NULL,
+                                           NULL, NULL, NULL, NULL,
+                                           NULL, NULL, PROCESS_PARAMS_FLAG_NORMALIZED );
+    ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
+
+    /* NtCreateUserProcess() may return STATUS_INVALID_PARAMATER with some unitialized data in create_info. */
+    memset( &create_info, 0, sizeof(create_info) );
+    create_info.Size = sizeof(create_info);
+
+    status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS,
+                                  NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED
+                                  | THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER, params,
+                                  &create_info, &ps_attr );
+    ok( status == STATUS_INVALID_PARAMETER, "Got unexpected status %#x.\n", status );
+    status = NtCreateUserProcess( &process, &thread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS,
+                                  NULL, NULL, 0, THREAD_CREATE_FLAGS_CREATE_SUSPENDED, params,
+                                  &create_info, &ps_attr );
+    ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
+    status = NtTerminateProcess( process, 0 );
+    ok( status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status );
+    CloseHandle( process );
+    CloseHandle( thread );
+}
+
+START_TEST(thread)
+{
+    init_function_pointers();
+
+    test_dbg_hidden_thread_creation();
+}
diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c
index c834ef85c79..b2457905698 100644
--- a/dlls/ntdll/unix/process.c
+++ b/dlls/ntdll/unix/process.c
@@ -705,6 +705,16 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
     data_size_t handles_size, jobs_size;
     obj_handle_t *handles, *jobs;
 
+    if (thread_flags & THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER)
+    {
+        WARN( "Invalid thread flags %#x.\n", thread_flags );
+
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    if (thread_flags & ~THREAD_CREATE_FLAGS_CREATE_SUSPENDED)
+        FIXME( "Unsupported thread flags %#x.\n", thread_flags );
+
     for (i = 0; i < attr_count; i++)
     {
         switch (ps_attr->Attributes[i].Attribute)
@@ -845,7 +855,7 @@ NTSTATUS WINAPI NtCreateUserProcess( HANDLE *process_handle_ptr, HANDLE *thread_
     {
         req->process    = wine_server_obj_handle( process_handle );
         req->access     = thread_access;
-        req->suspend    = !!(thread_flags & THREAD_CREATE_FLAGS_CREATE_SUSPENDED);
+        req->flags      = thread_flags;
         req->request_fd = -1;
         wine_server_add_data( req, objattr, attr_len );
         if (!(status = wine_server_call( req )))
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c
index 455272e07e0..00fe2b6fa1b 100644
--- a/dlls/ntdll/unix/thread.c
+++ b/dlls/ntdll/unix/thread.c
@@ -1243,6 +1243,7 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT
                                   ULONG flags, ULONG_PTR zero_bits, SIZE_T stack_commit,
                                   SIZE_T stack_reserve, PS_ATTRIBUTE_LIST *attr_list )
 {
+    static const ULONG supported_flags = THREAD_CREATE_FLAGS_CREATE_SUSPENDED | THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER;
     sigset_t sigset;
     pthread_t pthread_id;
     pthread_attr_t pthread_attr;
@@ -1254,6 +1255,9 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT
     TEB *teb;
     NTSTATUS status;
 
+    if (flags & ~supported_flags)
+        FIXME( "Unsupported flags %#x.\n", flags );
+
     if (zero_bits > 21 && zero_bits < 32) return STATUS_INVALID_PARAMETER_3;
 #ifndef _WIN64
     if (!is_wow64 && zero_bits >= 32) return STATUS_INVALID_PARAMETER_3;
@@ -1303,7 +1307,7 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT
     {
         req->process    = wine_server_obj_handle( process );
         req->access     = access;
-        req->suspend    = flags & THREAD_CREATE_FLAGS_CREATE_SUSPENDED;
+        req->flags      = flags;
         req->request_fd = request_pipe[0];
         wine_server_add_data( req, objattr, len );
         if (!(status = wine_server_call( req )))
diff --git a/server/protocol.def b/server/protocol.def
index c83e6a2ef7c..15bda74def1 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -871,7 +871,7 @@ typedef struct
 @REQ(new_thread)
     obj_handle_t process;      /* process in which to create thread */
     unsigned int access;       /* wanted access rights */
-    int          suspend;      /* new thread should be suspended on creation */
+    int          flags;        /* thread creation flags */
     int          request_fd;   /* fd for request pipe */
     VARARG(objattr,object_attributes); /* object attributes */
 @REPLY
diff --git a/server/thread.c b/server/thread.c
index 55eb8513af8..e9240a05659 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -1355,7 +1355,8 @@ DECL_HANDLER(new_thread)
     if ((thread = create_thread( request_fd, process, sd )))
     {
         thread->system_regs = current->system_regs;
-        if (req->suspend) thread->suspend++;
+        if (req->flags & THREAD_CREATE_FLAGS_CREATE_SUSPENDED) thread->suspend++;
+        thread->dbg_hidden = !!(req->flags & THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER);
         reply->tid = get_thread_id( thread );
         if ((reply->handle = alloc_handle_no_access_check( current->process, thread,
                                                            req->access, objattr->attributes )))
-- 
2.33.1




More information about the wine-devel mailing list