/********************************************************/ /* 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" static intptr_t __fork_retry_stats = 0; static intptr_t __fork_resume_stats = 0; static intptr_t __fastcall __ntapi_tt_fork_finalize(void ** hprocess) { int32_t status; int page; nt_rtdata * rtdata; ntapi_internals * __internals; __internals = __ntapi_internals(); rtdata = __internals->rtdata; rtdata->cid_parent.process_id = rtdata->cid_self.process_id; rtdata->cid_parent.thread_id = rtdata->cid_self.thread_id; rtdata->cid_self.process_id = pe_get_current_process_id(); rtdata->cid_self.thread_id = pe_get_current_thread_id(); if ((status = __ntapi->zw_duplicate_object( __internals->hprocess, __internals->hprocess, __internals->hprocess, hprocess,0,0, NT_DUPLICATE_SAME_ATTRIBUTES |NT_DUPLICATE_SAME_ACCESS))) return status; if ((status = dalist_init_ex( &__internals->ipc_conns, sizeof(nt_ipc_conn), NT_ALLOCATION_GRANULARITY, __ntapi_ipc_page_alloc, DALIST_MEMFN_CUSTOM))) return status; dalist_deposit_memory_block( &__internals->ipc_conns, __internals->ntapi_img_sec_bss->ipc_buffer, __NT_BSS_IPC_BUFFER_SIZE); for (page=0; page<__internals->ipc_page; page++) dalist_deposit_memory_block( &__internals->ipc_conns, __internals->ipc_pages[page], NT_ALLOCATION_GRANULARITY); rtdata->hsemctl = 0; rtdata->hsempid = 0; rtdata->hmsqctl = 0; rtdata->hmsqpid = 0; rtdata->haflctl = 0; rtdata->haflpid = 0; rtdata->ipc_keys[0] = 0; rtdata->ipc_keys[1] = 0; rtdata->ipc_keys[2] = 0; rtdata->ipc_keys[3] = 0; rtdata->ipc_keys[4] = 0; rtdata->ipc_keys[5] = 0; return 0; } static int32_t __stdcall __fork_thread(void * ctx) { intptr_t * pstate; intptr_t state; void * hready; pstate = (intptr_t *)ctx; state = *pstate; hready = (void *)state; at_store( pstate, 0); return __ntapi->zw_terminate_thread( NT_CURRENT_THREAD_HANDLE, __ntapi->zw_set_event( hready,0)); } static intptr_t __fastcall __ntapi_tt_fork_child( void * hresumed, void * hready, void ** hthread) { int32_t status; nt_thread_params tparams; nt_timeout timeout; nt_timeout zerowait; intptr_t state; nt_oa oa; nt_cid cid; ntapi_internals * __internals; oa.len = sizeof(oa); oa.root_dir = 0; oa.obj_name = 0; oa.obj_attr = 0; oa.sec_desc = &__internals->seq_desc; oa.sec_qos = &__internals->seq_qos; cid.process_id = pe_get_current_process_id(); cid.thread_id = pe_get_current_thread_id(); __internals = __ntapi_internals(); status = __ntapi->zw_open_process( &__internals->hprocess, NT_PROCESS_ALL_ACCESS, &oa,&cid); if (status == NT_STATUS_SUCCESS) status = __ntapi->zw_open_thread( hthread, NT_THREAD_ALL_ACCESS, &oa,&cid); if (status) { __ntapi->zw_set_event( hresumed,0); __ntapi->zw_terminate_process( NT_CURRENT_PROCESS_HANDLE, status); } at_store( &state, (intptr_t)hready); __ntapi->tt_aligned_block_memset( &tparams,0,sizeof(tparams)); tparams.start = __fork_thread; tparams.arg = &state; tparams.stack_size_commit = 0x10000; tparams.stack_size_reserve = 0x20000; status = __ntapi->tt_create_local_thread( &tparams); __ntapi->zw_set_event( hresumed,0); if (status) __ntapi->zw_terminate_process( NT_CURRENT_PROCESS_HANDLE, status); if (!state) { __ntapi->zw_close(hresumed); __ntapi->zw_close(hready); __ntapi->zw_close(tparams.hthread); return 0; } timeout.quad = (-1) * 10 * 1000 * 250; status = __ntapi->zw_wait_for_single_object( hready, NT_SYNC_NON_ALERTABLE, &timeout); if (status == NT_STATUS_SUCCESS) { __ntapi->zw_close(hresumed); __ntapi->zw_close(hready); __ntapi->zw_close(tparams.hthread); return 0; } __ntapi->zw_terminate_thread( tparams.hthread, NT_STATUS_MORE_PROCESSING_REQUIRED); zerowait.quad = 0; status = __ntapi->zw_wait_for_single_object( hready, NT_SYNC_NON_ALERTABLE, &zerowait); if (status == NT_STATUS_SUCCESS) { __ntapi->zw_close(hresumed); __ntapi->zw_close(hready); __ntapi->zw_close(tparams.hthread); return 0; } return __ntapi->zw_terminate_process( NT_CURRENT_PROCESS_HANDLE, status); } static intptr_t __fastcall __ntapi_tt_fork_parent( void ** hprocess, void ** hthread, void * hresumed, void * hready) { int32_t status; nt_timeout timeout; nt_timeout zerowait; uint32_t prev; __ntapi->zw_wait_for_single_object( hresumed, NT_SYNC_NON_ALERTABLE, 0); timeout.quad = (-1) * 10 * 1000 * 500; status = __ntapi->zw_wait_for_single_object( hready, NT_SYNC_NON_ALERTABLE, &timeout); if (status == NT_STATUS_SUCCESS) { __ntapi->zw_close(hresumed); __ntapi->zw_close(hready); return NT_STATUS_SUCCESS; } __ntapi->zw_suspend_thread( *hthread,&prev); zerowait.quad = 0; status = __ntapi->zw_wait_for_single_object( hready, NT_SYNC_NON_ALERTABLE, &zerowait); if (status == NT_STATUS_SUCCESS) { at_locked_inc( &__fork_resume_stats); __ntapi->zw_resume_thread( *hthread,0); __ntapi->zw_close(hresumed); __ntapi->zw_close(hready); return NT_STATUS_SUCCESS; } at_locked_inc( &__fork_retry_stats); __ntapi->zw_terminate_process( *hprocess, status); __ntapi->zw_close(*hprocess); __ntapi->zw_close(*hthread); return status; } intptr_t __fastcall __ntapi_tt_fork( __out void ** hprocess, __out void ** hthread) { int32_t status; intptr_t pid; void * hresumed; void * hready; int i; if ((status = __ntapi->tt_create_inheritable_event( &hresumed, NT_NOTIFICATION_EVENT, NT_EVENT_NOT_SIGNALED))) return -1; if ((status = __ntapi->tt_create_inheritable_event( &hready, NT_NOTIFICATION_EVENT, NT_EVENT_NOT_SIGNALED))) return -1; for (i=0; i<32; i++) { if (__ntapi->zw_create_user_process) pid = __ntapi_tt_fork_v2(hprocess,hthread); else pid = __ntapi_tt_fork_v1(hprocess,hthread); if (pid == 0) { __ntapi_tt_fork_child( hresumed,hready,hthread); return __ntapi_tt_fork_finalize( hprocess); } else if (pid > 0) { if (!(__ntapi_tt_fork_parent( hprocess,hthread, hresumed,hready))) return pid; } else { __ntapi->zw_close(hresumed); __ntapi->zw_close(hready); return -1; } } __ntapi->zw_close(hresumed); __ntapi->zw_close(hready); return -1; }