Tamper DLL Export Names & GetProcAddress Spoofing

Created the Thursday 11 August 2022. Updated 1 month, 3 weeks ago.

When a process is running, it is possible to change the results of the call to GetProcAddress API, for the exported functions of a module along with modifying the export’s offsets and name at runtime.

For example, the offset of kernel32.dll's function VirtualAlloc can be change to the offset of another function. When VirtualAlloc is called (after getting its address from GetProcAddress), the second function will be called instead.

To achieve this, it is possible to use the WINAPI MapAndLoad from ImageHlp.h, then use ImageDirectoryEntryToData to get the list of exports. Then the ImageRvaToVa API can be used to retrieve each exported functions names offset; if desired the export name can be overwritten, resulting in calls to GetProcAddress with that export name to fail or be directed to another function.


Technique Identifier

U1241


Code Snippets

#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;
}

Subscribe to our Newsletter


The information entered into this form is mandatory. It will be subjected to computer processing. It is processed by computer in order to support our users and readers. The recipients of the data will be : contact@unprotect.it.

According to the Data Protection Act of January 6th, 1978, you have at any time, a right of access to and rectification of all of your personal data. If you wish to exercise this right and gain access to your personal data, please write to Thomas Roccia at contact@unprotect.it.

You may also oppose, for legitimate reasons, the processing of your personal data.