Windows C++ / Tamper DLL Export Names & GetProcAddress Spoofing

Author Alex Schwarz
Platform Windows
Language C++
Technique Tamper DLL Export Names & GetProcAddress Spoofing

Code

#pragma comment(linker,"/export:FuncA=DLLExport.FunctionA,@1") 

#include <iostream>
#include <Windows.h>
#include <ImageHlp.h>
#pragma comment(lib, "ImageHlp")

using namespace std;

bool ModifyDLLExportName(string dllName, string functionName, string newName)
{
	DWORD* dNameRVAs(0);
	_IMAGE_EXPORT_DIRECTORY* ImageExportDirectory;
	unsigned long cDirSize;
	_LOADED_IMAGE LoadedImage;
	string sName;

	if (MapAndLoad(dllName.c_str(), NULL, &LoadedImage, TRUE, TRUE))
	{
		ImageExportDirectory = (_IMAGE_EXPORT_DIRECTORY*)ImageDirectoryEntryToData(LoadedImage.MappedAddress, false, IMAGE_DIRECTORY_ENTRY_EXPORT, &cDirSize);

		if (ImageExportDirectory != NULL)
		{

			dNameRVAs = (DWORD*)ImageRvaToVa(LoadedImage.FileHeader, LoadedImage.MappedAddress, ImageExportDirectory->AddressOfNames, NULL);

			for (size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++)
			{
				sName = (char*)ImageRvaToVa(LoadedImage.FileHeader, LoadedImage.MappedAddress, dNameRVAs[i], NULL);

				if (strcmp(functionName.c_str(), sName.c_str()) == 0)
				{
					UINT64 funcName_Address = (UINT64)GetModuleHandleA(dllName.c_str()) + dNameRVAs[i];

					DWORD oldProt = 0;

					if (!VirtualProtect((LPVOID)funcName_Address, 1024, PAGE_EXECUTE_READWRITE, &oldProt))
					{
						printf("VirtualProtect failed: %d\n", GetLastError());
						return false;
					}
					else
					{
						strcpy_s((char*)funcName_Address, 100, newName.c_str());
						printf("Copied over export function name..\n");
					}
				}
			}
		}
		UnMapAndLoad(&LoadedImage);
	}

	return true;
}


int main(void)
{
	//user-provided DLL Tests
	LoadLibraryA("DllExport.dll");

	DWORD addr_exe = (DWORD)GetProcAddress(GetModuleHandleA("ModifyExports.exe"), "FuncA"); //using linker statement at top
	printf("Addr: %x\n", addr_exe);

	DWORD addr_dll = (DWORD)GetProcAddress(GetModuleHandleA("DllExport.dll"), "FunctionA"); //returns same value as above line (address of DllExport.FunctionA)
	printf("Addr: %x\n", addr_dll);

	ModifyDLLExportName("DllExport.dll", "FunctionA", "FunctionB");
	DWORD addr_dll_B = (DWORD)GetProcAddress(GetModuleHandleA("DllExport.dll"), "FunctionB"); //returns same value as above, thus looking up FunctionB gives us FunctionA.
	printf("Addr: %x\n", addr_dll_B);

	//WINAPI Tests

	DWORD addr = (DWORD)GetProcAddress(GetModuleHandleA("kernel32.dll"), "VirtualAlloc"); //this will return 0, as the above line changed the export's name.
	printf("Addr: %x\n", addr);
	ModifyDLLExportName("kernel32.dll", "VirtualAlloc", "VirtualQuery");
	addr = (DWORD)GetProcAddress(GetModuleHandleA("kernel32.dll"), "VirtualAlloc"); //returns 0
	printf("Addr: %x\n", addr);
	addr = (DWORD)GetProcAddress(GetModuleHandleA("kernel32.dll"), "VirtualQuery"); //now returns the address of VirtualAlloc, not VirtualQuery!
	printf("Addr: %x\n", addr);
	system("pause");
	return 0;
}

Created

August 11, 2022

Last Revised

April 22, 2024