diff --git a/include/ntapi/nt_process.h b/include/ntapi/nt_process.h
index 092a95e..0443b3f 100644
--- a/include/ntapi/nt_process.h
+++ b/include/ntapi/nt_process.h
@@ -6,6 +6,7 @@
 #include "nt_object.h"
 #include "nt_memory.h"
 #include "nt_section.h"
+#include "nt_sync.h"
 
 typedef enum _nt_process_info_class {
 	NT_PROCESS_BASIC_INFORMATION,
@@ -533,6 +534,29 @@ typedef struct _nt_create_process_params {
 } nt_create_process_params;
 
 
+typedef struct _nt_spawn_process_params {
+	__out		void *				hprocess;
+	__out		void *				hthread;
+	__out		void *				rdata;
+	__out		nt_client_id			cid;
+	__out		nt_process_basic_information	pbi;
+	__out		nt_event_basic_information	eready;
+	__in		nt_runtime_data *		rtctx;
+	__in		void *				hsession;
+	__in		void *				htoken;
+	__in		void *				himage;
+	__in		char *				patharg;
+	__in		const char *			image;
+	__in		const char *			interpreter;
+	__in		const char *			optarg;
+	__in		char **				argv;
+	__in		char **				envp;
+	__in		void *				hready;
+	__in		nt_timeout *			timeout;
+	__in		int				fsuspended;
+} nt_spawn_process_params;
+
+
 typedef int32_t	__stdcall ntapi_zw_create_process(
 	__out	void **			hprocess,
 	__in	uint32_t		desired_access,
@@ -659,6 +683,8 @@ typedef int32_t	__stdcall ntapi_tt_create_remote_process_params(
 typedef int32_t __stdcall ntapi_tt_create_native_process(
 	__out	nt_create_process_params *	params);
 
+typedef int32_t __stdcall ntapi_tt_spawn_native_process(
+	__in_out	nt_spawn_process_params * sparams);
 
 typedef int32_t __stdcall ntapi_tt_get_runtime_data(
 	__out		nt_runtime_data **	pdata,
diff --git a/include/ntapi/ntapi.h b/include/ntapi/ntapi.h
index b596f32..21af579 100644
--- a/include/ntapi/ntapi.h
+++ b/include/ntapi/ntapi.h
@@ -448,6 +448,7 @@ typedef struct _ntapi_vtbl {
 	ntapi_tt_fork *					tt_fork;
 	ntapi_tt_create_remote_process_params *		tt_create_remote_process_params;
 	ntapi_tt_create_native_process *		tt_create_native_process;
+	ntapi_tt_spawn_native_process *			tt_spawn_native_process;
 	ntapi_tt_get_runtime_data *			tt_get_runtime_data;
 	ntapi_tt_init_runtime_data *			tt_init_runtime_data;
 	ntapi_tt_update_runtime_data *			tt_update_runtime_data;
diff --git a/project/common.mk b/project/common.mk
index 09f23bf..fe3a444 100644
--- a/project/common.mk
+++ b/project/common.mk
@@ -38,6 +38,7 @@ COMMON_SRCS = \
 	src/process/ntapi_tt_get_runtime_data.c \
 	src/process/ntapi_tt_init_runtime_data.c \
 	src/process/ntapi_tt_map_image_as_data.c \
+	src/process/ntapi_tt_spawn_native_process.c \
 	src/process/tt_fork_v1.c \
 	src/pty/ntapi_pty_cancel.c \
 	src/pty/ntapi_pty_fd.c \
diff --git a/src/internal/ntapi.c b/src/internal/ntapi.c
index de44bbf..6559b5b 100644
--- a/src/internal/ntapi.c
+++ b/src/internal/ntapi.c
@@ -212,6 +212,7 @@ static int32_t __fastcall __ntapi_init_once(ntapi_vtbl ** pvtbl)
 
 	/* nt_process.h */
 	__ntapi->tt_create_remote_process_params 		= __ntapi_tt_create_remote_process_params;
+	__ntapi->tt_spawn_native_process			= __ntapi_tt_spawn_native_process;
 	__ntapi->tt_get_runtime_data 				= __ntapi_tt_get_runtime_data;
 	__ntapi->tt_init_runtime_data				= __ntapi_tt_init_runtime_data;
 	__ntapi->tt_update_runtime_data				= __ntapi_tt_update_runtime_data;
diff --git a/src/internal/ntapi_fnapi.h b/src/internal/ntapi_fnapi.h
index 723576b..6a1f14c 100644
--- a/src/internal/ntapi_fnapi.h
+++ b/src/internal/ntapi_fnapi.h
@@ -100,6 +100,7 @@ ntapi_tt_create_remote_process_params		__ntapi_tt_create_remote_process_params;
 ntapi_tt_create_remote_runtime_data		__ntapi_tt_create_remote_runtime_data;
 ntapi_tt_create_native_process			__ntapi_tt_create_native_process_v1;
 ntapi_tt_create_native_process			__ntapi_tt_create_native_process_v2;
+ntapi_tt_spawn_native_process			__ntapi_tt_spawn_native_process;
 ntapi_tt_get_runtime_data			__ntapi_tt_get_runtime_data;
 ntapi_tt_init_runtime_data			__ntapi_tt_init_runtime_data;
 ntapi_tt_update_runtime_data			__ntapi_tt_update_runtime_data;
diff --git a/src/process/ntapi_tt_spawn_native_process.c b/src/process/ntapi_tt_spawn_native_process.c
new file mode 100644
index 0000000..f072426
--- /dev/null
+++ b/src/process/ntapi_tt_spawn_native_process.c
@@ -0,0 +1,319 @@
+/********************************************************/
+/*  ntapi: Native API core library                      */
+/*  Copyright (C) 2013--2016  Z. Gilboa                 */
+/*  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"
+
+static int32_t __stdcall __tt_spawn_return(
+	nt_runtime_data_block *	rtblock,
+	void *			hprocess,
+	void *			hthread,
+	int32_t			status)
+{
+	nt_runtime_data * rtdata;
+
+	rtdata = (nt_runtime_data *)rtblock->addr;
+
+	if (hprocess) {
+		__ntapi->zw_terminate_process(
+			hprocess,status);
+
+		__ntapi->zw_close(hprocess);
+		__ntapi->zw_close(hthread);
+	}
+
+	if (rtdata->hready)
+		__ntapi->zw_close(
+			rtdata->hready);
+
+	__ntapi->zw_free_virtual_memory(
+		NT_CURRENT_PROCESS_HANDLE,
+		&rtblock->addr,
+		&rtblock->size,
+		NT_MEM_RELEASE);
+
+	return status;
+}
+
+int32_t __stdcall __ntapi_tt_spawn_native_process(nt_spawn_process_params * sparams)
+{
+	int32_t				status;
+	nt_create_process_params	cparams;
+	nt_tty_session_info		session;
+	nt_runtime_data_block		rtblock;
+	nt_runtime_data_block		crtblock;
+	nt_runtime_data *		rtctx;
+	nt_runtime_data *		rdata;
+	nt_unicode_string *		imgname;
+	nt_peb * 			peb;
+	char *				patharg;
+	void *				hat;
+	void *				hfile;
+	char **				parg;
+	char **				rargv;
+	char **				renvp;
+	wchar16_t **			pwarg;
+	wchar16_t *			wch;
+	void *				hchild[2];
+	uint32_t			written;
+	wchar16_t *			imgbuf;
+
+	/* rtctx (convenience) */
+	rtctx = sparams->rtctx;
+
+	/* validation */
+	if (!sparams->himage && !sparams->patharg)
+		return NT_STATUS_OBJECT_PATH_INVALID;
+
+	if (rtctx->argc || rtctx->argv || rtctx->envc || rtctx->envp)
+		return NT_STATUS_INVALID_PARAMETER;
+
+	if (rtctx->hready || rtctx->hsession)
+		return NT_STATUS_INVALID_PARAMETER_MIX;
+
+	if (!(peb = (nt_peb *)pe_get_peb_address()))
+		return NT_STATUS_INTERNAL_ERROR;
+
+	if (!peb->process_params)
+		return NT_STATUS_INTERNAL_ERROR;
+
+	/* hat */
+	hat = (rtctx->hroot && (sparams->argv[0][0] == '/'))
+		? rtctx->hroot
+		: rtctx->hcwd
+			? rtctx->hcwd
+			: peb->process_params->cwd_handle;
+
+	/* patharg */
+	patharg = (sparams->patharg[0] == '/')
+		? (sparams->patharg[1] == '?')
+			? &sparams->patharg[0]
+			: &sparams->patharg[1]
+		: &sparams->patharg[0];
+
+	/* rtblock, rdata */
+	rtblock.addr		= 0;
+	rtblock.size		= 0x20000;
+	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);
+
+	__ntapi->tt_aligned_block_memcpy(
+		(uintptr_t *)(rdata = (nt_runtime_data *)rtblock.addr),
+		(const uintptr_t *)rtctx,
+		sizeof(*rtctx));
+
+	/* imgbuf */
+	imgbuf  = (wchar16_t *)rtblock.addr;
+	imgbuf += 0x10000 / sizeof(*imgbuf);
+
+	/* hfile */
+	if (sparams->himage)
+		hfile = sparams->himage;
+	else if ((status = __ntapi_tt_open_file_utf8(
+			&hfile,hat,patharg,1,
+			imgbuf,0x2000)))
+		return status;
+
+	/* imgname */
+	if ((status = __ntapi->zw_query_object(
+			hfile,
+			NT_OBJECT_NAME_INFORMATION,
+			imgbuf,0x10000,&written)))
+		return __tt_spawn_return(
+			&rtblock,0,0,status);
+
+	imgname = (nt_unicode_string *)imgbuf;
+
+	/* argv, envp */
+	if ((status = __ntapi->tt_array_copy_utf8(
+			&rdata->argc,
+			(const char **)sparams->argv,
+			(const char **)sparams->envp,
+			sparams->image,
+			sparams->interpreter,
+			sparams->optarg,
+			rtblock.addr,
+			rdata->buffer,
+			rtblock.size - sizeof(*rdata),
+			&rtblock.remote_size)))
+		return __tt_spawn_return(
+			&rtblock,0,0,status);
+
+	rdata->argv = (char **)&((nt_runtime_data *)0)->buffer;
+	rdata->envp = rdata->argv + rdata->argc + 1;
+
+	rdata->wargv = (wchar16_t **)(rdata->buffer + (rtblock.remote_size / sizeof(uintptr_t)) + 1);
+	rdata->wenvp = rdata->wargv + rdata->argc + 1;
+
+	rargv = rdata->argv + ((uintptr_t)rtblock.addr / sizeof(char *));
+	renvp = rdata->envp + ((uintptr_t)rtblock.addr / sizeof(char *));
+
+	for (rdata->envc=0, parg=sparams->envp; *parg; parg++)
+		rdata->envc++;
+
+	pwarg = rdata->wenvp + rdata->envc + 1;
+	wch = (wchar16_t *)pwarg;
+
+	if ((status = __ntapi->tt_array_convert_utf8_to_utf16(
+			rargv,
+			rdata->wargv,
+			rdata,
+			wch,
+			rtblock.size - sizeof(wchar16_t)*(wch-(wchar16_t *)rdata->buffer),
+			&rtblock.remote_size)))
+		return __tt_spawn_return(
+			&rtblock,0,0,status);
+
+	wch += rtblock.remote_size/sizeof(wchar16_t);
+
+	if ((status = __ntapi->tt_array_convert_utf8_to_utf16(
+			renvp,
+			rdata->wenvp,
+			rdata,
+			wch,
+			rtblock.size - sizeof(wchar16_t)*(wch-(wchar16_t *)rdata->buffer),
+			&rtblock.remote_size)))
+		return __tt_spawn_return(
+			&rtblock,0,0,status);
+
+	rdata->wargv -= (uintptr_t)rtblock.addr / sizeof(wchar16_t *);
+	rdata->wenvp -= (uintptr_t)rtblock.addr / sizeof(wchar16_t *);
+
+	/* session */
+	if (sparams->hready) {
+		if ((status = __ntapi->zw_duplicate_object(
+				NT_CURRENT_PROCESS_HANDLE,
+				sparams->hready,
+				NT_CURRENT_PROCESS_HANDLE,
+				&rdata->hready,
+				0,0,
+				NT_DUPLICATE_SAME_ACCESS|NT_DUPLICATE_SAME_ATTRIBUTES)))
+			return __tt_spawn_return(
+				&rtblock,0,0,status);
+	} else {
+		if ((status = __ntapi->tt_create_inheritable_event(
+				&rdata->hready,
+				NT_NOTIFICATION_EVENT,
+				NT_EVENT_NOT_SIGNALED)))
+			return __tt_spawn_return(
+				&rtblock,0,0,status);
+	}
+
+	/* cparams */
+	__ntapi->tt_aligned_block_memset(
+		&cparams,0,sizeof(cparams));
+
+	__ntapi->tt_generic_memcpy(
+		&crtblock,&rtblock,sizeof(rtblock));
+
+	cparams.image_name		= imgname->buffer;
+	cparams.creation_flags_process	= NT_PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
+	cparams.creation_flags_thread	= NT_PROCESS_CREATE_FLAGS_CREATE_THREAD_SUSPENDED;
+
+	crtblock.size   = 0x10000;
+	cparams.rtblock = &crtblock;
+
+	/* hoppla */
+	if ((status = __ntapi->tt_create_native_process(&cparams)))
+		return __tt_spawn_return(
+			&rtblock,0,0,status);
+
+	/* tty session (optional) */
+	if (sparams->hsession) {
+		if ((status = __ntapi->tty_client_process_register(
+				sparams->hsession,
+				cparams.pbi.unique_process_id,
+				0,NT_TTY_INHERIT_HANDLES,0)))
+			return __tt_spawn_return(
+				&rtblock,
+				cparams.hprocess,
+				cparams.hthread,
+				status);
+
+		session.pid    = rtctx->alt_cid_self.pid;
+		session.pgid   = rtctx->alt_cid_self.pgid;
+		session.sid    = rtctx->alt_cid_self.sid;
+		session.syspid = cparams.pbi.unique_process_id;
+
+		if ((status = __ntapi->tty_client_session_set(0,&session)))
+			return __tt_spawn_return(
+				&rtblock,
+				cparams.hprocess,
+				cparams.hthread,
+				status);
+	}
+
+	/* output */
+	sparams->hprocess = cparams.hprocess;
+	sparams->hthread  = cparams.hthread;
+	sparams->rdata    = crtblock.remote_addr;
+
+	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 (sparams->fsuspended)
+		return __tt_spawn_return(
+			&rtblock,0,0,NT_STATUS_SUCCESS);
+
+	/* tada */
+	if ((status = __ntapi->zw_resume_thread(cparams.hthread,0)))
+		return __tt_spawn_return(
+			&rtblock,
+			cparams.hprocess,
+			cparams.hthread,
+			status);
+
+	/* hready */
+	hchild[1] = cparams.hprocess;
+	hchild[0] = rdata->hready;
+
+	__ntapi->zw_wait_for_multiple_objects(
+		2,hchild,
+		NT_WAIT_ANY,
+		NT_SYNC_NON_ALERTABLE,
+		sparams->timeout);
+
+	if ((status = __ntapi->zw_query_event(
+			rdata->hready,
+			NT_EVENT_BASIC_INFORMATION,
+			&sparams->eready,
+			sizeof(sparams->eready),
+			&(size_t){0})))
+		return __tt_spawn_return(
+			&rtblock,
+			cparams.hprocess,
+			cparams.hthread,
+			status);
+
+	/* all done */
+	return __tt_spawn_return(
+		&rtblock,0,0,NT_STATUS_SUCCESS);
+}