/********************************************************/ /* 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 #include "ntapi_impl.h" /* (no planned support of alpha processors, use constant values) */ #define __PAGE_SIZE 0x001000 #define __GRANULARITY 0x010000 #define __RESERVE_ROUND_UP 0x100000 static int32_t __create_thread_fail( void * hprocess, void * stack_bottom, size_t stack_size, int32_t status) { __ntapi->zw_free_virtual_memory( hprocess, &stack_bottom, &stack_size, NT_MEM_RELEASE); return status; } static int32_t __tt_create_thread_stack( nt_thread_params * params, nt_user_stack * stack) { int32_t status; uint32_t protect_type_old; void * stack_system_limit; char * base; size_t size; size_t commit; /* caller-provided stack? */ if (params->stack_info && params->stack_info->expandable_stack_base) { stack->fixed_stack_base = params->stack_info->fixed_stack_base; stack->fixed_stack_limit = params->stack_info->fixed_stack_limit; stack->expandable_stack_base = params->stack_info->expandable_stack_base; stack->expandable_stack_limit = params->stack_info->expandable_stack_limit; stack->expandable_stack_bottom = params->stack_info->expandable_stack_bottom; return NT_STATUS_SUCCESS; } /* init */ params->stack_size_commit = __NT_ROUND_UP_TO_POWER_OF_2(params->stack_size_commit+params->ext_ctx_size, __PAGE_SIZE); params->stack_size_reserve = __NT_ROUND_UP_TO_POWER_OF_2(params->stack_size_reserve,__GRANULARITY); /* compare, round-up as needed */ if (params->stack_size_commit >= params->stack_size_reserve) params->stack_size_reserve = __NT_ROUND_UP_TO_POWER_OF_2(params->stack_size_commit,__RESERVE_ROUND_UP); /** * * --------- BASE ---------- * * ---- (COMMITED AREA) ---- * * --------- LIMIT --------- * * ------ GUARD PAGE ------- * * ------ ACTUAL LIMIT ----- * * ---- (RESERVED AREA) ---- * * -------- BOTTOM --------- * **/ /* stack structure: unused fields */ stack->fixed_stack_base = 0; stack->fixed_stack_limit = 0; /* first we reserve */ stack->expandable_stack_bottom = 0; if ((status = __ntapi->zw_allocate_virtual_memory( params->hprocess, &stack->expandable_stack_bottom, params->stack_zero_bits, ¶ms->stack_size_reserve, NT_MEM_RESERVE, NT_PAGE_READWRITE))) return status; /* calculate base and limit */ base = stack->expandable_stack_bottom; base += params->stack_size_reserve; stack->expandable_stack_base = base; stack->expandable_stack_limit = base - params->stack_size_commit; /* guard page */ commit = params->stack_size_commit + __PAGE_SIZE; stack_system_limit = base - commit; /* then we commit */ if ((status = __ntapi->zw_allocate_virtual_memory( params->hprocess, &stack_system_limit, 0,&commit, NT_MEM_COMMIT, NT_PAGE_READWRITE))) return __create_thread_fail( params->hprocess, stack->expandable_stack_bottom, params->stack_size_reserve, status); /* finally we protect the guard page */ size = __PAGE_SIZE; if ((status = __ntapi->zw_protect_virtual_memory( params->hprocess, &stack_system_limit, &size, NT_PAGE_READWRITE | NT_MEM_PAGE_GUARD, &protect_type_old))) return __create_thread_fail( params->hprocess, stack->expandable_stack_bottom, params->stack_size_reserve, status); /* all done */ if (params->stack_info) { params->stack_info->fixed_stack_base = stack->fixed_stack_base; params->stack_info->fixed_stack_limit = stack->fixed_stack_limit; params->stack_info->expandable_stack_base = stack->expandable_stack_base; params->stack_info->expandable_stack_limit = stack->expandable_stack_limit; params->stack_info->expandable_stack_bottom = stack->expandable_stack_bottom; return NT_STATUS_SUCCESS; } return NT_STATUS_SUCCESS; } int32_t __stdcall __ntapi_tt_create_thread(nt_thread_params * params) { int32_t status; ntapi_internals * __internals; nt_client_id cid; nt_port_message_csrss_process csrss_msg; nt_port_message_csrss_process * csrss_msg_1st; nt_port_message_csrss_thread * csrss_msg_any; nt_thread_context __attr_aligned__(0x40) context; nt_user_stack __attr_aligned__(0x10) stack; uintptr_t fsuspended; uintptr_t * parg; if (!(params->stack_size_commit)) return NT_STATUS_INVALID_PARAMETER; else if (!(params->stack_size_reserve)) return NT_STATUS_INVALID_PARAMETER; else if (params->ext_ctx_size > __NT_INTERNAL_PAGE_SIZE) return NT_STATUS_INVALID_PARAMETER; else if (params->ext_ctx_size % sizeof(intptr_t)) return NT_STATUS_INVALID_PARAMETER; else if (params->arg && params->ext_ctx) return NT_STATUS_INVALID_PARAMETER_MIX; else if (params->ext_ctx && !params->ext_ctx_size) return NT_STATUS_INVALID_PARAMETER_MIX; /* init */ __internals = __ntapi_internals(); /* stack */ if ((status = __tt_create_thread_stack(params,&stack))) return status; /* context */ if (params->reg_context) { __ntapi->tt_aligned_block_memcpy( (uintptr_t *)&context, (uintptr_t *)params->reg_context, sizeof(context)); } else { __ntapi->tt_aligned_block_memset( &context,0,sizeof(context)); __INIT_CONTEXT(context); context.INSTRUCTION_POINTER_REGISTER = (uintptr_t)params->start; context.STACK_POINTER_REGISTER = (uintptr_t)(stack.expandable_stack_base); } /*****************************************************************************/ /*-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-*/ /* */ /* */ /* INNOVATION IN THE FIELD OF MULTI-THREADED COMPUTER PROGRAMMING */ /* */ /* A "RAPUNZEL" TOP-OF-STACK, VARIABLE-SIZE ENTRY-ROUTINE CONTEXT */ /* */ /* COPYRIGHT (C) 2013,2014,2015 ZVI GILBOA */ /* */ /* */ /* */ /* Laß mir dein Haar herunter.« */ /**/ if (params->ext_ctx) { /**/ /**/ context.STACK_POINTER_REGISTER -= params->ext_ctx_size; /**/ /**/ params->arg = (void *)context.STACK_POINTER_REGISTER; /**/ /**/ /**/ /**/ if (params->creation_flags & NT_CREATE_LOCAL_THREAD) /**/ /**/ __ntapi->tt_aligned_block_memcpy( /**/ /**/ (uintptr_t *)params->arg, /**/ /**/ (uintptr_t *)params->ext_ctx, /**/ /**/ params->ext_ctx_size); /**/ /**/ else { /**/ /**/ status = __ntapi->zw_write_virtual_memory( /**/ /**/ params->hprocess, /**/ /**/ params->arg, /**/ /**/ (char *)params->ext_ctx, /**/ /**/ params->ext_ctx_size, /**/ /**/ 0); /**/ /**/ /**/ /**/ if (status) return __create_thread_fail( /**/ /**/ params->hprocess, /**/ /**/ stack.expandable_stack_bottom, /**/ /**/ params->stack_size_reserve, /**/ /**/ status); /**/ /**/ } /**/ /**/ } /**/ /**/ /**/ /**/ /**/ /**/ /**/ /* entry-routine argument address and stack pointer adjustment */ /**/ if (sizeof(intptr_t) == 4) { /**/ /**/ context.STACK_POINTER_REGISTER -= sizeof(intptr_t); /**/ /**/ parg = (uintptr_t *)context.STACK_POINTER_REGISTER; /**/ /**/ } else /**/ /**/ parg = &context.FAST_CALL_ARG0; /**/ /**/ /**/ /**/ /**/ /* write entry-routine argument */ /**/ if ((sizeof(size_t) == 8) /**/ /**/ || (params->creation_flags&NT_CREATE_LOCAL_THREAD))/**/ /**/ *parg = (uintptr_t)params->arg; /**/ /**/ else { /**/ /**/ status = __ntapi->zw_write_virtual_memory( /**/ /**/ params->hprocess, /**/ /**/ parg, /**/ /**/ (char *)¶ms->arg, /**/ /**/ sizeof(uintptr_t), /**/ /**/ 0); /**/ /**/ /**/ /**/ if (status) return __create_thread_fail( /**/ /**/ params->hprocess, /**/ /**/ stack.expandable_stack_bottom, /**/ /**/ params->stack_size_reserve, /**/ /**/ status); /**/ /**/ } /**/ /**/ /**/ /**/ /**/ /*-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-*/ /*****************************************************************************/ /* create thread */ fsuspended = (params->creation_flags & NT_CREATE_SUSPENDED) ? 1 : __ntapi->zw_create_user_process ? 0 : 1; if ((status = __ntapi->zw_create_thread( ¶ms->hthread, NT_THREAD_ALL_ACCESS, params->obj_attr, params->hprocess, &cid,&context,&stack, fsuspended))) return __create_thread_fail( params->hprocess, stack.expandable_stack_bottom, params->stack_size_reserve, status); /* for os versions prior to hasta la */ if (!__ntapi->zw_create_user_process) { __ntapi->tt_aligned_block_memset(&csrss_msg,0,sizeof(csrss_msg)); if (params->creation_flags & NT_CREATE_FIRST_THREAD_OF_PROCESS) { /* nt_port_message_csrss_process is the larger structure */ csrss_msg_1st = &csrss_msg; csrss_msg_1st->header.data_size = sizeof(nt_port_message_csrss_process) - sizeof(nt_port_message); csrss_msg_1st->header.msg_size = sizeof(nt_port_message_csrss_process); csrss_msg_1st->opcode = 0x10000; csrss_msg_1st->hprocess = params->hprocess; csrss_msg_1st->hthread = params->hthread; csrss_msg_1st->unique_process_id = cid.process_id; csrss_msg_1st->unique_thread_id = cid.thread_id; } else { /* nt_port_message_csrss_thread is the smaller structure */ csrss_msg_any = (nt_port_message_csrss_thread *)&csrss_msg; csrss_msg_any->header.data_size = sizeof(nt_port_message_csrss_thread) - sizeof(nt_port_message); csrss_msg_any->header.msg_size = sizeof(nt_port_message_csrss_thread); csrss_msg_any->opcode = 0x10001; csrss_msg_any->hthread = params->hthread; csrss_msg_any->unique_process_id = cid.process_id; csrss_msg_any->unique_thread_id = cid.thread_id; } /* send csrss a new-thread notification */ if (__internals->csr_port_handle_addr) { status = __ntapi->zw_request_wait_reply_port( *__internals->csr_port_handle_addr, &csrss_msg,&csrss_msg); } /* output csrss_status to caller */ params->csrss_status = status ? status : csrss_msg.status; } /* resume thread, close handle as needed */ if (fsuspended && !(params->creation_flags & NT_CREATE_SUSPENDED)) status = __ntapi->zw_resume_thread(params->hthread,0); if (params->creation_flags & NT_CLOSE_THREAD_HANDLE) __ntapi->zw_close(params->hthread); /* and finally */ params->thread_id = (uint32_t)cid.thread_id; return NT_STATUS_SUCCESS; } int32_t __stdcall __ntapi_tt_create_local_thread(nt_thread_params * params) { nt_status status; void * image_base; struct pe_stack_heap_info stack_heap_info; /* flags */ params->creation_flags |= NT_CREATE_LOCAL_THREAD; /* hprocess */ if (!params->hprocess) params->hprocess = __ntapi_internals()->hprocess; /* stack defaults */ if (params->stack_info) (void)0; else if (params->stack_size_commit && params->stack_size_reserve) (void)0; else { /* image_base*/ if (!(image_base = pe_get_first_module_handle())) return NT_STATUS_INVALID_IMPORT_OF_NON_DLL; if ((status = pe_get_image_stack_heap_info( image_base, &stack_heap_info))) return NT_STATUS_INVALID_IMAGE_FORMAT; /* stack_size_commit */ if (!params->stack_size_commit) params->stack_size_commit = stack_heap_info.size_of_stack_commit; /* stack_size_reserve */ if (!params->stack_size_reserve) params->stack_size_reserve = stack_heap_info.size_of_stack_reserve; if (!(params->stack_size_commit && params->stack_size_reserve)) return NT_STATUS_INVALID_IMAGE_FORMAT; } return __ntapi_tt_create_thread(params); } int32_t __stdcall __ntapi_tt_create_remote_thread(nt_thread_params * params) { return __ntapi_tt_create_thread(params); } void * __cdecl __ntapi_csr_port_handle(nt_status * pstatus) { nt_status status; ntapi_internals * __internals; pstatus = pstatus ? pstatus : &status; __internals = __ntapi_internals(); if (__internals->csr_port_handle_addr) { *pstatus = NT_STATUS_SUCCESS; return *__internals->csr_port_handle_addr; } else { *pstatus = NT_STATUS_UNSUCCESSFUL; return 0; } }