Click here to Skip to main content
15,894,540 members
Articles / Containers / Virtual Machine

.NET Internals and Native Compiling

Rate me:
Please Sign up or sign in to vote.
4.94/5 (75 votes)
30 May 2008CPOL33 min read 126.5K   323   163  
An article about .NET internals and native compiling.
// fxloader.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "fxloader.h"
#include "detours.h"

#define ASSEMBLY_TO_LOAD	_T("rebtest.exe")
#define ASSEMBLY_TO_LOAD_A	"rebtest.exe"
#define ASSEMBLY_TO_LOAD_W	L"rebtest.exe"

#define IS_FLAG(Value, Flag) ((Value & Flag) == Flag) 

typedef ULONG_PTR THUNK;

VOID FixIAT(VOID *pBase, IMAGE_NT_HEADERS *pNtHeaders);
VOID FixReloc(VOID *pBase, IMAGE_NT_HEADERS *pNtHeaders);

HMODULE pMainBaseAddr = NULL;

CHAR MainAsmNameA[MAX_PATH];
WCHAR MainAsmNameW[MAX_PATH];

HMODULE (WINAPI *pGetModuleHandleA)(LPCSTR lpModuleName) = GetModuleHandleA;
HMODULE (WINAPI *pGetModuleHandleW)(LPCWSTR lpModuleName) = GetModuleHandleW;

DWORD (WINAPI *pGetModuleFileNameA)(HMODULE hModule, LPCH lpFilename, 
									DWORD nSize) = GetModuleFileNameA;

DWORD (WINAPI *pGetModuleFileNameW)(HMODULE hModule, LPWCH lpFilename, 
									DWORD nSize) = GetModuleFileNameW;

HMODULE WINAPI MyGetModuleHandleA(LPCSTR lpModuleName);
HMODULE WINAPI MyGetModuleHandleW(LPCWSTR lpModuleName);

DWORD WINAPI MyGetModuleFileNameA(HMODULE hModule, LPCH lpFilename, DWORD nSize);
DWORD WINAPI MyGetModuleFileNameW(HMODULE hModule, LPWCH lpFilename, DWORD nSize);


int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	//////////////////////////////////////////////////////////////////////////
	// TODO: hook registry and load library
	//////////////////////////////////////////////////////////////////////////

	HMODULE hMainAsm = LoadLibrary(ASSEMBLY_TO_LOAD);

	if (hMainAsm == NULL) return 0;

	pMainBaseAddr = hMainAsm;

	GetModuleFileNameA(NULL, MainAsmNameA, MAX_PATH);
	CHAR *cSlash = strrchr(MainAsmNameA, '\\') + 1;
	strcpy(cSlash, ASSEMBLY_TO_LOAD_A);

	GetModuleFileNameW(NULL, MainAsmNameW, MAX_PATH);
	WCHAR *wSlash = wcsrchr(MainAsmNameW, '\\') + 1;
	wcscpy(wSlash, ASSEMBLY_TO_LOAD_W);

	//
	// Hook GetModuleXXXX APIs
	//

	DetourRestoreAfterWith();

	DetourTransactionBegin();
	DetourUpdateThread(GetCurrentThread());
	DetourAttach(&(PVOID&)pGetModuleFileNameA, MyGetModuleFileNameA);
	DetourAttach(&(PVOID&)pGetModuleFileNameW, MyGetModuleFileNameW);
	DetourAttach(&(PVOID&)pGetModuleHandleA, MyGetModuleHandleA);
	DetourAttach(&(PVOID&)pGetModuleHandleW, MyGetModuleHandleW);
	LONG err = DetourTransactionCommit();

	if (err != NO_ERROR) return 0;

	//

	IMAGE_DOS_HEADER *pDosHeader = (IMAGE_DOS_HEADER *) hMainAsm;

	IMAGE_NT_HEADERS *pNtHeaders = (IMAGE_NT_HEADERS *) (pDosHeader->e_lfanew +
		(ULONG_PTR) pDosHeader);

	if (pNtHeaders->OptionalHeader.ImageBase != (ULONG_PTR) pDosHeader)
		FixReloc(pDosHeader, pNtHeaders);

	FixIAT(pDosHeader, pNtHeaders);

	// retrieve entry point

	VOID *pEntryPoint = (VOID *) (pNtHeaders->OptionalHeader.AddressOfEntryPoint +
		(ULONG_PTR) pDosHeader);

	__asm
	{
		jmp pEntryPoint
	}

	return 0;
}

HMODULE WINAPI MyGetModuleHandleW(LPCWSTR lpModuleName)
{
	if (lpModuleName == NULL)
		return pMainBaseAddr;

	return pGetModuleHandleW(lpModuleName);
}

HMODULE WINAPI MyGetModuleHandleA(LPCSTR lpModuleName)
{
	if (lpModuleName == NULL)
		return pMainBaseAddr;

	return pGetModuleHandleA(lpModuleName);
}

DWORD WINAPI MyGetModuleFileNameA(HMODULE hModule, LPCH lpFilename, DWORD nSize)
{
	if (hModule == NULL)
	{
		strcpy_s(lpFilename, nSize, MainAsmNameA);
		return (DWORD) strlen(lpFilename);
	}

	return pGetModuleFileNameA(hModule, lpFilename, nSize);
}

DWORD WINAPI MyGetModuleFileNameW(HMODULE hModule, LPWCH lpFilename, DWORD nSize)
{
	if (hModule == NULL)
	{
		wcscpy_s(lpFilename, nSize, MainAsmNameW);
		return (DWORD) wcslen(lpFilename);
	}

	return pGetModuleFileNameW(hModule, lpFilename, nSize);
}

// x64 compatible
VOID FixIAT(VOID *pBase, IMAGE_NT_HEADERS *pNtHeaders)
{
	if (pNtHeaders->OptionalHeader.DataDirectory
		[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0)
		return;

	IMAGE_IMPORT_DESCRIPTOR *pImpDescr = (IMAGE_IMPORT_DESCRIPTOR *)
		(pNtHeaders->OptionalHeader.DataDirectory
		[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress +
		(ULONG_PTR) pBase);

	DWORD dwOldIATProtect;
	VOID *pIAT = NULL;

	if (pNtHeaders->OptionalHeader.DataDirectory
		[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress != 0)
	{
		VOID *pIAT = (VOID *) (pNtHeaders->OptionalHeader.DataDirectory
			[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
			(ULONG_PTR) pBase);

		VirtualProtect(pIAT,
			pNtHeaders->OptionalHeader.DataDirectory
			[IMAGE_DIRECTORY_ENTRY_IAT].Size,
			PAGE_EXECUTE_READWRITE,
			&dwOldIATProtect);
	}

	while (pImpDescr->Name != 0)
	{
		char *DllName = (char *) (pImpDescr->Name +
			(ULONG_PTR) pBase);

		HMODULE hImpDll = LoadLibraryA(DllName);

		if (hImpDll == NULL) continue;

		THUNK *pThunk;

		if (pImpDescr->OriginalFirstThunk)
			pThunk = (THUNK *)(pImpDescr->OriginalFirstThunk +
			(ULONG_PTR) pBase);
		else
			pThunk = (THUNK *)(pImpDescr->FirstThunk +
			(ULONG_PTR) pBase);

		THUNK *pIATThunk = (THUNK *) (pImpDescr->FirstThunk +
			(ULONG_PTR) pBase);

		while (*pThunk)
		{
			if (IS_FLAG(*pThunk, IMAGE_ORDINAL_FLAG))
			{
				*pIATThunk = (THUNK) GetProcAddress(hImpDll,
					(LPCSTR) (*pThunk ^ IMAGE_ORDINAL_FLAG));
			}
			else
			{
				char *pImpFunc = (char *) (sizeof (WORD) + ((ULONG_PTR) *pThunk) +
					((ULONG_PTR) pBase));

				*pIATThunk = (THUNK) GetProcAddress(hImpDll, pImpFunc);
			}

			pThunk++;
			pIATThunk++;
		}

		pImpDescr++;
	}

	if (pIAT)
	{
		VirtualProtect(pIAT,
			pNtHeaders->OptionalHeader.DataDirectory
			[IMAGE_DIRECTORY_ENTRY_IAT].Size,
			dwOldIATProtect,
			&dwOldIATProtect);
	}
}

// x86 recycled code from an older article
VOID FixReloc(VOID *pBase, IMAGE_NT_HEADERS *pNtHeaders)
{
	//
	// Set first section to writeable in order to fix
	// the relocations in the code
	//

	IMAGE_SECTION_HEADER *pCodeSect = (IMAGE_SECTION_HEADER *)
		IMAGE_FIRST_SECTION(pNtHeaders);

	VOID *pCode = (VOID *) (pCodeSect->VirtualAddress + (ULONG_PTR) pBase);

	DWORD dwOldCodeProtect;

	VirtualProtect(pCode,
		pCodeSect->Misc.VirtualSize,
		PAGE_READWRITE,
		&dwOldCodeProtect);

	//
	// Relocate
	//

	DWORD Delta = (DWORD)(((ULONG_PTR) pBase) - 
		pNtHeaders->OptionalHeader.ImageBase);

	DWORD RelocRva;

	if (!(RelocRva = pNtHeaders->OptionalHeader.DataDirectory
		[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress))
		return;

	IMAGE_BASE_RELOCATION *ImgBaseReloc = 
		(IMAGE_BASE_RELOCATION *) (RelocRva + (ULONG_PTR) pBase);

	WORD *wData;

	do
	{
		if (!ImgBaseReloc->SizeOfBlock)
			break;

		UINT nItems = (ImgBaseReloc->SizeOfBlock -
			IMAGE_SIZEOF_BASE_RELOCATION) / sizeof (WORD);

		wData = (WORD *)(IMAGE_SIZEOF_BASE_RELOCATION +
			(ULONG_PTR) ImgBaseReloc);

		for (UINT i = 0; i < nItems; i++)
		{
			DWORD Offset = (*wData & 0xFFF) + ImgBaseReloc->VirtualAddress;

			DWORD Type = *wData >> 12;

			if (Type != IMAGE_REL_BASED_ABSOLUTE)
			{
				DWORD *pBlock = (DWORD *)(Offset + (ULONG_PTR) pBase);

				*pBlock += Delta;
			}

			wData++;
		}

		ImgBaseReloc = (PIMAGE_BASE_RELOCATION) wData;

	} while (*(DWORD *) wData);

	//
	// Restore memory settings
	//

	VirtualProtect(pCode,
		pCodeSect->Misc.VirtualSize,
		dwOldCodeProtect,
		&dwOldCodeProtect);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Germany Germany
The languages I know best are: C, C++, C#, Assembly (x86, x64, ARM), MSIL, Python, Lua. The environments I frequently use are: Qt, Win32, MFC, .NET, WDK. I'm a developer and a reverse engineer and I like playing around with internals.

You can find most of my work at http://ntcore.com.

Comments and Discussions