static BOOL PatchImports(char* name) { LOADED_IMAGE image; if (!MapAndLoad(name, NULL, &image, FALSE, FALSE)) { fprintf(stderr, "couldn't MapAndLoad '%s'\n", name); return FALSE; } if (image.FileHeader->OptionalHeader.NumberOfRvaAndSizes >= 2) { PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR) GetPtrFromRVA( image.FileHeader->OptionalHeader.DataDirectory[1].VirtualAddress, image.FileHeader, image.MappedAddress); while(1) { char *s; int len; // We've reached an empty IMAGE_IMPORT_DESCRIPTOR? if ((importDesc->TimeDateStamp == 0) && (importDesc->Name==0)) { break; } s = (char *) GetPtrFromRVA(importDesc->Name, image.FileHeader, image.MappedAddress); len = strlen(s); if (!strcasecmp(s, "kernel32.dll") || !strcasecmp(s, "msvcrt.dll") || !strcasecmp(s, "user32.dll") || !strcasecmp(s, "gdi32.dll") || !strcasecmp(s, "ole32.dll") || !strcasecmp(s, "advapi32.dll") || !strcasecmp(s, "comctl32.dll") || !strcasecmp(s, "ntdll.dll")) { printf("...patching %s ->", s); s[len - 5] = '_'; printf(" %s\n", s); } importDesc++; } } UnMapAndLoad(&image); return TRUE; }
bool FixRelocs(void *base, void *rBase, IMAGE_NT_HEADERS *ntHd, IMAGE_BASE_RELOCATION *reloc, unsigned int size) { unsigned long ImageBase = ntHd->OptionalHeader.ImageBase; unsigned int nBytes = 0; unsigned long delta = MakeDelta(unsigned long, rBase, ImageBase); while(1) { unsigned long *locBase = (unsigned long *)GetPtrFromRVA((DWORD)(reloc->VirtualAddress), ntHd, (PBYTE)base); unsigned int numRelocs = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); if(nBytes >= size) break; unsigned short *locData = MakePtr(unsigned short *, reloc, 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 += reloc->SizeOfBlock; reloc = (IMAGE_BASE_RELOCATION *)locData; } return true; }
// Given a process ID, file pointer, NT header, and start of .idata, fix the imports bool FixImports(unsigned long pId, void *base, IMAGE_NT_HEADERS *ntHd, IMAGE_IMPORT_DESCRIPTOR *impDesc) { char *module; // Loop through all the required modules while((module = (char *)GetPtrFromRVA((DWORD)(impDesc->Name), ntHd, (PBYTE)base))) { // If the library is already loaded(like kernel32.dll or ntdll.dll) LoadLibrary will // just return the handle to that module. // A neat alternative would be to load them all again. This will help ensure // we have a working copy for our own function calls which is hidden :O HMODULE localMod = LoadLibraryA(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 modules dependencies will be just as invisible as this one. if(!GetRemoteModuleHandle(pId, module)) MapRemoteModule(pId, module); // fix the time/date stamp impDesc->TimeDateStamp = ntHd->FileHeader.TimeDateStamp; // 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 forward functions // a little bit later. IMAGE_THUNK_DATA *itd = (IMAGE_THUNK_DATA *)GetPtrFromRVA((DWORD)(impDesc->FirstThunk), ntHd, (PBYTE)base); while(itd->u1.AddressOfData) { IMAGE_IMPORT_BY_NAME *iibn; iibn = (IMAGE_IMPORT_BY_NAME *)GetPtrFromRVA((DWORD)(itd->u1.AddressOfData), ntHd, (PBYTE)base); itd->u1.Function = MakePtr(DWORD, GetRemoteProcAddress(pId, module, (char *)iibn->Name), 0); ++itd; } ++impDesc; } return true; }
LPVOID FindProcAddressByName(LPSTR ProcName, LPVOID ImageBase, PIMAGE_EXPORT_DIRECTORY ExportDirectory) { LPVOID ProcAddress = NULL; printf("\n[*] Parsing Export Table..."); printf("\n[*] Exports Name: %s", GetPtrFromRVA(ExportDirectory->Name, ImageBase)); #ifdef _DEBUG printf("\n[?] Ordinal Base: %d", ExportDirectory->Base); #endif PDWORD AddressOfNames = (PDWORD) GetPtrFromRVA(ExportDirectory->AddressOfNames, ImageBase); PWORD AddressOfOrdinals = (PWORD) GetPtrFromRVA(ExportDirectory->AddressOfNameOrdinals, ImageBase); PDWORD AddressOfFunctions = (PDWORD) GetPtrFromRVA(ExportDirectory->AddressOfFunctions,ImageBase); for (DWORD i = 0; i < ExportDirectory->NumberOfFunctions; i++) { LPCSTR FunctionName = (LPCSTR) GetPtrFromRVA(AddressOfNames[i], ImageBase); WORD Ordinal = AddressOfOrdinals[i]; ProcAddress = GetPtrFromRVA(AddressOfFunctions[Ordinal - ExportDirectory->Base + 1], ImageBase); #ifdef _DEBUG printf("\n[?] Found Function: %s", FunctionName); printf("\n[?] Ordinal: %d, RVA: %08X", Ordinal, AddressOfFunctions[Ordinal]); printf("\n[?] Function Entry Ptr: %08X\n[?]--", ProcAddress); #endif if (strcmp(ProcName,FunctionName) == 0) { printf("\n[*] Function Found @ %08X", ProcAddress); return ProcAddress; } } printf("\n[X] Function %s is not exported."); return NULL; }
void DumpStringTable( DWORD base, PIMAGE_NT_HEADERS pNTHeader, DWORD resourceBase, PIMAGE_RESOURCE_DIRECTORY_ENTRY pStrResEntry, DWORD cStrResEntries ) { for ( unsigned i = 0; i < cStrResEntries; i++, pStrResEntry++ ) { DWORD offsetToData = GetOffsetToDataFromResEntry( base, resourceBase, pStrResEntry ); PWORD pStrEntry = (PWORD)GetPtrFromRVA( offsetToData, pNTHeader, base ); if ( !pStrEntry) break; unsigned id = (pStrResEntry->Name - 1) << 4; for ( unsigned j = 0; j < 16; j++ ) { WORD len = *pStrEntry++; if ( len ) { printf( "%-5u: ", id + j ); for ( unsigned k = 0; k < min(len, (WORD)64); k++ ) { char * s; char szBuff[20]; char c = (char)pStrEntry[k]; switch( c ) { case '\t': s = "\\t"; break; case '\r': s = "\\r"; break; case '\n': s = "\\n"; break; default: wsprintf( szBuff, "%c", isprint(c) ? c : '.' ); s=szBuff; break; } printf( s ); } printf( "\n" ); } pStrEntry += len; } } }
CPEFile::operator PIMAGE_COR20_HEADER() const { if (!m_pBase) return NULL; DWORD dwRVA; if (m_bIs64Bit) dwRVA = ((PIMAGE_NT_HEADERS64)m_pNTHeader)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress; else dwRVA = m_pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress; if (!dwRVA) { return NULL; } return ((PIMAGE_COR20_HEADER)GetPtrFromRVA(dwRVA)); }
// // Top level routine called to dump out the entire resource hierarchy // void DumpResourceSection(DWORD base, PIMAGE_NT_HEADERS pNTHeader) { DWORD resourcesRVA; PIMAGE_RESOURCE_DIRECTORY resDir; resourcesRVA = GetImgDirEntryRVA(pNTHeader, IMAGE_DIRECTORY_ENTRY_RESOURCE); if ( !resourcesRVA ) return; resDir = (PIMAGE_RESOURCE_DIRECTORY) GetPtrFromRVA( resourcesRVA, pNTHeader, base ); if ( !resDir ) return; printf("Resources (RVA: %X)\n", resourcesRVA ); DumpResourceDirectory(resDir, (DWORD)resDir, 0, 0); printf( "\n" ); if ( !fShowResources ) return; if ( cStrResEntries ) { printf( "String Table\n" ); DumpStringTable( base, pNTHeader, (DWORD)resDir, pStrResEntries, cStrResEntries ); printf( "\n" ); } if ( cDlgResEntries ) { printf( "Dialogs\n" ); DumpDialogs( base, pNTHeader, (DWORD)resDir, pDlgResEntries, cDlgResEntries ); printf( "\n" ); } }
void DumpDialogs( DWORD base, PIMAGE_NT_HEADERS pNTHeader, DWORD resourceBase, PIMAGE_RESOURCE_DIRECTORY_ENTRY pDlgResEntry, DWORD cDlgResEntries ) { for ( unsigned i = 0; i < cDlgResEntries; i++, pDlgResEntry++ ) { DWORD offsetToData = GetOffsetToDataFromResEntry( base, resourceBase, pDlgResEntry ); PDWORD pDlgStyle = (PDWORD)GetPtrFromRVA( offsetToData, pNTHeader, base ); if ( !pDlgStyle ) break; printf( " ====================\n" ); if ( HIWORD(*pDlgStyle) != 0xFFFF ) { // A regular DLGTEMPLATE DLGTEMPLATE * pDlgTemplate = ( DLGTEMPLATE * )pDlgStyle; printf( " style: %08X\n", pDlgTemplate->style ); printf( " extended style: %08X\n", pDlgTemplate->dwExtendedStyle ); printf( " controls: %u\n", pDlgTemplate->cdit ); printf( " (%u,%u) - (%u,%u)\n", pDlgTemplate->x, pDlgTemplate->y, pDlgTemplate->x + pDlgTemplate->cx, pDlgTemplate->y + pDlgTemplate->cy ); PWORD pMenu = (PWORD)(pDlgTemplate + 1); // ptr math! // // First comes the menu // if ( *pMenu ) { if ( 0xFFFF == *pMenu ) { pMenu++; printf( " ordinal menu: %u\n", *pMenu ); } else { printf( " menu: " ); while ( *pMenu ) printf( "%c", LOBYTE(*pMenu++) ); pMenu++; printf( "\n" ); } } else pMenu++; // Advance past the menu name // // Next comes the class // PWORD pClass = pMenu; if ( *pClass ) { if ( 0xFFFF == *pClass ) { pClass++; printf( " ordinal class: %u\n", *pClass ); } else { printf( " class: " ); while ( *pClass ) { printf( "%c", LOBYTE(*pClass++) ); } pClass++; printf( "\n" ); } } else pClass++; // Advance past the class name // // Finally comes the title // PWORD pTitle = pClass; if ( *pTitle ) { printf( " title: " ); while ( *pTitle ) printf( "%c", LOBYTE(*pTitle++) ); pTitle++; } else pTitle++; // Advance past the Title name printf( "\n" ); PWORD pFont = pTitle; if ( pDlgTemplate->style & DS_SETFONT ) { printf( " Font: %u point ", *pFont++ ); while ( *pFont ) printf( "%c", LOBYTE(*pFont++) ); pFont++; printf( "\n" ); } else pFont = pTitle; // DLGITEMPLATE starts on a 4 byte boundary LPDLGITEMTEMPLATE pDlgItemTemplate = (LPDLGITEMTEMPLATE)pFont; for ( unsigned i=0; i < pDlgTemplate->cdit; i++ ) { // Control item header.... pDlgItemTemplate = (DLGITEMTEMPLATE *) (((DWORD)pDlgItemTemplate+3) & ~3); printf( " style: %08X\n", pDlgItemTemplate->style ); printf( " extended style: %08X\n", pDlgItemTemplate->dwExtendedStyle ); printf( " (%u,%u) - (%u,%u)\n", pDlgItemTemplate->x, pDlgItemTemplate->y, pDlgItemTemplate->x + pDlgItemTemplate->cx, pDlgItemTemplate->y + pDlgItemTemplate->cy ); printf( " id: %u\n", pDlgItemTemplate->id ); // // Next comes the control's class name or ID // PWORD pClass = (PWORD)(pDlgItemTemplate + 1); if ( *pClass ) { if ( 0xFFFF == *pClass ) { pClass++; printf( " ordinal class: %u", *pClass++ ); } else { printf( " class: " ); while ( *pClass ) printf( "%c", LOBYTE(*pClass++) ); pClass++; printf( "\n" ); } } else pClass++; printf( "\n" ); // // next comes the title // PWORD pTitle = pClass; if ( *pTitle ) { printf( " title: " ); if ( 0xFFFF == *pTitle ) { pTitle++; printf( "%u\n", *pTitle++ ); } else { while ( *pTitle ) printf( "%c", LOBYTE(*pTitle++) ); pTitle++; printf( "\n" ); } } else pTitle++; // Advance past the Title name printf( "\n" ); PBYTE pCreationData = (PBYTE)(((DWORD)pTitle + 1) & 0xFFFFFFFE); if ( *pCreationData ) pCreationData += *pCreationData; else pCreationData++; pDlgItemTemplate = (DLGITEMTEMPLATE *)pCreationData; printf( "\n" ); } printf( "\n" ); } else { // A DLGTEMPLATEEX } printf( "\n" ); } }
//////////////////////////////////////////////////////////////////////////////////////////// // MapRemoteModuleW //////////////////////////////////////////////////////////////////////////////////////////// BOOL MapRemoteModuleW( DWORD dwProcessId, LPCWSTR lpModulePath ) { BOOL bRet = FALSE; HANDLE hFile = 0; DWORD fileSize = 0; BYTE *dllBin = 0; PIMAGE_NT_HEADERS nt_header = 0; PIMAGE_DOS_HEADER dos_header = 0; HANDLE hProcess = 0; LPVOID lpModuleBase = 0; PIMAGE_IMPORT_DESCRIPTOR pImgImpDesc = 0; PIMAGE_BASE_RELOCATION pImgBaseReloc = 0; PIMAGE_TLS_DIRECTORY pImgTlsDir = 0; __try { // Get a handle for the target process. hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | // Required by Alpha PROCESS_CREATE_THREAD | // For CreateRemoteThread PROCESS_VM_OPERATION | // For VirtualAllocEx/VirtualFreeEx PROCESS_VM_WRITE | // For WriteProcessMemory PROCESS_VM_READ, FALSE, dwProcessId); if(!hProcess) { PRINT_ERROR_MSGA("Could not get handle to process (PID: 0x%X).", dwProcessId); __leave; } hFile = CreateFileW( lpModulePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { PRINT_ERROR_MSGA("CreateFileW failed."); __leave; } if(GetFileAttributesW(lpModulePath) & FILE_ATTRIBUTE_COMPRESSED) { fileSize = GetCompressedFileSizeW(lpModulePath, NULL); } else { fileSize = GetFileSize(hFile, NULL); } if(fileSize == INVALID_FILE_SIZE) { PRINT_ERROR_MSGA("Could not get size of file."); __leave; } dllBin = (BYTE*)malloc(fileSize); { DWORD NumBytesRead = 0; if(!ReadFile(hFile, dllBin, fileSize, &NumBytesRead, FALSE)) { PRINT_ERROR_MSGA("ReadFile failed."); } } dos_header = (PIMAGE_DOS_HEADER)dllBin; // Make sure we got a valid DOS header if(dos_header->e_magic != IMAGE_DOS_SIGNATURE) { PRINT_ERROR_MSGA("Invalid DOS header."); __leave; } // Get the real PE header from the DOS stub header nt_header = (PIMAGE_NT_HEADERS)( (DWORD_PTR)dllBin + dos_header->e_lfanew); // Verify the PE header if(nt_header->Signature != IMAGE_NT_SIGNATURE) { PRINT_ERROR_MSGA("Invalid PE header."); __leave; } // Allocate space for the module in the remote process lpModuleBase = VirtualAllocEx( hProcess, NULL, nt_header->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(!lpModuleBase) { PRINT_ERROR_MSGA("Could not allocate memory in remote process."); __leave; } // fix imports pImgImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)GetPtrFromRVA( nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, nt_header, (PBYTE)dllBin); if(nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) { if(!FixIAT(dwProcessId, hProcess, (PBYTE)dllBin, nt_header, pImgImpDesc)) { PRINT_ERROR_MSGA("@Fixing imports."); __leave; } } // fix relocs pImgBaseReloc = (PIMAGE_BASE_RELOCATION)GetPtrFromRVA( (DWORD)(nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress), nt_header, (PBYTE)dllBin); if(nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) { if(!FixRelocations(dllBin, lpModuleBase, nt_header, pImgBaseReloc)) { PRINT_ERROR_MSGA("@Fixing relocations."); __leave; } } // Write the PE header into the remote process's memory space { SIZE_T NumBytesWritten = 0; SIZE_T nSize = nt_header->FileHeader.SizeOfOptionalHeader + sizeof(nt_header->FileHeader) + sizeof(nt_header->Signature); if(!WriteProcessMemory(hProcess, lpModuleBase, dllBin, nSize, &NumBytesWritten) || NumBytesWritten != nSize) { PRINT_ERROR_MSGA("Could not write to memory in remote process."); __leave; } } // Map the sections into the remote process(they need to be aligned // along their virtual addresses) if(!MapSections(hProcess, lpModuleBase, dllBin, nt_header)) { PRINT_ERROR_MSGA("@Map sections."); __leave; } // call all tls callbacks // pImgTlsDir = (PIMAGE_TLS_DIRECTORY)GetPtrFromRVA( nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress, nt_header, (PBYTE)dllBin); if(nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size) { if(!CallTlsInitializers(dllBin, nt_header, hProcess, (HMODULE)lpModuleBase, DLL_PROCESS_ATTACH, pImgTlsDir)) { PRINT_ERROR_MSGA("@Call TLS initializers."); __leave; } } // call entry point if(!RemoteDllMainCall( hProcess, (LPVOID)( (DWORD_PTR)lpModuleBase + nt_header->OptionalHeader.AddressOfEntryPoint), (HMODULE)lpModuleBase, 1, 0)) { PRINT_ERROR_MSGA("@Call DllMain."); __leave; } bRet = TRUE; wprintf(L"Successfully injected (%s | PID: %x):\n\n" L" AllocationBase:\t0x%p\n" L" EntryPoint:\t\t0x%p\n" L" SizeOfImage:\t\t0x%p\n" L" CheckSum:\t\t0x%p\n", lpModulePath, dwProcessId, lpModuleBase, (DWORD_PTR)lpModuleBase + nt_header->OptionalHeader.AddressOfEntryPoint, nt_header->OptionalHeader.SizeOfImage, nt_header->OptionalHeader.CheckSum); } __finally { if(hFile) { CloseHandle(hFile); } if(dllBin) { free(dllBin); } if(hProcess) { CloseHandle(hProcess); } } return bRet; }
bool MapRemoteModule(unsigned long pId, char *module) { IMAGE_DOS_HEADER *dosHd; IMAGE_NT_HEADERS *ntHd; HANDLE hFile = CreateFile(module, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) return false; unsigned int fSize; if(GetFileAttributes(module) & FILE_ATTRIBUTE_COMPRESSED) fSize = GetCompressedFileSize(module, NULL); else fSize = GetFileSize(hFile, NULL); unsigned char *dllBin = new unsigned char[fSize]; unsigned int nBytes; ReadFile(hFile, dllBin, fSize, (LPDWORD)&nBytes, FALSE); CloseHandle(hFile); // Every PE file contains a little DOS stub for backwards compatibility // it's only real relevance is that it contains a pointer to the actual // PE header. dosHd = MakePtr(IMAGE_DOS_HEADER *, dllBin, 0); // Make sure we got a valid DOS header if(dosHd->e_magic != IMAGE_DOS_SIGNATURE) { delete dllBin; printf("invalid dos header\n"); return false; } // Get the real PE header from the DOS stub header ntHd = MakePtr(IMAGE_NT_HEADERS *, dllBin, dosHd->e_lfanew); // Verify the PE header if(ntHd->Signature != IMAGE_NT_SIGNATURE) { delete dllBin; printf("invalid nt header\n"); return false; } HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pId); if(!hProcess) { printf("open process failed\n"); return false; } // Allocate space for the module in the remote process void *moduleBase = VirtualAllocEx(hProcess, NULL, ntHd->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // Make sure we got the memory space we wanted if(!moduleBase) { printf("virtual alloc failed (moduleBase)\n"); return false; } // Allocate space for our stub void *stubBase = VirtualAllocEx(hProcess, NULL, MakeDelta(SIZE_T, DC_stubend, DllCall_stub), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // Make sure we got the memory space we wanted if(!stubBase) { printf("virtual alloc failed(stubBase)\n"); return false; } // Fix up the import table of the new module IMAGE_IMPORT_DESCRIPTOR *impDesc = (IMAGE_IMPORT_DESCRIPTOR *)GetPtrFromRVA( (DWORD)(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress), ntHd, (PBYTE)dllBin); if(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) FixImports(pId, (unsigned char *)dllBin, ntHd, impDesc); // 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. IMAGE_BASE_RELOCATION *reloc = (IMAGE_BASE_RELOCATION *)GetPtrFromRVA( (DWORD)(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress), ntHd, (PBYTE)dllBin); if(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) FixRelocs(dllBin, moduleBase, ntHd, reloc, ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size); // Write the PE header into the remote process's memory space WriteProcessMemory(hProcess, moduleBase, dllBin, ntHd->FileHeader.SizeOfOptionalHeader + sizeof(ntHd->FileHeader) + sizeof(ntHd->Signature), (SIZE_T *)&nBytes); // Map the sections into the remote process(they need to be aligned // along their virtual addresses) MapSections(hProcess, moduleBase, dllBin, ntHd); // 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, MakeDelta(SIZE_T, DC_stubend, DllCall_stub), PAGE_EXECUTE_READWRITE, (DWORD *)&nBytes); // Patch the stub so it calls the correct address *MakePtr(unsigned long *, DllCall_stub, 9) = MakePtr(unsigned long, moduleBase, ntHd->OptionalHeader.AddressOfEntryPoint); // Write the stub into the remote process WriteProcessMemory(hProcess, stubBase, (LPVOID)DllCall_stub, MakeDelta(SIZE_T, DC_stubend, DllCall_stub), (SIZE_T *)&nBytes); // Execute our stub in the remote process CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)stubBase, 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); delete dllBin; return true; }
bool MapRemoteModule(unsigned long pId, const char *module) { IMAGE_DOS_HEADER *dosHd; IMAGE_NT_HEADERS *ntHd; IMAGE_NT_HEADERS64 *ntHd64 = NULL; HANDLE hFile = CreateFileA(module, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) return false; unsigned int fSize; if(GetFileAttributesA(module) & FILE_ATTRIBUTE_COMPRESSED) fSize = GetCompressedFileSizeA(module, NULL); else fSize = GetFileSize(hFile, NULL); unsigned char *dllBin = new unsigned char[fSize]; unsigned int nBytes; // copy into our dllBin buffer the entire DLL ReadFile(hFile, dllBin, fSize, (LPDWORD)&nBytes, FALSE); CloseHandle(hFile); // Every PE file contains a little DOS stub for backwards compatibility // its only real relevance is that it contains a pointer to the actual // PE header. dosHd = MakePtr(IMAGE_DOS_HEADER *, dllBin, 0); // Make sure we got a valid DOS header if(dosHd->e_magic != IMAGE_DOS_SIGNATURE) { wxMessageBox( _("The DOS header was invalid"), _("Error: Invalid DOS header"), wxICON_ERROR ); delete [] dllBin; return false; } // Get the real PE header from the DOS stub header // This header contains pointers to the Optional Header and the // COFF File Header. ntHd = MakePtr(IMAGE_NT_HEADERS *, dllBin, dosHd->e_lfanew); // Verify that the PE header is PE00 if(ntHd->Signature != IMAGE_NT_SIGNATURE) { wxMessageBox( _("The PE Header was not found."), _("Error: PE Header not found"), wxICON_ERROR ); delete [] dllBin; return false; } // Verify that the image file is a DLL if( (ntHd->FileHeader.Characteristics & IMAGE_FILE_DLL) == false ) { wxMessageBox( _("You may only inject DLL image files."), _("Error: Injected image file was not a DLL"), wxICON_ERROR ); delete [] dllBin; return false; } // Open the target process HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pId); // We failed to open the process, so return false if(!hProcess) { wxMessageBox( _("Failed to open target process."), _("Error: Failed to open process"), wxICON_ERROR ); delete [] dllBin; return false; } // Determine whether the image is a PE32 or PE32+ executable if( ntHd->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC ) { if( ntHd->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC ) { ntHd64 = (IMAGE_NT_HEADERS64 *)ntHd; wxMessageBox( _("Image is a PE32+ executable.\nInjector Gadget doesn't support PE32+ executables yet."), _("Error: Image is not a PE32 executable"), wxICON_ERROR ); } else if( ntHd->OptionalHeader.Magic == IMAGE_ROM_OPTIONAL_HDR_MAGIC ) { wxMessageBox( _("Image is a ROM image.\nInjector Gadget doesn't support ROM images."), _("Error: Image is not a PE32 executable"), wxICON_ERROR ); } delete [] dllBin; return false; } // Allocate space for the module in the remote process void *imageBase = VirtualAllocEx(hProcess, NULL, ntHd->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // Make sure we got the memory space we wanted if(!imageBase) { delete [] dllBin; return false; } // Allocate space for our stub void *stubBase = VirtualAllocEx(hProcess, NULL, MakeDelta(SIZE_T, DC_stubend, DllCall_stub), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // Make sure we got the memory space we wanted if(!stubBase) { delete [] dllBin; return false; } // We now need to fix up the tables. // The tables are as follows (in order): // Export Table, Import Table, Resource Table, Exception Table, // Certificate Table, Base Relocation Table, Debug, Architecture, // Global Ptr, TLS Table, Load Config Table, Bound Import, IAT, // Delay Import Descriptor, CLR Runtime Header, Reserved // First important section is the Export Table. We're going to skip this // since the whole point of cloaking a DLL is to hide its presence. // Second important section is the Import Table. We really need this. if( ntHd->OptionalHeader.NumberOfRvaAndSizes > 1 ) { IMAGE_IMPORT_DESCRIPTOR *impDesc = (IMAGE_IMPORT_DESCRIPTOR *)GetPtrFromRVA( (DWORD)(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress), ntHd, (PBYTE)dllBin); if(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) FixImports(pId, (unsigned char *)dllBin, ntHd, impDesc); else // size was 0 for the import directory entry { wxMessageBox( _("Size of import directory entry was 0"), _("Error: Import Directory size is 0"), wxICON_ERROR); delete [] dllBin; return false; } } else // IMAGE_DIRECTORY_ENTRY_IMPORT didn't exist in the data directories { wxMessageBox( _("The import table referenced an invalid index in the data directory."), _("Error: Import table could not be located"), wxICON_ERROR ); delete [] dllBin; return false; } // 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. IMAGE_BASE_RELOCATION *reloc = (IMAGE_BASE_RELOCATION *)GetPtrFromRVA( (DWORD)(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress), ntHd, (PBYTE)dllBin); if(ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) FixRelocs(dllBin, imageBase, ntHd, reloc, ntHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size); // Write the PE header into the remote process's memory space WriteProcessMemory(hProcess, imageBase, dllBin, ntHd->FileHeader.SizeOfOptionalHeader + sizeof(ntHd->FileHeader) + sizeof(ntHd->Signature), (SIZE_T *)&nBytes); // Map the sections into the remote process(they need to be aligned // along their virtual addresses) MapSections(hProcess, imageBase, dllBin, ntHd); // 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, MakeDelta(SIZE_T, DC_stubend, DllCall_stub), PAGE_EXECUTE_READWRITE, (DWORD *)&nBytes); // Patch the stub so it calls the correct address *MakePtr(unsigned long *, DllCall_stub, 9) = MakePtr(unsigned long, imageBase, ntHd->OptionalHeader.AddressOfEntryPoint); // Write the stub into the remote process WriteProcessMemory(hProcess, stubBase, (LPVOID)DllCall_stub, MakeDelta(SIZE_T, DC_stubend, DllCall_stub), (SIZE_T *)&nBytes); #ifdef __USING_DEBUGGER wxMessageBox(_("Calling CreateRemoteThread")); wxMessageBox( _("hProcess: ") + wxString::Format("%08X",hProcess) ); wxMessageBox( _("stubBase: ") + wxString::Format("%08X",stubBase) ); wxMessageBox( _("moduleBas: ") + wxString::Format("%08X",imageBase) ); #endif // Execute our stub in the remote process CreateRemoteThread(hProcess, NULL, ntHd->OptionalHeader.SizeOfStackCommit, (LPTHREAD_START_ROUTINE)stubBase, imageBase, // 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); delete dllBin; return true; }
// Подменить Импортируемые функции в модуле static bool SetHook( HMODULE Module, BOOL abExecutable ) { IMAGE_IMPORT_DESCRIPTOR* Import = 0; DWORD Size = 0; //HMODULE hExecutable = GetModuleHandle( 0 ); //if( !Module ) // Module = hExecutable; //BOOL bExecutable = abExecutable; IMAGE_DOS_HEADER* dos_header = (IMAGE_DOS_HEADER*)Module; IMAGE_NT_HEADERS* nt_header = NULL; if( dos_header->e_magic == IMAGE_DOS_SIGNATURE /*'ZM'*/ ) { nt_header = (IMAGE_NT_HEADERS*)((char*)Module + dos_header->e_lfanew); if( nt_header->Signature != 0x004550 ) return false; else { Import = (IMAGE_IMPORT_DESCRIPTOR*)((char*)Module + (DWORD)(nt_header->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]. VirtualAddress)); Size = nt_header->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size; } } else return false; // if wrong module or no import table if( Module == INVALID_HANDLE_VALUE || !Import ) return false; #ifdef _DEBUG PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(nt_header); #endif #ifdef _WIN64 _ASSERTE(sizeof(DWORD_PTR)==8); #else _ASSERTE(sizeof(DWORD_PTR)==4); #endif #ifdef _WIN64 #define TOP_SHIFT 60 #else #define TOP_SHIFT 28 #endif TODO("!!! Сохранять ORDINAL процедур !!!"); bool res = false, bHooked = false; int i; int nCount = Size / sizeof(IMAGE_IMPORT_DESCRIPTOR); //_ASSERTE(Size == (nCount * sizeof(IMAGE_IMPORT_DESCRIPTOR))); -- ровно быть не обязано for( i = 0; i < nCount; i++ ) { if (Import[i].Name == 0) break; //DebugString( ToTchar( (char*)Module + Import[i].Name ) ); //#ifdef _DEBUG char* mod_name = (char*)Module + Import[i].Name; //#endif DWORD_PTR rvaINT = Import[i].OriginalFirstThunk; DWORD_PTR rvaIAT = Import[i].FirstThunk; if ( rvaINT == 0 ) // No Characteristics field? { // Yes! Gotta have a non-zero FirstThunk field then. rvaINT = rvaIAT; if ( rvaINT == 0 ) { // No FirstThunk field? Ooops!!! _ASSERTE(rvaINT!=0); break; } } //PIMAGE_IMPORT_BY_NAME pOrdinalName = NULL, pOrdinalNameO = NULL; //PIMAGE_IMPORT_BY_NAME pOrdinalNameO = NULL; //IMAGE_IMPORT_BY_NAME** byname = (IMAGE_IMPORT_BY_NAME**)((char*)Module + rvaINT); //IMAGE_THUNK_DATA* thunk = (IMAGE_THUNK_DATA*)((char*)Module + rvaIAT); IMAGE_THUNK_DATA* thunk = (IMAGE_THUNK_DATA*)GetPtrFromRVA( rvaIAT, nt_header, (PBYTE)Module ); IMAGE_THUNK_DATA* thunkO = (IMAGE_THUNK_DATA*)GetPtrFromRVA( rvaINT, nt_header, (PBYTE)Module ); if (!thunk || !thunkO) { _ASSERTE(thunk && thunkO); continue; } int f = 0; for(f = 0 ; thunk->u1.Function; thunk++, thunkO++, f++) { //const char* pszFuncName = NULL; WORD ordinalO = -1; if (thunk->u1.Function!=thunkO->u1.Function) { PWORD pOrdinal = (PWORD)GetPtrFromRVA(thunkO->u1.Ordinal, nt_header, (PBYTE)Module); if (pOrdinal) ordinalO = *pOrdinal; //if ( IMAGE_SNAP_BY_ORDINAL(thunkO->u1.Ordinal) ) { // ordinalO = IMAGE_ORDINAL(thunkO->u1.Ordinal); // pOrdinalNameO = NULL; //} //TODO("Возможно стоит искать имя функции не только для EXE, но и для всех dll"); //if (bExecutable) { // if (!IMAGE_SNAP_BY_ORDINAL(thunkO->u1.Ordinal)) { // pOrdinalNameO = (PIMAGE_IMPORT_BY_NAME)GetPtrFromRVA(thunkO->u1.AddressOfData, nt_header, (PBYTE)Module); // BOOL lbValidPtr = !IsBadReadPtr(pOrdinalNameO, sizeof(IMAGE_IMPORT_BY_NAME)); // _ASSERTE(lbValidPtr); // if (lbValidPtr) { // lbValidPtr = !IsBadStringPtrA((LPCSTR)pOrdinalNameO->Name, 10); // _ASSERTE(lbValidPtr); // if (lbValidPtr) // pszFuncName = (LPCSTR)pOrdinalNameO->Name; // } // } //} } int j = 0; for( j = 0; Hooks[j].Name; j++ ) { //if (Hooks[j].NewAddress == (void*)thunk->u1.Function) { // res = true; // это уже захучено // break; //} //WARNING("??? сомнение в этом условии"); //if( !Hooks[j].OldAddress || (void*)thunk->u1.Function != Hooks[j].OldAddress ) //{ // if (!pszFuncName || !bExecutable) { // continue; // } else { // if (strcmp(pszFuncName, Hooks[j].Name)) // continue; // } // // OldAddress уже может отличаться от оригинального экспорта библиотеки // // Это происходит например с PeekConsoleIntputW при наличии плагина Anamorphosis // Hooks[j].ExeOldAddress = (void*)thunk->u1.Function; //} //if (Hooks[j].nOrdinal == 0 && ordinalO != (ULONGLONG)-1) // Hooks[j].nOrdinal = (DWORD)ordinalO; if (Hooks[j].nOrdinal != ordinalO || !cmpmod(mod_name,Hooks[j].DllName)) { continue; } if (Hooks[j].NewAddress == (void*)thunk->u1.Function) { res = true; // это уже захучено break; } bHooked = true; DWORD old_protect = 0; VirtualProtect( &thunk->u1.Function, sizeof( thunk->u1.Function ), PAGE_READWRITE, &old_protect ); thunk->u1.Function = (DWORD_PTR)Hooks[j].NewAddress; VirtualProtect( &thunk->u1.Function, sizeof( DWORD ), old_protect, &old_protect ); #ifdef _DEBUG if (bExecutable) Hooks[j].ReplacedInExe = TRUE; #endif //DebugString( ToTchar( Hooks[j].Name ) ); res = true; break; } } } //#ifdef _DEBUG // if (bHooked) { // wchar_t szDbg[MAX_PATH*3], szModPath[MAX_PATH*2]; szModPath[0] = 0; // GetModuleFileNameW(Module, szModPath, MAX_PATH*2); // lstrcpyW(szDbg, L" ## Hooks was set by conemu: "); // lstrcatW(szDbg, szModPath); // lstrcatW(szDbg, L"\n"); // OutputDebugStringW(szDbg); // } //#endif return res; }
BOOL FixIAT( DWORD dwProcessId, HANDLE hProcess, PBYTE imageBase, PIMAGE_NT_HEADERS pNtHeader, PIMAGE_IMPORT_DESCRIPTOR pImgImpDesc ) { BOOL bRet = FALSE; LPSTR lpModuleName = 0; HMODULE hLocalModule = 0; HMODULE hRemoteModule = 0; WCHAR modulePath[MAX_PATH + 1] = {0}; WCHAR moduleNtPath[500 + 1] = {0}; WCHAR targetProcPath[MAX_PATH + 1] = {0}; WCHAR *pch = 0; __try { //printf("Fixing Imports:\n"); // get target process path if(!GetModuleFileNameExW(hProcess, (HMODULE)0, targetProcPath, MAX_PATH)) { PRINT_ERROR_MSGA("Could not get path to target process."); __leave; } pch = wcsrchr(targetProcPath, '\\'); if(pch) { targetProcPath[ pch - targetProcPath + 1 ] = (WCHAR)0; } if(!SetDllDirectoryW(targetProcPath)) { PRINT_ERROR_MSGW(L"Could not set path to target process (%s).", targetProcPath); __leave; } while((lpModuleName = (LPSTR)GetPtrFromRVA(pImgImpDesc->Name, pNtHeader, imageBase))) { PIMAGE_THUNK_DATA itd = 0; //printf("module: %s\n", lpModuleName); // ACHTUNG: LoadLibraryEx kann eine DLL nur anhand des Namen aus einem anderen // Verzeichnis laden wie der Zielprozess! hLocalModule = LoadLibraryExA(lpModuleName, 0, DONT_RESOLVE_DLL_REFERENCES); if(!hLocalModule) { PRINT_ERROR_MSGA("Could not load module locally."); __leave; } // get full path of module if(!GetModuleFileNameW(hLocalModule, modulePath, MAX_PATH)) { PRINT_ERROR_MSGA("Could not get path to module (%s).", lpModuleName); __leave; } // get nt path if(!GetFileNameNtW(modulePath, moduleNtPath, 500)) { PRINT_ERROR_MSGA("Could not get the NT namespace path."); __leave; } // Module already in process? hRemoteModule = (HMODULE)ModuleInjectedW(hProcess, moduleNtPath); if(!hRemoteModule) { if(!InjectLibraryW(dwProcessId, modulePath)) { PRINT_ERROR_MSGW(L"Could not inject required module (%s).\n", modulePath); __leave; } hRemoteModule = (HMODULE)ModuleInjectedW(hProcess, moduleNtPath); } itd = (PIMAGE_THUNK_DATA)GetPtrFromRVA(pImgImpDesc->FirstThunk, pNtHeader, imageBase); while(itd->u1.AddressOfData) { IMAGE_IMPORT_BY_NAME *iibn = (PIMAGE_IMPORT_BY_NAME)GetPtrFromRVA(itd->u1.AddressOfData, pNtHeader, imageBase); itd->u1.Function = (DWORD_PTR)GetRemoteProcAddress(hProcess, hRemoteModule, (LPCSTR)iibn->Name); //printf("Function: %s\n", (LPCSTR)iibn->Name); itd++; } pImgImpDesc++; } bRet = TRUE; } __finally { if(hLocalModule) { FreeLibrary(hLocalModule); } } return bRet; }
BOOL FixRelocations( PBYTE dllBin, LPVOID lpModuleBase, PIMAGE_NT_HEADERS pNtHeader, PIMAGE_BASE_RELOCATION pImgBaseReloc ) { LONG_PTR delta = (DWORD_PTR)lpModuleBase - pNtHeader->OptionalHeader.ImageBase; SIZE_T relocationSize = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; WORD *pRelocData = 0; //printf("FixRelocs:\n"); // image has no relocations if(!pImgBaseReloc->SizeOfBlock) { //printf("Image has no relocations\n"); return TRUE; } do { PBYTE pRelocBase = (PBYTE)GetPtrFromRVA(pImgBaseReloc->VirtualAddress, pNtHeader, dllBin); SIZE_T numRelocations = (pImgBaseReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); SIZE_T i = 0; //printf("numRelocations: %d\n", numRelocations); pRelocData = (WORD*)( (DWORD_PTR)pImgBaseReloc + sizeof(IMAGE_BASE_RELOCATION) ); // loop over all relocation entries for(i = 0; i < numRelocations; i++, pRelocData++) { // Get reloc data BYTE RelocType = *pRelocData >> 12; WORD Offset = *pRelocData & 0xFFF; switch(RelocType) { case IMAGE_REL_BASED_ABSOLUTE: break; case IMAGE_REL_BASED_HIGHLOW: *(DWORD32*)(pRelocBase + Offset) += (DWORD32)delta; break; case IMAGE_REL_BASED_DIR64: *(DWORD64*)(pRelocBase + Offset) += delta; break; default: PRINT_ERROR_MSGA("Unsuppported relocation type."); return FALSE; } } pImgBaseReloc = (PIMAGE_BASE_RELOCATION)pRelocData; } while( *(DWORD*)pRelocData ); return TRUE; }