diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-06 19:20:34 -0500 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-06 19:20:34 -0500 |
| commit | 828fff839f4e4de3e9582405dfec567531000ebc (patch) | |
| tree | ee0db0607edb0c76c653078672201c85e8a96d19 /view/pe/coffview.cpp | |
| parent | 47894a1ce20090849ad304c68e6c0b889665eda3 (diff) | |
Move view modules into the API repo
Diffstat (limited to 'view/pe/coffview.cpp')
| -rw-r--r-- | view/pe/coffview.cpp | 1689 |
1 files changed, 1689 insertions, 0 deletions
diff --git a/view/pe/coffview.cpp b/view/pe/coffview.cpp new file mode 100644 index 00000000..8422a07b --- /dev/null +++ b/view/pe/coffview.cpp @@ -0,0 +1,1689 @@ +#include <algorithm> + +#include <cstring> +#include <cctype> +#include <string.h> +#include <inttypes.h> +#include <iomanip> +#include <mutex> +#include <sstream> +#include <type_traits> +#include <utility> +#include "coffview.h" + +#define STRING_READ_CHUNK_SIZE 32 + +#define DEBUG_COFF_VERBOSELY 0 + +#if DEBUG_COFF_VERBOSELY +#define DEBUG_COFF(s) s +#else +#define DEBUG_COFF(s) +#endif + +// Define an Enumeration from an EnumerationBuilder: +// Ref<Enumeration> prefixEnum +// Ref<Type> prefixEnumType +// string prefixEnumName +// string prefixEnumId +// QualifiedName prefixEnumTypeName +#define BUILD_ENUM_TYPE(prefix, name, width) \ + Ref<Enumeration> prefix ## Enum = prefix ## Builder.Finalize(); \ + Ref<Type> prefix ## EnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), prefix ## Enum, width, false); \ + string prefix ## EnumName = name; \ + string prefix ## EnumId = Type::GenerateAutoTypeId("coff", prefix ## EnumName); \ + QualifiedName prefix ## EnumTypeName = DefineType(prefix ## EnumId, prefix ## EnumName, prefix ## EnumType) +// Add member to an EnumerationBuilder: +#define ADD_ENUM_MEMBER(type, name) type ## Builder.AddMemberWithValue(#name, name) + +using namespace BinaryNinja; +using namespace std; + +// From LLVM COFF.h: +// https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/BinaryFormat/COFF.h#L37-L45 +static const char BigObjMagic[] = { + '\xc7', '\xa1', '\xba', '\xd1', '\xee', '\xba', '\xa9', '\x4b', + '\xaf', '\x20', '\xfa', '\xf6', '\x6a', '\xa4', '\xdc', '\xb8', +}; + +static const char ClGlObjMagic[] = { + '\x38', '\xfe', '\xb3', '\x0c', '\xa5', '\xd9', '\xab', '\x4d', + '\xac', '\x9b', '\xd6', '\xb6', '\x22', '\x26', '\x53', '\xc2', +}; + +static COFFViewType* g_coffViewType = nullptr; + +void BinaryNinja::InitCOFFViewType() +{ + static COFFViewType type; + BinaryViewType::Register(&type); + g_coffViewType = &type; +} + +COFFView::COFFView(BinaryView* data, bool parseOnly): BinaryView("COFF", data->GetFile(), data), m_parseOnly(parseOnly) +{ +} + +bool COFFView::Init() +{ + std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now(); + map<string, size_t> usedSectionNames; + + BinaryReader reader(GetParentView(), LittleEndian); + uint64_t entryPointAddress; + Ref<Platform> platform; + + Ref<Settings> settings; + COFFHeader header; + BigObj_COFFHeader header2; + + memset(&header, 0, sizeof(header)); + memset(&header2, 0, sizeof(header2)); + size_t sectionIndexSize = sizeof(header.sectionCount); + size_t sizeofCOFFSymbol = sizeof(COFFSymbol16); + size_t sectionHeadersOffset = sizeof(COFFHeader); + uint32_t sectionCount = 0; + bool isCLRBinary = false; + bool isBigCOFF = false; + m_is64 = false; + try + { + // Read COFF header + reader.Seek(0); + header.machine = reader.Read16(); + + if (header.machine == IMAGE_FILE_MACHINE_UNKNOWN) + { + uint16_t sig2 = reader.Read16(); + if (sig2 == 0xFFFF) + { + header2.sig1 = header.machine; + header2.sig2 = sig2; + sectionIndexSize = sizeof(header2.sectionCount); + sizeofCOFFSymbol = sizeof(COFFSymbol32); + sectionHeadersOffset = sizeof(BigObj_COFFHeader); + header2.version = reader.Read16(); + header.machine = header2.machine = reader.Read16(); + header.timestamp = header2.timestamp = reader.Read32(); + reader.Read(&header2.UUID, sizeof(header2.UUID)); + if (memcmp(&header2.UUID, BigObjMagic, sizeof(BigObjMagic)) == 0) + { + LogDebug("COFF Header UUID is big object"); + } + else if (memcmp(&header2.UUID, ClGlObjMagic, sizeof(ClGlObjMagic)) == 0) + { + LogError("COFF: header UUID is CL.exe LTO object, no native code to disassemble."); + return false; + } + else + { + LogError("COFF: header UUID unknown, probably COFF import library (unsupported)."); + return false; + } + header2.unused1 = reader.Read32(); + header2.unused2 = reader.Read32(); + header2.unused3 = reader.Read32(); + header2.unused4 = reader.Read32(); + sectionCount = header2.sectionCount = reader.Read32(); + header.coffSymbolTable = header2.coffSymbolTable = reader.Read32(); + header.coffSymbolCount = header2.coffSymbolCount = reader.Read32(); + LogDebug("COFFHeader (big):\n" + "\tsig1: 0x%04x\n" + "\tsig2: 0x%04x\n" + "\tmachine: 0x%04x\n" + "\tsectionCount: 0x%08x\n" + "\ttimestamp: 0x%08x\n" + "\tcoffSymbolTable: 0x%08x\n" + "\tcoffSymbolCount: 0x%08x\n", + header2.sig1, + header2.sig2, + header2.machine, + header2.sectionCount, + header2.timestamp, + header2.coffSymbolTable, + header2.coffSymbolCount); + } + else + { + sectionCount = header.sectionCount = sig2; + } + } + else + { + sectionCount = header.sectionCount = reader.Read16(); + } + if (header2.sig2 != 0xFFFF) + { + header.timestamp = reader.Read32(); + header.coffSymbolTable = reader.Read32(); + header.coffSymbolCount = reader.Read32(); + header.optionalHeaderSize = reader.Read16(); + header.characteristics = reader.Read16(); + LogDebug("COFFHeader:\n" + "\tmachine: 0x%04x\n" + "\tsectionCount: 0x%04x\n" + "\ttimestamp: 0x%08x\n" + "\tcoffSymbolTable: 0x%08x\n" + "\tcoffSymbolCount: 0x%08x\n" + "\toptionalHeaderSize: 0x%04x\n" + "\tcharacteristics: 0x%04x %s, %s, %s\n", + header.machine, + header.sectionCount, + header.timestamp, + header.coffSymbolTable, + header.coffSymbolCount, + header.optionalHeaderSize, + header.characteristics, + header.characteristics & 1 ? "No Relocations" : "", + header.characteristics & 2 ? "Executable" : "", + header.characteristics & 0x2000 ? "Dll" : "Unknown"); + } + isBigCOFF = sectionIndexSize != sizeof(header.sectionCount); + + // TODO: Optional Header for executable COFF files: https://wiki.osdev.org/COFF#Optional_Header + + // Add the entry point as a function if the architecture is supported + // set m_arch early so the to make it available for the demangler + m_arch = g_coffViewType->GetArchitecture(header.machine, LittleEndian); + if (!m_arch) + { + LogError("COFF: Invalid or unknown machine type %#" PRIx16, header.machine); + return false; + } + LogDebug("COFF: Architecture(%#x): %s", header.machine, m_arch->GetName().c_str()); + + m_is64 = m_arch->GetAddressSize() == 8; + + m_imageBase = 0; // 0 for COFF? opt.imageBase; + settings = GetLoadSettings(GetTypeName()); + if (settings && settings->Contains("loader.imageBase") && settings->Contains("loader.architecture")) // handle overrides + { + m_imageBase = settings->Get<uint64_t>("loader.imageBase", this); + + Ref<Architecture> arch = Architecture::GetByName(settings->Get<string>("loader.architecture", this)); + if (!m_arch || (arch && (arch->GetName() != m_arch->GetName()))) + m_arch = arch; + } + + Ref<Settings> viewSettings = Settings::Instance(); + m_extractMangledTypes = viewSettings->Get<bool>("analysis.extractTypesFromMangledNames", this); + m_simplifyTemplates = viewSettings->Get<bool>("analysis.types.templateSimplifier", this); + + // Add extra segment to hold header so that it can be viewed. This must be first so + // that real sections take priority. + if (sectionCount) + { + m_sizeOfHeaders = sectionHeadersOffset + sectionCount * sizeof(COFFSectionHeader); + AddAutoSegment(m_imageBase, m_sizeOfHeaders, 0, m_sizeOfHeaders, SegmentReadable); + } + else + { + LogWarn("COFF header sectionCount is 0, no sections added"); + return false; + } + + // Since COFF files are not image files, they don't have an entry point. So set the entry point + // to the beginning of the first executable section. + m_entryPoint = 0; + + // COFF files are object files, to be relocated by the linker, so are always relocatable. + m_relocatable = true; + + // Read sections + reader.Seek(sectionHeadersOffset); + BinaryReader sectionNameReader(GetParentView(), LittleEndian); + + for (uint32_t i = 0; i < sectionCount; i++) + { + COFFSection section; + char name[9]; + memset(name, 0, sizeof(name)); + reader.Read(name, 8); + string resolvedName = name; + if (name[0] == '/' && header.coffSymbolTable) + { + uint32_t stringTableBase = header.coffSymbolTable + (header.coffSymbolCount * 18); + errno = 0; + uint32_t offset = strtoul(name+1, nullptr, 10); + if (errno == 0 && offset > 0 && stringTableBase + offset < GetParentView()->GetEnd()) + { + sectionNameReader.Seek(stringTableBase + offset); + resolvedName = sectionNameReader.ReadCString(); + } + } + section.name = resolvedName; + + section.virtualSize = reader.Read32(); + section.virtualAddress = reader.Read32(); + section.sizeOfRawData = reader.Read32(); + section.pointerToRawData = reader.Read32(); + section.pointerToRelocs = reader.Read32(); + section.pointerToLineNumbers = reader.Read32(); + section.relocCount = reader.Read16(); + section.lineNumberCount = reader.Read16(); + section.characteristics = reader.Read32(); + + if (section.virtualSize == 0) + { + section.virtualSize = section.sizeOfRawData; + } + if (section.virtualAddress == 0) + { + section.virtualAddress = section.pointerToRawData; + } + + if (i > 0) + { + auto previous = m_sections[i-1]; + DEBUG_COFF(LogDebug("COFF: previous section (%#" PRIx32 ", %#" PRIx32 ", %#" PRIx32 ") new section: %#" PRIx32 ", %#" PRIx32 ")", + previous.virtualAddress, + previous.virtualSize, + previous.virtualAddress + previous.virtualSize + previous.relocCount * sizeof(COFFRelocation), + section.virtualAddress, + section.virtualSize)); + if (section.virtualAddress < previous.virtualAddress + previous.virtualSize + previous.relocCount * sizeof(COFFRelocation)) + { + section.virtualAddress = previous.virtualAddress + previous.virtualSize + previous.relocCount * sizeof(COFFRelocation); + } + } + + uint32_t flags = 0; + if (section.characteristics & 0x80000000) + flags |= SegmentWritable; + if (section.characteristics & 0x40000000) + flags |= SegmentReadable; + if (section.characteristics & 0x20000000) + flags |= SegmentExecutable; + if (section.characteristics & 0x80) + flags |= SegmentContainsData; + if (section.characteristics & 0x40) + flags |= SegmentContainsData; + if (section.characteristics & 0x20) + flags |= SegmentContainsCode; + + uint32_t align_characteristic = (section.characteristics & (0x00F00000)); + DEBUG_COFF(LogDebug("COFF: align_characteristic: %#" PRIx32 ", %#" PRIx32 "\n", align_characteristic, align_characteristic >> 20)); + if (align_characteristic != 0) + { + uint32_t alignment = 1 << ((align_characteristic >> 20) - 1); + uint32_t mask = alignment - 1; + DEBUG_COFF(LogDebug("COFF: section alignment: %#" PRIx32 ", mask: %#" PRIx32 "\n", alignment, mask)); + section.virtualAddress += (-section.virtualAddress & mask); + } + + LogDebug("COFF: Section [%d]\n" + "\tsection.name %s\n"\ + "\tsection.virtualSize: %#" PRIx32 "\n"\ + /* "\tsection.physicalAddress: %#" PRIx32 "\n" */\ + "\tsection.sizeOfRawData: %#" PRIx32 "\n"\ + "\tsection.pointerToRawData: %#" PRIx32 "\n"\ + "\tsection.pointerToRelocs: %#" PRIx32 "\n"\ + "\tsection.pointerToLineNumbers: %#" PRIx32 "\n"\ + "\tsection.relocCount: %#" PRIx16 "\n"\ + "\tsection.lineNumberCount: %#" PRIx16 "\n"\ + "\tsection.characteristics: %#" PRIx32 "\n"\ + "\tsection.virtualAddress: %#" PRIx32 "\n",\ + i, section.name.c_str(), + section.virtualSize, + // section.physicalAddress, + section.sizeOfRawData, + section.pointerToRawData, + section.pointerToRelocs, + section.pointerToLineNumbers, + section.relocCount, + section.lineNumberCount, + section.characteristics, + section.virtualAddress + ); + + LogDebug("COFF: Segment: Vaddr: %08" PRIx64 " Vsize: %08" PRIx32 \ + " Offset: %08" PRIx32 " Rawsize: %08" PRIx32 " %c%c%c %s\n", + section.virtualAddress + m_imageBase, + section.virtualSize, + section.pointerToRawData, + section.sizeOfRawData, + (flags & SegmentExecutable) > 0 ? 'x':'-', + (flags & SegmentReadable) > 0 ? 'r':'-', + (flags & SegmentWritable) > 0 ? 'w':'-', + section.name.c_str()); + + m_sections.push_back(section); + if (!isCLRBinary && section.name == ".cormeta") + isCLRBinary = true; + + if (!section.virtualSize) + continue; + + AddAutoSegment(section.virtualAddress + m_imageBase, section.virtualSize, section.pointerToRawData, section.sizeOfRawData, flags); + + // Since COFF files are not image files, they don't have an entry point. So if the entry point isn't + // already set and this is an executable section, set the entry point to its beginning. + if (m_entryPoint == 0 && (flags & SegmentExecutable) != 0) + { + m_entryPoint = section.virtualAddress; + } + + BNSectionSemantics semantics = DefaultSectionSemantics; + uint32_t pFlags = flags & 0x7; + if (pFlags == (SegmentReadable | SegmentExecutable)) + semantics = ReadOnlyCodeSectionSemantics; + else if (pFlags == SegmentReadable) + semantics = ReadOnlyDataSectionSemantics; + else if (pFlags == (SegmentReadable | SegmentWritable)) + semantics = ReadWriteDataSectionSemantics; + + // FIXME: (from peview.cpp) For now override semantics for well known section names and warn about the semantic promotion + static map<string, BNSectionSemantics> promotedSectionSemantics = + { + {"text", ReadOnlyCodeSectionSemantics}, + {"code", ReadOnlyCodeSectionSemantics}, + {"rdata", ReadOnlyDataSectionSemantics}, + {"data", ReadWriteDataSectionSemantics}, + {"bss", ReadWriteDataSectionSemantics} + }; + string shortName = section.name; + if (shortName.length() && shortName[0] == '.') + shortName.erase(shortName.begin()); + transform(shortName.begin(), shortName.end(), shortName.begin(), ::tolower); + if (auto itr = promotedSectionSemantics.find(shortName); (itr != promotedSectionSemantics.end()) && (itr->second != semantics)) + { + LogInfo("COFF: %s section semantics have been promoted to facilitate analysis.", section.name.c_str()); + semantics = itr->second; + } + + auto emplaced = usedSectionNames.emplace(section.name, 1); + if (emplaced.second) + { + AddAutoSection(section.name, section.virtualAddress + m_imageBase, section.virtualSize, semantics); + } + else + { + stringstream ss; + ss << section.name << "_" << ++emplaced.first->second; + AddAutoSection(ss.str(), section.virtualAddress + m_imageBase, section.virtualSize, semantics); + } + } + + // Apply architecture and platform + if (!m_arch) + { + switch (header.machine) + { + case IMAGE_FILE_MACHINE_I386: + LogError("Support for COFF architecture 'x86' is not present"); + break; + case IMAGE_FILE_MACHINE_ARM: + LogError("Support for COFF architecture 'armv7' is not present"); + break; + case IMAGE_FILE_MACHINE_ARMNT: + LogError("Support for COFF architecture 'thumb2' is not present"); + break; + case IMAGE_FILE_MACHINE_AMD64: + LogError("Support for COFF architecture 'x86_64' is not present"); + break; + case IMAGE_FILE_MACHINE_ARM64: + LogError("Support for COFF architecture 'aarch64' is not present"); + break; + default: + LogError("COFF architecture '0x%x' is not supported", header.machine); + break; + } + return false; + } + + entryPointAddress = m_entryPoint; + if (header.machine == IMAGE_FILE_MACHINE_ARMNT) + { + // Special case for ARMNT machine type: all code is thumb2 + // but low bit of function symbol addresses are not set, so force the + // entry point address to have its low bit set, + // otherwise GetAssociatedPlatformByAddress will say it's armv7 + entryPointAddress |= 1; + LogDebug("COFF: IMAGE_FILE_MACHINE_ARMNT, setting low bit in entry point %#" PRIx64 " to %#" PRIx64 "", m_entryPoint, entryPointAddress); + m_entryPoint = entryPointAddress; + } + + Ref<Architecture> entryPointArch = m_arch->GetAssociatedArchitectureByAddress(entryPointAddress); + entryPointAddress = m_entryPoint; + SetDefaultArchitecture(entryPointArch); + GetParentView()->SetDefaultArchitecture(entryPointArch); + + platform = g_coffViewType->GetPlatform(IMAGE_SUBSYSTEM_UNKNOWN, m_arch); + LogDebug("COFF: initial platform (%#x, arch: %s): %s", header.machine, m_arch->GetName().c_str(), platform->GetName().c_str()); + + if (!platform) + platform = m_arch->GetStandalonePlatform(); + platform = platform->GetAssociatedPlatformByAddress(entryPointAddress); + entryPointAddress = m_entryPoint; + LogDebug("COFF: entry point %#" PRIx64 " associated platform (%#x, arch: %s): %s", entryPointAddress, header.machine, m_arch->GetName().c_str(), platform->GetName().c_str()); + + if (settings && settings->Contains("loader.platform")) // handle overrides + { + auto platformOverrideName = settings->Get<string>("loader.platform", this); + Ref<Platform> platformOverride = Platform::GetByName(platformOverrideName); + if (platformOverride) + { + platform = platformOverride; + LogDebug("COFF: loader.platform override (%#x, arch: %s): %s", header.machine, m_arch->GetName().c_str(), platform->GetName().c_str()); + } + else + { + LogError("COFF: Cannot find platform \"%s\" specified in loader.platform override", platformOverrideName.c_str()); + } + } + + SetDefaultPlatform(platform); + SetDefaultArchitecture(platform->GetArchitecture()); + LogDebug("COFF: final entry point %#" PRIx64 " default (%#x, arch: %s): %s", entryPointAddress, header.machine, platform->GetName().c_str(), GetDefaultPlatform()->GetName().c_str()); + + // Finished for parse only mode + if (m_parseOnly) + return true; + + // Create various COFF header yypes + + // Create COFF Header Type + EnumerationBuilder coffHeaderMachineBuilder; + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_UNKNOWN); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_AM33); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_AMD64); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_ARM); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_ARM64); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_ARMNT); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_EBC); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_I386); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_IA64); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_M32R); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_MIPS16); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_MIPSFPU); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_MIPSFPU16); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_POWERPC); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_POWERPCFP); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_R4000); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_RISCV32); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_RISCV64); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_RISCV128); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_SH3); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_SH3DSP); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_SH4); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_SH5); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_THUMB); + ADD_ENUM_MEMBER(coffHeaderMachine, IMAGE_FILE_MACHINE_WCEMIPSV2); + BUILD_ENUM_TYPE(coffHeaderMachine, "coff_machine", 2); + + // Ref<Enumeration> coffHeaderMachineEnum = coffHeaderMachineBuilder.Finalize(); + // Ref<Type> coffHeaderMachineEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), coffHeaderMachineEnum, 2, false); + // BUILD_ENUM_TYPE(coffHeaderMachine, "coff_machine"); + // string coffHeaderMachineEnumId = Type::GenerateAutoTypeId("coff", coffHeaderMachineEnumName); + // QualifiedName coffHeaderMachineEnumTypeName = DefineType(coffHeaderMachineEnumId, coffHeaderMachineEnumName, coffHeaderMachineEnumType); + + EnumerationBuilder coffCharacteristicsBuilder; + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_RELOCS_STRIPPED); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_EXECUTABLE_IMAGE); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_LINE_NUMS_STRIPPED); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_LOCAL_SYMS_STRIPPED); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_AGGRESIVE_WS_TRIM); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_LARGE_ADDRESS_AWARE); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_BYTES_REVERSED_LO); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_32BIT_MACHINE); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_DEBUG_STRIPPED); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_NET_RUN_FROM_SWAP); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_SYSTEM); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_DLL); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_UP_SYSTEM_ONLY); + ADD_ENUM_MEMBER(coffCharacteristics, IMAGE_FILE_BYTES_REVERSED_HI); + + // Ref<Enumeration> coffCharacteristicsEnum = coffCharacteristicsBuilder.Finalize(); + // Ref<Type> coffCharacteristicsEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), coffCharacteristicsEnum, 2, false); + BUILD_ENUM_TYPE(coffCharacteristics, "coff_characteristics", 2); + // string coffCharacteristicsEnumId = Type::GenerateAutoTypeId("coff", coffCharacteristicsEnumName); + // QualifiedName coffCharacteristicsEnumTypeName = DefineType(coffCharacteristicsEnumId, coffCharacteristicsEnumName, coffCharacteristicsEnumType); + + // TODO decorate members with comments once comments work with linear view + StructureBuilder coffHeaderBuilder; + coffHeaderBuilder.SetPacked(true); + if (header2.sig2 == 0xFFFF) + { + coffHeaderBuilder.AddMember(Type::IntegerType(2, false), "sig1"); + coffHeaderBuilder.AddMember(Type::IntegerType(2, false), "sig2"); + coffHeaderBuilder.AddMember(Type::IntegerType(2, false), "version"); + coffHeaderBuilder.AddMember(Type::NamedType(this, coffHeaderMachineEnumTypeName), "machine"); + coffHeaderBuilder.AddMember(Type::IntegerType(4, false), "timeDateStamp"); + coffHeaderBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, false), 16), "UUID"); + coffHeaderBuilder.AddMember(Type::IntegerType(4, false), "unused1"); + coffHeaderBuilder.AddMember(Type::IntegerType(4, false), "unused2"); + coffHeaderBuilder.AddMember(Type::IntegerType(4, false), "unused3"); + coffHeaderBuilder.AddMember(Type::IntegerType(4, false), "unused4"); + coffHeaderBuilder.AddMember(Type::IntegerType(4, false), "numberOfSections"); + coffHeaderBuilder.AddMember(Type::IntegerType(4, false), "pointerToSymbolTable"); + coffHeaderBuilder.AddMember(Type::IntegerType(4, false), "numberOfSymbols"); + } + else + { + coffHeaderBuilder.AddMember(Type::NamedType(this, coffHeaderMachineEnumTypeName), "machine"); + coffHeaderBuilder.AddMember(Type::IntegerType(2, false), "numberOfSections"); + coffHeaderBuilder.AddMember(Type::IntegerType(4, false), "timeDateStamp"); + coffHeaderBuilder.AddMember(Type::IntegerType(4, false), "pointerToSymbolTable"); + coffHeaderBuilder.AddMember(Type::IntegerType(4, false), "numberOfSymbols"); + coffHeaderBuilder.AddMember(Type::IntegerType(2, false), "sizeOfOptionalHeader"); + coffHeaderBuilder.AddMember(Type::NamedType(this, coffCharacteristicsEnumTypeName), "characteristics"); + } + + Ref<Structure> coffHeaderStruct = coffHeaderBuilder.Finalize(); + Ref<Type> coffHeaderType = Type::StructureType(coffHeaderStruct); + QualifiedName coffHeaderName = string("COFF_Header"); + string coffHeaderTypeId = Type::GenerateAutoTypeId("coff", coffHeaderName); + QualifiedName coffHeaderTypeName = DefineType(coffHeaderTypeId, coffHeaderName, coffHeaderType); + DefineDataVariable(m_imageBase, Type::NamedType(this, coffHeaderTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__coff_header", m_imageBase, NoBinding)); + + EnumerationBuilder coffSectionFlagsBuilder; + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_RESERVED_0001); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_RESERVED_0002); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_RESERVED_0004); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_TYPE_NO_PAD); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_RESERVED_0010); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_CNT_CODE); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_CNT_INITIALIZED_DATA); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_CNT_UNINITIALIZED_DATA); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_LNK_OTHER); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_LNK_INFO); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_RESERVED_0400); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_LNK_REMOVE); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_LNK_COMDAT); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_GPREL); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_MEM_PURGEABLE); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_MEM_16BIT); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_MEM_LOCKED); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_MEM_PRELOAD); + // TODO fix the bug that causes flags to not be displayed when these are added to the enumeration + // NOTE: not a bug, it's by design -- the EnumerationData::IsBitFieldEnum() condition isn't satisfied because these members have overlapping bits + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_1BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_2BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_4BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_8BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_16BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_32BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_64BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_128BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_256BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_512BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_1024BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_2048BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_4096BYTES); + // ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_ALIGN_8192BYTES); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_LNK_NRELOC_OVFL); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_MEM_DISCARDABLE); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_MEM_NOT_CACHED); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_MEM_NOT_PAGED); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_MEM_SHARED); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_MEM_EXECUTE); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_MEM_READ); + ADD_ENUM_MEMBER(coffSectionFlags, IMAGE_SCN_MEM_WRITE); + + // Ref<Enumeration> coffSectionFlagsEnum = coffSectionFlagsBuilder.Finalize(); + // Ref<Type> coffSectionFlagsEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), coffSectionFlagsEnum, 4, false); + BUILD_ENUM_TYPE(coffSectionFlags, "coff_section_flags", 4); + // string coffSectionFlagsEnumId = Type::GenerateAutoTypeId("coff", coffSectionFlagsEnumName); + // QualifiedName coffSectionFlagsEnumTypeName = DefineType(coffSectionFlagsEnumId, coffSectionFlagsEnumName, coffSectionFlagsEnumType); + + if (sectionCount) + { + StructureBuilder sectionHeaderBuilder; + sectionHeaderBuilder.SetPacked(true); + sectionHeaderBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 8), "name"); + sectionHeaderBuilder.AddMember(Type::IntegerType(4, false), "virtualSize"); + sectionHeaderBuilder.AddMember(Type::IntegerType(4, false), "virtualAddress"); + sectionHeaderBuilder.AddMember(Type::IntegerType(4, false), "sizeOfRawData"); + sectionHeaderBuilder.AddMember(Type::IntegerType(4, false), "pointerToRawData"); + sectionHeaderBuilder.AddMember(Type::IntegerType(4, false), "pointerToRelocations"); + sectionHeaderBuilder.AddMember(Type::IntegerType(4, false), "pointerToLineNumbers"); + sectionHeaderBuilder.AddMember(Type::IntegerType(2, false), "numberOfRelocations"); + sectionHeaderBuilder.AddMember(Type::IntegerType(2, false), "numberOfLineNumbers"); + sectionHeaderBuilder.AddMember(Type::NamedType(this, coffSectionFlagsEnumTypeName), "characteristics"); + + Ref<Structure> sectionHeaderStruct = sectionHeaderBuilder.Finalize(); + Ref<Type> sectionHeaderStructType = Type::StructureType(sectionHeaderStruct); + QualifiedName sectionHeaderName = string("COFF_Section_Header"); + string sectionHeaderTypeId = Type::GenerateAutoTypeId("COFF", sectionHeaderName); + QualifiedName sectionHeaderTypeName = DefineType(sectionHeaderTypeId, sectionHeaderName, sectionHeaderStructType); + + DefineAutoSymbol(new Symbol(DataSymbol, "__section_headers", m_imageBase + sectionHeadersOffset, NoBinding)); + + for (uint32_t i = 0; i < sectionCount; i++) + { + auto sectionHeaderOffset = sectionHeadersOffset + i * sizeof(COFFSectionHeader); + string sectionName = m_sections[i].name; + DefineDataVariable(m_imageBase + sectionHeaderOffset, Type::NamedType(this, sectionHeaderTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__sec_hdr_" + sectionName, m_imageBase + sectionHeaderOffset, NoBinding)); + } + } + } + catch (std::exception& e) + { + LogError("Failed to parse COFF headers: %s\n", e.what()); + return false; + } + + // The offset of the symbol table after adjusting for the alignment of the sections that precede it + uint64_t symbolTableAdjustedOffset = 0; + + try + { + // Process COFF symbol table + if (header.coffSymbolCount) + { + EnumerationBuilder coffSymbolTypeBuilder; + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_NULL); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_VOID); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_CHAR); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_SHORT); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_INT); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_LONG); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_FLOAT); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_DOUBLE); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_STRUCT); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_UNION); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_ENUM); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_MOE); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_BYTE); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_WORD); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_UINT); + ADD_ENUM_MEMBER(coffSymbolType, IMAGE_SYM_TYPE_DWORD); + ADD_ENUM_MEMBER(coffSymbolType, MS_IMAGE_SYM_TYPE_FUNCTION); + BUILD_ENUM_TYPE(coffSymbolType, "coff_symbol_type", 2); + + EnumerationBuilder coffSymbolStorageClassBuilder; + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_NULL); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_AUTOMATIC); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_EXTERNAL); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_STATIC); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_REGISTER); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_EXTERNAL_DEF); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_LABEL); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_UNDEFINED_LABEL); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_MEMBER_OF_STRUCT); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_ARGUMENT); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_STRUCT_TAG); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_MEMBER_OF_UNION); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_UNION_TAG); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_TYPE_DEFINITION); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_UNDEFINED_STATIC); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_ENUM_TAG); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_MEMBER_OF_ENUM); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_REGISTER_PARAM); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_BIT_FIELD); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_AUTOARG); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_LASTENT); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_BLOCK); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_FUNCTION); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_END_OF_STRUCT); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_FILE); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_SECTION); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_WEAK_EXTERNAL); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_HIDDEN); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_CLR_TOKEN); + ADD_ENUM_MEMBER(coffSymbolStorageClass, IMAGE_SYM_CLASS_END_OF_FUNCTION); + BUILD_ENUM_TYPE(coffSymbolStorageClass, "coff_symbol_storage_class", 1); + + StructureBuilder coffSymbolBuilder; + coffSymbolBuilder.SetPacked(true); + StructureBuilder longNameBuilder; + longNameBuilder.SetPacked(true); + longNameBuilder.AddMember(Type::IntegerType(4, false), "zeroes"); + longNameBuilder.AddMember(Type::IntegerType(4, false), "offset"); + Ref<Structure> longNameStruct = longNameBuilder.Finalize(); + Ref<Type> longNameStructType = Type::StructureType(longNameStruct); + StructureBuilder nameUnionBuilder; + nameUnionBuilder.SetStructureType(UnionStructureType); + nameUnionBuilder.SetPacked(true); + nameUnionBuilder.AddMember(longNameStructType, "longName"); + nameUnionBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 8), "shortName"); + Ref<Structure> nameUnion = nameUnionBuilder.Finalize(); + Ref<Type> nameUnionType = Type::StructureType(nameUnion); + + coffSymbolBuilder.AddMember(nameUnionType, "name"); + coffSymbolBuilder.AddMember(Type::IntegerType(4, false), "value"); + coffSymbolBuilder.AddMember(Type::IntegerType(sectionIndexSize, true), "sectionNumber"); + coffSymbolBuilder.AddMember(Type::NamedType(this, coffSymbolTypeEnumTypeName), "type"); + coffSymbolBuilder.AddMember(Type::NamedType(this, coffSymbolStorageClassEnumTypeName), "storageClass"); + coffSymbolBuilder.AddMember(Type::IntegerType(1, false), "numberOfAuxSymbols"); + + Ref<Structure> coffSymbolStruct = coffSymbolBuilder.Finalize(); + Ref<Type> coffSymbolStructType = Type::StructureType(coffSymbolStruct); + QualifiedName coffSymbolName = string("COFF_Symbol"); + string coffSymbolTypeId = Type::GenerateAutoTypeId("COFF", coffSymbolName); + QualifiedName coffSymbolTypeName = DefineType(coffSymbolTypeId, coffSymbolName, coffSymbolStructType); + + //Auxiliary Format 1: Function Definitions + StructureBuilder coffAuxFunctionDefinitionBuilder; + coffAuxFunctionDefinitionBuilder.SetPacked(true); + coffAuxFunctionDefinitionBuilder.AddMember(Type::IntegerType(4, false), "tagIndex"); + coffAuxFunctionDefinitionBuilder.AddMember(Type::IntegerType(4, false), "totalSize"); + coffAuxFunctionDefinitionBuilder.AddMember(Type::IntegerType(4, false), "pointerToLineNumber"); + coffAuxFunctionDefinitionBuilder.AddMember(Type::IntegerType(4, false), "pointerToNextFunction"); + coffAuxFunctionDefinitionBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, false), 2), "unused"); + Ref<Structure> coffAuxFunctionDefinitionStruct = coffAuxFunctionDefinitionBuilder.Finalize(); + Ref<Type> coffAuxFunctionDefinitionStructType = Type::StructureType(coffAuxFunctionDefinitionStruct); + QualifiedName coffAuxFunctionDefinitionName = string("COFF_AuxFunctionDefinition"); + string coffAuxFunctionDefinitionTypeId = Type::GenerateAutoTypeId("COFF", coffAuxFunctionDefinitionName); + QualifiedName coffAuxFunctionDefinitionTypeName = DefineType(coffAuxFunctionDefinitionTypeId, coffAuxFunctionDefinitionName, coffAuxFunctionDefinitionStructType); + + // Auxiliary Format 2: .bf and .ef Symbols + StructureBuilder coffAux_bf_And_ef_SymbolBuilder; + coffAux_bf_And_ef_SymbolBuilder.SetPacked(true); + coffAux_bf_And_ef_SymbolBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, false), 4), "unused0"); + coffAux_bf_And_ef_SymbolBuilder.AddMember(Type::IntegerType(4, false), "lineNumber"); + coffAux_bf_And_ef_SymbolBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, false), 6), "unused6"); + coffAux_bf_And_ef_SymbolBuilder.AddMember(Type::IntegerType(4, false), "pointerToNextFunction"); + coffAux_bf_And_ef_SymbolBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, false), 2), "unused16"); + Ref<Structure> coffAux_bf_And_ef_SymbolStruct = coffAux_bf_And_ef_SymbolBuilder.Finalize(); + Ref<Type> coffAux_bf_And_ef_SymbolStructType = Type::StructureType(coffAux_bf_And_ef_SymbolStruct); + QualifiedName coffAux_bf_And_ef_SymbolName = string("COFF_Aux_bf_And_ef_Symbol"); + string coffAux_bf_And_ef_SymbolTypeId = Type::GenerateAutoTypeId("COFF", coffAux_bf_And_ef_SymbolName); + QualifiedName coffAux_bf_And_ef_SymbolTypeName = DefineType(coffAux_bf_And_ef_SymbolTypeId, coffAux_bf_And_ef_SymbolName, coffAux_bf_And_ef_SymbolStructType); + + // Auxiliary Format 3: Weak Externals + StructureBuilder coffAuxWeakExternalBuilder; + coffAuxWeakExternalBuilder.SetPacked(true); + coffAuxWeakExternalBuilder.AddMember(Type::IntegerType(4, false), "tagIndex"); + coffAuxWeakExternalBuilder.AddMember(Type::IntegerType(4, false), "characteristics"); + coffAuxWeakExternalBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, false), 10), "unused"); + Ref<Structure> coffAuxWeakExternalStruct = coffAuxWeakExternalBuilder.Finalize(); + Ref<Type> coffAuxWeakExternalStructType = Type::StructureType(coffAuxWeakExternalStruct); + QualifiedName coffAuxWeakExternalName = string("COFF_AuxWeakExternal"); + string coffAuxWeakExternalTypeId = Type::GenerateAutoTypeId("COFF", coffAuxWeakExternalName); + QualifiedName coffAuxWeakExternalTypeName = DefineType(coffAuxWeakExternalTypeId, coffAuxWeakExternalName, coffAuxWeakExternalStructType); + + // Auxiliary Format 4: Files + StructureBuilder coffAuxFileBuilder; + coffAuxFileBuilder.SetPacked(true); + coffAuxFileBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 18), "fileName"); + Ref<Structure> coffAuxFileStruct = coffAuxFileBuilder.Finalize(); + Ref<Type> coffAuxFileStructType = Type::StructureType(coffAuxFileStruct); + QualifiedName coffAuxFileName = string("COFF_AuxFile"); + string coffAuxFileTypeId = Type::GenerateAutoTypeId("COFF", coffAuxFileName); + QualifiedName coffAuxFileTypeName = DefineType(coffAuxFileTypeId, coffAuxFileName, coffAuxFileStructType); + + // Auxiliary Format 5: Section Definitions + StructureBuilder coffAuxSectionDefinitionBuilder; + coffAuxSectionDefinitionBuilder.SetPacked(true); + coffAuxSectionDefinitionBuilder.AddMember(Type::IntegerType(4, false), "length"); + coffAuxSectionDefinitionBuilder.AddMember(Type::IntegerType(2, false), "numberOfRelocations"); + coffAuxSectionDefinitionBuilder.AddMember(Type::IntegerType(2, false), "numberOfLineNumbers"); + coffAuxSectionDefinitionBuilder.AddMember(Type::IntegerType(4, false), "checkSum"); + coffAuxSectionDefinitionBuilder.AddMember(Type::IntegerType(2, false), "number"); + coffAuxSectionDefinitionBuilder.AddMember(Type::IntegerType(1, false), "selection"); + if (!isBigCOFF) + coffAuxSectionDefinitionBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, false), 3), "unused"); + else + { + coffAuxSectionDefinitionBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, false), 1), "unused"); + coffAuxSectionDefinitionBuilder.AddMember(Type::IntegerType(2, false), "numberHighPart"); + } + Ref<Structure> coffAuxSectionDefinitionStruct = coffAuxSectionDefinitionBuilder.Finalize(); + Ref<Type> coffAuxSectionDefinitionStructType = Type::StructureType(coffAuxSectionDefinitionStruct); + QualifiedName coffAuxSectionDefinitionName = string("COFF_AuxSectionDefinition"); + string coffAuxSectionDefinitionTypeId = Type::GenerateAutoTypeId("COFF", coffAuxSectionDefinitionName); + QualifiedName coffAuxSectionDefinitionTypeName = DefineType(coffAuxSectionDefinitionTypeId, coffAuxSectionDefinitionName, coffAuxSectionDefinitionStructType); + + // CLR Token Definition + StructureBuilder coffAuxCLRTokenBuilder; + coffAuxCLRTokenBuilder.SetPacked(true); + coffAuxCLRTokenBuilder.AddMember(Type::IntegerType(1, false), "bAuxType"); + coffAuxCLRTokenBuilder.AddMember(Type::IntegerType(1, false), "bReserved"); + coffAuxCLRTokenBuilder.AddMember(Type::IntegerType(4, false), "SymbolTableIndex"); + coffAuxCLRTokenBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 12), "Reserved"); + Ref<Structure> coffAuxCLRTokenStruct = coffAuxCLRTokenBuilder.Finalize(); + Ref<Type> coffAuxCLRTokenStructType = Type::StructureType(coffAuxCLRTokenStruct); + QualifiedName coffAuxCLRTokenName = string("COFF_AuxCLRToken"); + string coffAuxCLRTokenTypeId = Type::GenerateAutoTypeId("COFF", coffAuxCLRTokenName); + QualifiedName coffAuxCLRTokenTypeName = DefineType(coffAuxCLRTokenTypeId, coffAuxCLRTokenName, coffAuxCLRTokenStructType); + + + // TODO: combine the aux symbol record struct types into a union: + // StructureBuilder coffAuxSymbolRecordBuilder(UnionStructureType); + + size_t symbolTableSize = header.coffSymbolCount * sizeofCOFFSymbol; + auto lastSection = m_sections.back(); + symbolTableAdjustedOffset = header.coffSymbolTable - lastSection.pointerToRawData + lastSection.virtualAddress; + auto coffSymbolTableBase = m_imageBase + symbolTableAdjustedOffset; + AddAutoSegment(coffSymbolTableBase, symbolTableSize, header.coffSymbolTable, symbolTableSize, SegmentReadable); + auto emplaced = usedSectionNames.emplace(".symtab", 1); + if (emplaced.second) + { + AddAutoSection(".symtab", coffSymbolTableBase, symbolTableSize, ReadOnlyDataSectionSemantics); + } + else + { + stringstream ss; + ss << ".symtab_" << ++emplaced.first->second; + AddAutoSection(ss.str(), coffSymbolTableBase, symbolTableSize, ReadOnlyDataSectionSemantics); + } + + DefineDataVariable(coffSymbolTableBase, Type::ArrayType(Type::NamedType(this, coffSymbolName), header.coffSymbolCount)); + DefineAutoSymbol(new Symbol(DataSymbol, "__symtab", coffSymbolTableBase, NoBinding)); + + BinaryReader stringReader(GetParentView(), LittleEndian); + uint64_t stringTableBaseRaw = header.coffSymbolTable + ((uint64_t) header.coffSymbolCount * sizeofCOFFSymbol); + + stringReader.Seek(stringTableBaseRaw); + uint32_t stringTableSize = stringReader.Read32(); + if ((stringTableBaseRaw + stringTableSize) > GetParentView()->GetEnd()) + { + throw COFFFormatException("invalid COFF string table size"); + } + int64_t stringTableBase = stringTableBaseRaw - header.coffSymbolTable + symbolTableAdjustedOffset; + AddAutoSegment(m_imageBase + stringTableBase, stringTableSize, stringTableBaseRaw, stringTableSize, SegmentReadable); + emplaced = usedSectionNames.emplace(".strtab", 1); + if (emplaced.second) + { + AddAutoSection(".strtab", m_imageBase + stringTableBase, stringTableSize, ReadOnlyDataSectionSemantics); + } + else + { + stringstream ss; + ss << ".strtab_" << ++emplaced.first->second; + AddAutoSection(ss.str(), m_imageBase + stringTableBase, stringTableSize, ReadOnlyDataSectionSemantics); + } + + DefineDataVariable(m_imageBase + stringTableBase, Type::IntegerType(4, false)); + DefineAutoSymbol(new Symbol(DataSymbol, "__strtab_size", m_imageBase + stringTableBase, NoBinding)); + + DefineAutoSymbol(new Symbol(DataSymbol, "__strtab", m_imageBase + stringTableBase + 4, NoBinding)); + + for (size_t i = 0; i < header.coffSymbolCount; i++) + { + reader.Seek(header.coffSymbolTable + (i * sizeofCOFFSymbol)); + uint32_t e_zeroes = reader.Read32(); + uint32_t e_offset = reader.Read32(); + uint32_t e_value = reader.Read32(); + uint32_t e_scnum = !isBigCOFF ? (uint32_t)reader.Read16() : reader.Read32(); + uint16_t e_type = reader.Read16(); + uint8_t e_sclass = reader.Read8(); + uint8_t e_numaux = reader.Read8(); + + uint64_t virtualAddress = 0; + switch (e_scnum) + { + case IMAGE_SYM_UNDEFINED: + case (uint16_t)IMAGE_SYM_ABSOLUTE: + case (uint16_t)IMAGE_SYM_DEBUG: + break; + default: + if (size_t(e_scnum - 1) < m_sections.size()) + virtualAddress = m_sections[size_t(e_scnum - 1)].virtualAddress + e_value; + break; + } + + // read symbol name + string symbolName; + if (e_zeroes) + { + stringReader.Seek(header.coffSymbolTable + (i * sizeofCOFFSymbol)); + symbolName = stringReader.ReadCString(8); + symbolName = symbolName.substr(0, strlen(symbolName.c_str())); + } + else + { + stringReader.Seek(stringTableBaseRaw + e_offset); + symbolName = stringReader.ReadCString(); + } + + BNSymbolBinding binding; + bool clrFunction = false; + switch (e_sclass) + { + case IMAGE_SYM_CLASS_EXTERNAL: + binding = GlobalBinding; + break; + case IMAGE_SYM_CLASS_STATIC: + binding = LocalBinding; + break; + case IMAGE_SYM_CLASS_CLR_TOKEN: + clrFunction = true; + binding = LocalBinding; + break; + default: + binding = NoBinding; + break; + } + + uint8_t baseType = (e_type >> 4) & 0x3; + switch (baseType) + { + case IMAGE_SYM_DTYPE_NULL: // no derived type + { + if (virtualAddress) + AddCOFFSymbol(DataSymbol, "", symbolName, virtualAddress, binding); + break; + } + case IMAGE_SYM_DTYPE_POINTER: // pointer to base type + { + break; + } + case IMAGE_SYM_DTYPE_FUNCTION: // function that returns base type + { + if (virtualAddress) + { + if (!isCLRBinary) + { + auto functionAddress = virtualAddress; + if (header.machine == IMAGE_FILE_MACHINE_ARMNT) + { + // NOTE: for IMAGE_FILE_MACHINE_ARMNT, there are only thumb2 functions, + // so we force the low bit on for all function symbols + functionAddress |= 1; + } + AddCOFFSymbol(FunctionSymbol, "", symbolName, functionAddress, binding); + } + else if (!clrFunction) + { + AddCOFFSymbol(DataSymbol, "", symbolName, virtualAddress, binding); + } + } + break; + } + case IMAGE_SYM_DTYPE_ARRAY: // array of base type + { + break; + } + default: + break; + } + + auto symbolVirtualAddress = symbolTableAdjustedOffset + (i * sizeofCOFFSymbol); + DefineDataVariable(m_imageBase + symbolVirtualAddress, Type::NamedType(this, coffSymbolTypeName)); + string symbolStructName = "__symbol(" + symbolName + ")"; + DefineAutoSymbol(new Symbol(DataSymbol, symbolStructName, m_imageBase + symbolVirtualAddress, NoBinding)); + + if (e_zeroes == 0) + { + DefineDataVariable(m_imageBase + stringTableBase + e_offset, Type::ArrayType(Type::IntegerType(1, true, "char"), symbolName.length() + 1)); + string symbolStringName = "__symbol_name(" + symbolName + ")"; + DefineAutoSymbol(new Symbol(DataSymbol, symbolStringName, m_imageBase + stringTableBase + e_offset, NoBinding)); + DEBUG_COFF(AddUserDataReference(m_imageBase + symbolVirtualAddress, m_imageBase + stringTableBase + e_offset)); + } + + if (e_sclass == IMAGE_SYM_CLASS_STATIC && e_value == 0) + { + size_t sectionHeaderOffset = sectionHeadersOffset + (e_scnum - 1) * sizeof(COFFSectionHeader); + (void)sectionHeaderOffset; + DEBUG_COFF(AddUserDataReference(m_imageBase + symbolVirtualAddress, m_imageBase + sectionHeaderOffset)); + } + else if (e_sclass == IMAGE_SYM_CLASS_EXTERNAL && e_value == 0 && e_scnum == IMAGE_SYM_UNDEFINED) + { + if (baseType == IMAGE_SYM_DTYPE_FUNCTION) + { + AddCOFFSymbol(ExternalSymbol, "", symbolName, symbolVirtualAddress); + } + else + { + AddCOFFSymbol(ExternalSymbol, "", symbolName, symbolVirtualAddress); + } + } + + // Reify auxiliary symbol record entries + for (size_t j = 0; j < e_numaux; j++) + { + auto auxSymbolAddress = symbolVirtualAddress + ((1 + j) * sizeofCOFFSymbol); + if (e_sclass == IMAGE_SYM_CLASS_EXTERNAL && baseType == IMAGE_SYM_DTYPE_FUNCTION && e_scnum > 0) + { + DefineDataVariable(m_imageBase + auxSymbolAddress, + Type::NamedType(this, coffAuxFunctionDefinitionTypeName)); + } + else if (e_sclass == IMAGE_SYM_CLASS_FUNCTION) + { + DefineDataVariable(m_imageBase + auxSymbolAddress, + Type::NamedType(this, coffAux_bf_And_ef_SymbolTypeName)); + } + else if (e_sclass == IMAGE_SYM_CLASS_EXTERNAL && e_scnum == IMAGE_SYM_UNDEFINED && e_value == 0) + { + DefineDataVariable(m_imageBase + auxSymbolAddress, + Type::NamedType(this, coffAuxWeakExternalTypeName)); + } + else if (e_sclass == IMAGE_SYM_CLASS_FILE && symbolName == ".file") + { + DefineDataVariable(m_imageBase + auxSymbolAddress, + Type::NamedType(this, coffAuxFileTypeName)); + } + else if (e_sclass == IMAGE_SYM_CLASS_STATIC && usedSectionNames.count(symbolName) > 0) + { + DefineDataVariable(m_imageBase + auxSymbolAddress, + Type::NamedType(this, coffAuxSectionDefinitionTypeName)); + } + else if (e_sclass == IMAGE_SYM_CLASS_CLR_TOKEN) + { + DefineDataVariable(m_imageBase + auxSymbolAddress, + Type::NamedType(this, coffAuxCLRTokenName)); + } + } + i += e_numaux; + } + } + } + catch (std::exception& e) + { + LogError("Failed to parse COFF symbol table: %s\n", e.what()); + } + + // From elfview.cpp: + // Sometimes ELF will specify Thumb entry points w/o the bottom bit set + // To deal with this we delay adding entry points until after symbols have been resolved + // and all the functions have been created. This allows us to query the existing functions + // platform. All in an effort to not create a function with the wrong architecture + if (entryPointAddress && !isCLRBinary) + { + auto entryPoint = entryPointAddress + m_imageBase; + auto platform = GetDefaultPlatform()->GetAssociatedPlatformByAddress(entryPoint); + auto func = GetAnalysisFunctionsForAddress(entryPoint); + if (func.size() == 1) + { + if (func[0]->GetPlatform() != platform) + { + RemoveAnalysisFunction(func[0], true); + } + AddEntryPointForAnalysis(platform, entryPoint); + } + else + AddEntryPointForAnalysis(platform, entryPoint); + } + + try + { + if (sectionCount) + { + EnumerationBuilder coffRelType_I386Builder; + ADD_ENUM_MEMBER(coffRelType_I386, IMAGE_REL_I386_ABSOLUTE); + ADD_ENUM_MEMBER(coffRelType_I386, IMAGE_REL_I386_DIR16); + ADD_ENUM_MEMBER(coffRelType_I386, IMAGE_REL_I386_REL16); + ADD_ENUM_MEMBER(coffRelType_I386, IMAGE_REL_I386_DIR32); + ADD_ENUM_MEMBER(coffRelType_I386, IMAGE_REL_I386_DIR32NB); + ADD_ENUM_MEMBER(coffRelType_I386, IMAGE_REL_I386_SEG12); + ADD_ENUM_MEMBER(coffRelType_I386, IMAGE_REL_I386_SECTION); + ADD_ENUM_MEMBER(coffRelType_I386, IMAGE_REL_I386_SECREL); + ADD_ENUM_MEMBER(coffRelType_I386, IMAGE_REL_I386_TOKEN); + ADD_ENUM_MEMBER(coffRelType_I386, IMAGE_REL_I386_SECREL7); + ADD_ENUM_MEMBER(coffRelType_I386, IMAGE_REL_I386_REL32); + BUILD_ENUM_TYPE(coffRelType_I386, "coff_rel_type_i386", 2); + + EnumerationBuilder coffRelType_AMD64Builder; + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_ABSOLUTE); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_ADDR64); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_ADDR32); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_ADDR32NB); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_REL32); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_REL32_1); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_REL32_2); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_REL32_3); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_REL32_4); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_REL32_5); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_SECTION); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_SECREL); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_SECREL7); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_TOKEN); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_SREL32); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_PAIR); + ADD_ENUM_MEMBER(coffRelType_AMD64, IMAGE_REL_AMD64_SSPAN32); + BUILD_ENUM_TYPE(coffRelType_AMD64, "coff_rel_type_amd64", 2); + + EnumerationBuilder coffRelType_ARMBuilder; + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_ABSOLUTE); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_ADDR32); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_ADDR32NB); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_BRANCH24); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_BRANCH11); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_BLX24); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_BLX11); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_REL32); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_SECTION); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_SECREL); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_MOV32); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_THUMB_MOV32); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_THUMB_BRANCH20); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_THUMB_UNUSED); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_THUMB_BRANCH24); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_THUMB_BLX23); + ADD_ENUM_MEMBER(coffRelType_ARM, IMAGE_REL_ARM_PAIR); + BUILD_ENUM_TYPE(coffRelType_ARM, "coff_rel_type_arm", 2); + + EnumerationBuilder coffRelType_ARM64Builder; + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_ABSOLUTE); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_ADDR32); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_ADDR32NB); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_BRANCH26); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_PAGEBASE_REL21); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_REL21); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_PAGEOFFSET_12A); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_PAGEOFFSET_12L); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_SECREL); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_SECREL_LOW12A); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_SECREL_HIGH12A); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_SECREL_LOW12L); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_TOKEN); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_SECTION); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_ADDR64); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_BRANCH19); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_BRANCH14); + ADD_ENUM_MEMBER(coffRelType_ARM64, IMAGE_REL_ARM64_REL32); + BUILD_ENUM_TYPE(coffRelType_ARM64, "coff_rel_type_arm64", 2); + + StructureBuilder coffRelocBuilder; + coffRelocBuilder.SetPacked(true); + coffRelocBuilder.AddMember(Type::IntegerType(4, false), "virtualAddress"); + coffRelocBuilder.AddMember(Type::IntegerType(4, false), "symbolTableIndex"); + map<string, QualifiedName> relocationEnumTypes + { + { "x86" , coffRelType_I386EnumTypeName }, + { "x86_64" , coffRelType_AMD64EnumTypeName }, + { "armv7" , coffRelType_ARMEnumTypeName }, + { "armv7eb" , coffRelType_ARMEnumTypeName }, // TODO: relocations probably will not work on armv7 Big Endian + { "thumb2" , coffRelType_ARMEnumTypeName }, + { "thumb2eb" , coffRelType_ARMEnumTypeName }, // TODO: relocations probably will not work on thumb2 Big Endian + { "aarch64" , coffRelType_ARM64EnumTypeName }, + }; + if (relocationEnumTypes.find(m_arch->GetName()) != relocationEnumTypes.end()) + { + coffRelocBuilder.AddMember(Type::NamedType(this, relocationEnumTypes[m_arch->GetName()]), "type"); + DEBUG_COFF(LogDebug("COFF: using relocation type '%s' for architecture '%s'", relocationEnumTypes[m_arch->GetName()].GetString().c_str(), m_arch->GetName().c_str())); + } + else { + coffRelocBuilder.AddMember(Type::IntegerType(2, false), "type"); + LogDebug("COFF: no relocation type found for architecture '%s', using uint16_t", m_arch->GetName().c_str()); + } + + Ref<Structure> coffRelocStruct = coffRelocBuilder.Finalize(); + Ref<Type> coffRelocStructType = Type::StructureType(coffRelocStruct); + QualifiedName coffRelocName = string("COFF_Relocation"); + string coffRelocTypeId = Type::GenerateAutoTypeId("COFF", coffRelocName); + QualifiedName coffRelocTypeName = DefineType(coffRelocTypeId, coffRelocName, coffRelocStructType); + + auto relocHandler = m_arch->GetRelocationHandler("COFF"); + + for (uint32_t i = 0; i < sectionCount; i++) + { + auto section = m_sections[i]; + if (section.relocCount) + { + uint32_t relocsFileOffset = section.pointerToRelocs; + auto relocsVirtualOffset = relocsFileOffset - section.pointerToRawData + section.virtualAddress; + DEBUG_COFF(LogDebug("COFF: section %d reading %d relocations from raw_data: 0x%" PRIx32 " relocs: 0x%" PRIx32 " adjusted offset: %#" PRIx32 " final address: %#" PRIx32, + i, section.relocCount, section.pointerToRawData, relocsFileOffset, relocsVirtualOffset, m_imageBase + relocsVirtualOffset)); + AddAutoSegment(m_imageBase + relocsVirtualOffset, section.relocCount * sizeof(COFFRelocation), relocsFileOffset, section.relocCount * sizeof(COFFRelocation), SegmentReadable); + + for (auto j = 0; j < section.relocCount; j++) + { + uint64_t relocationOffset = relocsVirtualOffset + j * sizeof(COFFRelocation); + uint64_t relocationFileOffset = relocsFileOffset + j * sizeof(COFFRelocation); + + reader.Seek(relocationFileOffset); + auto virtualAddress = reader.Read32(); + auto symbolTableIndex = reader.Read32(); + auto relocType = reader.Read16(); + + Ref<Type> type = Type::NamedType(this, coffRelocTypeName); + DefineDataVariable(m_imageBase + relocationOffset, type); + + uint64_t itemAddress = section.virtualAddress + virtualAddress; + + DEBUG_COFF(LogDebug("COFF: section %d reloc %d at: 0x%" PRIx32 " va: 0x%x, index: %d, type: 0x%hx, item at: 0x%x", + i, j, relocationOffset, virtualAddress, symbolTableIndex, relocType, itemAddress)); + + DEBUG_COFF(AddUserDataReference(m_imageBase + relocationOffset, m_imageBase + itemAddress)); + + uint64_t symbolOffset = symbolTableAdjustedOffset + symbolTableIndex * sizeofCOFFSymbol; + + DEBUG_COFF(AddUserDataReference(m_imageBase + relocationOffset, m_imageBase + symbolOffset)); + + const auto symbol = GetSymbolByAddress(m_imageBase + symbolOffset); + if (!symbol) + { + LogWarn("COFF: skipping relocation at 0x%" PRIx64 " with invalid symbol address 0x%" PRIx64, + relocationOffset, m_imageBase + symbolOffset); + continue; + } + string symbolName = symbol->GetRawName(); + + auto valueOffset = !isBigCOFF ? offsetof(COFFSymbol16, value) : offsetof(COFFSymbol32, value); + auto valueFileOffset = header.coffSymbolTable + (symbolTableIndex * sizeofCOFFSymbol) + valueOffset; + reader.Seek(valueFileOffset); + + COFFSymbol coffSymbol; + memset(&coffSymbol, 0, sizeof(coffSymbol)); + coffSymbol.value = reader.Read32(); + if (!isBigCOFF) + coffSymbol.sectionNumber.i16 = reader.Read16(); + else + coffSymbol.sectionNumber.i32 = reader.Read32(); + coffSymbol.type = reader.Read16(); + coffSymbol.storageClass = reader.Read8(); + + DEBUG_COFF(AddUserDataReference(m_imageBase + itemAddress, m_imageBase + symbolOffset)); + DEBUG_COFF(LogDebug("COFF: CREATING RELOC SYMBOL REF from 0x%" PRIx64 " to 0x%" PRIx64 " for \"%s\"", m_imageBase + itemAddress, m_imageBase + symbolOffset, symbolName.c_str())); + + DefineAutoSymbol(new Symbol(DataSymbol, "__reloc(" + symbolName + ")", m_imageBase + relocationOffset)); + + BNRelocationInfo reloc; + memset(&reloc, 0, sizeof(reloc)); + reloc.external = false; + reloc.nativeType = relocType; + reloc.baseRelative = true; + reloc.base = m_imageBase; + reloc.address = itemAddress; + reloc.size = 4; + reloc.addend = 0; + reloc.pcRelative = false; + reloc.implicitAddend = false; + int sectionIndex = !isBigCOFF ? coffSymbol.sectionNumber.i16 : coffSymbol.sectionNumber.i32; + if (sectionIndex > 0) + reloc.sectionIndex = sectionIndex - 1; + else + reloc.sectionIndex = SIZE_MAX; + DEBUG_COFF(if (sectionIndex <= 0) LogDebug("COFF: sectionIndex <= 0 (%d) at 0x%" PRIx64 " for symbol at 0x%" PRIx64, sectionIndex, m_imageBase + relocationOffset, m_imageBase + symbolOffset)); + if (coffSymbol.storageClass == IMAGE_SYM_CLASS_EXTERNAL || coffSymbol.storageClass == IMAGE_SYM_CLASS_STATIC) + { + vector<BNRelocationInfo> relocs; + relocs.push_back(reloc); + relocHandler->GetRelocationInfo(this, m_arch, relocs); + reloc = relocs[0]; + if (sectionIndex > 0) + { + uint64_t relocTargetOffset = m_sections[reloc.sectionIndex].virtualAddress + coffSymbol.value; + + DEBUG_COFF(LogError("COFF: CREATING RELOC (%d) REF from 0x%" PRIx64 " to 0x%" PRIx64 " for %s", relocType, m_imageBase + itemAddress, m_imageBase + relocTargetOffset, symbolName.c_str())); + DEBUG_COFF(AddUserDataReference(m_imageBase + itemAddress, m_imageBase + relocTargetOffset)); + + DefineRelocation(m_arch, reloc, m_imageBase + relocTargetOffset, m_imageBase + reloc.address); + + DEBUG_COFF(AddUserDataReference(m_imageBase + relocTargetOffset, m_imageBase + itemAddress)); + DEBUG_COFF(LogError("COFF: DEFINED RELOCATION for 0x%" PRIx64 ":0x%" PRIx64 " to 0x%" PRIx64 " reloc type %#04x", reloc.base, reloc.address, m_imageBase + relocTargetOffset, reloc.nativeType)); + } + else if (coffSymbol.storageClass == IMAGE_SYM_CLASS_EXTERNAL) + { + DEBUG_COFF(LogDebug("COFF: EXTERNAL RELOCATION for 0x%" PRIx64 ":0x%" PRIx64 " reloc type %#04x", reloc.base, reloc.address, reloc.nativeType)); + reloc.external = true; + Ref<Symbol> targetSymbol; + reloc.size = m_is64 ? 8 : 4; + for (const auto& symbol : GetSymbols(m_imageBase + symbolOffset, reloc.size)) + { + string name = symbol->GetRawName(); + if (name.find("__symbol(") == 0 && name.back() == ')') + { + string symbolName = name.substr(strlen("__symbol(")); + symbolName = symbolName.substr(0, symbolName.size() - 1); + for (const auto& externSymbol : GetSymbolsByName(symbolName)) + { + auto type = externSymbol->GetType(); + if (type == ExternalSymbol || type == ImportedFunctionSymbol || type == ImportedDataSymbol || type == ImportAddressSymbol) + { + targetSymbol = externSymbol; + DefineRelocation(m_arch, reloc, targetSymbol, m_imageBase + reloc.address); + DEBUG_COFF(LogDebug("COFF: created external relocation at %#" PRIx64 " for %#" PRIx64 ": %s", m_imageBase + relocationOffset, m_imageBase + reloc.address, name.c_str())); + break; + } + } + } + if (targetSymbol) + break; + } + if (! targetSymbol) + { + // TODO: determine whether this is actually worth logging -- may only be happening for NB (non-based) relocations? + LogError("COFF: no defined external symbol found for relocation at %#" PRIx64 " for symbol %s", m_imageBase + relocationOffset, symbolName.c_str()); + } + } + } + } + } + } + } + } + catch (std::exception& e) + { + LogError("Failed to parse COFF relocations: %s\n", e.what()); + } + + // Add a symbol for the entry point + // if (entryPointAddress) + // DefineAutoSymbol(new Symbol(FunctionSymbol, "_start", m_imageBase + entryPointAddress)); + std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now(); + double t = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count() / 1000.0; + LogInfo("COFF parsing took %.3f seconds\n", t); + + return true; +} + + +uint64_t COFFView::RVAToFileOffset(uint64_t offset, bool except) +{ + for (auto& i : m_sections) + { + if ((offset >= i.virtualAddress) && + (offset < (i.virtualAddress + i.sizeOfRawData)) && (i.virtualSize != 0)) + { + uint64_t progOfs = offset - i.virtualAddress; + return i.pointerToRawData + progOfs; + } + } + + if (!except) + return offset; + + throw COFFFormatException("encountered invalid offset"); +} + + +uint32_t COFFView::GetRVACharacteristics(uint64_t offset) +{ + for (auto& i : m_sections) + { + if ((offset >= i.virtualAddress) && (offset < (i.virtualAddress + i.virtualSize)) && (i.virtualSize != 0)) + return i.characteristics; + } + return 0; +} + + +string COFFView::ReadString(uint64_t rva) +{ + uint64_t offset = RVAToFileOffset(rva); + string result; + char data[STRING_READ_CHUNK_SIZE]; + while (true) + { + size_t len = GetParentView()->Read(data, offset, STRING_READ_CHUNK_SIZE); + if (len == 0) + break; + + size_t i; + for (i = 0; i < len; i++) + { + if (data[i] == 0) + break; + } + + result += string(&data[0], &data[i]); + if (i < len) + break; + offset += len; + } + return result; +} + + +uint16_t COFFView::Read16(uint64_t rva) +{ + uint64_t ofs = RVAToFileOffset(rva); + BinaryReader reader(GetParentView(), LittleEndian); + reader.Seek(ofs); + return reader.Read16(); +} + + +uint32_t COFFView::Read32(uint64_t rva) +{ + uint64_t ofs = RVAToFileOffset(rva); + BinaryReader reader(GetParentView(), LittleEndian); + reader.Seek(ofs); + return reader.Read32(); +} + + +uint64_t COFFView::Read64(uint64_t rva) +{ + uint64_t ofs = RVAToFileOffset(rva); + BinaryReader reader(GetParentView(), LittleEndian); + reader.Seek(ofs); + return reader.Read64(); +} + + +void COFFView::AddCOFFSymbol(BNSymbolType type, const string& dll, const string& name, uint64_t addr, + BNSymbolBinding binding, uint64_t ordinal, TypeLibrary* lib) +{ + // If name is empty, symbol is not valid + if (name.size() == 0) + return; + + // Ensure symbol is within the executable + NameSpace nameSpace = GetInternalNameSpace(); + if (type == ExternalSymbol) + { + nameSpace = GetExternalNameSpace(); + } + else if (!IsValidOffset(m_imageBase + addr)) + { + return; + } + else if (dll.size()) + { + nameSpace = NameSpace(dll); + } + + if (!(type == ExternalSymbol || type == ImportedDataSymbol || type == ImportedFunctionSymbol)) + { + // Ensure symbol is within the executable + bool ok = false; + for (auto& i : m_sections) + { + if ((addr >= i.virtualAddress) && (addr < (i.virtualAddress + i.virtualSize))) + { + ok = true; + break; + } + } + if (!ok) + { + LogDebug("COFF: %s symbol %s at %#" PRIx64 " is not in any section", __func__, name.c_str(), addr); + return; + } + } + + Ref<Type> symbolTypeRef; + auto address = type == ExternalSymbol ? addr : m_imageBase + addr; + if (lib && ((type == ImportAddressSymbol) || (type == ImportedDataSymbol))) + { + QualifiedName n(name); + Ref<TypeLibrary> appliedLib = lib; + symbolTypeRef = ImportTypeLibraryObject(appliedLib, n); + if (symbolTypeRef && type != ExternalSymbol) + { + LogDebug("COFF: type library '%s' found hit for '%s'", lib->GetGuid().c_str(), name.c_str()); + RecordImportedObjectLibrary(GetDefaultPlatform(), m_imageBase + addr, appliedLib, n); + } + } + + // If name does not start with alphabetic character or symbol, prepend an underscore + string rawName = name; + if (!(((name[0] >= 'A') && (name[0] <= 'Z')) || + ((name[0] >= 'a') && (name[0] <= 'z')) || + (name[0] == '_') || (name[0] == '?') || (name[0] == '$') || (name[0] == '@'))) + rawName = "_" + name; + + string shortName = rawName; + string fullName = rawName; + + if (m_arch && name.size() > 0 && name[0] == '?') + { + QualifiedName demangleName; + Ref<Type> demangledType; + if (DemangleMS(m_arch, name, demangledType, demangleName, m_simplifyTemplates)) + { + shortName = demangleName.GetString(); + fullName = shortName + demangledType->GetStringAfterName(); + if (!symbolTypeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) + symbolTypeRef = demangledType; + } + else + { + // TODO: This is happening a lot, so figure out why the demangler can't handle symbols like "??_C@_0M@LLLPOAKG@hasChildren@" + // For now, disable the message because it's very noisy for some binaries + DEBUG_COFF(LogDebug("COFF: Failed to demangle: '%s'\n", name.c_str())); + } + } + + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), + new Symbol(type, shortName, fullName, rawName, address, binding, nameSpace, ordinal), symbolTypeRef); +} + + +uint64_t COFFView::PerformGetEntryPoint() const +{ + return m_imageBase + m_entryPoint; +} + + +size_t COFFView::PerformGetAddressSize() const +{ + return m_is64 ? 8 : 4; +} + + +COFFViewType::COFFViewType(): BinaryViewType("COFF", "COFF") +{ +} + + +Ref<BinaryView> COFFViewType::Create(BinaryView* data) +{ + try + { + return new COFFView(data); + } + catch (std::exception& e) + { + LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + + +Ref<BinaryView> COFFViewType::Parse(BinaryView* data) +{ + try + { + return new COFFView(data, true); + } + catch (std::exception& e) + { + LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + +bool COFFViewType::IsValidMachineType(uint16_t machineType) +{ + switch (machineType) + { + case IMAGE_FILE_MACHINE_AM33: + case IMAGE_FILE_MACHINE_AMD64: + case IMAGE_FILE_MACHINE_ARM: + case IMAGE_FILE_MACHINE_ARM64: + case IMAGE_FILE_MACHINE_ARMNT: + case IMAGE_FILE_MACHINE_EBC: + case IMAGE_FILE_MACHINE_I386: + case IMAGE_FILE_MACHINE_IA64: + case IMAGE_FILE_MACHINE_M32R: + case IMAGE_FILE_MACHINE_MIPS16: + case IMAGE_FILE_MACHINE_MIPSFPU: + case IMAGE_FILE_MACHINE_MIPSFPU16: + case IMAGE_FILE_MACHINE_POWERPC: + case IMAGE_FILE_MACHINE_POWERPCFP: + case IMAGE_FILE_MACHINE_R4000: + case IMAGE_FILE_MACHINE_RISCV32: + case IMAGE_FILE_MACHINE_RISCV64: + case IMAGE_FILE_MACHINE_RISCV128: + case IMAGE_FILE_MACHINE_SH3: + case IMAGE_FILE_MACHINE_SH3DSP: + case IMAGE_FILE_MACHINE_SH4: + case IMAGE_FILE_MACHINE_SH5: + case IMAGE_FILE_MACHINE_THUMB: + case IMAGE_FILE_MACHINE_WCEMIPSV2: + return true; + default: + return false; + } +} + +bool COFFViewType::IsSupportedMachineType(uint16_t machineType) +{ + switch (machineType) + { + case IMAGE_FILE_MACHINE_AMD64: + case IMAGE_FILE_MACHINE_ARM: + case IMAGE_FILE_MACHINE_ARM64: + case IMAGE_FILE_MACHINE_ARMNT: + case IMAGE_FILE_MACHINE_I386: + case IMAGE_FILE_MACHINE_THUMB: + return true; + default: + return false; + } +} + + +bool COFFViewType::IsTypeValidForData(BinaryView* data) +{ + COFFHeader header; + BinaryReader reader(data, LittleEndian); + if (!reader.TryRead(&header, sizeof(COFFHeader))) + return false; + if (!(COFFViewType::IsSupportedMachineType(header.machine) || (header.machine == IMAGE_FILE_MACHINE_UNKNOWN && COFFViewType::IsSupportedMachineType(((BigObj_COFFHeader*) &header)->machine)))) + return false; + return true; +} + + +Ref<Settings> COFFViewType::GetLoadSettingsForData(BinaryView* data) +{ + Ref<BinaryView> viewRef = Parse(data); + if (!viewRef || !viewRef->Init()) + { + LogError("View type '%s' could not be created", GetName().c_str()); + return nullptr; + } + + Ref<Settings> settings = GetDefaultLoadSettingsForData(viewRef); + + // specify default load settings that can be overridden + vector<string> overrides = {"loader.architecture", "loader.imageBase", "loader.platform"}; + if (!viewRef->IsRelocatable()) + settings->UpdateProperty("loader.imageBase", "message", "Note: File indicates image is not relocatable."); + + for (const auto& override : overrides) + { + if (settings->Contains(override)) + settings->UpdateProperty(override, "readOnly", false); + } + + // TODO: additional settings + + // register additional settings + // settings->RegisterSetting("loader.coff.processCfgTable", + // R"({ + // "title" : "Process PE Control Flow Guard Table", + // "type" : "boolean", + // "default" : true, + // "description" : "Add function starts sourced from the Control Flow Guard (CFG) table to the core for analysis." + // })"); + + // settings->RegisterSetting("loader.coff.processExceptionTable", + // R"({ + // "title" : "Process COFF Exception Handling Table", + // "type" : "boolean", + // "default" : true, + // "description" : "Add function starts sourced from the Exception Handling table (.pdata) to the core for analysis." + // })"); + + // settings->RegisterSetting("loader.coff.processSehTable", + // R"({ + // "title" : "Process COFF Structured Exception Handling Table", + // "type" : "boolean", + // "default" : true, + // "description" : "Add function starts sourced from the Structured Exception Handling (SEH) table to the core for analysis." + // })"); + + + return settings; +} |
