/********************************************************/
/* ntapi: Native API core library */
/* Copyright (C) 2013--2021 SysDeer Technologies, LLC */
/* Released under GPLv2 and GPLv3; see COPYING.NTAPI. */
/********************************************************/
#include <psxtypes/psxtypes.h>
#include <pemagine/pemagine.h>
#include <ntapi/nt_status.h>
#include <ntapi/nt_object.h>
#include <ntapi/nt_thread.h>
#include <ntapi/nt_process.h>
#include <ntapi/nt_string.h>
#include <ntapi/ntapi.h>
#include "ntapi_impl.h"
#define __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_ALLOC_SIZE (0x50000)
#define __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_BUFFER_SIZE (0x30000)
#define __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_VECTOR_SIZE (0x10000)
#define __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_IMGBUF_SIZE (0x10000)
#define NT_PROCESS_SPAWN_FLAG_DEBUG_MASK \
(NT_PROCESS_SPAWN_FLAG_DEBUG_EXECUTION \
| NT_PROCESS_SPAWN_FLAG_DEBUG_SUSPENDED)
typedef int32_t win32_create_process_utf16(
__in_opt wchar16_t * appname,
__in_out_opt wchar16_t * cmdline,
__in_opt nt_sa * process_sa_attr,
__in_opt nt_sa * thread_sa_attr,
__in int32_t inherit_handles,
__in uint32_t creation_flags,
__in wchar16_t * environment,
__in_opt wchar16_t * cwd,
__in nt_process_startup_info * startup_info,
__out nt_process_info * process_info);
static int32_t __stdcall __tt_spawn_return(
nt_runtime_data_block * rtblock,
void * himgfile,
void * hprocess,
void * hthread,
int32_t status)
{
if (hprocess) {
__ntapi->zw_terminate_process(
hprocess,status);
__ntapi->zw_close(hprocess);
__ntapi->zw_close(hthread);
}
if (himgfile)
__ntapi->zw_close(himgfile);
__ntapi->zw_free_virtual_memory(
NT_CURRENT_PROCESS_HANDLE,
&rtblock->addr,
&rtblock->size,
NT_MEM_RELEASE);
return status;
}
int32_t __stdcall __ntapi_tt_spawn_foreign_process(nt_spawn_process_params * sparams)
{
int32_t status;
nt_process_info processinfo;
nt_create_process_params cparams;
nt_runtime_data_block rtblock;
nt_unicode_string * imgname;
nt_peb * peb;
char * patharg;
void * hkernel32;
void * hat;
void * hfile;
void * himgfile;
uint32_t written;
int32_t envc;
wchar16_t * imgbuf;
wchar16_t * wenv;
wchar16_t ** wenvp;
char ** parg;
char * mark;
char * ch;
char * ch_arg;
char * ch_cap;
int fquote;
uint32_t finherit;
uint32_t fsuspended;
uint32_t fdebug;
wchar16_t * cmdline;
nt_strconv_mbtonative uparams;
nt_unicode_string nt_image;
nt_unicode_string nt_cmd_line;
win32_create_process_utf16 * create_process_fn;
char create_process_fn_name[]
= "CreateProcessW";
/* validation */
if (!sparams->argv)
return NT_STATUS_INVALID_PARAMETER;
if (!sparams->himage && !sparams->patharg)
return NT_STATUS_OBJECT_PATH_INVALID;
if (!(peb = (nt_peb *)pe_get_peb_address()))
return NT_STATUS_INTERNAL_ERROR;
if (!peb->process_params)
return NT_STATUS_INTERNAL_ERROR;
if (sparams->rtctx || sparams->hsession || sparams->hready)
return NT_STATUS_INVALID_PARAMETER;
/* hkernel32 */
if (!(hkernel32 = pe_get_kernel32_module_handle()))
return NT_STATUS_DLL_NOT_FOUND;
if (!(create_process_fn = (win32_create_process_utf16 *)
(pe_get_procedure_address(
hkernel32,create_process_fn_name))))
return NT_STATUS_PROCEDURE_NOT_FOUND;
/* hat */
hat = (sparams->hroot && (sparams->argv[0][0] == '/'))
? sparams->hroot
: sparams->hcwd
? sparams->hcwd
: peb->process_params->cwd_handle;
/* patharg */
patharg = sparams->patharg
? (sparams->patharg[0] == '/')
? (sparams->patharg[1] == '?')
? &sparams->patharg[0]
: &sparams->patharg[1]
: &sparams->patharg[0]
: 0;
/* rtblock, rdata */
rtblock.addr = 0;
rtblock.size = __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_ALLOC_SIZE;
rtblock.remote_addr = 0;
rtblock.remote_size = 0;
rtblock.flags = 0;
if ((status = __ntapi->zw_allocate_virtual_memory(
NT_CURRENT_PROCESS_HANDLE,
&rtblock.addr,0,
&rtblock.size,
NT_MEM_COMMIT,
NT_PAGE_READWRITE)))
return status;
__ntapi->tt_aligned_block_memset(
rtblock.addr,0,rtblock.size);
/* environment */
for (envc=0,parg=sparams->envp; *parg; parg++)
envc++;
wenvp = rtblock.addr;
wenvp += __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_BUFFER_SIZE / sizeof(*wenvp);
wenv = (wchar16_t *)&wenvp[++envc];
if ((status = __ntapi->tt_array_convert_utf8_to_utf16(
sparams->envp,
wenvp,0,wenv,
__SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_VECTOR_SIZE,
&(size_t){0})))
return __tt_spawn_return(
&rtblock,0,
0,0,status);
/* imgbuf */
imgbuf = rtblock.addr;
imgbuf += __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_BUFFER_SIZE / sizeof(wchar16_t);
imgbuf += __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_VECTOR_SIZE / sizeof(wchar16_t);
/* hfile */
if (sparams->himage) {
hfile = sparams->himage;
himgfile = 0;
} else if ((status = __ntapi_tt_open_file_utf8(
&hfile,hat,patharg,1,
imgbuf,__SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_IMGBUF_SIZE))) {
return __tt_spawn_return(
&rtblock,0,
0,0,status);
himgfile = hfile;
}
/* imgname */
if ((status = __ntapi->zw_query_object(
hfile,
NT_OBJECT_NAME_INFORMATION,
imgbuf,__SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_VECTOR_SIZE,
&written)))
return __tt_spawn_return(
&rtblock,himgfile,
0,0,status);
imgname = (nt_unicode_string *)imgbuf;
/* argv --> cmdline (utf8) */
ch_arg = rtblock.addr;
ch_cap = ch_arg + __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_VECTOR_SIZE;
for (parg=sparams->argv; *parg; parg++) {
for (ch=*parg, fquote=0; *ch && !fquote; ch++)
fquote = ((*ch == ' ')
|| (*ch == '\t')
|| (*ch == '"'));
if (fquote)
*ch_arg++ = '"';
for (ch=*parg; *ch; ) {
if (ch[0] == '\\') {
for (mark=&ch[1]; *mark=='\\'; )
mark++;
if ((ch_arg + 2*(mark-ch)) >= ch_cap)
return __tt_spawn_return(
&rtblock,himgfile,0,0,
NT_STATUS_NAME_TOO_LONG);
if (!mark[0] && fquote) {
for (; *ch=='\\'; ch++) {
*ch_arg++ = '\\';
*ch_arg++ = '\\';
}
} else if (mark[0] == '"') {
for (; *ch=='\\'; ch++) {
*ch_arg++ = '\\';
*ch_arg++ = '\\';
}
} else {
*ch_arg++ = *ch++;
}
} else if (ch[0] == '"') {
*ch_arg++ = '\\';
*ch_arg++ = *ch++;
} else {
*ch_arg++ = *ch++;
}
}
if (fquote)
*ch_arg++ = '"';
*ch_arg++ = ' ';
if (ch_arg >= ch_cap)
return __tt_spawn_return(
&rtblock,himgfile,0,0,
NT_STATUS_NAME_TOO_LONG);
}
ch_arg[-1] = 0;
/* cmdline (utf8) --> cmdline (utf16) */
cmdline = rtblock.addr;
cmdline += __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_VECTOR_SIZE / sizeof(wchar16_t);
uparams.src = rtblock.addr;
uparams.src_size_in_bytes = 0;
uparams.dst = cmdline;
uparams.dst_size_in_bytes = __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_VECTOR_SIZE - sizeof(wchar16_t);
uparams.code_points = 0;
uparams.bytes_written = 0;
if ((status = __ntapi->uc_convert_unicode_stream_utf8_to_utf16(&uparams)))
return __tt_spawn_return(
&rtblock,himgfile,
0,0,status);
else if (uparams.leftover_count)
return __tt_spawn_return(
&rtblock,himgfile,0,0,
NT_STATUS_ILLEGAL_CHARACTER);
/* nt_cmd_line */
nt_cmd_line.strlen = uparams.bytes_written;
nt_cmd_line.maxlen = uparams.bytes_written + sizeof(wchar16_t);
nt_cmd_line.buffer = cmdline;
/* nt_image */
nt_image.buffer = rtblock.addr;
nt_image.buffer += __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_VECTOR_SIZE / sizeof(wchar16_t);
nt_image.buffer += __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_VECTOR_SIZE / sizeof(wchar16_t);
uparams.src = (unsigned char *)sparams->argv[0];
uparams.src_size_in_bytes = 0;
uparams.dst = nt_image.buffer;
uparams.dst_size_in_bytes = __SPAWN_FOREIGN_PROCESS_RUNTIME_BLOCK_VECTOR_SIZE - sizeof(wchar16_t);
uparams.code_points = 0;
uparams.bytes_written = 0;
if ((status = __ntapi->uc_convert_unicode_stream_utf8_to_utf16(&uparams)))
return __tt_spawn_return(
&rtblock,himgfile,
0,0,status);
else if (uparams.leftover_count)
return __tt_spawn_return(
&rtblock,himgfile,0,0,
NT_STATUS_ILLEGAL_CHARACTER);
nt_image.strlen = uparams.bytes_written;
nt_image.maxlen = uparams.bytes_written + sizeof(wchar16_t);
nt_image.buffer[uparams.bytes_written / sizeof(wchar16_t)] = 0;
/* cparams */
__ntapi->tt_aligned_block_memset(
&cparams,0,sizeof(cparams));
cparams.image_name = imgname->buffer;
cparams.creation_flags_thread = NT_PROCESS_CREATE_FLAGS_CREATE_THREAD_SUSPENDED;
/* process_params */
if ((status = __ntapi->rtl_create_process_parameters(
&cparams.process_params,
&nt_image,
(nt_unicode_string *)0,
(nt_unicode_string *)0,
&nt_cmd_line,
wenv,
(nt_unicode_string *)0,
(nt_unicode_string *)0,
(nt_unicode_string *)0,
(nt_unicode_string *)0)))
return __tt_spawn_return(
&rtblock,himgfile,
0,0,status);
__ntapi->rtl_normalize_process_params(cparams.process_params);
if (sparams->startupinfo) {
cparams.process_params->hstdin = sparams->startupinfo->hstdin;
cparams.process_params->hstdout = sparams->startupinfo->hstdout;
cparams.process_params->hstderr = sparams->startupinfo->hstderr;
}
/* inherit handles? */
if (cparams.process_params->hstdin
|| cparams.process_params->hstdout
|| cparams.process_params->hstderr)
finherit = 1;
else if (sparams->processflags & NT_PROCESS_CREATE_FLAGS_INHERIT_HANDLES)
finherit = 1;
else
finherit = 0;
/* process flags */
fsuspended = 0;
fdebug = 0;
if (sparams->processflags & NT_PROCESS_CREATE_FLAGS_CREATE_THREAD_SUSPENDED)
fsuspended = NT_CREATE_SUSPENDED;
else if (sparams->threadflags & NT_CREATE_SUSPENDED)
fsuspended = NT_CREATE_SUSPENDED;
else if (sparams->spawnflags & NT_PROCESS_SPAWN_FLAG_DEBUG_SUSPENDED)
fsuspended = NT_CREATE_SUSPENDED;
else if (sparams->spawnflags & NT_PROCESS_SPAWN_FLAG_DEBUG_EXECUTION)
fdebug = NT_CREATE_SUSPENDED;
/* hoppla: try either via kernel32 (sparams->startupinfo), or natively */
if (sparams->spawnflags & NT_PROCESS_SPAWN_FLAG_DELEGATE_TO_SYSTEM_LIBRARY) {
processinfo.hprocess = 0;
processinfo.hthread = 0;
processinfo.process_id = 0;
processinfo.thread_id = 0;
if (!(create_process_fn(
nt_image.buffer,
nt_cmd_line.buffer,
0,
0,
finherit,
NT_PROCESS_INTEROP_FLAG_UNICODE_ENVIRONMENT
| sparams->interopflags | fsuspended | fdebug,
wenv,
sparams->cwd,
sparams->startupinfo,
&processinfo)))
return __tt_spawn_return(
&rtblock,himgfile,
0,0,status);
if ((status = __ntapi->zw_query_information_process(
processinfo.hprocess,
NT_PROCESS_BASIC_INFORMATION,
&cparams.pbi,sizeof(cparams.pbi),
0)))
return __tt_spawn_return(
&rtblock,himgfile,
processinfo.hprocess,
processinfo.hthread,
status);
cparams.hprocess = processinfo.hprocess;
cparams.hthread = processinfo.hthread;
cparams.cid.process_id = processinfo.process_id;
cparams.cid.thread_id = processinfo.thread_id;
} else {
cparams.creation_flags_thread = NT_PROCESS_CREATE_FLAGS_CREATE_THREAD_SUSPENDED;
if (finherit)
cparams.creation_flags_process |= NT_PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
if ((status = __ntapi->tt_create_native_process(&cparams)))
return __tt_spawn_return(
&rtblock,himgfile,
0,0,status);
}
/* debug */
if (sparams->spawnflags & NT_PROCESS_SPAWN_FLAG_DEBUG_MASK)
if ((status = __ntapi->tt_debug_create_attach_object(
&sparams->hdbgobj,
cparams.hprocess,
NT_DEBUG_KILL_ON_EXIT)))
return __tt_spawn_return(
&rtblock,
himgfile,
cparams.hprocess,
cparams.hthread,
status);
/* output */
sparams->hprocess = cparams.hprocess;
sparams->hthread = cparams.hthread;
sparams->cid.process_id = cparams.pbi.unique_process_id;
sparams->cid.thread_id = cparams.cid.thread_id;
__ntapi->tt_generic_memcpy(
&sparams->pbi,
&cparams.pbi,
sizeof(nt_pbi));
/* create suspended? */
if (fsuspended)
return __tt_spawn_return(
&rtblock,himgfile,
0,0,NT_STATUS_SUCCESS);
/* tada */
if ((status = __ntapi->zw_resume_thread(cparams.hthread,0)))
return __tt_spawn_return(
&rtblock,himgfile,
cparams.hprocess,
cparams.hthread,
status);
/* all done */
return __tt_spawn_return(
&rtblock,himgfile,
0,0,NT_STATUS_SUCCESS);
}