diff options
| author | Josh Ferrell <josh@vector35.com> | 2024-05-15 17:36:42 -0400 |
|---|---|---|
| committer | Josh Ferrell <josh@vector35.com> | 2024-05-15 17:53:54 -0400 |
| commit | c579c0f78a39af7441e3aac5d640825f2fcc50d9 (patch) | |
| tree | 9d02852dddd603d49895c5133e4b9f0c7844d90f /view/pe/peview.cpp | |
| parent | 140f7c442883f815f5cfdddd673dca03cc87004f (diff) | |
Initial parsing of PE resources
Diffstat (limited to 'view/pe/peview.cpp')
| -rw-r--r-- | view/pe/peview.cpp | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/view/pe/peview.cpp b/view/pe/peview.cpp index 7f8ff19b..a81399b6 100644 --- a/view/pe/peview.cpp +++ b/view/pe/peview.cpp @@ -1,6 +1,7 @@ #include <algorithm> #include <cstring> #include <cctype> +#include <list> #include <string.h> #include <inttypes.h> #include <iomanip> @@ -2619,6 +2620,166 @@ bool PEView::Init() DefineRelocation(m_arch, reloc, symbol, reloc.address); } + try + { + //TODO: properly name tables, entries, data entries + + PEDataDirectory dir; + // Read resource directory + if (m_dataDirs.size() > IMAGE_DIRECTORY_ENTRY_RESOURCE) + dir = m_dataDirs[IMAGE_DIRECTORY_ENTRY_RESOURCE]; + else + dir.virtualAddress = 0; + + if (dir.virtualAddress > 0) + { + // Create Resource Directory Table Type + StructureBuilder resourceDirTableBuilder; + resourceDirTableBuilder.AddMember(Type::IntegerType(4, false), "characteristics"); + resourceDirTableBuilder.AddMember(Type::IntegerType(4, false), "timeDateStamp"); + resourceDirTableBuilder.AddMember(Type::IntegerType(2, false), "majorVersion"); + resourceDirTableBuilder.AddMember(Type::IntegerType(2, false), "minorVersion"); + resourceDirTableBuilder.AddMember(Type::IntegerType(2, false), "numNameEntries"); + resourceDirTableBuilder.AddMember(Type::IntegerType(2, false), "numIdEntries"); + + Ref<Structure> resourceTableStruct = resourceDirTableBuilder.Finalize(); + Ref<Type> resourceDirTableType = Type::StructureType(resourceTableStruct); + QualifiedName resourceDirTableName = string("Resource_Directory_Table"); + string resourceDirTableTypeId = Type::GenerateAutoTypeId("pe", resourceDirTableName); + QualifiedName resourceDirTableTypeName = DefineType(resourceDirTableTypeId, resourceDirTableName, resourceDirTableType); + + // Create Resource Directory Entry Type + StructureBuilder resourceDirEntryBuilder; + resourceDirEntryBuilder.AddMember(Type::IntegerType(4, false), "id"); + resourceDirEntryBuilder.AddMember(Type::IntegerType(4, false), "offset"); + + Ref<Structure> resourceDirEntryStruct = resourceDirEntryBuilder.Finalize(); + Ref<Type> resourceDirEntryType = Type::StructureType(resourceDirEntryStruct); + QualifiedName resourceDirEntryName = string("Resource_Directory_Table_Entry"); + string resourceDirEntryTypeId = Type::GenerateAutoTypeId("pe", resourceDirEntryName); + QualifiedName resourceDirEntryTypeName = DefineType(resourceDirEntryTypeId, resourceDirEntryName, resourceDirEntryType); + + // Create Resource Data Entry Type + StructureBuilder resourceDataEntryBuilder; + resourceDataEntryBuilder.AddMember(Type::IntegerType(4, false), "dataRva"); + resourceDataEntryBuilder.AddMember(Type::IntegerType(4, false), "dataSize"); + resourceDataEntryBuilder.AddMember(Type::IntegerType(4, false), "codepage"); + resourceDataEntryBuilder.AddMember(Type::IntegerType(4, false), "reserved"); + + Ref<Structure> resourceDataEntryStruct = resourceDataEntryBuilder.Finalize(); + Ref<Type> resourceDataEntryType = Type::StructureType(resourceDataEntryStruct); + QualifiedName resourceDataEntryName = string("Resource_Data_Entry"); + string resourceDataEntryTypeId = Type::GenerateAutoTypeId("pe", resourceDataEntryName); + QualifiedName resourceDataEntryTypeName = DefineType(resourceDataEntryTypeId, resourceDataEntryName, resourceDataEntryType); + + std::list<uint64_t> tableAddrsToParse = {dir.virtualAddress}; + + uint32_t resourceDirectoryTableNum = 0; + while (!tableAddrsToParse.empty()) + { + uint64_t tableAddr = tableAddrsToParse.front(); + tableAddrsToParse.pop_front(); + // Read in next directory entry + reader.Seek(RVAToFileOffset(tableAddr)); + PEResourceDirectoryTable importDirTable; + importDirTable.characteristics = reader.Read32(); + importDirTable.timeDateStamp = reader.Read32(); + importDirTable.majorVersion = reader.Read16(); + importDirTable.minorVersion = reader.Read16(); + importDirTable.numNameEntries = reader.Read16(); + importDirTable.numIdEntries = reader.Read16(); + + DefineDataVariable(m_imageBase + tableAddr, Type::NamedType(this, resourceDirTableTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, fmt::format("__resource_directory_table_{}", resourceDirectoryTableNum), m_imageBase + tableAddr, NoBinding)); + + // All the Name entries precede all the ID entries for the table but we treat them the same + // All entries for the table are sorted in ascending order: the Name entries by case-sensitive string and the ID entries by numeric value. + // Offsets are relative to the address in the IMAGE_DIRECTORY_ENTRY_RESOURCE DataDirectory. + + // Offset value: + // High bit 0. Address of a Resource Data entry (a leaf). + // High bit 1. The lower 31 bits are the address of another resource directory table (the next level down). + + std::vector<size_t> dataEntryOffsets; + + size_t numTableEntries = importDirTable.numNameEntries + importDirTable.numIdEntries; + + if (numTableEntries > 0) + { + for (size_t entryNum = 0; entryNum < numTableEntries; entryNum++) + { + PEResourceDirectoryEntry importDirEntry; + importDirEntry.id = reader.Read32(); + importDirEntry.offset = reader.Read32(); + + if (importDirEntry.id & 0x80000000) + { + // Name entry + // First 2 bytes of name are length + + size_t nameAddr = dir.virtualAddress + (importDirEntry.id ^ 0x80000000); + + BinaryReader nameReader(GetParentView(), LittleEndian); + nameReader.Seek(RVAToFileOffset(nameAddr)); + + uint16_t nameLen = nameReader.Read16(); + // Plus 2 because it's length-prefixed + DefineDataVariable(m_imageBase + nameAddr + 2, Type::ArrayType(Type::WideCharType(2), nameLen)); + } + + if (importDirEntry.offset & 0x80000000) + { + // Lower 31 bits are address of another table + tableAddrsToParse.push_back(dir.virtualAddress + (importDirEntry.offset ^ 0x80000000)); + } + else + { + // Address of data entry + dataEntryOffsets.push_back(importDirEntry.offset); + } + } + + size_t tableEntriesStart = m_imageBase + tableAddr + sizeof(PEResourceDirectoryTable); + DefineDataVariable(tableEntriesStart, Type::ArrayType(Type::NamedType(this, resourceDirEntryTypeName), numTableEntries)); + DefineAutoSymbol(new Symbol(DataSymbol, fmt::format("__resource_directory_table_{}_entries", resourceDirectoryTableNum), tableEntriesStart, NoBinding)); + } + + for(size_t dataEntryNum = 0; dataEntryNum < dataEntryOffsets.size(); dataEntryNum++) + { + BinaryReader entryReader(GetParentView(), LittleEndian); + + size_t entryOffset = dataEntryOffsets[dataEntryNum]; + entryReader.Seek(RVAToFileOffset(dir.virtualAddress + entryOffset)); + PEResourceDataEntry dataEntry; + dataEntry.dataRva = entryReader.Read32(); + dataEntry.dataSize = entryReader.Read32(); + dataEntry.dataCodePage = entryReader.Read32(); + dataEntry.reserved = entryReader.Read32(); + + if (dataEntry.reserved != 0) + { + // Invalid entry, this needs to be 0 + continue; + } + + size_t entryAddr = m_imageBase + dir.virtualAddress + entryOffset; + + DefineDataVariable(entryAddr, Type::NamedType(this, resourceDataEntryTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, fmt::format("__resource_directory_table_{}_data_entry_{}", resourceDirectoryTableNum, dataEntryNum), entryAddr, NoBinding)); + + //TODO: properly name based on path taken to get here + DefineDataVariable(m_imageBase + dataEntry.dataRva, Type::ArrayType(Type::IntegerType(1, true), dataEntry.dataSize)); + } + + resourceDirectoryTableNum++; + } + } + } + catch (std::exception& e) + { + m_logger->LogWarn("Failed to parse resource directory: %s\n", e.what()); + } + // Add a symbol for the entry point if (m_entryPoint) DefineAutoSymbol(new Symbol(FunctionSymbol, "_start", m_imageBase + m_entryPoint)); |
