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 | |
| parent | 47894a1ce20090849ad304c68e6c0b889665eda3 (diff) | |
Move view modules into the API repo
Diffstat (limited to 'view/pe')
| -rw-r--r-- | view/pe/.gitignore | 8 | ||||
| -rw-r--r-- | view/pe/CMakeLists.txt | 33 | ||||
| -rw-r--r-- | view/pe/LICENSE | 13 | ||||
| -rw-r--r-- | view/pe/README.md | 27 | ||||
| -rw-r--r-- | view/pe/coffview.cpp | 1689 | ||||
| -rw-r--r-- | view/pe/coffview.h | 372 | ||||
| -rw-r--r-- | view/pe/peview.cpp | 2970 | ||||
| -rw-r--r-- | view/pe/peview.h | 479 |
8 files changed, 5591 insertions, 0 deletions
diff --git a/view/pe/.gitignore b/view/pe/.gitignore new file mode 100644 index 00000000..d4c4c4dc --- /dev/null +++ b/view/pe/.gitignore @@ -0,0 +1,8 @@ +CMakeFiles +CMakeCache.txt +cmake_install.cmake +Makefile +api +build +libview_* +view_*.dll diff --git a/view/pe/CMakeLists.txt b/view/pe/CMakeLists.txt new file mode 100644 index 00000000..bc113b1e --- /dev/null +++ b/view/pe/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(view_pe) + +if(NOT BN_INTERNAL_BUILD) + add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api) +endif() + +file(GLOB SOURCES + *.cpp + *.h) + +if(DEMO) + add_library(view_pe STATIC ${SOURCES}) +else() + add_library(view_pe SHARED ${SOURCES}) +endif() + +target_link_libraries(view_pe binaryninjaapi) + +set_target_properties(view_pe PROPERTIES + CXX_STANDARD 17 + CXX_VISIBILITY_PRESET hidden + CXX_STANDARD_REQUIRED ON + VISIBILITY_INLINES_HIDDEN ON + POSITION_INDEPENDENT_CODE ON) + +if(BN_INTERNAL_BUILD) + plugin_rpath(view_pe) + set_target_properties(view_pe PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} + RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) +endif() diff --git a/view/pe/LICENSE b/view/pe/LICENSE new file mode 100644 index 00000000..265883c5 --- /dev/null +++ b/view/pe/LICENSE @@ -0,0 +1,13 @@ +Copyright 2021 Vector 35 Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/view/pe/README.md b/view/pe/README.md new file mode 100644 index 00000000..42e2ffe2 --- /dev/null +++ b/view/pe/README.md @@ -0,0 +1,27 @@ +# view-pe +This is the PE/COFF view plugin that ships with Binary Ninja. + +## Issues + +Issues for this repository have been disabled. Please file an issue for this repository at https://github.com/Vector35/binaryninja-api/issues. All previously existing issues for this repository have been transferred there as well. + +## Building + +Building the architecture plugin requires `cmake` 3.9 or above. You will also need the +[Binary Ninja API source](https://github.com/Vector35/binaryninja-api). + +Run `cmake`. This can be done either from a separate build directory or from the source +directory. Once that is complete, run `make` in the build directory to compile the plugin. + +The plugin can be found in the root of the build directory as `libview_pe.so`, +`libview_pe.dylib` or `view_pe.dll` depending on your platform. + +To install the plugin, first launch Binary Ninja and uncheck the "PE/COFF view plugin" +option in the "Core Plugins" section. This will cause Binary Ninja to stop loading the +bundled plugin so that its replacement can be loaded. Once this is complete, you can copy +the plugin into the user plugins directory (you can locate this by using the "Open Plugin Folder" +option in the Binary Ninja UI). + +**Do not replace the view plugin in the Binary Ninja install directory. This will be overwritten +every time there is a Binary Ninja update. Use the above process to ensure that updates do not +automatically uninstall your custom build.** 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; +} diff --git a/view/pe/coffview.h b/view/pe/coffview.h new file mode 100644 index 00000000..b1cf7780 --- /dev/null +++ b/view/pe/coffview.h @@ -0,0 +1,372 @@ +#pragma once + +#include "binaryninjaapi.h" +#include "peview.h" +#include <exception> + +#ifdef WIN32 +#pragma warning(disable: 4005) +#endif + +#define IMAGE_SYM_TYPE_NULL 0 // No type information or unknown base type. +#define IMAGE_SYM_TYPE_VOID 1 // Used with void pointers and functions. +#define IMAGE_SYM_TYPE_CHAR 2 // A character (signed byte). +#define IMAGE_SYM_TYPE_SHORT 3 // A 2-byte signed integer. +#define IMAGE_SYM_TYPE_INT 4 // A natural integer type on the target. +#define IMAGE_SYM_TYPE_LONG 5 // A 4-byte signed integer. +#define IMAGE_SYM_TYPE_FLOAT 6 // A 4-byte floating-point number. +#define IMAGE_SYM_TYPE_DOUBLE 7 // An 8-byte floating-point number. +#define IMAGE_SYM_TYPE_STRUCT 8 // A structure. +#define IMAGE_SYM_TYPE_UNION 9 // An union. +#define IMAGE_SYM_TYPE_ENUM 10 // An enumerated type. +#define IMAGE_SYM_TYPE_MOE 11 // A member of enumeration (a specific value). +#define IMAGE_SYM_TYPE_BYTE 12 // A byte; unsigned 1-byte integer. +#define IMAGE_SYM_TYPE_WORD 13 // A word; unsigned 2-byte integer. +#define IMAGE_SYM_TYPE_UINT 14 // An unsigned integer of natural size. +#define IMAGE_SYM_TYPE_DWORD 15 // An unsigned 4-byte integer. + +#define MS_IMAGE_SYM_TYPE_FUNCTION 0x20 // Microsoft-specific + +#define IMAGE_REL_I386_ABSOLUTE 0x0000 +#define IMAGE_REL_I386_DIR16 0x0001 +#define IMAGE_REL_I386_REL16 0x0002 +#define IMAGE_REL_I386_DIR32 0x0006 +#define IMAGE_REL_I386_DIR32NB 0x0007 +#define IMAGE_REL_I386_SEG12 0x0009 +#define IMAGE_REL_I386_SECTION 0x000A +#define IMAGE_REL_I386_SECREL 0x000B +#define IMAGE_REL_I386_TOKEN 0x000C +#define IMAGE_REL_I386_SECREL7 0x000D +#define IMAGE_REL_I386_REL32 0x0014 + +#define IMAGE_REL_AMD64_ABSOLUTE 0x0000 +#define IMAGE_REL_AMD64_ADDR64 0x0001 +#define IMAGE_REL_AMD64_ADDR32 0x0002 +#define IMAGE_REL_AMD64_ADDR32NB 0x0003 +#define IMAGE_REL_AMD64_REL32 0x0004 +#define IMAGE_REL_AMD64_REL32_1 0x0005 +#define IMAGE_REL_AMD64_REL32_2 0x0006 +#define IMAGE_REL_AMD64_REL32_3 0x0007 +#define IMAGE_REL_AMD64_REL32_4 0x0008 +#define IMAGE_REL_AMD64_REL32_5 0x0009 +#define IMAGE_REL_AMD64_SECTION 0x000A +#define IMAGE_REL_AMD64_SECREL 0x000B +#define IMAGE_REL_AMD64_SECREL7 0x000C +#define IMAGE_REL_AMD64_TOKEN 0x000D +#define IMAGE_REL_AMD64_SREL32 0x000E +#define IMAGE_REL_AMD64_PAIR 0x000F +#define IMAGE_REL_AMD64_SSPAN32 0x0010 + +#define IMAGE_REL_ARM_ABSOLUTE 0x0000 +#define IMAGE_REL_ARM_ADDR32 0x0001 +#define IMAGE_REL_ARM_ADDR32NB 0x0002 +#define IMAGE_REL_ARM_BRANCH24 0x0003 +#define IMAGE_REL_ARM_BRANCH11 0x0004 +#define IMAGE_REL_ARM_BLX24 0x0008 +#define IMAGE_REL_ARM_BLX11 0x0009 +#define IMAGE_REL_ARM_REL32 0x000A +#define IMAGE_REL_ARM_SECTION 0x000E +#define IMAGE_REL_ARM_SECREL 0x000F +#define IMAGE_REL_ARM_MOV32 0x0010 +#define IMAGE_REL_THUMB_MOV32 0x0011 +#define IMAGE_REL_THUMB_BRANCH20 0x0012 +#define IMAGE_REL_THUMB_UNUSED 0x0013 +#define IMAGE_REL_THUMB_BRANCH24 0x0014 +#define IMAGE_REL_THUMB_BLX23 0x0015 +#define IMAGE_REL_ARM_PAIR 0x0016 + +// The following names for armv7/thumb2 relocations are from LLVM source, +// but are commented out in favor of the names defined above, +// which correspond to Microsoft PE Format documentation: +// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#type-indicators +// #define IMAGE_REL_ARM_ABSOLUTE 0x0000 +// #define IMAGE_REL_ARM_ADDR32 0x0001 +// #define IMAGE_REL_ARM_ADDR32NB 0x0002 +// #define IMAGE_REL_ARM_BRANCH24 0x0003 +// #define IMAGE_REL_ARM_BRANCH11 0x0004 +// #define IMAGE_REL_ARM_TOKEN 0x0005 +// #define IMAGE_REL_ARM_BLX24 0x0008 +// #define IMAGE_REL_ARM_BLX11 0x0009 +// #define IMAGE_REL_ARM_REL32 0x000A +// #define IMAGE_REL_ARM_SECTION 0x000E +// #define IMAGE_REL_ARM_SECREL 0x000F +// #define IMAGE_REL_ARM_MOV32A 0x0010 +// #define IMAGE_REL_ARM_MOV32T 0x0011 +// #define IMAGE_REL_ARM_BRANCH20T 0x0012 +// #define IMAGE_REL_ARM_BRANCH24T 0x0014 +// #define IMAGE_REL_ARM_BLX23T 0x0015 +// #define IMAGE_REL_ARM_PAIR 0x0016 + +#define IMAGE_REL_ARM64_ABSOLUTE 0x0000 +#define IMAGE_REL_ARM64_ADDR32 0x0001 +#define IMAGE_REL_ARM64_ADDR32NB 0x0002 +#define IMAGE_REL_ARM64_BRANCH26 0x0003 +#define IMAGE_REL_ARM64_PAGEBASE_REL21 0x0004 +#define IMAGE_REL_ARM64_REL21 0x0005 +#define IMAGE_REL_ARM64_PAGEOFFSET_12A 0x0006 +#define IMAGE_REL_ARM64_PAGEOFFSET_12L 0x0007 +#define IMAGE_REL_ARM64_SECREL 0x0008 +#define IMAGE_REL_ARM64_SECREL_LOW12A 0x0009 +#define IMAGE_REL_ARM64_SECREL_HIGH12A 0x000A +#define IMAGE_REL_ARM64_SECREL_LOW12L 0x000B +#define IMAGE_REL_ARM64_TOKEN 0x000C +#define IMAGE_REL_ARM64_SECTION 0x000D +#define IMAGE_REL_ARM64_ADDR64 0x000E +#define IMAGE_REL_ARM64_BRANCH19 0x000F +#define IMAGE_REL_ARM64_BRANCH14 0x0010 +#define IMAGE_REL_ARM64_REL32 0x0011 + +namespace BinaryNinja +{ + class COFFFormatException: public std::exception + { + std::string m_message; + + public: + COFFFormatException(const std::string& msg = "invalid format"): m_message(msg) {} + virtual const char* what() const NOEXCEPT { return m_message.c_str(); } + }; + + #pragma pack(push, 2) + struct COFFHeader + { + uint16_t machine; + uint16_t sectionCount; + uint32_t timestamp; + uint32_t coffSymbolTable; + uint32_t coffSymbolCount; + uint16_t optionalHeaderSize; + uint16_t characteristics; + }; + + // Use this for sizeof + struct COFFSectionHeader + { + uint8_t name[8]; + union + { + uint32_t virtualSize; + uint32_t physicalAddress; + }; + uint32_t virtualAddress; + uint32_t sizeOfRawData; + uint32_t pointerToRawData; + uint32_t pointerToRelocs; + uint32_t pointerToLineNumbers; + uint16_t relocCount; + uint16_t lineNumberCount; + uint32_t characteristics; + }; + + // Can only find reference to this alernate COFF header in the LLVM source and docs: + // https://llvm.org/doxygen/structllvm_1_1COFF_1_1BigObjHeader.html + // https://github.com/llvm/llvm-project/commit/44f51e511374f467d70c37324a8e5bbf2ea270d8#diff-c8c9ae93373622c2303ca157ecdc8bf16204567b57e391381ed4740e0c38c82aR67-R82 + struct BigObj_COFFHeader + { + uint16_t sig1; ///< Must be IMAGE_FILE_MACHINE_UNKNOWN (0). + uint16_t sig2; ///< Must be 0xFFFF. + uint16_t version; + uint16_t machine; + uint32_t timestamp; + uint8_t UUID[16]; + uint32_t unused1; + uint32_t unused2; + uint32_t unused3; + uint32_t unused4; + uint32_t sectionCount; + uint32_t coffSymbolTable; + uint32_t coffSymbolCount; + }; + + struct COFFSection + { + std::string name; + union + { + uint32_t virtualSize; + uint32_t physicalAddress; + }; + uint32_t virtualAddress; + uint32_t sizeOfRawData; + uint32_t pointerToRawData; + uint32_t pointerToRelocs; + uint32_t pointerToLineNumbers; + uint16_t relocCount; + uint16_t lineNumberCount; + uint32_t characteristics; + }; + + + template<typename SectionCountType> struct _COFFSymbol + { + union + { + char shortName[8]; + struct { + uint32_t zeroes; + uint32_t offset; + } longName; + } name; + uint32_t value; + // 1-based index into section table + SectionCountType sectionNumber; + uint16_t type; + uint8_t storageClass; + uint8_t numberOfAuxSymbols; + }; + + using COFFSymbol16 = _COFFSymbol<uint16_t>; + using COFFSymbol32 = _COFFSymbol<uint32_t>; + + struct COFFSymbol + { + union + { + char shortName[8]; + struct { + uint32_t zeroes; + uint32_t offset; + } longName; + } name; + uint32_t value; + // 1-based index into section table + union { int16_t i16; int32_t i32; } sectionNumber; + uint16_t type; + uint8_t storageClass; + uint8_t numberOfAuxSymbols; + }; + + struct COFFAuxFunctionDefinition + { + uint32_t tagIndex; + uint32_t totalSize; + uint32_t pointerToLineNumber; + uint32_t pointerToNextFunction; + uint8_t unused[2]; + }; + + struct COFFAux_bf_And_ef_Symbol + { + uint8_t unused0[4]; + uint16_t lineNumber; + uint8_t unused6[6]; + uint32_t pointerToNextFunction; + uint8_t unused16[2]; + }; + + struct COFFAuxWeakExternal + { + uint32_t tagIndex; + uint32_t characteristics; + uint8_t unused[10]; + }; + + struct COFFAuxFile + { + uint8_t fileName[18]; + }; + + #pragma pack(push, 1) + struct COFFAuxSectionDefinition + { + uint32_t length; + uint16_t numberOfRelocations; + uint16_t numberOfLineNumbers; + uint32_t checkSum; + uint16_t number; + uint8_t selection; + union { + uint8_t unused3[3]; + struct + { + uint8_t unused; + uint16_t numberHighPart; + }; + }; + }; + #pragma pack(pop) + + union COFFAuxSymbolRecord + { + COFFAuxFunctionDefinition functionDefinition; + COFFAux_bf_And_ef_Symbol bf_And_ef_Symbol; + COFFAuxWeakExternal weakExternal; + COFFAuxFile file; + COFFAuxSectionDefinition sectionDefinition; + }; + + struct COFFRelocation + { + uint32_t virtualAddress; + uint32_t symbolTableIndex; + // union + // { + // enum x64_coff_reloc_type x64_type; + // enum arm_coff_reloc_type arm_type; + // enum x86_coff_reloc_type x86_type; + uint16_t type; + // }; + // COFFRelocation(uint32_t va, uint32_t i, uint16_t t) : virtualAddress(va), symbolTableIndex(i), type(t) {} + }; + + struct COFF_PdataFunctionTableEntry + { + uint32_t beginAddress; + uint32_t endAddress; + uint32_t unwindInformation; + }; + + #pragma pack(pop) + + class COFFView : public BinaryView + { + bool m_parseOnly; + uint64_t m_entryPoint; + + uint64_t m_imageBase; + uint32_t m_sizeOfHeaders; + std::vector<COFFSection> m_sections; + std::vector<COFFRelocation> m_relocs; + Ref<Architecture> m_arch; + bool m_is64; + bool m_extractMangledTypes; + bool m_simplifyTemplates; + bool m_relocatable = false; + + virtual uint64_t PerformGetEntryPoint() const override; + + virtual bool PerformIsExecutable() const override { return true; } + virtual BNEndianness PerformGetDefaultEndianness() const override { return LittleEndian; } + virtual bool PerformIsRelocatable() const override { return m_relocatable; } + virtual size_t PerformGetAddressSize() const override; + + uint64_t RVAToFileOffset(uint64_t rva, bool except = true); + uint32_t GetRVACharacteristics(uint64_t rva); + std::string ReadString(uint64_t rva); + uint16_t Read16(uint64_t rva); + uint32_t Read32(uint64_t rva); + uint64_t Read64(uint64_t rva); + void AddCOFFSymbol(BNSymbolType type, const std::string& dll, const std::string& name, uint64_t addr, + BNSymbolBinding binding = NoBinding, uint64_t ordinal = 0, TypeLibrary* lib = nullptr); + // void COFFView::GetRelocs(BinaryReader& reader, const vector<COFFSection>& sections, vector<COFFRelocation>& result, const QualifiedName& coffRelocTypeName, const map<uint64_t, string>& symbolNames); + + public: + COFFView(BinaryView* data, bool parseOnly = false); + + virtual bool Init() override; + }; + + class COFFViewType: public BinaryViewType + { + public: + COFFViewType(); + virtual Ref<BinaryView> Create(BinaryView* data) override; + virtual Ref<BinaryView> Parse(BinaryView* data) override; + virtual bool IsTypeValidForData(BinaryView* data) override; + virtual Ref<Settings> GetLoadSettingsForData(BinaryView* data) override; + static bool IsValidMachineType(uint16_t machineType); + static bool IsSupportedMachineType(uint16_t machineType); + }; + + void InitCOFFViewType(); +} diff --git a/view/pe/peview.cpp b/view/pe/peview.cpp new file mode 100644 index 00000000..794cb5d6 --- /dev/null +++ b/view/pe/peview.cpp @@ -0,0 +1,2970 @@ +#include <algorithm> +#include <cstring> +#include <cctype> +#include <string.h> +#include <inttypes.h> +#include <iomanip> +#include <mutex> +#include <sstream> +#include <type_traits> +#include <utility> +#include "peview.h" +#include "coffview.h" + +#define STRING_READ_CHUNK_SIZE 32 + +using namespace BinaryNinja; +using namespace std; + + +static PEViewType* g_peViewType = nullptr; +static const char* imageDirName[] = { "exportTable", "importTable", "resourceTable", "exceptionTable", "certificateTable", "baseRelocationTable", "debug", "architecture", "globalPtr", "tlsTable", "loadConfigTable", "boundImport", "iat", "delayImportDescriptor", "clrRuntimeHeader", "reserved"}; + +void BinaryNinja::InitPEViewType() +{ + static PEViewType type; + BinaryViewType::Register(&type); + g_peViewType = &type; +} + +// String representation of Rich header object types +static const string kProdId_C = "[ C ]"; +static const string kProdId_CPP = "[C++]"; +static const string kProdId_RES = "[RES]"; +static const string kProdId_IMP = "[IMP]"; +static const string kProdId_EXP = "[EXP]"; +static const string kProdId_ASM = "[ASM]"; +static const string kProdId_LNK = "[LNK]"; +static const string kProdId_UNK = "[ ? ]"; + +static const std::map<uint16_t, string> ProductIdMap = { + {0x0000, kProdId_UNK}, + {0x0002, kProdId_IMP}, + {0x0004, kProdId_LNK}, + {0x0006, kProdId_RES}, + {0x000A, kProdId_C}, + {0x000B, kProdId_CPP}, + {0x000F, kProdId_ASM}, + {0x0015, kProdId_C}, + {0x0016, kProdId_CPP}, + {0x0019, kProdId_IMP}, + {0x001C, kProdId_C}, + {0x001D, kProdId_CPP}, + {0x003D, kProdId_LNK}, + {0x003F, kProdId_EXP}, + {0x0040, kProdId_ASM}, + {0x0045, kProdId_RES}, + {0x005A, kProdId_LNK}, + {0x005C, kProdId_EXP}, + {0x005D, kProdId_IMP}, + {0x005E, kProdId_RES}, + {0x005F, kProdId_C}, + {0x0060, kProdId_CPP}, + {0x006D, kProdId_C}, + {0x006E, kProdId_CPP}, + {0x0078, kProdId_LNK}, + {0x007A, kProdId_EXP}, + {0x007B, kProdId_IMP}, + {0x007C, kProdId_RES}, + {0x007D, kProdId_ASM}, + {0x0083, kProdId_C}, + {0x0084, kProdId_CPP}, + {0x0091, kProdId_LNK}, + {0x0092, kProdId_EXP}, + {0x0093, kProdId_IMP}, + {0x0094, kProdId_RES}, + {0x0095, kProdId_ASM}, + {0x009A, kProdId_RES}, + {0x009B, kProdId_EXP}, + {0x009C, kProdId_IMP}, + {0x009D, kProdId_LNK}, + {0x009E, kProdId_ASM}, + {0x00AA, kProdId_C}, + {0x00AB, kProdId_CPP}, + {0x00C9, kProdId_RES}, + {0x00CA, kProdId_EXP}, + {0x00CB, kProdId_IMP}, + {0x00CC, kProdId_LNK}, + {0x00CD, kProdId_ASM}, + {0x00CE, kProdId_C}, + {0x00CF, kProdId_CPP}, + {0x00DB, kProdId_RES}, + {0x00DC, kProdId_EXP}, + {0x00DD, kProdId_IMP}, + {0x00DE, kProdId_LNK}, + {0x00DF, kProdId_ASM}, + {0x00E0, kProdId_C}, + {0x00E1, kProdId_CPP}, + {0x00FF, kProdId_RES}, + {0x0100, kProdId_EXP}, + {0x0101, kProdId_IMP}, + {0x0102, kProdId_LNK}, + {0x0103, kProdId_ASM}, + {0x0104, kProdId_C}, + {0x0105, kProdId_CPP} +}; + + +// Mapping of Rich header build number to version strings +static const std::map<uint16_t, const string> ProductMap = { + // Source: https://github.com/dishather/richprint/blob/master/comp_id.txt + {0x0000, "Imported Functions"}, + {0x0684, "VS97 v5.0 SP3 cvtres 5.00.1668"}, + {0x06B8, "VS98 v6.0 cvtres build 1720"}, + {0x06C8, "VS98 v6.0 SP6 cvtres build 1736"}, + {0x1C87, "VS97 v5.0 SP3 link 5.10.7303"}, + {0x5E92, "VS2015 v14.0 UPD3 build 24210"}, + {0x5E95, "VS2015 UPD3 build 24213"}, + + // http://bytepointer.com/articles/the_microsoft_rich_header.htm + {0x0BEC, "VS2003 v7.1 Free Toolkit .NET build 3052"}, + {0x0C05, "VS2003 v7.1 .NET build 3077"}, + {0x0FC3, "VS2003 v7.1 | Windows Server 2003 SP1 DDK build 4035"}, + {0x1C83, "MASM 6.13.7299"}, + {0x178E, "VS2003 v7.1 SP1 .NET build 6030"}, + {0x1FE8, "VS98 v6.0 RTM/SP1/SP2 build 8168"}, + {0x1FE9, "VB 6.0/SP1/SP2 build 8169"}, + {0x20FC, "MASM 6.14.8444"}, + {0x20FF, "VC++ 6.0 SP3 build 8447"}, + {0x212F, "VB 6.0 SP3 build 8495"}, + {0x225F, "VS 6.0 SP4 build 8799"}, + {0x2263, "MASM 6.15.8803"}, + {0x22AD, "VB 6.0 SP4 build 8877"}, + {0x2304, "VB 6.0 SP5 build 8964"}, + {0x2306, "VS 6.0 SP5 build 8966"}, + // {0x2346, "MASM 6.15.9030 (VS.NET 7.0 BETA 1)"}, + {0x2346, "VS 7.0 2000 Beta 1 build 9030"}, + {0x2354, "VS 6.0 SP5 Processor Pack build 9044"}, + {0x2426, "VS2001 v7.0 Beta 2 build 9254"}, + {0x24FA, "VS2002 v7.0 .NET build 9466"}, + {0x2636, "VB 6.0 SP6 / VC++ build 9782"}, + {0x26E3, "VS2002 v7.0 SP1 build 9955"}, + {0x520D, "VS2013 v12.[0,1] build 21005"}, + {0x521E, "VS2008 v9.0 build 21022"}, + {0x56C7, "VS2015 v14.0 build 22215"}, + {0x59F2, "VS2015 v14.0 build 23026"}, + {0x5BD2, "VS2015 v14.0 UPD1 build 23506"}, + {0x5D10, "VS2015 v14.0 UPD2 build 23824"}, + {0x5E97, "VS2015 v14.0 UPD3.1 build 24215"}, + {0x7725, "VS2013 v12.0 UPD2 build 30501"}, + {0x766F, "VS2010 v10.0 build 30319"}, + {0x7809, "VS2008 v9.0 SP1 build 30729"}, + {0x797D, "VS2013 v12.0 UPD4 build 31101"}, + {0x9D1B, "VS2010 v10.0 SP1 build 40219"}, + {0x9EB5, "VS2013 v12.0 UPD5 build 40629"}, + {0xC497, "VS2005 v8.0 (Beta) build 50327"}, + {0xC627, "VS2005 v8.0 | VS2012 v11.0 build 50727"}, + {0xC751, "VS2012 v11.0 Nov CTP build 51025"}, + {0xC7A2, "VS2012 v11.0 UPD1 build 51106"}, + {0xEB9B, "VS2012 v11.0 UPD2 build 60315"}, + {0xECC2, "VS2012 v11.0 UPD3 build 60610"}, + {0xEE66, "VS2012 v11.0 UPD4 build 61030"}, + {0x5E9A, "VS2015 v14.0 build 24218"}, + {0x61BB, "VS2017 v14.1 build 25019"}, + + // https://dev.to/yumetodo/list-of-mscver-and-mscfullver-8nd + {0x2264, "VS 6 [SP5,SP6] build 8804"}, + {0x23D8, "Windows XP SP1 DDK"}, + {0x0883, "Windows Server 2003 DDK"}, + {0x08F4, "VS2003 v7.1 .NET Beta build 2292"}, + {0x9D76, "Windows Server 2003 SP1 DDK (for AMD64)"}, + {0x9E9F, "VS2005 v8.0 Beta 1 build 40607"}, + {0xC427, "VS2005 v8.0 Beta 2 build 50215"}, + {0xC490, "VS2005 v8.0 build 50320"}, + {0x50E2, "VS2008 v9.0 Beta 2 build 20706"}, + {0x501A, "VS2010 v10.0 Beta 1 build 20506"}, + {0x520B, "VS2010 v10.0 Beta 2 build 21003"}, + {0x5089, "VS2013 v12.0 Preview build 20617"}, + {0x515B, "VS2013 v12.0 RC build 20827"}, + {0x527A, "VS2013 v12.0 Nov CTP build 21114"}, + {0x7674, "VS2013 v12.0 UPD2 RC build 30324"}, + {0x63A3, "VS2017 v15.3.3 build 25507"}, + {0x63C6, "VS2017 v15.4.4 build 25542"}, + {0x63CB, "VS2017 v15.4.5 build 25547"}, + + // https://walbourn.github.io/visual-studio-2015-update-2/ + {0x5D6E, "VS2015 v14.0 UPD2 build 23918"}, + + // https://walbourn.github.io/visual-studio-2017/ + {0x61B9, "VS2017 v15.[0,1] build 25017"}, + {0x63A2, "VS2017 v15.2 build 25019"}, + + // https://walbourn.github.io/vs-2017-15-5-update/ + {0x64E6, "VS2017 v15 build 25830"}, + {0x64E7, "VS2017 v15.5.2 build 25831"}, + {0x64EA, "VS2017 v15.5.[3,4] build 25834"}, + {0x64EB, "VS2017 v15.5.[5,6,7] build 25835"}, + + // https://walbourn.github.io/vs-2017-15-6-update/ + {0x6610, "VS2017 v15.6.[0,1,2] build 26128"}, + {0x6611, "VS2017 v15.6.[3,4] build 26129"}, + {0x6613, "VS2017 v15.6.6 build 26131"}, + {0x6614, "VS2017 v15.6.7 build 26132"}, + + // https://devblogs.microsoft.com/visualstudio/visual-studio-2017-update/ + {0x6723, "VS2017 v15.1 build 26403"}, + + // https://walbourn.github.io/vs-2017-15-7-update/ + {0x673C, "VS2017 v15.7.[0,1] build 26428"}, + {0x673D, "VS2017 v15.7.2 build 26429"}, + {0x673E, "VS2017 v15.7.3 build 26430"}, + {0x673F, "VS2017 v15.7.4 build 26431"}, + {0x6741, "VS2017 v15.7.5 build 26433"}, + + // https://walbourn.github.io/visual-studio-2019/ + {0x6B74, "VS2019 v16.0.0 build 27508"}, + + // https://walbourn.github.io/vs-2017-15-8-update/ + {0x6866, "VS2017 v15.8.0 build 26726"}, + {0x6869, "VS2017 v15.8.4 build 26729"}, + {0x686A, "VS2017 v15.8.9 build 26730"}, + {0x686C, "VS2017 v15.8.5 build 26732"}, + + // https://walbourn.github.io/vs-2017-15-9-update/ + {0x698F, "VS2017 v15.9.[0,1] build 27023"}, + {0x6990, "VS2017 v15.9.2 build 27024"}, + {0x6991, "VS2017 v15.9.4 build 27025"}, + {0x6992, "VS2017 v15.9.5 build 27026"}, + {0x6993, "VS2017 v15.9.7 build 27027"}, + {0x6996, "VS2017 v15.9.11 build 27030"}, + {0x6997, "VS2017 v15.9.12 build 27031"}, + {0x6998, "VS2017 v15.9.14 build 27032"}, + {0x699A, "VS2017 v15.9.16 build 27034"}, + + // https://walbourn.github.io/vs-2019-update-3/ + {0x6DC9, "VS2019 v16.3.2 UPD3 build 28105"}, + + // https://walbourn.github.io/visual-studio-2013-update-3/ + {0x7803, "VS2013 v12.0 UPD3 build 30723"}, + + // experimentation + {0x685B, "VS2017 v15.8.? build 26715"}, + + {27508, "VS2019 v16.0.0 build 27508"}, + + // https://walbourn.github.io/vs-2019-update-1/ + {27702, "VS2019 v16.1.2 build 27702"}, + + // https://walbourn.github.io/vs-2019-update-2/ + {27905, "VS2019 v16.2.3 build 27905"}, + + // https://walbourn.github.io/vs-2019-update-3/ + {28105, "VS2019 v16.3.2 build 28105"}, + + // https://walbourn.github.io/vs-2019-update-4/ + {28314, "VS2019 v16.4.0 build 28314"}, + {28315, "VS2019 v16.4.3 build 28315"}, + {28316, "VS2019 v16.4.4 build 28316"}, + {28319, "VS2019 v16.4.6 build 28319"}, + + // https://walbourn.github.io/vs-2019-update-5/ + {28610, "VS2019 v16.5.0 build 28610"}, + {28611, "VS2019 v16.5.1 build 28611"}, + {28612, "VS2019 v16.5.2 build 28612"}, + {28614, "VS2019 v16.5.4 build 28614"}, + + // https://walbourn.github.io/vs-2019-update-6/ + {28805, "VS2019 v16.6.0 build 28805"}, + {28806, "VS2019 v16.6.1 build 28806"}, + + // https://walbourn.github.io/vs-2019-update-7/ + {29110, "VS2019 v16.7.0 build 29110"}, + {29111, "VS2019 v16.7.1 build 29111"}, + {29112, "VS2019 v16.7.5 build 29112"}, + + // https://walbourn.github.io/vs-2019-update-8/ + {29333, "VS2019 v16.8.0 build 29333"}, + {29334, "VS2019 v16.8.2 build 29334"}, + {29335, "VS2019 v16.8.3 build 29335"}, + {29336, "VS2019 v16.8.4 build 29336"}, + {29337, "VS2019 v16.8.5 build 29337"}, + + // https://walbourn.github.io/vs-2019-update-9/ + {29910, "VS2019 v16.9.0 build 29910"}, + {29911, "VS2019 v16.9.1 build 29911"}, + {29912, "VS2019 v16.9.2 build 29912"}, + {29913, "VS2019 v16.9.3 build 29913"}, + {29914, "VS2019 v16.9.4 build 29914"}, + {29915, "VS2019 v16.9.5 build 29915"}, + + // https://walbourn.github.io/vs-2019-update-10/ + {30037, "VS2019 v16.10.0 build 30037"}, + {30038, "VS2019 v16.10.2 build 30038"}, + {30040, "VS2019 v16.10.4 build 30040"}, + + // https://walbourn.github.io/vs-2019-update-11/ + {30133, "VS2019 v16.11.0 build 30133"}, + {30136, "VS2019 v16.11.4 build 30136"}, + {30137, "VS2019 v16.11.6 build 30137"}, + {30138, "VS2019 v16.11.8 build 30138"}, + {30139, "VS2019 v16.11.9 build 30139"}, + {30140, "VS2019 v16.11.10 build 30140"}, + {30141, "VS2019 v16.11.11 build 30141"}, + {30142, "VS2019 v16.11.12 build 30142"}, + {30143, "VS2019 v16.11.13 build 30143"}, + {30145, "VS2019 v16.11.14 build 30145"}, + {30146, "VS2019 v16.11.16 build 30146"}, + {30147, "VS2019 v16.11.19 build 30147"}, + {30148, "VS2019 v16.11.24 build 30148"}, + + // https://walbourn.github.io/visual-studio-2022/ + {30705, "VS2022 17.0.0 build 30705"}, + {30706, "VS2022 17.0.2 build 30706"}, + {30709, "VS2022 17.0.5 build 30709"}, + + // https://walbourn.github.io/vs-2022-update-1/ + {31104, "VS2022 17.1.0 build 31104"}, + {31105, "VS2022 17.1.2 build 31105"}, + {31106, "VS2022 17.1.4 build 31106"}, + {31107, "VS2022 17.1.6 build 31107"}, + + // https://walbourn.github.io/vs-2022-update-2/ + {31328, "VS2022 v17.2.0 build 31328"}, + {31329, "VS2022 v17.2.1 build 31329"}, + {31332, "VS2022 v17.2.5 build 31332"}, + + // https://walbourn.github.io/vs-2022-update-3/ + {31629, "VS2022 v17.3.0 build 31629"}, + {31630, "VS2022 v17.3.4 build 31630"}, + + // https://walbourn.github.io/vs-2022-update-4/ + {31933, "VS2022 17.4.0 build 31933"}, + {31935, "VS2022 17.4.2 build 31935"}, + {31937, "VS2022 17.4.3 build 31937"}, + {31942, "VS2022 17.4.5 build 31942"}, + + // https://walbourn.github.io/vs-2022-update-5/ + {32215, "VS2022 17.5.0 build 32215"}, + {32216, "VS2022 17.5.3 build 32216"}, + {32217, "VS2022 17.5.4 build 32217"}, +}; + +static const string kUnknownProduct = "<unknown>"; + +// Returns a stringified Rich header object type given a product id +const string &GetRichObjectType(uint16_t prodId) { + + auto it = ProductIdMap.find(prodId); + if (it != ProductIdMap.end()) { + return it->second; + } else { + return kProdId_UNK; + } +} + +// Returns a stringified Rich header product name given a build number +const string &GetRichProductName(uint16_t buildNum) { + + auto it = ProductMap.find(buildNum); + if (it != ProductMap.end()) { + return it->second; + } else { + return kUnknownProduct; + } +} + +static string GetDebugTypeName(int type) +{ + switch (type) + { + case IMAGE_DEBUG_TYPE_UNKNOWN: return "debug_type_unknown"; + case IMAGE_DEBUG_TYPE_COFF: return "debug_type_coff"; + case IMAGE_DEBUG_TYPE_CODEVIEW: return "debug_type_codeview"; + case IMAGE_DEBUG_TYPE_FPO: return "debug_type_fpo"; + case IMAGE_DEBUG_TYPE_MISC: return "debug_type_misc"; + case IMAGE_DEBUG_TYPE_EXCEPTION: return "debug_type_exception"; + case IMAGE_DEBUG_TYPE_FIXUP: return "debug_type_fixup"; + case IMAGE_DEBUG_TYPE_OMAP_TO_SRC: return "debug_type_omap_to_src"; + case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: return "debug_type_omap_from_src"; + case IMAGE_DEBUG_TYPE_BORLAND: return "debug_type_borland"; + case IMAGE_DEBUG_TYPE_RESERVED10: return "debug_type_reserved10"; + case IMAGE_DEBUG_TYPE_CLSID: return "debug_type_clsid"; + case IMAGE_DEBUG_TYPE_VC_FEATURE: return "debug_type_vc_feature"; + case IMAGE_DEBUG_TYPE_POGO: return "debug_type_pogo"; + case IMAGE_DEBUG_TYPE_ILTCG: return "debug_type_iltcg"; + case IMAGE_DEBUG_TYPE_MPX: return "debug_type_mpx"; + case IMAGE_DEBUG_TYPE_REPRO: return "debug_type_repro"; + case IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS: return "debug_type_ex_dll_characteristics"; + default: return string("debug_type_unknown(") + string(std::to_string(type)) + string(")"); + } +} + + +PEView::PEView(BinaryView* data, bool parseOnly) : BinaryView("PE", data->GetFile(), data), m_parseOnly(parseOnly) +{ + CreateLogger("BinaryView"); + m_logger = CreateLogger("BinaryView.PEView"); + m_backedByDatabase = data->GetFile()->IsBackedByDatabase("PE"); +} + + +bool PEView::Init() +{ + std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now(); + map<string, size_t> usedSectionNames; + + BinaryReader reader(GetParentView(), LittleEndian); + Ref<Platform> platform; + + Ref<Settings> settings; + PEHeader header; + PEOptionalHeader opt; + memset(&opt, 0, sizeof(opt)); + + try + { + // Read PE offset + reader.Seek(0x3c); + uint32_t peOfs = reader.Read32(); + + // Read Rich header + vector<pair<uint32_t, uint32_t>> richValues; + const uint32_t richHeaderBase = 0x80; + if (peOfs > richHeaderBase) + { + reader.Seek(richHeaderBase); + for (uint32_t i = 0; i < ((peOfs - richHeaderBase) / 8); i++) + { + uint32_t var1 = reader.Read32(); + uint32_t var2 = reader.Read32(); + richValues.push_back({var1, var2}); + } + } + + // Read PE header + reader.Seek(peOfs); + header.magic = reader.Read32(); + header.machine = reader.Read16(); + header.sectionCount = reader.Read16(); + header.timestamp = reader.Read32(); + header.coffSymbolTable = reader.Read32(); + header.coffSymbolCount = reader.Read32(); + header.optionalHeaderSize = reader.Read16(); + header.characteristics = reader.Read16(); + m_logger->LogDebug( + "PEHeader:\n" + "\tmagic: 0x%08x\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.magic, + 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"); + + uint64_t optionalHeaderOffset = reader.GetOffset(); + // Read optional header + opt.magic = reader.Read16(); + opt.majorLinkerVersion = reader.Read8(); + opt.minorLinkerVersion = reader.Read8(); + opt.sizeOfCode = reader.Read32(); + opt.sizeOfInitData = reader.Read32(); + opt.sizeOfUninitData = reader.Read32(); + opt.addressOfEntry = reader.Read32(); + opt.baseOfCode = reader.Read32(); + + m_logger->LogDebug( + "PEOptionalHeader:\n" + "\tmagic %04x (%s-bit)\n" + "\tmajorLinkerVersion: %02x\n" + "\tminorLinkerVersion: %02x\n" + "\tsizeOfCode: %08x\n" + "\tsizeOfInitData: %08x\n" + "\tsizeOfUninitData: %08x\n" + "\taddressOfEntry: %08x\n" + "\tbaseOfCode: %08x\n", + opt.magic, opt.magic == 0x10b ? "32" : opt.magic == 0x20b ? "64" : "??", + opt.majorLinkerVersion, + opt.minorLinkerVersion, + opt.sizeOfCode, + opt.sizeOfInitData, + opt.sizeOfUninitData, + opt.addressOfEntry, + opt.baseOfCode); + + if (opt.magic == 0x10b) // 32-bit + { + m_is64 = false; + opt.baseOfData = reader.Read32(); + opt.imageBase = reader.Read32(); + opt.sectionAlign = reader.Read32(); + opt.fileAlign = reader.Read32(); + opt.majorOSVersion = reader.Read16(); + opt.minorOSVersion = reader.Read16(); + opt.majorImageVersion = reader.Read16(); + opt.minorImageVersion = reader.Read16(); + opt.majorSubsystemVersion = reader.Read16(); + opt.minorSubsystemVersion = reader.Read16(); + opt.win32Version = reader.Read32(); + opt.sizeOfImage = reader.Read32(); + opt.sizeOfHeaders = reader.Read32(); + opt.checksum = reader.Read32(); + opt.subsystem = reader.Read16(); + opt.dllCharacteristics = reader.Read16(); + opt.sizeOfStackReserve = reader.Read32(); + opt.sizeOfStackCommit = reader.Read32(); + opt.sizeOfHeapReserve = reader.Read32(); + opt.sizeOfHeapCommit = reader.Read32(); + opt.loaderFlags = reader.Read32(); + opt.dataDirCount = reader.Read32(); + } + else if (opt.magic == 0x20b) // 64-bit + { + m_is64 = true; + opt.baseOfData = 0; + opt.imageBase = reader.Read64(); + opt.sectionAlign = reader.Read32(); + opt.fileAlign = reader.Read32(); + opt.majorOSVersion = reader.Read16(); + opt.minorOSVersion = reader.Read16(); + opt.majorImageVersion = reader.Read16(); + opt.minorImageVersion = reader.Read16(); + opt.majorSubsystemVersion = reader.Read16(); + opt.minorSubsystemVersion = reader.Read16(); + opt.win32Version = reader.Read32(); + opt.sizeOfImage = reader.Read32(); + opt.sizeOfHeaders = reader.Read32(); + opt.checksum = reader.Read32(); + opt.subsystem = reader.Read16(); + opt.dllCharacteristics = reader.Read16(); + opt.sizeOfStackReserve = reader.Read64(); + opt.sizeOfStackCommit = reader.Read64(); + opt.sizeOfHeapReserve = reader.Read64(); + opt.sizeOfHeapCommit = reader.Read64(); + opt.loaderFlags = reader.Read32(); + opt.dataDirCount = reader.Read32(); + } + else + { + m_logger->LogError("invalid PE optional header type"); + return false; + } + + map<string, Ref<Metadata>> metadataMap = { + {"Machine", new Metadata((uint64_t) header.machine)}, + {"Characteristics", new Metadata((uint64_t) header.characteristics)}, + {"Magic", new Metadata((uint64_t) opt.magic)}, + {"MajorLinkerVersion", new Metadata((uint64_t) opt.majorLinkerVersion)}, + {"MinorLinkerVersion", new Metadata((uint64_t) opt.minorLinkerVersion)}, + {"MajorOSVersion", new Metadata((uint64_t) opt.majorOSVersion)}, + {"MinorOSVersion", new Metadata((uint64_t) opt.minorOSVersion)}, + {"MajorImageVersion", new Metadata((uint64_t) opt.majorImageVersion)}, + {"MajorImageVersion", new Metadata((uint64_t) opt.majorImageVersion)}, + {"MinorSubsystemVersion", new Metadata((uint64_t) opt.minorSubsystemVersion)}, + {"MinorSubsystemVersion", new Metadata((uint64_t) opt.minorSubsystemVersion)}, + {"Subsystem", new Metadata((uint64_t) opt.subsystem)}, + {"DllCharacteristics", new Metadata((uint64_t) opt.dllCharacteristics)}, + }; + + Ref<Metadata> metadata = new Metadata(metadataMap); + + platform = g_peViewType->RecognizePlatform(header.machine, LittleEndian, GetParentView(), metadata); + + // set m_arch early so the to make it available for the demangler + m_arch = platform ? platform->GetArchitecture() : g_peViewType->GetArchitecture(header.machine, LittleEndian); + if (!m_arch) + { + // There is no registered architecture for this header.machine likely malware doing something funky + // assume x86/x86_64 + m_arch = g_peViewType->GetArchitecture(opt.magic == 0x20b ? 0x8664 : 0x14c, LittleEndian); + m_logger->LogWarn( + "This binary doesn't specify its architecture. Defaulting to x86. If this isn't correct please " + "re-open with 'with options' and specify the correct architecture."); + } + if (!platform) + platform = g_peViewType->GetPlatform(opt.subsystem, m_arch); + if (!platform) + platform = m_arch->GetStandalonePlatform(); + + m_imageBase = m_peImageBase = opt.imageBase; + SetOriginalBase(m_peImageBase); + m_entryPoint = opt.addressOfEntry; + + Ref<Settings> viewSettings = Settings::Instance(); + m_extractMangledTypes = viewSettings->Get<bool>("analysis.extractTypesFromMangledNames", this); + m_simplifyTemplates = viewSettings->Get<bool>("analysis.types.templateSimplifier", this); + + 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; + } + + // Apply architecture and platform + if (!m_arch) + { + switch (header.machine) + { + case 0x14c: + m_logger->LogError("Support for PE architecture 'x86' is not present"); + break; + case 0x1c0: + m_logger->LogError("Support for PE architecture 'armv7' is not present"); + break; + case 0x8664: + m_logger->LogError("Support for PE architecture 'x86_64' is not present"); + break; + case 0xaa64: + #ifndef DEMO_VERSION + m_logger->LogError("Support for PE architecture 'arm64' is not present"); + #else + m_logger->LogError("Binary Ninja free does not support PE architecture 'arm64'. " + "Purchase Binary Ninja to unlock all features."); + #endif + break; + default: + m_logger->LogError("PE architecture '0x%x' is not supported", header.machine); + break; + } + return false; + } + + + platform = platform->GetAssociatedPlatformByAddress(m_entryPoint); + + if (settings && settings->Contains("loader.platform")) // handle overrides + { + Ref<Platform> platformOverride = Platform::GetByName(settings->Get<string>("loader.platform", this)); + if (platformOverride) + platform = platformOverride; + } + + SetDefaultPlatform(platform); + SetDefaultArchitecture(platform->GetArchitecture()); + + bool fileAlignmentValid = ((opt.fileAlign >= 0x200) && (opt.fileAlign <= 0x10000)) ? (opt.fileAlign & (opt.fileAlign - 1)) == 0 : false; + uint32_t resolvedSectionAlignment = fileAlignmentValid ? opt.sectionAlign : (header.machine == IMAGE_FILE_MACHINE_IA64) ? 0x2000 : 0x1000; + uint32_t resolvedFileAlignment = fileAlignmentValid ? opt.fileAlign : 0x200; + if (!fileAlignmentValid) + m_logger->LogWarn("PE has invalid FileAlignment with value: 0x%x", opt.fileAlign); + m_sizeOfHeaders = opt.sizeOfHeaders; + if (opt.sizeOfHeaders % resolvedFileAlignment) + m_sizeOfHeaders = (opt.sizeOfHeaders + resolvedFileAlignment) & ~(resolvedFileAlignment - 1); + m_relocatable = (opt.dllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) > 0; + m_logger->LogDebug( + "OptionalHeaderComponents:\n" + "\topt.baseOfData %08lx\n" + "\topt.imageBase %016lx\n" + "\topt.sectionAlign %08lx\n" + "\topt.fileAlign %08lx\n" + "\topt.majorOSVersion %04hx\n" + "\topt.minorOSVersion %04hx\n" + "\topt.majorImageVersion %04hx\n" + "\topt.minorImageVersion %04hx\n" + "\topt.majorSubsystemVersion %04hx\n" + "\topt.minorSubsystemVersion %04hx\n" + "\topt.win32Version %08lx\n" + "\topt.sizeOfImage %08lx\n" + "\topt.sizeOfHeaders %08lx\n" + "\topt.checksum %08lx\n" + "\topt.subsystem %04hx\n" + "\topt.dllCharacteristics %04hx\n" + "\topt.sizeOfStackReserve %016lx\n" + "\topt.sizeOfStackCommit %016lx\n" + "\topt.sizeOfHeapReserve %016lx\n" + "\topt.sizeOfHeapCommit %016lx\n" + "\topt.loaderFlags %08lx\n" + "\topt.dataDirCount %016llx\n" + "\topt.imageBase %016llx\n" + "\topt.sizeOfHeaders %08lx\n" + "\topt.addressOfEntry %08lx\n", + opt.baseOfData, + opt.imageBase, + opt.sectionAlign, + opt.fileAlign, + opt.majorOSVersion, + opt.minorOSVersion, + opt.majorImageVersion, + opt.minorImageVersion, + opt.majorSubsystemVersion, + opt.minorSubsystemVersion, + opt.win32Version, + opt.sizeOfImage, + opt.sizeOfHeaders, + opt.checksum, + opt.subsystem, + opt.dllCharacteristics, + opt.sizeOfStackReserve, + opt.sizeOfStackCommit, + opt.sizeOfHeapReserve, + opt.sizeOfHeapCommit, + opt.loaderFlags, + opt.dataDirCount, + opt.imageBase, + opt.sizeOfHeaders, + opt.addressOfEntry); + + // PE Optional Header Validation + if (opt.dataDirCount > 16) + { + m_logger->LogWarn("PE Optional Header: NumberOfRvaAndSizes exceeds allowable size. Truncating count: %d to 16.", + opt.dataDirCount); + opt.dataDirCount = 16; + } + + // Read data directories + for (uint32_t i = 0; i < opt.dataDirCount; i++) + { + PEDataDirectory dir; + dir.virtualAddress = reader.Read32(); + dir.size = reader.Read32(); + m_dataDirs.push_back(dir); + } + + // Add extra segment to hold header so that it can be viewed. This must be first so + // that real sections take priority. + if (header.sectionCount) + AddAutoSegment(m_imageBase, m_sizeOfHeaders, 0, m_sizeOfHeaders, SegmentReadable); + else + { + uint64_t sizeOfImage = opt.sizeOfImage; + if (opt.sizeOfImage % resolvedSectionAlignment) + sizeOfImage = (opt.sizeOfImage + resolvedSectionAlignment) & ~(resolvedSectionAlignment - 1); + uint64_t dataLength = GetParentView()->GetEnd(); + dataLength = std::min(std::max((uint64_t)m_sizeOfHeaders, sizeOfImage), dataLength); + AddAutoSegment(m_imageBase, sizeOfImage, 0, dataLength, SegmentReadable); + } + reader.Seek(optionalHeaderOffset + header.optionalHeaderSize); + // Read sections + BinaryReader sectionNameReader(GetParentView(), LittleEndian); + for (uint16_t i = 0; i < header.sectionCount; i++) + { + PESection section; + m_logger->LogDebug("Offset: %lx\n", reader.GetOffset()); + 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; + if (section.name == ".reloc") + m_relocatable = true; + + section.virtualSize = reader.Read32(); + section.virtualAddress = reader.Read32(); + section.sizeOfRawData = reader.Read32(); + section.pointerToRawData = reader.Read32(); + if (fileAlignmentValid && (section.pointerToRawData & (resolvedFileAlignment - 1))) + { + m_logger->LogWarn("PE section[%u] violates file alignment: pointerToRawData: 0x%x. Aligning to 0x%x.", i, + section.pointerToRawData, resolvedFileAlignment); + section.pointerToRawData &= ~(resolvedFileAlignment - 1); + } + 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; + } + m_sections.push_back(section); + + 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; + + + m_logger->LogDebug( + "Section [%d]\n" + "\tsection.name %s\n" + "\tsection.virtualSize: %lx\n" + "\tsection.virtualAddress: %lx\n" + "\tsection.sizeOfRawData: %lx\n" + "\tsection.pointerToRawData: %lx\n" + "\tsection.pointerToRelocs: %lx\n" + "\tsection.pointerToLineNumbers: %lx\n" + "\tsection.relocCount: %hx\n" + "\tsection.lineNumberCount: %hx\n" + "\tsection.characteristics: %lx\n" + "\tsection.virtualSize: %lx\n", + i, section.name.c_str(), + section.virtualAddress, + section.sizeOfRawData, + section.pointerToRawData, + section.pointerToRelocs, + section.pointerToLineNumbers, + section.relocCount, + section.lineNumberCount, + section.characteristics); + + m_logger->LogDebug("Segment: Vaddr: %08" PRIx64 " Vsize: %08" PRIx64 " Offset: %08" PRIx64 " Rawsize: %08" PRIx64 + " %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()); + + if (!section.virtualSize) + continue; + + AddAutoSegment(section.virtualAddress + m_imageBase, section.virtualSize, section.pointerToRawData, section.sizeOfRawData, flags); + + 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: For now everride 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)) + { + m_logger->LogInfo("%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); + } + } + + // Finished for parse only mode + if (m_parseOnly) + return true; + + // Add the entry point as a function if the architecture is supported + if (m_entryPoint) + AddEntryPointForAnalysis(platform, m_imageBase + m_entryPoint); + + // Create various PE header yypes + + // Create MS-DOS Header Type + StructureBuilder dosHeaderBuilder; + dosHeaderBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 2), "e_magic"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_cblp"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_cp"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_crlc"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_cparhdr"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_minalloc"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_maxalloc"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_ss"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_sp"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_csum"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_ip"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_cs"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_lfarlc"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_ovno"); + dosHeaderBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 8), "e_res1"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_oemid"); + dosHeaderBuilder.AddMember(Type::IntegerType(2, false), "e_oeminfo"); + dosHeaderBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 20), "e_res2"); + dosHeaderBuilder.AddMember(Type::IntegerType(4, false), "e_lfanew"); + + Ref<Structure> dosHeaderStruct = dosHeaderBuilder.Finalize(); + Ref<Type> dosHeaderType = Type::StructureType(dosHeaderStruct); + QualifiedName dosHeaderName = string("DOS_Header"); + string dosHeaderTypeId = Type::GenerateAutoTypeId("pe", dosHeaderName); + QualifiedName dosHeaderTypeName = DefineType(dosHeaderTypeId, dosHeaderName, dosHeaderType); + DefineDataVariable(m_imageBase, Type::NamedType(this, dosHeaderTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__dos_header", m_imageBase, NoBinding)); + DefineDataVariable(m_imageBase + 0x40, Type::VoidType()); + DefineAutoSymbol(new Symbol(DataSymbol, "__dos_stub", m_imageBase + 0x40, NoBinding)); + + // Create Rich Header Type + // TODO move decoded rich info to comments once comments work with linear view + if (richValues.size() >= 4) + { + bool validRichHeader = false; + uint32_t xorKey = richValues[0].second; + uint32_t entryIdx; + vector<uint64_t> richMetadataLookupIdentifiers; + vector<string> richMetadataLookupNames; + for (const auto& [id, name] : ProductMap) + { + richMetadataLookupIdentifiers.push_back(id); + richMetadataLookupNames.push_back(name); + } + StoreMetadata("RichHeaderLookupIdentifiers", new Metadata(richMetadataLookupIdentifiers), true); + StoreMetadata("RichHeaderLookupNames", new Metadata(richMetadataLookupNames), true); + + vector<Ref<Metadata>> richMetadata; + for (entryIdx = 0; entryIdx < richValues.size(); entryIdx++) + { + if ((richValues[entryIdx].first == 0x68636952) && (richValues[entryIdx].second == xorKey)) + { + validRichHeader = true; + break; + } + + richValues[entryIdx].first ^= xorKey; + richValues[entryIdx].second ^= xorKey; + if (entryIdx > 1) // Skip the first 2 entries as they don't contain interesting information + { + map<string, Ref<Metadata>> entryMetadata = { + {string("ObjectTypeValue"), new Metadata((uint64_t)richValues[entryIdx].first >> 16)}, + {string("ObjectTypeName"), new Metadata(GetRichObjectType(richValues[entryIdx].first >> 16))}, + {string("ObjectVersionValue"), new Metadata((uint64_t)richValues[entryIdx].first & 0xffff)}, + {string("ObjectVersionName"), new Metadata(GetRichProductName(richValues[entryIdx].first & 0xffff))}, + {string("ObjectCount"), new Metadata((uint64_t)richValues[entryIdx].second)} + }; + richMetadata.push_back(new Metadata(entryMetadata)); + } + if (!entryIdx && richValues[entryIdx].first != 0x536e6144) + break; + } + + if (validRichHeader) + { + StoreMetadata("RichHeader", new Metadata(richMetadata), true); + StructureBuilder richHeaderBuilder; + richHeaderBuilder.AddMember(Type::IntegerType(4, false), "e_magic__DanS"); + richHeaderBuilder.AddMember(Type::ArrayType(Type::IntegerType(4, false), 3), "e_align"); + + for (uint32_t i = 2; i < entryIdx; i++) + { + stringstream ss; + ss << "e_entry_id" << std::dec << i-2 << "__" << std::hex << std::setw(8) << std::setfill('0') << richValues[i].first; + richHeaderBuilder.AddMember(Type::IntegerType(4, false), ss.str()); + ss.str(""); + ss.clear(); + ss << "e_entry_count" << std::dec << i-2 << "__" << richValues[i].second; + richHeaderBuilder.AddMember(Type::IntegerType(4, false), ss.str()); + } + + richHeaderBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 4), "e_magic"); + richHeaderBuilder.AddMember(Type::IntegerType(4, false), "e_checksum"); + + Ref<Structure> richHeaderStruct = richHeaderBuilder.Finalize(); + Ref<Type> richHeaderType = Type::StructureType(richHeaderStruct); + QualifiedName richHeaderName = string("Rich_Header"); + string richHeaderTypeId = Type::GenerateAutoTypeId("pe", richHeaderName); + QualifiedName richHeaderTypeName = DefineType(richHeaderTypeId, richHeaderName, richHeaderType); + DefineDataVariable(m_imageBase + richHeaderBase, Type::NamedType(this, richHeaderTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__rich_header", m_imageBase + richHeaderBase, NoBinding)); + } + } + + // Create COFF Header Type + EnumerationBuilder coffHeaderMachineBuilder; + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_UNKNOWN", IMAGE_FILE_MACHINE_UNKNOWN); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_AM33", IMAGE_FILE_MACHINE_AM33); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_AMD64", IMAGE_FILE_MACHINE_AMD64); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_ARM", IMAGE_FILE_MACHINE_ARM); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_ARM64", IMAGE_FILE_MACHINE_ARM64); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_ARMNT", IMAGE_FILE_MACHINE_ARMNT); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_EBC", IMAGE_FILE_MACHINE_EBC); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_I386", IMAGE_FILE_MACHINE_I386); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_IA64", IMAGE_FILE_MACHINE_IA64); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_M32R", IMAGE_FILE_MACHINE_M32R); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_MIPS16", IMAGE_FILE_MACHINE_MIPS16); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_MIPSFPU", IMAGE_FILE_MACHINE_MIPSFPU); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_MIPSFPU16", IMAGE_FILE_MACHINE_MIPSFPU16); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_POWERPC", IMAGE_FILE_MACHINE_POWERPC); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_POWERPCFP", IMAGE_FILE_MACHINE_POWERPCFP); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_R4000", IMAGE_FILE_MACHINE_R4000); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_RISCV32", IMAGE_FILE_MACHINE_RISCV32); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_RISCV64", IMAGE_FILE_MACHINE_RISCV64); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_RISCV128", IMAGE_FILE_MACHINE_RISCV128); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_SH3", IMAGE_FILE_MACHINE_SH3); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_SH3DSP", IMAGE_FILE_MACHINE_SH3DSP); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_SH4", IMAGE_FILE_MACHINE_SH4); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_SH5", IMAGE_FILE_MACHINE_SH5); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_THUMB", IMAGE_FILE_MACHINE_THUMB); + coffHeaderMachineBuilder.AddMemberWithValue("IMAGE_FILE_MACHINE_WCEMIPSV2", IMAGE_FILE_MACHINE_WCEMIPSV2); + + Ref<Enumeration> coffHeaderMachineEnum = coffHeaderMachineBuilder.Finalize(); + Ref<Type> coffHeaderMachineEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), coffHeaderMachineEnum, 2, false); + string coffHeaderMachineEnumName = "coff_machine"; + string coffHeaderMachineEnumId = Type::GenerateAutoTypeId("pe", coffHeaderMachineEnumName); + QualifiedName coffHeaderMachineEnumTypeName = DefineType(coffHeaderMachineEnumId, coffHeaderMachineEnumName, coffHeaderMachineEnumType); + + EnumerationBuilder coffCharacteristicsBuilder; + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_RELOCS_STRIPPED", IMAGE_FILE_RELOCS_STRIPPED); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_EXECUTABLE_IMAGE", IMAGE_FILE_EXECUTABLE_IMAGE); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_LINE_NUMS_STRIPPED", IMAGE_FILE_LINE_NUMS_STRIPPED); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_LOCAL_SYMS_STRIPPED", IMAGE_FILE_LOCAL_SYMS_STRIPPED); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_AGGRESIVE_WS_TRIM", IMAGE_FILE_AGGRESIVE_WS_TRIM); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_LARGE_ADDRESS_AWARE", IMAGE_FILE_LARGE_ADDRESS_AWARE); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_BYTES_REVERSED_LO", IMAGE_FILE_BYTES_REVERSED_LO); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_32BIT_MACHINE", IMAGE_FILE_32BIT_MACHINE); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_DEBUG_STRIPPED", IMAGE_FILE_DEBUG_STRIPPED); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP", IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_NET_RUN_FROM_SWAP", IMAGE_FILE_NET_RUN_FROM_SWAP); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_SYSTEM", IMAGE_FILE_SYSTEM); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_DLL", IMAGE_FILE_DLL); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_UP_SYSTEM_ONLY", IMAGE_FILE_UP_SYSTEM_ONLY); + coffCharacteristicsBuilder.AddMemberWithValue("IMAGE_FILE_BYTES_REVERSED_HI", IMAGE_FILE_BYTES_REVERSED_HI); + + Ref<Enumeration> coffCharacteristicsEnum = coffCharacteristicsBuilder.Finalize(); + Ref<Type> coffCharacteristicsEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), coffCharacteristicsEnum, 2, false); + string coffCharacteristicsEnumName = "coff_characteristics"; + string coffCharacteristicsEnumId = Type::GenerateAutoTypeId("pe", coffCharacteristicsEnumName); + QualifiedName coffCharacteristicsEnumTypeName = DefineType(coffCharacteristicsEnumId, coffCharacteristicsEnumName, coffCharacteristicsEnumType); + + // TODO decorate members with comments once comments work with linear view + StructureBuilder coffHeaderBuilder; + coffHeaderBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 4), "magic"); + 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("pe", coffHeaderName); + QualifiedName coffHeaderTypeName = DefineType(coffHeaderTypeId, coffHeaderName, coffHeaderType); + DefineDataVariable(m_imageBase + peOfs, Type::NamedType(this, coffHeaderTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__coff_header", m_imageBase + peOfs, NoBinding)); + + EnumerationBuilder peMagicBuilder; + peMagicBuilder.AddMemberWithValue("PE_ROM_IMAGE", 0x107); + peMagicBuilder.AddMemberWithValue("PE_32BIT", 0x10b); + peMagicBuilder.AddMemberWithValue("PE_64BIT", 0x20b); + + Ref<Enumeration> peMagicEnum = peMagicBuilder.Finalize(); + Ref<Type> peMagicEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), peMagicEnum, 2, false); + string peMagicEnumName = "pe_magic"; + string peMagicEnumId = Type::GenerateAutoTypeId("pe", peMagicEnumName); + QualifiedName peMagicEnumTypeName = DefineType(peMagicEnumId, peMagicEnumName, peMagicEnumType); + + EnumerationBuilder peSubsystemBuilder; + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_UNKNOWN", IMAGE_SUBSYSTEM_UNKNOWN); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_NATIVE", IMAGE_SUBSYSTEM_NATIVE); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_WINDOWS_GUI", IMAGE_SUBSYSTEM_WINDOWS_GUI); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_WINDOWS_CUI", IMAGE_SUBSYSTEM_WINDOWS_CUI); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_OS2_CUI", IMAGE_SUBSYSTEM_OS2_CUI); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_POSIX_CUI", IMAGE_SUBSYSTEM_POSIX_CUI); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_NATIVE_WINDOWS", IMAGE_SUBSYSTEM_NATIVE_WINDOWS); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_WINDOWS_CE_GUI", IMAGE_SUBSYSTEM_WINDOWS_CE_GUI); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_EFI_APPLICATION", IMAGE_SUBSYSTEM_EFI_APPLICATION); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_EFI_ROM", IMAGE_SUBSYSTEM_EFI_ROM); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_XBOX", IMAGE_SUBSYSTEM_XBOX); + peSubsystemBuilder.AddMemberWithValue("IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION); + + Ref<Enumeration> peSubsystemEnum = peSubsystemBuilder.Finalize(); + Ref<Type> peSubsystemEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), peSubsystemEnum, 2, false); + string peSubsystemEnumName = "pe_subsystem"; + string peSubsystemEnumId = Type::GenerateAutoTypeId("pe", peSubsystemEnumName); + QualifiedName peSubsystemEnumTypeName = DefineType(peSubsystemEnumId, peSubsystemEnumName, peSubsystemEnumType); + + EnumerationBuilder dllCharacteristicsBuilder; + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_0001", IMAGE_DLLCHARACTERISTICS_0001); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_0002", IMAGE_DLLCHARACTERISTICS_0002); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_0004", IMAGE_DLLCHARACTERISTICS_0004); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_0008", IMAGE_DLLCHARACTERISTICS_0008); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA", IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE", IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY", IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_NX_COMPAT", IMAGE_DLLCHARACTERISTICS_NX_COMPAT); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_NO_ISOLATION", IMAGE_DLLCHARACTERISTICS_NO_ISOLATION); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_NO_SEH", IMAGE_DLLCHARACTERISTICS_NO_SEH); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_NO_BIND", IMAGE_DLLCHARACTERISTICS_NO_BIND); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_APPCONTAINER", IMAGE_DLLCHARACTERISTICS_APPCONTAINER); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_WDM_DRIVER", IMAGE_DLLCHARACTERISTICS_WDM_DRIVER); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_GUARD_CF", IMAGE_DLLCHARACTERISTICS_GUARD_CF); + dllCharacteristicsBuilder.AddMemberWithValue("IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE", IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE); + + Ref<Enumeration> dllCharacteristicsEnum = dllCharacteristicsBuilder.Finalize(); + Ref<Type> dllCharacteristicsEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), dllCharacteristicsEnum, 2, false); + string dllCharacteristicsEnumName = "pe_dll_characteristics"; + string dllCharacteristicsEnumId = Type::GenerateAutoTypeId("pe", dllCharacteristicsEnumName); + QualifiedName dllCharacteristicsEnumTypeName = DefineType(dllCharacteristicsEnumId, dllCharacteristicsEnumName, dllCharacteristicsEnumType); + + // Create PE Optional Header Type + StructureBuilder peOptionalHeaderBuilder; + peOptionalHeaderBuilder.AddMember(Type::NamedType(this, peMagicEnumTypeName), "magic"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(1, false), "majorLinkerVersion"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(1, false), "minorLinkerVersion"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "sizeOfCode"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "sizeOfInitializedData"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "sizeOfUninitializedData"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "addressOfEntryPoint"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "baseOfCode"); + if (!m_is64) + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "baseOfData"); + size_t opFieldSize = m_is64 ? 8 : 4; + peOptionalHeaderBuilder.AddMember(Type::IntegerType(opFieldSize, false), "imageBase"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "sectionAlignment"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "fileAlignment"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(2, false), "majorOperatingSystemVersion"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(2, false), "minorOperatingSystemVersion"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(2, false), "majorImageVersion"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(2, false), "minorImageVersion"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(2, false), "majorSubsystemVersion"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(2, false), "minorSubsystemVersion"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "win32VersionValue"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "sizeOfImage"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "sizeOfHeaders"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "checkSum"); + peOptionalHeaderBuilder.AddMember(Type::NamedType(this, peSubsystemEnumTypeName), "subsystem"); + peOptionalHeaderBuilder.AddMember(Type::NamedType(this, dllCharacteristicsEnumTypeName), "dllCharacteristics"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(opFieldSize, false), "sizeOfStackReserve"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(opFieldSize, false), "sizeOfStackCommit"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(opFieldSize, false), "sizeOfHeapReserve"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(opFieldSize, false), "sizeOfHeapCommit"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "loaderFlags"); + peOptionalHeaderBuilder.AddMember(Type::IntegerType(4, false), "numberOfRvaAndSizes"); + + if (opt.dataDirCount) + { + StructureBuilder dataDirEntryBuilder; + dataDirEntryBuilder.AddMember(Type::IntegerType(4, false), "virtualAddress"); + dataDirEntryBuilder.AddMember(Type::IntegerType(4, false), "size"); + + Ref<Structure> dataDirEntryStruct = dataDirEntryBuilder.Finalize(); + Ref<Type> dataDirEntryType = Type::StructureType(dataDirEntryStruct); + QualifiedName dataDirName = string("PE_Data_Directory_Entry"); + string dataDirTypeId = Type::GenerateAutoTypeId("pe", dataDirName); + QualifiedName dataDirTypeName = DefineType(dataDirTypeId, dataDirName, dataDirEntryType); + size_t dataDirNameCount = std::extent<decltype(imageDirName)>::value; + for (size_t i = 0; i < opt.dataDirCount; i++) + { + string dirName = (i < std::extent<decltype(imageDirName)>::value) ? imageDirName[i] : imageDirName[dataDirNameCount - 1]; + peOptionalHeaderBuilder.AddMember(Type::NamedType(this, dataDirTypeName), dirName + "Entry"); + } + + } + + string peHdrPrefix = m_is64 ? "pe64" : "pe32"; + Ref<Structure> peOptionalHeaderStruct = peOptionalHeaderBuilder.Finalize(); + Ref<Type> peOptionalHeaderType = Type::StructureType(peOptionalHeaderStruct); + QualifiedName peOptionalHeaderName = m_is64 ? string("PE64_Optional_Header") : string("PE32_Optional_Header"); + string peOptionalHeaderTypeId = Type::GenerateAutoTypeId("pe", peOptionalHeaderName); + QualifiedName peOptionalHeaderTypeName = DefineType(peOptionalHeaderTypeId, peOptionalHeaderName, peOptionalHeaderType); + DefineDataVariable(m_imageBase + optionalHeaderOffset, Type::NamedType(this, peOptionalHeaderTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__" + peHdrPrefix + "_optional_header", m_imageBase + optionalHeaderOffset, NoBinding)); + + EnumerationBuilder peSectionFlagsBuilder; + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_RESERVED_0001", IMAGE_SCN_RESERVED_0001); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_RESERVED_0002", IMAGE_SCN_RESERVED_0002); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_RESERVED_0004", IMAGE_SCN_RESERVED_0004); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_TYPE_NO_PAD", IMAGE_SCN_TYPE_NO_PAD); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_RESERVED_0010", IMAGE_SCN_RESERVED_0010); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_CNT_CODE", IMAGE_SCN_CNT_CODE); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_CNT_INITIALIZED_DATA", IMAGE_SCN_CNT_INITIALIZED_DATA); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_CNT_UNINITIALIZED_DATA", IMAGE_SCN_CNT_UNINITIALIZED_DATA); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_LNK_OTHER", IMAGE_SCN_LNK_OTHER); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_LNK_INFO", IMAGE_SCN_LNK_INFO); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_RESERVED_0400", IMAGE_SCN_RESERVED_0400); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_LNK_REMOVE", IMAGE_SCN_LNK_REMOVE); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_LNK_COMDAT", IMAGE_SCN_LNK_COMDAT); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_GPREL", IMAGE_SCN_GPREL); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_MEM_PURGEABLE", IMAGE_SCN_MEM_PURGEABLE); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_MEM_16BIT", IMAGE_SCN_MEM_16BIT); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_MEM_LOCKED", IMAGE_SCN_MEM_LOCKED); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_MEM_PRELOAD", IMAGE_SCN_MEM_PRELOAD); + // TODO fix the bug that causes flags to not be displayed when these are added to the enumeration + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_1BYTES", IMAGE_SCN_ALIGN_1BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_2BYTES", IMAGE_SCN_ALIGN_2BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_4BYTES", IMAGE_SCN_ALIGN_4BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_8BYTES", IMAGE_SCN_ALIGN_8BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_16BYTES", IMAGE_SCN_ALIGN_16BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_32BYTES", IMAGE_SCN_ALIGN_32BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_64BYTES", IMAGE_SCN_ALIGN_64BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_128BYTES", IMAGE_SCN_ALIGN_128BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_256BYTES", IMAGE_SCN_ALIGN_256BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_512BYTES", IMAGE_SCN_ALIGN_512BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_1024BYTES", IMAGE_SCN_ALIGN_1024BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_2048BYTES", IMAGE_SCN_ALIGN_2048BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_4096BYTES", IMAGE_SCN_ALIGN_4096BYTES); + // peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_ALIGN_8192BYTES", IMAGE_SCN_ALIGN_8192BYTES); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_LNK_NRELOC_OVFL", IMAGE_SCN_LNK_NRELOC_OVFL); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_MEM_DISCARDABLE", IMAGE_SCN_MEM_DISCARDABLE); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_MEM_NOT_CACHED", IMAGE_SCN_MEM_NOT_CACHED); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_MEM_NOT_PAGED", IMAGE_SCN_MEM_NOT_PAGED); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_MEM_SHARED", IMAGE_SCN_MEM_SHARED); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_MEM_EXECUTE", IMAGE_SCN_MEM_EXECUTE); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_MEM_READ", IMAGE_SCN_MEM_READ); + peSectionFlagsBuilder.AddMemberWithValue("IMAGE_SCN_MEM_WRITE", IMAGE_SCN_MEM_WRITE); + + Ref<Enumeration> peSectionFlagsEnum = peSectionFlagsBuilder.Finalize(); + Ref<Type> peSectionFlagsEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), peSectionFlagsEnum, 4, false); + string peSectionFlagsEnumName = "pe_section_flags"; + string peSectionFlagsEnumId = Type::GenerateAutoTypeId("pe", peSectionFlagsEnumName); + QualifiedName peSectionFlagsEnumTypeName = DefineType(peSectionFlagsEnumId, peSectionFlagsEnumName, peSectionFlagsEnumType); + + if (header.sectionCount) + { + StructureBuilder sectionHeaderBuilder; + 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, peSectionFlagsEnumTypeName), "characteristics"); + + Ref<Structure> sectionHeaderStruct = sectionHeaderBuilder.Finalize(); + Ref<Type> sectionHeaderStructType = Type::StructureType(sectionHeaderStruct); + QualifiedName sectionHeaderName = string("Section_Header"); + string sectionHeaderTypeId = Type::GenerateAutoTypeId("pe", sectionHeaderName); + QualifiedName sectionHeaderTypeName = DefineType(sectionHeaderTypeId, sectionHeaderName, sectionHeaderStructType); + + size_t sectionHeaderOffset = optionalHeaderOffset + header.optionalHeaderSize; + DefineDataVariable(m_imageBase + sectionHeaderOffset, Type::ArrayType(Type::NamedType(this, sectionHeaderTypeName), header.sectionCount)); + DefineAutoSymbol(new Symbol(DataSymbol, "__section_headers", m_imageBase + sectionHeaderOffset, NoBinding)); + } + } + catch (std::exception& e) + { + m_logger->LogError("Failed to parse PE headers: %s\n", e.what()); + return false; + } + + vector<pair<BNRelocationInfo, string>> relocs; + BeginBulkModifySymbols(); + m_symbolQueue = new SymbolQueue(); + m_symExternMappingMetadata = new Metadata(KeyValueDataType); + + try + { + // Process COFF symbol table + if (header.coffSymbolCount) + { + BinaryReader stringReader(GetParentView(), LittleEndian); + uint64_t stringTableBase = header.coffSymbolTable + (header.coffSymbolCount * 18); + stringReader.Seek(stringTableBase); + if ((stringTableBase + stringReader.Read32()) > GetParentView()->GetEnd()) + { + throw PEFormatException("invalid COFF string table size"); + } + + for (size_t i = 0; i < header.coffSymbolCount; i++) + { + reader.Seek(header.coffSymbolTable + (i * 18)); + uint32_t e_zeroes = reader.Read32(); + uint32_t e_offset = reader.Read32(); + uint32_t e_value = reader.Read32(); + uint16_t e_scnum = reader.Read16(); + 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 (virtualAddress) + { + if (e_zeroes) + { + stringReader.Seek(header.coffSymbolTable + (i * 18)); + symbolName = stringReader.ReadCString(8); + } + else + { + stringReader.Seek(stringTableBase + e_offset); + symbolName = stringReader.ReadCString(); + } + } + + BNSymbolBinding binding; + switch (e_sclass) + { + case IMAGE_SYM_CLASS_EXTERNAL: + case IMAGE_SYM_CLASS_STATIC: + binding = LocalBinding; + break; + default: + binding = NoBinding; + break; + } + + // if (virtualAddress) + // m_logger->LogError("RawOffset:0x%x StorageClass:%u Type:%x NumAux:%x VA: 0x%x section:%x %s", + // header.coffSymbolTable + (i * 18), e_sclass, e_type, e_numaux, virtualAddress + m_imageBase, e_scnum, + // symbolName.c_str()); else m_logger->LogError("RawOffset:0x%x StorageClass:%u Type:%x NumAux:%x VA: 0x%x + // section:%x value: %x", header.coffSymbolTable + (i * 18), e_sclass, e_type, e_numaux, virtualAddress + // + m_imageBase, e_scnum, e_value); + + uint8_t baseType = (e_type >> 4) & 0x3; + switch (baseType) + { + case IMAGE_SYM_DTYPE_NULL: // no derived type + { + if (virtualAddress) + AddPESymbol(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 + { + //LogError("%x StorageClass:%u Type:%x NumAux:%x COFF_DT_FCN at %x section:%x %s ", header.coffSymbolTable + (i * 18), e_sclass, e_type, e_numaux, virtualAddress + m_imageBase, e_scnum, symbolName.c_str()); + if (virtualAddress) + AddPESymbol(FunctionSymbol, "", symbolName, virtualAddress, binding); + break; + } + case IMAGE_SYM_DTYPE_ARRAY: // array of base type + { + break; + } + default: + break; + } + + // TODO handle auxiliary entries + i += e_numaux; + } + } + } + catch (std::exception& e) + { + m_logger->LogError("Failed to parse COFF symbol table: %s\n", e.what()); + } + + try + { + PEDataDirectory dir; + // Read import directory + if (m_dataDirs.size() > IMAGE_DIRECTORY_ENTRY_IMPORT) + dir = m_dataDirs[IMAGE_DIRECTORY_ENTRY_IMPORT]; + else + dir.virtualAddress = 0; + + if (dir.virtualAddress > 0) + { + size_t numImportEntries = 0; + vector<Ref<Metadata>> libraries; + vector<Ref<Metadata>> libraryFound; + while (true) + { + // Read in next directory entry + reader.Seek(RVAToFileOffset(dir.virtualAddress + (numImportEntries * 20))); + PEImportDirectoryEntry importDirEntry; + importDirEntry.lookup = reader.Read32(); + importDirEntry.timestamp = reader.Read32(); + importDirEntry.forwardChain = reader.Read32(); + importDirEntry.nameAddress = reader.Read32(); + importDirEntry.iat = reader.Read32(); + + // Windows PE loader ignores the dir.size; instead, it looks for the first + // Import_Directory_Table that has a null nameAddress to stop the iteration + if (importDirEntry.nameAddress == 0) + { + if (numImportEntries + 1 != dir.size / 20) + m_logger->LogWarn( + "The number of Import_Directory_Table reported by the Data Directories is different from " + "its correct amount. " + "There are actually %d Import_Directory_Table in the file, but SizeOfImportTable reports " + "%d. " + "The PE parsing continues with the actual number of Import_Directory_Table", + numImportEntries + 1, dir.size / 20); + break; + } + + // Read name of imported DLL, and trim extension for creating symbol name + importDirEntry.name = ReadString(importDirEntry.nameAddress); + Ref<ExternalLibrary> externLib = GetExternalLibrary(importDirEntry.name); + if (!externLib) + { + externLib = AddExternalLibrary(importDirEntry.name, {}, true); + } + libraries.push_back(new Metadata(string(importDirEntry.name))); + string lowerName = importDirEntry.name; + std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), + [](unsigned char c){ return std::tolower(c); }); + + vector<Ref<TypeLibrary>> typeLibs = platform->GetTypeLibrariesByName(lowerName); + for (const auto& typeLib : typeLibs) + { + // Check if the type library is already added + if (GetTypeLibrary(typeLib->GetName())) + continue; + AddTypeLibrary(typeLib); + + m_logger->LogDebug("pe: adding type library for '%s': %s (%s)", lowerName.c_str(), + typeLib->GetName().c_str(), typeLib->GetGuid().c_str()); + } + + Ref<Metadata> ordinals; + if (typeLibs.size()) + { + for (const auto& typeLib : typeLibs) + { + char ordinal_subsystem[64]; + snprintf(ordinal_subsystem, sizeof(ordinal_subsystem), "ordinals_%hu_%hu", opt.majorOSVersion, opt.minorOSVersion); + ordinals = typeLib->QueryMetadata("ordinals"); + libraryFound.push_back(new Metadata(string(typeLib->GetName()))); + if (ordinals && ordinals->IsString()) + ordinals = typeLib->QueryMetadata(ordinals->GetString()); + + if (ordinals && !ordinals->IsKeyValueStore()) + ordinals = nullptr; + } + } + else + libraryFound.push_back(new Metadata(string(""))); + + + size_t dotPos = importDirEntry.name.rfind('.'); + string dllName; + if (dotPos == string::npos) + dllName = importDirEntry.name; + else + dllName = importDirEntry.name.substr(0, dotPos); + + // Create Import DLL Name Type + DefineDataVariable(m_imageBase + importDirEntry.nameAddress, Type::ArrayType(Type::IntegerType(1, true), importDirEntry.name.size() + 1)); + DefineAutoSymbol(new Symbol(DataSymbol, "__import_dll_name(" + dllName + ")", m_imageBase + importDirEntry.nameAddress, NoBinding)); + + // Parse list of imported functions + uint32_t entryOffset = importDirEntry.lookup; + uint32_t iatOffset = importDirEntry.iat; + + if ((entryOffset == 0) && (iatOffset != 0)) + entryOffset = iatOffset; + + // TODO: entryOffset and iatOffset point to two copies of the same data + // We should make this second unused data a structure containing this information information + // and default it to collapsed...IDA Just doesn't show anything at all + m_logger->LogDebug("Name: %s\n", dllName.c_str()); + while (true) + { + uint64_t entry; + bool isOrdinal; + if (m_is64) + { + entry = Read64(entryOffset); + isOrdinal = (entry & 0x8000000000000000LL) != 0; + entry &= 0x7fffffffffffffffLL; + DefineDataVariable(m_imageBase + entryOffset, Type::IntegerType(8, false)); + } + else + { + entry = Read32(entryOffset); + isOrdinal = (entry & 0x80000000) != 0; + entry &= 0x7fffffff; + DefineDataVariable(m_imageBase + entryOffset, Type::IntegerType(4, false)); + } + m_logger->LogDebug("Entry 0x%llx isOrdinal: %s\n", entry, isOrdinal ? "True" : "False"); + + if ((!isOrdinal) && (entry == 0)) + break; + + string func; + uint16_t ordinal; + if (isOrdinal) + { + ordinal = (uint16_t)entry; + string ordString = to_string(ordinal); + Ref<Metadata> ordInfo = nullptr; + + if (ordinals) + ordInfo = ordinals->Get(ordString); + + if (ordInfo && ordInfo->IsString()) + func = ordInfo->GetString(); + else + func = "Ordinal_" + dllName + "_" + to_string((int)entry); + } + else + { + ordinal = Read16(entry); + func = ReadString(entry + 2); + DefineDataVariable(m_imageBase + entry, Type::IntegerType(2, false)); + DefineAutoSymbol(new Symbol(DataSymbol, "__export_name_ptr_table_" + to_string(numImportEntries) + "(" + dllName + ":" + func + ")", m_imageBase + entry, NoBinding)); + DefineDataVariable(m_imageBase + entry + 2, Type::ArrayType(Type::IntegerType(1, true), func.size() + 1)); + DefineAutoSymbol(new Symbol(DataSymbol, "__import_name_" + to_string(numImportEntries) + "(" + dllName + ":" + func + ")", m_imageBase + entry + 2, NoBinding)); + DefineAutoSymbol(new Symbol(DataSymbol, "__import_lookup_table_" + to_string(numImportEntries) + "(" + dllName + ":" + func + ")", m_imageBase + entryOffset, NoBinding)); + } + m_logger->LogDebug("FuncString: %s\n", func.c_str()); + AddPESymbol(ImportAddressSymbol, dllName, func, iatOffset, NoBinding, ordinal, typeLibs); + AddPESymbol(ExternalSymbol, dllName, func, 0, NoBinding, ordinal, typeLibs); + + if (externLib) + m_symExternMappingMetadata->SetValueForKey(func, new Metadata(externLib->GetName())); + + BNRelocationInfo reloc; + memset(&reloc, 0, sizeof(reloc)); + reloc.nativeType = -1; + reloc.address = m_imageBase + iatOffset; + reloc.size = m_is64 ? 8 : 4; + reloc.pcRelative = false; + reloc.base = m_imageBase - m_peImageBase; + reloc.external = true; + relocs.push_back({reloc, func}); + entryOffset += m_is64 ? 8 : 4; + iatOffset += m_is64 ? 8 : 4; + } + + numImportEntries++; + } + + StoreMetadata("Libraries", new Metadata(libraries), true); + StoreMetadata("LibraryFound", new Metadata(libraryFound), true); + if (numImportEntries) + { + // Create Import Directory Table Type + StructureBuilder importDirBuilder; + importDirBuilder.AddMember(Type::IntegerType(4, false), "importLookupTableRva"); + importDirBuilder.AddMember(Type::IntegerType(4, false), "timeDateStamp"); + importDirBuilder.AddMember(Type::IntegerType(4, false), "forwarderChain"); + importDirBuilder.AddMember(Type::IntegerType(4, false), "nameRva"); + importDirBuilder.AddMember(Type::IntegerType(4, false), "importAddressTableRva"); + + Ref<Structure> importDirStruct = importDirBuilder.Finalize(); + Ref<Type> importDirType = Type::StructureType(importDirStruct); + QualifiedName importDirName = string("Import_Directory_Table"); + string importDirTypeId = Type::GenerateAutoTypeId("pe", importDirName); + QualifiedName importDirTypeName = DefineType(importDirTypeId, importDirName, importDirType); + DefineDataVariable(m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_IMPORT].virtualAddress, Type::ArrayType(Type::NamedType(this, importDirTypeName), numImportEntries + 1)); + DefineAutoSymbol(new Symbol(DataSymbol, "__import_directory_entries", m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_IMPORT].virtualAddress, NoBinding)); + } + } + } + catch (std::exception& e) + { + m_logger->LogWarn("Failed to parse import directory: %s\n", e.what()); + } + + try + { + if ((m_dataDirs.size() > IMAGE_DIRECTORY_ENTRY_EXCEPTION) && m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXCEPTION].size) + { + // Create Exception Directory Table Entry Type + size_t entrySize; + size_t numExceptionEntries; + StructureBuilder exceptionEntryBuilder; + switch (header.machine) + { + case IMAGE_FILE_MACHINE_AMD64: + case IMAGE_FILE_MACHINE_IA64: + { + entrySize = 12; + exceptionEntryBuilder.AddMember(Type::IntegerType(4, false), "beginAddress"); + exceptionEntryBuilder.AddMember(Type::IntegerType(4, false), "endAddress"); + exceptionEntryBuilder.AddMember(Type::IntegerType(4, false), "unwindInformation"); + break; + } + case IMAGE_FILE_MACHINE_MIPSFPU: + case IMAGE_FILE_MACHINE_R4000: + case IMAGE_FILE_MACHINE_WCEMIPSV2: + { + entrySize = 20; + exceptionEntryBuilder.AddMember(Type::IntegerType(4, false), "beginAddress"); + exceptionEntryBuilder.AddMember(Type::IntegerType(4, false), "endAddress"); + exceptionEntryBuilder.AddMember(Type::IntegerType(4, false), "exceptionHandler"); + exceptionEntryBuilder.AddMember(Type::IntegerType(4, false), "handlerData"); + exceptionEntryBuilder.AddMember(Type::IntegerType(4, false), "prologEndAddress"); + break; + } + default: + { + entrySize = 8; + exceptionEntryBuilder.AddMember(Type::IntegerType(4, false), "beginAddress"); + exceptionEntryBuilder.AddMember(Type::IntegerType(4, false), "otherInformation"); + break; + } + } + + if (m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXCEPTION].size % entrySize) + throw PEFormatException("invalid table size"); + numExceptionEntries = m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXCEPTION].size / entrySize; + + // This DataVariable can end up creating a large array and rendering this in LinearView currently has performance implications + // So instead we just create separate structures not in an array + Ref<Structure> exceptionEntryStruct = exceptionEntryBuilder.Finalize(); + Ref<Type> exceptionEntryType = Type::StructureType(exceptionEntryStruct); + QualifiedName exceptionEntryName = string("Exception_Directory_Entry"); + string exceptionEntryTypeId = Type::GenerateAutoTypeId("pe", exceptionEntryName); + QualifiedName exceptionEntryTypeName = DefineType(exceptionEntryTypeId, exceptionEntryName, exceptionEntryType); + for (size_t i = 0; i < numExceptionEntries; i++) + { + DefineDataVariable(m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXCEPTION].virtualAddress + (entrySize * i), Type::NamedType(this, exceptionEntryTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__exception_directory_entries(" + string(std::to_string(i)) + ")", m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXCEPTION].virtualAddress + (entrySize * i), NoBinding)); + } + + // parse exception table and add functions + bool processExceptionTable = true; + if (settings && settings->Contains("loader.pe.processExceptionTable")) + processExceptionTable = settings->Get<bool>("loader.pe.processExceptionTable", this); + if (processExceptionTable) + { + StructureBuilder unwindInfoStructBuilder; + unwindInfoStructBuilder.AddMember(Type::IntegerType(1, false), "VersionAndFlag"); + unwindInfoStructBuilder.AddMember(Type::IntegerType(1, false), "SizeOfProlog"); + unwindInfoStructBuilder.AddMember(Type::IntegerType(1, false), "CountOfUnwindCodes"); + unwindInfoStructBuilder.AddMember(Type::IntegerType(1, false), "FrameRegisterAndFrameRegisterOffset"); + + Ref<Structure> unwindInfoStruct = unwindInfoStructBuilder.Finalize(); + Ref<Type> unwindInfoStructType = Type::StructureType(unwindInfoStruct); + QualifiedName unwindInfoName = string("UNWIND_INFO"); + string unwindInfoTypeId = Type::GenerateAutoTypeId("pe", unwindInfoName); + QualifiedName unwindInfo = DefineType(unwindInfoTypeId, unwindInfoName, unwindInfoStructType); + + BinaryReader unwindReader(GetParentView(), LittleEndian); + for (size_t i = 0; i < numExceptionEntries; i++) + { + reader.Seek(RVAToFileOffset(m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXCEPTION].virtualAddress + (i * entrySize))); + uint32_t beginAddress = reader.Read32(); + switch (header.machine) + { + case IMAGE_FILE_MACHINE_AMD64: + case IMAGE_FILE_MACHINE_IA64: + { + reader.SeekRelative(4); // EndAddress + uint32_t unwindRva = reader.Read32(); + DefineDataVariable(m_imageBase + unwindRva, Type::NamedType(this, unwindInfo)); + unwindReader.Seek(RVAToFileOffset(unwindRva)); + uint32_t unwindInformation = unwindReader.Read32(); + uint8_t unwindCodeCount = (unwindInformation >> 16) & 0xff; + if (unwindCodeCount > 0) + DefineDataVariable(m_imageBase + unwindRva + 4, Type::ArrayType(Type::IntegerType(2, false), unwindCodeCount)); + + auto current = m_imageBase + unwindRva + 4 + (unwindCodeCount * 2); + if (current % 4 != 0) + current += 4 - (current % 4); // Align to DWORD + + if (unwindInformation & (UNW_FLAG_CHAININFO << 3)) + { + DefineDataVariable(current, Type::NamedType(this, exceptionEntryTypeName)); + continue; + } + else if ((unwindInformation & (UNW_FLAG_UHANDLER << 3)) || (unwindInformation & (UNW_FLAG_EHANDLER << 3))) + { + DefineDataVariable(current, Type::IntegerType(4, false)); + // unwindReader.Seek(RVAToFileOffset(unwindRva + 8 + (unwindCodeCount * 2))); + // uint32_t count = unwindReader.Read32(); + // DefineDataVariable(current + 4, Type::ArrayType(Type::IntegerType(4, false), 3)); + } + break; + } + default: + break; + } + uint64_t exceptionEntry = m_imageBase + beginAddress; + Ref<Platform> targetPlatform = platform->GetAssociatedPlatformByAddress(exceptionEntry); + AddFunctionForAnalysis(targetPlatform, exceptionEntry); + } + } + } + } + catch (std::exception& e) + { + m_logger->LogWarn("Failed to parse exception directory: %s\n", e.what()); + } + + try + { + if (m_dataDirs.size() > IMAGE_DIRECTORY_ENTRY_DEBUG) + { + PEDataDirectory dir = m_dataDirs[IMAGE_DIRECTORY_ENTRY_DEBUG]; + if (dir.size >= sizeof(DebugDirectory)) + { + + m_logger->LogDebug("Parsing IMAGE_DIRECTORY_ENTRY_DEBUG: %08x", dir.size); + for (uint32_t i = 0; i < dir.size / sizeof(DebugDirectory); i++) + { + reader.Seek(RVAToFileOffset(dir.virtualAddress + i * sizeof(DebugDirectory))); + DebugDirectory debugDir; + reader.Read(&debugDir, sizeof(DebugDirectory)); + + m_logger->LogDebug( + "DebugDirectory:\n" + "\tcharacteristics: %08x\n" + "\ttimeDateStamp: %08x\n" + "\tmajorVersion: %08x\n" + "\tminorVersion: %08x\n" + "\ttype: %08x\n" + "\tsizeOfData: %08x\n" + "\taddressOfRawData: %08x\n" + "\tpointerToRawData: %08x\n", + debugDir.characteristics, + debugDir.timeDateStamp, + debugDir.majorVersion, + debugDir.minorVersion, + debugDir.type, + debugDir.sizeOfData, + RVAToFileOffset(debugDir.addressOfRawData, false), + debugDir.pointerToRawData + ); + + if (!debugDir.addressOfRawData) + continue; + + if (debugDir.type == IMAGE_DEBUG_TYPE_CODEVIEW) // PDB Information + { + auto type = TypeBuilder::IntegerType(4, false); + type.SetIntegerTypeDisplayType(CharacterConstantDisplayType); + DefineDataVariable(m_imageBase + debugDir.addressOfRawData, type.Finalize()); + DefineAutoSymbol(new Symbol(DataSymbol, "debugInfoType", m_imageBase + debugDir.addressOfRawData, NoBinding)); + + + reader.Seek(RVAToFileOffset(debugDir.addressOfRawData)); + uint32_t signature = reader.Read32(); + StoreMetadata("DEBUG_INFO_TYPE", new Metadata((uint64_t)signature), true); + if (signature == 0x53445352) // SDSR + { + vector<uint8_t> guid(16); + reader.Read(&guid[0], 16); + uint32_t age = reader.Read32(); + StoreMetadata("PDB_GUID", new Metadata(guid), true); + StoreMetadata("PDB_AGE", new Metadata((uint64_t)age), true); + string pdbFileName = reader.ReadCString(); + StoreMetadata("PDB_FILENAME", new Metadata(pdbFileName), true); + m_logger->LogInfo("PDBFileName: %s\n", pdbFileName.c_str()); + + DefineDataVariable(m_imageBase + debugDir.addressOfRawData + 4, Type::ArrayType(Type::IntegerType(1, false), 16)); + DefineAutoSymbol(new Symbol(DataSymbol, "PDBGuid", m_imageBase + debugDir.addressOfRawData + 4, NoBinding)); + DefineDataVariable(m_imageBase + debugDir.addressOfRawData + 20, Type::IntegerType(4, false)); + DefineAutoSymbol(new Symbol(DataSymbol, "PDBAge", m_imageBase + debugDir.addressOfRawData + 20, NoBinding)); + DefineDataVariable(m_imageBase + debugDir.addressOfRawData + 24, Type::ArrayType(Type::IntegerType(1, true), pdbFileName.size() + 1)); + DefineAutoSymbol(new Symbol(DataSymbol, "PDBFileName", m_imageBase + debugDir.addressOfRawData + 24, NoBinding)); + } + } + else if (debugDir.type == IMAGE_DEBUG_TYPE_RESERVED10) + { + DefineDataVariable(m_imageBase + debugDir.addressOfRawData, Type::IntegerType(4, false)); + DefineAutoSymbol(new Symbol(DataSymbol, "debugTypeReserved", m_imageBase + debugDir.addressOfRawData, NoBinding)); + } + else + { + DefineDataVariable(m_imageBase + debugDir.addressOfRawData, Type::ArrayType(Type::IntegerType(1, false), debugDir.sizeOfData)); + string name = GetDebugTypeName(debugDir.type); + DefineAutoSymbol(new Symbol(DataSymbol, name, m_imageBase + debugDir.addressOfRawData, NoBinding)); + } + } + + // Create Debug Directory Type + StructureBuilder debugDirBuilder; + debugDirBuilder.AddMember(Type::IntegerType(4, false), "characteristics"); + debugDirBuilder.AddMember(Type::IntegerType(4, false), "timeDateStamp"); + debugDirBuilder.AddMember(Type::IntegerType(2, false), "majorVersion"); + debugDirBuilder.AddMember(Type::IntegerType(2, false), "minorVersion"); + EnumerationBuilder debugType; + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_UNKNOWN", IMAGE_DEBUG_TYPE_UNKNOWN); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_COFF", IMAGE_DEBUG_TYPE_COFF); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_CODEVIEW", IMAGE_DEBUG_TYPE_CODEVIEW); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_FPO", IMAGE_DEBUG_TYPE_FPO); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_MISC", IMAGE_DEBUG_TYPE_MISC); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_EXCEPTION", IMAGE_DEBUG_TYPE_EXCEPTION); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_FIXUP", IMAGE_DEBUG_TYPE_FIXUP); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_OMAP_TO_SRC", IMAGE_DEBUG_TYPE_OMAP_TO_SRC); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_OMAP_FROM_SRC", IMAGE_DEBUG_TYPE_OMAP_FROM_SRC); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_BORLAND", IMAGE_DEBUG_TYPE_BORLAND); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_RESERVED10", IMAGE_DEBUG_TYPE_RESERVED10); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_CLSID", IMAGE_DEBUG_TYPE_CLSID); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_VC_FEATURE", IMAGE_DEBUG_TYPE_VC_FEATURE); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_POGO", IMAGE_DEBUG_TYPE_POGO); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_ILTCG", IMAGE_DEBUG_TYPE_ILTCG); + debugType.AddMemberWithValue("IMAGE_DEBUG_TYPE_MPX", IMAGE_DEBUG_TYPE_MPX); + debugDirBuilder.AddMember(Type::EnumerationType(debugType.Finalize(), 4), "type"); + debugDirBuilder.AddMember(Type::IntegerType(4, false), "sizeOfData"); + debugDirBuilder.AddMember(Type::IntegerType(4, false), "addressOfRawData"); + debugDirBuilder.AddMember(Type::IntegerType(4, false), "pointerToRawData"); + + size_t numDebugEntries = dir.size / 24; + Ref<Structure> debugDirStruct = debugDirBuilder.Finalize(); + Ref<Type> debugDirType = Type::StructureType(debugDirStruct); + QualifiedName debugDirName = string("Debug_Directory_Table"); + string debugDirTypeId = Type::GenerateAutoTypeId("pe", debugDirName); + QualifiedName debugDirTypeName = DefineType(debugDirTypeId, debugDirName, debugDirType); + DefineDataVariable(m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_DEBUG].virtualAddress, Type::ArrayType(Type::NamedType(this, debugDirTypeName), numDebugEntries)); + DefineAutoSymbol(new Symbol(DataSymbol, "__debug_directory_entries", m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_DEBUG].virtualAddress, NoBinding)); + } + } + } + catch (std::exception& e) + { + m_logger->LogWarn("Failed to parse debug directory: %s\n", e.what()); + } + + try + { + if (m_dataDirs.size() > IMAGE_DIRECTORY_ENTRY_TLS) + { + PEDataDirectory dir = m_dataDirs[IMAGE_DIRECTORY_ENTRY_TLS]; + if (dir.size != 0) + { + reader.Seek(RVAToFileOffset(dir.virtualAddress)); + ImageTLSDirectory tlsEntry; + if (m_is64) + { + tlsEntry.startAddressOfRawData = reader.Read64(); + tlsEntry.endAddressOfRawData = reader.Read64(); + tlsEntry.addressOfIndex = reader.Read64(); + tlsEntry.addressOfCallBacks = reader.Read64(); + tlsEntry.sizeOfZeroFill = reader.Read32(); + tlsEntry.characteristics = reader.Read32(); + } + else + { + tlsEntry.startAddressOfRawData = reader.Read32(); + tlsEntry.endAddressOfRawData = reader.Read32(); + tlsEntry.addressOfIndex = reader.Read32(); + tlsEntry.addressOfCallBacks = reader.Read32(); + tlsEntry.sizeOfZeroFill = reader.Read32(); + tlsEntry.characteristics = reader.Read32(); + } + + m_logger->LogDebug( + "Parsing IMAGE_DIRECTORY_ENTRY_TLS: %08x\n" + "\tstartAddressOfRawData %016x\n" + "\tendAddressOfRawData %016x\n" + "\taddressOfIndex %016x\n" + "\taddressOfCallBacks %016x\n" + "\tsizeOfZeroFill %08x\n" + "\tcharacteristics %08x\n", + dir.size, + tlsEntry.startAddressOfRawData, + tlsEntry.endAddressOfRawData, + tlsEntry.addressOfIndex, + tlsEntry.addressOfCallBacks, + tlsEntry.sizeOfZeroFill, + tlsEntry.characteristics + ); + + uint64_t address = 0; + uint32_t i = 0; + try + { + // TODO: I'm pretty sure we're going to have to change this + // when we deal with relocations properly + reader.Seek(RVAToFileOffset(tlsEntry.addressOfCallBacks - m_peImageBase)); + while (true) + { + if (m_is64) + address = reader.Read64(); + else + address = reader.Read32(); + + if (address == 0) + break; + + // This address is a VA, we must handle the relocation by ourselves + address += (m_imageBase - m_peImageBase); + + char name[64]; + snprintf(name, sizeof(name), "_TLS_Entry_%x", i++); + if (m_arch) + { + if (IsOffsetBackedByFile(address)) + { + m_logger->LogInfo("Found TLS entrypoint %s: 0x%" PRIx64, name, address); + Ref<Platform> assPlatform = platform->GetAssociatedPlatformByAddress(address); + AddPESymbol(FunctionSymbol, "", name, address - m_imageBase); + } + else + m_logger->LogInfo("Found TLS entrypoint %s: 0x%" PRIx64 " however it is not backed by file!", + name, address); + } + } + } + catch (std::exception&) + { + m_logger->LogWarn("TLS data is malformed"); + } + + // Create TLS Directory Type + size_t opFieldSize = m_is64 ? 8 : 4; + StructureBuilder tlsDirBuilder; + tlsDirBuilder.SetPacked(true); + tlsDirBuilder.AddMember(Type::IntegerType(opFieldSize, false), "rawDataStartVirtualAddress"); + tlsDirBuilder.AddMember(Type::IntegerType(opFieldSize, false), "rawDataEndVirtualAddress"); + tlsDirBuilder.AddMember(Type::IntegerType(opFieldSize, false), "addressOfIndex"); + tlsDirBuilder.AddMember(Type::IntegerType(opFieldSize, false), "addressOfCallbacks"); + tlsDirBuilder.AddMember(Type::IntegerType(4, false), "sizeOfZeroFill"); + tlsDirBuilder.AddMember(Type::IntegerType(4, false), "characteristics"); + + Ref<Structure> tlsDirStruct = tlsDirBuilder.Finalize(); + Ref<Type> tlsDirType = Type::StructureType(tlsDirStruct); + QualifiedName tlsDirName = string("TLS_Directory"); + string tlsDirTypeId = Type::GenerateAutoTypeId("pe", tlsDirName); + QualifiedName tlsDirTypeName = DefineType(tlsDirTypeId, tlsDirName, tlsDirType); + DefineDataVariable(m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_TLS].virtualAddress, Type::NamedType(this, tlsDirTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__tls_directory", m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_TLS].virtualAddress, NoBinding)); + } + } + } + catch (std::exception& e) + { + m_logger->LogWarn("Failed to parse TLS directory: %s\n", e.what()); + } + + try + { + PEDataDirectory dir; + if (m_dataDirs.size() > IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT) + dir = m_dataDirs[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT]; + else + dir.virtualAddress = 0; + + if (dir.virtualAddress > 0) + { + size_t numImportDelayEntries = 0; + + while (true) + { + // Read in next delay directory entry + reader.Seek(RVAToFileOffset(dir.virtualAddress + (numImportDelayEntries * 32))); + DelayImportDescriptorEntry entry; + entry.attributes = reader.Read32(); + entry.name = reader.Read32(); + entry.moduleHandle = reader.Read32(); + entry.delayImportAddressTable = reader.Read32(); + entry.delayImportNameTable = reader.Read32(); + entry.boundDelayImportTable = reader.Read32(); + entry.unloadDelayImportTable = reader.Read32(); + entry.timestamp = reader.Read32(); + + if (entry.name == 0) + { + if (numImportDelayEntries + 1 != dir.size / 32) + m_logger->LogWarn( + "The number of Import_Directory_Table reported by the Data Directories is different from " + "its correct amount. " + "There are actually %d Import_Directory_Table in the file, but SizeOfImportTable reports %d. " + "The PE parsing continues with the actual number of Import_Directory_Table", + numImportDelayEntries + 1, dir.size / 32); + break; + } + + // https://reverseengineering.stackexchange.com/questions/16261/should-the-delay-import-directory-contain-virtual-addresses + // When the attributes has the lowest bit set, the addresses are RVA. + // For older binaries, e.g., those generated by VC 6.0, the lowest bit is zero, and the addresses are VA. + bool isAddrRVA = entry.attributes & PE_DLATTR_RVA; + if (!isAddrRVA) + { + entry.name -= m_imageBase; + entry.moduleHandle -= m_imageBase; + entry.delayImportAddressTable -= m_imageBase; + entry.delayImportNameTable -= m_imageBase; + entry.boundDelayImportTable -= m_imageBase; + entry.unloadDelayImportTable -= m_imageBase; + } + + string entryName = ReadString(entry.name); + string lowerName = entryName; + std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), + [](unsigned char c){ return std::tolower(c); }); + + + vector<Ref<TypeLibrary>> typeLibs = platform->GetTypeLibrariesByName(lowerName); + for (const auto& typeLib : typeLibs) + { + // Check if the type library is already added + if (GetTypeLibrary(typeLib->GetName())) + continue; + AddTypeLibrary(typeLib); + + m_logger->LogDebug("pe: adding type library for '%s': %s (%s)", lowerName.c_str(), + typeLib->GetName().c_str(), typeLib->GetGuid().c_str()); + } + + Ref<Metadata> ordinals; + for (const auto& typeLib : typeLibs) + ordinals = typeLib->QueryMetadata("ordinals"); + if (ordinals && !ordinals->IsKeyValueStore()) + ordinals = nullptr; + + size_t dotPos = entryName.rfind('.'); + string dllName; + if (dotPos == string::npos) + dllName = entryName; + else + dllName = entryName.substr(0, dotPos); + + // Create Delay Import DLL Name Type + DefineDataVariable(m_imageBase + entry.name, Type::ArrayType(Type::IntegerType(1, true), entryName.size() + 1)); + DefineAutoSymbol(new Symbol(DataSymbol, "__delay_import_dll_name_" + to_string(numImportDelayEntries) + "(" + dllName + ")", m_imageBase + entry.name, NoBinding)); + + // Parse delay import name table + uint32_t entryOffset = entry.delayImportNameTable; + uint32_t iatOffset = entry.delayImportAddressTable; + while (true) + { + uint64_t entry; + bool isOrdinal; + if (m_is64) + { + entry = Read64(entryOffset); + isOrdinal = (entry & 0x8000000000000000LL) != 0; + entry &= 0x7fffffffffffffffLL; + DefineDataVariable(m_imageBase + entryOffset, Type::IntegerType(8, false)); + } + else + { + entry = Read32(entryOffset); + isOrdinal = (entry & 0x80000000) != 0; + entry &= 0x7fffffff; + DefineDataVariable(m_imageBase + entryOffset, Type::IntegerType(4, false)); + } + m_logger->LogDebug("Entry 0x%llx isOrdinal: %s\n", entry, isOrdinal ? "True" : "False"); + + if ((!isOrdinal) && (entry == 0)) + break; + + if (!isAddrRVA) + entry -= m_imageBase; + + string func; + uint16_t ordinal; + if (isOrdinal) + { + ordinal = (uint16_t)entry; + string ordString = to_string(ordinal); + Ref<Metadata> ordInfo = nullptr; + + if (ordinals) + ordInfo = ordinals->Get(ordString); + + if (ordInfo && ordInfo->IsString()) + func = ordInfo->GetString(); + else + func = "Ordinal_" + dllName + "_" + to_string((int)entry); + } + else + { + ordinal = Read16(entry); + func = ReadString(entry + 2); + DefineDataVariable(m_imageBase + entry, Type::IntegerType(2, false)); + DefineAutoSymbol(new Symbol(DataSymbol, "__delay_export_name_ptr_table_" + to_string(numImportDelayEntries) + "(" + dllName + ":" + func + ")", m_imageBase + entry, NoBinding)); + DefineDataVariable(m_imageBase + entry + 2, Type::ArrayType(Type::IntegerType(1, true), func.size() + 1)); + DefineAutoSymbol(new Symbol(DataSymbol, "__delay_import_name_" + to_string(numImportDelayEntries) + "(" + dllName + ":" + func + ")", m_imageBase + entry + 2, NoBinding)); + DefineAutoSymbol(new Symbol(DataSymbol, "__delay_import_lookup_table_" + to_string(numImportDelayEntries) + "(" + dllName + ":" + func + ")", m_imageBase + entryOffset, NoBinding)); + } + m_logger->LogDebug("FuncString: %s\n", func.c_str()); + AddPESymbol(ImportAddressSymbol, dllName, func, iatOffset, NoBinding, ordinal, typeLibs); + AddPESymbol(ExternalSymbol, dllName, func, 0, NoBinding, ordinal, typeLibs); + BNRelocationInfo reloc; + memset(&reloc, 0, sizeof(reloc)); + reloc.nativeType = -1; + reloc.address = m_imageBase + iatOffset; + reloc.size = m_is64 ? 8 : 4; + reloc.pcRelative = false; + reloc.base = m_imageBase - m_peImageBase; + reloc.external = true; + relocs.push_back({reloc, func}); + entryOffset += m_is64 ? 8 : 4; + iatOffset += m_is64 ? 8 : 4; + } + + numImportDelayEntries++; + } + + if (numImportDelayEntries) + { + // Create Delay Import Descriptor Type + StructureBuilder delayImportDirBuilder; + delayImportDirBuilder.AddMember(Type::IntegerType(4, false), "attributes"); + delayImportDirBuilder.AddMember(Type::IntegerType(4, false), "name"); + delayImportDirBuilder.AddMember(Type::IntegerType(4, false), "moduleHandle"); + delayImportDirBuilder.AddMember(Type::IntegerType(4, false), "delayImportAddressTable"); + delayImportDirBuilder.AddMember(Type::IntegerType(4, false), "delayImportNameTable"); + delayImportDirBuilder.AddMember(Type::IntegerType(4, false), "boundDelayImportTable"); + delayImportDirBuilder.AddMember(Type::IntegerType(4, false), "unloadDelayImportTable"); + delayImportDirBuilder.AddMember(Type::IntegerType(4, false), "timestamp"); + + Ref<Structure> delayImportDirStruct = delayImportDirBuilder.Finalize(); + Ref<Type> delayImportDirType = Type::StructureType(delayImportDirStruct); + QualifiedName delayImportDirName = string("Delay_Import_Directory"); + string delayImportDirTypeId = Type::GenerateAutoTypeId("pe", delayImportDirName); + QualifiedName delayImportDirTypeName = DefineType(delayImportDirTypeId, delayImportDirName, delayImportDirType); + DefineDataVariable(m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].virtualAddress, Type::ArrayType(Type::NamedType(this, delayImportDirTypeName), numImportDelayEntries + 1)); + DefineAutoSymbol(new Symbol(DataSymbol, "__delay_import_directory_entries", m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].virtualAddress, NoBinding)); + } + } + } + catch (std::exception& e) + { + m_logger->LogWarn("Failed to parse Delay Import Descriptor directory: %s\n", e.what()); + } + + try + { + if ((m_dataDirs.size() > IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG) && (m_dataDirs[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].size >= 40)) + { + reader.Seek(RVAToFileOffset(m_dataDirs[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].virtualAddress)); + uint32_t loadConfigSize = reader.Read32(); + if (!loadConfigSize || (loadConfigSize > 0x80)) + loadConfigSize = m_dataDirs[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].size; + + // Create Load Configuration Directory Table Type + StructureBuilder loadConfigBuilder; + size_t opFieldSize = m_is64 ? 8 : 4; + loadConfigBuilder.SetPacked(true); + loadConfigBuilder.AddMember(Type::IntegerType(4, false), "characteristics"); + loadConfigBuilder.AddMember(Type::IntegerType(4, false), "timeDateStamp"); + loadConfigBuilder.AddMember(Type::IntegerType(2, false), "majorVersion"); + loadConfigBuilder.AddMember(Type::IntegerType(2, false), "minorVersion"); + loadConfigBuilder.AddMember(Type::IntegerType(4, false), "globalFlagsClear"); + loadConfigBuilder.AddMember(Type::IntegerType(4, false), "globalFlagsSet"); + loadConfigBuilder.AddMember(Type::IntegerType(4, false), "criticalSectionDefaultTimeout"); + loadConfigBuilder.AddMember(Type::IntegerType(opFieldSize, false), "deCommitFreeBlockThreshold"); + loadConfigBuilder.AddMember(Type::IntegerType(opFieldSize, false), "deCommitTotalFreeThreshold"); + loadConfigBuilder.AddMember(Type::IntegerType(opFieldSize, false), "lockPrefixTable"); + loadConfigBuilder.AddMember(Type::IntegerType(opFieldSize, false), "maximumAllocationSize"); + loadConfigBuilder.AddMember(Type::IntegerType(opFieldSize, false), "virtualMemoryThreshold"); + loadConfigBuilder.AddMember(Type::IntegerType(opFieldSize, false), "processAffinityMask"); + loadConfigBuilder.AddMember(Type::IntegerType(4, false), "processHeapFlags"); + loadConfigBuilder.AddMember(Type::IntegerType(2, false), "csdVersion"); + loadConfigBuilder.AddMember(Type::IntegerType(2, false), "reserved"); + loadConfigBuilder.AddMember(Type::IntegerType(opFieldSize, false), "editList"); + loadConfigBuilder.AddMember(Type::IntegerType(opFieldSize, false), "securityCookie"); + + // 32-bit images specify a size of 0x40 for compatability reasons + size_t curSize = (m_is64 ? 0x70 : 0x40); + vector<pair<Ref<Type>, string>> fields = { + {Type::IntegerType(opFieldSize, false), "seHandlerTable" }, + {Type::IntegerType(opFieldSize, false), "seHandlerCount" }, + {Type::IntegerType(opFieldSize, false), "guardCFCheckFunctionPointer" }, + {Type::IntegerType(opFieldSize, false), "guardCFDispatchFunctionPointer" }, + {Type::IntegerType(opFieldSize, false), "guardCFFunctionTable" }, + {Type::IntegerType(opFieldSize, false), "guardCFFunctionCount" }, + {Type::IntegerType(4, false), "guardFlags" }, + {Type::IntegerType(2, false), "Flags" }, + {Type::IntegerType(2, false), "Catalog" }, + {Type::IntegerType(4, false), "CatalogOffset" }, + {Type::IntegerType(4, false), "Reserved" }, + {Type::IntegerType(opFieldSize, false), "guardAddressTakenIatEntryTable" }, + {Type::IntegerType(opFieldSize, false), "guardAddressTakenIatEntryCount" }, + {Type::IntegerType(opFieldSize, false), "guardLongJumpTargetTable" }, + {Type::IntegerType(opFieldSize, false), "guardLongJumpTargetCount" }, + {Type::IntegerType(opFieldSize, false), "dynamicValueRelocTable" }, + {Type::IntegerType(opFieldSize, false), "CHPEMetadataPointer" }, + {Type::IntegerType(opFieldSize, false), "guardRFFailureRoutine" }, + {Type::IntegerType(opFieldSize, false), "guardRFFailureRoutineFunctionPointer" }, + {Type::IntegerType(4, false), "dynamicValueRelocTableOffset" }, + {Type::IntegerType(2, false), "dynamicValueRelocTableSection" }, + {Type::IntegerType(2, false), "reserved2" }, + {Type::IntegerType(opFieldSize, false), "guardRFVerifyStackPointerFunctionPointer" }, + {Type::IntegerType(4, false), "hotPatchTableOffset" }, + {Type::IntegerType(4, false), "reserved3" }, + {Type::IntegerType(opFieldSize, false), "enclaveConfigurationPointer" }, + {Type::IntegerType(opFieldSize, false), "volatileMetadataPointer" }, + {Type::IntegerType(opFieldSize, false), "guardEHContinuationTable" }, + {Type::IntegerType(opFieldSize, false), "guardEHContinuationCount" }, + {Type::IntegerType(opFieldSize, false), "guardXFGCheckFunctionPointer" }, + {Type::IntegerType(opFieldSize, false), "guardXFGDispatchFunctionPointer" }, + {Type::IntegerType(opFieldSize, false), "guardXFGTableDispatchFunctionPointer" }, + {Type::IntegerType(opFieldSize, false), "castGuardOsDeterminedFailureMode" }, + {Type::IntegerType(opFieldSize, false), "guardMemcpyFunctionPointer" } + }; + + for (const auto& [type, name] : fields) + { + curSize += type->GetWidth(); + if (curSize > loadConfigSize) + break; + loadConfigBuilder.AddMember(type, name); + } + + Ref<Structure> loadConfigStruct = loadConfigBuilder.Finalize(); + Ref<Type> loadConfigType = Type::StructureType(loadConfigStruct); + QualifiedName loadConfigName = string("Load_Configuration_Directory_Table"); + string loadConfigTypeId = Type::GenerateAutoTypeId("pe", loadConfigName); + QualifiedName loadConfigTypeName = DefineType(loadConfigTypeId, loadConfigName, loadConfigType); + DefineDataVariable(m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].virtualAddress, Type::NamedType(this, loadConfigTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__load_configuration_directory_table", m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].virtualAddress, NoBinding)); + + // parse securityCookie + size_t securityCookieOffset = m_is64 ? 88 : 60; + reader.Seek(RVAToFileOffset(m_dataDirs[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].virtualAddress + securityCookieOffset)); + auto securityCookieAddress = m_is64 ? reader.Read64() : reader.Read32(); + // The securityCookieAddress reported by the file is a VA, which does not account for rebase. We must + // calculate the rebased value for it. + securityCookieAddress += (m_imageBase - m_peImageBase); + m_logger->LogDebug("securityCookieAddress: 0x%" PRIx64, securityCookieAddress); + DefineDataVariable(securityCookieAddress, Type::IntegerType(m_is64 ? 8 : 4, false)); + DefineAutoSymbol(new Symbol(DataSymbol, "__security_cookie", securityCookieAddress, NoBinding)); + + // parse SEH table + size_t seHandlerTableTableOffset = m_is64 ? 96 : 64; + reader.Seek(RVAToFileOffset(m_dataDirs[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].virtualAddress + seHandlerTableTableOffset)); + uint64_t seHandlerTable = m_is64 ? reader.Read64() : reader.Read32(); + seHandlerTable += (m_imageBase - m_peImageBase); + m_logger->LogDebug("seHandlerTable: 0x%" PRIx64, seHandlerTable); + uint64_t seHandlerCount = m_is64 ? reader.Read64() : reader.Read32(); + if (seHandlerTable && seHandlerCount) + { + DefineDataVariable(seHandlerTable, Type::ArrayType(Type::IntegerType(4, false), seHandlerCount)); + DefineAutoSymbol(new Symbol(DataSymbol, "__seh_table", seHandlerTable, NoBinding)); + + bool processSehTable = true; + if (settings && settings->Contains("loader.pe.processSehTable")) + processSehTable = settings->Get<bool>("loader.pe.processSehTable", this); + if (processSehTable) + { + reader.Seek(RVAToFileOffset(seHandlerTable - m_imageBase)); + for (size_t i = 0; i < seHandlerCount; i++) + { + uint64_t sehEntry = m_imageBase + reader.Read32(); + Ref<Platform> targetPlatform = platform->GetAssociatedPlatformByAddress(sehEntry); + AddFunctionForAnalysis(targetPlatform, sehEntry); + // TODO possibly auto name these entries + //DefineAutoSymbol(new Symbol(FunctionSymbol, "__seh_entry_" + to_string(i), sehEntry)); + } + } + } + + // parse CFG table + if ((loadConfigSize >= (uint32_t)(m_is64 ? 0x94 : 0x40)) && (m_is64 || (opt.dllCharacteristics & IMAGE_DLLCHARACTERISTICS_GUARD_CF))) + { + size_t cfgFields = m_is64 ? 112 : 72; + reader.Seek(RVAToFileOffset(m_dataDirs[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].virtualAddress + cfgFields)); + + uint64_t guardCFCheckFunctionPointer = m_is64 ? reader.Read64() : reader.Read32(); + if (guardCFCheckFunctionPointer != 0) + { + guardCFCheckFunctionPointer += (m_imageBase - m_peImageBase); + m_logger->LogDebug("guardCFCheckFunctionPointer: 0x%" PRIx64, guardCFCheckFunctionPointer); + } + uint64_t guardCFDispatchFunctionPointer = m_is64 ? reader.Read64() : reader.Read32(); + if (guardCFDispatchFunctionPointer != 0) + { + guardCFDispatchFunctionPointer += (m_imageBase - m_peImageBase); + m_logger->LogDebug("guardCFDispatchFunctionPointer: 0x%" PRIx64, guardCFDispatchFunctionPointer); + } + uint64_t guardCFFunctionTable = m_is64 ? reader.Read64() : reader.Read32(); + uint64_t guardCFFunctionCount = m_is64 ? reader.Read64() : reader.Read32(); + uint32_t guardFlags = reader.Read32(); + + uint64_t guardCFCheckFunction = 0; + if (guardCFCheckFunctionPointer != 0) + { + reader.Seek(RVAToFileOffset(guardCFCheckFunctionPointer - m_imageBase)); + guardCFCheckFunction = m_is64 ? reader.Read64() : reader.Read32(); + } + + uint64_t guardCFDispatchFunction = 0; + if (guardCFDispatchFunctionPointer != 0) + { + reader.Seek(RVAToFileOffset(guardCFDispatchFunctionPointer - m_imageBase)); + guardCFDispatchFunction = m_is64 ? reader.Read64() : reader.Read32(); + } + + auto functionPointer = Type::PointerType(platform->GetArchitecture(), Type::FunctionType(Type::VoidType(), platform->GetDefaultCallingConvention(), {})); + auto guardCFCheckFunctionType = Type::FunctionType(Type::VoidType(), + platform->GetDefaultCallingConvention(), + { + FunctionParameter("", functionPointer) + }); + auto pointerGuardCFCheckFunctionType = Type::PointerType(platform->GetArchitecture(), guardCFCheckFunctionType); + + if (guardCFCheckFunctionPointer != 0) + { + auto guardCFCheckPointerSymbol = new Symbol(DataSymbol, "__guard_check_icall_fptr", guardCFCheckFunctionPointer, NoBinding); + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), guardCFCheckPointerSymbol, pointerGuardCFCheckFunctionType); + } + + if (guardCFDispatchFunctionPointer != 0) + { + auto guardCFCheckDispatchSymbol = new Symbol(DataSymbol, "__guard_dispatch_icall_fptr", guardCFDispatchFunctionPointer, NoBinding); + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), guardCFCheckDispatchSymbol, pointerGuardCFCheckFunctionType); + } + + if (guardCFCheckFunction != 0) + { + auto guardCFCheckSymbol = new Symbol(FunctionSymbol, "_guard_check_icall", guardCFCheckFunction, NoBinding); + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), guardCFCheckSymbol, guardCFCheckFunctionType); + } + + if (guardCFDispatchFunction != 0) + { + auto guardCFDispatchSymbol = new Symbol(FunctionSymbol, "_guard_dispatch_icall_nop", guardCFDispatchFunction, NoBinding); + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), guardCFDispatchSymbol, guardCFCheckFunctionType); + } + + if (guardFlags & IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT) + { + size_t mdSize = ((guardFlags & IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK) >> IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT); + + // Create GFIDS Table Type + if (mdSize) + { + StructureBuilder gfidsBuilder; + gfidsBuilder.SetPacked(true); + gfidsBuilder.AddMember(Type::IntegerType(4, false), "rvAddr"); + gfidsBuilder.AddMember(Type::IntegerType(mdSize, false), "metadata"); + + Ref<Structure> gfidsStruct = gfidsBuilder.Finalize(); + Ref<Type> gfidsTableType = Type::StructureType(gfidsStruct); + QualifiedName gfidsTableName = string("Guard_Control_Flow_Function_Table"); + string gfidsTypeId = Type::GenerateAutoTypeId("pe", gfidsTableName); + QualifiedName gfidsTypeName = DefineType(gfidsTypeId, gfidsTableName, gfidsTableType); + DefineDataVariable(guardCFFunctionTable, Type::ArrayType(Type::NamedType(this, gfidsTypeName), guardCFFunctionCount)); + DefineAutoSymbol(new Symbol(DataSymbol, "__gfids_table", guardCFFunctionTable, NoBinding)); + } + else + { + DefineDataVariable(guardCFFunctionTable, Type::ArrayType(Type::IntegerType(4, false), guardCFFunctionCount)); + DefineAutoSymbol(new Symbol(DataSymbol, "__gfids_table", guardCFFunctionTable, NoBinding)); + } + + bool processCfgTable = true; + if (settings && settings->Contains("loader.pe.processCfgTable")) + processCfgTable = settings->Get<bool>("loader.pe.processCfgTable", this); + if (processCfgTable) + { + reader.Seek(RVAToFileOffset(guardCFFunctionTable - m_peImageBase)); + for (size_t i = 0; i < guardCFFunctionCount; i++) + { + uint64_t cfgAddr = m_imageBase + reader.Read32(); + Ref<Platform> targetPlatform = platform->GetAssociatedPlatformByAddress(cfgAddr); + AddFunctionForAnalysis(targetPlatform, cfgAddr); + for (size_t mdIdx = 0; mdIdx < mdSize; mdIdx++) + { + auto value = reader.Read8(); + if (mdIdx == 0 && (value & IMAGE_GUARD_FLAG_FID_XFG) != 0) + { + DefineDataVariable(cfgAddr - 8, Type::IntegerType(8, false)); + } + } + } + } + } + } + } + } + catch (std::exception& e) + { + m_logger->LogWarn("Failed to parse load configuration directory: %s\n", e.what()); + } + + try + { + if ((m_dataDirs.size() > IMAGE_DIRECTORY_ENTRY_EXPORT) && (m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXPORT].size >= 40)) + { + PEExportDirectory dir; + reader.Seek(RVAToFileOffset(m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXPORT].virtualAddress)); + dir.characteristics = reader.Read32(); + dir.timestamp = reader.Read32(); + dir.majorVersion = reader.Read16(); + dir.minorVersion = reader.Read16(); + dir.dllNameAddress = reader.Read32(); + dir.base = reader.Read32(); + dir.functionCount = reader.Read32(); + dir.nameCount = reader.Read32(); + dir.addressOfFunctions = reader.Read32(); + dir.addressOfNames = reader.Read32(); + dir.addressOfNameOrdinals = reader.Read32(); + + // Create Export Directory Table Type + StructureBuilder exportDirBuilder; + exportDirBuilder.SetPacked(true); + exportDirBuilder.AddMember(Type::IntegerType(4, false), "exportFlags"); + exportDirBuilder.AddMember(Type::IntegerType(4, false), "timeDateStamp"); + exportDirBuilder.AddMember(Type::IntegerType(2, false), "majorVersion"); + exportDirBuilder.AddMember(Type::IntegerType(2, false), "minorVersion"); + exportDirBuilder.AddMember(Type::IntegerType(4, false), "nameRva"); + exportDirBuilder.AddMember(Type::IntegerType(4, false), "ordinalBase"); + exportDirBuilder.AddMember(Type::IntegerType(4, false), "addressTableEntries"); + exportDirBuilder.AddMember(Type::IntegerType(4, false), "numberOfNamePointers"); + exportDirBuilder.AddMember(Type::IntegerType(4, false), "exportAddressTableRva"); + exportDirBuilder.AddMember(Type::IntegerType(4, false), "namePointerRva"); + exportDirBuilder.AddMember(Type::IntegerType(4, false), "ordinalTableRva"); + + Ref<Structure> exportDirStruct = exportDirBuilder.Finalize(); + Ref<Type> exportDirType = Type::StructureType(exportDirStruct); + QualifiedName exportDirName = string("Export_Directory_Table"); + string exportDirTypeId = Type::GenerateAutoTypeId("pe", exportDirName); + QualifiedName exportDirTypeName = DefineType(exportDirTypeId, exportDirName, exportDirType); + DefineDataVariable(m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXPORT].virtualAddress, Type::NamedType(this, exportDirTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__export_directory_table", m_imageBase + m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXPORT].virtualAddress, NoBinding)); + + // Read name of imported DLL, and trim extension for creating symbol name + string dllName = ReadString(dir.dllNameAddress); + size_t strPos = dllName.rfind('.'); + string dllShortName = (strPos != string::npos) ? dllName.substr(0, strPos) : dllName; + DefineDataVariable(m_imageBase + dir.dllNameAddress, Type::ArrayType(Type::IntegerType(1, true), dllName.size() + 1)); + DefineAutoSymbol(new Symbol(DataSymbol, "__pe_" + dllShortName + "_export_dll_name", m_imageBase + dir.dllNameAddress, NoBinding)); + + string tableName = "__pe_" + dllShortName + "_export_address_table"; + DefineDataVariable(m_imageBase + dir.addressOfFunctions, Type::ArrayType(Type::IntegerType(4, false), dir.functionCount)); + DefineAutoSymbol(new Symbol(DataSymbol, tableName, m_imageBase + dir.addressOfFunctions, NoBinding)); + + vector<uint32_t> funcs; + reader.Seek(RVAToFileOffset(dir.addressOfFunctions)); + funcs.reserve(dir.functionCount); + for (uint32_t i = 0; i < dir.functionCount; i++) + funcs.push_back(reader.Read32()); + + vector<uint32_t> nameAddrs; + if (dir.addressOfNames != 0) + { + string tableName = "__pe_" + dllShortName + "_export_name_pointer_table"; + DefineDataVariable(m_imageBase + dir.addressOfNames, Type::ArrayType(Type::IntegerType(4, false), dir.nameCount)); + DefineAutoSymbol(new Symbol(DataSymbol, tableName, m_imageBase + dir.addressOfNames, NoBinding)); + + nameAddrs.reserve(dir.nameCount); + reader.Seek(RVAToFileOffset(dir.addressOfNames)); + for (uint32_t i = 0; i < dir.nameCount; i++) + nameAddrs.push_back(reader.Read32()); + } + + vector<uint16_t> nameOrdinals; + if (dir.addressOfNameOrdinals != 0) + { + string tableName = "__pe_" + dllShortName + "_export_ordinal_table"; + DefineDataVariable(m_imageBase + dir.addressOfNameOrdinals, Type::ArrayType(Type::IntegerType(2, false), dir.nameCount)); + DefineAutoSymbol(new Symbol(DataSymbol, tableName, m_imageBase + dir.addressOfNameOrdinals, NoBinding)); + + nameOrdinals.reserve(dir.nameCount); + reader.Seek(RVAToFileOffset(dir.addressOfNameOrdinals)); + for (uint32_t i = 0; i < dir.nameCount; i++) + nameOrdinals.push_back(reader.Read16()); + } + + map<uint16_t, string> namesByOrdinal; + for (uint32_t i = 0; i < dir.nameCount; i++) + { + if (i >= nameOrdinals.size()) + break; + if (i >= nameAddrs.size()) + break; + + string name = ReadString(nameAddrs[i]); + namesByOrdinal[nameOrdinals[i]] = name; + + DefineDataVariable(m_imageBase + nameAddrs[i], Type::ArrayType(Type::IntegerType(1, true), name.size() + 1)); + DefineAutoSymbol(new Symbol(DataSymbol, "__export_name(" + name + ")", m_imageBase + nameAddrs[i], NoBinding)); + } + + // Create symbols for the exports + uint32_t exportTableStart = m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXPORT].virtualAddress; + uint32_t exportTableEnd = exportTableStart + m_dataDirs[IMAGE_DIRECTORY_ENTRY_EXPORT].size; + for (uint32_t i = 0; i < dir.functionCount; i++) + { + uint32_t rvAddr = funcs[i]; + if (rvAddr == 0) + continue; + string name; + auto nameIter = namesByOrdinal.find(i); + if (nameIter == namesByOrdinal.end()) + { + char buff[32]; + snprintf(buff, sizeof(buff), "ordinal_%u", i + dir.base); + name = buff; + } + else + { + name = nameIter->second; + } + uint32_t characteristics = GetRVACharacteristics(rvAddr); + + if ((rvAddr >= exportTableStart) && (rvAddr < exportTableEnd)) + { + string forwarderName = ReadString(rvAddr); + DefineDataVariable(m_imageBase + rvAddr, Type::ArrayType(Type::IntegerType(1, true), forwarderName.size() + 1)); + DefineAutoSymbol(new Symbol(DataSymbol, "__forwarder_name(" + forwarderName + ")", m_imageBase + rvAddr, GlobalBinding, + NameSpace(DEFAULT_INTERNAL_NAMESPACE), i + dir.base)); + } + else + { + if ((characteristics & (PE_ATTR_CODE | PE_ATTR_EXEC)) != 0) + AddPESymbol(FunctionSymbol, "", name, rvAddr, GlobalBinding, i + dir.base); + else if (characteristics != 0) + AddPESymbol(DataSymbol, "", name, rvAddr, GlobalBinding, i + dir.base); + //else // TODO need to handle other data symbols + } + } + } + } + catch (std::exception& e) + { + m_logger->LogWarn("Failed to parse export directory: %s\n", e.what()); + } + + m_symbolQueue->Process(); + delete m_symbolQueue; + m_symbolQueue = nullptr; + + EndBulkModifySymbols(); + + StoreMetadata("SymbolExternalLibraryMapping", m_symExternMappingMetadata, true); + + try + { + if (m_dataDirs.size() > IMAGE_DIRECTORY_ENTRY_BASERELOC) + { + PEDataDirectory dir = m_dataDirs[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + // Check if there is a '.reloc' section that is different than this directory entry + vector<PEDataDirectory> dirs = { m_dataDirs[IMAGE_DIRECTORY_ENTRY_BASERELOC]}; + auto section = find_if(m_sections.begin(), m_sections.end(), [](const PESection& section) { return section.name == ".reloc"; }); + if (section != m_sections.end()) + { + if (section->virtualAddress != dir.virtualAddress) + dirs.push_back({ section->virtualAddress, section->sizeOfRawData }); + } + for (auto& dir : dirs) + { + if (dir.size == 0 || dir.virtualAddress == 0) + continue; + + reader.Seek(RVAToFileOffset(dir.virtualAddress)); + uint64_t size = 0; + while (size < dir.size) + { + ImageBaseRelocation baseReloc; + baseReloc.VirtualAddress = reader.Read32() + m_imageBase; + baseReloc.SizeOfBlock = reader.Read32(); + if (baseReloc.SizeOfBlock < 8) + break; + if (baseReloc.SizeOfBlock == 8) + { + size += baseReloc.SizeOfBlock; + continue; + } + size_t nEntries = (baseReloc.SizeOfBlock - 8) / sizeof(uint16_t); + uint16_t* relocEntries = new uint16_t[nEntries]; + if (relocEntries) + { + reader.Read(relocEntries, nEntries * sizeof(uint16_t)); + for (size_t i = 0; i < nEntries; i++) + { + BNRelocationInfo reloc; + memset(&reloc, 0, sizeof(reloc)); + reloc.nativeType = relocEntries[i] >> 12; + if (!reloc.nativeType) // IMAGE_REL_BASED_ABSOLUTE relocations are skipped/used for padding + continue; + reloc.address = baseReloc.VirtualAddress + (relocEntries[i] & 0xfff); + reloc.size = m_is64 ? 8 : 4; + reloc.pcRelative = false; + reloc.base = m_imageBase - m_peImageBase; + DefineRelocation(m_arch, reloc, 0, reloc.address); + } + delete[] relocEntries; + } + size += baseReloc.SizeOfBlock; + } + } + } + } + catch (std::exception& e) + { + m_logger->LogWarn("Failed to parse relocation directory: %s\n", e.what()); + } + + for (auto& [reloc, name] : relocs) + { + if (auto symbol = GetSymbolByRawName(name, GetExternalNameSpace()); symbol) + DefineRelocation(m_arch, reloc, symbol, reloc.address); + } + + // Add a symbol for the entry point + if (m_entryPoint) + DefineAutoSymbol(new Symbol(FunctionSymbol, "_start", m_imageBase + m_entryPoint)); + 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; + m_logger->LogInfo("PE parsing took %.3f seconds\n", t); + + return true; +} + + +uint64_t PEView::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 PEFormatException("encountered invalid offset"); +} + + +uint32_t PEView::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 PEView::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 PEView::Read16(uint64_t rva) +{ + uint64_t ofs = RVAToFileOffset(rva); + BinaryReader reader(GetParentView(), LittleEndian); + reader.Seek(ofs); + return reader.Read16(); +} + + +uint32_t PEView::Read32(uint64_t rva) +{ + uint64_t ofs = RVAToFileOffset(rva); + BinaryReader reader(GetParentView(), LittleEndian); + reader.Seek(ofs); + return reader.Read32(); +} + + +uint64_t PEView::Read64(uint64_t rva) +{ + uint64_t ofs = RVAToFileOffset(rva); + BinaryReader reader(GetParentView(), LittleEndian); + reader.Seek(ofs); + return reader.Read64(); +} + + +// The addr is RVA +void PEView::AddPESymbol(BNSymbolType type, const string& dll, const string& name, uint64_t addr, + BNSymbolBinding binding, uint64_t ordinal, vector<Ref<TypeLibrary>> libs) +{ + // Don't create symbols that are present in the database snapshot now + if (type != ExternalSymbol && m_backedByDatabase) + return; + + // If name is empty, symbol is not valid + if (name.size() == 0) + return; + + // Ensure symbol is within the executable + if (type != ExternalSymbol) + { + bool ok = false; + for (auto& i : m_sections) + { + if ((addr >= i.virtualAddress) && (addr < (i.virtualAddress + i.virtualSize))) + { + ok = true; + break; + } + } + if (!ok) + return; + } + + auto address = type == ExternalSymbol ? addr : m_imageBase + addr; + Ref<Type> symbolTypeRef; + + if (libs.size() && ((type == ExternalSymbol) || (type == ImportAddressSymbol) || (type == ImportedDataSymbol))) + { + QualifiedName n(name); + for (auto lib : libs) + { + Ref<TypeLibrary> appliedLib = lib; + symbolTypeRef = ImportTypeLibraryObject(appliedLib, n); + if (symbolTypeRef) + { + m_logger->LogDebug("pe: type library '%s' found hit for '%s'", lib->GetGuid().c_str(), name.c_str()); + RecordImportedObjectLibrary(GetDefaultPlatform(), address, appliedLib, n); + } + } + } + + m_symbolQueue->Append( + [=]() { + // 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; + Ref<Type> typeRef = symbolTypeRef; + + if (m_arch && name.size() > 0) + { + QualifiedName demangleName; + Ref<Type> demangledType; + if (name[0] == '?') + { + if (DemangleMS(m_arch, name, demangledType, demangleName, m_simplifyTemplates)) + { + shortName = demangleName.GetString(); + fullName = shortName + demangledType->GetStringAfterName(); + if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) + typeRef = demangledType; + } + else + { + m_logger->LogDebug("Failed to demangle: '%s'\n", name.c_str()); + } + } + else if (IsGNU3MangledString(rawName)) + { + if (DemangleGNU3(m_arch, rawName, demangledType, demangleName, m_simplifyTemplates)) + { + shortName = demangleName.GetString(); + fullName = shortName; + if (demangledType) + fullName += demangledType->GetStringAfterName(); + if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) + typeRef = demangledType; + } + else + { + m_logger->LogDebug("Failed to demangle name: '%s'\n", rawName.c_str()); + } + } + // Not a mangled string + } + + NameSpace ns(dll); + if (type == ExternalSymbol) + ns = GetExternalNameSpace(); + + return pair<Ref<Symbol>, Ref<Type>>( + new Symbol(type, shortName, fullName, rawName, address, binding, ns, ordinal), + typeRef); + }, + [this](Symbol* symbol, Type* type) { + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), symbol, type); + }); +} + + +uint64_t PEView::PerformGetEntryPoint() const +{ + return m_imageBase + m_entryPoint; +} + + +size_t PEView::PerformGetAddressSize() const +{ + return m_is64 ? 8 : 4; +} + + +PEViewType::PEViewType() : BinaryViewType("PE", "PE") +{ + m_logger = LogRegistry::CreateLogger("BinaryView"); +} + + +Ref<BinaryView> PEViewType::Create(BinaryView* data) +{ + try + { + return new PEView(data); + } + catch (std::exception& e) + { + m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + + +Ref<BinaryView> PEViewType::Parse(BinaryView* data) +{ + try + { + return new PEView(data, true); + } + catch (std::exception& e) + { + m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + + +bool PEViewType::IsTypeValidForData(BinaryView* data) +{ + // Check MZ header signature + DataBuffer sig = data->ReadBuffer(0, 2); + if (sig.GetLength() != 2) + return false; + if (memcmp(sig.GetData(), "MZ", 2) != 0) + return false; + + BinaryReader reader(data, LittleEndian); + + // Read PE offset + uint32_t peOfs; + reader.Seek(0x3c); + if (!reader.TryRead32(peOfs)) + return false; + + // Check PE signature + DataBuffer peSig = data->ReadBuffer(peOfs, 4); + if (peSig.GetLength() != 4) + return false; + if (memcmp(peSig.GetData(), "PE\0\0", 4) != 0) + return false; + + // Check optional header signature + uint16_t magic; + reader.Seek(peOfs + 24); + if (!reader.TryRead16(magic)) + return false; + + return (magic == 0x10b) || (magic == 0x20b); +} + + +Ref<Settings> PEViewType::GetLoadSettingsForData(BinaryView* data) +{ + Ref<BinaryView> viewRef = Parse(data); + if (!viewRef || !viewRef->Init()) + { + m_logger->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); + } + + // register additional settings + settings->RegisterSetting("loader.pe.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.pe.processExceptionTable", + R"({ + "title" : "Process PE 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.pe.processSehTable", + R"({ + "title" : "Process PE 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; +} + + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + +#ifdef DEMO_VERSION + bool PEPluginInit() +#else + BINARYNINJAPLUGIN bool CorePluginInit() +#endif + { + InitPEViewType(); + InitCOFFViewType(); + return true; + } +} diff --git a/view/pe/peview.h b/view/pe/peview.h new file mode 100644 index 00000000..34e1099d --- /dev/null +++ b/view/pe/peview.h @@ -0,0 +1,479 @@ +#pragma once + +#include "binaryninjaapi.h" +#include <exception> + +#ifdef WIN32 +#pragma warning(disable: 4005) +#endif + +#define PE_ATTR_CODE 0x20 +#define PE_ATTR_INIT_DATA 0x40 +#define PE_ATTR_UNINIT_DATA 0x80 +#define PE_ATTR_EXEC 0x20000000 + +// The dalay load table uses RVA, rather than VA +#define PE_DLATTR_RVA 0x1 + +// Relocation information was stripped from the file. The file must be loaded at its preferred base address. +// If the base address is not available, the loader reports an error. +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 +// The file is executable (there are no unresolved external references). +#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 +// COFF line numbers were stripped from the file. +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 +// COFF symbol table entries were stripped from file. +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 +// Aggressively trim the working set. This value is obsolete. +#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 +// The application can handle addresses larger than 2 GB. +#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 +// The bytes of the word are reversed. This flag is obsolete. +#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 +// The computer supports 32-bit words. +#define IMAGE_FILE_32BIT_MACHINE 0x0100 +// Debugging information was removed and stored separately in another file. +#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 +// If the image is on removable media, copy it to and run it from the swap file. +#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 +// If the image is on the network, copy it to and run it from the swap file. +#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 +// The image is a system file. +#define IMAGE_FILE_SYSTEM 0x1000 +// The image is a DLL file. While it is an executable file, it cannot be run directly. +#define IMAGE_FILE_DLL 0x2000 +// The file should be run only on a uniprocessor computer. +#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 +// The bytes of the word are reversed. This flag is obsolete. +#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 + +#define IMAGE_FILE_MACHINE_UNKNOWN 0x0 +#define IMAGE_FILE_MACHINE_AM33 0x1d3 +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_ARM 0x1c0 +#define IMAGE_FILE_MACHINE_ARM64 0xaa64 +#define IMAGE_FILE_MACHINE_ARMNT 0x1c4 +#define IMAGE_FILE_MACHINE_EBC 0xebc +#define IMAGE_FILE_MACHINE_I386 0x14c +#define IMAGE_FILE_MACHINE_IA64 0x200 +#define IMAGE_FILE_MACHINE_M32R 0x9041 +#define IMAGE_FILE_MACHINE_MIPS16 0x266 +#define IMAGE_FILE_MACHINE_MIPSFPU 0x366 +#define IMAGE_FILE_MACHINE_MIPSFPU16 0x466 +#define IMAGE_FILE_MACHINE_POWERPC 0x1f0 +#define IMAGE_FILE_MACHINE_POWERPCFP 0x1f1 +#define IMAGE_FILE_MACHINE_R4000 0x166 +#define IMAGE_FILE_MACHINE_RISCV32 0x5032 +#define IMAGE_FILE_MACHINE_RISCV64 0x5064 +#define IMAGE_FILE_MACHINE_RISCV128 0x5128 +#define IMAGE_FILE_MACHINE_SH3 0x1a2 +#define IMAGE_FILE_MACHINE_SH3DSP 0x1a3 +#define IMAGE_FILE_MACHINE_SH4 0x1a6 +#define IMAGE_FILE_MACHINE_SH5 0x1a8 +#define IMAGE_FILE_MACHINE_THUMB 0x1c2 +#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x169 + +#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 +#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 +#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 +#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 +#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 +#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 +#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 +#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 +#define IMAGE_DIRECTORY_ENTRY_TLS 9 +#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 +#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 +#define IMAGE_DIRECTORY_ENTRY_IAT 12 +#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 +#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 + +#define IMAGE_DEBUG_TYPE_UNKNOWN 0 +#define IMAGE_DEBUG_TYPE_COFF 1 +#define IMAGE_DEBUG_TYPE_CODEVIEW 2 +#define IMAGE_DEBUG_TYPE_FPO 3 +#define IMAGE_DEBUG_TYPE_MISC 4 +#define IMAGE_DEBUG_TYPE_EXCEPTION 5 +#define IMAGE_DEBUG_TYPE_FIXUP 6 +#define IMAGE_DEBUG_TYPE_OMAP_TO_SRC 7 +#define IMAGE_DEBUG_TYPE_OMAP_FROM_SRC 8 +#define IMAGE_DEBUG_TYPE_BORLAND 9 +#define IMAGE_DEBUG_TYPE_RESERVED10 10 +#define IMAGE_DEBUG_TYPE_CLSID 11 +#define IMAGE_DEBUG_TYPE_VC_FEATURE 12 +#define IMAGE_DEBUG_TYPE_POGO 13 +#define IMAGE_DEBUG_TYPE_ILTCG 14 +#define IMAGE_DEBUG_TYPE_MPX 15 +#define IMAGE_DEBUG_TYPE_REPRO 16 +#define IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS 20 + +#define UNW_FLAG_NHANDLER 0x0 // The function has no handler. +#define UNW_FLAG_EHANDLER 0x1 // The function has an exception handler that should be called. +#define UNW_FLAG_UHANDLER 0x2 // The function has a termination handler that should be called when unwinding an exception. +#define UNW_FLAG_CHAININFO 0x4 // The FunctionEntry member is the contents of a previous function table entry. + +#define IMAGE_SUBSYSTEM_UNKNOWN 0 +#define IMAGE_SUBSYSTEM_NATIVE 1 +#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define IMAGE_SUBSYSTEM_OS2_CUI 5 +#define IMAGE_SUBSYSTEM_POSIX_CUI 7 +#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 +#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 +#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 +#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 +#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 +#define IMAGE_SUBSYSTEM_EFI_ROM 13 +#define IMAGE_SUBSYSTEM_XBOX 14 +#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16 + +#define IMAGE_DLLCHARACTERISTICS_0001 0x0001 //Reserved. +#define IMAGE_DLLCHARACTERISTICS_0002 0x0002 //Reserved. +#define IMAGE_DLLCHARACTERISTICS_0004 0x0004 //Reserved. +#define IMAGE_DLLCHARACTERISTICS_0008 0x0008 //Reserved. +#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020 // Image can handle a high entropy 64-bit virtual address space. +#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 // The DLL can be relocated at load time. +#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080 // Code integrity checks are forced. If you set this flag and a section contains only uninitialized data, set the PointerToRawData member of IMAGE_SECTION_HEADER for that section to zero; otherwise, the image will fail to load because the digital signature cannot be verified. +#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 // Image is NX compatible. +#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 // Isolation aware, but do not isolate the image. +#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 // The image does not use structured exception handling (SEH). No handlers can be called in this image. +#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 // Do not bind the image. +#define IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000 // Image must execute in an AppContainer. +#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 // A WDM driver. +#define IMAGE_DLLCHARACTERISTICS_GUARD_CF 0x4000 // Image supports Control Flow Guard. +#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 // Terminal Server aware. + +#define IMAGE_SCN_RESERVED_0001 0x00000001 +#define IMAGE_SCN_RESERVED_0002 0x00000002 +#define IMAGE_SCN_RESERVED_0004 0x00000004 +#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 // The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files. +#define IMAGE_SCN_RESERVED_0010 0x00000010 +#define IMAGE_SCN_CNT_CODE 0x00000020 // The section contains executable code. +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // The section contains initialized data. +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // The section contains uninitialized data. +#define IMAGE_SCN_LNK_OTHER 0x00000100 // Reserved for future use. +#define IMAGE_SCN_LNK_INFO 0x00000200 // The section contains comments or other information. The .drectve section has this type. This is valid for object files only. +#define IMAGE_SCN_RESERVED_0400 0x00000400 +#define IMAGE_SCN_LNK_REMOVE 0x00000800 // The section will not become part of the image. This is valid only for object files. +#define IMAGE_SCN_LNK_COMDAT 0x00001000 // The section contains COMDAT data. For more information, see COMDAT Sections (Object Only). This is valid only for object files. +#define IMAGE_SCN_GPREL 0x00008000 // The section contains data referenced through the global pointer (GP). +#define IMAGE_SCN_MEM_PURGEABLE 0x00010000 // Reserved for future use. +#define IMAGE_SCN_MEM_16BIT 0x00020000 // Reserved for future use. +#define IMAGE_SCN_MEM_LOCKED 0x00040000 // Reserved for future use. +#define IMAGE_SCN_MEM_PRELOAD 0x00080000 // Reserved for future use. +#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 // Align data on a 1-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 // Align data on a 2-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 // Align data on a 4-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 // Align data on an 8-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 // Align data on a 16-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 // Align data on a 32-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 // Align data on a 64-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 // Align data on a 128-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 // Align data on a 256-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_512BYTES 0x00A00000 // Align data on a 512-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_1024BYTES 0x00B00000 // Align data on a 1024-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_2048BYTES 0x00C00000 // Align data on a 2048-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_4096BYTES 0x00D00000 // Align data on a 4096-byte boundary. Valid only for object files. +#define IMAGE_SCN_ALIGN_8192BYTES 0x00E00000 // Align data on an 8192-byte boundary. Valid only for object files. +#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 // The section contains extended relocations. +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 // The section can be discarded as needed. +#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // The section cannot be cached. +#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // The section is not pageable. +#define IMAGE_SCN_MEM_SHARED 0x10000000 // The section can be shared in memory. +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // The section can be executed as code. +#define IMAGE_SCN_MEM_READ 0x40000000 // The section can be read. +#define IMAGE_SCN_MEM_WRITE 0x80000000 // The section can be written to. + +#define IMAGE_GUARD_CF_INSTRUMENTED 0x00000100 // Module performs control flow integrity checks using system-supplied support +#define IMAGE_GUARD_CFW_INSTRUMENTED 0x00000200 // Module performs control flow and write integrity checks +#define IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT 0x00000400 // Module contains valid control flow target metadata +#define IMAGE_GUARD_SECURITY_COOKIE_UNUSED 0x00000800 // Module does not make use of the /GS security cookie +#define IMAGE_GUARD_PROTECT_DELAYLOAD_IAT 0x00001000 // Module supports read only delay load IAT +#define IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION 0x00002000 // Delayload import table in its own .didat section (with nothing else in it) that can be freely reprotected +#define IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT 0x00004000 // Module contains suppressed export information. This also infers that the address taken +// taken IAT table is also present in the load config. + +#define IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION 0x00008000 // Module enables suppression of exports +#define IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT 0x00010000 // Module contains longjmp target information +#define IMAGE_GUARD_RF_INSTRUMENTED 0x00020000 // Module contains return flow instrumentation and metadata +#define IMAGE_GUARD_RF_ENABLE 0x00040000 // Module requests that the OS enable return flow protection +#define IMAGE_GUARD_RF_STRICT 0x00080000 // Module requests that the OS enable return flow protection in strict mode +#define IMAGE_GUARD_RETPOLINE_PRESENT 0x00100000 // Module was built with retpoline support + +#define IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK 0xF0000000 // Stride of Guard CF function table encoded in these bits (additional count of bytes per element) +#define IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT 28 // Shift to right-justify Guard CF function table stride + + +#define IMAGE_GUARD_FLAG_FID_SUPPRESSED 1 // Call target is explicitly suppressed (do not treat it as valid for purposes of CFG) +#define IMAGE_GUARD_FLAG_EXPORT_SUPPRESSED 2 // Call target is export suppressed. See https://docs.microsoft.com/en-us/windows/win32/secbp/pe-metadata#export-suppression for more details +#define IMAGE_GUARD_FLAG_FID_LANGEXCPTHANDLER 4 +#define IMAGE_GUARD_FLAG_FID_XFG 8 // Call target supports XFG + + +// COFF symbol support +#define IMAGE_SYM_DTYPE_NULL 0x0 +#define IMAGE_SYM_DTYPE_POINTER 0x1 +#define IMAGE_SYM_DTYPE_FUNCTION 0x2 +#define IMAGE_SYM_DTYPE_ARRAY 0x3 + +#define IMAGE_SYM_UNDEFINED 0 +#define IMAGE_SYM_ABSOLUTE -1 +#define IMAGE_SYM_DEBUG -2 + +#define IMAGE_SYM_CLASS_NULL 0 +#define IMAGE_SYM_CLASS_AUTOMATIC 1 +#define IMAGE_SYM_CLASS_EXTERNAL 2 +#define IMAGE_SYM_CLASS_STATIC 3 +#define IMAGE_SYM_CLASS_REGISTER 4 +#define IMAGE_SYM_CLASS_EXTERNAL_DEF 5 +#define IMAGE_SYM_CLASS_LABEL 6 +#define IMAGE_SYM_CLASS_UNDEFINED_LABEL 7 +#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8 +#define IMAGE_SYM_CLASS_ARGUMENT 9 +#define IMAGE_SYM_CLASS_STRUCT_TAG 10 +#define IMAGE_SYM_CLASS_MEMBER_OF_UNION 11 +#define IMAGE_SYM_CLASS_UNION_TAG 12 +#define IMAGE_SYM_CLASS_TYPE_DEFINITION 13 +#define IMAGE_SYM_CLASS_UNDEFINED_STATIC 14 +#define IMAGE_SYM_CLASS_ENUM_TAG 15 +#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16 +#define IMAGE_SYM_CLASS_REGISTER_PARAM 17 +#define IMAGE_SYM_CLASS_BIT_FIELD 18 +#define IMAGE_SYM_CLASS_AUTOARG 19 +#define IMAGE_SYM_CLASS_LASTENT 20 +#define IMAGE_SYM_CLASS_BLOCK 100 +#define IMAGE_SYM_CLASS_FUNCTION 101 +#define IMAGE_SYM_CLASS_END_OF_STRUCT 102 +#define IMAGE_SYM_CLASS_FILE 103 +#define IMAGE_SYM_CLASS_SECTION 104 +#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 105 +#define IMAGE_SYM_CLASS_HIDDEN 106 +#define IMAGE_SYM_CLASS_CLR_TOKEN 107 +#define IMAGE_SYM_CLASS_END_OF_FUNCTION 255 + +namespace BinaryNinja +{ + class PEFormatException: public std::exception + { + std::string m_message; + + public: + PEFormatException(const std::string& msg = "invalid format"): m_message(msg) {} + virtual const char* what() const NOEXCEPT { return m_message.c_str(); } + }; + + struct PEHeader + { + uint32_t magic; + uint16_t machine; + uint16_t sectionCount; + uint32_t timestamp; + uint32_t coffSymbolTable; + uint32_t coffSymbolCount; + uint16_t optionalHeaderSize; + uint16_t characteristics; + }; + + struct PEOptionalHeader + { + uint16_t magic; + uint8_t majorLinkerVersion; + uint8_t minorLinkerVersion; + uint32_t sizeOfCode; + uint32_t sizeOfInitData; + uint32_t sizeOfUninitData; + uint32_t addressOfEntry; + uint32_t baseOfCode; + uint32_t baseOfData; + uint64_t imageBase; + uint32_t sectionAlign; + uint32_t fileAlign; + uint16_t majorOSVersion; + uint16_t minorOSVersion; + uint16_t majorImageVersion; + uint16_t minorImageVersion; + uint16_t majorSubsystemVersion; + uint16_t minorSubsystemVersion; + uint32_t win32Version; + uint32_t sizeOfImage; + uint32_t sizeOfHeaders; + uint32_t checksum; + uint16_t subsystem; + uint16_t dllCharacteristics; + uint64_t sizeOfStackReserve; + uint64_t sizeOfStackCommit; + uint64_t sizeOfHeapReserve; + uint64_t sizeOfHeapCommit; + uint32_t loaderFlags; + uint32_t dataDirCount; + }; + + struct PEDataDirectory + { + uint32_t virtualAddress; + uint32_t size; + }; + + struct PESection + { + std::string name; + uint32_t virtualSize; + uint32_t virtualAddress; + uint32_t sizeOfRawData; + uint32_t pointerToRawData; + uint32_t pointerToRelocs; + uint32_t pointerToLineNumbers; + uint16_t relocCount; + uint16_t lineNumberCount; + uint32_t characteristics; + }; + + struct PEImportDirectoryEntry + { + uint32_t lookup; + uint32_t timestamp; + uint32_t forwardChain; + uint32_t nameAddress; + uint32_t iat; + std::string name; + }; + + struct PEExportDirectory + { + uint32_t characteristics; + uint32_t timestamp; + uint16_t majorVersion; + uint16_t minorVersion; + uint32_t dllNameAddress; + uint32_t base; + uint32_t functionCount; + uint32_t nameCount; + uint32_t addressOfFunctions; + uint32_t addressOfNames; + uint32_t addressOfNameOrdinals; + }; + + struct DebugDirectory + { + uint32_t characteristics; + uint32_t timeDateStamp; + uint16_t majorVersion; + uint16_t minorVersion; + uint32_t type; + uint32_t sizeOfData; + uint32_t addressOfRawData; + uint32_t pointerToRawData; + }; + + struct ImageTLSDirectory + { + uint64_t startAddressOfRawData; + uint64_t endAddressOfRawData; + uint64_t addressOfIndex; + uint64_t addressOfCallBacks; + uint32_t sizeOfZeroFill; + uint32_t characteristics; + }; + + struct DelayImportDescriptorEntry + { + uint32_t attributes; + uint32_t name; + uint32_t moduleHandle; + uint32_t delayImportAddressTable; + uint32_t delayImportNameTable; + uint32_t boundDelayImportTable; + uint32_t unloadDelayImportTable; + uint32_t timestamp; + }; + + struct ImageBaseRelocation + { + uint64_t VirtualAddress; + uint32_t SizeOfBlock; + }; + + struct CodeViewHeader + { + uint32_t signature; + uint32_t offset; + }; + + struct CodeViewInfoPDB20 + { + uint32_t signature; + uint32_t age; + }; + + struct GUID + { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; + }; + + struct CodeViewInfoPDB70 + { + GUID Signature; + uint32_t Age; + }; + + class PEView: public BinaryView + { + bool m_parseOnly, m_backedByDatabase; + uint64_t m_entryPoint; + + uint64_t m_peImageBase; + uint64_t m_imageBase; + uint32_t m_sizeOfHeaders; + std::vector<PEDataDirectory> m_dataDirs; + std::vector<PESection> m_sections; + Ref<Architecture> m_arch; + bool m_is64; + bool m_extractMangledTypes; + bool m_simplifyTemplates; + Ref<Logger> m_logger; + bool m_relocatable = false; + + SymbolQueue* m_symbolQueue = nullptr; + + Ref<Metadata> m_symExternMappingMetadata; + + uint64_t RVAToFileOffset(uint64_t rva, bool except = true); + uint32_t GetRVACharacteristics(uint64_t rva); + std::string ReadString(uint64_t rva); + uint16_t Read16(uint64_t rva); + uint32_t Read32(uint64_t rva); + uint64_t Read64(uint64_t rva); + void AddPESymbol(BNSymbolType type, const std::string& dll, const std::string& name, uint64_t addr, + BNSymbolBinding binding = NoBinding, uint64_t ordinal = 0, std::vector<Ref<TypeLibrary>> lib = {}); + + protected: + virtual uint64_t PerformGetEntryPoint() const override; + + virtual bool PerformIsExecutable() const override { return true; } + virtual BNEndianness PerformGetDefaultEndianness() const override { return LittleEndian; } + virtual bool PerformIsRelocatable() const override { return m_relocatable; } + virtual size_t PerformGetAddressSize() const override; + + public: + PEView(BinaryView* data, bool parseOnly = false); + + virtual bool Init() override; + }; + + class PEViewType: public BinaryViewType + { + Ref<Logger> m_logger; + public: + PEViewType(); + virtual Ref<BinaryView> Create(BinaryView* data) override; + virtual Ref<BinaryView> Parse(BinaryView* data) override; + virtual bool IsTypeValidForData(BinaryView* data) override; + virtual Ref<Settings> GetLoadSettingsForData(BinaryView* data) override; + }; + + void InitPEViewType(); +} |
