From 6a793d101420ee0ea8f30daa5240e85e31075dc6 Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Fri, 27 Aug 2021 12:05:00 +0200 Subject: [PATCH] Fixed discrepancies in icon hash between YARA and retdec-fileinfo --- .../types/resource_table/resource_table.h | 17 +++ .../types/resource_table/resource_table.cpp | 116 ++++++++++++++++-- src/pelib/ResourceDirectory.cpp | 2 +- 3 files changed, 127 insertions(+), 8 deletions(-) diff --git a/include/retdec/fileformat/types/resource_table/resource_table.h b/include/retdec/fileformat/types/resource_table/resource_table.h index c74000c96..833af204b 100644 --- a/include/retdec/fileformat/types/resource_table/resource_table.h +++ b/include/retdec/fileformat/types/resource_table/resource_table.h @@ -17,6 +17,22 @@ namespace retdec { namespace fileformat { +/** + * Definition of the icon priority structure + */ + +struct IconPriorityEntry +{ + IconPriorityEntry(std::uint16_t width, std::uint16_t bitCount) + { + iconWidth = width; + iconBitCount = bitCount; + } + + std::uint16_t iconWidth; + std::uint16_t iconBitCount; +}; + /** * Table of resources */ @@ -63,6 +79,7 @@ class ResourceTable const std::string& getResourceIconhashSha256() const; const std::string& getResourceIconPerceptualAvgHash() const; const ResourceIconGroup* getPriorResourceIconGroup() const; + const ResourceIcon* getIconForIconHash() const; /// @} /// @name Iterators diff --git a/src/fileformat/types/resource_table/resource_table.cpp b/src/fileformat/types/resource_table/resource_table.cpp index 98dacf196..05886bc8a 100644 --- a/src/fileformat/types/resource_table/resource_table.cpp +++ b/src/fileformat/types/resource_table/resource_table.cpp @@ -70,6 +70,27 @@ struct VersionInfoHeader namespace retdec { namespace fileformat { +// Icon priority from YARA +static const std::vector iconPriority_YARA = +{ + {32, 32}, + {24, 32}, + {48, 32}, + {32, 8}, + {16, 32}, + {64, 32}, + {24, 8}, + {48, 8}, + {16, 8}, + {64, 8}, + {96, 32}, + {96, 8}, + {128, 32}, + {128, 8}, + {256, 32}, + {256, 8} +}; + /** * Compute icon perceptual hashes * @param icon Icon to compute the hash of @@ -368,6 +389,93 @@ const ResourceIconGroup* ResourceTable::getPriorResourceIconGroup() const return nullptr; } +/** + * Get the icon that will be used for calculation of the icon hash. + * This algorithm is supposed to be YARA-compatible + * @return Prior icon + */ +const ResourceIcon* ResourceTable::getIconForIconHash() const +{ + ResourceIconGroup * iconGroup = nullptr; + ResourceIcon * theBestIcon = nullptr; + std::size_t number_icon_ordinals = 0; + std::size_t best_icon_priority = 0xFF; + + // + // Step 1: Get the suitable icon group. YARA takes the first icon group + // YARA: Done in module "pe.c", function "pe_collect_icon_ordinals()" + // + + if(iconGroups.size()) + { + iconGroup = iconGroups[0]; + iconGroup->getNumberOfEntries(number_icon_ordinals); + } + + // + // Step 2: Parse all icons in the PE and retrieve the + // YARA: Done in module "pe.c", function "pe_collect_icon_data()" + // + + if(iconGroup && number_icon_ordinals) + { + for(ResourceIcon * icon : icons) + { + std::uint32_t icon_data_offset = icon->getOffset(); + std::uint32_t icon_data_size = icon->getSizeInFile(); + + // Skip icons with zero offset or zero size + if(icon_data_offset == 0 || icon_data_size == 0 /* || icon_data_offset > pe->data_size */) + continue; + + // Parse all icons in the group + for(std::size_t i = 0; i < number_icon_ordinals; i++) + { + std::size_t nameIdInGroup = 0; + std::size_t nameIdOfIcon = 0; + std::uint16_t iconWidth = 0; + std::uint16_t iconHeight = 0; + std::uint16_t iconBitCount = 0; + + // Skip icons that are of different ID + iconGroup->getEntryNameID(i, nameIdInGroup); + icon->getNameId(nameIdOfIcon); + if(nameIdOfIcon != nameIdInGroup /* || fits_in_pe() */) + continue; + + // Retrieve size and bit count + iconGroup->getEntryWidth(i, iconWidth); + iconGroup->getEntryHeight(i, iconHeight); + iconGroup->getEntryBitCount(i, iconBitCount); + + // YARA ignores any icons that have width != height + if(iconWidth == iconHeight) + { + for(size_t j = 0; j < iconPriority_YARA.size() && j < best_icon_priority; j++) + { + if(iconWidth == iconPriority_YARA[j].iconWidth && iconBitCount == iconPriority_YARA[j].iconBitCount) + { + best_icon_priority = j; + theBestIcon = icon; + break; + } + } + } + + // Set the current icon as the best one + if(!theBestIcon && icons.size()) + { + best_icon_priority = iconPriority_YARA.size(); + theBestIcon = icon; + } + } + } + } + + // Return whatever best icon we found + return theBestIcon; +} + /** * Get begin iterator * @return Begin iterator @@ -393,13 +501,7 @@ void ResourceTable::computeIconHashes() { std::vector iconHashBytes; - auto priorGroup = getPriorResourceIconGroup(); - if(!priorGroup) - { - return; - } - - auto priorIcon = priorGroup->getPriorIcon(); + auto priorIcon = getIconForIconHash(); if(!priorIcon) { return; diff --git a/src/pelib/ResourceDirectory.cpp b/src/pelib/ResourceDirectory.cpp index ab219f1b8..8a419d4fb 100644 --- a/src/pelib/ResourceDirectory.cpp +++ b/src/pelib/ResourceDirectory.cpp @@ -329,7 +329,7 @@ namespace PeLib // No data or invalid leaf if(entry.OffsetToData == 0 && entry.Size == 0) - return ERROR_INVALID_FILE; + return ERROR_SKIP_RESOURCE; // Be in sync with YARA if(entry.OffsetToData > sizeOfImage || entry.Size > sizeOfImage) return ERROR_NONE; if((uiRsrcRva + entry.OffsetToData) >= sizeOfImage || (uiRsrcRva + entry.OffsetToData + entry.Size) > sizeOfImage)