/********************************************************/ /* ntapi: Native API core library */ /* Copyright (C) 2013--2017 Z. Gilboa */ /* 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) 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]; wchar16_t * imgbuf; uint32_t fsuspended; size_t buflen; size_t written; 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; /* rtblock, rdata */ rtblock.addr = 0; rtblock.size = 0x40000; 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 += 0x30000 / 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, &(uint32_t){0}))) return __tt_spawn_return( &rtblock,0,0,status); imgname = (nt_unicode_string *)imgbuf; /* argv, envp */ buflen = rtblock.size; buflen -= sizeof(*rdata); 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,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; if ((written == (uintptr_t)wch - (uintptr_t)rdata) > rtblock.size) return __tt_spawn_return( &rtblock,0,0,NT_STATUS_BUFFER_TOO_SMALL); buflen = rtblock.size; buflen -= written; if ((status = __ntapi->tt_array_convert_utf8_to_utf16( rargv, rdata->wargv, rdata,wch, buflen,&written))) return __tt_spawn_return( &rtblock,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,0,0,status); rdata->wargv -= (uintptr_t)rtblock.addr / sizeof(wchar16_t *); rdata->wenvp -= (uintptr_t)rtblock.addr / sizeof(wchar16_t *); wch += written/sizeof(wchar16_t); buflen -= written; if (buflen < 0x10000) return __tt_spawn_return( &rtblock,0,0,NT_STATUS_BUFFER_TOO_SMALL); /* 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); } /* 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; 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,0,0,status); /* tidy up */ if (!sparams->himage) __ntapi->zw_close(hfile); /* 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, 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, 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, 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, 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, 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, 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,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); }