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