From 77adf1e1ae08ba77fc6bcef6f9ccde5515ead38b Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Thu, 17 Feb 2022 13:15:56 +0100 Subject: [PATCH 1/2] Fixed loading import directory that is modified by relocations --- .../file_format/pe/pe_format_parser.h | 6 +- include/retdec/pelib/ImageLoader.h | 9 +-- src/pelib/ImageLoader.cpp | 55 ++++++++++++++++--- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/include/retdec/fileformat/file_format/pe/pe_format_parser.h b/include/retdec/fileformat/file_format/pe/pe_format_parser.h index 049d3a674..943cddc48 100644 --- a/include/retdec/fileformat/file_format/pe/pe_format_parser.h +++ b/include/retdec/fileformat/file_format/pe/pe_format_parser.h @@ -343,6 +343,7 @@ class PeFormatParser const auto imageBase = peFile->imageLoader().getImageBase(); const auto bits = peFile->imageLoader().getImageBitability(); std::string importName; + std::uint64_t addressMask = (bits == 0x20) ? 0xFFFFFFFF : 0xFFFFFFFFFFFFFFFF; std::uint32_t ordinalNumber = 0; std::uint32_t patchRva = 0; std::uint16_t importHint = 0; @@ -362,7 +363,10 @@ class PeFormatParser import->setName(importName); import->setLibraryIndex(fileIndex); - import->setAddress(imageBase + patchRva); + + // Don't allow address overflow for samples with high image bases + // (342EE6CCB04AB0194275360EE6F752007B9F0CE5420203A41C8C9B5BAC7626DD) + import->setAddress((imageBase + patchRva) & addressMask); return import; } diff --git a/include/retdec/pelib/ImageLoader.h b/include/retdec/pelib/ImageLoader.h index cf2802e23..c8c575893 100644 --- a/include/retdec/pelib/ImageLoader.h +++ b/include/retdec/pelib/ImageLoader.h @@ -147,9 +147,9 @@ class ImageLoader ImageLoader(std::uint32_t loaderFlags = 0); - int Load(ByteBuffer & fileData, bool loadHeadersOnly = false); - int Load(std::istream & fs, std::streamoff fileOffset = 0, bool loadHeadersOnly = false); - int Load(const char * fileName, bool loadHeadersOnly = false); + int Load(ByteBuffer & fileData, std::uint32_t loadFlags = 0); + int Load(std::istream & fs, std::streamoff fileOffset = 0, std::uint32_t loadFlags = 0); + int Load(const char * fileName, std::uint32_t loadFlags = 0); int Save(std::ostream & fs, std::streamoff fileOffset = 0, std::uint32_t saveFlags = 0); int Save(const char * fileName, std::uint32_t saveFlags = 0); @@ -410,7 +410,7 @@ class ImageLoader int captureSectionHeaders(ByteBuffer & fileData); int saveSectionHeadersNew(std::ostream & fs, std::streamoff fileOffset); int saveSectionHeaders(std::ostream & fs, std::streamoff fileOffset); - int captureImageSections(ByteBuffer & fileData); + int captureImageSections(ByteBuffer & fileData, std::uint32_t loadFlags); int captureOptionalHeader32(std::uint8_t * fileData, std::uint8_t * filePtr, std::uint8_t * fileEnd); int captureOptionalHeader64(std::uint8_t * fileData, std::uint8_t * filePtr, std::uint8_t * fileEnd); std::uint32_t copyDataDirectories(std::uint8_t * optionalHeaderPtr, std::uint8_t * dataDirectoriesPtr, std::size_t optionalHeaderMax, std::uint32_t numberOfRvaAndSizes); @@ -436,6 +436,7 @@ class ImageLoader bool isLegacyImageArchitecture(std::uint16_t Machine); bool checkForValid64BitMachine(); bool checkForValid32BitMachine(); + bool checkForInvalidImageRange(); bool isValidMachineForCodeIntegrifyCheck(std::uint32_t Bits); bool checkForSectionTablesWithinHeader(std::uint32_t e_lfanew); bool checkForBadCodeIntegrityImages(ByteBuffer & fileData); diff --git a/src/pelib/ImageLoader.cpp b/src/pelib/ImageLoader.cpp index b1205be2f..86bad654d 100644 --- a/src/pelib/ImageLoader.cpp +++ b/src/pelib/ImageLoader.cpp @@ -912,9 +912,10 @@ PeLib::LoaderError PeLib::ImageLoader::loaderError() const //----------------------------------------------------------------------------- // Interface for loading files + int PeLib::ImageLoader::Load( ByteBuffer & fileData, - bool loadHeadersOnly) + std::uint32_t loadFlags) { int fileError; @@ -942,7 +943,7 @@ int PeLib::ImageLoader::Load( setLoaderError(LDR_ERROR_IMAGE_NON_EXECUTABLE); // Shall we map the image content? - if(loadHeadersOnly == false) + if(!(loadFlags & IoFlagHeadersOnly)) { // Large amount of memory may be allocated during loading the image to memory. // We need to handle low memory condition carefully here @@ -951,7 +952,7 @@ int PeLib::ImageLoader::Load( // If there was no detected image error, map the image as if Windows loader would do if(isImageLoadable()) { - fileError = captureImageSections(fileData); + fileError = captureImageSections(fileData, loadFlags); // If needed, also perform image load config directory check if(fileError == ERROR_NONE) @@ -959,6 +960,18 @@ int PeLib::ImageLoader::Load( if(checkImagePostMapping && checkForImageAfterMapping()) setLoaderError(LDR_ERROR_IMAGE_NON_EXECUTABLE); } + + // Fix for imaged that modify themselves via relocations + // Sample: 342EE6CCB04AB0194275360EE6F752007B9F0CE5420203A41C8C9B5BAC7626DD + // Modifies code and import directory via relocation table. + // This only works in Windows 7 or newer + if(ldrError == LDR_ERROR_NONE && checkForInvalidImageRange()) + { + // The image is gonna be relocated to address 0x10000, + // which is the first valid base address that can happen + // The relocation is done by ntdll!LdrpProtectAndRelocateImage -> ntdll!LdrRelocateImage + relocateImage(0x10000); + } } // If there was any kind of error that prevents the image from being mapped, @@ -980,7 +993,7 @@ int PeLib::ImageLoader::Load( int PeLib::ImageLoader::Load( std::istream & fs, std::streamoff fileOffset, - bool loadHeadersOnly) + std::uint32_t loadFlags) { ByteBuffer fileData; std::streampos fileSize; @@ -1032,18 +1045,18 @@ int PeLib::ImageLoader::Load( } // Call the Load interface on char buffer - return Load(fileData, loadHeadersOnly); + return Load(fileData, loadFlags); } int PeLib::ImageLoader::Load( const char * fileName, - bool loadHeadersOnly) + std::uint32_t loadFlags) { std::ifstream fs(fileName, std::ifstream::in | std::ifstream::binary); if(!fs.is_open()) return ERROR_OPENING_FILE; - return Load(fs, loadHeadersOnly); + return Load(fs, 0, loadFlags); } //----------------------------------------------------------------------------- @@ -2193,7 +2206,7 @@ int PeLib::ImageLoader::saveSectionHeaders( return saveToFile(fs, fileOffset, offsetOfHeaders, sizeOfHeaders); } -int PeLib::ImageLoader::captureImageSections(ByteBuffer & fileData) +int PeLib::ImageLoader::captureImageSections(ByteBuffer & fileData, std::uint32_t loadFlags) { std::uint32_t virtualAddress = 0; std::uint32_t sizeOfHeaders = optionalHeader.SizeOfHeaders; @@ -2224,10 +2237,13 @@ int PeLib::ImageLoader::captureImageSections(ByteBuffer & fileData) { for(auto & sectionHeader : sections) { + // If loading as image, we need to take data from its virtual address + std::uint32_t pointerToRawData = (loadFlags & IoFlagLoadAsImage) ? sectionHeader.VirtualAddress : sectionHeader.PointerToRawData; + // Capture all pages from the section if(captureImageSection(fileData, sectionHeader.VirtualAddress, sectionHeader.VirtualSize, - sectionHeader.PointerToRawData, + pointerToRawData, sectionHeader.SizeOfRawData, sectionHeader.Characteristics) == 0) { @@ -2668,6 +2684,27 @@ bool PeLib::ImageLoader::checkForValid32BitMachine() return (fileHeader.Machine == PELIB_IMAGE_FILE_MACHINE_I386); } +bool PeLib::ImageLoader::checkForInvalidImageRange() +{ + // Only do the check for 32-bit images + if(optionalHeader.Magic == PELIB_IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + std::uint64_t MmHighestUserAddress = 0x7FFEFFFF; + std::uint64_t MmHighestImageBase = MmHighestUserAddress - 0x10000; + std::uint64_t MmLowestImageBase = 0x00010000; + std::uint64_t ImageBase = optionalHeader.ImageBase; + std::uint32_t AlignedSizeOfImage = AlignToSize(optionalHeader.SizeOfImage, PELIB_PAGE_SIZE); + + // Align size of image to page size + if(ImageBase < MmLowestImageBase || ImageBase > MmHighestImageBase || (ImageBase + AlignedSizeOfImage) > MmHighestImageBase) + { + return true; + } + } + + return false; +} + bool PeLib::ImageLoader::isValidMachineForCodeIntegrifyCheck(std::uint32_t Bits) { if(Bits & 64) From cb5f30ba5169fc74382e2bcc22e2518ed0ca84dd Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Thu, 17 Feb 2022 13:18:47 +0100 Subject: [PATCH 2/2] Fixed comment --- src/pelib/ImageLoader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pelib/ImageLoader.cpp b/src/pelib/ImageLoader.cpp index 86bad654d..770920ce0 100644 --- a/src/pelib/ImageLoader.cpp +++ b/src/pelib/ImageLoader.cpp @@ -2695,7 +2695,8 @@ bool PeLib::ImageLoader::checkForInvalidImageRange() std::uint64_t ImageBase = optionalHeader.ImageBase; std::uint32_t AlignedSizeOfImage = AlignToSize(optionalHeader.SizeOfImage, PELIB_PAGE_SIZE); - // Align size of image to page size + // If any part of the image goes out of the allowed range, it's invalid + // Windows will do the same check and relocate the image if possible if(ImageBase < MmLowestImageBase || ImageBase > MmHighestImageBase || (ImageBase + AlignedSizeOfImage) > MmHighestImageBase) { return true;