Blob Blame History Raw
/*****************************************************************************/
/*  pemagination: a (virtual) tour into portable bits and executable bytes   */
/*  Copyright (C) 2013--2020  SysDeer Technologies, LLC                      */
/*  Released under GPLv2 and GPLv3; see COPYING.PEMAGINE.                    */
/*****************************************************************************/

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

static int32_t pe_get_device_dos_drive_letter(
	void *			hntdll,
	os_zw_query_object *	zw_query_object,
	uintptr_t *		dbuffer,
	uint32_t		dbufsize,
	wchar16_t *		devname,
	size_t			devnamelen,
	wchar16_t *		letter)
{
	int32_t			status;
	void *			hdevice;
	int			sigh;
	size_t			idx;
	size_t			cap;
	uint32_t		len;
	struct os_oa		oa;
	struct os_iosb		iosb;
	wchar16_t *		src;
	wchar16_t *		dst;
	struct pe_unicode_str	path;
	struct pe_unicode_str * dpath;
	os_zw_open_file *	zw_open_file;
	os_zw_close *		zw_close;

	wchar16_t		namebuf[8] = {
				'\\','?','?','\\',
				'X',':',0};

	unsigned char		letters[26] = {
				'C','Z','Y','X','W','V',
				'E','H','F','G','D','I',
				'P','Q','R','S','T','U',
				'J','K','L','M','N','O',
				'A','B'};

	/* init */
	if (!(zw_open_file = (os_zw_open_file *)pe_get_procedure_address(
			hntdll,"ZwOpenFile")))
		return OS_STATUS_INTERNAL_ERROR;

	if (!(zw_close = (os_zw_close *)pe_get_procedure_address(
			hntdll,"ZwClose")))
		return OS_STATUS_INTERNAL_ERROR;

	/* path */
	path.buffer = namebuf;
	path.strlen = 6 * sizeof(wchar16_t);
	path.maxlen = 0;

	/* oa */
	oa.len      = sizeof(struct os_oa);
	oa.root_dir = 0;
	oa.obj_name = &path;
	oa.obj_attr = 0;
	oa.sec_desc = 0;
	oa.sec_qos  = 0;

	/* just a few rounds of submissiveness */
	for (sigh=0; sigh<26; sigh++) {
		namebuf[4] = letters[sigh];

		status = zw_open_file(
			&hdevice,
			OS_SEC_SYNCHRONIZE
				| OS_FILE_READ_ATTRIBUTES,
			&oa,&iosb,
			OS_FILE_SHARE_READ
				| OS_FILE_SHARE_WRITE
				| OS_FILE_SHARE_DELETE,
			OS_FILE_SYNCHRONOUS_IO_ALERT);

		if (status == OS_STATUS_SUCCESS) {
			status = zw_query_object(
				hdevice,
				OS_OBJECT_NAME_INFORMATION,
				dbuffer,dbufsize,&len);

			if (status == OS_STATUS_SUCCESS) {
				dpath = (struct pe_unicode_str *)dbuffer;

				src = devname;
				dst = dpath->buffer;

				if (devnamelen == dpath->strlen) {
					idx = 0;
					cap = devnamelen / sizeof(wchar16_t);

					for (; idx<cap && src[idx]==dst[idx]; )
						idx++;

					if (idx==cap) {
						zw_close(hdevice);
						*letter = letters[sigh];

						return OS_STATUS_SUCCESS;
					}
				}
			}

			zw_close(hdevice);
		}
	}

	return OS_STATUS_NOT_SUPPORTED;
}


static int32_t pe_load_library_impl(
	void **		baseaddr,
	void *		hdsolib,
	uintptr_t *	buffer,
	uint32_t	bufsize,
	uint32_t *	flags)
{
	int32_t			status;
	struct pe_unicode_str	path;
	struct pe_unicode_str *	npath;
	uintptr_t		addr;
	uintptr_t *		dbuffer;
	uint32_t		dbufsize;
	wchar16_t *		ldrdir;
	wchar16_t *		slash;
	wchar16_t *		wch;
	wchar16_t *		cap;
	wchar16_t		letter;
	uint32_t		len;
	int			mup;
	void *			hntdll;
	os_zw_query_object *	zw_query_object;
	os_ldr_load_dll *	ldr_load_dll;

	/* init */
	if (!(hntdll = pe_get_ntdll_module_handle()))
		return OS_STATUS_INTERNAL_ERROR;

	if (!(zw_query_object = (os_zw_query_object *)pe_get_procedure_address(
			hntdll,"ZwQueryObject")))
		return OS_STATUS_INTERNAL_ERROR;

	if (!(ldr_load_dll = (os_ldr_load_dll *)pe_get_procedure_address(
			hntdll,"LdrLoadDll")))
		return OS_STATUS_INTERNAL_ERROR;

	/* loader native path */
	if ((status = zw_query_object(
			hdsolib,
			OS_OBJECT_NAME_INFORMATION,
			buffer,bufsize,&len)))
		return status;

	/* integrity */
	if (len == sizeof(struct pe_unicode_str))
		return OS_STATUS_BAD_FILE_TYPE;

	/* first sigh */
	npath = (struct pe_unicode_str *)buffer;

	wch = npath->buffer;
	cap = npath->buffer + (npath->strlen / sizeof(wchar16_t));

	if ((cap < &wch[8])
			|| (wch[0] != '\\')
			|| (wch[1] != 'D')
			|| (wch[2] != 'e')
			|| (wch[3] != 'v')
			|| (wch[4] != 'i')
			|| (wch[5] != 'c')
			|| (wch[6] != 'e')
			|| (wch[7] != '\\'))
		return OS_STATUS_NOT_SUPPORTED;

	mup = (cap > &wch[11])
		&& (wch[8]=='M')
		&& (wch[9]=='u')
		&& (wch[10]=='p')
		&& (wch[11]=='\\');

	slash = mup ? &wch[12] : &wch[8];

	for (; (*slash != '\\') && (slash < cap); )
		slash++;

	if (slash == cap)
		return OS_STATUS_INTERNAL_ERROR;

	if (mup)
		for (++slash; (*slash != '\\') && (slash < cap); )
			slash++;

	if (slash == cap)
		return OS_STATUS_INTERNAL_ERROR;

	/* second sigh */
	addr  = (uintptr_t)cap;
	addr += sizeof(uintptr_t) - 1;
	addr /= sizeof(uintptr_t);
	addr *= sizeof(uintptr_t);

	dbuffer   = (uintptr_t *)addr;
	dbufsize  = bufsize;
	dbufsize -= (dbuffer - buffer) * sizeof(uintptr_t);

	if ((status = pe_get_device_dos_drive_letter(
			hntdll,
			zw_query_object,
			dbuffer,dbufsize,
			npath->buffer,
			(slash - npath->buffer) * sizeof(wchar16_t),
			&letter)))
		return status;

	slash    = &slash[-2];
	slash[0] = letter;
	slash[1] = ':';

	path.buffer = slash;
	path.strlen = (cap - slash) * sizeof(wchar16_t);

	/* third sigh */
	for (slash=cap; (slash > path.buffer) && (*slash != '\\'); )
		slash--;

	if (slash == path.buffer)
		return OS_STATUS_INTERNAL_ERROR;

	ldrdir = path.buffer;
	*slash = 0;

	path.strlen -= (++slash - ldrdir) * sizeof(wchar16_t);
	path.maxlen  = path.strlen;
	path.buffer  = slash;

	/* hoppla */
	return ldr_load_dll(
		ldrdir,flags,
		&path,baseaddr);
}


int32_t pe_load_framework_loader(
	void **					baseaddr,
	struct pe_framework_runtime_data *	rtdata,
	uintptr_t *				buffer,
	uint32_t				bufsize,
	uint32_t *				sysflags)
{
	return pe_load_library_impl(
		baseaddr,
		rtdata->hloader,
		buffer,bufsize,
		sysflags);
}


int32_t pe_load_framework_library(
	void **					baseaddr,
	void *					hat,
	const wchar16_t *			atrelname,
	uintptr_t *				buffer,
	uint32_t				bufsize,
	uint32_t *				sysflags)
{
	int32_t			status;
	struct pe_unicode_str	path;
	struct os_oa		oa;
	struct os_iosb		iosb;
	const wchar16_t *	wch;
	const wchar16_t *	wch_cap;
	void *			hdsolib;
	void *			hntdll;
	os_zw_close *		zw_close;
	os_zw_open_file *	zw_open_file;

	/* init */
	if (!(hntdll = pe_get_ntdll_module_handle()))
		return OS_STATUS_INTERNAL_ERROR;

	if (!(zw_close = (os_zw_close *)pe_get_procedure_address(
			hntdll,"ZwClose")))
		return OS_STATUS_INTERNAL_ERROR;

	if (!(zw_open_file = (os_zw_open_file *)pe_get_procedure_address(
			hntdll,"ZwOpenFile")))
		return OS_STATUS_INTERNAL_ERROR;

	/* oa */
	oa.len      = sizeof(struct os_oa);
	oa.root_dir = hat;
	oa.obj_name = &path;
	oa.obj_attr = 0;
	oa.sec_desc = 0;
	oa.sec_qos  = 0;

	/* at-relative path */
	wch     = atrelname;
	wch_cap = atrelname + 512;

	for (; *wch && wch<wch_cap; )
		wch++;

	if (*wch)
		return OS_STATUS_NAME_TOO_LONG;

	path.buffer = (wchar16_t *)atrelname;
	path.strlen = sizeof(wchar16_t) * (wch - atrelname);
	path.maxlen = 0;

	/* open at */
	if ((status = zw_open_file(
			&hdsolib,
			OS_SEC_SYNCHRONIZE
				| OS_FILE_READ_ACCESS
				| OS_FILE_READ_ATTRIBUTES,
			&oa,&iosb,
			OS_FILE_SHARE_READ
				| OS_FILE_SHARE_WRITE
				| OS_FILE_SHARE_DELETE,
			OS_FILE_NON_DIRECTORY_FILE)))
		return status;

	/* hoppla */
	status = pe_load_library_impl(
		baseaddr,hdsolib,
		buffer,bufsize,
		sysflags);

	/* all done */
	zw_close(hdsolib);

	return status;
}