diff --git a/project/common.mk b/project/common.mk index fe3a444..5854810 100644 --- a/project/common.mk +++ b/project/common.mk @@ -33,6 +33,7 @@ COMMON_SRCS = \ src/process/ntapi_tt_create_native_process_v2.c \ src/process/ntapi_tt_create_remote_process_params.c \ src/process/ntapi_tt_create_remote_runtime_data.c \ + src/process/ntapi_tt_fork.c \ src/process/ntapi_tt_fork_v1.c \ src/process/ntapi_tt_fork_v2.c \ src/process/ntapi_tt_get_runtime_data.c \ diff --git a/src/internal/ntapi.c b/src/internal/ntapi.c index 8ebdf22..fe928ac 100644 --- a/src/internal/ntapi.c +++ b/src/internal/ntapi.c @@ -211,6 +211,7 @@ static int32_t __fastcall __ntapi_init_once(ntapi_vtbl ** pvtbl) __ntapi->tt_create_thread = __ntapi_tt_create_thread; /* nt_process.h */ + __ntapi->tt_fork = __ntapi_tt_fork; __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; @@ -361,7 +362,6 @@ static int32_t __fastcall __ntapi_init_once(ntapi_vtbl ** pvtbl) /* OS version dependent functions */ if (__ntapi->zw_create_user_process) { - __ntapi->tt_fork = __ntapi_tt_fork_v2; __ntapi->tt_create_native_process = __ntapi_tt_create_native_process_v2; __ntapi->ipc_create_pipe = __ntapi_ipc_create_pipe_v2; __ntapi->sc_socket = __ntapi_sc_socket_v2; @@ -371,7 +371,6 @@ static int32_t __fastcall __ntapi_init_once(ntapi_vtbl ** pvtbl) __ntapi->sc_getsockname = __ntapi_sc_getsockname_v2; __ntapi->sc_getpeername = __ntapi_sc_getpeername_v2; } else { - __ntapi->tt_fork = __ntapi_tt_fork_v1; __ntapi->tt_create_native_process = __ntapi_tt_create_native_process_v1; __ntapi->ipc_create_pipe = __ntapi_ipc_create_pipe_v1; __ntapi->sc_socket = __ntapi_sc_socket_v1; diff --git a/src/internal/ntapi_fnapi.h b/src/internal/ntapi_fnapi.h index ee291ff..650839e 100644 --- a/src/internal/ntapi_fnapi.h +++ b/src/internal/ntapi_fnapi.h @@ -94,6 +94,7 @@ ntapi_tt_create_local_thread __ntapi_tt_create_local_thread; ntapi_tt_create_remote_thread __ntapi_tt_create_remote_thread; /* nt_process.h */ +ntapi_tt_fork __ntapi_tt_fork; ntapi_tt_fork __ntapi_tt_fork_v1; ntapi_tt_fork __ntapi_tt_fork_v2; ntapi_tt_create_remote_process_params __ntapi_tt_create_remote_process_params; diff --git a/src/process/ntapi_tt_fork.c b/src/process/ntapi_tt_fork.c new file mode 100644 index 0000000..9f498eb --- /dev/null +++ b/src/process/ntapi_tt_fork.c @@ -0,0 +1,230 @@ +/********************************************************/ +/* ntapi: Native API core library */ +/* Copyright (C) 2013--2016 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 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) +{ + int32_t status; + nt_thread_params tparams; + nt_timeout timeout; + nt_timeout zerowait; + intptr_t state; + + 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_v2(hprocess,hthread); + + if (pid == 0) { + return __ntapi_tt_fork_child( + hresumed,hready); + + } 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; +}