Blame src/process/ntapi_tt_spawn_foreign_process.c

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