Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed Lz issue 967 #970

Merged
merged 3 commits into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/retdec/fileformat/file_format/pe/pe_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ class PeFormat : public FileFormat
bool getNumberOfRelocations(unsigned long long &relocs) const;
bool getDataDirectoryRelative(unsigned long long index, unsigned long long &relAddr, unsigned long long &size) const;
bool getDataDirectoryAbsolute(unsigned long long index, unsigned long long &absAddr, unsigned long long &size) const;
bool getComDirectoryRelative(unsigned long long &relAddr, unsigned long long &size) const;
const PeCoffSection* getPeSection(const std::string &secName) const;
const PeCoffSection* getPeSection(unsigned long long secIndex) const;
const CLRHeader* getCLRHeader() const;
Expand Down
7 changes: 7 additions & 0 deletions include/retdec/fileformat/file_format/pe/pe_format_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,13 @@ class PeFormatParser
return (relAddr != 0);
}

bool getComDirectoryRelative(unsigned long long &relAddr, unsigned long long &size) const
{
relAddr = peFile->imageLoader().getComDirRva();
size = peFile->imageLoader().getComDirSize();
return (relAddr != 0);
}

bool getDataDirectoryAbsolute(unsigned long long index, unsigned long long &absAddr, unsigned long long &size) const
{
if(getDataDirectoryRelative(index, absAddr, size))
Expand Down
20 changes: 20 additions & 0 deletions include/retdec/pelib/ImageLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,24 @@ class ImageLoader
return (optionalHeader.NumberOfRvaAndSizes > dataDirIndex) ? optionalHeader.DataDirectory[dataDirIndex].Size : 0;
}

std::uint32_t getComDirRva() const
{
// For 32-bit binaries, the COM directory is valid even if NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR
// Sample: 58b0147d7dd3cd73cb8bf8df077e244650621174f7ff788ad06fd0c1f82aac40
if(optionalHeader.Magic == PELIB_IMAGE_NT_OPTIONAL_HDR32_MAGIC)
return optionalHeader.DataDirectory[PELIB_IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress;
return getDataDirRva(PELIB_IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
}

std::uint32_t getComDirSize() const
{
// For 32-bit binaries, the COM directory is valid even if NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR
// Sample: 58b0147d7dd3cd73cb8bf8df077e244650621174f7ff788ad06fd0c1f82aac40
if(optionalHeader.Magic == PELIB_IMAGE_NT_OPTIONAL_HDR32_MAGIC)
return optionalHeader.DataDirectory[PELIB_IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size;
return getDataDirSize(PELIB_IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
}

std::uint64_t getVirtualAddressMasked(std::uint32_t rva)
{
std::uint64_t virtualAddress = getImageBase() + rva;
Expand Down Expand Up @@ -382,6 +400,7 @@ class ImageLoader
int captureImageSections(ByteBuffer & fileData);
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);

int verifyDosHeader(PELIB_IMAGE_DOS_HEADER & hdr, std::size_t fileSize);
int verifyDosHeader(std::istream & fs, std::streamoff fileOffset, std::size_t fileSize);
Expand Down Expand Up @@ -471,6 +490,7 @@ class ImageLoader
bool forceIntegrityCheckCertificate; // If true, extra check for certificate will be provided
bool checkNonLegacyDllCharacteristics; // If true, extra checks will be performed on DllCharacteristics
bool checkImagePostMapping; // If true, extra checks will be performed after the image is mapped
bool alignSingleSectionImagesToPage; // Align single-section images to page size in 64-bit windows
};

} // namespace PeLib
Expand Down
30 changes: 27 additions & 3 deletions src/fileformat/file_format/pe/pe_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1835,17 +1835,27 @@ void PeFormat::loadDotnetHeaders()
{
std::uint64_t metadataHeaderAddress = 0;

// If our file contains CLR header, then use it
// If our file contains CLR header, then use it. Note that .NET framework doesn't
// verify the OPTIONAL_HEADER::NumberOfRvaAndSizes, so we must do it the same way.
unsigned long long comHeaderAddress, comHeaderSize;
if(getDataDirectoryRelative(PELIB_IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, comHeaderAddress, comHeaderSize) && comHeaderSize)
if(getComDirectoryRelative(comHeaderAddress, comHeaderSize) && comHeaderSize)
{
clrHeader = formatParser->getClrHeader();
metadataHeaderAddress = formatParser->getImageBaseAddress() + clrHeader->getMetadataDirectoryAddress();
}
else
{
return;
}

// If not, then try to guess whether the file could possibly be .NET file based on imports and try to search for metadata header
// LZ: Don't. This will lead to the situation when totally unrelated .NET metadata will be read from the binary,
// for example from a binary embedded in resources or in overlay.
// Sample: 76360c777ac93d7fc7398b5d140c4117eb08501cac30d170f33ab260e1788e74
/*
else
{
if (importTable && importTable->getNumberOfImportsInLibraryCaseInsensitive("mscoree.dll"))
if (importTable && importTable->getImport("mscoree.dll"))
{
metadataHeaderAddress = detectPossibleMetadataHeaderAddress();
if (metadataHeaderAddress == 0)
Expand All @@ -1856,6 +1866,7 @@ void PeFormat::loadDotnetHeaders()
return;
}
}
*/

// This explicit initialization needs to be here, because clang 4.0 has bug in optimizer and it causes problem in valgrind.
std::uint64_t signature = 0;
Expand Down Expand Up @@ -3294,6 +3305,19 @@ bool PeFormat::getDataDirectoryAbsolute(unsigned long long index, unsigned long
return formatParser->getDataDirectoryAbsolute(index, absAddr, size);
}

/**
* Special for .NET data directory to correctly process data directory on 32-bit binaries
* @param relAddr Into this parameter is stored relative virtual address of directory
* @param size Into this parameter is stored size of directory
* @return @c true if index of selected directory is valid, @c false otherwise
*
* If method returns @c false, @a relAddr and @a size are left unchanged.
*/
bool PeFormat::getComDirectoryRelative(unsigned long long &relAddr, unsigned long long &size) const
{
return formatParser->getComDirectoryRelative(relAddr, size);
}

/**
* Get information about section with name @a secName
* @param secName Name of section
Expand Down
4 changes: 2 additions & 2 deletions src/pelib/ComHeaderDirectory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ namespace PeLib

int ComHeaderDirectory::read(ImageLoader & imageLoader)
{
std::uint32_t rva = imageLoader.getDataDirRva(PELIB_IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
std::uint32_t size = imageLoader.getDataDirSize(PELIB_IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
std::uint32_t rva = imageLoader.getComDirRva();
std::uint32_t size = imageLoader.getComDirSize();
std::uint32_t sizeOfImage = imageLoader.getSizeOfImage();
if(rva >= sizeOfImage || (rva + size) > sizeOfImage)
{
Expand Down
86 changes: 52 additions & 34 deletions src/pelib/ImageLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ PeLib::ImageLoader::ImageLoader(uint32_t versionInfo)

// Since build 18362, extra checks are performed on non-intel platforms
checkNonLegacyDllCharacteristics = (windowsBuildNumber >= 18362);

// Since build 21996, single-section images only contain data up to the image size
alignSingleSectionImagesToPage = !(windowsBuildNumber >= 21996);
}
}

Expand Down Expand Up @@ -879,7 +882,7 @@ int PeLib::ImageLoader::Load(
if(fileError != ERROR_NONE)
return fileError;

// Check and capture NT headers. Don't go any further than here if the NT headers were detected as bad.
// Check and capture NT headers. Don't go any fuhrter than here if the NT headers were detected as bad.
// Sample: retdec-regression-tests\tools\fileinfo\features\pe-loader-corruptions\001-pe-header-cut-001.ex_
fileError = captureNtHeaders(fileData);
if(fileError != ERROR_NONE || ldrError == LDR_ERROR_NTHEADER_OUT_OF_FILE)
Expand Down Expand Up @@ -2118,7 +2121,7 @@ int PeLib::ImageLoader::captureImageSections(ByteBuffer & fileData)
// * Windows 10: no align
// If the image is smaller than one page, it is aligned to one page
sizeOfImage = AlignToSize(sizeOfImage, ssiImageAlignment32);
if(is64BitWindows)
if(is64BitWindows && alignSingleSectionImagesToPage)
sizeOfImage = AlignToSize(sizeOfImage, PELIB_PAGE_SIZE);
if(sizeOfImage < PELIB_PAGE_SIZE)
sizeOfImage = PELIB_PAGE_SIZE;
Expand Down Expand Up @@ -2178,15 +2181,42 @@ int PeLib::ImageLoader::loadImageAsIs(ByteBuffer & fileData)
return ERROR_NONE;
}

// While copying the data directories, we take into account possible out-of-bounds
// data directory entries, as long as they fit into sizeOfOptionalHeader
// Sample: 53b13d7cfb97b5475e21717397c85376a1de2b18c2eeb6532b160fb8aa3a393d
// This sample has NumberOfRvaAndSizes set to 0x0E, but the IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR (0x0E),
// but the .NET framework (_CorExeMain) does not care about NumberOfRvaAndSizes
// and directly takes the DataDirectory without checking NumberOfRvaAndSizes
std::uint32_t PeLib::ImageLoader::copyDataDirectories(
uint8_t * optionalHeaderPtr,
uint8_t * dataDirectoriesPtr,
size_t optionalHeaderMax, // How many bytes do we have from the beginning of the optional header till the end of the file
uint32_t numberOfRvaAndSizes)
{
uint8_t * dataDirectoriesEnd = dataDirectoriesPtr + PELIB_IMAGE_NUMBEROF_DIRECTORY_ENTRIES * sizeof(PELIB_IMAGE_DATA_DIRECTORY);

// Do not leave numberOfRvaAndSizes higher than the maximum possible value
if(numberOfRvaAndSizes > PELIB_IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
numberOfRvaAndSizes = PELIB_IMAGE_NUMBEROF_DIRECTORY_ENTRIES;

// Determine the end of data directories based on file size.
// Note that the SizeOfOptionalHeader does NOT have any meaning in this
if(dataDirectoriesEnd > (optionalHeaderPtr + optionalHeaderMax))
dataDirectoriesEnd = (optionalHeaderPtr + optionalHeaderMax);

// Copy data directories, up to SizeOfOptionalHeader
if(dataDirectoriesEnd > dataDirectoriesPtr)
memcpy(optionalHeader.DataDirectory, dataDirectoriesPtr, (dataDirectoriesEnd - dataDirectoriesPtr));
return numberOfRvaAndSizes;
}

int PeLib::ImageLoader::captureOptionalHeader64(
uint8_t * fileBegin,
uint8_t * filePtr,
uint8_t * fileEnd)
{
PELIB_IMAGE_OPTIONAL_HEADER64 optionalHeader64{};
uint8_t * dataDirectoryPtr;
uint32_t sizeOfOptionalHeader = sizeof(PELIB_IMAGE_OPTIONAL_HEADER64);
uint32_t numberOfRvaAndSizes;

// Capture optional header. Note that IMAGE_FILE_HEADER::SizeOfOptionalHeader
// is not taken into account by the Windows loader - it simply assumes that the entire optional header is present
Expand Down Expand Up @@ -2230,18 +2260,10 @@ int PeLib::ImageLoader::captureOptionalHeader64(
optionalHeader.NumberOfRvaAndSizes = optionalHeader64.NumberOfRvaAndSizes;

// Copy data directories
if((numberOfRvaAndSizes = optionalHeader64.NumberOfRvaAndSizes) > PELIB_IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
numberOfRvaAndSizes = PELIB_IMAGE_NUMBEROF_DIRECTORY_ENTRIES;
memcpy(optionalHeader.DataDirectory, optionalHeader64.DataDirectory, sizeof(PELIB_IMAGE_DATA_DIRECTORY) * numberOfRvaAndSizes);

// Cut the real number of data directory entries by the file size
dataDirectoryPtr = filePtr + offsetof(PELIB_IMAGE_OPTIONAL_HEADER64, DataDirectory);
if(dataDirectoryPtr < fileEnd)
{
if((dataDirectoryPtr + numberOfRvaAndSizes * sizeof(PELIB_IMAGE_DATA_DIRECTORY)) > fileEnd)
numberOfRvaAndSizes = (fileEnd - dataDirectoryPtr + sizeof(PELIB_IMAGE_DATA_DIRECTORY) - 1) / sizeof(PELIB_IMAGE_DATA_DIRECTORY);
}
realNumberOfRvaAndSizes = numberOfRvaAndSizes;
realNumberOfRvaAndSizes = copyDataDirectories((uint8_t *)(&optionalHeader64),
(uint8_t *)(&optionalHeader64.DataDirectory[0]),
fileEnd - filePtr,
optionalHeader64.NumberOfRvaAndSizes);

// Remember the offset of the checksum field
checkSumFileOffset = (filePtr - fileBegin) + offsetof(PELIB_IMAGE_OPTIONAL_HEADER64, CheckSum);
Expand All @@ -2255,9 +2277,7 @@ int PeLib::ImageLoader::captureOptionalHeader32(
uint8_t * fileEnd)
{
PELIB_IMAGE_OPTIONAL_HEADER32 optionalHeader32{};
uint8_t * dataDirectoryPtr;
uint32_t sizeOfOptionalHeader = sizeof(PELIB_IMAGE_OPTIONAL_HEADER32);
uint32_t numberOfRvaAndSizes;

// Capture optional header. Note that IMAGE_FILE_HEADER::SizeOfOptionalHeader
// is not taken into account by the Windows loader - it simply assumes that the entire optional header is present
Expand Down Expand Up @@ -2302,18 +2322,10 @@ int PeLib::ImageLoader::captureOptionalHeader32(
optionalHeader.NumberOfRvaAndSizes = optionalHeader32.NumberOfRvaAndSizes;

// Copy data directories
if((numberOfRvaAndSizes = optionalHeader32.NumberOfRvaAndSizes) > PELIB_IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
numberOfRvaAndSizes = PELIB_IMAGE_NUMBEROF_DIRECTORY_ENTRIES;
memcpy(optionalHeader.DataDirectory, optionalHeader32.DataDirectory, sizeof(PELIB_IMAGE_DATA_DIRECTORY) * numberOfRvaAndSizes);

// Cut the real number of data directory entries by the file size
dataDirectoryPtr = filePtr + offsetof(PELIB_IMAGE_OPTIONAL_HEADER32, DataDirectory);
if(dataDirectoryPtr < fileEnd)
{
if((dataDirectoryPtr + numberOfRvaAndSizes * sizeof(PELIB_IMAGE_DATA_DIRECTORY)) > fileEnd)
numberOfRvaAndSizes = (fileEnd - dataDirectoryPtr + sizeof(PELIB_IMAGE_DATA_DIRECTORY) - 1) / sizeof(PELIB_IMAGE_DATA_DIRECTORY);
}
realNumberOfRvaAndSizes = numberOfRvaAndSizes;
realNumberOfRvaAndSizes = copyDataDirectories((uint8_t *)(&optionalHeader32),
(uint8_t *)(&optionalHeader32.DataDirectory[0]),
fileEnd - filePtr,
optionalHeader32.NumberOfRvaAndSizes);

// Remember the offset of the checksum field
checkSumFileOffset = (filePtr - fileBegin) + offsetof(PELIB_IMAGE_OPTIONAL_HEADER32, CheckSum);
Expand Down Expand Up @@ -2799,9 +2811,13 @@ bool PeLib::ImageLoader::checkForSectionTablesWithinHeader(uint32_t e_lfanew)
{
uint32_t OffsetToSectionTable = sizeof(uint32_t) + sizeof(PELIB_IMAGE_FILE_HEADER) + fileHeader.SizeOfOptionalHeader;
uint32_t NumberOfSubsections = fileHeader.NumberOfSections;
uint32_t NtHeaderSize = PELIB_PAGE_SIZE - e_lfanew;
uint32_t NtHeaderSize;

// Sample: retdec-regression-tests\features\corkami\inputs\96emptysections.ex
// Must count with more pages if the header size is greater than one page
NtHeaderSize = AlignToSize(optionalHeader.SizeOfHeaders, PELIB_PAGE_SIZE) - e_lfanew;

// If this condition is true, then the image header contains data up fo SizeofHeaders
// If this condition is true, then the image header contains data up to SizeofHeaders
// If not, the image header contains the entire page.
if((e_lfanew + OffsetToSectionTable + (NumberOfSubsections + 1) * sizeof(PELIB_IMAGE_SECTION_HEADER)) <= NtHeaderSize)
return false;
Expand Down Expand Up @@ -2886,8 +2902,10 @@ void PeLib::ImageLoader::compareWithWindowsMappedImage(
size_t mismatchOffset;
size_t rva = 0;

// Are both loaded?
if(winImageData != nullptr && isImageMappedOk())
// Check if the image was loaded by both Windows and us
// Note that in Windows 7, the image can actually be mapped at base address 0
// Sample: retdec-regression-tests\features\corkami\inputs\ibnullXP.ex
if((winImageData || imageSize) && isImageMappedOk())
{
// Check whether the image size is the same
if(imageSize != getSizeOfImageAligned())
Expand Down
4 changes: 2 additions & 2 deletions src/pelib/PeFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,8 @@ namespace PeLib

int PeFileT::readComHeaderDirectory()
{
if(m_imageLoader.getDataDirRva(PELIB_IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) &&
m_imageLoader.getDataDirSize(PELIB_IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR))
// Need to do this regardless of NumberOf
if(m_imageLoader.getComDirRva() && m_imageLoader.getComDirSize())
{
return comDir().read(m_imageLoader);
}
Expand Down