AddExeImport - 将硬编码的 DLL 依赖项添加到任何 EXE
这种方法可能对持久性有用——例如,如果系统在启动时已经运行了一个特定的进程(abc.exe),我们可以修改这个 EXE 以在后台自动加载我们的 DLL。
IAT表中的一个DLL入口至少需要导入一个函数。我们将再次使用序数 #1。IAT 表也将被重新定位到文件的末尾,因为现有标题中不太可能存在足够的空间来添加额外的 DLL。
我还第一次为这个项目添加了 64 位支持。该工具可以编译为 32/64 位,默认情况下它可以与两种架构一起使用。
该工具的工作原理如下:
1. 将原始 EXE 文件加载到内存中。
2. 检查 EXE 是 32 位还是 64 位 - 这很重要,因为 NT 标头的长度不同。3. 从IMAGE_DIRECTORY_ENTRY_IMPORT数据目录
中找到原始导入表位置。
4. 将导入表位置从虚拟地址转换为文件指针 - 我创建了一个函数来自动执行此操作,称为VirtualAddressToFilePtr。
5. 将原始导入表复制一份并存储在内存中。
6. 创建此导入表的克隆版本,并为我们的新 DLL 文件添加一个额外条目。
7. 创建一个空白的 EXE 输出文件。
8. 将原始 EXE 文件的内容写入新的输出文件(直到最后一节的末尾)。
9. 将新的扩展 IAT 表附加到新的 EXE 文件中(直接在最后一节之后)。
10. 更新IMAGE_DIRECTORY_ENTRY_IMPORT目录以指向新的 IAT 表。
11. 增加SizeOfRawData和最后一节的VirtualSize字段,以确保新的 IAT 表正确加载到内存中。磁盘上的节数据必须与pImageNtHeader->OptionalHeader.FileAlignment的值对齐,因此此处可能需要额外的填充。
12. 检查原始EXE中最后一个header之后是否还有剩余数据。调试符号和嵌入文件通常附加到 EXE 文件的末尾 - 这些不是 EXE 格式的一部分,因此默认情况下它们不会加载到内存中。如有必要,pImageNtHeader->FileHeader.PointerToSymbolTable值将被更新以指向正确的位置,尽管这些不太可能存在于生产软件中。
#include <stdio.h>#include <windows.h>DWORD LoadFileIntoMemory(char *pPath, BYTE **pFileData, DWORD *pdwFileSize){HANDLE hFile = NULL;DWORD dwFileSize = 0;BYTE *pFileDataBuffer = NULL;DWORD dwBytesRead = 0;// open filehFile = CreateFile(pPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if(hFile == INVALID_HANDLE_VALUE){return 1;}// calculate file sizedwFileSize = GetFileSize(hFile, NULL);// allocate bufferpFileDataBuffer = (BYTE*)malloc(dwFileSize);if(pFileDataBuffer == NULL){return 1;}// read file contentsif(ReadFile(hFile, pFileDataBuffer, dwFileSize, &dwBytesRead, NULL) == 0){return 1;}// verify byte countif(dwBytesRead != dwFileSize){return 1;}// close file handleCloseHandle(hFile);// store values*pFileData = pFileDataBuffer;*pdwFileSize = dwFileSize;return 0;}DWORD WriteToFile(char *pPath, BYTE *pFileData, DWORD dwOrigFileSize, DWORD dwNewDataFilePosition, BYTE *pNewImportDirectory, DWORD dwNewImportDirectorySize, char *pDllName, BYTE *pImportLookupTable, DWORD dwImportLookupTableSize, DWORD dwPaddingBytes){HANDLE hFile = NULL;DWORD dwBytesWritten = 0;BYTE bPaddingByte = 0;// create filehFile = CreateFile(pPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if(hFile == INVALID_HANDLE_VALUE){return 1;}// write original EXE dataif(WriteFile(hFile, (void *)pFileData, dwNewDataFilePosition, &dwBytesWritten, NULL) == 0){return 1;}// write new import directoryif(WriteFile(hFile, (void *)pNewImportDirectory, dwNewImportDirectorySize, &dwBytesWritten, NULL) == 0){return 1;}// write DLL nameif(WriteFile(hFile, (void *)pDllName, (DWORD)(strlen(pDllName) + 1), &dwBytesWritten, NULL) == 0){return 1;}// write import lookup tableif(WriteFile(hFile, (void *)pImportLookupTable, dwImportLookupTableSize, &dwBytesWritten, NULL) == 0){return 1;}// write import lookup tableif(WriteFile(hFile, (void*)pImportLookupTable, dwImportLookupTableSize, &dwBytesWritten, NULL) == 0){return 1;}// write section paddingfor(DWORD i = 0; i < dwPaddingBytes; i++){if(WriteFile(hFile, (void*)&bPaddingByte, 1, &dwBytesWritten, NULL) == 0){return 1;}}// write original appended data (debug symbols, installation data, etc)if(WriteFile(hFile, (void *)(pFileData + dwNewDataFilePosition), dwOrigFileSize - dwNewDataFilePosition, &dwBytesWritten, NULL) == 0){return 1;}// close file handleCloseHandle(hFile);return 0;}BYTE *VirtualAddressToFilePtr(BYTE *pFileData, IMAGE_NT_HEADERS32 *pImageNtHeader, DWORD dwVirtualAddress){IMAGE_SECTION_HEADER *pCurrSectionHeader = NULL;BYTE *pFilePtr = NULL;DWORD dwSectionDataLength = 0;// loop through all sectionsfor(DWORD i = 0; i < pImageNtHeader->FileHeader.NumberOfSections; i++){// get current section headerpCurrSectionHeader = (IMAGE_SECTION_HEADER *)((BYTE*)&pImageNtHeader->OptionalHeader + pImageNtHeader->FileHeader.SizeOfOptionalHeader + (i * sizeof(IMAGE_SECTION_HEADER)));if(pCurrSectionHeader->SizeOfRawData != 0){// calculate section data length (on disk)dwSectionDataLength = pCurrSectionHeader->SizeOfRawData;if(dwVirtualAddress >= pCurrSectionHeader->VirtualAddress && dwVirtualAddress < (pCurrSectionHeader->VirtualAddress + dwSectionDataLength)){pFilePtr = pFileData;pFilePtr += pCurrSectionHeader->PointerToRawData;pFilePtr += (dwVirtualAddress - pCurrSectionHeader->VirtualAddress);return pFilePtr;}}}return NULL;}int main(int argc, char *argv[]){DWORD dwFileSize = 0;BYTE *pFileData = NULL;IMAGE_DOS_HEADER *pImageDosHeader = NULL;IMAGE_NT_HEADERS32 *pImageNtHeader = NULL;IMAGE_NT_HEADERS64 *pImageNtHeader64 = NULL;IMAGE_DATA_DIRECTORY *pImageDataDirectory = NULL;char *pInputFilePath = NULL;char *pDllName = NULL;char szOutputFilePath[512];IMAGE_THUNK_DATA32 ImportLookupTable32[2];IMAGE_THUNK_DATA64 ImportLookupTable64[2];DWORD dwTotalAddedSize = 0;IMAGE_IMPORT_DESCRIPTOR *pImageImportDescriptor = NULL;BYTE *pImportBaseAddr = NULL;DWORD dwCurrImportBlockOffset = 0;IMAGE_SECTION_HEADER *pCurrSectionHeader = NULL;IMAGE_SECTION_HEADER *pLastSectionHeader = NULL;DWORD dwNewDataVirtualAddress = 0;DWORD dwModuleCount = 0;DWORD dwNewDataFilePosition = 0;IMAGE_IMPORT_DESCRIPTOR NewDllImportDescriptors[2];DWORD dwOrigImportSize = 0;DWORD dwNewImportDirectorySize = 0;BYTE *pNewImportDirectory = NULL;BYTE *pCopyImportPtr = NULL;DWORD dwFileAlignment = 0;DWORD dwPaddingBytes = 0;BYTE *pImportLookupTable = NULL;DWORD dwImportLookupTableSize = 0;printf("AddExeImport - www.x86matthew.com\n\n");if(argc != 3){printf("Usage: %s [input_exe_path] [add_dll_name]\n\n", argv[0]);return 1;}// get cmd parampInputFilePath = argv[1];pDllName = argv[2];printf("Opening EXE: '%s'...\n", pInputFilePath);// load dll into memoryif(LoadFileIntoMemory(pInputFilePath, &pFileData, &dwFileSize) != 0){printf("Error: Failed to load EXE into memory\n");return 1;}// get dos headerpImageDosHeader = (IMAGE_DOS_HEADER *)pFileData;if(pImageDosHeader->e_magic != 0x5A4D){printf("Error: Invalid EXE\n");free(pFileData);return 1;}// get nt headerpImageNtHeader = (IMAGE_NT_HEADERS32 *)(pFileData + pImageDosHeader->e_lfanew);if(pImageNtHeader->Signature != IMAGE_NT_SIGNATURE){printf("Error: Invalid EXE\n");free(pFileData);return 1;}// check exe typeif(pImageNtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64){// 64-bitprintf("64-bit EXE detected\n");pImageNtHeader64 = (IMAGE_NT_HEADERS64 *)pImageNtHeader;pImageDataDirectory = pImageNtHeader64->OptionalHeader.DataDirectory;}else if(pImageNtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386){// 32-bitprintf("32-bit EXE detected\n");pImageNtHeader64 = NULL;pImageDataDirectory = pImageNtHeader->OptionalHeader.DataDirectory;}else{printf("Error: Invalid EXE\n");free(pFileData);return 1;}// find import tablepImportBaseAddr = VirtualAddressToFilePtr(pFileData, pImageNtHeader, pImageDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);if(pImportBaseAddr == NULL){printf("Error: Invalid EXE\n");free(pFileData);return 1;}// find last section in file (this should be the last entry in the list but this is not necessarily the case)for(DWORD i = 0; i < pImageNtHeader->FileHeader.NumberOfSections; i++){// get current section headerpCurrSectionHeader = (IMAGE_SECTION_HEADER*)((BYTE*)&pImageNtHeader->OptionalHeader + pImageNtHeader->FileHeader.SizeOfOptionalHeader + (i * sizeof(IMAGE_SECTION_HEADER)));if(pLastSectionHeader == NULL){// set initial valuepLastSectionHeader = pCurrSectionHeader;}else{// check if this section is the last entry so farif(pCurrSectionHeader->PointerToRawData > pLastSectionHeader->PointerToRawData){// store current valuepLastSectionHeader = pCurrSectionHeader;}}}// ensure the last section was foundif(pLastSectionHeader == NULL){printf("Error: Invalid EXE\n");free(pFileData);return 1;}// store positions of the end of the current EXE contents (virtual address + file position)dwNewDataVirtualAddress = pLastSectionHeader->VirtualAddress + pLastSectionHeader->SizeOfRawData;dwNewDataFilePosition = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;// check if the exe already contains importsif(pImageDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size != 0){// calculate number of existing imported modulesfor(;;){pImageImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)(pImportBaseAddr + dwCurrImportBlockOffset);if(pImageImportDescriptor->Name == 0){// finishedbreak;}// increase counterdwModuleCount++;// update import block offsetdwCurrImportBlockOffset += sizeof(IMAGE_IMPORT_DESCRIPTOR);}}printf("Adding '%s' to import table...\n", pDllName);// allocate memory for new (enlarged) import tabledwOrigImportSize = dwModuleCount * sizeof(IMAGE_IMPORT_DESCRIPTOR);dwNewImportDirectorySize = dwOrigImportSize + sizeof(NewDllImportDescriptors);pNewImportDirectory = (BYTE*)malloc(dwNewImportDirectorySize);if(pNewImportDirectory == NULL){printf("Error: Failed to allocate memory\n");free(pFileData);return 1;}// set import descriptor values for new dllNewDllImportDescriptors[0].Name = dwNewDataVirtualAddress + dwNewImportDirectorySize;NewDllImportDescriptors[0].OriginalFirstThunk = NewDllImportDescriptors[0].Name + (DWORD)strlen(pDllName) + 1;NewDllImportDescriptors[0].FirstThunk = NewDllImportDescriptors[0].OriginalFirstThunk;if(pImageNtHeader64 == NULL){// 32-bitNewDllImportDescriptors[0].FirstThunk += sizeof(ImportLookupTable32);}else{// 64-bitNewDllImportDescriptors[0].FirstThunk += sizeof(ImportLookupTable64);}NewDllImportDescriptors[0].TimeDateStamp = 0;NewDllImportDescriptors[0].ForwarderChain = 0;// end of import descriptor chainNewDllImportDescriptors[1].OriginalFirstThunk = 0;NewDllImportDescriptors[1].TimeDateStamp = 0;NewDllImportDescriptors[1].ForwarderChain = 0;NewDllImportDescriptors[1].Name = 0;NewDllImportDescriptors[1].FirstThunk = 0;// copy original imports to the bufferpCopyImportPtr = pNewImportDirectory;if(dwModuleCount != 0){memcpy(pNewImportDirectory, pImportBaseAddr, dwOrigImportSize);pCopyImportPtr += dwOrigImportSize;}// append the new imported module to the end of the listmemcpy((void*)pCopyImportPtr, (void*)&NewDllImportDescriptors, sizeof(NewDllImportDescriptors));// initialise import lookup table for the new DLL (1 import - ordinal #1) - 32-bitImportLookupTable32[0].u1.Ordinal = 0x80000001;ImportLookupTable32[1].u1.Ordinal = 0;// initialise import lookup table for the new DLL (1 import - ordinal #1) - 64-bitImportLookupTable64[0].u1.Ordinal = 0x8000000000000001;ImportLookupTable64[1].u1.Ordinal = 0;// update IAT directory positionpImageDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = dwNewDataVirtualAddress;pImageDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = dwNewImportDirectorySize;// calculate total length of additional data to appenddwTotalAddedSize = dwNewImportDirectorySize;dwTotalAddedSize += (DWORD)strlen(pDllName) + 1;if(pImageNtHeader64 == NULL){// 32-bitdwTotalAddedSize += (sizeof(ImportLookupTable32) * 2);}else{// 64-bitdwTotalAddedSize += (sizeof(ImportLookupTable64) * 2);}// get file alignment valueif(pImageNtHeader64 == NULL){// 32-bitdwFileAlignment = pImageNtHeader->OptionalHeader.FileAlignment;}else{// 64-bitdwFileAlignment = pImageNtHeader64->OptionalHeader.FileAlignment;}// calculate number of bytes to pad (section data in file must be aligned)dwPaddingBytes = dwFileAlignment - (dwTotalAddedSize % dwFileAlignment);if(dwPaddingBytes == dwFileAlignment){dwPaddingBytes = 0;}dwTotalAddedSize += dwPaddingBytes;// the last section must have read/write permissions at minimum to allow the loader to store the resolved IAT valuepLastSectionHeader->Characteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;pLastSectionHeader->SizeOfRawData += dwTotalAddedSize;pLastSectionHeader->Misc.VirtualSize += dwTotalAddedSize;if(pImageNtHeader64 == NULL){// 32-bitpImageNtHeader->OptionalHeader.SizeOfImage += dwTotalAddedSize;}else{// 64-bitpImageNtHeader64->OptionalHeader.SizeOfImage += dwTotalAddedSize;}// check if debug symbols are currently stored at the end of the exeif(pImageNtHeader->FileHeader.PointerToSymbolTable == dwNewDataFilePosition){// adjust debug symbol ptrpImageNtHeader->FileHeader.PointerToSymbolTable += dwTotalAddedSize;}// get import lookup table valuesif(pImageNtHeader64 == NULL){// 32-bitpImportLookupTable = (BYTE*)&ImportLookupTable32[0];dwImportLookupTableSize = sizeof(ImportLookupTable32);}else{// 64-bitpImportLookupTable = (BYTE*)&ImportLookupTable64[0];dwImportLookupTableSize = sizeof(ImportLookupTable64);}// write new exe to filememset(szOutputFilePath, 0, sizeof(szOutputFilePath));_snprintf_s(szOutputFilePath, sizeof(szOutputFilePath) - 1, "%s_modified.exe", pInputFilePath);printf("Writing new file to '%s'...\n", szOutputFilePath);if(WriteToFile(szOutputFilePath, pFileData, dwFileSize, dwNewDataFilePosition, pNewImportDirectory, dwNewImportDirectorySize, pDllName, pImportLookupTable, dwImportLookupTableSize, dwPaddingBytes) != 0){printf("Error: Failed to write new EXE\n");free(pNewImportDirectory);free(pFileData);return 1;}printf("Finished\n");// free memoryfree(pNewImportDirectory);free(pFileData);return 0;}
侵权请私聊公众号删文
热文推荐