Here's some code you can use to call the exports of a module in a remote process. My code assumes the function prototype is:
extern "C" void ();
But you can easily modify it to whatever you want.
Requires EnsureCleanup class collection posted in the utilities section. The function is a class member, but you can easily change it to a free function, it's a static member so simply removing the member qualification should do.
// Call an exported function in a module in a remote process
inline DWORD Injector::CallExport(DWORD ProcID, const std::string& ModuleName,
const std::string& ExportName)
{
// For HCommon
using namespace Hades;
// For C++ Standard Library
using namespace std;
// Grab a new snapshot of the process
EnsureCloseHandle Snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPMODUL E,
ProcID));
if (Snapshot == INVALID_HANDLE_VALUE)
{
throw runtime_error("Injector::CallExport: Could not get module "
"snapshot for remote process.");
}
// Get the HMODULE of the desired library
MODULEENTRY32W ModEntry = { sizeof(ModEntry) };
bool Found = false;
BOOL bMoreMods = Module32FirstW(Snapshot, &ModEntry);
for (; bMoreMods; bMoreMods = Module32NextW(Snapshot, &ModEntry))
{
wstring ExePath(ModEntry.szExePath);
wstring ModuleTmp(ModuleName.begin(), ModuleName.end());
Found = (ExePath == ModuleTmp);
if (Found) break;
}
if (!Found)
{
throw runtime_error("Injector::CallExport: Could not find module in "
"remote process.");
}
// Get module base address
PBYTE ModuleBase = ModEntry.modBaseAddr;
// Get a handle for the target process.
EnsureCloseHandle Process(OpenProcess(
PROCESS_QUERY_INFORMATION |
PROCESS_CREATE_THREAD |
PROCESS_VM_OPERATION |
PROCESS_VM_READ,
FALSE, ProcID));
if (!Process)
{
throw runtime_error("Injector::CallExport: Could not get handle to "
"process.");
}
// Load module as data so we can read the EAT locally
EnsureFreeLibrary MyModule(LoadLibraryExA(ModuleName.c_str(), NULL,
DONT_RESOLVE_DLL_REFERENCES));
// Get module pointer
PVOID Module = static_cast<PVOID>(MyModule);
// Get pointer to DOS header
PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(
static_cast<HMODULE>(Module));
if (!pDosHeader || pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
throw runtime_error("Injector::CallExport: DOS PE header "
"is invalid.");
}
// Get pointer to NT header
PIMAGE_NT_HEADERS pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>
(reinterpret_cast<PCHAR>(Module) + pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
throw runtime_error("Injector::CallExport: NT PE header "
"is invalid.");
// Get pointer to image export directory
PVOID pExportDirTemp = reinterpret_cast<PBYTE>(Module) +
pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
VirtualAddress;
PIMAGE_EXPORT_DIRECTORY pExportDir =
reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(pExportD irTemp);
// Symbol names could be missing entirely
if (pExportDir->AddressOfNames == NULL)
{
throw runtime_error("Injector::CallExport: Symbol names "
"missing entirely.");
}
// Get pointer to export names table
PDWORD pNamesRvas = reinterpret_cast<PDWORD>(
reinterpret_cast<PBYTE>(Module) + pExportDir->AddressOfNames);
// Get pointer to export ordinal table
PWORD pNameOrdinals = reinterpret_cast<PWORD>(
reinterpret_cast<PBYTE>(Module) + pExportDir->AddressOfNameOrdinals);
// Get pointer to export address table
PDWORD pFunctionAddresses = reinterpret_cast<PDWORD>(
reinterpret_cast<PBYTE>(Module) + pExportDir->AddressOfFunctions);
// Variable to hold the export address
FARPROC pExportAddr = 0;
// Walk the array of this module's function names
for (DWORD n = 0; n < pExportDir->NumberOfNames; n++)
{
// Get the function name
PSTR CurrentName = reinterpret_cast<PSTR>(
reinterpret_cast<PBYTE>(Module) + pNamesRvas[n]);
// If not the specified function, try the next function
if (ExportName != CurrentName) continue;
// We found the specified function
// Get this function's Ordinal value
WORD Ordinal = pNameOrdinals[n];
// Get the address of this function's address
pExportAddr = reinterpret_cast<FARPROC>(reinterpret_cast<PBYTE>( Module)
+ pFunctionAddresses[Ordinal]);
// We got the func. Break out.
break;
}
// Nothing found, throw exception
if (!pExportAddr)
{
throw runtime_error("Injector::CallExport: Could not find "
+ ExportName + ".");
}
// Convert local address to remote address
// TODO: Clean the casts. Currently working but could be simplified.
PTHREAD_START_ROUTINE pfnThreadRtn =
reinterpret_cast<PTHREAD_START_ROUTINE>((reinterpr et_cast<DWORD_PTR>(
pExportAddr) - reinterpret_cast<DWORD_PTR>(Module)) +
reinterpret_cast<DWORD_PTR>(ModuleBase));
// Create a remote thread that calls the desired export
EnsureCloseHandle Thread(CreateRemoteThread(Process, NULL, 0,
pfnThreadRtn, ModEntry.modBaseAddr, 0, NULL));
if (!Thread)
{
throw runtime_error("Injector::CallExport: Could not create thread in "
"remote process.");
}
// Wait for the remote thread to terminate
WaitForSingleObject(Thread, INFINITE);
// Get thread exit code
DWORD ExitCode = 0;
if (!GetExitCodeThread(Thread,&ExitCode))
{
throw runtime_error("Injector::CallExport: Could not get thread exit "
"code.");
}
// Return thread exit code
return ExitCode;
}
Author: Chazwazza