ntdll: CreateRemoteThread and friends for remote processes, take 3, via APCs

Thomas Kho tkho at ucla.edu
Thu Aug 3 02:08:26 CDT 2006


ntdll: CreateRemoteThread and friends for remote processes, take 3, via APCs

Hello,

Much thanks for the comments on yesterday's patch of a service thread
implementation for remote process operations. This patch uses APCs to
implement CreateRemoteThread/RtlCreateUserThread,
VirtualAllocEx/NtAllocateVirtualMemory and VirtualQueryEx/NtQueryVirtualMemory.

APCs are queued per-thread, so I made minimal changes to add a per-process
APC queue. Of course, this implementation suffers from the limitation that the
remote process must go into an interuptible wait for the operation to succeed;
an operation targeting a suspended process would block.

Stefan Siebert mentioned that VirtualQueryEx is necessary for Lotus Notes
Software Diagnostics (nsd.exe). I successfully tested this function with a
simple program that prints a process' memory map:
http://www.codecomments.com/archive371-2005-3-421246.html

Lastly, I should mention that I have two pending patches to add conformance
tests for remote processes operations:
http://www.winehq.org/pipermail/wine-patches/2006-July/029259.html
http://www.winehq.org/pipermail/wine-patches/2006-July/029260.html

Thanks,

Thomas Kho

---

 include/wine/server_protocol.h |    2 +-
 server/process.c               |    1 +
 server/process.h               |    1 +
 server/thread.c                |   50 ++++++++++++++++++++++++----------------
 server/thread.h                |   23 +++++++++++++++++-
 server/timer.c                 |    2 +-
 6 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/server/process.c b/server/process.c
index 3e5ef97..75ca490 100644
--- a/server/process.c
+++ b/server/process.c
@@ -254,6 +254,7 @@ struct thread *create_process( int fd, s
     list_init( &process->locks );
     list_init( &process->classes );
     list_init( &process->dlls );
+    list_init( &process->system_apc );
 
     gettimeofday( &process->start_time, NULL );
     process->end_time.tv_sec = process->end_time.tv_usec = 0;
diff --git a/server/process.h b/server/process.h
index 6edb1e6..2e9c9b8 100644
--- a/server/process.h
+++ b/server/process.h
@@ -78,6 +78,7 @@ struct process
     struct list          dlls;            /* list of loaded dlls */
     void                *peb;             /* PEB address in client address space */
     void                *ldt_copy;        /* pointer to LDT copy in client addr space */
+    struct list          system_apc;      /* queue of system aync procedure calls */
 };
 
 struct process_snapshot
diff --git a/server/thread.c b/server/thread.c
index 60b74c9..d21df7f 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -64,19 +64,6 @@ struct thread_wait
     struct wait_queue_entry queues[1];
 };
 
-/* asynchronous procedure calls */
-
-struct thread_apc
-{
-    struct list         entry;    /* queue linked list */
-    struct object      *owner;    /* object that queued this apc */
-    void               *func;     /* function to call in client */
-    enum apc_type       type;     /* type of apc function */
-    void               *arg1;     /* function arguments */
-    void               *arg2;
-    void               *arg3;
-};
-
 
 /* thread operations */
 
@@ -464,6 +451,8 @@ static int check_wait( struct thread *th
     }
 
  other_checks:
+    /* FIXME remote operations will block on suspended threads */
+    if ((wait->flags & SELECT_INTERRUPTIBLE) && !list_empty(&thread->process->system_apc)) return STATUS_USER_APC;
     if ((wait->flags & SELECT_INTERRUPTIBLE) && !list_empty(&thread->system_apc)) return STATUS_USER_APC;
     if ((wait->flags & SELECT_ALERTABLE) && !list_empty(&thread->user_apc)) return STATUS_USER_APC;
     if (wait->flags & SELECT_TIMEOUT)
@@ -622,15 +611,32 @@ void wake_up( struct object *obj, int ma
     }
 }
 
+/* return a pointer to the correct queue */
+static struct list *get_apc_queue( struct thread *thread,
+                                   enum apc_queue apc_queue )
+{
+    switch (apc_queue)
+    {
+        case APC_PROCESS_QUEUE_SYSTEM:
+            return &thread->process->system_apc;
+        case APC_THREAD_QUEUE_SYSTEM:
+            return &thread->system_apc;
+        case APC_THREAD_QUEUE_USER:
+        default:
+            return &thread->user_apc;
+    }
+}
+
 /* queue an async procedure call */
 int thread_queue_apc( struct thread *thread, struct object *owner, void *func,
-                      enum apc_type type, int system, void *arg1, void *arg2, void *arg3 )
+                      enum apc_type type, enum apc_queue apc_queue, void *arg1,
+                      void *arg2, void *arg3 )
 {
     struct thread_apc *apc;
-    struct list *queue = system ? &thread->system_apc : &thread->user_apc;
+    struct list *queue = get_apc_queue( thread, apc_queue );
 
     /* cancel a possible previous APC with the same owner */
-    if (owner) thread_cancel_apc( thread, owner, system );
+    if (owner) thread_cancel_apc( thread, owner, apc_queue );
     if (thread->state == TERMINATED) return 0;
 
     if (!(apc = mem_alloc( sizeof(*apc) ))) return 0;
@@ -648,10 +654,11 @@ int thread_queue_apc( struct thread *thr
 }
 
 /* cancel the async procedure call owned by a specific object */
-void thread_cancel_apc( struct thread *thread, struct object *owner, int system )
+void thread_cancel_apc( struct thread *thread, struct object *owner,
+                        enum apc_queue apc_queue )
 {
     struct thread_apc *apc;
-    struct list *queue = system ? &thread->system_apc : &thread->user_apc;
+    struct list *queue = get_apc_queue( thread, apc_queue );
     LIST_FOR_EACH_ENTRY( apc, queue, struct thread_apc, entry )
     {
         if (apc->owner != owner) continue;
@@ -665,8 +672,9 @@ void thread_cancel_apc( struct thread *t
 static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system_only )
 {
     struct thread_apc *apc = NULL;
-    struct list *ptr = list_head( &thread->system_apc );
+    struct list *ptr = list_head( &thread->process->system_apc );
 
+    if (!ptr) ptr = list_head( &thread->system_apc );
     if (!ptr && !system_only) ptr = list_head( &thread->user_apc );
     if (ptr)
     {
@@ -1007,7 +1015,9 @@ DECL_HANDLER(queue_apc)
     struct thread *thread;
     if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT )))
     {
-        thread_queue_apc( thread, NULL, req->func, APC_USER, !req->user,
+        enum apc_queue queue;
+        queue = req->user ? APC_THREAD_QUEUE_USER : APC_THREAD_QUEUE_SYSTEM;
+        thread_queue_apc( thread, NULL, req->func, APC_USER, queue,
                           req->arg1, req->arg2, req->arg3 );
         release_object( thread );
     }
diff --git a/server/thread.h b/server/thread.h
index 61911cd..b8b6cf0 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -96,6 +96,25 @@ struct thread_snapshot
     int             priority;  /* priority class */
 };
 
+/* asynchronous procedure calls */
+struct thread_apc
+{
+    struct list         entry;    /* queue linked list */
+    struct object      *owner;    /* object that queued this apc */
+    void               *func;     /* function to call in client */
+    enum apc_type       type;     /* type of apc function */
+    void               *arg1;     /* function arguments */
+    void               *arg2;
+    void               *arg3;
+};
+
+enum apc_queue
+{
+    APC_THREAD_QUEUE_USER,
+    APC_THREAD_QUEUE_SYSTEM,
+    APC_PROCESS_QUEUE_SYSTEM
+};
+
 extern struct thread *current;
 
 /* thread functions */
@@ -112,8 +131,8 @@ extern void kill_thread( struct thread *
 extern void break_thread( struct thread *thread );
 extern void wake_up( struct object *obj, int max );
 extern int thread_queue_apc( struct thread *thread, struct object *owner, void *func,
-                             enum apc_type type, int system, void *arg1, void *arg2, void *arg3 );
-extern void thread_cancel_apc( struct thread *thread, struct object *owner, int system );
+                             enum apc_type type, enum apc_queue apc_queue, void *arg1, void *arg2, void *arg3 );
+extern void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_queue apc_queue );
 extern int thread_add_inflight_fd( struct thread *thread, int client, int server );
 extern int thread_get_inflight_fd( struct thread *thread, int client );
 extern struct thread_snapshot *thread_snap( int *count );
diff --git a/server/timer.c b/server/timer.c
index d1c035b..f6e23c8 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -136,7 +136,7 @@ static int cancel_timer( struct timer *t
     }
     if (timer->thread)
     {
-        thread_cancel_apc( timer->thread, &timer->obj, 0 );
+        thread_cancel_apc( timer->thread, &timer->obj, APC_THREAD_QUEUE_USER );
         release_object( timer->thread );
         timer->thread = NULL;
     }

---

 dlls/kernel/kernel32.spec      |    3 +
 dlls/kernel/process.c          |  108 ++++++++++++++++++++++++++++++++++++++++
 dlls/kernel/thread.c           |   24 +++++++--
 dlls/ntdll/ntdll_misc.h        |    2 +
 dlls/ntdll/thread.c            |   85 +++++++++++++++++++++++++++++++
 dlls/ntdll/virtual.c           |   37 ++++++++++++--
 include/wine/server_protocol.h |   31 +++++++++++
 include/winternl.h             |   58 +++++++++++++++++++++
 server/process.c               |   67 +++++++++++++++++++++++++
 server/protocol.def            |   21 ++++++++
 server/request.h               |    2 +
 server/trace.c                 |   12 ++++
 12 files changed, 436 insertions(+), 14 deletions(-)

diff --git a/dlls/kernel/kernel32.spec b/dlls/kernel/kernel32.spec
index 7369b59..80fcba4 100644
--- a/dlls/kernel/kernel32.spec
+++ b/dlls/kernel/kernel32.spec
@@ -1239,3 +1239,6 @@ # Unix files
 
 # Init code
 @ cdecl __wine_kernel_init()
+
+# Remote process operations
+@ cdecl __wine_RemoteProcessOperation(ptr ptr ptr)
diff --git a/dlls/kernel/process.c b/dlls/kernel/process.c
index 7092d2c..07fa4ef 100644
--- a/dlls/kernel/process.c
+++ b/dlls/kernel/process.c
@@ -2827,3 +2827,111 @@ BOOL WINAPI CmdBatNotification( BOOL bBa
     FIXME("%d\n", bBatchRunning);
     return FALSE;
 }
+
+/***********************************************************************
+ *      __wine_RemoteProcessOperation   (KERNEL32.@) Not a Windows API
+ *
+ * Execute a remote operation.
+ * arg1 is an all-access handle to the requesting process.
+ * arg2 is a pointer to the RemoteOp structure in the requesting process.
+ * arg3 is an event to notify the requesting process of completion.
+ */
+void WINAPI CALLBACK __wine_RemoteProcessOperation( ULONG_PTR arg1,
+                                                    ULONG_PTR arg2,
+                                                    ULONG_PTR arg3 )
+{
+    HANDLE hProcess = (HANDLE) arg1;
+    void *args = (void *) arg2;
+    HANDLE hEvent = (HANDLE) arg3;
+    struct RemoteOp ro;
+
+    NtReadVirtualMemory( hProcess, args, &ro, sizeof(ro), NULL );
+
+    switch( ro.op )
+    {
+      case RO_NEW_THREAD:
+      {
+        CLIENT_ID cidLocal;
+        HANDLE hThread;
+        NTSTATUS ret;
+
+        ro.status = RtlCreateUserThread( NtCurrentProcess(), NULL,
+                                         ro.thread.suspended,
+                                         ro.thread.stack_addr,
+                                         ro.thread.stack_reserve,
+                                         ro.thread.stack_commit,
+                                         ro.thread.start,
+                                         ro.thread.param,
+                                         ro.thread.handle_ptr ? &hThread : 0,
+                                         ro.thread.id_ptr ? &cidLocal : 0);
+        if (ro.status) break;
+
+        if (ro.thread.handle_ptr)
+        {
+            HANDLE hRemoteNewThread;
+            if ((ret = NtDuplicateObject( NtCurrentProcess(), hThread,
+                                          hProcess, &hRemoteNewThread, 0, 0,
+                                          DUPLICATE_SAME_ACCESS|
+                                          DUPLICATE_CLOSE_SOURCE )))
+                ERR("Cannot duplicate handle in remote process, error %lu\n",
+                    ret);
+            else
+            {
+                if ((ret = NtWriteVirtualMemory( hProcess,
+                                                 ro.thread.handle_ptr,
+                                                 &hRemoteNewThread,
+                                                 sizeof(HANDLE), NULL )))
+                    ERR("Cannot write back handle, error %lu\n", ret);
+            }
+        }
+        if (ro.thread.id_ptr)
+            if ((ret = NtWriteVirtualMemory( hProcess, ro.thread.id_ptr,
+                                             &cidLocal, sizeof(CLIENT_ID),
+                                             NULL )))
+                ERR("Cannot write back thread id, error %lu\n", ret);
+
+        break;
+      }
+      case RO_ALLOCATE:
+      {
+        ro.status = NtAllocateVirtualMemory( NtCurrentProcess(),
+                                             &ro.alloc.ret,
+                                             ro.alloc.zero_bits,
+                                             &ro.alloc.size,
+                                             ro.alloc.type,
+                                             ro.alloc.protect );
+        break;
+      }
+      case RO_QUERY:
+      {
+        void *res;
+        NTSTATUS ret;
+        SIZE_T res_len;
+
+        if (!(res = HeapAlloc( GetProcessHeap(), 0, ro.query.len )))
+        {
+            ro.status = STATUS_NO_MEMORY;
+            break;
+        }
+        ro.status = NtQueryVirtualMemory( NtCurrentProcess(), ro.query.addr,
+                                          ro.query.info_class, res,
+                                          ro.query.len, &res_len );
+        if (ro.query.res_len)
+            if ((ret = NtWriteVirtualMemory( hProcess, ro.query.res_len,
+                                             &res_len, sizeof(SIZE_T), NULL )))
+                ERR("Cannot write back thread id, error %lu\n", ret);
+        if (ro.query.buffer)
+            if ((ret = NtWriteVirtualMemory( hProcess, ro.query.buffer,
+                                             res, ro.query.len, NULL )))
+                ERR("Cannot write back thread id, error %lu\n", ret);
+        HeapFree( GetProcessHeap(), 0, res );
+      }
+    }
+
+    /* FIXME optimize write back */
+    NtWriteVirtualMemory( hProcess, args, &ro, sizeof(ro), NULL );
+
+    NtSetEvent( hEvent, NULL );
+    NtClose( hEvent );
+    NtClose( hProcess );
+}
diff --git a/dlls/kernel/thread.c b/dlls/kernel/thread.c
index bf29aac..bfa8ef4 100644
--- a/dlls/kernel/thread.c
+++ b/dlls/kernel/thread.c
@@ -66,7 +66,7 @@ static void CALLBACK THREAD_Start( void 
     LPTHREAD_START_ROUTINE func = info->func;
     void *arg = info->arg;
 
-    RtlFreeHeap( GetProcessHeap(), 0, info );
+    VirtualFreeEx( GetCurrentProcess(), info, 0, MEM_RELEASE );
 
     if (TRACE_ON(relay))
         DPRINTF("%04lx:Starting thread (entryproc=%p)\n", GetCurrentThreadId(), func );
@@ -122,13 +122,25 @@ HANDLE WINAPI CreateRemoteThread( HANDLE
     SIZE_T stack_reserve = 0, stack_commit = 0;
     struct new_thread_info *info;
 
-    if (!(info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*info) )))
+    if (!(info = VirtualAllocEx( hProcess, NULL, sizeof(*info),
+                                 MEM_COMMIT|MEM_RESERVE,
+                                 PAGE_EXECUTE_READWRITE )))
     {
         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
         return 0;
     }
-    info->func = start;
-    info->arg  = param;
+
+    /* FIXME sub with is_current_process() in ntdll */
+    if (hProcess != GetCurrentProcess())
+    {
+        struct new_thread_info nti = { start, param };
+        WriteProcessMemory( hProcess, info, &nti, sizeof(nti), NULL );
+    }
+    else
+    {
+        info->func = start;
+        info->arg  = param;
+    }
 
     if (flags & STACK_SIZE_PARAM_IS_A_RESERVATION) stack_reserve = stack;
     else stack_commit = stack;
@@ -147,7 +159,7 @@ HANDLE WINAPI CreateRemoteThread( HANDLE
             if (NtResumeThread( handle, &ret ))
             {
                 NtClose( handle );
-                RtlFreeHeap( GetProcessHeap(), 0, info );
+                VirtualFreeEx( hProcess, info, 0, MEM_RELEASE );
                 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
                 handle = 0;
             }
@@ -155,7 +167,7 @@ HANDLE WINAPI CreateRemoteThread( HANDLE
     }
     else
     {
-        RtlFreeHeap( GetProcessHeap(), 0, info );
+        VirtualFreeEx( hProcess, info, 0, MEM_RELEASE );
         SetLastError( RtlNtStatusToDosError(status) );
         handle = 0;
     }
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index 03110a4..cdf6c63 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -61,6 +61,8 @@ extern void DECLSPEC_NORETURN server_pro
 extern void DECLSPEC_NORETURN server_protocol_perror( const char *err );
 extern void DECLSPEC_NORETURN server_exit_thread( int status );
 extern void DECLSPEC_NORETURN server_abort_thread( int status );
+extern NTSTATUS NTDLL_remote_call( HANDLE process, struct RemoteOp *ro );
+
 
 /* module handling */
 extern NTSTATUS MODULE_DllThreadAttach( LPVOID lpReserved );
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index dbcc5a6..94c146b 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -385,6 +385,73 @@ static void start_thread( struct wine_pt
     func( arg );
 }
 
+/***********************************************************************
+ *              get_remote_call_handler
+ */
+static NTSTATUS get_remote_call_handler( void **func )
+{
+    static void *handler = NULL;
+    HMODULE hkernel32;
+    UNICODE_STRING module;
+    ANSI_STRING function;
+    WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2','.','d','l','l',0};
+    NTSTATUS ret;
+
+    if (handler != NULL)
+    {
+        *func = handler;
+        return STATUS_SUCCESS;
+    }
+
+    RtlInitUnicodeString(&module, kernel32W);
+    RtlInitAnsiString(&function, "__wine_RemoteProcessOperation");
+    if ((ret = LdrGetDllHandle(0, 0, &module, &hkernel32)))
+        return ret;
+    if ((ret = LdrGetProcedureAddress(hkernel32, &function, 0, &handler)))
+        return ret;
+    *func = handler;
+    return STATUS_SUCCESS;
+}
+
+/***********************************************************************
+ *              NTDLL_remote_call
+ */
+NTSTATUS NTDLL_remote_call( HANDLE process, struct RemoteOp *ro )
+{
+    NTSTATUS ret;
+    HANDLE done_event;
+    void *func;
+
+    /* FIXME check this */
+    if ((ret = NtCreateEvent(&done_event, EVENT_ALL_ACCESS, NULL, FALSE,
+                             FALSE)))
+        return ret;
+
+    if ((ret = get_remote_call_handler(&func)))
+    {
+        ERR("Cannot locate remote call handler\n");
+        return ret;
+    }
+
+    SERVER_START_REQ( remote_op )
+    {
+        req->handle = process;
+        req->op = ro->op;
+        req->event = done_event;
+        req->args = ro;
+        req->func = func;
+
+        ret = wine_server_call( req );
+    }
+    SERVER_END_REQ;
+
+    if (ret) return ret;
+
+    NtWaitForSingleObject( done_event, FALSE, NULL );
+    NtClose( done_event );
+
+    return STATUS_SUCCESS;
+}
 
 /***********************************************************************
  *              RtlCreateUserThread   (NTDLL.@)
@@ -408,8 +475,22 @@ NTSTATUS WINAPI RtlCreateUserThread( HAN
 
     if( ! is_current_process( process ) )
     {
-        ERR("Unsupported on other process\n");
-        return STATUS_ACCESS_DENIED;
+        struct RemoteOp ro;
+
+        ro.op = RO_NEW_THREAD;
+        ro.thread.suspended = suspended;
+        ro.thread.stack_addr = stack_addr;
+        ro.thread.stack_reserve = stack_reserve;
+        ro.thread.stack_commit = stack_commit;
+        ro.thread.start = start;
+        ro.thread.param = param;
+        ro.thread.handle_ptr = handle_ptr;
+        ro.thread.id_ptr = id;
+
+        if ((status = NTDLL_remote_call( process, &ro )))
+            return status;
+
+        return ro.status;
     }
 
     if (pipe( request_pipe ) == -1) return STATUS_TOO_MANY_OPENED_FILES;
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c
index f7b829f..ee6a196 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -1317,8 +1317,25 @@ NTSTATUS WINAPI NtAllocateVirtualMemory(
 
     if (!is_current_process( process ))
     {
-        ERR("Unsupported on other process\n");
-        return STATUS_ACCESS_DENIED;
+        struct RemoteOp ro;
+
+        ro.op = RO_ALLOCATE;
+        ro.alloc.ret = *ret;
+        ro.alloc.zero_bits = zero_bits;
+        ro.alloc.size = *size_ptr;
+        ro.alloc.type = type;
+        ro.alloc.protect = protect;
+
+        if ((status = NTDLL_remote_call( process, &ro )))
+            return status;
+
+        TRACE("NtAllocateVirtualMemory addr=%x, status=%x\n", ro.alloc.ret,
+              (unsigned) status);
+
+        *ret = ro.alloc.ret;
+        *size_ptr = ro.alloc.size;
+
+        return ro.status;
     }
 
     /* Round parameters to a page boundary */
@@ -1578,8 +1595,20 @@ NTSTATUS WINAPI NtQueryVirtualMemory( HA
 
     if (!is_current_process( process ))
     {
-        ERR("Unsupported on other process\n");
-        return STATUS_ACCESS_DENIED;
+        struct RemoteOp ro;
+        NTSTATUS status;
+
+        ro.op = RO_QUERY;
+        ro.query.addr = (void *) addr;
+        ro.query.info_class = info_class;
+        ro.query.buffer = buffer;
+        ro.query.len = len;
+        ro.query.res_len = res_len;
+
+        if ((status = NTDLL_remote_call( process, &ro )))
+            return status;
+
+        return ro.status;
     }
 
     base = ROUND_ADDR( addr, page_mask );
diff --git a/include/winternl.h b/include/winternl.h
index 5177348..4d92e42 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -2243,7 +2243,6 @@ extern NTSTATUS wine_nt_to_unix_file_nam
                                            UINT disposition, BOOLEAN check_case );
 extern NTSTATUS wine_unix_to_nt_file_name( const ANSI_STRING *name, UNICODE_STRING *nt );
 
-
 /***********************************************************************
  * Inline functions
  */
@@ -2396,6 +2395,63 @@ static inline PLIST_ENTRY RemoveTailList
     return e;
 }
 
+struct RemoteOp {
+    int op; /* in, FIXME enum remote_op */
+    NTSTATUS status; /* out */
+
+    union {
+        struct {
+            /* in */
+            ULONG zero_bits;
+            ULONG type;
+            ULONG protect;
+            /* in/out */
+            PVOID ret;
+            SIZE_T size;
+        } alloc;
+        struct {
+            PVOID addr;
+            SIZE_T size;
+            ULONG type;
+        } free;
+        struct {
+            PVOID addr;
+            SIZE_T size;
+            ULONG new_prot;
+            ULONG old_prot;
+        } protect;
+        struct {
+            LPVOID addr;
+            MEMORY_INFORMATION_CLASS info_class;
+            PVOID buffer;
+            SIZE_T len;
+            SIZE_T *res_len;
+        } query;
+        struct {
+            PVOID addr;
+            SIZE_T size;
+            ULONG unknown;
+        } lock;
+        struct {
+            PVOID addr;
+            SIZE_T size;
+            ULONG unknown;
+        } unlock;
+        struct {
+            /* in */
+            BOOLEAN suspended;
+            PVOID stack_addr;
+            SIZE_T stack_reserve;
+            SIZE_T stack_commit;
+            PRTL_THREAD_START_ROUTINE start;
+            void *param;
+            /* out */
+            HANDLE *handle_ptr;
+            CLIENT_ID *id_ptr;
+        } thread;
+    };
+};
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/server/process.c b/server/process.c
index 75ca490..6ecc7b7 100644
--- a/server/process.c
+++ b/server/process.c
@@ -1056,3 +1056,70 @@ DECL_HANDLER(get_process_idle_event)
         release_object( process );
     }
 }
+
+/* Signal a remote operation it */
+DECL_HANDLER(remote_op)
+{
+    int access;
+    struct process *src = NULL, *dst = NULL;
+    struct thread *thread;
+    obj_handle_t dupevent = NULL, dupprocess = NULL;
+
+    switch ( req->op )
+    {
+        case RO_NEW_THREAD:
+            access = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION
+                     | PROCESS_VM_OPERATION | PROCESS_VM_READ
+                     | PROCESS_VM_WRITE;
+            break;
+        case RO_ALLOCATE:
+        case RO_FREE:
+        case RO_PROTECT:
+        case RO_LOCK: /* FIXME check */
+        case RO_UNLOCK: /* FIXME check */
+            access = PROCESS_VM_OPERATION;
+            break;
+        case RO_QUERY:
+            access = PROCESS_QUERY_INFORMATION;
+            break;
+        default:
+            set_error( STATUS_INVALID_PARAMETER );
+            return;
+    }
+
+    if (!(src = get_process_from_handle( (obj_handle_t) 0xffffffff, 0 )))
+        goto cleanup;
+    if (!(dst = get_process_from_handle( req->handle, access )))
+        goto cleanup;
+    if (!(thread = get_process_first_thread( dst )))
+    {
+        /* FIXME check if this can even happen */
+        set_error( STATUS_ACCESS_DENIED );
+        goto cleanup;
+    }
+
+    /* duplicate done event handle */
+    dupevent = duplicate_handle(src, req->event, dst, 0, 0,
+                                DUPLICATE_SAME_ACCESS);
+    if (!dupevent) goto cleanup;
+    /* duplicate an all-access handle to requesting process */
+    dupprocess = duplicate_handle(src, (obj_handle_t) 0xffffffff, dst, 0, 0,
+                                  DUPLICATE_SAME_ACCESS);
+    if (!dupprocess)
+    {
+        close_handle( dst, dupevent, 0 );
+        goto cleanup;
+    }
+
+    /* queue remote_op on the remote process' apc queue */
+    if (!thread_queue_apc( thread, NULL, req->func, APC_USER,
+                           APC_PROCESS_QUEUE_SYSTEM,
+                           dupprocess, req->args, dupevent ))
+        set_error( STATUS_INVALID_HANDLE );
+
+cleanup:
+    if (src)
+        release_object( src );
+    if (dst)
+        release_object( dst );
+}
diff --git a/server/protocol.def b/server/protocol.def
index e64cf43..98671ae 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2618,3 +2618,24 @@ #define MAILSLOT_SET_READ_TIMEOUT  1
 @REPLY
     VARARG(target_name,unicode_str); /* target name */
 @END
+
+
+/* Signal a remote operation */
+ at REQ(remote_op)
+    obj_handle_t handle;       /* handle to target process */
+    int          op;           /* remote operation */
+    obj_handle_t event;        /* completion event */
+    void*        args;         /* pointer to arguments in requesting process */
+    void*        func;         /* pointer to remote op handler in kernel32 */
+ at REPLY
+ at END
+enum remote_op
+{
+    RO_NEW_THREAD,
+    RO_ALLOCATE,
+    RO_FREE,
+    RO_LOCK,
+    RO_PROTECT,
+    RO_QUERY,
+    RO_UNLOCK
+};



More information about the wine-devel mailing list