/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** Class Name: cModuleLoader ** Written by: 2ci- ** http://www.cod2hacks.com ** ** This is a class implementation based on ManualMap ** written by Darawk. Full credits go to him. ** This version has an extra feature which i needed. ** * * * * * * * * * * * * * * * * * * * * * * * * * * * */// ManualMap - by Darawk//// v1.1// - Fixed a bug that would cause it not to load modules// that were in folders in the PATH environment variable// - Fixed a bug that made it so that when ManualMap loaded// a dependency of the main module it was loading, it// wouldn't keep track of them, so GetRemoteModuleHandle()// wouldn't be able to find it them. This would have// made it impossible for the main module to import// functions from some of it's dependencies.// - Also new is that I wrote my own implementation of// GetProcAddress. Available here:// http://www.darawk.com/Code/GetProcAddress2.cpp// This allows you to dynamically locate functions// exported by ManualMap'd modules. //// The purpose of ManualMap is to "manually map" a dll// module into a remote process's address space. This// means that instead of just manipulating the remote// process into calling the LoadLibrary function, we// have our own emulation of what LoadLibrary does// without all those annoying detectability issues ^^.// The advantage of this method over using something // like my CloakDll function, is that this method never // has to call a function like LoadLibrary inside the // remote process. Since LoadLibrary can be hooked, // the dll could still be caught at the injection stage. // Or possibly also through the weakness I discussed in // the comment header of that file, which is not present// when using this technique.#include <windows.h>#include <tlhelp32.h>#include <shlwapi.h>#include <vector>#include "cModuleLoader.h"// Pietrek's macro//// MakePtr is a macro that allows you to easily add to values (including// pointers) together without dealing with C's pointer arithmetic. It// essentially treats the last two parameters as DWORDs. The first// parameter is used to typecast the result to the appropriate pointer type.#define MakePtr( cast, ptr, addValue ) (cast)( (DWORD_PTR)(ptr) + (DWORD_PTR)(addValue))// This one is mine (Darawk), but obviously..."adapted" from matt's original idea =p#define MakeDelta(cast, x, y) (cast) ( (DWORD_PTR)(x) - (DWORD_PTR)(y))// Stub that calls the Dll from within the remote process.// This is necessary because a DllMain function takes 3// arguments, and CreateRemoteThread can pass only 1.#define DLL_BASEADDR_OFFSET 9// This wont work in debug mode!__declspec( naked )void DllCall_stub( HMODULE hMod ){ _asm {// int 3; push 0 push 1 push [esp+0Ch] // Pointer to the hMod argument mov eax, 0xDEADBEEF // Patch this in with the real value at run-time call eax // MSVC++ doesn't like direct absolute calls, so we have to be // clever about it. ret // Don't have to clean up the stack because the calling function // is just going to call ExitThread() immediately after this // function returns. }}//unsigned char *DllCall_stub = (unsigned char * )"\x6A\x00\x6A\x01\xFF\x74\x24\x0C\xB8\xEF\xBE\xAD\xDE\xFF\xD0\xC3";// Marker for the end of the DllCall_stub function__declspec(naked) void DC_stubend(void) { } std::vector<MODULE*> cModuleLoader::mappedMods;//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////cModuleLoader::cModuleLoader( unsigned long processId, char *pszmoduleName ){ m_ModuleBase = NULL; m_ProcessId = processId; strncpy( m_szModuleName, pszmoduleName, sizeof(m_szModuleName) ); InitFileMapping( pszmoduleName );}cModuleLoader::cModuleLoader( unsigned long processId, unsigned char *pbModule ){ m_ModuleBase = NULL; m_ProcessId = processId; strncpy( m_szModuleName, "(NULL)", sizeof(m_szModuleName) ); InitMemoryMapping( pbModule );}cModuleLoader::~cModuleLoader(){}HMODULE cModuleLoader::GetModuleHandle(){ int somecrap = 123; somecrap+=123; DWORD moduleBase = PtrToUlong( m_ModuleBase ); return( (HMODULE)m_ModuleBase );}char * cModuleLoader::GetModuleName(){ return( m_szModuleName );}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////void cModuleLoader::InitFileMapping( char *pszmoduleName ){ HANDLE hFile = OpenModule( pszmoduleName ); unsigned int fileSize = 0; unsigned long nBytesRead = 0; if( hFile == INVALID_HANDLE_VALUE ) { return; } fileSize = GetModuleSize( hFile, pszmoduleName ); if( fileSize == INVALID_FILE_SIZE ) { return; } m_pbRawDll = new unsigned char[ fileSize ]; ReadFile( hFile, m_pbRawDll, fileSize, &nBytesRead, FALSE ); CloseHandle( hFile );}void cModuleLoader::InitMemoryMapping( unsigned char *pbModule ){ m_pbRawDll = pbModule;}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////HMODULE cModuleLoader::LoadDll(){ PIMAGE_DOS_HEADER pDOSHeader = NULL; PIMAGE_NT_HEADERS pNTHeader = NULL; DWORD dwCallStubSize = 0; DWORD dwPageAddress = 0; // Enough to be safe #if defined( _DEBUG ) dwCallStubSize = 0x20;#else dwCallStubSize = 0x20;// dwCallStubSize = MakeDelta(SIZE_T, DC_stubend, DllCall_stub);#endif if( !GetPEHeaders( (DWORD*)m_pbRawDll, pDOSHeader, pNTHeader ) ) { delete[]m_pbRawDll; return( NULL ); } dwPageAddress = pNTHeader->OptionalHeader.ImageBase; HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, m_ProcessId ); if( hProcess == NULL ) { delete[]m_pbRawDll; return( NULL ); } m_ModuleBase = VirtualAllocEx( hProcess, (LPVOID)dwPageAddress, pNTHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE ); if( m_ModuleBase == NULL ) { return( NULL ); } m_StubBase = VirtualAllocEx( hProcess, NULL, dwCallStubSize, // THIS WILL BREAK IN DEBUG MODE!! MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE ); ////////////////////////////////////////////////////////////////////////// // Fix up the import table of the new module ////////////////////////////////////////////////////////////////////////// PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)GetPtrFromRVA( (DWORD)( pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress ), pNTHeader, m_pbRawDll ); if( pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size ) { FixImports( (unsigned char *)m_pbRawDll, pNTHeader, pImportDesc ); } // Fix "base relocations" of the new module. Base relocations are places // in the module that use absolute addresses to reference data. Since // the base address of the module can be different at different times, // the base relocation data is necessary to make the module loadable // at any address. PIMAGE_BASE_RELOCATION pReloc = ( PIMAGE_BASE_RELOCATION )GetPtrFromRVA( (DWORD)(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress), pNTHeader, m_pbRawDll ); if( pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size ) { FixRelocs( m_pbRawDll, m_ModuleBase, pNTHeader, pReloc, pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size); } DWORD dwNumberOfBytes = 0; // Write the PE header into the remote process's memory space WriteProcessMemory( hProcess, m_ModuleBase, m_pbRawDll, pNTHeader->FileHeader.SizeOfOptionalHeader + sizeof(pNTHeader->FileHeader) + sizeof(pNTHeader->Signature), &dwNumberOfBytes ); // Map the sections into the remote process(they need to be aligned // along their virtual addresses) MapSections( hProcess, m_ModuleBase, m_pbRawDll, pNTHeader ); // Change the page protection on the DllCall_stub function from PAGE_EXECUTE_READ // to PAGE_EXECUTE_READWRITE, so we can patch it. VirtualProtect( (LPVOID)DllCall_stub, dwCallStubSize, PAGE_EXECUTE_READWRITE, &dwNumberOfBytes); // Patch the stub so it calls the correct address *MakePtr(unsigned long *, DllCall_stub, DLL_BASEADDR_OFFSET) = MakePtr(unsigned long, m_ModuleBase, pNTHeader->OptionalHeader.AddressOfEntryPoint); // Write the stub into the remote process WriteProcessMemory( hProcess, m_StubBase, (LPVOID)DllCall_stub, dwCallStubSize, &dwNumberOfBytes ); // Execute our stub in the remote process CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)m_StubBase, m_ModuleBase, // Pass the base address of the module as the argument to the stub. // All a module handle is, is the base address of the module(except // in windows CE), so we're really passing a handle to the module // so that it can refer to itself, create dialogs, etc.. 0, NULL); // Cleanup after ourselves VirtualFreeEx( hProcess, m_StubBase, dwCallStubSize, MEM_DECOMMIT | MEM_RELEASE ); CloseHandle( hProcess ); delete[] m_pbRawDll; return (HMODULE)m_ModuleBase;}HMODULE cModuleLoader::LoadLibraryDll( char *pszmoduleName ){ PTHREAD_START_ROUTINE pfnThread; HANDLE hThread; LPSTR pszRemotePath = NULL; HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, m_ProcessId ); // Allocate some memory for the string pszRemotePath = ( LPSTR )VirtualAllocEx( hProcess, NULL, 255, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); // Write the file path into the memory WriteProcessMemory( hProcess, pszRemotePath, pszmoduleName, lstrlen( pszmoduleName ), 0 ); // GetProcAddress of LoadLibrary pfnThread = ( PTHREAD_START_ROUTINE )GetProcAddress( LoadLibrary( "kernel32.dll"), "LoadLibraryA" ); // push LoadLibary as the func to call with "path.to.dll" as the params. hThread = CreateRemoteThread( hProcess, NULL, 0, pfnThread, pszRemotePath, 0, NULL ); // Wait for exit WaitForSingleObject( hThread, INFINITE ); DWORD dwLibBase = 0; if (!GetExitCodeThread( hThread, &dwLibBase ) ) { VirtualFreeEx( hProcess, pszRemotePath, 255, MEM_DECOMMIT ); return( NULL ); } CloseHandle( hThread ); VirtualFreeEx( hProcess, pszRemotePath, 255, MEM_DECOMMIT ); return( GetRemoteModuleHandle( m_ProcessId, pszmoduleName) );}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////HANDLE cModuleLoader::OpenModule( char *pszModuleName ){#define MAX_ENV_LEN 32767 char pathVar[MAX_ENV_LEN], *p, *p2; char modPath[MAX_PATH] = {0}; char c; HANDLE hFile; // If it's an absolute path that was passed to us(i.e. C:\xxx or D:\xxx) then we don't need // do scan the PATH folders. if(!strchr(pszModuleName, ':')) { GetEnvironmentVariable("PATH", pathVar, MAX_ENV_LEN); p = pathVar; while(p2 = strchr(p, ';')) { memset(modPath, 0, sizeof(modPath)); c = *p2; *p2 = '\0'; strncpy(modPath, p, MAX_PATH); strcat(modPath, "\\"); strcat(modPath, pszModuleName); p = ++p2; hFile = CreateFile(modPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile && hFile != INVALID_HANDLE_VALUE) return hFile; } } hFile = CreateFile(pszModuleName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); return hFile;}unsigned int cModuleLoader::GetModuleSize( HANDLE hFile, char *pszModuleName ){ unsigned int fSize = 0;// if( GetFileAttributes(pszModuleName) & FILE_ATTRIBUTE_COMPRESSED ) {// fSize = GetCompressedFileSize( pszModuleName, NULL );// } else { fSize = GetFileSize( hFile, NULL );// } return( fSize );}bool cModuleLoader::GetPEHeaders( DWORD * dllBase, PIMAGE_DOS_HEADER & pDOSHeader, PIMAGE_NT_HEADERS & pNTHeader ){ // Grab the Dos Header pDOSHeader = MakePtr( PIMAGE_DOS_HEADER, dllBase, 0 ); if( pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE ) { return( false ); } // Grab the NT Header pNTHeader = MakePtr( PIMAGE_NT_HEADERS, dllBase, pDOSHeader->e_lfanew ); if( pNTHeader->Signature != IMAGE_NT_SIGNATURE ) { return( false ); } return( true );}////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////FARPROC cModuleLoader::GetRemoteProcAddress( unsigned long pId, char *module, char *func ){ HMODULE remoteMod = GetRemoteModuleHandle( pId, module ); HMODULE localMod = ::GetModuleHandle( module ); // Account for potential differences in base address // of modules in different processes. unsigned long delta = MakeDelta( unsigned long, remoteMod, localMod ); return MakePtr( FARPROC, GetProcAddress( localMod, func ), delta );}HMODULE cModuleLoader::GetRemoteModuleHandle( unsigned long pId, char *module ){ MODULEENTRY32 modEntry; HANDLE tlh = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pId); modEntry.dwSize = sizeof(MODULEENTRY32); Module32First( tlh, &modEntry ); do { if( !stricmp( modEntry.szModule, module ) ) { return( modEntry.hModule ); } modEntry.dwSize = sizeof(MODULEENTRY32); } while( Module32Next(tlh, &modEntry) ); // If we didn't find it in the real list, check to see if it was one // of the dependencies that we just loaded. for( unsigned int i = 0; i < mappedMods.size(); i++ ) { if( !stricmp( mappedMods[i]->name, module ) ) { return mappedMods[i]->handle; } } return( NULL );}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////bool cModuleLoader::MapSections( HANDLE hProcess, void *moduleBase, void *dllBin, PIMAGE_NT_HEADERS pNTHeader ){ PIMAGE_SECTION_HEADER header = IMAGE_FIRST_SECTION(pNTHeader); unsigned int nBytes = 0; unsigned int virtualSize = 0; unsigned int n = 0; // Loop through the list of sections for( unsigned int i = 0; pNTHeader->FileHeader.NumberOfSections; i++ ) { // Once we've reached the SizeOfImage, the rest of the sections // don't need to be mapped, if there are any. if( nBytes >= pNTHeader->OptionalHeader.SizeOfImage ) { break; } WriteProcessMemory (hProcess, MakePtr(LPVOID, moduleBase, header->VirtualAddress), MakePtr(LPCVOID, dllBin, header->PointerToRawData), header->SizeOfRawData, (LPDWORD)&n ); virtualSize = header->VirtualAddress; header++; virtualSize = header->VirtualAddress - virtualSize; nBytes += virtualSize; // Set the proper page protections for this section. // This really could be skipped, but it's not that // hard to implement and it makes it more like a // real loader. VirtualProtectEx( hProcess, MakePtr(LPVOID, moduleBase, header->VirtualAddress), virtualSize, header->Characteristics & 0x00FFFFFF, NULL ); } return( true );}bool cModuleLoader::FixImports( void *base, PIMAGE_NT_HEADERS pNTHeader, PIMAGE_IMPORT_DESCRIPTOR pImportDesc ){ char *module; // Loop through all the required modules while( ( module = (char *)GetPtrFromRVA( (DWORD)(pImportDesc->Name), pNTHeader, (PBYTE)base))) { // If the library is already loaded(like kernel32.dll or ntdll.dll) LoadLibrary will // just return the handle to that module. HMODULE localMod = LoadLibrary(module); // If the module isn't loaded in the remote process, we recursively call the // module mapping code. This has the added benefit of ensuring that any of // the current module's dependencies will be just as invisible as this one. if( !(GetRemoteModuleHandle(m_ProcessId, module) ) ) { if( strstr( module, "ntdll") || strstr( module, "kernel32") ) { LoadLibraryDll( module ); } else { cModuleLoader * pModule = new cModuleLoader( m_ProcessId, module ); MODULE * mod = new MODULE; memset( mod, 0, sizeof(MODULE) ); mod->handle = pModule->LoadDll(); strncpy( mod->name, module, sizeof( mod->name ) ); // mod->handle = pModule->GetModuleHandle(); if( mod->handle == 0 ) { MessageBox( 0, mod->name, "handle == 0", MB_OK ); } mappedMods.push_back( mod ); } } // Lookup the first import thunk for this module // NOTE: It is possible this module could forward functions...which is something // that I really should handle. Maybe i'll add support for forwarded functions // a little bit later. PIMAGE_THUNK_DATA itd = (PIMAGE_THUNK_DATA)GetPtrFromRVA((DWORD)(pImportDesc->FirstThunk), pNTHeader, (PBYTE)base); while( itd->u1.AddressOfData ) { PIMAGE_IMPORT_BY_NAME iibn = NULL; iibn = ( PIMAGE_IMPORT_BY_NAME )GetPtrFromRVA((DWORD)(itd->u1.AddressOfData), pNTHeader, (PBYTE)base);// unsigned long delta = MakeDelta(unsigned long, remoteMod, localMod);// itd->u1.Function = MakePtr( DWORD, GetProcAddress( localMod, ( char * )iibn->Name ), delta ); itd->u1.Function = MakePtr(DWORD, GetRemoteProcAddress(m_ProcessId, module, (char *)iibn->Name), 0 ); itd++; } pImportDesc++; } return( true );}bool cModuleLoader::FixRelocs(void *base, void *rBase, PIMAGE_NT_HEADERS pNTHeader, PIMAGE_BASE_RELOCATION pReloc, unsigned int size ){ unsigned long ImageBase = pNTHeader->OptionalHeader.ImageBase; unsigned int nBytes = 0; unsigned long delta = MakeDelta( unsigned long, rBase, ImageBase ); while( 1 ) { unsigned long *locBase = (unsigned long *)GetPtrFromRVA( (DWORD)(pReloc->VirtualAddress), pNTHeader, (PBYTE)base ); unsigned int numRelocs = ( pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) ) / sizeof(WORD); if( nBytes >= size ) { break; } unsigned short *locData = MakePtr( unsigned short *, pReloc, sizeof(IMAGE_BASE_RELOCATION) ); for( unsigned int i = 0; i < numRelocs; i++ ) { if( ( (*locData >> 12) & IMAGE_REL_BASED_HIGHLOW ) ) { *MakePtr( unsigned long *, locBase, (*locData & 0x0FFF) ) += delta; } locData++; } nBytes += pReloc->SizeOfBlock; pReloc = (PIMAGE_BASE_RELOCATION)locData; } return( true );}////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////PIMAGE_SECTION_HEADER cModuleLoader::GetEnclosingSectionHeader(DWORD rva, PIMAGE_NT_HEADERS pNTHeader ){ PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader); unsigned int i; for ( i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++ ) { // This 3 line idiocy is because Watcom's linker actually sets the // Misc.VirtualSize field to 0. (!!! - Retards....!!!) DWORD size = section->Misc.VirtualSize; if ( 0 == size ) size = section->SizeOfRawData; // Is the RVA within this section? if ( (rva >= section->VirtualAddress) && (rva < (section->VirtualAddress + size))) return section; } return 0;}LPVOID cModuleLoader::GetPtrFromRVA( DWORD rva, PIMAGE_NT_HEADERS pNTHeader, PBYTE imageBase ){ PIMAGE_SECTION_HEADER pSectionHeader = NULL; INT delta; pSectionHeader = GetEnclosingSectionHeader( rva, pNTHeader ); if( pSectionHeader == NULL ) { return( NULL ); } delta = (INT)( pSectionHeader->VirtualAddress-pSectionHeader->PointerToRawData ); return( imageBase + rva - delta );}