[5/5] ntdll: add remote process operations (update 2)
Thomas Kho
tkho at ucla.edu
Fri Aug 18 03:25:59 CDT 2006
[5/5] ntdll: add remote process operations (update 2)
This patch adds support for CreateRemoteThread, RtlCreateUserThread,
NtAllocateVirtualMemory, NtFreeVirtualMemory and NtQueryVirtualMemory in
another process.
This patch was updated to address an issue where a stop signal was not being
delivered when it was intended to.
Thomas Kho
Copyright 2006 Google (Thomas Kho)
License: LGPL
---
dlls/kernel/tests/thread.c | 37 ++++-----
dlls/kernel/tests/virtual.c | 66 +++++++---------
dlls/kernel/thread.c | 64 ++++++++++++++--
dlls/ntdll/ntdll_misc.h | 6 +
dlls/ntdll/process.c | 162 ++++++++++++++++++++++++++++++++++++++++
dlls/ntdll/server.c | 12 +++
dlls/ntdll/thread.c | 48 +++++++++++-
dlls/ntdll/virtual.c | 56 ++++++++++++--
include/wine/server_protocol.h | 29 +++++++
include/winternl.h | 58 ++++++++++++++
server/process.c | 82 ++++++++++++++++++++
server/process.h | 1
server/protocol.def | 19 +++++
server/request.h | 2
server/thread.c | 14 +++
server/trace.c | 12 +++
16 files changed, 591 insertions(+), 77 deletions(-)
diff --git a/dlls/kernel/tests/thread.c b/dlls/kernel/tests/thread.c
index 0c97bf2..8bd66d9 100644
--- a/dlls/kernel/tests/thread.c
+++ b/dlls/kernel/tests/thread.c
@@ -210,48 +210,43 @@ static VOID test_CreateRemoteThread(void
/* create suspended remote thread with entry point SetEvent() */
hThread = CreateRemoteThread(hProcess, NULL, 0, threadFunc_SetEvent,
hRemoteEvent, CREATE_SUSPENDED, &tid);
- todo_wine ok(hThread != NULL, "CreateRemoteThread failed, err=%lu\n",
- GetLastError());
+ ok(hThread != NULL, "CreateRemoteThread failed, err=%lu\n", GetLastError());
ok(tid != 0, "null tid\n");
ret = SuspendThread(hThread);
- todo_wine ok(ret == 1, "ret=%lu, err=%lu\n", ret, GetLastError());
+ ok(ret == 1, "ret=%lu, err=%lu\n", ret, GetLastError());
ret = ResumeThread(hThread);
- todo_wine ok(ret == 2, "ret=%lu, err=%lu\n", ret, GetLastError());
+ ok(ret == 2, "ret=%lu, err=%lu\n", ret, GetLastError());
/* thread still suspended, so wait times out */
ret = WaitForSingleObject(hEvent, 100);
ok(ret == WAIT_TIMEOUT, "wait did not time out, ret=%lu\n", ret);
ret = ResumeThread(hThread);
- todo_wine ok(ret == 1, "ret=%lu, err=%lu\n", ret, GetLastError());
+ ok(ret == 1, "ret=%lu, err=%lu\n", ret, GetLastError());
/* wait that doesn't time out */
ret = WaitForSingleObject(hEvent, 100);
- todo_wine ok(ret == WAIT_OBJECT_0, "object not signaled, ret=%lu\n", ret);
+ ok(ret == WAIT_OBJECT_0, "object not signaled, ret=%lu\n", ret);
/* wait for thread end */
ret = WaitForSingleObject(hThread, 100);
- todo_wine ok(ret == WAIT_OBJECT_0,
- "waiting for thread failed, ret=%lu\n", ret);
+ ok(ret == WAIT_OBJECT_0, "waiting for thread failed, ret=%lu\n", ret);
CloseHandle(hThread);
/* create and wait for remote thread with entry point CloseHandle() */
hThread = CreateRemoteThread(hProcess, NULL, 0,
threadFunc_CloseHandle,
hRemoteEvent, 0, &tid);
- todo_wine ok(hThread != NULL,
- "CreateRemoteThread failed, err=%lu\n", GetLastError());
+ ok(hThread != NULL, "CreateRemoteThread failed, err=%lu\n", GetLastError());
ret = WaitForSingleObject(hThread, 100);
- todo_wine ok(ret == WAIT_OBJECT_0,
- "waiting for thread failed, ret=%lu\n", ret);
+ ok(ret == WAIT_OBJECT_0, "waiting for thread failed, ret=%lu\n", ret);
CloseHandle(hThread);
/* create remote thread with entry point SetEvent() */
hThread = CreateRemoteThread(hProcess, NULL, 0,
threadFunc_SetEvent,
hRemoteEvent, 0, &tid);
- todo_wine ok(hThread != NULL,
- "CreateRemoteThread failed, err=%lu\n", GetLastError());
+ ok(hThread != NULL, "CreateRemoteThread failed, err=%lu\n", GetLastError());
/* closed handle, so wait times out */
ret = WaitForSingleObject(hEvent, 100);
@@ -259,10 +254,8 @@ static VOID test_CreateRemoteThread(void
/* check that remote SetEvent() failed */
ret = GetExitCodeThread(hThread, &exitcode);
- todo_wine ok(ret != 0,
- "GetExitCodeThread failed, err=%lu\n", GetLastError());
- todo_wine ok(exitcode == 0,
- "SetEvent succeeded, expected to fail\n");
+ ok(ret != 0, "GetExitCodeThread failed, err=%lu\n", GetLastError());
+ ok(exitcode == 0, "SetEvent succeeded, expected to fail\n");
CloseHandle(hThread);
TerminateProcess(hProcess, 0);
@@ -277,10 +270,10 @@ static DWORD WINAPI create_remote_thread
HANDLE hThread;
hThread = CreateRemoteThread(hStressTestProcess, NULL, 0,
threadFunc2, NULL, 0, NULL);
- todo_wine ok(hThread != NULL, "CreateRemoteThread failed, error %lu\n",
- GetLastError());
- todo_wine ok(WaitForSingleObject(hThread, 200) == WAIT_OBJECT_0,
- "Remote thread did not exit in time\n");
+ ok(hThread != NULL, "CreateRemoteThread failed, error %lu\n",
+ GetLastError());
+ ok(WaitForSingleObject(hThread, 200) == WAIT_OBJECT_0,
+ "Remote thread did not exit in time\n");
if (hThread == NULL) break;
CloseHandle(hThread);
}
diff --git a/dlls/kernel/tests/virtual.c b/dlls/kernel/tests/virtual.c
index 75dd0d6..ca26511 100644
--- a/dlls/kernel/tests/virtual.c
+++ b/dlls/kernel/tests/virtual.c
@@ -107,7 +107,7 @@ static void test_VirtualAllocEx(void)
addr1 = VirtualAllocEx(hProcess, NULL, alloc_size, MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
- todo_wine ok(addr1 != NULL, "VirtualAllocEx error %lu\n", GetLastError());
+ ok(addr1 != NULL, "VirtualAllocEx error %lu\n", GetLastError());
b = WriteProcessMemory(hProcess, addr1, src, alloc_size, &bytes_written);
ok(b && (bytes_written == alloc_size), "%lu bytes written\n",
bytes_written);
@@ -115,7 +115,7 @@ static void test_VirtualAllocEx(void)
ok(b && (bytes_read == alloc_size), "%lu bytes read\n", bytes_read);
ok(!memcmp(src, dst, alloc_size), "Data from remote process differs\n");
b = VirtualFreeEx(hProcess, addr1, 0, MEM_RELEASE);
- todo_wine ok(b != 0, "VirtualFreeEx, error %lu\n", GetLastError());
+ ok(b != 0, "VirtualFreeEx, error %lu\n", GetLastError());
HeapFree( GetProcessHeap(), 0, src );
HeapFree( GetProcessHeap(), 0, dst );
@@ -132,26 +132,23 @@ static void test_VirtualAllocEx(void)
"got %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
addr1 = VirtualAllocEx(hProcess, 0, 0xFFFC, MEM_RESERVE, PAGE_NOACCESS);
- todo_wine ok(addr1 != NULL, "VirtualAllocEx failed\n");
+ ok(addr1 != NULL, "VirtualAllocEx failed\n");
/* test a not committed memory */
memset(&info, 'q', sizeof(info));
- todo_wine ok(VirtualQueryEx(hProcess, addr1, &info, sizeof(info))
+ ok(VirtualQueryEx(hProcess, addr1, &info, sizeof(info))
== sizeof(info), "VirtualQueryEx failed\n");
- todo_wine ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress,
- addr1);
- todo_wine ok(info.AllocationBase == addr1, "%p != %p\n",
- info.AllocationBase, addr1);
- todo_wine ok(info.AllocationProtect == PAGE_NOACCESS,
+ ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress, addr1);
+ ok(info.AllocationBase == addr1, "%p != %p\n", info.AllocationBase, addr1);
+ ok(info.AllocationProtect == PAGE_NOACCESS,
"%lx != PAGE_NOACCESS\n", info.AllocationProtect);
- todo_wine ok(info.RegionSize == 0x10000, "%lx != 0x10000\n",
- info.RegionSize);
- todo_wine ok(info.State == MEM_RESERVE, "%lx != MEM_RESERVE\n", info.State);
+ ok(info.RegionSize == 0x10000, "%lx != 0x10000\n", info.RegionSize);
+ ok(info.State == MEM_RESERVE, "%lx != MEM_RESERVE\n", info.State);
/* NT reports Protect == 0 for a not committed memory block */
- todo_wine ok(info.Protect == 0 /* NT */ ||
+ ok(info.Protect == 0 /* NT */ ||
info.Protect == PAGE_NOACCESS, /* Win9x */
"%lx != PAGE_NOACCESS\n", info.Protect);
- todo_wine ok(info.Type == MEM_PRIVATE, "%lx != MEM_PRIVATE\n", info.Type);
+ ok(info.Type == MEM_PRIVATE, "%lx != MEM_PRIVATE\n", info.Type);
SetLastError(0xdeadbeef);
ok(!VirtualProtectEx(hProcess, addr1, 0xFFFC, PAGE_READONLY, &old_prot),
@@ -164,21 +161,17 @@ static void test_VirtualAllocEx(void)
ok(addr1 == addr2, "VirtualAllocEx failed\n");
/* test a committed memory */
- todo_wine ok(VirtualQueryEx(hProcess, addr1, &info, sizeof(info))
- == sizeof(info),
- "VirtualQueryEx failed\n");
- todo_wine ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress,
- addr1);
- todo_wine ok(info.AllocationBase == addr1, "%p != %p\n",
- info.AllocationBase, addr1);
- todo_wine ok(info.AllocationProtect == PAGE_NOACCESS,
- "%lx != PAGE_NOACCESS\n", info.AllocationProtect);
- todo_wine ok(info.RegionSize == 0x1000, "%lx != 0x1000\n", info.RegionSize);
- todo_wine ok(info.State == MEM_COMMIT, "%lx != MEM_COMMIT\n", info.State);
+ ok(VirtualQueryEx(hProcess, addr1, &info, sizeof(info)) == sizeof(info),
+ "VirtualQueryEx failed\n");
+ ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress, addr1);
+ ok(info.AllocationBase == addr1, "%p != %p\n", info.AllocationBase, addr1);
+ ok(info.AllocationProtect == PAGE_NOACCESS,
+ "%lx != PAGE_NOACCESS\n", info.AllocationProtect);
+ ok(info.RegionSize == 0x1000, "%lx != 0x1000\n", info.RegionSize);
+ ok(info.State == MEM_COMMIT, "%lx != MEM_COMMIT\n", info.State);
/* this time NT reports PAGE_NOACCESS as well */
- todo_wine ok(info.Protect == PAGE_NOACCESS, "%lx != PAGE_NOACCESS\n",
- info.Protect);
- todo_wine ok(info.Type == MEM_PRIVATE, "%lx != MEM_PRIVATE\n", info.Type);
+ ok(info.Protect == PAGE_NOACCESS, "%lx != PAGE_NOACCESS\n", info.Protect);
+ ok(info.Type == MEM_PRIVATE, "%lx != MEM_PRIVATE\n", info.Type);
/* this should fail, since not the whole range is committed yet */
SetLastError(0xdeadbeef);
@@ -200,20 +193,19 @@ static void test_VirtualAllocEx(void)
ok(!VirtualFreeEx(hProcess, addr1, 0x10000, 0),
"VirtualFreeEx should fail with type 0\n");
- todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER,
- "got %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
+ ok(GetLastError() == ERROR_INVALID_PARAMETER,
+ "got %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
- todo_wine ok(VirtualFreeEx(hProcess, addr1, 0x10000, MEM_DECOMMIT),
- "VirtualFreeEx failed\n");
+ ok(VirtualFreeEx(hProcess, addr1, 0x10000, MEM_DECOMMIT),
+ "VirtualFreeEx failed\n");
/* if the type is MEM_RELEASE, size must be 0 */
ok(!VirtualFreeEx(hProcess, addr1, 1, MEM_RELEASE),
"VirtualFreeEx should fail\n");
- todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER,
+ ok(GetLastError() == ERROR_INVALID_PARAMETER,
"got %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
- todo_wine ok(VirtualFreeEx(hProcess, addr1, 0, MEM_RELEASE),
- "VirtualFreeEx failed\n");
+ ok(VirtualFreeEx(hProcess, addr1, 0, MEM_RELEASE), "VirtualFreeEx failed\n");
TerminateProcess(hProcess, 0);
CloseHandle(hProcess);
@@ -226,8 +218,8 @@ static DWORD WINAPI create_virtualalloce
VOID *mem;
mem = VirtualAllocEx(hStressTestProcess, NULL, 1<<20,
MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
- todo_wine ok(mem != NULL, "VirtualAllocEx error %lu\n", GetLastError());
- todo_wine ok(VirtualFreeEx(hStressTestProcess, mem, 0, MEM_RELEASE) != 0,
+ ok(mem != NULL, "VirtualAllocEx error %lu\n", GetLastError());
+ ok(VirtualFreeEx(hStressTestProcess, mem, 0, MEM_RELEASE) != 0,
"VirtualFreeEx error %lu\n", GetLastError());
if (!mem) break;
}
diff --git a/dlls/kernel/thread.c b/dlls/kernel/thread.c
index bf29aac..7531622 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,65 @@ 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 (GetProcessId( hProcess ) != GetCurrentProcessId())
+ {
+ /* We need a dedicated remote call for CreateRemoteThread because it
+ * calls RtlCreateUserThread with a pointer to THREAD_start, which
+ * isn't valid across processes. Once Wine always locates kernel32 and
+ * ntdll in the same place, we can remove this remote call and move
+ * struct RemoteOp into ntdll_misc.h */
+ struct RemoteOp ro;
+ HANDLE done_event;
+
+ ro.op = REMOTE_OP_CREATEREMOTETHREAD;
+ ro.kernel32thread.stack = stack;
+ ro.kernel32thread.start = start;
+ ro.kernel32thread.param = param;
+ ro.kernel32thread.flags = flags;
+
+ if ((status = NtCreateEvent(&done_event, EVENT_ALL_ACCESS, NULL, FALSE,
+ FALSE)))
+ return 0;
+
+ SERVER_START_REQ( remote_op )
+ {
+ req->handle = hProcess;
+ req->op = ro.op;
+ req->event = done_event;
+ req->args = &ro;
+
+ status = wine_server_call( req );
+ }
+ SERVER_END_REQ;
+ if (status)
+ return 0;
+
+ NtWaitForSingleObject( done_event, FALSE, NULL );
+ NtClose( done_event );
+
+ if (id) *id = ro.kernel32thread.id;
+
+ return ro.kernel32thread.handle;
+ }
+
+ 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;
+
+ if (GetProcessId( hProcess ) != GetCurrentProcessId())
+ {
+ 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 +199,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 +207,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 ab01bde..5c8bd44 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -62,6 +62,12 @@ extern void DECLSPEC_NORETURN server_pro
extern void DECLSPEC_NORETURN server_exit_thread( int status );
extern void DECLSPEC_NORETURN server_abort_thread( int status );
+/* remote operations support */
+extern NTSTATUS NTDLL_remote_call( HANDLE process, struct RemoteOp *ro );
+extern void CALLBACK __wine_RemoteProcessOperation( ULONG_PTR arg1,
+ ULONG_PTR arg2,
+ ULONG_PTR arg3 );
+
/* module handling */
extern NTSTATUS MODULE_DllThreadAttach( LPVOID lpReserved );
extern FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c
index 02e2b15..6b262a1 100644
--- a/dlls/ntdll/process.c
+++ b/dlls/ntdll/process.c
@@ -395,3 +395,165 @@ NTSTATUS WINAPI NtOpenProcess(PHANDLE h
SERVER_END_REQ;
return status;
}
+
+/***********************************************************************
+ * __wine_RemoteProcessOperation
+ *
+ * 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 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 REMOTE_OP_CREATEREMOTETHREAD:
+ {
+ typedef HANDLE (WINAPI *func_type) ( HANDLE, SECURITY_ATTRIBUTES *,
+ SIZE_T, LPTHREAD_START_ROUTINE,
+ LPVOID, DWORD, LPDWORD );
+ static func_type func = NULL;
+ HANDLE hThread;
+ NTSTATUS ret;
+
+ ro.status = 0;
+ ro.kernel32thread.handle = NULL;
+
+ if (!func)
+ {
+ HMODULE hkernel32;
+ UNICODE_STRING module;
+ ANSI_STRING function;
+ WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2',
+ '.','d','l','l',0};
+
+ RtlInitUnicodeString(&module, kernel32W);
+ RtlInitAnsiString(&function, "CreateRemoteThread");
+ if ((ro.status = LdrGetDllHandle(0, 0, &module, &hkernel32)))
+ break;
+ if ((ro.status = LdrGetProcedureAddress(hkernel32, &function, 0,
+ (PVOID *) &func)))
+ break;
+ }
+
+ hThread = func( NtCurrentProcess(), NULL,
+ ro.kernel32thread.stack,
+ ro.kernel32thread.start,
+ ro.kernel32thread.param,
+ ro.kernel32thread.flags,
+ &ro.kernel32thread.id );
+ if ((ret = NtDuplicateObject( NtCurrentProcess(), hThread,
+ hProcess, &ro.kernel32thread.handle,
+ 0, 0, DUPLICATE_SAME_ACCESS|
+ DUPLICATE_CLOSE_SOURCE )))
+ ERR("Cannot duplicate handle in remote process, error %x\n",
+ (unsigned) ret);
+ break;
+ }
+ case REMOTE_OP_RTLCREATEUSERTHREAD:
+ {
+ 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 %x\n",
+ (unsigned) ret);
+ else if ((ret = NtWriteVirtualMemory( hProcess,
+ ro.thread.handle_ptr,
+ &hRemoteNewThread,
+ sizeof(HANDLE),
+ NULL )))
+ ERR("Cannot write back handle, error %x\n", (unsigned) 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 %x\n", (unsigned) ret);
+ }
+ break;
+ }
+ case REMOTE_OP_NTALLOCATEVIRTUALMEMORY:
+ {
+ ro.status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &ro.alloc.ret,
+ ro.alloc.zero_bits,
+ &ro.alloc.size,
+ ro.alloc.type,
+ ro.alloc.protect );
+ break;
+ }
+ case REMOTE_OP_NTFREEVIRTUALMEMORY:
+ {
+ ro.status = NtFreeVirtualMemory( NtCurrentProcess(),
+ &ro.free.addr,
+ &ro.free.size,
+ ro.free.type );
+ break;
+ }
+ case REMOTE_OP_NTQUERYVIRTUALMEMORY:
+ {
+ void *res;
+ NTSTATUS ret;
+ SIZE_T res_len;
+
+ if (!(res = RtlAllocateHeap( 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.status) break;
+ 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 %x\n", (unsigned) ret);
+ }
+ if (ro.query.buffer)
+ {
+ if ((ret = NtWriteVirtualMemory( hProcess, ro.query.buffer,
+ res, ro.query.len, NULL )))
+ ERR("Cannot write back thread id, error %x\n", (unsigned) ret);
+ }
+ RtlFreeHeap( NtCurrentTeb()->Peb->ProcessHeap, 0, res );
+ break;
+ }
+ }
+
+ /* FIXME optimize write back, perhaps req/ret args? */
+ NtWriteVirtualMemory( hProcess, args, &ro, sizeof(ro), NULL );
+
+ NtSetEvent( hEvent, NULL );
+ NtClose( hEvent );
+ NtClose( hProcess );
+}
diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c
index 702980f..09c8d31 100644
--- a/dlls/ntdll/server.c
+++ b/dlls/ntdll/server.c
@@ -900,6 +900,7 @@ size_t server_init_thread( int unix_pid,
int reply_pipe[2];
struct sigaction sig_act;
size_t info_size;
+ sigset_t sigset;
sig_act.sa_handler = SIG_IGN;
sig_act.sa_flags = 0;
@@ -932,6 +933,9 @@ #endif
req->unix_tid = unix_tid;
req->teb = NtCurrentTeb();
req->peb = NtCurrentTeb()->Peb;
+ /* necessary as ntdll and kernel32 are not always in the same place in
+ * memory for all processes */
+ req->remote_op_func = __wine_RemoteProcessOperation;
req->entry = entry_point;
req->ldt_copy = &wine_ldt_copy;
req->reply_fd = reply_pipe[1];
@@ -946,6 +950,14 @@ #endif
}
SERVER_END_REQ;
+ /* Unconditionally unblock SIGINT, SIGUSR1 and SIGUSR2. If this thread was
+ * created from within a signal handler, then they would be masked. */
+ sigemptyset( &sigset );
+ sigaddset( &sigset, SIGINT );
+ sigaddset( &sigset, SIGUSR1 );
+ sigaddset( &sigset, SIGUSR2 );
+ pthread_functions.sigprocmask( SIG_UNBLOCK, &sigset, NULL );
+
if (ret) server_protocol_error( "init_thread failed with status %x\n", ret );
if (version != SERVER_PROTOCOL_VERSION)
server_protocol_error( "version mismatch %d/%d.\n"
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index f732573..27986bd 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -395,6 +395,36 @@ static void start_thread( struct wine_pt
func( arg );
}
+/***********************************************************************
+ * NTDLL_remote_call
+ */
+NTSTATUS NTDLL_remote_call( HANDLE process, struct RemoteOp *ro )
+{
+ NTSTATUS ret;
+ HANDLE done_event;
+
+ if ((ret = NtCreateEvent(&done_event, EVENT_ALL_ACCESS, NULL, FALSE,
+ FALSE)))
+ return ret;
+
+ SERVER_START_REQ( remote_op )
+ {
+ req->handle = process;
+ req->op = ro->op;
+ req->event = done_event;
+ req->args = ro;
+
+ 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.@)
@@ -419,8 +449,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 = REMOTE_OP_RTLCREATEUSERTHREAD;
+ 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 8c6de4a..4a07247 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -1322,8 +1322,25 @@ NTSTATUS WINAPI NtAllocateVirtualMemory(
if (!is_current_process( process ))
{
- ERR("Unsupported on other process\n");
- return STATUS_ACCESS_DENIED;
+ struct RemoteOp ro;
+
+ ro.op = REMOTE_OP_NTALLOCATEVIRTUALMEMORY;
+ 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 */
@@ -1422,8 +1439,23 @@ NTSTATUS WINAPI NtFreeVirtualMemory( HAN
if (!is_current_process( process ))
{
- ERR("Unsupported on other process\n");
- return STATUS_ACCESS_DENIED;
+ struct RemoteOp ro;
+
+ ro.op = REMOTE_OP_NTFREEVIRTUALMEMORY;
+ ro.free.addr = *addr_ptr;
+ ro.free.size = *size_ptr;
+ ro.free.type = type;
+
+ if ((status = NTDLL_remote_call( process, &ro )))
+ return status;
+
+ TRACE("NtFreeVirtualMemory addr=%x, status=%x\n", ro.free.addr,
+ (unsigned) status);
+
+ *addr_ptr = ro.free.addr;
+ *size_ptr = ro.free.size;
+
+ return ro.status;
}
/* Fix the parameters */
@@ -1586,8 +1618,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 = REMOTE_OP_NTQUERYVIRTUALMEMORY;
+ 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 193e02a..08e746a 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -2396,6 +2396,64 @@ static inline PLIST_ENTRY RemoveTailList
return e;
}
+/***********************************************************************
+ * Remote operations support
+ */
+struct RemoteOp {
+ int op; /* in */
+ NTSTATUS status; /* out */
+
+ union {
+ struct {
+ /* in */
+ ULONG zero_bits;
+ ULONG type;
+ ULONG protect;
+ /* in/out */
+ PVOID ret;
+ SIZE_T size;
+ } alloc; /* REMOTE_OP_NTALLOCATEVIRUALMEMORY */
+ struct {
+ /* in */
+ ULONG type;
+ /* in/out */
+ PVOID addr;
+ SIZE_T size;
+ } free; /* REMOTE_OP_NTFREEVIRTUALMEMORY */
+ struct {
+ /* in */
+ LPVOID addr;
+ MEMORY_INFORMATION_CLASS info_class;
+ PVOID buffer;
+ SIZE_T len;
+ /* out */
+ SIZE_T *res_len;
+ } query; /* REMOTE_OP_NTQUERYVIRTUALMEMORY */
+ 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; /* REMOTE_OP_RTLCREATEUSERTHREAD */
+ struct {
+ /* in */
+ SIZE_T stack;
+ DWORD (CALLBACK *start) (LPVOID);
+ LPVOID param;
+ DWORD flags;
+ /* out */
+ DWORD id;
+ HANDLE handle;
+ } kernel32thread; /* REMOTE_OP_CREATEUSERTHREAD */
+ };
+};
+
#ifdef __cplusplus
} /* extern "C" */
#endif /* defined(__cplusplus) */
diff --git a/server/process.c b/server/process.c
index 75ed6f9..9f3e88a 100644
--- a/server/process.c
+++ b/server/process.c
@@ -1112,3 +1112,85 @@ DECL_HANDLER(get_process_idle_event)
release_object( process );
}
}
+
+/* Signal a remote operation */
+DECL_HANDLER(remote_op)
+{
+ int access, waiting_thread;
+ struct process *src = NULL, *dst = NULL;
+ struct thread *dst_thread, *thread;
+ obj_handle_t dupevent = NULL, dupprocess = NULL;
+
+ switch ( req->op )
+ {
+ case REMOTE_OP_CREATEREMOTETHREAD:
+ case REMOTE_OP_RTLCREATEUSERTHREAD:
+ access = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION
+ | PROCESS_VM_OPERATION | PROCESS_VM_READ
+ | PROCESS_VM_WRITE;
+ break;
+ case REMOTE_OP_NTALLOCATEVIRTUALMEMORY:
+ case REMOTE_OP_NTFREEVIRTUALMEMORY:
+ access = PROCESS_VM_OPERATION;
+ break;
+ case REMOTE_OP_NTQUERYVIRTUALMEMORY:
+ 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;
+ dst_thread = get_process_first_thread( dst );
+ if (!is_process_init_done( dst ) || !dst_thread)
+ {
+ 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( dst_thread, NULL, dst->remote_op_func, APC_KERNEL,
+ APC_PROCESS_QUEUE_SYSTEM,
+ dupprocess, req->args, dupevent ))
+ goto cleanup;
+
+ /* force an unlucky thread to do a wait if no other thread is waiting,
+ * otherwise, we have no guarantee a wait will ever happen. */
+ waiting_thread = 0;
+ dst_thread = NULL;
+ LIST_FOR_EACH_ENTRY( thread, &dst->thread_list, struct thread, proc_entry )
+ {
+ if (thread->suspend)
+ {
+ waiting_thread = 1;
+ break;
+ }
+ else if (!dst_thread && thread->unix_pid != -1)
+ dst_thread = thread;
+ }
+ if (!waiting_thread)
+ send_thread_signal( dst_thread, SIGUSR1 );
+
+cleanup:
+ if (src)
+ release_object( src );
+ if (dst)
+ release_object( dst );
+}
diff --git a/server/process.h b/server/process.h
index 02fb95e..b04cb48 100644
--- a/server/process.h
+++ b/server/process.h
@@ -81,6 +81,7 @@ struct process
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 */
+ void *remote_op_func; /* remote op handler address in client */
};
struct process_snapshot
diff --git a/server/protocol.def b/server/protocol.def
index 3166696..8825824 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -277,6 +277,7 @@ struct token_groups
void* peb; /* address of PEB (in thread address space) */
void* entry; /* thread entry point (in thread address space) */
void* ldt_copy; /* address of LDT copy (in thread address space) */
+ void* remote_op_func; /* remote op handler address in client */
int reply_fd; /* fd for reply pipe */
int wait_fd; /* fd for blocking calls pipe */
int debug_level; /* new debug level */
@@ -2630,3 +2631,21 @@ #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 */
+ at REPLY
+ at END
+enum remote_op
+{
+ REMOTE_OP_RTLCREATEUSERTHREAD,
+ REMOTE_OP_NTALLOCATEVIRTUALMEMORY,
+ REMOTE_OP_NTFREEVIRTUALMEMORY,
+ REMOTE_OP_NTQUERYVIRTUALMEMORY,
+ REMOTE_OP_CREATEREMOTETHREAD /* in kernel32 */
+};
diff --git a/server/thread.c b/server/thread.c
index 6d41c65..76151a3 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -648,9 +648,16 @@ int thread_queue_apc( struct thread *thr
/* cancel a possible previous APC with the same owner */
if (owner) thread_cancel_apc( thread, owner, apc_queue );
- if (thread->state == TERMINATED) return 0;
-
- if (!(apc = mem_alloc( sizeof(*apc) ))) return 0;
+ if (thread->state == TERMINATED)
+ {
+ set_error( STATUS_ACCESS_DENIED );
+ return 0;
+ }
+ if (!(apc = mem_alloc( sizeof(*apc) )))
+ {
+ set_error( STATUS_NO_MEMORY );
+ return 0;
+ }
apc->owner = owner;
apc->func = func;
apc->type = type;
@@ -901,6 +908,7 @@ DECL_HANDLER(init_thread)
process->unix_pid = current->unix_pid;
process->peb = req->peb;
process->ldt_copy = req->ldt_copy;
+ process->remote_op_func = req->remote_op_func;
reply->info_size = init_process( current );
}
else
More information about the wine-patches
mailing list