diff --git a/deps/pelib/CMakeLists.txt b/deps/pelib/CMakeLists.txt index 39ff2ed47..a8bf5b4d6 100644 --- a/deps/pelib/CMakeLists.txt +++ b/deps/pelib/CMakeLists.txt @@ -8,8 +8,8 @@ if(CMAKE_CXX_COMPILER) endif() ExternalProject_Add(pelib-project - URL https://github.com/avast-tl/pelib/archive/e93eaa7c150f4608a5d02a67f5edc9e54456fe24.zip - URL_HASH SHA256=2ffd7e89451c980a1af6d24d4f6dfbb69a660b06ad5de44c481f6431e21de394 + URL https://github.com/avast-tl/pelib/archive/9ccd3943c1a0917ffdaff5ab983fa7cdbb6dc5a2.zip + URL_HASH SHA256=a502c9023a52b3f248b331ce3a12a04c1b668a1d077dc69e03ea86aa9b0543df DOWNLOAD_NAME pelib.zip CMAKE_ARGS # This does not work on MSVC, but may be useful on Linux. diff --git a/include/retdec/fileformat/file_format/file_format.h b/include/retdec/fileformat/file_format/file_format.h index bbd629307..7b4259a44 100644 --- a/include/retdec/fileformat/file_format/file_format.h +++ b/include/retdec/fileformat/file_format/file_format.h @@ -21,6 +21,20 @@ namespace retdec { namespace fileformat { +/** +* LoaderErrorInfo - common structure that contains loader error code, error message and user-friendly error message +*/ + +struct LoaderErrorInfo +{ + LoaderErrorInfo() : loaderErrorCode(0), loaderError(nullptr), loaderErrorUserFriendly(nullptr) + {} + + std::uint32_t loaderErrorCode; // Loader error code, cast to uint32_t + const char * loaderError; + const char * loaderErrorUserFriendly; +}; + /** * FileFormat - abstract class for parsing files */ @@ -67,10 +81,11 @@ class FileFormat : public retdec::utils::ByteValueStorage, private retdec::utils PdbInfo *pdbInfo; ///< information about related PDB debug file CertificateTable *certificateTable; ///< table of certificates Format fileFormat; ///< format of input file + LoaderErrorInfo _ldrErrInfo; ///< loader error (e.g. Windows loader error for PE files) bool stateIsValid; ///< internal state of instance std::vector<std::pair<std::size_t, std::size_t>> secHashInfo; ///< information for calculation of section table hash - retdec::utils::Maybe<bool> signatureVerified; ///< indicates whether the signature is present and also verified - retdec::utils::RangeContainer<std::uint64_t> nonDecodableRanges; ///< Address ranges which should not be decoded for instructions. + retdec::utils::Maybe<bool> signatureVerified; ///< indicates whether the signature is present and also verified + retdec::utils::RangeContainer<std::uint64_t> nonDecodableRanges; ///< Address ranges which should not be decoded for instructions. /// @name Clear methods /// @{ @@ -128,7 +143,9 @@ class FileFormat : public retdec::utils::ByteValueStorage, private retdec::utils virtual std::size_t getByteLength() const override; virtual std::size_t getWordLength() const override; virtual std::size_t getNumberOfNibblesInByte() const override; + /// @} + const LoaderErrorInfo & getLoaderErrorInfo() const; /// @name Detection methods /// @{ diff --git a/include/retdec/fileformat/file_format/pe/pe_format.h b/include/retdec/fileformat/file_format/pe/pe_format.h index 2e60ed8f8..cd11a5f50 100644 --- a/include/retdec/fileformat/file_format/pe/pe_format.h +++ b/include/retdec/fileformat/file_format/pe/pe_format.h @@ -44,6 +44,7 @@ class PeFormat : public FileFormat /// @name Initialization methods /// @{ + void initLoaderErrorInfo(); void initStructures(); /// @} @@ -147,6 +148,7 @@ class PeFormat : public FileFormat std::size_t getSizeOfHeapCommit() const; std::size_t getNumberOfDataDirectories() const; std::size_t getDeclaredNumberOfDataDirectories() const; + int getPeClass() const; bool isDotNet() const; bool isPackedDotNet() const; diff --git a/include/retdec/fileformat/file_format/pe/pe_template.h b/include/retdec/fileformat/file_format/pe/pe_template.h index 77137ee04..4c35a65bb 100644 --- a/include/retdec/fileformat/file_format/pe/pe_template.h +++ b/include/retdec/fileformat/file_format/pe/pe_template.h @@ -579,7 +579,7 @@ template<int bits> bool peDelayImport(const PeLib::PeHeaderT<bits> &peHeader, import.setAddress(peImageBase(peHeader) + function->address.Value); import.setLibraryIndex(fileIndex); import.invalidateOrdinalNumber(); - if(library->ordinalNumbersAreValid()) + if(library->ordinalNumbersAreValid() && function->hint != 0) { import.setOrdinalNumber(function->hint); } diff --git a/include/retdec/loader/loader/image.h b/include/retdec/loader/loader/image.h index bea68e453..7136c4dad 100644 --- a/include/retdec/loader/loader/image.h +++ b/include/retdec/loader/loader/image.h @@ -74,6 +74,7 @@ class Image : public retdec::utils::ByteValueStorage std::pair<const std::uint8_t*, std::uint64_t> getRawSegmentData(std::uint64_t address) const; const std::string& getStatusMessage() const; + const retdec::fileformat::LoaderErrorInfo & getLoaderErrorInfo() const; protected: Segment* insertSegment(std::unique_ptr<Segment> segment); diff --git a/include/retdec/utils/conversion.h b/include/retdec/utils/conversion.h index 7c3d14a5e..95fe70f39 100644 --- a/include/retdec/utils/conversion.h +++ b/include/retdec/utils/conversion.h @@ -205,16 +205,24 @@ template<typename N> void bytesToHexString(const N *data, std::size_t dataSize, size = (size == 0 || offset + size > dataSize) ? dataSize - offset : size; } - result.clear(); - result.reserve(size * 2); - std::ostringstream oStr; - - for(std::size_t i = 0; i < size; ++i) + // Sample: 4A2A008CF1AEE9BA49D8D1DAA22D8E868365ACE633823D464478239F27ED4F18 + // Tool: redec-fileinfo.exe, Debug, x64, data = image, dataSize = 0xE1BC00 + // Optimized: This code now takes 0.106 seconds to convert (measured in VS 2015 IDE) + // (down from about 40 seconds) + const char * intToHex = uppercase ? "0123456789ABCDEF" : "0123456789abcdef"; + std::size_t hexIndex = 0; + + // Reserve the necessary space for the hexa string + result.resize(size * 2); + + // Convert to hexa byte-by-byte. No reallocations + for (std::size_t i = 0; i < size; ++i, hexIndex += 2) { - byteToHexString(oStr, data[offset + i], uppercase); - } + std::uint8_t oneByte = data[offset + i]; - result = oStr.str(); + result[hexIndex + 0] = intToHex[(oneByte >> 0x04) & 0x0F]; + result[hexIndex + 1] = intToHex[(oneByte >> 0x00) & 0x0F]; + } } /** diff --git a/src/fileformat/file_format/file_format.cpp b/src/fileformat/file_format/file_format.cpp index 329d4cdcc..97b3923c5 100644 --- a/src/fileformat/file_format/file_format.cpp +++ b/src/fileformat/file_format/file_format.cpp @@ -117,7 +117,7 @@ bool isAddressFromRegion(const SecSeg *actualRegion, const SecSeg *newRegion, st * @param loadFlags Load flags */ FileFormat::FileFormat(std::istream &inputStream, LoadFlags loadFlags) : loadedBytes(&bytes), - loadFlags(loadFlags), fileStream(inputStream) + loadFlags(loadFlags), fileStream(inputStream), _ldrErrInfo() { stateIsValid = !inputStream.fail(); init(); @@ -129,7 +129,7 @@ FileFormat::FileFormat(std::istream &inputStream, LoadFlags loadFlags) : loadedB * @param loadFlags Load flags */ FileFormat::FileFormat(std::string pathToFile, LoadFlags loadFlags) : loadedBytes(&bytes), - loadFlags(loadFlags), filePath(pathToFile), fileStream(auxStream) + loadFlags(loadFlags), filePath(pathToFile), fileStream(auxStream), _ldrErrInfo() { auxStream.open(filePath, std::ifstream::binary); stateIsValid = auxStream.is_open(); @@ -700,6 +700,15 @@ std::size_t FileFormat::getNumberOfNibblesInByte() const return !getNibbleLength() ? 0 : !(getByteLength() % getNibbleLength()) ? getByteLength() / getNibbleLength() : 0; } +/** +* Get reference to the loader error info +* @return The LoaderErrorInfo structire +*/ +const LoaderErrorInfo & FileFormat::getLoaderErrorInfo() const +{ + return _ldrErrInfo; +} + /** * Find out if target architecture is x86 * @return @c true if target architecture is x86, @c false otherwise diff --git a/src/fileformat/file_format/pe/pe_format.cpp b/src/fileformat/file_format/pe/pe_format.cpp index 8f1032d99..e581a6e2d 100644 --- a/src/fileformat/file_format/pe/pe_format.cpp +++ b/src/fileformat/file_format/pe/pe_format.cpp @@ -305,6 +305,18 @@ PeFormat::~PeFormat() delete formatParser; } +/** +* Init information from PE loader +*/ +void PeFormat::initLoaderErrorInfo() +{ + PeLib::LoaderError ldrError = file->loaderError(); + + _ldrErrInfo.loaderErrorCode = static_cast<std::uint32_t>(ldrError); + _ldrErrInfo.loaderError = getLoaderErrorString(ldrError, false); + _ldrErrInfo.loaderErrorUserFriendly = getLoaderErrorString(ldrError, true); +} + /** * Init internal structures */ @@ -332,6 +344,10 @@ void PeFormat::initStructures() file->readResourceDirectory(); file->readSecurityDirectory(); file->readComHeaderDirectory(); + + // Fill-in the loader error info from PE file + initLoaderErrorInfo(); + mzHeader = file->mzHeader(); switch((peClass = getFileType(filePath))) { diff --git a/src/fileinfo/file_detector/file_detector.cpp b/src/fileinfo/file_detector/file_detector.cpp index e7f21b13f..2e59feded 100644 --- a/src/fileinfo/file_detector/file_detector.cpp +++ b/src/fileinfo/file_detector/file_detector.cpp @@ -245,8 +245,16 @@ void FileDetector::getCertificates() /** * Get loader information */ + void FileDetector::getLoaderInfo() { + // Propagate loader error no matter if the Image pointer will be created or not + auto ldrErrInfo = getFileParser()->getLoaderErrorInfo(); + if (ldrErrInfo.loaderErrorCode != 0) + { + fileInfo.setLoaderErrorInfo(ldrErrInfo); + } + std::unique_ptr<retdec::loader::Image> image = retdec::loader::createImage(fileParser); if(!image) { diff --git a/src/fileinfo/file_information/file_information.cpp b/src/fileinfo/file_information/file_information.cpp index 3422ff166..fa2749241 100644 --- a/src/fileinfo/file_information/file_information.cpp +++ b/src/fileinfo/file_information/file_information.cpp @@ -2716,6 +2716,15 @@ const std::string& FileInformation::getLoaderStatusMessage() const return loaderInfo.getStatusMessage(); } +/** +* Gets loader error message. +* @return The error message of the loader. +*/ +const retdec::fileformat::LoaderErrorInfo & FileInformation::getLoaderErrorInfo() const +{ + return loaderInfo.getLoaderErrorInfo(); +} + /** * Checks whether .NET information are used. * @return @c true if it is used, otherwise @c false/ @@ -3483,6 +3492,15 @@ void FileInformation::setLoaderStatusMessage(const std::string& statusMessage) loaderInfo.setStatusMessage(statusMessage); } +/** +* Sets loader error message. +* @param statusMessage The loader error message. +*/ +void FileInformation::setLoaderErrorInfo(const retdec::fileformat::LoaderErrorInfo & ldrErrInfo) +{ + loaderInfo.setLoaderErrorInfo(ldrErrInfo); +} + /** * Sets whether .NET information are used. * @param set @c true if used, otherwise @c false. diff --git a/src/fileinfo/file_information/file_information.h b/src/fileinfo/file_information/file_information.h index 37566562b..40892bf54 100644 --- a/src/fileinfo/file_information/file_information.h +++ b/src/fileinfo/file_information/file_information.h @@ -55,7 +55,7 @@ class FileInformation std::vector<Pattern> malwarePatterns; ///< detected malware patterns std::vector<Pattern> otherPatterns; ///< other detected patterns Strings strings; ///< detected strings - retdec::utils::Maybe<bool> signatureVerified; ///< indicates whether the signature is present and if it is verified + retdec::utils::Maybe<bool> signatureVerified; ///< indicates whether the signature is present and if it is verified DotnetInfo dotnetInfo; ///< .NET information public: retdec::cpdetect::ToolInformation toolInfo; ///< detected tools @@ -400,7 +400,8 @@ class FileInformation std::string getNumberOfLoadedSegmentsStr(std::ios_base &(* format)(std::ios_base &)) const; const LoadedSegment& getLoadedSegment(std::size_t index) const; const std::string& getLoaderStatusMessage() const; - /// @} + const retdec::fileformat::LoaderErrorInfo & getLoaderErrorInfo() const; + /// @} /// @name Getters of @a dotnetInfo /// @{ @@ -492,6 +493,7 @@ class FileInformation void setSignatureVerified(bool verified); void setLoadedBaseAddress(unsigned long long baseAddress); void setLoaderStatusMessage(const std::string& statusMessage); + void setLoaderErrorInfo(const retdec::fileformat::LoaderErrorInfo & ldrErrInfo); void setDotnetUsed(bool set); void setDotnetRuntimeVersion(std::uint64_t majorVersion, std::uint64_t minorVersion); void setDotnetMetadataHeaderAddress(std::uint64_t address); diff --git a/src/fileinfo/file_information/file_information_types/loader_info.cpp b/src/fileinfo/file_information/file_information_types/loader_info.cpp index db6926992..f00db0b66 100644 --- a/src/fileinfo/file_information/file_information_types/loader_info.cpp +++ b/src/fileinfo/file_information/file_information_types/loader_info.cpp @@ -4,6 +4,7 @@ * @copyright (c) 2017 Avast Software, licensed under the MIT license */ +#include "retdec/fileformat/file_format/file_format.h" #include "fileinfo/file_information/file_information_types/loader_info.h" #include "fileinfo/file_information/file_information_types/type_conversions.h" @@ -85,6 +86,11 @@ const std::string& LoaderInfo::getStatusMessage() const return _statusMessage; } +const retdec::fileformat::LoaderErrorInfo & LoaderInfo::getLoaderErrorInfo() const +{ + return _ldrErrInfo; +} + void LoaderInfo::setBaseAddress(unsigned long long baseAddress) { _baseAddress = baseAddress; @@ -95,6 +101,11 @@ void LoaderInfo::setStatusMessage(const std::string& statusMessage) _statusMessage = statusMessage; } +void LoaderInfo::setLoaderErrorInfo(const retdec::fileformat::LoaderErrorInfo & ldrErrInfo) +{ + _ldrErrInfo = ldrErrInfo; +} + void LoaderInfo::addLoadedSegment(const LoadedSegment& segment) { _loadedSegments.push_back(segment); diff --git a/src/fileinfo/file_information/file_information_types/loader_info.h b/src/fileinfo/file_information/file_information_types/loader_info.h index 1a7b69d1c..ce4c74de6 100644 --- a/src/fileinfo/file_information/file_information_types/loader_info.h +++ b/src/fileinfo/file_information/file_information_types/loader_info.h @@ -37,6 +37,8 @@ class LoaderInfo unsigned long long _baseAddress; std::vector<LoadedSegment> _loadedSegments; std::string _statusMessage; + retdec::fileformat::LoaderErrorInfo _ldrErrInfo; + public: LoaderInfo(); ~LoaderInfo(); @@ -48,12 +50,14 @@ class LoaderInfo unsigned long long getNumberOfLoadedSegments() const; const LoadedSegment& getLoadedSegment(unsigned long long index) const; const std::string& getStatusMessage() const; + const retdec::fileformat::LoaderErrorInfo & getLoaderErrorInfo() const; /// @} /// @name Setters /// @{ void setBaseAddress(unsigned long long baseAddress); void setStatusMessage(const std::string& statusMessage); + void setLoaderErrorInfo(const retdec::fileformat::LoaderErrorInfo & ldrErrInfo); /// @} /// @name Other methods diff --git a/src/fileinfo/file_presentation/getters/simple_getter/basic_plain_getter.cpp b/src/fileinfo/file_presentation/getters/simple_getter/basic_plain_getter.cpp index 6f04ab462..990fcad3e 100644 --- a/src/fileinfo/file_presentation/getters/simple_getter/basic_plain_getter.cpp +++ b/src/fileinfo/file_presentation/getters/simple_getter/basic_plain_getter.cpp @@ -32,6 +32,8 @@ BasicPlainGetter::~BasicPlainGetter() std::size_t BasicPlainGetter::loadInformation(std::vector<std::string> &desc, std::vector<std::string> &info) const { + const char * loaderErrorUserFriendly = fileinfo.getLoaderErrorInfo().loaderErrorUserFriendly; + desc.clear(); info.clear(); @@ -41,6 +43,11 @@ std::size_t BasicPlainGetter::loadInformation(std::vector<std::string> &desc, st desc.push_back("File format : "); desc.push_back("File class : "); desc.push_back("File type : "); + + // Save the title for loader error (if there was a loader error detected) + if(loaderErrorUserFriendly != nullptr) + desc.push_back("Loader error : "); + desc.push_back("Architecture : "); desc.push_back("Endianness : "); desc.push_back("Image base address : "); @@ -56,6 +63,11 @@ std::size_t BasicPlainGetter::loadInformation(std::vector<std::string> &desc, st info.push_back(fileinfo.getFileFormat()); info.push_back(fileinfo.getFileClass()); info.push_back(fileinfo.getFileType()); + + // Save the text loader error + if(loaderErrorUserFriendly != nullptr) + info.push_back(loaderErrorUserFriendly); + info.push_back(fileinfo.getTargetArchitecture()); info.push_back(fileinfo.getEndianness()); info.push_back(fileinfo.getImageBaseStr(hexWithPrefix)); diff --git a/src/fileinfo/file_presentation/json_presentation.cpp b/src/fileinfo/file_presentation/json_presentation.cpp index 758579dce..7b0bc4287 100644 --- a/src/fileinfo/file_presentation/json_presentation.cpp +++ b/src/fileinfo/file_presentation/json_presentation.cpp @@ -125,6 +125,26 @@ void JsonPresentation::presentErrors(Json::Value &root) const } } +/** +* Present information about Windows PE loader error +* @param root Parent node in output document +*/ +void JsonPresentation::presentLoaderError(Json::Value &root) const +{ + auto ldrErrInfo = fileinfo.getLoaderErrorInfo(); + + if (ldrErrInfo.loaderErrorCode != 0) + { + Json::Value loaderErrorNode; + + loaderErrorNode["code"] = ldrErrInfo.loaderErrorCode; + loaderErrorNode["code_text"] = ldrErrInfo.loaderError; + loaderErrorNode["description"] = ldrErrInfo.loaderErrorUserFriendly; + + root["loaderError"] = loaderErrorNode; + } +} + /** * Present information about detected compilers and packers * @param root Parent node in output document @@ -570,6 +590,7 @@ bool JsonPresentation::present() Value root, jEp; root["inputFile"] = fileinfo.getPathToFile(); presentErrors(root); + presentLoaderError(root); presentSimple(BasicJsonGetter(fileinfo), root); if(presentSimple(EntryPointJsonGetter(fileinfo), jEp)) { diff --git a/src/fileinfo/file_presentation/json_presentation.h b/src/fileinfo/file_presentation/json_presentation.h index d96d92877..2adb75979 100644 --- a/src/fileinfo/file_presentation/json_presentation.h +++ b/src/fileinfo/file_presentation/json_presentation.h @@ -23,6 +23,7 @@ class JsonPresentation : public FilePresentation /// @name Auxiliary presentation methods /// @{ void presentErrors(Json::Value &root) const; + void presentLoaderError(Json::Value &root) const; void presentCompiler(Json::Value &root) const; void presentLanguages(Json::Value &root) const; void presentRichHeader(Json::Value &root) const; diff --git a/src/loader/loader/image.cpp b/src/loader/loader/image.cpp index ef96592e4..f58449fd9 100644 --- a/src/loader/loader/image.cpp +++ b/src/loader/loader/image.cpp @@ -424,6 +424,11 @@ void Image::setStatusMessage(const std::string& message) _statusMessage = message; } +const retdec::fileformat::LoaderErrorInfo & Image::getLoaderErrorInfo() const +{ + return getFileFormat()->getLoaderErrorInfo(); +} + Segment* Image::insertSegment(std::unique_ptr<Segment> segment) { _segments.push_back(std::move(segment));