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:
Jonathan Doron 2017-09-14 11:49:36 +03:00 committed by Alexandre Julliard
parent a1f7339137
commit 6a49f4d5f3
1 changed files with 234 additions and 0 deletions

View File

@ -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