Blame src/process/ntapi_tt_spawn_native_process.c

c164ff
/********************************************************/
c164ff
/*  ntapi: Native API core library                      */
64e606
/*  Copyright (C) 2013--2021  SysDeer Technologies, LLC */
c164ff
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
c164ff
/********************************************************/
c164ff
c164ff
#include <psxtypes/psxtypes.h>
c164ff
#include <pemagine/pemagine.h>
c164ff
#include <ntapi/nt_status.h>
c164ff
#include <ntapi/nt_object.h>
c164ff
#include <ntapi/nt_thread.h>
c164ff
#include <ntapi/nt_process.h>
c164ff
#include <ntapi/nt_string.h>
c164ff
#include <ntapi/ntapi.h>
c164ff
#include "ntapi_impl.h"
c164ff
b625d1
#define NT_PROCESS_SPAWN_FLAG_DEBUG_MASK                \
b625d1
		(NT_PROCESS_SPAWN_FLAG_DEBUG_EXECUTION   \
b625d1
		 | NT_PROCESS_SPAWN_FLAG_DEBUG_SUSPENDED)
b625d1
c164ff
static int32_t __stdcall __tt_spawn_return(
c164ff
	nt_runtime_data_block *	rtblock,
c164ff
	void *			hprocess,
c164ff
	void *			hthread,
c164ff
	int32_t			status)
c164ff
{
c164ff
	nt_runtime_data * rtdata;
c164ff
c164ff
	rtdata = (nt_runtime_data *)rtblock->addr;
c164ff
c164ff
	if (hprocess) {
c164ff
		__ntapi->zw_terminate_process(
c164ff
			hprocess,status);
c164ff
c164ff
		__ntapi->zw_close(hprocess);
c164ff
		__ntapi->zw_close(hthread);
c164ff
	}
c164ff
c164ff
	if (rtdata->hready)
c164ff
		__ntapi->zw_close(
c164ff
			rtdata->hready);
c164ff
c164ff
	__ntapi->zw_free_virtual_memory(
c164ff
		NT_CURRENT_PROCESS_HANDLE,
c164ff
		&rtblock->addr,
c164ff
		&rtblock->size,
c164ff
		NT_MEM_RELEASE);
c164ff
c164ff
	return status;
c164ff
}
c164ff
c164ff
int32_t __stdcall __ntapi_tt_spawn_native_process(nt_spawn_process_params * sparams)
c164ff
{
c164ff
	int32_t				status;
c164ff
	nt_create_process_params	cparams;
c164ff
	nt_tty_session_info		session;
c164ff
	nt_runtime_data_block		rtblock;
c164ff
	nt_runtime_data_block		crtblock;
c164ff
	nt_runtime_data *		rtctx;
c164ff
	nt_runtime_data *		rdata;
c164ff
	nt_unicode_string *		imgname;
c164ff
	nt_peb * 			peb;
c164ff
	char *				patharg;
c164ff
	void *				hat;
c164ff
	void *				hfile;
fd587c
	char *				src;
fd587c
	char *				dst;
fd587c
	int				envc;
fd587c
	char **				penv;
fd587c
	char **				pref;
c164ff
	char **				parg;
c164ff
	char **				rargv;
c164ff
	char **				renvp;
c164ff
	wchar16_t **			pwarg;
f2efc5
	wchar16_t *			wenv;
c164ff
	wchar16_t *			wch;
c164ff
	void *				hchild[2];
c164ff
	wchar16_t *			imgbuf;
ec0b96
	uint32_t			fsuspended;
da4030
	size_t				buflen;
da4030
	size_t				written;
fd587c
	size_t				needed;
fd587c
	uintptr_t			addr;
103960
	char *				raddr;
103960
	size_t				rsize;
c164ff
c164ff
	/* rtctx (convenience) */
c164ff
	rtctx = sparams->rtctx;
c164ff
c164ff
	/* validation */
c164ff
	if (!sparams->himage && !sparams->patharg)
c164ff
		return NT_STATUS_OBJECT_PATH_INVALID;
c164ff
c164ff
	if (rtctx->argc || rtctx->argv || rtctx->envc || rtctx->envp)
c164ff
		return NT_STATUS_INVALID_PARAMETER;
c164ff
c164ff
	if (rtctx->hready || rtctx->hsession)
c164ff
		return NT_STATUS_INVALID_PARAMETER_MIX;
c164ff
c164ff
	if (!(peb = (nt_peb *)pe_get_peb_address()))
c164ff
		return NT_STATUS_INTERNAL_ERROR;
c164ff
c164ff
	if (!peb->process_params)
c164ff
		return NT_STATUS_INTERNAL_ERROR;
c164ff
c164ff
	/* hat */
3c26f0
	hat = (sparams->hroot && (sparams->argv[0][0] == '/'))
3c26f0
		? sparams->hroot
c164ff
		: rtctx->hcwd
c164ff
			? rtctx->hcwd
c164ff
			: peb->process_params->cwd_handle;
c164ff
c164ff
	/* patharg */
c03267
	patharg = sparams->patharg
c03267
		? (sparams->patharg[0] == '/')
c03267
			? (sparams->patharg[1] == '?')
c03267
				? &sparams->patharg[0]
c03267
				: &sparams->patharg[1]
c03267
			: &sparams->patharg[0]
c03267
		: 0;
c164ff
c164ff
	/* rtblock, rdata */
c164ff
	rtblock.addr		= 0;
da4030
	rtblock.size		= 0x40000;
c164ff
	rtblock.remote_addr	= 0;
c164ff
	rtblock.remote_size	= 0;
c164ff
	rtblock.flags		= 0;
c164ff
c164ff
	if ((status = __ntapi->zw_allocate_virtual_memory(
c164ff
			NT_CURRENT_PROCESS_HANDLE,
c164ff
			&rtblock.addr,0,
c164ff
			&rtblock.size,
c164ff
			NT_MEM_COMMIT,
c164ff
			NT_PAGE_READWRITE)))
c164ff
		return status;
c164ff
c164ff
	__ntapi->tt_aligned_block_memset(
c164ff
		rtblock.addr,0,rtblock.size);
c164ff
c164ff
	__ntapi->tt_aligned_block_memcpy(
c164ff
		(uintptr_t *)(rdata = (nt_runtime_data *)rtblock.addr),
c164ff
		(const uintptr_t *)rtctx,
c164ff
		sizeof(*rtctx));
c164ff
804edf
	/* abi */
804edf
	if (!(__ntapi->tt_guid_compare(&rdata->abi,&(nt_guid)NT_PROCESS_GUID_UNSPEC)))
804edf
		__ntapi->tt_guid_copy(
804edf
			&rdata->abi,
804edf
			&(nt_guid)NT_PROCESS_GUID_RTDATA);
804edf
c164ff
	/* imgbuf */
c164ff
	imgbuf  = (wchar16_t *)rtblock.addr;
da4030
	imgbuf += 0x30000 / sizeof(*imgbuf);
c164ff
c164ff
	/* hfile */
c164ff
	if (sparams->himage)
c164ff
		hfile = sparams->himage;
c164ff
	else if ((status = __ntapi_tt_open_file_utf8(
c164ff
			&hfile,hat,patharg,1,
c164ff
			imgbuf,0x2000)))
c164ff
		return status;
c164ff
c164ff
	/* imgname */
c164ff
	if ((status = __ntapi->zw_query_object(
c164ff
			hfile,
c164ff
			NT_OBJECT_NAME_INFORMATION,
da4030
			imgbuf,0x10000,
da4030
			&(uint32_t){0})))
c164ff
		return __tt_spawn_return(
c164ff
			&rtblock,0,0,status);
c164ff
c164ff
	imgname = (nt_unicode_string *)imgbuf;
c164ff
c164ff
	/* argv, envp */
da4030
	buflen  = rtblock.size;
da4030
	buflen -= sizeof(*rdata);
da4030
c164ff
	if ((status = __ntapi->tt_array_copy_utf8(
c164ff
			&rdata->argc,
c164ff
			(const char **)sparams->argv,
c164ff
			(const char **)sparams->envp,
2104cf
			sparams->interp,
c164ff
			sparams->optarg,
15812f
			sparams->script,
c164ff
			rtblock.addr,
c164ff
			rdata->buffer,
da4030
			buflen,&written)))
c164ff
		return __tt_spawn_return(
c164ff
			&rtblock,0,0,status);
c164ff
da4030
	rdata->argv   = (char **)&((nt_runtime_data *)0)->buffer;
da4030
	rdata->envp   = rdata->argv + rdata->argc + 1;
c164ff
da4030
	rdata->wargv  = (wchar16_t **)rdata->buffer;
da4030
	rdata->wargv += written / sizeof(wchar16_t **) + 1;
da4030
	rdata->wenvp  = rdata->wargv + rdata->argc + 1;
c164ff
c164ff
	rargv = rdata->argv + ((uintptr_t)rtblock.addr / sizeof(char *));
c164ff
	renvp = rdata->envp + ((uintptr_t)rtblock.addr / sizeof(char *));
c164ff
c164ff
	for (rdata->envc=0, parg=sparams->envp; *parg; parg++)
c164ff
		rdata->envc++;
c164ff
c164ff
	pwarg = rdata->wenvp + rdata->envc + 1;
da4030
	wch   = (wchar16_t *)pwarg;
da4030
de53a2
	if ((written = (uintptr_t)wch - (uintptr_t)rdata) > rtblock.size)
da4030
		return __tt_spawn_return(
da4030
			&rtblock,0,0,NT_STATUS_BUFFER_TOO_SMALL);
da4030
da4030
	buflen  = rtblock.size;
da4030
	buflen -= written;
c164ff
c164ff
	if ((status = __ntapi->tt_array_convert_utf8_to_utf16(
c164ff
			rargv,
c164ff
			rdata->wargv,
da4030
			rdata,wch,
da4030
			buflen,&written)))
c164ff
		return __tt_spawn_return(
c164ff
			&rtblock,0,0,status);
c164ff
da4030
	wch    += written/sizeof(wchar16_t);
da4030
	buflen -= written;
c164ff
c164ff
	if ((status = __ntapi->tt_array_convert_utf8_to_utf16(
c164ff
			renvp,
c164ff
			rdata->wenvp,
da4030
			rdata,wch,
da4030
			buflen,&written)))
c164ff
		return __tt_spawn_return(
c164ff
			&rtblock,0,0,status);
c164ff
c164ff
	rdata->wargv -= (uintptr_t)rtblock.addr / sizeof(wchar16_t *);
c164ff
	rdata->wenvp -= (uintptr_t)rtblock.addr / sizeof(wchar16_t *);
c164ff
f2efc5
	wenv    = wch;
da4030
	wch    += written/sizeof(wchar16_t);
da4030
	buflen -= written;
da4030
fd587c
	/* w32 environment */
fd587c
	if (rtctx->w32_envp) {
fd587c
		addr  = (uintptr_t)wch;
fd587c
		addr += sizeof(uintptr_t) - 1;
fd587c
		addr /= sizeof(uintptr_t);
fd587c
		addr *= sizeof(uintptr_t);
fd587c
fd587c
		penv   = rtctx->w32_envp;
fd587c
		needed = sizeof(char *);
fd587c
fd587c
		for (envc=0; *penv; envc++) {
fd587c
			needed += sizeof(char *);
fd587c
			needed += __ntapi->tt_string_null_offset_multibyte(*penv);
fd587c
			penv++;
fd587c
		}
fd587c
fd587c
		needed += sizeof(uintptr_t) - 1;
fd587c
		needed /= sizeof(uintptr_t);
fd587c
		needed *= sizeof(uintptr_t);
fd587c
fd587c
		if (buflen < needed)
fd587c
			return __tt_spawn_return(
fd587c
				&rtblock,0,0,NT_STATUS_BUFFER_TOO_SMALL);
fd587c
fd587c
		rdata->w32_envp = (char **)(addr - (uintptr_t)rtblock.addr);
fd587c
fd587c
		pref = (char **)addr;
fd587c
		dst  = (char *)&pref[++envc];
fd587c
fd587c
fd587c
		for (penv=rtctx->w32_envp; *penv; penv++) {
fd587c
			*pref++ = dst - (uintptr_t)rtblock.addr;
fd587c
fd587c
			for (src=*penv; *src; src++,dst++)
fd587c
				*dst = *src;
fd587c
fd587c
			*dst++ = 0;
fd587c
		}
fd587c
fd587c
		wch    += needed / sizeof(wchar16_t);
fd587c
		buflen -= needed;
fd587c
	}
fd587c
da4030
	if (buflen < 0x10000)
da4030
		return __tt_spawn_return(
da4030
			&rtblock,0,0,NT_STATUS_BUFFER_TOO_SMALL);
da4030
c164ff
	/* session */
c164ff
	if (sparams->hready) {
c164ff
		if ((status = __ntapi->zw_duplicate_object(
c164ff
				NT_CURRENT_PROCESS_HANDLE,
c164ff
				sparams->hready,
c164ff
				NT_CURRENT_PROCESS_HANDLE,
c164ff
				&rdata->hready,
c164ff
				0,0,
c164ff
				NT_DUPLICATE_SAME_ACCESS|NT_DUPLICATE_SAME_ATTRIBUTES)))
c164ff
			return __tt_spawn_return(
c164ff
				&rtblock,0,0,status);
c164ff
	} else {
c164ff
		if ((status = __ntapi->tt_create_inheritable_event(
c164ff
				&rdata->hready,
c164ff
				NT_NOTIFICATION_EVENT,
c164ff
				NT_EVENT_NOT_SIGNALED)))
c164ff
			return __tt_spawn_return(
c164ff
				&rtblock,0,0,status);
c164ff
	}
c164ff
ec0b96
	/* process flags */
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
b625d1
	else if (sparams->spawnflags & NT_PROCESS_SPAWN_FLAG_DEBUG_SUSPENDED)
b625d1
		fsuspended = NT_CREATE_SUSPENDED;
b625d1
b625d1
	else if (sparams->spawnflags & NT_PROCESS_SPAWN_FLAG_DEBUG_EXECUTION)
b625d1
		fsuspended = NT_CREATE_SUSPENDED;
b625d1
c164ff
	/* cparams */
c164ff
	__ntapi->tt_aligned_block_memset(
c164ff
		&cparams,0,sizeof(cparams));
c164ff
c164ff
	__ntapi->tt_generic_memcpy(
c164ff
		&crtblock,&rtblock,sizeof(rtblock));
c164ff
c164ff
	cparams.image_name		= imgname->buffer;
c164ff
	cparams.creation_flags_process	= NT_PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
c164ff
	cparams.creation_flags_thread	= NT_PROCESS_CREATE_FLAGS_CREATE_THREAD_SUSPENDED;
f2efc5
	cparams.environment		= wenv;
c164ff
da4030
	crtblock.size    = (size_t)wch - (size_t)rdata;
da4030
	crtblock.size   += 0xFFFF;
da4030
	crtblock.size   |= 0xFFFF;
da4030
	crtblock.size   ^= 0xFFFF;
da4030
	cparams.rtblock  = &crtblock;
c164ff
c164ff
	/* hoppla */
c164ff
	if ((status = __ntapi->tt_create_native_process(&cparams)))
c164ff
		return __tt_spawn_return(
c164ff
			&rtblock,0,0,status);
c164ff
39c844
	/* tidy up */
39c844
	if (!sparams->himage)
39c844
		__ntapi->zw_close(hfile);
39c844
b625d1
	/* debug */
b625d1
	if (sparams->spawnflags & NT_PROCESS_SPAWN_FLAG_DEBUG_MASK)
b625d1
		if ((status = __ntapi->tt_debug_create_attach_object(
b625d1
				&sparams->hdbgobj,
b625d1
				cparams.hprocess,
b625d1
				NT_DEBUG_KILL_ON_EXIT)))
b625d1
			return __tt_spawn_return(
b625d1
				&rtblock,
b625d1
				cparams.hprocess,
b625d1
				cparams.hthread,
b625d1
				status);
b625d1
103960
	/* additional context */
103960
	if (rtctx->ctx_addr) {
103960
		rdata->ctx_addr   = 0;
103960
		rdata->ctx_commit = rtctx->ctx_size;
103960
103960
		rdata->ctx_commit += (__NT_INTERNAL_PAGE_SIZE - 1);
103960
		rdata->ctx_commit |= (__NT_INTERNAL_PAGE_SIZE - 1);
103960
		rdata->ctx_commit ^= (__NT_INTERNAL_PAGE_SIZE - 1);
103960
103960
		if ((status = __ntapi->zw_allocate_virtual_memory(
103960
				cparams.hprocess,
103960
				&rdata->ctx_addr,0,
103960
				&rdata->ctx_commit,
103960
				NT_MEM_COMMIT,
103960
				NT_PAGE_READWRITE)))
103960
			return __tt_spawn_return(
103960
				&rtblock,
103960
				cparams.hprocess,
103960
				cparams.hthread,
103960
				status);
103960
103960
		if ((status = __ntapi->zw_write_virtual_memory(
103960
				cparams.hprocess,
103960
				rdata->ctx_addr,
103960
				rtctx->ctx_addr,
103960
				rtctx->ctx_size,
103960
				&rdata->ctx_size)))
103960
			return __tt_spawn_return(
103960
				&rtblock,
103960
				cparams.hprocess,
103960
				cparams.hthread,
103960
				status);
103960
103960
		raddr  = crtblock.remote_addr;
103960
		raddr += __offsetof(nt_runtime_data,ctx_addr);
103960
103960
		rsize  = __offsetof(nt_runtime_data,ctx_offset);
103960
		rsize -= __offsetof(nt_runtime_data,ctx_addr);
103960
103960
		if ((status = __ntapi->zw_write_virtual_memory(
103960
				cparams.hprocess,
103960
				raddr,(char *)&rdata->ctx_addr,
103960
				rsize,&rsize)))
103960
			return __tt_spawn_return(
103960
				&rtblock,
103960
				cparams.hprocess,
103960
				cparams.hthread,
103960
				status);
103960
	}
103960
c164ff
	/* tty session (optional) */
c164ff
	if (sparams->hsession) {
c164ff
		if ((status = __ntapi->tty_client_process_register(
c164ff
				sparams->hsession,
c164ff
				cparams.pbi.unique_process_id,
c164ff
				0,NT_TTY_INHERIT_HANDLES,0)))
c164ff
			return __tt_spawn_return(
c164ff
				&rtblock,
c164ff
				cparams.hprocess,
c164ff
				cparams.hthread,
c164ff
				status);
c164ff
c164ff
		session.pid    = rtctx->alt_cid_self.pid;
c164ff
		session.pgid   = rtctx->alt_cid_self.pgid;
c164ff
		session.sid    = rtctx->alt_cid_self.sid;
3e609e
		session.syspid = (uint32_t)cparams.pbi.unique_process_id;
c164ff
875bdc
		if ((status = __ntapi->tty_client_session_set(
875bdc
				sparams->hsession,
875bdc
				&session)))
c164ff
			return __tt_spawn_return(
c164ff
				&rtblock,
c164ff
				cparams.hprocess,
c164ff
				cparams.hthread,
c164ff
				status);
c164ff
	}
c164ff
c164ff
	/* output */
c164ff
	sparams->hprocess = cparams.hprocess;
c164ff
	sparams->hthread  = cparams.hthread;
c164ff
	sparams->rdata    = crtblock.remote_addr;
c164ff
c164ff
	sparams->cid.process_id = cparams.pbi.unique_process_id;
c164ff
	sparams->cid.thread_id  = cparams.cid.thread_id;
c164ff
c164ff
	__ntapi->tt_generic_memcpy(
c164ff
		&sparams->pbi,
c164ff
		&cparams.pbi,
c164ff
		sizeof(nt_pbi));
c164ff
c164ff
	/* create suspended? */
ec0b96
	if (fsuspended)
c164ff
		return __tt_spawn_return(
c164ff
			&rtblock,0,0,NT_STATUS_SUCCESS);
c164ff
c164ff
	/* tada */
c164ff
	if ((status = __ntapi->zw_resume_thread(cparams.hthread,0)))
c164ff
		return __tt_spawn_return(
c164ff
			&rtblock,
c164ff
			cparams.hprocess,
c164ff
			cparams.hthread,
c164ff
			status);
c164ff
c164ff
	/* hready */
c164ff
	hchild[1] = cparams.hprocess;
c164ff
	hchild[0] = rdata->hready;
c164ff
c164ff
	__ntapi->zw_wait_for_multiple_objects(
c164ff
		2,hchild,
c164ff
		NT_WAIT_ANY,
c164ff
		NT_SYNC_NON_ALERTABLE,
c164ff
		sparams->timeout);
c164ff
c164ff
	if ((status = __ntapi->zw_query_event(
c164ff
			rdata->hready,
c164ff
			NT_EVENT_BASIC_INFORMATION,
c164ff
			&sparams->eready,
c164ff
			sizeof(sparams->eready),
c164ff
			&(size_t){0})))
c164ff
		return __tt_spawn_return(
c164ff
			&rtblock,
c164ff
			cparams.hprocess,
c164ff
			cparams.hthread,
c164ff
			status);
c164ff
c164ff
	/* all done */
c164ff
	return __tt_spawn_return(
c164ff
		&rtblock,0,0,NT_STATUS_SUCCESS);
c164ff
}