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"

int32_t pe_find_framework_loader(
	struct pe_framework_runtime_data *	rtdata,
	const wchar16_t *			basename,
	const wchar16_t *			rrelname,
	void *					refaddr,
	uintptr_t *				buffer,
	uint32_t				bufsize,
	uint32_t				flags)
{
	int32_t			status;
	struct pe_unicode_str	path;
	struct os_oa		oa;
	struct os_iosb		iosb;
	void *			himage;
	void *			himgdir;
	void *			hdsodir;
	void *			hloader;
	void *			hparent;
	void *			hprevious;
	const wchar16_t *	wch;
	const wchar16_t *	wch_cap;
	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;

	/* flags */
	if (flags == PE_LDSO_INTEGRAL_ONLY)
		(void)0;
	else if (flags == PE_LDSO_DEFAULT_EXECUTABLE)
		(void)0;
	else if (flags == PE_LDSO_STANDALONE_EXECUTABLE)
		(void)0;
	else
		return OS_STATUS_INVALID_PARAMETER;

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

	/* standalone executable? */
	if (flags == PE_LDSO_STANDALONE_EXECUTABLE) {
		wch     = basename;
		wch_cap = basename + 512;

		for (; *wch && wch<wch_cap; wch++)
			if ((*wch == '\\') || (*wch == '/'))
				return OS_STATUS_INVALID_PARAMETER;

		if (*wch)
			return OS_STATUS_NAME_TOO_LONG;

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

		if ((status = pe_open_image_from_addr(
				&himage,refaddr,
				buffer,bufsize,
				0,
				OS_SEC_SYNCHRONIZE
					| OS_FILE_READ_ACCESS
					| OS_FILE_READ_ATTRIBUTES,
				OS_FILE_SHARE_READ
					| OS_FILE_SHARE_WRITE
					| OS_FILE_SHARE_DELETE,
				0)))
			return status;

		if ((status = pe_open_physical_parent_directory(
				&hdsodir,himage,
				buffer,bufsize,
				0,
				OS_SEC_SYNCHRONIZE
					| OS_FILE_READ_ACCESS
					| OS_FILE_READ_ATTRIBUTES,
				OS_FILE_SHARE_READ
					| OS_FILE_SHARE_WRITE
					| OS_FILE_SHARE_DELETE,
				0)))
			return status;

		oa.root_dir = hdsodir;

		if ((status = zw_open_file(
				&hloader,
				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;

		if (rtdata->hloader)
			zw_close(hloader);
		else
			rtdata->hloader = hloader;

		rtdata->hdsodir = hdsodir;
		rtdata->himage  = himage;

		return OS_STATUS_SUCCESS;
	}

	/* root-relative loader path */
	wch     = rrelname;
	wch_cap = rrelname + 512;

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

	if (*wch)
		return OS_STATUS_NAME_TOO_LONG;

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

	/* inherited root directory? */
	if (rtdata->hroot) {
		oa.root_dir = rtdata->hroot;

		if ((status = zw_open_file(
				&hloader,
				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;

		if (rtdata->hloader)
			zw_close(hloader);
		else
			rtdata->hloader = hloader;

		rtdata->hdsodir = 0;

		return OS_STATUS_SUCCESS;
	}

	/* integral only and no inherited root directory? */
	if (flags == PE_LDSO_INTEGRAL_ONLY)
		return OS_STATUS_COULD_NOT_INTERPRET;

	/* finde Pluto / find Waldo */
	if ((status = pe_open_image_from_addr(
			&himage,refaddr,
			buffer,bufsize,
			0,
			OS_SEC_SYNCHRONIZE
				| OS_FILE_READ_ACCESS
				| OS_FILE_READ_ATTRIBUTES,
			OS_FILE_SHARE_READ
				| OS_FILE_SHARE_WRITE
				| OS_FILE_SHARE_DELETE,
			0)))
		return status;

	if ((status = pe_open_physical_parent_directory(
			&himgdir,himage,
			buffer,bufsize,
			0,
			OS_SEC_SYNCHRONIZE
				| OS_FILE_READ_ACCESS
				| OS_FILE_READ_ATTRIBUTES,
			OS_FILE_SHARE_READ
				| OS_FILE_SHARE_WRITE
				| OS_FILE_SHARE_DELETE,
			0)))
		return status;

	if ((status = pe_open_physical_parent_directory(
			&hparent,himgdir,
			buffer,bufsize,
			OS_OBJ_INHERIT,
			OS_SEC_SYNCHRONIZE
				| OS_FILE_READ_ACCESS
				| OS_FILE_READ_ATTRIBUTES,
			OS_FILE_SHARE_READ
				| OS_FILE_SHARE_WRITE
				| OS_FILE_SHARE_DELETE,
			0)))
		return status;

	zw_close(himgdir);

	hloader     = 0;
	oa.root_dir = hparent;

	status = zw_open_file(
		&hloader,
		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);

	while (!hloader) {
		if (status == OS_STATUS_OBJECT_NAME_NOT_FOUND)
			(void)0;
		else if (status == OS_STATUS_OBJECT_PATH_NOT_FOUND)
			(void)0;
		else
			return status;

		hprevious = hparent;

		if ((status = pe_open_physical_parent_directory(
				&hparent,hprevious,
				buffer,bufsize,
				OS_OBJ_INHERIT,
				OS_SEC_SYNCHRONIZE
					| OS_FILE_READ_ACCESS
					| OS_FILE_READ_ATTRIBUTES,
				OS_FILE_SHARE_READ
					| OS_FILE_SHARE_WRITE
					| OS_FILE_SHARE_DELETE,
				0)))
			return status;

		oa.root_dir = hparent;

		status = zw_open_file(
			&hloader,
			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);

		zw_close(hprevious);
	}

	if (rtdata->hloader)
		zw_close(hloader);
	else
		rtdata->hloader = hloader;

	rtdata->hdsodir = 0;
	rtdata->himage  = himage;
	rtdata->hroot   = hparent;

	return OS_STATUS_SUCCESS;
}