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
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,
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
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;
d326cc
	uint32_t			written;
d326cc
	wchar16_t *			imgbuf;
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;
d326cc
	rtblock.size		= 0x40000;
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
d326cc
	/* imgbuf */
d326cc
	imgbuf  = (wchar16_t *)rtblock.addr;
d326cc
	imgbuf += 0x30000 / sizeof(*imgbuf);
d326cc
d326cc
	/* hfile */
d326cc
	if (sparams->himage)
d326cc
		hfile = sparams->himage;
d326cc
d326cc
	else if ((status = __ntapi_tt_open_file_utf8(
d326cc
			&hfile,hat,patharg,1,
d326cc
			imgbuf,0x2000)))
d326cc
		return status;
d326cc
d326cc
	/* imgname */
d326cc
	if ((status = __ntapi->zw_query_object(
d326cc
			hfile,
d326cc
			NT_OBJECT_NAME_INFORMATION,
d326cc
			imgbuf,0x10000,&written)))
d326cc
		return __tt_spawn_return(
d326cc
			&rtblock,0,0,status);
d326cc
d326cc
	imgname = (nt_unicode_string *)imgbuf;
d326cc
d326cc
	/* argv --> cmdline (utf8) */
d326cc
	ch_arg = (char *)rtblock.addr;
d326cc
	ch_cap = ch_arg + 0x10000;
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(
d326cc
						&rtblock,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(
d326cc
				&rtblock,0,0,
d326cc
				NT_STATUS_NAME_TOO_LONG);
d326cc
d326cc
	}
d326cc
d326cc
	ch_arg[-1] = 0;
d326cc
d326cc
	/* cmdline (utf8) --> cmdline (utf16) */
d326cc
	cmdline  = (wchar16_t *)rtblock.addr;
d326cc
	cmdline += (0x10000 / sizeof(wchar16_t));
d326cc
d326cc
	uparams.src                = (unsigned char *)rtblock.addr;
d326cc
	uparams.src_size_in_bytes  = 0;
d326cc
	uparams.dst                = cmdline;
d326cc
	uparams.dst_size_in_bytes  = 0x10000 - 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(
d326cc
			&rtblock,0,0,status);
d326cc
d326cc
	else if (uparams.leftover_count)
d326cc
		return __tt_spawn_return(
d326cc
			&rtblock,0,0,
d326cc
			NT_STATUS_ILLEGAL_CHARACTER);
d326cc
d326cc
	cmdline[uparams.bytes_written / sizeof(wchar16_t)] = 0;
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 */
d326cc
	nt_image.buffer  = (wchar16_t *)rtblock.addr;
d326cc
	nt_image.buffer += (0x20000 / 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;
d326cc
	uparams.dst_size_in_bytes  = 0x10000 - 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(
d326cc
			&rtblock,0,0,status);
d326cc
d326cc
	else if (uparams.leftover_count)
d326cc
		return __tt_spawn_return(
d326cc
			&rtblock,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,
d326cc
			__ntapi->tt_get_peb_env_block_utf16(),
d326cc
			(nt_unicode_string *)0,
d326cc
			(nt_unicode_string *)0,
d326cc
			(nt_unicode_string *)0,
d326cc
			(nt_unicode_string *)0)))
d326cc
		return 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,
168f83
				sparams->interopflags | fsuspended | fdebug,
ec0b96
				0,
29b2c1
				sparams->cwd,
ec0b96
				sparams->startupinfo,
ec0b96
				&processinfo)))
ec0b96
			return __tt_spawn_return(
ec0b96
				&rtblock,0,0,status);
ec0b96
ec0b96
		if ((status = __ntapi->zw_query_information_process(
ec0b96
			processinfo.hprocess,
ec0b96
			NT_PROCESS_BASIC_INFORMATION,
ec0b96
			&cparams.pbi,sizeof(cparams.pbi),
ec0b96
			0)))
ec0b96
			return __tt_spawn_return(
ec0b96
				&rtblock,0,0,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(
ec0b96
				&rtblock,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,
168f83
				cparams.hprocess,
168f83
				cparams.hthread,
168f83
				status);
168f83
d326cc
	/* tidy up */
d326cc
	if (!sparams->himage)
d326cc
		__ntapi->zw_close(hfile);
d326cc
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(
d326cc
			&rtblock,0,0,NT_STATUS_SUCCESS);
d326cc
d326cc
	/* tada */
d326cc
	if ((status = __ntapi->zw_resume_thread(cparams.hthread,0)))
d326cc
		return __tt_spawn_return(
d326cc
			&rtblock,
d326cc
			cparams.hprocess,
d326cc
			cparams.hthread,
d326cc
			status);
d326cc
d326cc
	/* all done */
d326cc
	return __tt_spawn_return(
d326cc
		&rtblock,0,0,NT_STATUS_SUCCESS);
d326cc
}