kernel32/tests: Test suspended process states.
Signed-off-by: Jonathan Doron <jond@wizery.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
a1f7339137
commit
6a49f4d5f3
|
@ -2944,6 +2944,239 @@ static void test_DetachConsoleHandles(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
static BOOL read_nt_header(HANDLE process_handle, MEMORY_BASIC_INFORMATION *mbi,
|
||||
IMAGE_NT_HEADERS *nt_header)
|
||||
{
|
||||
IMAGE_DOS_HEADER dos_header;
|
||||
|
||||
if (!ReadProcessMemory(process_handle, mbi->BaseAddress, &dos_header, sizeof(dos_header), NULL))
|
||||
return FALSE;
|
||||
|
||||
if ((dos_header.e_magic != IMAGE_DOS_SIGNATURE) ||
|
||||
((ULONG)dos_header.e_lfanew > mbi->RegionSize) ||
|
||||
(dos_header.e_lfanew < sizeof(dos_header)))
|
||||
return FALSE;
|
||||
|
||||
if (!ReadProcessMemory(process_handle, (char *)mbi->BaseAddress + dos_header.e_lfanew,
|
||||
nt_header, sizeof(*nt_header), NULL))
|
||||
return FALSE;
|
||||
|
||||
return (nt_header->Signature == IMAGE_NT_SIGNATURE);
|
||||
}
|
||||
|
||||
static void test_SuspendProcessState(void)
|
||||
{
|
||||
struct pipe_params
|
||||
{
|
||||
ULONG pipe_write_buf;
|
||||
ULONG pipe_read_buf;
|
||||
ULONG bytes_returned;
|
||||
CHAR pipe_name[MAX_PATH];
|
||||
};
|
||||
|
||||
#ifdef __x86_64__
|
||||
struct remote_rop_chain
|
||||
{
|
||||
void *exit_process_ptr;
|
||||
ULONG_PTR home_rcx;
|
||||
ULONG_PTR home_rdx;
|
||||
ULONG_PTR home_r8;
|
||||
ULONG_PTR home_r9;
|
||||
ULONG_PTR pipe_read_buf_size;
|
||||
ULONG_PTR bytes_returned;
|
||||
ULONG_PTR timeout;
|
||||
};
|
||||
#else
|
||||
struct remote_rop_chain
|
||||
{
|
||||
void *exit_process_ptr;
|
||||
ULONG_PTR pipe_name;
|
||||
ULONG_PTR pipe_write_buf;
|
||||
ULONG_PTR pipe_write_buf_size;
|
||||
ULONG_PTR pipe_read_buf;
|
||||
ULONG_PTR pipe_read_buf_size;
|
||||
ULONG_PTR bytes_returned;
|
||||
ULONG_PTR timeout;
|
||||
void *unreached_ret;
|
||||
ULONG_PTR exit_code;
|
||||
};
|
||||
#endif
|
||||
|
||||
static const char pipe_name[] = "\\\\.\\pipe\\TestPipe";
|
||||
static const ULONG pipe_write_magic = 0x454e4957;
|
||||
STARTUPINFOA si = {0};
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
PVOID exe_base, address, remote_pipe_params, exit_process_ptr,
|
||||
call_named_pipe_a;
|
||||
IMAGE_NT_HEADERS nt_header;
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
IMAGE_IMPORT_DESCRIPTOR iid;
|
||||
ULONG_PTR orig_iat_entry_value, iat_entry_value;
|
||||
struct pipe_params pipe_params;
|
||||
struct remote_rop_chain rop_chain;
|
||||
CONTEXT ctx;
|
||||
HANDLE server_pipe_handle;
|
||||
BOOL pipe_connected;
|
||||
ULONG pipe_magic, numb;
|
||||
BOOL ret;
|
||||
|
||||
exit_process_ptr = GetProcAddress(hkernel32, "ExitProcess");
|
||||
ok(exit_process_ptr != NULL, "GetProcAddress ExitProcess failed\n");
|
||||
|
||||
call_named_pipe_a = GetProcAddress(hkernel32, "CallNamedPipeA");
|
||||
ok(call_named_pipe_a != NULL, "GetProcAddress CallNamedPipeA failed\n");
|
||||
|
||||
si.cb = sizeof(si);
|
||||
ret = CreateProcessA(NULL, selfname, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
|
||||
ok(ret, "Failed to create process (%d)\n", GetLastError());
|
||||
|
||||
/* Find the EXE base in the new process */
|
||||
exe_base = NULL;
|
||||
for (address = NULL ;
|
||||
VirtualQueryEx(pi.hProcess, address, &mbi, sizeof(mbi)) ;
|
||||
address = (char *)mbi.BaseAddress + mbi.RegionSize) {
|
||||
if ((mbi.Type == SEC_IMAGE) &&
|
||||
read_nt_header(pi.hProcess, &mbi, &nt_header) &&
|
||||
!(nt_header.FileHeader.Characteristics & IMAGE_FILE_DLL)) {
|
||||
exe_base = mbi.BaseAddress;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure we found the EXE in the new process */
|
||||
ok(exe_base != NULL, "Could not find EXE in remote process\n");
|
||||
|
||||
/* Check the EXE has import table */
|
||||
ok(nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, "Import table VA is zero\n");
|
||||
ok(nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size, "Import table Size is zero\n");
|
||||
|
||||
/* Read the first IID */
|
||||
ret = ReadProcessMemory(pi.hProcess,
|
||||
(char *)exe_base + nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress,
|
||||
&iid, sizeof(iid), NULL);
|
||||
ok(ret, "Failed to read process EXE IID (%d)\n", GetLastError());
|
||||
|
||||
/* Validate the IID is present and not a bound import, and that we have
|
||||
an OriginalFirstThunk to compare with */
|
||||
ok(iid.Name, "Exe first IID does not have a Name\n");
|
||||
ok(iid.FirstThunk, "Exe first IID does not have a FirstThunk\n");
|
||||
ok(!iid.TimeDateStamp, "Exe first IID is a bound import (UNSUPPORTED for current test)\n");
|
||||
ok(iid.OriginalFirstThunk, "Exe first IID does not have an OriginalFirstThunk (UNSUPPORTED for current test)\n");
|
||||
|
||||
/* Read a single IAT entry from the FirstThunk */
|
||||
ret = ReadProcessMemory(pi.hProcess, (char *)exe_base + iid.FirstThunk,
|
||||
&iat_entry_value, sizeof(iat_entry_value), NULL);
|
||||
ok(ret, "Failed to read IAT entry from FirstThunk (%d)\n", GetLastError());
|
||||
ok(iat_entry_value, "IAT entry in FirstThunk is NULL\n");
|
||||
|
||||
/* Read a single IAT entry from the OriginalFirstThunk */
|
||||
ret = ReadProcessMemory(pi.hProcess, (char *)exe_base + iid.OriginalFirstThunk,
|
||||
&orig_iat_entry_value, sizeof(orig_iat_entry_value), NULL);
|
||||
ok(ret, "Failed to read IAT entry from OriginalFirstThunk (%d)\n", GetLastError());
|
||||
ok(orig_iat_entry_value, "IAT entry in OriginalFirstThunk is NULL\n");
|
||||
|
||||
/* The IAT should be UNRESOLVED */
|
||||
todo_wine
|
||||
ok(iat_entry_value == orig_iat_entry_value, "IAT entry resolved prematurely\n");
|
||||
|
||||
server_pipe_handle = CreateNamedPipeA(pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_WRITE_THROUGH,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, 0x20000, 0x20000,
|
||||
0, NULL);
|
||||
ok(server_pipe_handle != INVALID_HANDLE_VALUE, "Failed to create communication pipe (%d)\n", GetLastError());
|
||||
|
||||
/* Setup the remote process enviornment */
|
||||
ctx.ContextFlags = CONTEXT_FULL;
|
||||
ret = GetThreadContext(pi.hThread, &ctx);
|
||||
ok(ret, "Failed retrieving remote thread context (%d)\n", GetLastError());
|
||||
|
||||
remote_pipe_params = VirtualAllocEx(pi.hProcess, NULL, sizeof(pipe_params), MEM_COMMIT, PAGE_READWRITE);
|
||||
ok(remote_pipe_params != NULL, "Failed allocating memory in remote process (%d)\n", GetLastError());
|
||||
|
||||
pipe_params.pipe_write_buf = pipe_write_magic;
|
||||
pipe_params.pipe_read_buf = 0;
|
||||
pipe_params.bytes_returned = 0;
|
||||
strcpy(pipe_params.pipe_name, pipe_name);
|
||||
|
||||
ret = WriteProcessMemory(pi.hProcess, remote_pipe_params,
|
||||
&pipe_params, sizeof(pipe_params), NULL);
|
||||
ok(ret, "Failed to write to remote process memory (%d)\n", GetLastError());
|
||||
|
||||
#ifdef __x86_64__
|
||||
rop_chain.exit_process_ptr = exit_process_ptr;
|
||||
ctx.Rcx = (ULONG_PTR)remote_pipe_params + offsetof(struct pipe_params, pipe_name);
|
||||
ctx.Rdx = (ULONG_PTR)remote_pipe_params + offsetof(struct pipe_params, pipe_write_buf);
|
||||
ctx.R8 = sizeof(pipe_params.pipe_write_buf);
|
||||
ctx.R9 = (ULONG_PTR)remote_pipe_params + offsetof(struct pipe_params, pipe_read_buf);
|
||||
rop_chain.pipe_read_buf_size = sizeof(pipe_params.pipe_read_buf);
|
||||
rop_chain.bytes_returned = (ULONG_PTR)remote_pipe_params + offsetof(struct pipe_params, bytes_returned);
|
||||
rop_chain.timeout = 10000;
|
||||
|
||||
ctx.Rip = (ULONG_PTR)call_named_pipe_a;
|
||||
ctx.Rsp -= sizeof(rop_chain);
|
||||
ret = WriteProcessMemory(pi.hProcess, (void *)ctx.Rsp, &rop_chain, sizeof(rop_chain), NULL);
|
||||
ok(ret, "Failed to write to remote process thread stack (%d)\n", GetLastError());
|
||||
#else
|
||||
rop_chain.exit_process_ptr = exit_process_ptr;
|
||||
rop_chain.pipe_name = (ULONG_PTR)remote_pipe_params + offsetof(struct pipe_params, pipe_name);
|
||||
rop_chain.pipe_write_buf = (ULONG_PTR)remote_pipe_params + offsetof(struct pipe_params, pipe_write_buf);
|
||||
rop_chain.pipe_write_buf_size = sizeof(pipe_params.pipe_write_buf);
|
||||
rop_chain.pipe_read_buf = (ULONG_PTR)remote_pipe_params + offsetof(struct pipe_params, pipe_read_buf);
|
||||
rop_chain.pipe_read_buf_size = sizeof(pipe_params.pipe_read_buf);
|
||||
rop_chain.bytes_returned = (ULONG_PTR)remote_pipe_params + offsetof(struct pipe_params, bytes_returned);
|
||||
rop_chain.timeout = 10000;
|
||||
rop_chain.exit_code = 0;
|
||||
|
||||
ctx.Eip = (ULONG_PTR)call_named_pipe_a;
|
||||
ctx.Esp -= sizeof(rop_chain);
|
||||
ret = WriteProcessMemory(pi.hProcess, (void *)ctx.Esp, &rop_chain, sizeof(rop_chain), NULL);
|
||||
ok(ret, "Failed to write to remote process thread stack (%d)\n", GetLastError());
|
||||
#endif
|
||||
ret = SetThreadContext(pi.hThread, &ctx);
|
||||
ok(ret, "Failed to set remote thread context (%d)\n", GetLastError());
|
||||
|
||||
ResumeThread(pi.hThread);
|
||||
|
||||
pipe_connected = ConnectNamedPipe(server_pipe_handle, NULL) || (GetLastError() == ERROR_PIPE_CONNECTED);
|
||||
ok(pipe_connected, "Pipe did not connect\n");
|
||||
|
||||
ret = ReadFile(server_pipe_handle, &pipe_magic, sizeof(pipe_magic), &numb, NULL);
|
||||
ok(ret, "Failed to read buffer from pipe (%d)\n", GetLastError());
|
||||
|
||||
ok(pipe_magic == pipe_write_magic, "Did not get the correct magic from the remote process\n");
|
||||
|
||||
/* Validate the Imports, at this point the thread in the new process should have
|
||||
initialized the EXE module imports and call each dll DllMain notifying it on
|
||||
the new thread in the process. */
|
||||
iat_entry_value = orig_iat_entry_value = 0;
|
||||
|
||||
/* Read a single IAT entry from the FirstThunk */
|
||||
ret = ReadProcessMemory(pi.hProcess, (char *)exe_base + iid.FirstThunk,
|
||||
&iat_entry_value, sizeof(iat_entry_value), NULL);
|
||||
ok(ret, "Failed to read IAT entry from FirstThunk [2] (%d)\n", GetLastError());
|
||||
|
||||
/* Read a single IAT entry from the OriginalFirstThunk */
|
||||
ret = ReadProcessMemory(pi.hProcess, (char *)exe_base + iid.OriginalFirstThunk,
|
||||
&orig_iat_entry_value, sizeof(orig_iat_entry_value), NULL);
|
||||
ok(ret, "Failed to read IAT entry from OriginalFirstThunk [2] (%d)\n", GetLastError());
|
||||
|
||||
/* The IAT should be RESOLVED */
|
||||
ok(iat_entry_value != orig_iat_entry_value, "EXE IAT is not resolved\n");
|
||||
|
||||
ret = WriteFile(server_pipe_handle, &pipe_magic, sizeof(pipe_magic), &numb, NULL);
|
||||
ok(ret, "Failed to write the magic back to the pipe (%d)\n", GetLastError());
|
||||
|
||||
CloseHandle(server_pipe_handle);
|
||||
WaitForSingleObject(pi.hProcess, 10000);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
#else
|
||||
static void test_SuspendProcessState(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void test_DetachStdHandles(void)
|
||||
{
|
||||
#ifndef _WIN64
|
||||
|
@ -3436,6 +3669,7 @@ START_TEST(process)
|
|||
test_GetLogicalProcessorInformationEx();
|
||||
test_largepages();
|
||||
test_ProcThreadAttributeList();
|
||||
test_SuspendProcessState();
|
||||
|
||||
/* things that can be tested:
|
||||
* lookup: check the way program to be executed is searched
|
||||
|
|
Loading…
Reference in New Issue