diff --git a/include/pemagine/pemagine.h b/include/pemagine/pemagine.h
index 8990b15..61ffb7a 100644
--- a/include/pemagine/pemagine.h
+++ b/include/pemagine/pemagine.h
@@ -30,6 +30,29 @@ struct pe_export_sym {
 };
 
 
+struct pe_guid {
+	uint32_t	data1;
+	uint16_t	data2;
+	uint16_t	data3;
+	unsigned char	data4[8];
+};
+
+
+struct pe_guid_str_utf16 {
+	wchar16_t	lbrace;
+	wchar16_t	group1[8];
+	wchar16_t	dash1;
+	wchar16_t	group2[4];
+	wchar16_t	dash2;
+	wchar16_t	group3[4];
+	wchar16_t	dash3;
+	wchar16_t	group4[4];
+	wchar16_t	dash4;
+	wchar16_t	group5[12];
+	wchar16_t	rbrace;
+};
+
+
 struct pe_unicode_str {
 	uint16_t	strlen;
 	uint16_t	maxlen;
@@ -109,6 +132,16 @@ struct pe_ldr_tbl_entry {
 };
 
 
+struct pe_framework_runtime_data {
+	void *		hself;
+	void *		hparent;
+	void *		himage;
+	void *		hroot;
+	void *		hcwd;
+	void *		hdrive;
+	struct pe_guid	abi;
+};
+
 
 /*  static inlined functions */
 static __inline__ void *	pe_get_teb_address(void);
@@ -200,6 +233,11 @@ pe_api void *				pe_get_kernel32_module_handle	(void);
 pe_api wchar16_t *			pe_get_peb_command_line(void);
 pe_api wchar16_t *			pe_get_peb_environment_block(void);
 
+pe_api int32_t				pe_get_framework_runtime_data(
+						struct pe_framework_runtime_data **	rtdata,
+						const wchar16_t *			cmdline,
+						const struct pe_guid *			abi);
+
 pe_api int32_t				pe_open_image_from_addr(
 						void **			himage,
 						void *			addr,
diff --git a/project/common.mk b/project/common.mk
index 695a656..40bfe21 100644
--- a/project/common.mk
+++ b/project/common.mk
@@ -10,6 +10,7 @@ API_SRCS = \
 	src/headers/pe_get_image_section_tbl_addr.c \
 	src/headers/pe_get_image_special_hdr_addr.c \
 	src/imports/pe_enum_image_import_hdrs.c \
+	src/ldso/pe_get_framework_runtime_data.c \
 	src/ldso/pe_get_peb_strings.c \
 	src/ldso/pe_open_image_from_addr.c \
 	src/ldso/pe_open_physical_parent_directory.c \
diff --git a/src/internal/pe_os.h b/src/internal/pe_os.h
index f8a787a..d0dc903 100644
--- a/src/internal/pe_os.h
+++ b/src/internal/pe_os.h
@@ -3,6 +3,12 @@
 
 #include <pemagine/pe_structs.h>
 
+#define OS_STATUS_SUCCESS			0x00000000
+#define OS_STATUS_INVALID_PARAMETER		0xC000000D
+#define OS_STATUS_ILLEGAL_CHARACTER		0xC0000161
+#define OS_STATUS_NO_MATCH			0xC0000272
+#define OS_STATUS_INVALID_ADDRESS		0xC0000141
+#define OS_STATUS_CONTEXT_MISMATCH		0xC0000719
 #define OS_STATUS_INTERNAL_ERROR		0xC00000E5
 #define OS_STATUS_BAD_FILE_TYPE			0xC0000903
 #define OS_STATUS_OBJECT_NAME_NOT_FOUND 	0xC0000034
@@ -124,6 +130,14 @@ typedef int32_t __stdcall os_zw_query_virtual_memory(
 	__out	uint32_t *		returned_length	__optional);
 
 
+typedef int32_t __stdcall os_zw_read_virtual_memory(
+	__in	void *			hprocess,
+	__in	void *			base_address,
+	__out	char *			buffer,
+	__in	size_t			buffer_length,
+	__out	size_t *		bytes_written);
+
+
 typedef int32_t __stdcall os_zw_open_file(
 	__out	void **			hfile,
 	__in	uint32_t		desired_access,
diff --git a/src/ldso/pe_get_framework_runtime_data.c b/src/ldso/pe_get_framework_runtime_data.c
new file mode 100644
index 0000000..6e024c3
--- /dev/null
+++ b/src/ldso/pe_get_framework_runtime_data.c
@@ -0,0 +1,287 @@
+/*****************************************************************************/
+/*  pemagination: a (virtual) tour into portable bits and executable bytes   */
+/*  Copyright (C) 2013--2017  Z. Gilboa                                      */
+/*  Released under GPLv2 and GPLv3; see COPYING.PEMAGINE.                    */
+/*****************************************************************************/
+
+#include <psxtypes/psxtypes.h>
+#include <pemagine/pemagine.h>
+#include "pe_os.h"
+
+struct pe_framework_cmdline {
+	struct pe_guid_str_utf16	guid;
+	wchar16_t			space1;
+	wchar16_t			rarg[2];
+	wchar16_t			space2;
+	wchar16_t			addr[2*__SIZEOF_POINTER__];
+	wchar16_t			null;
+};
+
+static int32_t pe_hex_utf16_to_uint32(
+	const wchar16_t	hex_key_utf16[8],
+	uint32_t *	key)
+{
+	int		i;
+	unsigned char	uch[8];
+	unsigned char	ubytes[4];
+	uint32_t *	key_ret;
+
+	/* input validation */
+	for (i=0; i<8; i++)
+		if (!((hex_key_utf16[i] >= 'a') && (hex_key_utf16[i] <= 'f')))
+			if (!((hex_key_utf16[i] >= 'A') && (hex_key_utf16[i] <= 'F')))
+				if (!((hex_key_utf16[i] >= '0') && (hex_key_utf16[i] <= '9')))
+					return OS_STATUS_ILLEGAL_CHARACTER;
+
+	/* intermediate step: little endian byte order */
+	uch[0] = (unsigned char)hex_key_utf16[6];
+	uch[1] = (unsigned char)hex_key_utf16[7];
+	uch[2] = (unsigned char)hex_key_utf16[4];
+	uch[3] = (unsigned char)hex_key_utf16[5];
+	uch[4] = (unsigned char)hex_key_utf16[2];
+	uch[5] = (unsigned char)hex_key_utf16[3];
+	uch[6] = (unsigned char)hex_key_utf16[0];
+	uch[7] = (unsigned char)hex_key_utf16[1];
+
+	for (i=0; i<8; i++) {
+		/* 'a' > 'A' > '0' */
+		if (uch[i] >= 'a')
+			uch[i] -= ('a' - 0x0a);
+		else if (uch[i] >= 'A')
+			uch[i] -= ('A' - 0x0a);
+		else
+			uch[i] -= '0';
+	}
+
+	ubytes[0] = uch[0] * 0x10 + uch[1];
+	ubytes[1] = uch[2] * 0x10 + uch[3];
+	ubytes[2] = uch[4] * 0x10 + uch[5];
+	ubytes[3] = uch[6] * 0x10 + uch[7];
+
+	key_ret = (uint32_t *)ubytes;
+	*key = *key_ret;
+
+	return OS_STATUS_SUCCESS;
+}
+
+static int32_t pe_hex_utf16_to_uint16(
+	const wchar16_t hex_key_utf16[4],
+	uint16_t *	key)
+{
+	int32_t		status;
+	uint32_t	dword_key;
+	wchar16_t	hex_buf[8] = {'0','0','0','0'};
+
+	hex_buf[4] = hex_key_utf16[0];
+	hex_buf[5] = hex_key_utf16[1];
+	hex_buf[6] = hex_key_utf16[2];
+	hex_buf[7] = hex_key_utf16[3];
+
+	if ((status = pe_hex_utf16_to_uint32(hex_buf,&dword_key)))
+		return status;
+
+	*key = (uint16_t)dword_key;
+
+	return OS_STATUS_SUCCESS;
+}
+
+static int32_t pe_hex_utf16_to_uint64(
+	const wchar16_t	hex_key_utf16[16],
+	uint64_t *	key)
+{
+	int32_t		status;
+	uint32_t	x64_key[2];
+	uint64_t *	key_ret;
+
+	if ((status = pe_hex_utf16_to_uint32(
+			&hex_key_utf16[0],
+			&x64_key[1])))
+		return status;
+
+	if ((status = pe_hex_utf16_to_uint32(
+			&hex_key_utf16[8],
+			&x64_key[0])))
+		return status;
+
+	key_ret = (uint64_t *)x64_key;
+	*key    = *key_ret;
+
+	return OS_STATUS_SUCCESS;
+}
+
+
+static int32_t pe_hex_utf16_to_uintptr(
+	const wchar16_t	hex_key_utf16[],
+	uintptr_t *	key)
+{
+	#if (__SIZEOF_POINTER__ == 4)
+		return pe_hex_utf16_to_uint32(hex_key_utf16,key);
+	#elif (__SIZEOF_POINTER__ == 8)
+		return pe_hex_utf16_to_uint64(hex_key_utf16,key);
+	#endif
+}
+
+static int32_t pe_string_to_guid_utf16(
+	const struct pe_guid_str_utf16 *guid_str,
+	struct pe_guid *		guid)
+{
+	int32_t			status;
+	uint16_t		key;
+	const wchar16_t *	wch;
+
+	if ((guid_str->lbrace != '{')
+			|| (guid_str->rbrace != '}')
+			|| (guid_str->dash1 != '-')
+			|| (guid_str->dash2 != '-')
+			|| (guid_str->dash3 != '-')
+			|| (guid_str->dash4 != '-'))
+		return OS_STATUS_INVALID_PARAMETER;
+
+	wch = &(guid_str->group5[0]);
+
+	if ((status = pe_hex_utf16_to_uint32(
+			guid_str->group1,
+			&guid->data1)))
+		return status;
+
+	if ((status = pe_hex_utf16_to_uint16(
+			guid_str->group2,
+			&guid->data2)))
+		return status;
+
+	if ((status = pe_hex_utf16_to_uint16(
+			guid_str->group3,
+			&guid->data3)))
+		return status;
+
+	if ((status = pe_hex_utf16_to_uint16(
+			guid_str->group4,
+			&key)))
+		return status;
+
+	guid->data4[0] = key >> 8;
+	guid->data4[1] = key % 0x100;
+
+	if ((status = pe_hex_utf16_to_uint16(
+			&(wch[0]),
+			&key)))
+		return status;
+
+	guid->data4[2] = key >> 8;
+	guid->data4[3] = key % 0x100;
+
+	if ((status = pe_hex_utf16_to_uint16(
+			&(wch[4]),
+			&key)))
+		return status;
+
+	guid->data4[4] = key >> 8;
+	guid->data4[5] = key % 0x100;
+
+	if ((status = pe_hex_utf16_to_uint16(
+			&(wch[8]),
+			&key)))
+		return status;
+
+	guid->data4[6] = key >> 8;
+	guid->data4[7] = key % 0x100;
+
+	return OS_STATUS_SUCCESS;
+}
+
+static int32_t pe_guid_compare(
+	const struct pe_guid *	pguid_dst,
+	const struct pe_guid *	pguid_src)
+{
+	const uint32_t *	dst;
+	const uint32_t *	src;
+
+	dst = &pguid_dst->data1;
+	src = &pguid_src->data1;
+
+	if ((dst[0] == src[0])
+			&& (dst[1] == src[1])
+			&& (dst[2] == src[2])
+			&& (dst[3] == src[3]))
+		return OS_STATUS_SUCCESS;
+
+	return OS_STATUS_NO_MATCH;
+}
+
+int32_t pe_get_framework_runtime_data(
+	struct pe_framework_runtime_data **	rtdata,
+	const wchar16_t *			cmdline,
+	const struct pe_guid *			abi)
+{
+	int32_t					status;
+	struct pe_framework_runtime_data *	prtdata;
+	struct pe_framework_cmdline *		fcmdline;
+	struct pe_guid				guid;
+	uintptr_t				address;
+	uintptr_t				buffer;
+	void *					hntdll;
+	os_zw_read_virtual_memory *		zw_read_virtual_memory;
+
+	/* init */
+	if (!(hntdll = pe_get_ntdll_module_handle()))
+		return OS_STATUS_INTERNAL_ERROR;
+
+	if (!(zw_read_virtual_memory = (os_zw_read_virtual_memory *)pe_get_procedure_address(
+			hntdll,"ZwReadVirtualMemory")))
+		return OS_STATUS_INTERNAL_ERROR;
+
+	/* framework cmdline */
+	fcmdline = (struct pe_framework_cmdline *)cmdline;
+
+	/* framework cmdline: conformance */
+	if (fcmdline->null)
+		return OS_STATUS_INVALID_PARAMETER;
+
+	/* framework cmdline: rarg */
+	if ((fcmdline->space1 != ' ')
+			|| (fcmdline->space2  != ' ')
+			|| (fcmdline->rarg[0] != '-')
+			|| (fcmdline->rarg[1] != 'r'))
+		return OS_STATUS_INVALID_PARAMETER;
+
+	/* framework cmdline: address */
+	if ((status = pe_hex_utf16_to_uintptr(
+			fcmdline->addr,
+			&address)))
+		return status;
+
+	/* framework cmdline: guid */
+	if ((status = pe_string_to_guid_utf16(
+			&fcmdline->guid,
+			&guid)))
+		return status;
+
+	/* framework cmdline: abi */
+	if ((status = pe_guid_compare(&guid,abi)))
+		return status;
+
+	/* address: alignment */
+	if (address & 0xFFF)
+		return OS_STATUS_INVALID_ADDRESS;
+
+	/* address is aligned at page boundary */
+	if ((status = zw_read_virtual_memory(
+			OS_CURRENT_PROCESS_HANDLE,
+			(void *)address,
+			(char *)&buffer,
+			sizeof(buffer),
+			0)))
+		return status;
+
+	/* rtdata */
+	prtdata = (struct pe_framework_runtime_data *)address;
+
+	/* rtdata: abi */
+	if (pe_guid_compare(&prtdata->abi,abi))
+		return OS_STATUS_CONTEXT_MISMATCH;
+
+	/* yay */
+	*rtdata = prtdata;
+
+	return OS_STATUS_SUCCESS;
+}