Files
raven/testdata/native_loader/remote_process_client.c
T

490 lines
22 KiB
C

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0601
#include <windows.h>
typedef LONG (WINAPI *query_process)(HANDLE, ULONG, void *, ULONG, ULONG *);
typedef LONG (WINAPI *copy_memory)(HANDLE, void *, void *, SIZE_T, SIZE_T *);
typedef LONG (WINAPI *open_thread)(HANDLE *, DWORD, void *, void *);
struct thread_attributes {
ULONG length;
HANDLE root;
void *name;
ULONG attributes;
void *security, *quality;
};
struct thread_information {
LONG exit_status;
void *teb;
ULONG_PTR pid, tid, affinity;
LONG priority, base_priority;
};
struct basic_information {
LONG exit_status;
void *peb;
ULONG_PTR affinity;
LONG priority;
ULONG_PTR pid, parent;
};
static volatile DWORD child_value = 0x13579bdf;
static volatile BYTE child_buffer[120017];
static BYTE transferred[120017];
static int test_image_name(HANDLE process, const WCHAR *expected)
{
WCHAR path[512];
char narrow[512], expected_a[512];
DWORD size = 512, length = 0;
while (expected[length]) ++length;
if (!QueryFullProcessImageNameW(process, 0, path, &size) || size != length || path[size]) return 100;
for (unsigned index = 0; index < length; ++index)
if (path[index] != expected[index]) return 101;
const int bytes = WideCharToMultiByte(CP_ACP, 0, expected, -1, expected_a, sizeof expected_a, 0, 0);
size = sizeof narrow;
if (!bytes || !QueryFullProcessImageNameA(process, 0, narrow, &size) || size != (DWORD)bytes - 1) return 102;
for (unsigned index = 0; index < (unsigned)bytes; ++index)
if (narrow[index] != expected_a[index]) return 103;
return 0;
}
static int test_handle_count(query_process query, HANDLE process, BOOL own)
{
DWORD before, during, count;
DWORD handles[256];
ULONG length;
HANDLE event, sent, received;
if (!GetProcessHandleCount(process, &before) || query(process, 20, &count, sizeof(count), &length) || count != before || length != sizeof(count))
return 90;
event = CreateEventW(NULL, FALSE, FALSE, NULL);
if (!event || !GetProcessHandleCount(process, &during) || during != before + (own ? 1 : 0) ||
!DuplicateHandle(GetCurrentProcess(), event, process, &sent, 0, FALSE, DUPLICATE_SAME_ACCESS) ||
!GetProcessHandleCount(process, &during) || during != before + (own ? 2 : 1))
return 91;
if (query(process, 58, handles, sizeof(handles), &length) || length != during * sizeof(DWORD))
return 97;
BOOL found = FALSE;
for (unsigned index = 0; index < length / sizeof(DWORD); ++index) {
if (handles[index] == (DWORD)(ULONG_PTR)sent) found = TRUE;
if (!handles[index] || (index && handles[index] <= handles[index - 1])) return 98;
}
if (!found || query(process, 58, handles, sizeof(DWORD), &length) || length != sizeof(DWORD) || !handles[0] ||
(ULONG)query(process, 58, (void *)1, sizeof(DWORD), &length) != 0xc0000005)
return 99;
if (!DuplicateHandle(process, sent, GetCurrentProcess(), &received, 0, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE) ||
!GetProcessHandleCount(process, &during) || during != before + (own ? 2 : 0))
return 92;
CloseHandle(received);
CloseHandle(event);
if (!GetProcessHandleCount(process, &during) || during != before)
return 93;
return 0;
}
#ifdef _WIN64
static int test_thread_thunk(open_thread open, ULONG_PTR *client)
{
BYTE original[11], *source = (BYTE *)open;
BYTE *code = VirtualAlloc(NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
HANDLE handle = NULL;
DWORD previous, ignored;
LONG patched, copied;
unsigned index;
if (!code || source[0] != 0x4c || source[1] != 0x8b || source[2] != 0xd1 || source[3] != 0xb8 ||
source[8] != 0x0f || source[9] != 0x05 || source[10] != 0xc3)
return 64;
for (index = 0; index < sizeof(original); ++index)
code[index] = original[index] = source[index];
if (!VirtualProtect(code, 4096, PAGE_EXECUTE_READ, &previous) ||
!VirtualProtect(source, sizeof(original), PAGE_EXECUTE_WRITECOPY, &previous))
return 65;
source[0] = 0xb8;
source[1] = 0x22;
source[2] = source[3] = 0;
source[4] = 0xc0;
source[5] = 0xc3;
FlushInstructionCache(GetCurrentProcess(), source, sizeof(original));
patched = open(&handle, THREAD_QUERY_INFORMATION, NULL, client);
copied = ((open_thread)code)(&handle, THREAD_QUERY_INFORMATION, NULL, client);
for (index = 0; index < sizeof(original); ++index)
source[index] = original[index];
VirtualProtect(source, sizeof(original), previous, &ignored);
FlushInstructionCache(GetCurrentProcess(), source, sizeof(original));
VirtualFree(code, 0, MEM_RELEASE);
if ((ULONG)patched != 0xc0000022 || copied || !handle || GetThreadId(handle) != client[1])
return 66;
CloseHandle(handle);
return 0;
}
#endif
static int test_open_thread(HMODULE ntdll)
{
open_thread open = (open_thread)GetProcAddress(ntdll, "NtOpenThread");
query_process query = (query_process)GetProcAddress(ntdll, "NtQueryInformationThread");
struct thread_attributes attributes = {sizeof(attributes), NULL, NULL, 2, NULL, NULL};
struct thread_information information;
ULONG_PTR client[2] = {GetCurrentProcessId(), GetCurrentThreadId()};
HANDLE handle = NULL, sentinel = (HANDLE)(ULONG_PTR)0x12345678;
ULONG length = 0;
DWORD flags = 0, previous;
void *readonly;
if (!open || !query)
return 50;
if (open(&handle, THREAD_QUERY_INFORMATION, &attributes, client) || !handle || GetThreadId(handle) != client[1] ||
!GetHandleInformation(handle, &flags) || !(flags & HANDLE_FLAG_INHERIT))
return 51;
if (query(handle, 0, &information, sizeof(information), &length) || length != sizeof(information) ||
information.pid != client[0] || information.tid != client[1] || !information.teb || information.exit_status != 259)
return 52;
if ((ULONG)query(handle, 0, (void *)1, sizeof(information), &length) != 0xc0000005 ||
(ULONG)query(handle, 0, &information, sizeof(information), (ULONG *)1) != 0xc0000005)
return 53;
CloseHandle(handle);
if (open(&handle, READ_CONTROL, NULL, client) ||
(ULONG)query(handle, 0, &information, sizeof(information), &length) != 0xc0000022)
return 54;
CloseHandle(handle);
handle = OpenThread(GENERIC_READ, FALSE, (DWORD)client[1]);
if (!handle || query(handle, 0, &information, sizeof(information), &length) ||
!GetHandleInformation(handle, &flags) || (flags & HANDLE_FLAG_INHERIT))
return 55;
CloseHandle(handle);
handle = sentinel;
client[0] = GetCurrentProcessId() + 1;
if ((ULONG)open(&handle, THREAD_ALL_ACCESS, NULL, client) != 0xc000000b || handle != sentinel)
return 56;
client[0] = GetCurrentProcessId();
client[1] = 0xffffffff;
if ((ULONG)open(&handle, THREAD_ALL_ACCESS, NULL, client) != 0xc000000b || handle != sentinel)
return 57;
client[1] = GetCurrentThreadId();
if ((ULONG)open(NULL, THREAD_ALL_ACCESS, NULL, client) != 0xc0000005 ||
(ULONG)open((HANDLE *)1, THREAD_ALL_ACCESS, NULL, client) != 0xc0000005 ||
(ULONG)open(&handle, THREAD_ALL_ACCESS, NULL, (void *)1) != 0xc0000005 || handle != sentinel)
return 58;
attributes.length = 0;
if ((ULONG)open(&handle, THREAD_ALL_ACCESS, &attributes, client) != 0xc000000d || handle != sentinel)
return 59;
attributes.length = sizeof(attributes);
attributes.name = (void *)1;
if ((ULONG)open(&handle, THREAD_ALL_ACCESS, &attributes, client) != 0xc000000d || handle != sentinel)
return 60;
attributes.name = NULL;
attributes.attributes = 0x80000000;
if ((ULONG)open(&handle, THREAD_ALL_ACCESS, &attributes, client) != 0xc00000bb || handle != sentinel)
return 61;
readonly = VirtualAlloc(NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!readonly || !VirtualProtect(readonly, 4096, PAGE_READONLY, &previous))
return 62;
if ((ULONG)open((HANDLE *)readonly, THREAD_ALL_ACCESS, NULL, client) != 0xc0000005 ||
(ULONG)query(GetCurrentThread(), 0, readonly, sizeof(information), &length) != 0xc0000005)
return 63;
VirtualFree(readonly, 0, MEM_RELEASE);
#ifdef _WIN64
return test_thread_thunk(open, client);
#else
return 0;
#endif
}
static int test_allocation(HANDLE process)
{
MEMORY_BASIC_INFORMATION information;
DWORD value = 1234, read_back = 1, previous = 0;
SIZE_T copied = 0;
void *block = VirtualAllocEx(process, NULL, 8192, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!block || !ReadProcessMemory(process, block, &read_back, sizeof(read_back), &copied) || copied != sizeof(read_back) || read_back)
return 30;
if (!WriteProcessMemory(process, block, &value, sizeof(value), &copied) || copied != sizeof(value) ||
!VirtualProtectEx(process, block, 8192, PAGE_READONLY, &previous) || previous != PAGE_READWRITE)
return 31;
if (VirtualQueryEx(process, block, &information, sizeof(information)) != sizeof(information) || information.BaseAddress != block ||
information.RegionSize != 8192 || information.State != MEM_COMMIT || information.Protect != PAGE_READONLY)
return 32;
if (WriteProcessMemory(process, block, &value, sizeof(value), &copied) || GetLastError() != ERROR_PARTIAL_COPY || copied ||
!ReadProcessMemory(process, block, &read_back, sizeof(read_back), &copied) || read_back != value)
return 33;
if (VirtualProtectEx(process, block, 8192, PAGE_READWRITE, NULL) ||
VirtualQueryEx(process, block, &information, sizeof(information)) != sizeof(information) || information.Protect != PAGE_READONLY)
return 34;
if (!VirtualFreeEx(process, block, 0, MEM_RELEASE) || VirtualQueryEx(process, block, &information, sizeof(information)) != sizeof(information) ||
information.State != MEM_FREE)
return 35;
return 0;
}
static int test_child(query_process query, copy_memory read_memory, copy_memory write_memory, PROCESS_INFORMATION *child)
{
open_thread open = (open_thread)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtOpenProcess");
struct basic_information information;
ULONG length = 0;
SIZE_T copied = 0;
ULONG_PTR image = 0, address, buffer;
DWORD value = 0, replacement = 0x2468ace0, exit_code = 0;
HANDLE limited;
unsigned index;
int result;
{
ULONG_PTR client[2] = {child->dwProcessId, 0};
HANDLE handle = NULL;
if (!open || open(&handle, PROCESS_QUERY_INFORMATION, NULL, client) ||
query(handle, 0, &information, sizeof(information), &length) || information.pid != child->dwProcessId)
return 72;
CloseHandle(handle);
}
if (query(child->hProcess, 0, &information, sizeof(information), &length) || length != sizeof(information) ||
information.pid != child->dwProcessId || information.parent != GetCurrentProcessId() || information.exit_status != 259 || !information.peb)
return 10;
if (!ReadProcessMemory(child->hProcess, (BYTE *)information.peb + 2 * sizeof(void *), &image, sizeof(image), &copied) ||
copied != sizeof(image) || !image)
return 11;
address = image + (ULONG_PTR)&child_value - (ULONG_PTR)GetModuleHandleW(NULL);
buffer = image + (ULONG_PTR)child_buffer - (ULONG_PTR)GetModuleHandleW(NULL);
if (read_memory(child->hProcess, (void *)address, &value, sizeof(value), &copied) || copied != sizeof(value) || value != child_value)
return 12;
if (write_memory(child->hProcess, (void *)address, &replacement, sizeof(replacement), &copied) || copied != sizeof(replacement))
return 13;
if (!ReadProcessMemory(child->hProcess, (void *)address, &value, sizeof(value), &copied) || copied != sizeof(value) || value != replacement)
return 14;
for (index = 0; index < sizeof(transferred); ++index)
transferred[index] = (BYTE)(index * 17 + 3);
if (!WriteProcessMemory(child->hProcess, (void *)buffer, transferred, sizeof(transferred), &copied) || copied != sizeof(transferred))
return 15;
for (index = 0; index < sizeof(transferred); ++index)
transferred[index] = 0;
if (!ReadProcessMemory(child->hProcess, (void *)buffer, transferred, sizeof(transferred), &copied) || copied != sizeof(transferred))
return 16;
for (index = 0; index < sizeof(transferred); ++index)
if (transferred[index] != (BYTE)(index * 17 + 3))
return 17;
if (ReadProcessMemory(child->hProcess, (void *)1, &value, sizeof(value), &copied) || GetLastError() != ERROR_PARTIAL_COPY || copied)
return 18;
if (WriteProcessMemory(child->hProcess, (void *)1, &value, sizeof(value), &copied) || GetLastError() != ERROR_PARTIAL_COPY || copied)
return 19;
limited = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, child->dwProcessId);
if (!limited)
return 20;
if (ReadProcessMemory(limited, (void *)address, &value, sizeof(value), &copied) || GetLastError() != ERROR_ACCESS_DENIED || copied ||
WriteProcessMemory(limited, (void *)address, &replacement, sizeof(replacement), &copied) || GetLastError() != ERROR_ACCESS_DENIED || copied) {
CloseHandle(limited);
return 21;
}
if (query(limited, 0, &information, sizeof(information), &length)) {
CloseHandle(limited);
return 22;
}
if (VirtualAllocEx(limited, NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) || GetLastError() != ERROR_ACCESS_DENIED) {
CloseHandle(limited);
return 24;
}
CloseHandle(limited);
result = test_allocation(child->hProcess);
if (result)
return result;
#ifdef _WIN64
{
BYTE patch[6] = {0xb8, 0x22, 0, 0, 0xc0, 0xc3};
MEMORY_BASIC_INFORMATION memory;
DWORD previous, ignored;
void *target = (void *)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtOpenThread");
if (VirtualQueryEx(child->hProcess, target, &memory, sizeof(memory)) != sizeof(memory) || memory.Type != MEM_IMAGE ||
!VirtualProtectEx(child->hProcess, target, sizeof(patch), PAGE_WRITECOPY, &previous))
return 70;
if (!WriteProcessMemory(child->hProcess, target, patch, sizeof(patch), &copied) || copied != sizeof(patch) ||
!VirtualProtectEx(child->hProcess, target, sizeof(patch), previous, &ignored) || ignored != PAGE_WRITECOPY)
return 71;
}
#endif
if (WaitForSingleObject(child->hProcess, 0) != WAIT_TIMEOUT || ResumeThread(child->hThread) != 1 ||
WaitForSingleObject(child->hProcess, 15000) != WAIT_OBJECT_0 || !GetExitCodeProcess(child->hProcess, &exit_code) || exit_code)
return 23;
return 0;
}
int raven_entry(void)
{
const WCHAR *line = GetCommandLineW();
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
query_process query = (query_process)GetProcAddress(ntdll, "NtQueryInformationProcess");
copy_memory read_memory = (copy_memory)GetProcAddress(ntdll, "NtReadVirtualMemory");
copy_memory write_memory = (copy_memory)GetProcAddress(ntdll, "NtWriteVirtualMemory");
open_thread open_process = (open_thread)GetProcAddress(ntdll, "NtOpenProcess");
struct basic_information information;
STARTUPINFOW startup;
PROCESS_INFORMATION child;
WCHAR command[512], source[512], child_image[512], child_directory[512];
ULONG length = 0;
ULONG_PTR peb = 0;
DWORD used;
DWORD value = 123, replacement = 456, count;
HANDLE limited;
SIZE_T copied;
unsigned index;
int result;
for (index = 0; line[index]; ++index)
#ifdef _WIN64
if (line[index] == '-' && line[index + 1] == '-' && line[index + 2] == 'u') {
__asm__ volatile("mov $39, %%eax; syscall" : : : "rax", "rcx", "r11", "memory");
return 0;
} else
#endif
if (line[index] == '-' && line[index + 1] == '-' && line[index + 2] == 'c') {
#ifdef _WIN64
ULONG_PTR client[2] = {GetCurrentProcessId(), GetCurrentThreadId()};
HANDLE handle;
open_thread open = (open_thread)GetProcAddress(ntdll, "NtOpenThread");
if (!open || (ULONG)open(&handle, THREAD_QUERY_INFORMATION, NULL, client) != 0xc0000022)
return 92;
#endif
if (child_value != 0x2468ace0)
return 90;
for (index = 0; index < sizeof(child_buffer); ++index)
if (child_buffer[index] != (BYTE)(index * 17 + 3))
return 91;
return 0;
}
if (!query || !read_memory || !write_memory || !open_process)
return 1;
result = test_handle_count(query, GetCurrentProcess(), TRUE);
if (result) return result;
if (GetProcessHandleCount((HANDLE)(ULONG_PTR)0x12345678, &count) || GetLastError() != ERROR_INVALID_HANDLE ||
GetProcessHandleCount(GetCurrentProcess(), (DWORD *)1) || GetLastError() != ERROR_NOACCESS)
return 94;
limited = OpenProcess(PROCESS_VM_READ, FALSE, GetCurrentProcessId());
if (!limited || GetProcessHandleCount(limited, &count) || GetLastError() != ERROR_ACCESS_DENIED) return 95;
CloseHandle(limited);
limited = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentProcessId());
if (!limited || !GetProcessHandleCount(limited, &count)) return 96;
CloseHandle(limited);
result = test_open_thread(ntdll);
if (result)
return result;
{
ULONG_PTR client[2] = {GetCurrentProcessId(), 0};
struct thread_attributes attributes = {sizeof(attributes), NULL, NULL, 2, NULL, NULL};
HANDLE handle = NULL, sentinel = (HANDLE)(ULONG_PTR)0x12345678;
DWORD flags = 0;
if (open_process(&handle, GENERIC_READ, &attributes, client) || !GetHandleInformation(handle, &flags) ||
!(flags & HANDLE_FLAG_INHERIT) || query(handle, 0, &information, sizeof(information), &length) || information.pid != client[0])
return 73;
CloseHandle(handle);
if (open_process(&handle, READ_CONTROL, NULL, client) ||
(ULONG)query(handle, 0, &information, sizeof(information), &length) != 0xc0000022)
return 74;
CloseHandle(handle);
handle = sentinel;
if ((ULONG)open_process(NULL, GENERIC_READ, NULL, client) != 0xc0000005 ||
(ULONG)open_process((HANDLE *)1, GENERIC_READ, NULL, client) != 0xc0000005 ||
(ULONG)open_process(&handle, GENERIC_READ, NULL, (void *)1) != 0xc0000005 || handle != sentinel)
return 75;
if ((ULONG)open_process(&handle, GENERIC_READ, NULL, NULL) != 0xc0000030 || handle != sentinel)
return 76;
attributes.name = (void *)1;
if ((ULONG)open_process(&handle, GENERIC_READ, &attributes, client) != 0xc0000030 || handle != sentinel)
return 77;
client[0] = 0xffffffff;
if ((ULONG)open_process(&handle, GENERIC_READ, NULL, client) != 0xc000000b || handle != sentinel)
return 78;
}
#ifdef _WIN64
__asm__("movq %%gs:0x60,%0" : "=r"(peb));
#else
__asm__("movl %%fs:0x30,%0" : "=r"(peb));
#endif
if (query(GetCurrentProcess(), 0, &information, sizeof(information), &length) || length != sizeof(information) ||
(ULONG_PTR)information.peb != peb || information.pid != GetCurrentProcessId() || !information.affinity || information.priority != 8)
return 2;
if ((ULONG)query(GetCurrentProcess(), 0, &information, sizeof(information) - 1, &length) != 0xc0000004 || length != sizeof(information))
return 3;
if ((ULONG)query((HANDLE)(ULONG_PTR)0x12345678, 0, &information, sizeof(information), &length) != 0xc0000008)
return 4;
if ((ULONG)query(GetCurrentProcess(), 0xffffffff, &information, sizeof(information), &length) != 0xc0000003)
return 5;
if ((ULONG)query(GetCurrentProcess(), 0, NULL, sizeof(information), &length) != 0xc0000005)
return 6;
if ((ULONG)query(GetCurrentProcess(), 0, (void *)1, sizeof(information), &length) != 0xc0000005 ||
(ULONG)query(GetCurrentProcess(), 0, &information, sizeof(information), (ULONG *)1) != 0xc0000005)
return 42;
if ((ULONG)read_memory(GetCurrentProcess(), &value, &replacement, sizeof(value), (SIZE_T *)1) != 0xc0000005 || replacement != 456)
return 43;
if ((ULONG)write_memory(GetCurrentProcess(), &value, &replacement, sizeof(value), (SIZE_T *)1) != 0xc0000005 || value != 123)
return 44;
limited = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, GetCurrentProcessId());
if (!limited || (ULONG)read_memory(limited, &value, &replacement, sizeof(value), &copied) != 0xc0000022 || copied ||
ReadProcessMemory(limited, &value, &replacement, sizeof(value), &copied) || GetLastError() != ERROR_ACCESS_DENIED ||
VirtualAllocEx(limited, NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) || GetLastError() != ERROR_ACCESS_DENIED)
return 45;
CloseHandle(limited);
limited = OpenProcess(PROCESS_VM_READ, FALSE, GetCurrentProcessId());
if (!limited || (ULONG)query(limited, 0, &information, sizeof(information), &length) != 0xc0000022)
return 46;
CloseHandle(limited);
{
MEMORY_BASIC_INFORMATION memory;
void *block = VirtualAllocEx(GetCurrentProcess(), NULL, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!block || VirtualProtectEx(GetCurrentProcess(), block, 4096, PAGE_READONLY, (DWORD *)block) ||
VirtualQueryEx(GetCurrentProcess(), block, &memory, sizeof(memory)) != sizeof(memory) || memory.Protect != PAGE_READWRITE ||
!VirtualFreeEx(GetCurrentProcess(), block, 0, MEM_RELEASE))
return 47;
}
for (index = 0; index < sizeof(startup); ++index)
((BYTE *)&startup)[index] = 0;
startup.cb = sizeof(startup);
command[0] = '"';
used = GetModuleFileNameW(NULL, source, 400);
if (!used || used >= 400)
return 7;
for (index = 0; index < used; ++index) child_directory[index] = source[index];
const WCHAR suffix[] = L".image-\u00e9";
for (index = 0; index < sizeof suffix / sizeof(WCHAR); ++index) child_directory[used + index] = suffix[index];
if (!CreateDirectoryW(child_directory, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) return 105;
DWORD name = used;
while (name && source[name - 1] != '\\') --name;
used += sizeof suffix / sizeof(WCHAR) - 1;
if (used + 1 + lstrlenW(source + name) >= 500) return 106;
for (index = 0; index < used; ++index) child_image[index] = child_directory[index];
child_image[used++] = '\\';
for (index = name; source[index]; ++index) child_image[used++] = source[index];
child_image[used] = 0;
if (!CopyFileW(source, child_image, FALSE)) return 104;
for (index = 0; index < used; ++index) command[index + 1] = child_image[index];
command[++used] = '"';
command[++used] = ' ';
command[++used] = '-';
command[++used] = '-';
command[++used] = 'c';
command[++used] = 0;
if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup, &child)) {
DeleteFileW(child_image);
RemoveDirectoryW(child_directory);
return 8;
}
result = test_image_name(child.hProcess, child_image);
if (!result) result = test_handle_count(query, child.hProcess, FALSE);
if (!result) result = test_child(query, read_memory, write_memory, &child);
if (result) {
TerminateProcess(child.hProcess, 99);
WaitForSingleObject(child.hProcess, 5000);
}
CloseHandle(child.hThread);
CloseHandle(child.hProcess);
#ifdef _WIN64
if (!result) {
DWORD exit_code = 0;
command[used - 1] = 'u';
if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &child))
return 67;
if (WaitForSingleObject(child.hProcess, 15000) != WAIT_OBJECT_0 || !GetExitCodeProcess(child.hProcess, &exit_code) || exit_code != 1)
result = 68;
CloseHandle(child.hThread);
CloseHandle(child.hProcess);
}
#endif
DeleteFileW(child_image);
RemoveDirectoryW(child_directory);
return result;
}