Blob Blame History Raw
/********************************************************/
/*  ntapi: Native API core library                      */
/*  Copyright (C) 2013--2021  SysDeer Technologies, LLC */
/*  Released under GPLv2 and GPLv3; see COPYING.NTAPI.  */
/********************************************************/

#include <psxtypes/psxtypes.h>
#include <pemagine/pemagine.h>

#include <ntapi/nt_status.h>
#include <ntapi/nt_crc32.h>
#include <ntapi/nt_crc64.h>
#include <ntapi/nt_object.h>
#include <ntapi/nt_sysinfo.h>
#include <ntapi/nt_memory.h>
#include <ntapi/nt_section.h>
#include <ntapi/nt_thread.h>
#include <ntapi/nt_process.h>
#include <ntapi/nt_job.h>
#include <ntapi/nt_token.h>
#include <ntapi/nt_sync.h>
#include <ntapi/nt_time.h>
#include <ntapi/nt_profiling.h>
#include <ntapi/nt_port.h>
#include <ntapi/nt_device.h>
#include <ntapi/nt_file.h>
#include <ntapi/nt_registry.h>
#include <ntapi/nt_security.h>
#include <ntapi/nt_pnp.h>
#include <ntapi/nt_exception.h>
#include <ntapi/nt_locale.h>
#include <ntapi/nt_uuid.h>
#include <ntapi/nt_atom.h>
#include <ntapi/nt_os.h>
#include <ntapi/nt_ldr.h>
#include <ntapi/nt_string.h>
#include <ntapi/nt_guid.h>
#include <ntapi/nt_argv.h>
#include <ntapi/nt_blitter.h>
#include <ntapi/nt_unicode.h>
#include <ntapi/nt_socket.h>
#include <ntapi/nt_mount.h>
#include <ntapi/nt_istat.h>
#include <ntapi/nt_stat.h>
#include <ntapi/nt_statfs.h>
#include <ntapi/nt_daemon.h>
#include <ntapi/nt_tty.h>
#include <ntapi/nt_afl.h>
#include <ntapi/nt_hash.h>
#include <ntapi/nt_log.h>
#include <ntapi/nt_atomic.h>
#include <ntapi/ntapi.h>

#include "ntapi_impl.h"
#include "ntapi_hash_table.h"

/* simplified once mechanism for free-standing applications */
typedef int32_t __fastcall __ntapi_init_fn(ntapi_vtbl ** pvtbl);

static __ntapi_init_fn __ntapi_init_once;
static __ntapi_init_fn __ntapi_init_pending;
static __ntapi_init_fn __ntapi_init_completed;

static intptr_t		 __ntapi_init_idx = 0;
static __ntapi_init_fn * __ntapi_init_vtbl[3] = {
	__ntapi_init_once,
	__ntapi_init_pending,
	__ntapi_init_completed};

/* accessor */
ntapi_vtbl ___ntapi;
ntapi_vtbl ___ntapi_shadow;

/* .bss */
static __ntapi_img_sec_bss __ntapi_img_bss;

/* .rdata */
static union __ntapi_img_rdata __ntapi_rdata = {{
		{__NTAPI_HASH_TABLE},		/* __ntapi_import_table */
		0,				/* __ntapi */
		{				/* __session_name */
			{0},0,{0},
			{0,{0},0},
			{{0},0,{0},0,{0},0,{0},0,{0},0,{0}},
			0},
		0}};

#define internals	__ntapi_rdata.img_sec_data.__internals
#define import_table	__ntapi_rdata.img_sec_data.__ntapi_import_table


static int __ipc_memfn(
	struct dalist_ex *	dlist,
	void **			addr,
	size_t *		alloc_size)
{
	(void)dlist;
	(void)addr;
	(void)alloc_size;

	return DALIST_EMEMFN;
}

static void __ntapi_init_adjust_privileges(void)
{
	uintptr_t		buffer[64];
	nt_token_privileges *	tokprivs;

	/* token privileges */
	tokprivs = (nt_token_privileges *)buffer;
	tokprivs->privilege_count = 6;

	tokprivs->privileges[0].attributes = NT_SE_ENABLE_PRIVILEGE;
	tokprivs->privileges[0].luid.low   = NT_SE_CREATE_SYMBOLIC_LINK_PRIVILEGE;
	tokprivs->privileges[0].luid.high  = 0;

	tokprivs->privileges[1].attributes = NT_SE_ENABLE_PRIVILEGE;
	tokprivs->privileges[1].luid.low   = NT_SE_TAKE_OWNERSHIP_PRIVILEGE;
	tokprivs->privileges[1].luid.high  = 0;

	tokprivs->privileges[2].attributes = NT_SE_ENABLE_PRIVILEGE;
	tokprivs->privileges[2].luid.low   = NT_SE_BACKUP_PRIVILEGE;
	tokprivs->privileges[2].luid.high  = 0;

	tokprivs->privileges[3].attributes = NT_SE_ENABLE_PRIVILEGE;
	tokprivs->privileges[3].luid.low   = NT_SE_RESTORE_PRIVILEGE;
	tokprivs->privileges[3].luid.high  = 0;

	tokprivs->privileges[4].attributes = NT_SE_ENABLE_PRIVILEGE;
	tokprivs->privileges[4].luid.low   = NT_SE_AUDIT_PRIVILEGE;
	tokprivs->privileges[4].luid.high  = 0;

	tokprivs->privileges[5].attributes = NT_SE_ENABLE_PRIVILEGE;
	tokprivs->privileges[5].luid.low   = NT_SE_DEBUG_PRIVILEGE;
	tokprivs->privileges[5].luid.high  = 0;

	/* (attempt to) set any or all */
	__ntapi->zw_adjust_privileges_token(
		internals->htoken,0,tokprivs,
		0,0,0);
}


static int32_t __fastcall __ntapi_init_once(ntapi_vtbl ** pvtbl)
{
	int32_t					status;
	void * 					hntdll;
	size_t					block_size;
	size_t					buf[64];
	unsigned char *				value;
	uint16_t				sacnt;
	nt_oa					oa;
	nt_cid					cid;
	ntapi_zw_allocate_virtual_memory *	pfn_zw_allocate_virtual_memory;
	char					fname_allocate_virtual_memory[] =
							"ZwAllocateVirtualMemory";
	/* once */
	at_locked_inc(&__ntapi_init_idx);

	/* pvtbl */
	if (!(pvtbl))
		return NT_STATUS_INVALID_PARAMETER;
	else
		*pvtbl = (ntapi_vtbl *)0;

	/* ntdll */
	if (!(hntdll = pe_get_ntdll_module_handle()))
		return NT_STATUS_DLL_INIT_FAILED;

	pfn_zw_allocate_virtual_memory	= (ntapi_zw_allocate_virtual_memory *)
		pe_get_procedure_address(
			hntdll,
			fname_allocate_virtual_memory);

	if (!pfn_zw_allocate_virtual_memory)
		return NT_STATUS_DLL_INIT_FAILED;

	/* ntapi_internals: alloc */
	block_size = sizeof(ntapi_internals);
	status = pfn_zw_allocate_virtual_memory(
		NT_CURRENT_PROCESS_HANDLE,
		(void **)&internals,
		0,
		&block_size,
		NT_MEM_COMMIT,
		NT_PAGE_READWRITE);

	if (status != NT_STATUS_SUCCESS)
		return status;

	/* ipc connection list */
	if ((status = dalist_init_ex(
			&internals->ipc_conns,
			sizeof(nt_ipc_conn),
			NT_ALLOCATION_GRANULARITY,
			__ipc_memfn,
			DALIST_MEMFN_CUSTOM)))
		return status;

	dalist_deposit_memory_block(
		&internals->ipc_conns,
		&__ntapi_img_bss.ipc_buffer,
		__NT_BSS_IPC_BUFFER_SIZE);

	/* hashed import table */
	__ntapi_tt_populate_hashed_import_table(
		pe_get_ntdll_module_handle(),
		__ntapi,
		import_table,
		__NT_IMPORTED_SYMBOLS_ARRAY_SIZE);

	/* alternate implementation */
	__ntapi->rtl_init_unicode_string 			= __ntapi_tt_init_unicode_string_from_utf16;
	__ntapi->strlen						= __ntapi_tt_string_null_offset_multibyte;
	__ntapi->wcslen						= __ntapi_tt_wcslen;

	/* extension functions */
	/* nt_object.h */
	__ntapi->tt_open_ipc_object_directory			= __ntapi_tt_open_ipc_object_directory;
	__ntapi->tt_create_ipc_object_directory_entry		= __ntapi_tt_create_ipc_object_directory_entry;
	__ntapi->tt_open_dev_object_directory			= __ntapi_tt_open_dev_object_directory;
	__ntapi->tt_create_dev_object_directory_entry		= __ntapi_tt_create_dev_object_directory_entry;
	__ntapi->tt_create_keyed_object_directory		= __ntapi_tt_create_keyed_object_directory;
	__ntapi->tt_open_keyed_object_directory			= __ntapi_tt_open_keyed_object_directory;
	__ntapi->tt_create_keyed_object_directory_entry		= __ntapi_tt_create_keyed_object_directory_entry;
	__ntapi->tt_keyed_object_to_key				= __ntapi_tt_keyed_object_to_key;
	__ntapi->tt_sid_copy					= __ntapi_tt_sid_copy;
	__ntapi->tt_sid_compare					= __ntapi_tt_sid_compare;

	/* nt_crc32.h */
	__ntapi->tt_buffer_crc32 				= __ntapi_tt_buffer_crc32;
	__ntapi->tt_mbstr_crc32					= __ntapi_tt_mbstr_crc32;
	__ntapi->tt_crc32_table					= __ntapi_tt_crc32_table;

	/* nt_crc64.h */
	__ntapi->tt_buffer_crc64 				= __ntapi_tt_buffer_crc64;
	__ntapi->tt_mbstr_crc64					= __ntapi_tt_mbstr_crc64;
	__ntapi->tt_crc64_table					= __ntapi_tt_crc64_table;

	/* nt_file.h */
	__ntapi->tt_get_file_handle_type			= __ntapi_tt_get_file_handle_type;
	__ntapi->tt_open_logical_parent_directory		= __ntapi_tt_open_logical_parent_directory;
	__ntapi->tt_open_physical_parent_directory		= __ntapi_tt_open_physical_parent_directory;
	__ntapi->tt_open_volume_by_guid				= __ntapi_tt_open_volume_by_guid;

	/* nt_ipc.h */
	__ntapi->ipc_connect_by_attr				= __ntapi_ipc_connect_by_attr;
	__ntapi->ipc_connect_by_name				= __ntapi_ipc_connect_by_name;
	__ntapi->ipc_connect_by_symlink				= __ntapi_ipc_connect_by_symlink;
	__ntapi->ipc_connect_by_port				= __ntapi_ipc_connect_by_port;
	__ntapi->ipc_connect_section_by_attr			= __ntapi_ipc_connect_section_by_attr;
	__ntapi->ipc_connect_section_by_name			= __ntapi_ipc_connect_section_by_name;
	__ntapi->ipc_connect_section_by_symlink			= __ntapi_ipc_connect_section_by_symlink;
	__ntapi->ipc_connect_section_by_port			= __ntapi_ipc_connect_section_by_port;
	__ntapi->ipc_get_port_by_attr				= __ntapi_ipc_get_port_by_attr;
	__ntapi->ipc_get_port_section_by_attr			= __ntapi_ipc_get_port_section_by_attr;
	__ntapi->ipc_init_section_by_port			= __ntapi_ipc_init_section_by_port;
	__ntapi->ipc_disconnect_unmap_section_by_port		= __ntapi_ipc_disconnect_unmap_section_by_port;

	/* nt_sem.h */
	__ntapi->sem_create					= __ntapi_sem_create;
	__ntapi->sem_open					= __ntapi_sem_open;
	__ntapi->sem_fcntl					= __ntapi_sem_fcntl;
	__ntapi->sem_ioctl					= __ntapi_sem_ioctl;
	__ntapi->sem_query					= __ntapi_sem_query;
	__ntapi->sem_set					= __ntapi_sem_set;
	__ntapi->sem_cancel					= __ntapi_sem_cancel;
	__ntapi->sem_free					= __ntapi_sem_free;

	/* nt_msq.h */
	__ntapi->msq_create					= __ntapi_msq_create;
	__ntapi->msq_open					= __ntapi_msq_open;
	__ntapi->msq_send					= __ntapi_msq_send;
	__ntapi->msq_recv					= __ntapi_msq_recv;
	__ntapi->msq_fcntl					= __ntapi_msq_fcntl;
	__ntapi->msq_ioctl					= __ntapi_msq_ioctl;
	__ntapi->msq_query					= __ntapi_msq_query;
	__ntapi->msq_set					= __ntapi_msq_set;
	__ntapi->msq_cancel					= __ntapi_msq_cancel;
	__ntapi->msq_free					= __ntapi_msq_free;

	/* nt_afl.h */
	__ntapi->afl_create					= __ntapi_afl_create;
	__ntapi->afl_open					= __ntapi_afl_open;
	__ntapi->afl_fcntl					= __ntapi_afl_fcntl;
	__ntapi->afl_ioctl					= __ntapi_afl_ioctl;
	__ntapi->afl_query					= __ntapi_afl_query;
	__ntapi->afl_set					= __ntapi_afl_set;
	__ntapi->afl_cancel					= __ntapi_afl_cancel;
	__ntapi->afl_free					= __ntapi_afl_free;

	/* nt_ldr.h */
	__ntapi->ldr_load_system_dll				= __ntapi_ldr_load_system_dll;
	__ntapi->ldr_create_state_snapshot			= __ntapi_ldr_create_state_snapshot;
	__ntapi->ldr_revert_state_to_snapshot			= __ntapi_ldr_revert_state_to_snapshot;

	/* nt_string.h */
	__ntapi->tt_string_null_offset_multibyte 		= __ntapi_tt_string_null_offset_multibyte;
	__ntapi->tt_string_null_offset_short			= __ntapi_tt_string_null_offset_short;
	__ntapi->tt_string_null_offset_dword			= __ntapi_tt_string_null_offset_dword;
	__ntapi->tt_string_null_offset_qword			= __ntapi_tt_string_null_offset_qword;
	__ntapi->tt_string_null_offset_ptrsize			= __ntapi_tt_string_null_offset_ptrsize;
	__ntapi->tt_strcmp_multibyte				= __ntapi_tt_strcmp_multibyte;
	__ntapi->tt_strcmp_utf16				= __ntapi_tt_strcmp_utf16;
	__ntapi->tt_strncmp_multibyte				= __ntapi_tt_strncmp_multibyte;
	__ntapi->tt_strncmp_utf16				= __ntapi_tt_strncmp_utf16;
	__ntapi->tt_aligned_block_memset 			= __ntapi_tt_aligned_block_memset;
	__ntapi->tt_aligned_block_memcpy 			= __ntapi_tt_aligned_block_memcpy;
	__ntapi->tt_aligned_block_memlock 			= __ntapi_tt_aligned_block_memlock;
	__ntapi->tt_memcpy_utf16 				= __ntapi_tt_memcpy_utf16;
	__ntapi->tt_aligned_memcpy_utf16 			= __ntapi_tt_aligned_memcpy_utf16;
	__ntapi->tt_generic_memset				= __ntapi_tt_generic_memset;
	__ntapi->tt_generic_memcpy 				= __ntapi_tt_generic_memcpy;
	__ntapi->tt_uint16_to_hex_utf16				= __ntapi_tt_uint16_to_hex_utf16;
	__ntapi->tt_uint32_to_hex_utf16				= __ntapi_tt_uint32_to_hex_utf16;
	__ntapi->tt_uint64_to_hex_utf16				= __ntapi_tt_uint64_to_hex_utf16;
	__ntapi->tt_uintptr_to_hex_utf16 			= __ntapi_tt_uintptr_to_hex_utf16;

	__ntapi->tt_hex_utf16_to_uint16				= __ntapi_tt_hex_utf16_to_uint16;
	__ntapi->tt_hex_utf16_to_uint32				= __ntapi_tt_hex_utf16_to_uint32;
	__ntapi->tt_hex_utf16_to_uint64				= __ntapi_tt_hex_utf16_to_uint64;
	__ntapi->tt_hex_utf16_to_uintptr 			= __ntapi_tt_hex_utf16_to_uintptr;

	__ntapi->tt_hex_utf8_to_uint16				= __ntapi_tt_hex_utf8_to_uint16;
	__ntapi->tt_hex_utf8_to_uint32				= __ntapi_tt_hex_utf8_to_uint32;
	__ntapi->tt_hex_utf8_to_uint64				= __ntapi_tt_hex_utf8_to_uint64;
	__ntapi->tt_hex_utf8_to_uintptr 			= __ntapi_tt_hex_utf8_to_uintptr;

	__ntapi->tt_init_unicode_string_from_utf16 		= __ntapi_tt_init_unicode_string_from_utf16;
	__ntapi->tt_uint16_to_hex_utf8				= __ntapi_tt_uint16_to_hex_utf8;
	__ntapi->tt_uint32_to_hex_utf8				= __ntapi_tt_uint32_to_hex_utf8;
	__ntapi->tt_uint64_to_hex_utf8				= __ntapi_tt_uint64_to_hex_utf8;
	__ntapi->tt_uintptr_to_hex_utf8				= __ntapi_tt_uintptr_to_hex_utf8;
	__ntapi->tt_uintptr_to_dec_utf16			= __ntapi_tt_uintptr_to_dec_utf16;
	__ntapi->tt_uintptr_to_dec_utf8				= __ntapi_tt_uintptr_to_dec_utf8;
	__ntapi->tt_uintptr_to_dec_null_utf16			= __ntapi_tt_uintptr_to_dec_null_utf16;
	__ntapi->tt_uintptr_to_dec_null_utf8			= __ntapi_tt_uintptr_to_dec_null_utf8;
	__ntapi->tt_dec_utf16_to_uint16				= __ntapi_tt_dec_utf16_to_uint16;
	__ntapi->tt_dec_utf16_to_uint32				= __ntapi_tt_dec_utf16_to_uint32;
	__ntapi->tt_dec_utf16_to_uint64				= __ntapi_tt_dec_utf16_to_uint64;
	__ntapi->tt_dec_utf16_to_uintptr			= __ntapi_tt_dec_utf16_to_uintptr;

	/* nt_guid.h */
	__ntapi->tt_guid_copy 					= __ntapi_tt_guid_copy;
	__ntapi->tt_guid_compare 				= __ntapi_tt_guid_compare;
	__ntapi->tt_guid_to_string_utf16 			= __ntapi_tt_guid_to_string_utf16;
	__ntapi->tt_string_to_guid_utf16 			= __ntapi_tt_string_to_guid_utf16;
	__ntapi->tt_guid_to_string_utf8				= __ntapi_tt_guid_to_string_utf8;
	__ntapi->tt_string_to_guid_utf8 			= __ntapi_tt_string_to_guid_utf8;

	/* nt_sysinfo.h */
	__ntapi->tt_get_system_directory_native_path 		= __ntapi_tt_get_system_directory_native_path;
	__ntapi->tt_get_system_directory_dos_path 		= __ntapi_tt_get_system_directory_dos_path;
	__ntapi->tt_get_system_directory_handle			= __ntapi_tt_get_system_directory_handle;
	__ntapi->tt_get_system_info_snapshot  			= __ntapi_tt_get_system_info_snapshot;

	/* nt_thread.h */
	__ntapi->tt_create_local_thread				= __ntapi_tt_create_local_thread;
	__ntapi->tt_create_remote_thread 			= __ntapi_tt_create_remote_thread;
	__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_spawn_foreign_process			= __ntapi_tt_spawn_foreign_process;
	__ntapi->tt_get_runtime_data 				= __ntapi_tt_get_runtime_data;
	__ntapi->tt_init_runtime_data				= __ntapi_tt_init_runtime_data;
	__ntapi->tt_update_runtime_data				= __ntapi_tt_update_runtime_data;
	__ntapi->tt_exec_map_image_as_data			= __ntapi_tt_exec_map_image_as_data;
	__ntapi->tt_exec_unmap_image				= __ntapi_tt_exec_unmap_image;

	/* nt_token.h */
	__ntapi->tt_enable_token_privilege			= __ntapi_tt_enable_token_privilege;
	__ntapi->tt_disable_token_privilege			= __ntapi_tt_disable_token_privilege;

	/* nt_section.h */
	__ntapi->tt_get_section_name 				= __ntapi_tt_get_section_name;

	/* nt_sync.h */
	__ntapi->tt_create_inheritable_event 			= __ntapi_tt_create_inheritable_event;
	__ntapi->tt_create_private_event 			= __ntapi_tt_create_private_event;
	__ntapi->tt_wait_for_dummy_event 			= __ntapi_tt_wait_for_dummy_event;

	/* nt_port.h */
	__ntapi->csr_port_handle 				= __ntapi_csr_port_handle;
	__ntapi->tt_port_guid_from_type				= __ntapi_tt_port_guid_from_type;
	__ntapi->tt_port_type_from_guid				= __ntapi_tt_port_type_from_guid;
	__ntapi->tt_port_prefix_from_type			= __ntapi_tt_port_prefix_from_type;
	__ntapi->tt_port_generate_keys 				= __ntapi_tt_port_generate_keys;
	__ntapi->tt_port_format_keys 				= __ntapi_tt_port_format_keys;
	__ntapi->tt_port_name_from_attr 			= __ntapi_tt_port_name_from_attr;
	__ntapi->tt_port_attr_from_name 			= __ntapi_tt_port_attr_from_name;
	__ntapi->tt_port_attr_from_string 			= __ntapi_tt_port_attr_from_string;
	__ntapi->tt_port_attr_from_symlink 			= __ntapi_tt_port_attr_from_symlink;

	/* nt_argv.h */
	__ntapi->tt_get_cmd_line_utf16 				= __ntapi_tt_get_cmd_line_utf16;
	__ntapi->tt_get_peb_env_block_utf16 			= __ntapi_tt_get_peb_env_block_utf16;
	__ntapi->tt_parse_cmd_line_args_utf16 			= __ntapi_tt_parse_cmd_line_args_utf16;
	__ntapi->tt_get_argv_envp_utf8 				= __ntapi_tt_get_argv_envp_utf8;
	__ntapi->tt_get_argv_envp_utf16				= __ntapi_tt_get_argv_envp_utf16;
	__ntapi->tt_get_env_var_meta_utf8 			= __ntapi_tt_get_env_var_meta_utf8;
	__ntapi->tt_get_env_var_meta_utf16 			= __ntapi_tt_get_env_var_meta_utf16;
	__ntapi->tt_array_copy_utf16				= __ntapi_tt_array_copy_utf16;
	__ntapi->tt_array_copy_utf8				= __ntapi_tt_array_copy_utf8;
	__ntapi->tt_array_convert_utf8_to_utf16			= __ntapi_tt_array_convert_utf8_to_utf16;
	__ntapi->tt_array_convert_utf16_to_utf8			= __ntapi_tt_array_convert_utf16_to_utf8;

	/* nt_blitter.h */
	__ntapi->blt_alloc					= __ntapi_blt_alloc;
	__ntapi->blt_free					= __ntapi_blt_free;
	__ntapi->blt_acquire					= __ntapi_blt_acquire;
	__ntapi->blt_obtain					= __ntapi_blt_obtain;
	__ntapi->blt_possess					= __ntapi_blt_possess;
	__ntapi->blt_release					= __ntapi_blt_release;
	__ntapi->blt_get 					= __ntapi_blt_get;
	__ntapi->blt_set 					= __ntapi_blt_set;

	/* nt_unicode.h */
	__ntapi->uc_validate_unicode_stream_utf8 		= __ntapi_uc_validate_unicode_stream_utf8;
	__ntapi->uc_validate_unicode_stream_utf16 		= __ntapi_uc_validate_unicode_stream_utf16;
	__ntapi->uc_get_code_point_byte_count_utf8		= __ntapi_uc_get_code_point_byte_count_utf8;
	__ntapi->uc_get_code_point_byte_count_utf16		= __ntapi_uc_get_code_point_byte_count_utf16;
	__ntapi->uc_convert_unicode_stream_utf8_to_utf16 	= __ntapi_uc_convert_unicode_stream_utf8_to_utf16;
	__ntapi->uc_convert_unicode_stream_utf8_to_utf32 	= __ntapi_uc_convert_unicode_stream_utf8_to_utf32;
	__ntapi->uc_convert_unicode_stream_utf16_to_utf8 	= __ntapi_uc_convert_unicode_stream_utf16_to_utf8;
	__ntapi->uc_convert_unicode_stream_utf16_to_utf32 	= __ntapi_uc_convert_unicode_stream_utf16_to_utf32;

	/* nt_daemon.h */
	__ntapi->dsr_init					= __ntapi_dsr_init;
	__ntapi->dsr_create_port 				= __ntapi_dsr_create_port;

	/* nt_acl.h */
	__ntapi->acl_init_common_descriptor			= __ntapi_acl_init_common_descriptor;
	__ntapi->acl_init_common_descriptor_meta			= __ntapi_acl_init_common_descriptor_meta;

	/* nt_vfd.h */
	__ntapi->vfd_dev_name_init				= __ntapi_vfd_dev_name_init;

	/* nt_tty.h */
	__ntapi->tty_create_session				= __ntapi_tty_create_session;
	__ntapi->tty_join_session				= __ntapi_tty_join_session;
	__ntapi->tty_connect					= __ntapi_tty_connect;
	__ntapi->tty_client_session_disconnect			= __ntapi_tty_client_session_disconnect;
	__ntapi->tty_client_session_query			= __ntapi_tty_client_session_query;
	__ntapi->tty_client_session_set				= __ntapi_tty_client_session_set;
	__ntapi->tty_client_process_register			= __ntapi_tty_client_process_register;
	__ntapi->tty_query_information_section			= __ntapi_tty_query_information_section;
	__ntapi->tty_query_information_server			= __ntapi_tty_query_information_server;
	__ntapi->tty_query_information_service			= __ntapi_tty_query_information_service;
	__ntapi->tty_query_server_pts_slot_info			= __ntapi_tty_query_server_pts_slot_info;
	__ntapi->tty_request_peer				= __ntapi_tty_request_peer;
	__ntapi->pty_open					= __ntapi_pty_open;
	__ntapi->pty_open_pair					= __ntapi_pty_open_pair;
	__ntapi->pty_reopen					= __ntapi_pty_reopen;
	__ntapi->pty_inherit 					= __ntapi_pty_inherit;
	__ntapi->pty_inherit_runtime_ctty 			= __ntapi_pty_inherit_runtime_ctty;
	__ntapi->pty_close					= __ntapi_pty_close;
	__ntapi->pty_read					= __ntapi_pty_read;
	__ntapi->pty_write					= __ntapi_pty_write;
	__ntapi->pty_ioctl					= __ntapi_pty_ioctl;
	__ntapi->pty_query					= __ntapi_pty_query;
	__ntapi->pty_xquery					= __ntapi_pty_xquery;
	__ntapi->pty_set					= __ntapi_pty_set;
	__ntapi->pty_cancel					= __ntapi_pty_cancel;

	/* nt_socket.h */
	__ntapi->sc_listen					= __ntapi_sc_listen;
	__ntapi->sc_accept					= __ntapi_sc_accept;
	__ntapi->sc_send 					= __ntapi_sc_send;
	__ntapi->sc_recv 					= __ntapi_sc_recv;
	__ntapi->sc_shutdown					= __ntapi_sc_shutdown;
	__ntapi->sc_server_duplicate_socket			= __ntapi_sc_server_duplicate_socket;
	__ntapi->sc_setsockopt					= __ntapi_sc_setsockopt;
	__ntapi->sc_getsockopt					= __ntapi_sc_getsockopt;
	__ntapi->sc_wait = __ntapi_sc_wait;

	/* nt_mount.h */
	__ntapi->tt_get_dos_drive_device_handle			= __ntapi_tt_get_dos_drive_device_handle;
	__ntapi->tt_get_dos_drive_root_handle			= __ntapi_tt_get_dos_drive_root_handle;
	__ntapi->tt_get_dos_drive_device_name			= __ntapi_tt_get_dos_drive_device_name;
	__ntapi->tt_get_dos_drive_mount_points			= __ntapi_tt_get_dos_drive_mount_points;
	__ntapi->tt_dev_mount_points_to_statfs			= __ntapi_tt_dev_mount_points_to_statfs;
	__ntapi->tt_get_dos_drive_letter_from_device		= __ntapi_tt_get_dos_drive_letter_from_device;

	/* nt_istat.h */
	__ntapi->tt_istat					= __ntapi_tt_istat;

	/* nt_stat.h */
	__ntapi->tt_stat 					= __ntapi_tt_stat;

	/* nt_statfs.h */
	__ntapi->tt_statfs					= __ntapi_tt_statfs;

	/* nt_log.h */
	__ntapi->log_write					= __ntapi_log_write;
	__ntapi->log_fn_call					= __ntapi_log_fn_call;
	__ntapi->log_msg 					= __ntapi_log_msg;

	/* nt_debug.h */
	__ntapi->tt_debug_create_object				= __ntapi_tt_debug_create_object;
	__ntapi->tt_debug_create_attach_object			= __ntapi_tt_debug_create_attach_object;
	__ntapi->tt_debug_execution_flow			= __ntapi_tt_debug_execution_flow;
	__ntapi->tt_debug_break_process				= __ntapi_tt_debug_break_process;


	/* OS version dependent functions */
	if (__ntapi->zw_create_user_process) {
		__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;
		__ntapi->sc_bind 				= __ntapi_sc_bind_v2;
		__ntapi->sc_connect				= __ntapi_sc_connect_v2;
		__ntapi->sc_server_accept_connection		= __ntapi_sc_server_accept_connection_v2;
		__ntapi->sc_getsockname				= __ntapi_sc_getsockname_v2;
		__ntapi->sc_getpeername				= __ntapi_sc_getpeername_v2;
	} else {
		__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;
		__ntapi->sc_bind 				= __ntapi_sc_bind_v1;
		__ntapi->sc_connect				= __ntapi_sc_connect_v1;
		__ntapi->sc_server_accept_connection		= __ntapi_sc_server_accept_connection_v1;
		__ntapi->sc_getsockname				= __ntapi_sc_getsockname_v1;
		__ntapi->sc_getpeername				= __ntapi_sc_getpeername_v1;
	}

	/* internals */
	internals->ntapi_img_sec_bss				= &__ntapi_img_bss;
	internals->subsystem					= &__ntapi_rdata.img_sec_data.__session_name;

	internals->tt_get_csr_port_handle_addr_by_logic		= __GET_CSR_PORT_HANDLE_BY_LOGIC;
	internals->csr_port_handle_addr				= __GET_CSR_PORT_HANDLE_BY_LOGIC();

	/* shadow copy for client libraries */
	__ntapi->tt_aligned_block_memcpy(
		(uintptr_t *)&___ntapi_shadow,
		(uintptr_t *)&___ntapi,
		sizeof(ntapi_vtbl));

	/* process handle */
	oa.len      = sizeof(oa);
	oa.root_dir = 0;
	oa.obj_name = 0;
	oa.obj_attr = 0;
	oa.sec_desc = 0;
	oa.sec_qos  = 0;

	cid.process_id = pe_get_current_process_id();
	cid.thread_id  = pe_get_current_thread_id();

	if ((status = __ntapi->zw_open_process(
			&internals->hprocess,
			NT_PROCESS_ALL_ACCESS,
			&oa,&cid)))
		return status;

	/* process token */
	 if ((status = __ntapi->zw_open_process_token(
			NT_CURRENT_PROCESS_HANDLE,
			NT_TOKEN_ALL_ACCESS,
			&internals->htoken)))
		return status;

	__ntapi_init_adjust_privileges();

	/* user */
	if ((status = __ntapi->zw_query_information_token(
			internals->htoken,
			NT_TOKEN_USER,
			buf,sizeof(buf),
			&block_size)))
		return status;

	internals->user  = (nt_sid *)&internals->sid_buffer[0];
	internals->admin = (nt_sid *)&internals->sid_buffer[1];

	__ntapi->tt_sid_copy(
		internals->user,
		((nt_sid_and_attributes *)buf)->sid);

	/* admin */
	value = internals->user->identifier_authority.value;
	sacnt = internals->user->sub_authority_count;

	if ((value[0] == 0) && (value[1] == 0)
			&& (value[2] == 0) && (value[3] == 0)
			&& (value[4] == 0) && (value[5] == 5)
			&& internals->user->sub_authority[0] == 21) {
		__ntapi->tt_sid_copy(
			internals->admin,
			internals->user);

		internals->admin->sub_authority[sacnt - 1] = 500;
	}

	/* done */
	*pvtbl	= &___ntapi_shadow;
	at_locked_inc(&__ntapi_init_idx);

	return NT_STATUS_SUCCESS;
}

static int32_t __ntapi_init_solib_fork_child_finalize(void)
{
	int32_t			status;
	int			page;
	nt_rtdata *		rtdata;
	nt_oa			oa;
	ntapi_internals *	__internals;

	/* detect the rare scenario where libntapi.so is (indirectly)      */
	/* used by a hosted process, in which case post-fork adjustments  */
	/* to internal structures would take place in the copy of ntapi  */
	/* that is statically linked into the system call layer         */
	/* library, thereby requiring similar adjustmets to be made    */
	/* once here as well.                                         */

	__internals = __ntapi_internals();

	if (!(rtdata = __internals->rtdata))
		return NT_STATUS_SUCCESS;

	else if (rtdata->cid_self.process_id == pe_get_current_process_id())
		return NT_STATUS_SUCCESS;

	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 (rtdata->hparent)
		__ntapi->zw_close(rtdata->hparent);

	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;

	status = __ntapi->zw_open_process(
		&__internals->hprocess,
		NT_PROCESS_ALL_ACCESS,
		&oa,&rtdata->cid_self);

	rtdata->hparent = rtdata->hself;
	rtdata->hself   = __internals->hprocess;

	if ((status = dalist_init_ex(
			&__internals->ipc_conns,
			sizeof(nt_ipc_conn),
			NT_ALLOCATION_GRANULARITY,
			__ipc_memfn,
			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 __fastcall __ntapi_init_pending(ntapi_vtbl ** pvtbl)
{
	(void)pvtbl;
	return NT_STATUS_PENDING;
}

static int32_t __fastcall __ntapi_init_completed(ntapi_vtbl ** pvtbl)
{
	*pvtbl = &___ntapi_shadow;
	return __ntapi_init_solib_fork_child_finalize();
}


__ntapi_api
int32_t __fastcall ntapi_init(ntapi_vtbl ** pvtbl)
{
	return __ntapi_init_vtbl[__ntapi_init_idx](pvtbl);
}


ntapi_internals * __cdecl __ntapi_internals(void)
{
	return internals;
}