/********************************************************/ /* ntapi: Native API core library */ /* Copyright (C) 2013--2021 SysDeer Technologies, LLC */ /* Released under GPLv2 and GPLv3; see COPYING.NTAPI. */ /********************************************************/ #include #include #include #include #include #include #include #include #include "ntapi_impl.h" #define NT_PROCESS_SPAWN_FLAG_DEBUG_MASK \ (NT_PROCESS_SPAWN_FLAG_DEBUG_EXECUTION \ | NT_PROCESS_SPAWN_FLAG_DEBUG_SUSPENDED) #define __SPAWN_NATIVE_PROCESS_RUNTIME_BLOCK_IMGBUF_SIZE (0x10000) #define __SPAWN_NATIVE_PROCESS_RUNTIME_BLOCK_ALLOC_SIZE_DEF (0x80000) #define __SPAWN_NATIVE_PROCESS_RUNTIME_BLOCK_ALLOC_SIZE_MAX (0x800000) static int32_t __stdcall __tt_spawn_return( nt_runtime_data_block * rtblock, void * himgfile, 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); 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_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; size_t asize; char * patharg; void * hat; void * hfile; void * himgfile; char * src; char * dst; int envc; char ** penv; char ** pref; char ** parg; char ** rargv; char ** renvp; wchar16_t ** pwarg; wchar16_t * wenv; wchar16_t * wch; wchar16_t * wcap; void * hchild[2]; wchar16_t * imgbuf; uint32_t fsuspended; size_t buflen; size_t written; size_t needed; uintptr_t addr; char * raddr; size_t rsize; /* 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 = (sparams->hroot && (sparams->argv[0][0] == '/')) ? sparams->hroot : rtctx->hcwd ? rtctx->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; /* quickly determine whether a large buffer is needed */ for (asize=0,parg=sparams->argv; parg && *parg; asize++) { asize += __ntapi_tt_string_null_offset_multibyte(*parg++); if (asize > (__SPAWN_NATIVE_PROCESS_RUNTIME_BLOCK_ALLOC_SIZE_DEF >> 4)) parg = 0; } for (penv=sparams->envp; penv && *penv; asize++) { asize += __ntapi_tt_string_null_offset_multibyte(*penv++); if (asize > (__SPAWN_NATIVE_PROCESS_RUNTIME_BLOCK_ALLOC_SIZE_DEF >> 4)) penv = 0; } if (parg && penv) { asize += (parg - sparams->argv) * sizeof(char *); asize += (penv - sparams->envp) * sizeof(char *); } asize = (asize <= (__SPAWN_NATIVE_PROCESS_RUNTIME_BLOCK_ALLOC_SIZE_DEF >> 4)) ? __SPAWN_NATIVE_PROCESS_RUNTIME_BLOCK_ALLOC_SIZE_DEF : __SPAWN_NATIVE_PROCESS_RUNTIME_BLOCK_ALLOC_SIZE_MAX; /* rtblock, rdata */ rtblock.addr = 0; rtblock.size = asize; 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)); /* abi */ if (!(__ntapi->tt_guid_compare(&rdata->abi,&(nt_guid)NT_PROCESS_GUID_UNSPEC))) __ntapi->tt_guid_copy( &rdata->abi, &(nt_guid)NT_PROCESS_GUID_RTDATA); /* imgbuf */ imgbuf = (wchar16_t *)rtblock.addr; imgbuf += rtblock.size / sizeof(*imgbuf); imgbuf -= __SPAWN_NATIVE_PROCESS_RUNTIME_BLOCK_IMGBUF_SIZE / sizeof(*imgbuf); /* hfile */ if (sparams->himage) { hfile = sparams->himage; himgfile = 0; } else if ((status = __ntapi_tt_open_file_utf8( &hfile,hat,patharg,1, imgbuf,__SPAWN_NATIVE_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_NATIVE_PROCESS_RUNTIME_BLOCK_IMGBUF_SIZE, &(uint32_t){0}))) return __tt_spawn_return( &rtblock,himgfile,0,0, status); imgname = (nt_unicode_string *)imgbuf; /* imgbuf must remain intact until after creation of the child process */ buflen = rtblock.size; buflen -= sizeof(*rdata); buflen -= __SPAWN_NATIVE_PROCESS_RUNTIME_BLOCK_IMGBUF_SIZE; /* argv, envp */ if ((status = __ntapi->tt_array_copy_utf8( &rdata->argc, (const char **)sparams->argv, (const char **)sparams->envp, sparams->interp, sparams->optarg, sparams->script, rtblock.addr, rdata->buffer, buflen,&written))) return __tt_spawn_return( &rtblock,himgfile,0,0, status); rdata->argv = (char **)&((nt_runtime_data *)0)->buffer; rdata->envp = rdata->argv + rdata->argc + 1; rdata->wargv = (wchar16_t **)rdata->buffer; rdata->wargv += written / sizeof(wchar16_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; wcap = (wchar16_t *)rtblock.addr; wcap += rtblock.size / sizeof(wchar16_t); buflen = (wcap - wch) * sizeof(wchar16_t); buflen -= __SPAWN_NATIVE_PROCESS_RUNTIME_BLOCK_IMGBUF_SIZE; if ((status = __ntapi->tt_array_convert_utf8_to_utf16( rargv, rdata->wargv, rdata,wch, buflen,&written))) return __tt_spawn_return( &rtblock,himgfile,0,0, status); wch += written / sizeof(wchar16_t); buflen -= written; if ((status = __ntapi->tt_array_convert_utf8_to_utf16( renvp, rdata->wenvp, rdata,wch, buflen,&written))) return __tt_spawn_return( &rtblock,himgfile,0,0, status); rdata->wargv -= (uintptr_t)rtblock.addr / sizeof(wchar16_t *); rdata->wenvp -= (uintptr_t)rtblock.addr / sizeof(wchar16_t *); wenv = wch; wch += written / sizeof(wchar16_t); buflen -= written; /* w32 environment */ if (rtctx->w32_envp) { addr = (uintptr_t)wch; addr += sizeof(uintptr_t) - 1; addr /= sizeof(uintptr_t); addr *= sizeof(uintptr_t); penv = rtctx->w32_envp; needed = sizeof(char *); for (envc=0; *penv; envc++) { needed += sizeof(char *); needed += __ntapi->tt_string_null_offset_multibyte(*penv); penv++; } needed += sizeof(uintptr_t) - 1; needed /= sizeof(uintptr_t); needed *= sizeof(uintptr_t); if (buflen < needed) return __tt_spawn_return( &rtblock,himgfile,0,0, NT_STATUS_BUFFER_TOO_SMALL); rdata->w32_envp = (char **)(addr - (uintptr_t)rtblock.addr); pref = (char **)addr; dst = (char *)&pref[++envc]; for (penv=rtctx->w32_envp; *penv; penv++) { *pref++ = dst - (uintptr_t)rtblock.addr; for (src=*penv; *src; src++,dst++) *dst = *src; *dst++ = 0; } wch += needed / sizeof(wchar16_t); buflen -= needed; } /* 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,himgfile,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,himgfile,0,0, status); } /* process flags */ 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) fsuspended = NT_CREATE_SUSPENDED; /* 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; cparams.environment = wenv; /* crtblock: alloc size: _needed_only_, round up to system granularity */ crtblock.size = (size_t)wch - (size_t)rdata; crtblock.size += 0xFFFF; crtblock.size |= 0xFFFF; crtblock.size ^= 0xFFFF; cparams.rtblock = &crtblock; /* hoppla */ 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); /* additional context */ if (rtctx->ctx_addr) { rdata->ctx_addr = 0; rdata->ctx_commit = rtctx->ctx_size; rdata->ctx_commit += (__NT_INTERNAL_PAGE_SIZE - 1); rdata->ctx_commit |= (__NT_INTERNAL_PAGE_SIZE - 1); rdata->ctx_commit ^= (__NT_INTERNAL_PAGE_SIZE - 1); if ((status = __ntapi->zw_allocate_virtual_memory( cparams.hprocess, &rdata->ctx_addr,0, &rdata->ctx_commit, NT_MEM_COMMIT, NT_PAGE_READWRITE))) return __tt_spawn_return( &rtblock, himgfile, cparams.hprocess, cparams.hthread, status); if ((status = __ntapi->zw_write_virtual_memory( cparams.hprocess, rdata->ctx_addr, rtctx->ctx_addr, rtctx->ctx_size, &rdata->ctx_size))) return __tt_spawn_return( &rtblock, himgfile, cparams.hprocess, cparams.hthread, status); raddr = crtblock.remote_addr; raddr += __offsetof(nt_runtime_data,ctx_addr); rsize = __offsetof(nt_runtime_data,ctx_offset); rsize -= __offsetof(nt_runtime_data,ctx_addr); if ((status = __ntapi->zw_write_virtual_memory( cparams.hprocess, raddr,(char *)&rdata->ctx_addr, rsize,&rsize))) return __tt_spawn_return( &rtblock, himgfile, cparams.hprocess, cparams.hthread, 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, himgfile, 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 = (uint32_t)cparams.pbi.unique_process_id; if ((status = __ntapi->tty_client_session_set( sparams->hsession, &session))) return __tt_spawn_return( &rtblock, himgfile, 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 (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); /* 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, himgfile, cparams.hprocess, cparams.hthread, status); /* all done */ return __tt_spawn_return( &rtblock,himgfile,0,0,NT_STATUS_SUCCESS); }