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 | |
| parent | 47894a1ce20090849ad304c68e6c0b889665eda3 (diff) | |
Move view modules into the API repo
Diffstat (limited to 'view')
| -rw-r--r-- | view/elf/.gitignore | 8 | ||||
| -rw-r--r-- | view/elf/CMakeLists.txt | 33 | ||||
| -rw-r--r-- | view/elf/LICENSE | 13 | ||||
| -rw-r--r-- | view/elf/README.md | 27 | ||||
| -rw-r--r-- | view/elf/elfview.cpp | 2772 | ||||
| -rw-r--r-- | view/elf/elfview.h | 558 | ||||
| -rw-r--r-- | view/macho/.gitignore | 8 | ||||
| -rw-r--r-- | view/macho/CMakeLists.txt | 35 | ||||
| -rw-r--r-- | view/macho/LICENSE | 13 | ||||
| -rw-r--r-- | view/macho/README.md | 30 | ||||
| -rw-r--r-- | view/macho/fatmachoview.cpp | 266 | ||||
| -rw-r--r-- | view/macho/fatmachoview.h | 50 | ||||
| -rw-r--r-- | view/macho/machoview.cpp | 3399 | ||||
| -rw-r--r-- | view/macho/machoview.h | 1375 | ||||
| -rw-r--r-- | view/macho/universalview.cpp | 369 | ||||
| -rw-r--r-- | view/macho/universalview.h | 40 | ||||
| -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 |
24 files changed, 14587 insertions, 0 deletions
diff --git a/view/elf/.gitignore b/view/elf/.gitignore new file mode 100644 index 00000000..d4c4c4dc --- /dev/null +++ b/view/elf/.gitignore @@ -0,0 +1,8 @@ +CMakeFiles +CMakeCache.txt +cmake_install.cmake +Makefile +api +build +libview_* +view_*.dll diff --git a/view/elf/CMakeLists.txt b/view/elf/CMakeLists.txt new file mode 100644 index 00000000..dbd252d2 --- /dev/null +++ b/view/elf/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(view_elf) + +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_elf STATIC ${SOURCES}) +else() + add_library(view_elf SHARED ${SOURCES}) +endif() + +target_link_libraries(view_elf binaryninjaapi) + +set_target_properties(view_elf 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_elf) + set_target_properties(view_elf PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} + RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) +endif() diff --git a/view/elf/LICENSE b/view/elf/LICENSE new file mode 100644 index 00000000..265883c5 --- /dev/null +++ b/view/elf/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/elf/README.md b/view/elf/README.md new file mode 100644 index 00000000..bb81d1da --- /dev/null +++ b/view/elf/README.md @@ -0,0 +1,27 @@ +# view-elf +This is the ELF 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_elf.so`, +`libview_elf.dylib` or `view_elf.dll` depending on your platform. + +To install the plugin, first launch Binary Ninja and uncheck the "ELF 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/elf/elfview.cpp b/view/elf/elfview.cpp new file mode 100644 index 00000000..94b3c37b --- /dev/null +++ b/view/elf/elfview.cpp @@ -0,0 +1,2772 @@ +#include <string.h> +#ifndef _MSC_VER +#include <cxxabi.h> +#endif +#include <inttypes.h> +#include "elfview.h" + +#define STRING_READ_CHUNK_SIZE 32 + +using namespace BinaryNinja; +using namespace std; + + +static ElfViewType* g_elfViewType = nullptr; + + +void BinaryNinja::InitElfViewType() +{ + static ElfViewType type; + BinaryViewType::Register(&type); + g_elfViewType = &type; + + Ref<Settings> settings = Settings::Instance(); + settings->RegisterSetting("files.elf.maxSectionHeaderCount", + R"({ + "title" : "Maximum ELF Section Header Count", + "type" : "number", + "default" : 100, + "minValue" : 0, + "maxValue" : 65536, + "description" : "Maximum number of entries to include in section header array", + "ignore" : ["SettingsProjectScope"] + })"); +} + + +ElfView::ElfView(BinaryView* data, bool parseOnly): BinaryView("ELF", data->GetFile(), data), m_parseOnly(parseOnly) +{ + Elf64Header header; + string errorMsg; + BNEndianness endian; + if (!g_elfViewType->ParseHeaders(data, m_ident, m_commonHeader, header, &m_arch, &m_plat, errorMsg, endian)) + throw ElfFormatException(errorMsg); + + CreateLogger("BinaryView"); + m_logger = CreateLogger("BinaryView.ElfView"); + m_elf32 = m_ident.fileClass == 1; + m_addressSize = (m_ident.fileClass == 1) ? 4 : 8; + m_endian = endian; + m_relocatable = m_commonHeader.type == ET_DYN || m_commonHeader.type == ET_REL; + m_objectFile = m_commonHeader.type == ET_REL; + m_backedByDatabase = data->GetFile()->IsBackedByDatabase("ELF"); + + memset(&m_symbolTableSection, 0, sizeof(m_symbolTableSection)); + memset(&m_dynamicSymbolTableSection, 0, sizeof(m_dynamicSymbolTableSection)); + memset(&m_dynamicStringTable, 0, sizeof(m_dynamicStringTable)); + memset(&m_dynamicTable, 0, sizeof(m_dynamicTable)); + memset(&m_tlsSegment, 0, sizeof(m_tlsSegment)); + memset(&m_auxSymbolTable, 0, sizeof(m_auxSymbolTable)); + memset(&m_sectionStringTable, 0, sizeof(m_sectionStringTable)); + memset(&m_sectionOpd, 0, sizeof(m_sectionOpd)); + + m_logger->LogInfo("Detected %s endian ELF", m_endian == LittleEndian ? "Little Endian" : "Big Endian"); + + + if (m_elf32 && (header.sectionHeaderSize != sizeof(Elf32SectionHeader))) + { + m_logger->LogWarn( + "The section header size reported by e_shentsize (0x%lx) is different from the size of Elf32_Shdr (0x%lx). " + "The parsing proceeds with the size of Elf32_Shdr.", + header.sectionHeaderSize, sizeof(Elf32SectionHeader)); + header.sectionHeaderSize = sizeof(Elf32SectionHeader); + } + else if (!m_elf32 && (header.sectionHeaderSize != sizeof(Elf64SectionHeader))) + { + m_logger->LogWarn( + "The section header size reported by e_shentsize (0x%lx) is different from the size of Elf64_Shdr (0x%lx). " + "The parsing proceeds with the size of Elf64_Shdr.", + header.sectionHeaderSize, sizeof(Elf64SectionHeader)); + header.sectionHeaderSize = sizeof(Elf64SectionHeader); + } + + if (m_elf32 && (header.programHeaderSize != sizeof(Elf32ProgramHeader))) + { + m_logger->LogWarn( + "The program header size reported by e_phentsize (0x%lx) is different from the size of Elf32_Phdr (0x%lx). " + "The parsing proceeds with the size of Elf32_Phdr.", + header.programHeaderSize, sizeof(Elf32ProgramHeader)); + header.programHeaderSize = sizeof(Elf32ProgramHeader); + } + else if (!m_elf32 && (header.programHeaderSize != sizeof(Elf64ProgramHeader))) + { + m_logger->LogWarn( + "The program header size reported by e_phentsize (0x%lx) is different from the size of Elf64_Phdr (0x%lx). " + "The parsing proceeds with the size of Elf64_Phdr.", + header.programHeaderSize, sizeof(Elf64ProgramHeader)); + header.programHeaderSize = sizeof(Elf64ProgramHeader); + } + + m_entryPoint = header.entry; + m_sectionHeaderOffset = header.sectionHeaderOffset; + m_sectionHeaderCount = header.sectionHeaderCount; + m_programHeaderOffset = header.programHeaderOffset; + m_programHeaderCount = header.programHeaderCount; + m_headerFlags = header.flags; + m_fileSize = data->GetLength(); + + m_logger->LogDebug( + "ELF Header\n" + "\t%d bits\n" + "\theader.entry %016x\n" + "\theader.programHeaderOffset %016x\n" + "\theader.sectionHeaderOffset %016x\n" + "\theader.flags %016x\n" + "\theader.headerSize %016x\n" + "\theader.programHeaderSize %016x\n" + "\theader.programHeaderCount %016x\n" + "\theader.sectionHeaderSize %016x\n" + "\theader.sectionHeaderCount %016x\n" + "\theader.stringTable %016x\n", + m_addressSize * 8, header.entry, header.programHeaderOffset, header.sectionHeaderOffset, header.flags, + header.headerSize, header.programHeaderSize, header.programHeaderCount, header.sectionHeaderSize, + header.sectionHeaderCount, header.stringTable); + + BinaryReader reader(data); + reader.SetEndianness(m_endian); + + // Parse program headers + reader.Seek(header.programHeaderOffset); + for (size_t i = 0; i < header.programHeaderCount; i++) + { + Elf64ProgramHeader progHeader; + if (m_elf32) // 32-bit ELF + { + progHeader.type = reader.Read32(); + progHeader.offset = reader.Read32(); + progHeader.virtualAddress = reader.Read32(); + progHeader.physicalAddress = reader.Read32(); + progHeader.fileSize = reader.Read32(); + progHeader.memorySize = reader.Read32(); + progHeader.flags = reader.Read32(); + progHeader.align = reader.Read32(); + } + else // 64-bit ELF + { + progHeader.type = reader.Read32(); + progHeader.flags = reader.Read32(); + progHeader.offset = reader.Read64(); + progHeader.virtualAddress = reader.Read64(); + progHeader.physicalAddress = reader.Read64(); + progHeader.fileSize = reader.Read64(); + progHeader.memorySize = reader.Read64(); + progHeader.align = reader.Read64(); + } + + m_logger->LogDebug( + "\tSegment: %d\n" + "\t\tprogHeader.type %08x\n" + "\t\tprogHeader.offset %08x\n" + "\t\tprogHeader.virtualAddress %016x\n" + "\t\tprogHeader.physicalAddress %016x\n" + "\t\tprogHeader.fileSize %016x\n" + "\t\tprogHeader.memorySize %016x\n" + "\t\tprogHeader.flags %016x\n" + "\t\tprogHeader.align %016x\n", + i, progHeader.type, progHeader.offset, progHeader.virtualAddress, progHeader.physicalAddress, + progHeader.fileSize, progHeader.memorySize, progHeader.flags, progHeader.align); + + if (!memcmp(m_ident.signature, "\x7f" "CGC", 4)) + { + // CGC uses physical address for loading + uint64_t temp = progHeader.virtualAddress; + progHeader.virtualAddress = progHeader.physicalAddress; + progHeader.physicalAddress = temp; + } + + m_programHeaders.push_back(progHeader); + + if (progHeader.type == ELF_PT_DYNAMIC) + m_dynamicTable = progHeader; + if (progHeader.type == ELF_PT_TLS) + m_tlsSegment = progHeader; + } + + // Parse section headers + try + { + reader.Seek(header.sectionHeaderOffset); + m_logger->LogDebug("Section List"); + for (size_t i = 0; i < header.sectionHeaderCount; i++) + { + Elf64SectionHeader section; + reader.Seek(header.sectionHeaderOffset + (i * header.sectionHeaderSize)); + if (m_elf32) // 32-bit ELF + { + section.name = reader.Read32(); + section.type = reader.Read32(); + section.flags = reader.Read32(); + section.address = reader.Read32(); + section.offset = reader.Read32(); + section.size = reader.Read32(); + section.link = reader.Read32(); + section.info = reader.Read32(); + section.align = reader.Read32(); + section.entrySize = reader.Read32(); + } + else // 64-bit ELF + { + section.name = reader.Read32(); + section.type = reader.Read32(); + section.flags = reader.Read64(); + section.address = reader.Read64(); + section.offset = reader.Read64(); + section.size = reader.Read64(); + section.link = reader.Read32(); + section.info = reader.Read32(); + section.align = reader.Read64(); + section.entrySize = reader.Read64(); + } + + m_elfSections.push_back(section); + + if (section.size > m_fileSize) + { + m_logger->LogWarn("Section %lu has a size (0x%lx) larger than file size (0x%lx), skipping creation", i, + section.size, m_fileSize); + continue; + } + + if (section.type == ELF_SHT_SYMTAB) + m_symbolTableSection = section; + else if (section.type == ELF_SHT_DYNSYM) + m_dynamicSymbolTableSection = section; + else if (section.type == ELF_SHT_STRTAB) + { + ApplyTypesToParentStringTable(section); + if (i == header.stringTable) + { + m_sectionStringTable = section; + } + continue; + } + + if (section.flags & ELF_SHF_STRINGS) + ApplyTypesToParentStringTable(section, false); + } + } + catch (ReadException&) + { + // Section headers are not required for a valid ELF, skip errors + m_logger->LogError("ELF section headers invalid"); + } + + for (size_t i = 0; i < m_elfSections.size(); i++) + { + const string scnNameString = ReadStringTable(reader, m_sectionStringTable, m_elfSections[i].name); + m_logger->LogDebug("\tSection: %d\n" + "\t\tsection.name %08x (%s)\n" + "\t\tsection.type %08x\n" + "\t\tsection.flags %016x\n" + "\t\tsection.address %016x\n" + "\t\tsection.offset %016x\n" + "\t\tsection.size %016x\n" + "\t\tsection.link %08x\n" + "\t\tsection.info %016x\n" + "\t\tsection.align %016x\n" + "\t\tsection.entrySize %016x", + i, + m_elfSections[i].name, scnNameString.c_str(), + m_elfSections[i].type, + m_elfSections[i].flags, + m_elfSections[i].address, + m_elfSections[i].offset, + m_elfSections[i].size, + m_elfSections[i].link, + m_elfSections[i].info, + m_elfSections[i].align, + m_elfSections[i].entrySize + ); + + if (!m_elf32 && m_commonHeader.arch == EM_PPC64 && scnNameString == ".opd") + m_sectionOpd = m_elfSections[i]; + } + + /* TODO: PPC64 specific entrypoint handling to be moved to architecture extension for ELF */ + if (m_commonHeader.arch == EM_PPC64) + { + uint64_t entry; + if (DerefPpc64Descriptor(reader, m_entryPoint, entry)) + { + m_logger->LogDebug("PPC64 dereference m_entryPoint=%016x to %016x\n", m_entryPoint, entry); + m_entryPoint = entry; + } + else + { + m_logger->LogDebug("PPC64 unable to dereference m_entryPoint=%016x\n", m_entryPoint); + } + } +} + + +ElfView::~ElfView() +{ + for (auto& i : m_relocationInfo) + { + auto cur = i.next; + while (cur) + { + auto next = cur->next; + delete cur; + cur = next; + } + } +} + + +static BNSymbolBinding TranslateELFBindingType(uint8_t type) +{ + switch (type) + { + case ELF_STB_LOCAL: return LocalBinding; + case ELF_STB_GLOBAL: return GlobalBinding; + case ELF_STB_WEAK: return WeakBinding; + default: + return NoBinding; + } +} + + +bool ElfView::ParseSymbolTableEntry(BinaryReader& reader, ElfSymbolTableEntry& entry, uint64_t sym, + const Elf64SectionHeader& symbolTable, const Elf64SectionHeader& stringTable, bool dynamic) +{ + try + { + entry.dynamic = dynamic; + if (m_elf32) + { + reader.Seek(symbolTable.offset + (sym * 16)); + entry.nameOffset = reader.Read32(); + entry.value = reader.Read32(); + entry.size = reader.Read32(); + uint8_t info = reader.Read8(); + entry.type = ELF_ST_TYPE(info); + entry.binding = TranslateELFBindingType(ELF_ST_BIND(info)); + entry.other = reader.Read8(); + entry.section = reader.Read16(); + } + else + { + reader.Seek(symbolTable.offset + (sym * 24)); + entry.nameOffset = reader.Read32(); + uint8_t info = reader.Read8(); + entry.type = ELF_ST_TYPE(info); + entry.binding = TranslateELFBindingType(ELF_ST_BIND(info)); + entry.other = reader.Read8(); + entry.section = reader.Read16(); + entry.value = reader.Read64(); + entry.size = reader.Read64(); + } + + if (entry.type == ELF_STT_SECTION) + { + if (entry.section < m_elfSections.size()) + { + entry.name = ReadStringTable(reader, m_sectionStringTable, m_elfSections[entry.section].name); + } + } + else + { + entry.name = ReadStringTable(reader, stringTable, entry.nameOffset); + } + } + catch (ReadException&) + { + return false; + } + + m_logger->LogDebug( + "Symbol: %d - symbolSection.offset: %lx - stringSection.offset: %lx\n" + "\tnameOffset = %#08lx\n" + "\ttype = %#02x\n" + "\tbinding = %#02x\n" + "\tother = %#02x\n" + "\tsection = %#04x\n" + "\tvalue = %#012lx\n" + "\tsize = %#012lx\n" + "\tname = %#s", + sym, symbolTable.offset, stringTable.offset, + entry.nameOffset, + entry.type, + entry.binding, + entry.other, + entry.section, + entry.value, + entry.size, + entry.name.c_str()); + return true; +} + +void ElfView::GetRelocEntries(BinaryReader& reader, const vector<Elf64SectionHeader>& sections, + bool implicit, vector<ELFRelocEntry>& result) +{ + size_t relocSize = m_elf32 ? 8 : 16; + if (!implicit) + relocSize += m_elf32 ? 4 : 8; + + for (auto& section : sections) + { + for (uint64_t j = 0; j < section.size / relocSize; j++) + { + reader.Seek(section.offset + (j * relocSize)); + uint64_t ofs = m_elf32 ? reader.Read32() : reader.Read64(); + uint64_t info = m_elf32 ? reader.Read32() : reader.Read64(); + uint64_t addend = 0; + if (!implicit) + addend = m_elf32 ? reader.Read32() : reader.Read64(); + + result.push_back(ELFRelocEntry(ofs, info >> (m_elf32 ? 8 : 32), info & (m_elf32 ? 0xff : 0xffffffff), + addend, section.info, implicit)); + } + } +} + +static bool In(const string& str, const vector<string>& list) +{ + for (auto& a : list) + if (a == str) + return true; + return false; +} + +bool ElfView::Init() +{ + std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now(); + // Add segments for the program headers + BinaryReader reader(GetParentView()); + BinaryReader virtualReader(this); + + uint64_t initialImageBase = 0; + bool initialImageBaseSet = false; + for (const auto& i : m_programHeaders) + { + if ((i.type != ELF_PT_LOAD) || (!i.fileSize)) + continue; + + if (!initialImageBaseSet) + { + initialImageBase = i.virtualAddress; + initialImageBaseSet = true; + } + else if (i.virtualAddress < initialImageBase) + initialImageBase = i.virtualAddress; + } + + SetOriginalBase(initialImageBase); + uint64_t preferredImageBase = initialImageBase; + Ref<Settings> viewSettings = Settings::Instance(); + m_extractMangledTypes = viewSettings->Get<bool>("analysis.extractTypesFromMangledNames", this); + m_simplifyTemplates = viewSettings->Get<bool>("analysis.types.templateSimplifier", this); + + Ref<Settings> settings = GetLoadSettings(GetTypeName()); + if (settings && settings->Contains("loader.imageBase") && settings->Contains("loader.architecture")) // handle overrides + { + preferredImageBase = 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; + } + + int64_t imageBaseAdjustment = 0; + if (!initialImageBase) + imageBaseAdjustment = preferredImageBase; + else if (initialImageBase <= preferredImageBase) + imageBaseAdjustment = preferredImageBase - initialImageBase; + else + imageBaseAdjustment = -(int64_t)(initialImageBase - preferredImageBase); + + m_entryPoint = m_entryPoint + imageBaseAdjustment; + + for (auto& i : m_programHeaders) + { + uint64_t adjustedVirtualAddr = i.virtualAddress + imageBaseAdjustment; + + if (i.type == ELF_PT_LOAD) // || i.type == ELF_PT_GNU_RELRO) + { + uint32_t flags = 0; + if (i.flags & 1) + flags |= SegmentExecutable; + if (i.flags & 2) + flags |= SegmentWritable; + if (i.flags & 4) + flags |= SegmentReadable; + AddAutoSegment(adjustedVirtualAddr, i.memorySize, i.offset, i.fileSize, flags); + } + + // Create sections for the program headers with the standard section names. This will ensure that + // the standard name for sections such as .dynamic will always refer to what the loader actually uses. + if (i.type == ELF_PT_DYNAMIC) + { + uint64_t entrySize = m_elf32 ? 8 : 16; + + bool foundMatch = false; + for (size_t j = 1; j < m_elfSections.size(); j++) + { + uint64_t adjustedSectionAddr = m_elfSections[j].address + imageBaseAdjustment; + + if (m_elfSections[j].type != ELF_SHT_DYNAMIC) + continue; + if (adjustedSectionAddr != adjustedVirtualAddr) + continue; + if (m_elfSections[j].size != i.memorySize) + continue; + if (m_elfSections[j].entrySize != entrySize) + continue; + if (ReadStringTable(reader, m_sectionStringTable, m_elfSections[j].name) != ".dynamic") + continue; + foundMatch = true; + break; + } + + if (!foundMatch) + AddAutoSection(".dynamic", adjustedVirtualAddr, i.memorySize, ReadOnlyDataSectionSemantics, "DYNAMIC", i.align, entrySize); + } + else if (i.type == ELF_PT_INTERP) + { + bool foundMatch = false; + for (size_t j = 1; j < m_elfSections.size(); j++) + { + uint64_t adjustedSectionAddr = m_elfSections[j].address + imageBaseAdjustment; + + if (m_elfSections[j].type != ELF_SHT_PROGBITS) + continue; + if (adjustedSectionAddr != adjustedVirtualAddr) + continue; + if (m_elfSections[j].size != i.memorySize) + continue; + if (ReadStringTable(reader, m_sectionStringTable, m_elfSections[j].name) != ".interp") + continue; + foundMatch = true; + break; + } + + if (!foundMatch) + AddAutoSection(".interp", adjustedVirtualAddr, i.memorySize, ReadOnlyDataSectionSemantics, "PROGBITS", i.align); + + DefineDataVariable(adjustedVirtualAddr, Type::ArrayType(Type::IntegerType(1, true), i.fileSize)); + DefineAutoSymbol(new Symbol(DataSymbol, "__elf_interp", adjustedVirtualAddr, LocalBinding)); + } + } + + // Gather names for the sections + vector<string> sectionNames {""}; + for (size_t i = 1; i < m_elfSections.size(); i++) + sectionNames.push_back(ReadStringTable(reader, m_sectionStringTable, m_elfSections[i].name)); + sectionNames = GetUniqueSectionNames(sectionNames); + + // Add sections to the view + vector<Elf64SectionHeader> dynRelocSections, dynRelocASections; + vector<Elf64SectionHeader> relocSections, relocASections; + Elf64SectionHeader symbolTableSection; + + uint64_t segmentStart = 0; + for (size_t i = 1; i < m_elfSections.size(); i++) + { + string type; + string linkedSection, infoSection; + switch (m_elfSections[i].type) + { + case ELF_SHT_PROGBITS: + type = "PROGBITS"; + break; + case ELF_SHT_SYMTAB: + type = "SYMTAB"; + symbolTableSection = m_elfSections[i]; + break; + case ELF_SHT_STRTAB: + type = "STRTAB"; + break; + case ELF_SHT_RELA: + type = "RELA"; + relocASections.push_back(m_elfSections[i]); + break; + case ELF_SHT_REL: + type = "REL"; + relocSections.push_back(m_elfSections[i]); + break; + case ELF_SHT_HASH: + type = "HASH"; + break; + case ELF_SHT_DYNAMIC: + type = "DYNAMIC"; + break; + case ELF_SHT_NOTE: + type = "NOTE"; + break; + case ELF_SHT_NOBITS: + type = "NOBITS"; + break; + case ELF_SHT_SHLIB: + type = "SHLIB"; + break; + case ELF_SHT_DYNSYM: + type = "DYNSYM"; + break; + default: + break; + } + + // Add sections that aren't in the virtual address space only to the raw parent view + if (!(m_elfSections[i].flags & ELF_SHF_ALLOC)) + { + if (m_elfSections[i].size != 0 && m_elfSections[i].type != ELF_SHT_NOBITS) + GetParentView()->AddAutoSection(sectionNames[i], m_elfSections[i].offset, m_elfSections[i].size, DefaultSectionSemantics, + type, m_elfSections[i].align, m_elfSections[i].entrySize, linkedSection, infoSection, m_elfSections[i].info); + continue; + } + + if (m_elfSections[i].type == ELF_SHT_STRTAB) + ApplyTypesToStringTable(m_elfSections[i], imageBaseAdjustment); + else if (m_elfSections[i].flags & ELF_SHF_STRINGS) + ApplyTypesToStringTable(m_elfSections[i], imageBaseAdjustment, false); + + if (m_elfSections[i].link < m_elfSections.size()) + linkedSection = sectionNames[m_elfSections[i].link]; + + BNSectionSemantics semantics = DefaultSectionSemantics; + vector<string> readOnlyCodeSectionNames = {".text", ".init", ".fini"}; + vector<string> readWriteDataSectionNames = {".data", ".bss"}; + vector<string> readOnlyDataSectionNames = {".rodata", ".dynamic", ".dynsym", ".dynstr", ".ehframe", + ".ctors", ".dtors", ".got", ".got2", ".data.rel.ro", ".gnu.hash"}; + if ((m_elfSections[i].flags & ELF_SHF_EXECINSTR) || In(sectionNames[i], readOnlyCodeSectionNames)) + semantics = ReadOnlyCodeSectionSemantics; + else if (!(m_elfSections[i].flags & ELF_SHF_WRITE) || In(sectionNames[i], readOnlyDataSectionNames)) + semantics = ReadOnlyDataSectionSemantics; + else if ((m_elfSections[i].flags & ELF_SHF_WRITE) || In(sectionNames[i], readWriteDataSectionNames)) + semantics = ReadWriteDataSectionSemantics; + + if (m_elfSections[i].size != 0) + { + if (m_programHeaders.size() == 0) + { + // We have an object file so we'll just create segments for the sections + uint32_t flags = 0; + if (semantics == ReadOnlyCodeSectionSemantics) + flags = SegmentReadable | SegmentExecutable; + else if (semantics == ReadWriteDataSectionSemantics) + flags = SegmentReadable | SegmentWritable; + else if (semantics == ReadOnlyDataSectionSemantics) + flags = SegmentReadable; + m_elfSections[i].address = segmentStart; + size_t size = m_elfSections[i].type == ELF_SHT_NOBITS ? 0 : m_elfSections[i].size; + uint64_t adjustedSectionAddr = m_elfSections[i].address + imageBaseAdjustment; + AddAutoSegment(adjustedSectionAddr, m_elfSections[i].size, m_elfSections[i].offset, size, flags); + segmentStart += ((m_elfSections[i].size + 15) & ~15); + } + else if ((m_elfSections[i].address + m_elfSections[i].size + imageBaseAdjustment) > GetEnd() || ((m_elfSections[i].address + imageBaseAdjustment) < GetStart())) + { + LogWarn("Section %s is outside of the address space of the file and will not be added", sectionNames[i].c_str()); + continue; + } + + uint64_t adjustedVirtualAddr = m_elfSections[i].address + imageBaseAdjustment; + AddAutoSection(sectionNames[i], adjustedVirtualAddr, m_elfSections[i].size, semantics, type, m_elfSections[i].align, + m_elfSections[i].entrySize, linkedSection, infoSection, m_elfSections[i].info); + if (m_elfSections[i].type != ELF_SHT_NOBITS) + GetParentView()->AddAutoSection(sectionNames[i], m_elfSections[i].offset, m_elfSections[i].size, DefaultSectionSemantics, type, m_elfSections[i].align, m_elfSections[i].entrySize, linkedSection, infoSection, m_elfSections[i].info); + } + } + + // Apply architecture and platform + if (!m_arch) + { + switch (m_commonHeader.arch) + { + case 3: + m_logger->LogError("Support for ELF architecture 'x86' is not present"); + break; + case 8: + #ifndef DEMO_VERSION + m_logger->LogError("Support for ELF architecture 'mips' is not present"); + #else + m_logger->LogError("Binary Ninja free does not support ELF architecture 'mips'. " + "Purchase Binary Ninja to unlock all features."); + #endif + break; + case 20: + #ifndef DEMO_VERSION + m_logger->LogError("Support for ELF architecture 'ppc' is not present"); + #else + m_logger->LogError("Binary Ninja free does not support ELF architecture 'ppc'. " + "Purchase Binary Ninja to unlock all features."); + #endif + break; + case 21: + #ifndef DEMO_VERSION + m_logger->LogError("Support for ELF architecture 'ppc64' is not present"); + #else + m_logger->LogError("Binary Ninja free does not support ELF architecture 'ppc64'. " + "Purchase Binary Ninja to unlock all features."); + #endif + break; + case 40: + m_logger->LogError("Support for ELF architecture 'armv7' is not present"); + break; + case 62: + m_logger->LogError("Support for ELF architecture 'x86_64' is not present"); + break; + case 183: + #ifndef DEMO_VERSION + m_logger->LogError("Support for ELF architecture 'arm64' is not present"); + #else + m_logger->LogError("Binary Ninja free does not support ELF architecture 'arm64'. " + "Purchase Binary Ninja to unlock all features."); + #endif + break; + default: + m_logger->LogError("ELF architecture %d is not supported", m_commonHeader.arch); + break; + } + return false; + } + + // Add the entry point as a function if the architecture is supported + uint64_t entryPointAddress = m_entryPoint; + Ref<Architecture> entryPointArch = m_arch->GetAssociatedArchitectureByAddress(entryPointAddress); + SetDefaultArchitecture(entryPointArch); + GetParentView()->SetDefaultArchitecture(entryPointArch); + + Ref<Platform> platform = m_plat ? m_plat : g_elfViewType->GetPlatform(m_ident.os, m_arch); + if (platform && (entryPointArch != m_arch)) + platform = platform->GetRelatedPlatform(entryPointArch); + if (!platform) + platform = entryPointArch->GetStandalonePlatform(); + + 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); + GetParentView()->SetDefaultPlatform(platform); + + // Finished for parse only mode + if (m_parseOnly) + { + m_stringTableCache.clear(); + return true; + } + + // Set reader endianness + reader.SetEndianness(m_endian); + virtualReader.SetEndianness(m_endian); + + // FIXME: MIPS specific GOT entries should be done in the MIPS plugin, once there is a way to have + // ELF parsing extensions in an architecture plugin + uint64_t gotStart = 0; + uint64_t localMipsSyms = 0; + uint64_t firstMipsSym = 0; + uint64_t baseAddress = GetStart(); + vector<uint64_t> neededLibraries; + bool mipsSymValid = false; + // FIXME: ARM specific GOT entries should be done in the MIPS plugin, as above + bool isArmV7 = m_arch->GetName() == "armv7"; + vector<uint64_t> tlsModuleStarts; + vector<uint64_t> tlsOffsets; + + // Parse dynamic table + if (auto dynSeg = GetSegmentAt(m_dynamicTable.virtualAddress + imageBaseAdjustment); dynSeg && m_dynamicTable.virtualAddress) + { + try + { + uint64_t adjustedVirtualAddr = m_dynamicTable.virtualAddress + imageBaseAdjustment; + reader.Seek(adjustedVirtualAddr - dynSeg->GetStart() + dynSeg->GetDataOffset()); + + Elf64SectionHeader reloca, plt, rel; + memset(&reloca, 0, sizeof(reloca)); + memset(&plt, 0, sizeof(plt)); + memset(&rel, 0, sizeof(rel)); + uint64_t pltType = ELF_DT_RELA; + bool end = false; + uint64_t entrySize = m_elf32 ? 8 : 16; + size_t i = 0; + while (!end) + { + uint64_t tag, value; + if (m_elf32) // 32-bit ELF + { + tag = reader.Read32(); + value = reader.Read32(); + i += 8; + } + else // 64-bit ELF + { + tag = reader.Read64(); + value = reader.Read64(); + i += 16; + } + + switch (tag) + { + case ELF_DT_NULL: + end = true; + m_numDynamicTableEntries = i ? i / entrySize : 0; + break; + case ELF_DT_NEEDED: + neededLibraries.push_back(value); + break; + case ELF_DT_PLTREL: + pltType = value; + break; + case ELF_DT_JMPREL: + plt.offset = value + imageBaseAdjustment; + break; + case ELF_DT_PLTRELSZ: + plt.size = value; + break; + case ELF_DT_STRTAB: + m_dynamicStringTable.offset = value + imageBaseAdjustment; + break; + case ELF_DT_SYMTAB: + m_auxSymbolTable.offset = value + imageBaseAdjustment; + break; + case ELF_DT_SYMENT: + m_auxSymbolTableEntrySize = value; + break; + case ELF_DT_INIT: + case ELF_DT_FINI: + { + uint64_t target = value + imageBaseAdjustment; + string autoName = (tag == ELF_DT_INIT) ? "_init" : "_fini"; + DefineAutoSymbol(new Symbol(FunctionSymbol, autoName, target, NoBinding)); + Ref<Platform> targetPlatform = platform->GetAssociatedPlatformByAddress(target); + AddFunctionForAnalysis(targetPlatform, target); + break; + } + case ELF_DT_HASH: + m_hashHeader = value + imageBaseAdjustment; + break; + case ELF_DT_GNU_HASH: + m_gnuHashHeader = value + imageBaseAdjustment; + break; + case ELF_DT_RELA: + reloca.offset = value + imageBaseAdjustment; + break; + case ELF_DT_RELASZ: + reloca.size = value; + break; + case ELF_DT_RELAENT: + reloca.entrySize = value; + break; + case ELF_DT_STRSZ: + m_dynamicStringTable.size = value; + break; + case ELF_DT_REL: + rel.offset = value + imageBaseAdjustment; + break; + case ELF_DT_RELSZ: + rel.size = value; + break; + case ELF_DT_RELENT: + rel.entrySize = value; + break; + case ELF_DT_PLTGOT: + gotStart = value + imageBaseAdjustment; + break; + case ELF_DT_MIPS_SYMTABNO: + m_auxSymbolTable.size = m_elf32 ? value * 16 : value * 24; + break; + case ELF_DT_MIPS_LOCAL_GOTNO: + localMipsSyms = value; + break; + case ELF_DT_MIPS_GOTSYM: + firstMipsSym = value; + mipsSymValid = true; + break; + case ELF_DT_MIPS_BASE_ADDRESS: + baseAddress = value + imageBaseAdjustment; + break; + default: + break; + } + } + + vector<Ref<Metadata>> libraries; + vector<Ref<Metadata>> libraryFound; + for (auto& libNameOffset : neededLibraries) + { + const string libName = ReadStringTable(virtualReader, m_dynamicStringTable, libNameOffset); + if (!GetExternalLibrary(libName)) + { + AddExternalLibrary(libName, {}, true); + } + libraries.push_back(new Metadata(string(libName))); + Ref<TypeLibrary> typeLib = GetTypeLibrary(libName); + if (!typeLib) + { + vector<Ref<TypeLibrary>> typeLibs = platform->GetTypeLibrariesByName(libName); + if (typeLibs.size()) + { + typeLib = typeLibs[0]; + AddTypeLibrary(typeLib); + + m_logger->LogDebug("elf: adding type library for '%s': %s (%s)", libName.c_str(), typeLib->GetName().c_str(), + typeLib->GetGuid().c_str()); + } + } + + if (typeLib) + libraryFound.push_back(new Metadata(typeLib->GetName())); + else + libraryFound.push_back(new Metadata(string(""))); + } + StoreMetadata("Libraries", new Metadata(libraries), true); + StoreMetadata("LibraryFound", new Metadata(libraryFound), true); + + if (reloca.size > 0) + { + bool alreadyExists = false; + for (auto& relSec : relocASections) + if (relSec.offset == reloca.offset) + alreadyExists = true; + if (!alreadyExists) + { + dynRelocASections.push_back(reloca); + AddAutoSection(".dynamic_rela", reloca.offset, reloca.size, ReadOnlyDataSectionSemantics); + } + } + if (plt.size > 0) + { + if (pltType == ELF_DT_REL) + { + bool alreadyExists = false; + for (auto& relSec : relocSections) + if ((relSec.address + imageBaseAdjustment) == plt.offset) + alreadyExists = true; + if (!alreadyExists) + { + dynRelocSections.push_back(plt); + AddAutoSection(".dynamic_jmprel", plt.offset, plt.size, ReadOnlyDataSectionSemantics); + } + } + else + { + bool alreadyExists = false; + for (auto& relSec : relocASections) + if ((relSec.address + imageBaseAdjustment) == plt.offset) + alreadyExists = true; + if (!alreadyExists) + { + dynRelocASections.push_back(plt); + AddAutoSection(".dynamic_jmprel", plt.offset, plt.size, ReadOnlyDataSectionSemantics); + } + } + } + if (rel.size > 0) + { + bool alreadyExists = false; + for (auto& relSec : relocSections) + if ((relSec.address + imageBaseAdjustment) == rel.offset) + alreadyExists = true; + if (!alreadyExists) + { + dynRelocSections.push_back(rel); + AddAutoSection(".dynamic_rel", rel.offset, rel.size, ReadOnlyDataSectionSemantics); + } + } + } + catch (ReadException&) + { + m_logger->LogError("ELF dynamic table invalid"); + } + } + + // Parse symbol table + vector<ElfSymbolTableEntry> symbolTable, dynamicSymbolTable; + try + { + if ((m_symbolTableSection.size > 0) && (m_symbolTableSection.link < m_elfSections.size())) + { + symbolTable = ParseSymbolTable(reader, m_symbolTableSection, m_elfSections[m_symbolTableSection.link], false); + } + + if ((m_dynamicSymbolTableSection.size > 0) && (m_dynamicSymbolTableSection.link < m_elfSections.size())) + { + dynamicSymbolTable = ParseSymbolTable(reader, m_dynamicSymbolTableSection, + m_elfSections[m_dynamicSymbolTableSection.link], true); + } + } + catch (ReadException&) + { + // Symbol table is not critical to execution, skip errors + m_logger->LogError("ELF symbol table invalid"); + } + + vector<ELFRelocEntry> relocs; + try + { + GetRelocEntries(virtualReader, dynRelocSections, true, relocs); + GetRelocEntries(virtualReader, dynRelocASections, false, relocs); + GetRelocEntries(reader, relocSections, true, relocs); + GetRelocEntries(reader, relocASections, false, relocs); + for (auto& reloc : relocs) + reloc.offset += imageBaseAdjustment; + } + catch (ReadException&) + { + // Skip errors in relocation tables + m_logger->LogError("ELF relocation table invalid"); + } + + BeginBulkModifySymbols(); + + vector<ElfSymbolTableEntry> auxSymbolTable; + try + { + if (!m_auxSymbolTableEntrySize) + m_auxSymbolTableEntrySize = m_elf32 ? 16 : 24; + + // Parse and create types for ELF hash table + if (m_hashHeader) + { + virtualReader.Seek(m_hashHeader); + uint32_t nbucket = virtualReader.Read32(); + uint32_t nchain = virtualReader.Read32(); + m_auxSymbolTable.size = m_auxSymbolTableEntrySize * nchain; + auxSymbolTable = ParseSymbolTable(virtualReader, m_auxSymbolTable, m_dynamicStringTable, true); + + StructureBuilder hashTableBuilder; + hashTableBuilder.AddMember(Type::IntegerType(4, false), "nbucket"); + hashTableBuilder.AddMember(Type::IntegerType(4, false), "nchain"); + hashTableBuilder.AddMember(Type::ArrayType(Type::IntegerType(4, false), nbucket), "buckets"); + hashTableBuilder.AddMember(Type::ArrayType(Type::IntegerType(4, false), nchain), "chains"); + Ref<Structure> hashTableStruct = hashTableBuilder.Finalize(); + Ref<Type> hashTableType = Type::StructureType(hashTableStruct); + QualifiedName hashTableName = string("Elf_HashTable"); + const string hashTableTypeId = Type::GenerateAutoTypeId("elf", hashTableName); + QualifiedName hashTableTypeName = DefineType(hashTableTypeId, hashTableName, hashTableType); + DefineDataVariable(m_hashHeader, Type::NamedType(this, hashTableTypeName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__elf_hash_table", m_hashHeader, NoBinding)); + + // Gratuitously create sections for the symbol and string tables if none exist + if (!GetSectionsAt(m_auxSymbolTable.offset).size()) + AddAutoSection(".dynamic_symtab", m_auxSymbolTable.offset, m_auxSymbolTable.size, ReadOnlyDataSectionSemantics); + if (!GetSectionsAt(m_dynamicStringTable.offset).size()) + AddAutoSection(".dynamic_strtab", m_dynamicStringTable.offset, m_dynamicStringTable.size, ReadOnlyDataSectionSemantics); + } + + // Parse and create types for ELF GNU hash table + if (m_gnuHashHeader) + { + // try to extract dynamic symbol table size from section information if it exists + if (!m_auxSymbolTable.size) + { + auto sections = GetSectionsAt(m_auxSymbolTable.offset); + if (sections.size() && (sections[0]->GetStart() == m_auxSymbolTable.offset)) + m_auxSymbolTable.size = sections[0]->GetLength(); + else + { + // TODO section information not available; calculate the dynamic symbol table size from the gnu hash table + } + } + } + } + catch (ReadException&) + { + // Skip errors in hash tables + m_logger->LogError("ELF hash/symbol table parsing failed"); + } + + try + { + if (mipsSymValid && (gotStart != 0)) + { + for (size_t i = 2; i < localMipsSyms; i++) + { + m_gotEntryLocations.emplace(gotStart + i * (m_elf32 ? 4 : 8)); + } + for (uint64_t i = firstMipsSym; i < (m_auxSymbolTable.size / (m_elf32 ? 16 : 24)); i++) + { + uint64_t gotEntry = gotStart + ((localMipsSyms + i - firstMipsSym) * (m_elf32 ? 4 : 8)); + + ElfSymbolTableEntry entry; + if (!ParseSymbolTableEntry(virtualReader, entry, i, m_auxSymbolTable, m_dynamicStringTable, true)) + continue; + // TODO dynamic symbol table already parsed above; don't add duplicate entries + if (!m_hashHeader) + auxSymbolTable.push_back(entry); + switch (entry.type) + { + case ELF_STT_OBJECT: + case ELF_STT_NOTYPE: + if (entry.section != ELF_SHN_UNDEF) + DefineElfSymbol(DataSymbol, entry.name, gotEntry, true, entry.binding, 4, Type::PointerType( + GetDefaultPlatform()->GetArchitecture(), Type::VoidType())->WithConfidence(BN_FULL_CONFIDENCE)); + else + { + bool relocationExists = false; + for (auto& reloc : relocs) + { + if (reloc.offset == gotEntry) + { + relocationExists = true; + break; + } + } + if (!relocationExists) + { + int relocType = m_arch->GetAddressSize() == 4 ? 126 /* R_MIPS_COPY */ : 125 /* R_MIPS64_COPY */; + relocs.push_back(ELFRelocEntry(gotEntry, i, relocType, 0, 0, false)); + } + DefineElfSymbol(ImportAddressSymbol, entry.name, gotEntry, true, entry.binding, entry.size); + } + break; + case ELF_STT_FUNC: + if (entry.section != ELF_SHN_UNDEF) + DefineElfSymbol(DataSymbol, entry.name, gotEntry, true, entry.binding, 4, + Type::PointerType(GetDefaultPlatform()->GetArchitecture(), + Type::FunctionType(Type::IntegerType(GetDefaultPlatform()->GetArchitecture()->GetAddressSize(), true), + GetDefaultPlatform()->GetDefaultCallingConvention(), vector<FunctionParameter>())->WithConfidence(0))); + else + { + bool relocationExists = false; + for (auto& reloc : relocs) + { + if (reloc.offset == gotEntry) + { + relocationExists = true; + break; + } + } + if (!relocationExists) + { + int relocType = m_arch->GetAddressSize() == 4 ? 127 /*R_MIPS_JUMP_SLOT*/ : 125 /* R_MIPS64_COPY */; + relocs.push_back(ELFRelocEntry(gotEntry, i, relocType, 0, 0, false)); + } + DefineElfSymbol(ImportAddressSymbol, entry.name, gotEntry, true, entry.binding, entry.size); + // TODO for now create associated PLT entry if it exists. At some point we could extend the detection in RecognizeELFPLTEntries in arch_mips. + Ref<Symbol> sym = GetSymbolByAddress(gotEntry); + if (entry.value && sym && (sym->GetType() == ImportAddressSymbol)) + { + Ref<Platform> targetPlatform = platform->GetAssociatedPlatformByAddress(entry.value); + Ref<Function> func = AddFunctionForAnalysis(targetPlatform, entry.value); + if (func) + { + Ref<Symbol> funcSym = new Symbol(ImportedFunctionSymbol, + sym->GetShortName(), sym->GetFullName(), sym->GetRawName(), + entry.value, NoBinding, sym->GetNameSpace(), sym->GetOrdinal()); + DefineAutoSymbol(funcSym); + func->ApplyImportedTypes(funcSym); + } + } + } + break; + default: + m_logger->LogDebug("ELF symbol type of %d not handled.", entry.type); + break; + } + } + } + } + catch (ReadException&) + { + // Symbol table is not critical to execution, skip errors + m_logger->LogError("ELF symbol table invalid"); + } + + // No longer need to look up symbols during creation, start a parallelized queue for + // demangling and preparing symbols. + m_symbolQueue = new SymbolQueue(); + + // Now define symbols and resolve relocations + vector<ElfSymbolTableEntry> combinedSymbolTable; + if (symbolTable.size() > 1) + combinedSymbolTable.insert(combinedSymbolTable.end(), symbolTable.begin() + 1, symbolTable.end()); + if (dynamicSymbolTable.size() > 1) + combinedSymbolTable.insert(combinedSymbolTable.end(), dynamicSymbolTable.begin() + 1, dynamicSymbolTable.end()); + if (auxSymbolTable.size() > 1) + combinedSymbolTable.insert(combinedSymbolTable.end(), auxSymbolTable.begin() + 1, auxSymbolTable.end()); + for (auto entry = combinedSymbolTable.begin(); entry != combinedSymbolTable.end(); entry++) + { + if (m_objectFile) + { + if (entry->section >= m_elfSections.size()) + continue; + + // Object files "entry.value" is section relative + uint64_t adjustedSectionAddr = m_elfSections[entry->section].address + imageBaseAdjustment; + auto secs = GetSectionsAt(adjustedSectionAddr); + if (secs.size() < 1) + continue; + + entry->value += secs[0]->GetStart(); + } + else + entry->value += imageBaseAdjustment; + + if (entry->section == ELF_SHN_UNDEF) + { + DefineElfSymbol(ExternalSymbol, entry->name, 0, false, entry->binding, entry->size); + } + else + { + switch (entry->type) + { + case ELF_STT_GNU_IFUNC: + // Only handle this symbol type if the platform is a linux. Otherwise, we don't know what it is. + if (GetDefaultPlatform()->GetName().rfind("linux", 0) != 0) + goto unknownType; + DefineElfSymbol(FunctionSymbol, entry->name, entry->value, false, entry->binding, entry->size); + break; + case ELF_STT_FUNC: + DefineElfSymbol(FunctionSymbol, entry->name, entry->value, false, entry->binding, entry->size); + break; + case ELF_STT_TLS: + /* - only create Binja symbols for .symtab (not .dynsym) symbols + - ignore mapping symbols, all is assumed data + - ignore 0-length symbols (like _TLS_MODULE_BASE_) that are just informative */ + if (entry->dynamic || entry->size == 0 || entry->name == "$d") + /* is the value a valid offset in the TLS template? */ + if (m_tlsSegment.virtualAddress == 0 || (entry->value + entry->size) > m_tlsSegment.memorySize) + break; + /* the value is the offset into the TLS template, specified by program header type 7 (PT_TLS) */ + DefineElfSymbol(DataSymbol, entry->name, m_tlsSegment.virtualAddress + entry->value, false, entry->binding, entry->size); + break; + case ELF_STT_NOTYPE: + // TODO: ARM specific local entry handling to be moved to architecture extension for ELF + if (entry->binding == LocalBinding && In(m_arch->GetName(), {"aarch64", "armv7", "armv7eb", "thumb2", "thumb2eb"})) + { + // ARM Mapping Symbols + // $a labels the first byte of a sequence of ARM instructions. Its type is STT_FUNC. + // $b labels a Thumb BL instruction. Its type is STT_FUNC. + // $d labels the first byte of a sequence of data items. Its type is STT_OBJECT. + // $f labels a function pointer constant (static pointer to code). Its type is STT_OBJECT. + // $t labels the first byte of a sequence of Thumb instructions. Its type is STT_FUNC. + // $*.<symbolName> is an optional long form + + // AArch64 Mapping Symbols + // $x labels start of sequence of A64 instructions + // $d labels start of sequence of data items + // $*.<symbolName> is an optional long form + bool isMappingSymbol = false; + bool isMappingFunctionSymbol = false; + string entryName(entry->name); + uint64_t target = entry->value; + if (m_arch->GetName() == "aarch64") + { + if (!entryName.rfind("$x", 0)) + isMappingSymbol = isMappingFunctionSymbol = true; + else if (!entryName.rfind("$d", 0)) + isMappingSymbol = true; + } + else + { + if (!entryName.rfind("$a", 0)) + isMappingSymbol = isMappingFunctionSymbol = true; + else if (!entryName.rfind("$b", 0) || !entryName.rfind("$t", 0)) + { + target |= 1; + isMappingSymbol = isMappingFunctionSymbol = true; + } + else if (!entryName.rfind("$d", 0) || !entryName.rfind("$f", 0)) + isMappingSymbol = true; + } + + if (isMappingSymbol) + { + if (!m_elfSections[entry->section].address) // only add artifacts for mapped sections + break; + + // Note: It appears that several '$a' or '$t' mapping symbols to not always indicate function starts. Occasionally, an address is marked + // immediately after a literal pool which is not a function. We explicitly allow mapping symbols for .plt sections for now. + if (isMappingFunctionSymbol && (sectionNames[entry->section] == ".plt")) + { + Ref<Platform> targetPlatform = platform->GetAssociatedPlatformByAddress(target); + AddFunctionForAnalysis(targetPlatform, target); + } + // else // TODO $d and %f + // m_logger->LogError("TODO: %s %p", entryName.c_str(), entry->value); + + // handle long form symbols + if (auto pos = entryName.find(".", 2); (pos != std::string::npos)) + { + entryName = entryName.substr(pos + 1); + if (entryName.size()) + DefineElfSymbol(isMappingFunctionSymbol ? FunctionSymbol : DataSymbol, entryName, entry->value, false, entry->binding, entry->size); + } + break; + } + } + DefineElfSymbol(DataSymbol, entry->name, entry->value, false, entry->binding, entry->size); + break; + case ELF_STT_OBJECT: + DefineElfSymbol(DataSymbol, entry->name, entry->value, false, entry->binding, entry->size); + break; + default: + unknownType: + m_logger->LogDebug("ELF symbol type of %d not handled.", entry->type); + break; + } + } + } + + // Process the queued symbols + m_symbolQueue->Process(); + delete m_symbolQueue; + m_symbolQueue = nullptr; + + EndBulkModifySymbols(); + + auto relocHandler = m_arch->GetRelocationHandler("ELF"); + if (relocHandler) + { + try + { + for (auto& reloc: relocs) + { + BNRelocationInfo relocInfo; + memset(&relocInfo, 0, sizeof(BNRelocationInfo)); + if (m_objectFile) + { + // In unlinked images reloc.offset is relative to the info section specified + if (reloc.sectionIdx >= m_elfSections.size()) + throw ReadException(); + auto sectionName = ReadStringTable(reader, m_sectionStringTable, m_elfSections[reloc.sectionIdx].name); + auto sec = GetSectionByName(sectionName); + if (!sec) + continue; + reloc.offset += sec->GetStart() - imageBaseAdjustment; + } + + relocInfo.symbolIndex = reloc.sym; + relocInfo.address = reloc.offset; + relocInfo.nativeType = reloc.relocType; + relocInfo.addend = reloc.addend; + relocInfo.implicitAddend = reloc.implicit; + relocInfo.base = baseAddress; + virtualReader.Seek(relocInfo.address); + memset(relocInfo.relocationDataCache, 0, sizeof(relocInfo.relocationDataCache)); + virtualReader.TryRead(relocInfo.relocationDataCache, MAX_RELOCATION_SIZE); + m_relocationInfo.push_back(relocInfo); + + if (isArmV7) + { + if(reloc.relocType == R_ARM_TLS_DTPOFF32) + tlsOffsets.push_back(reloc.offset); + else if(reloc.relocType == R_ARM_TLS_DTPMOD32) + tlsModuleStarts.push_back(reloc.offset); + } + } + + if (relocHandler->GetRelocationInfo(this, m_arch, m_relocationInfo)) + { + vector<ElfSymbolTableEntry>* symTable = &symbolTable; + if (!m_objectFile) + symTable = &dynamicSymbolTable; + + size_t anonymousEntryCount = 0; + for (auto& relocInfo : m_relocationInfo) + { + if (relocInfo.type == IgnoredRelocation) + continue; + + // Define absolute relocations with no symbol specified such as R_PPC_RELATIVE and R_ARM_IRELATIVE + // Define unhandled relocations in order to detect them and avoid creating functions at invalid target addresses + if ((relocInfo.symbolIndex == 0) || (relocInfo.type == UnhandledRelocation)) + { + relocInfo.baseRelative = imageBaseAdjustment != 0; + DefineRelocation(m_arch, relocInfo, imageBaseAdjustment, relocInfo.address); + } + else + { + ElfSymbolTableEntry entry; + if (relocInfo.symbolIndex < symTable->size()) + entry = (*symTable)[relocInfo.symbolIndex]; + else if (!ParseSymbolTableEntry(virtualReader, entry, relocInfo.symbolIndex, m_auxSymbolTable, m_dynamicStringTable, true)) + continue; + + if (relocInfo.type == ELFGlobalRelocationType) + DefineElfSymbol(ImportAddressSymbol, entry.name, relocInfo.address, true, entry.binding, entry.size); + else if (relocInfo.type == ELFCopyRelocationType) + DefineElfSymbol(ImportedDataSymbol, entry.name, relocInfo.address, false, entry.binding, entry.size); + else if (relocInfo.type == ELFJumpSlotRelocationType) + DefineElfSymbol(ImportAddressSymbol, entry.name, relocInfo.address, true, entry.binding, entry.size); + + if (entry.type == ELF_STT_SECTION) + { + // Section relative relocation + if (auto section = GetSectionByName(entry.name); section) + { + DefineRelocation(m_arch, relocInfo, section->GetStart(), relocInfo.address); + continue; + } + } + else if (!entry.section) + { + // handle anonymous symbol generation + if (!entry.name.size()) + { + entry.name = "anonymous_"; + if (entry.type == ELF_STT_FUNC) + entry.name += "func"; + else if (entry.type == ELF_STT_OBJECT) + entry.name += "object"; + else + entry.name += "data"; + entry.name += "_"; + + switch(entry.binding) + { + case NoBinding: + entry.name += "bind_none"; + break; + case LocalBinding: + entry.name += "bind_local"; + break; + case GlobalBinding: + entry.name += "bind_global"; + break; + case WeakBinding: + entry.name += "bind_weak"; + break; + default: + break; + } + entry.name += "_"; + entry.name += std::to_string(anonymousEntryCount++); + DefineElfSymbol(ExternalSymbol, entry.name, 0, false, entry.binding, entry.size); + } + + // section undefined so query for external symbol directly + auto symbol = GetSymbolByRawName(entry.name, GetExternalNameSpace()); + if (symbol) + { + DefineRelocation(m_arch, relocInfo, symbol, relocInfo.address); + continue; + } + } + else if (entry.section < m_elfSections.size()) + { + // symbol is relative to a section, look up by address instead of name to avoid ambiguity + uint64_t target = m_elfSections[entry.section].address + entry.value; + auto symbol = GetSymbolByAddress(target); + if (symbol) + { + DefineRelocation(m_arch, relocInfo, symbol, relocInfo.address); + continue; + } + } + + // retrieve first symbol that is not a symbol relocation + auto symbols = GetSymbolsByName(entry.name); + for (const auto& symbol : symbols) + { + if (symbol->GetAddress() == relocInfo.address) + continue; + DefineRelocation(m_arch, relocInfo, symbol, relocInfo.address); + break; + } + } + } + } + } + catch (ReadException&) + { + // Skip errors in relocation tables + m_logger->LogError("Failed to parse relocations"); + } + } + + // Add additional function starts, after symbols have been processed + for (const auto& section : GetSections()) + { + if ((section->GetLength() > 0) && ((section->GetName() == ".init") || (section->GetName() == ".fini"))) + { + string autoSectionName = section->GetName(); + autoSectionName.replace(0, 1, "_"); + auto funcs = GetAnalysisFunctionsForAddress(section->GetStart()); + + if (!m_backedByDatabase) // Don't create symbols that are present in the database snapshot now + { + auto symbol = GetSymbolByAddress(section->GetStart()); + if (!symbol) + DefineAutoSymbol(new Symbol(FunctionSymbol, autoSectionName, section->GetStart(), GlobalBinding)); + } + + // Prefer function creation via recursive descent when encountering mixed architecture binaries where the entry point arch differs from the file arch + if ((funcs.size() == 0) && (entryPointArch == m_arch)) + { + AddFunctionForAnalysis(GetDefaultPlatform(), section->GetStart()); + m_logger->LogDebug("Adding function start: %#" PRIx64 "\n", section->GetStart()); + } + } + else if (!(section->GetLength() % m_addressSize) && + ((section->GetName() == ".init_array") || (section->GetName() == ".fini_array") || + (section->GetName() == ".ctors") || (section->GetName() == ".dtors"))) + { + // define a function pointer array: void (*init_array[])(void) + auto function = Type::FunctionType(Type::VoidType(), platform->GetDefaultCallingConvention(), vector<FunctionParameter>())->WithConfidence(0); + auto function_pointer = Type::PointerType(m_addressSize, function)->WithConfidence(0); + auto array = Type::ArrayType(function_pointer, section->GetLength() / m_addressSize); + DefineDataVariable(section->GetStart(), array); + // trim the first '.' from the section name + string autoSectionName = section->GetName().substr(1); + // define a symbol for the array + if (auto symbol = GetSymbolByAddress(section->GetStart()); !symbol) + DefineAutoSymbol(new Symbol(DataSymbol, autoSectionName, section->GetStart(), NoBinding)); + + virtualReader.Seek(section->GetStart()); + for (uint32_t i = 0; i < section->GetLength() / m_addressSize; i++) + { + uint64_t entry; + try + { + entry = virtualReader.ReadPointer(); + if ((int64_t)entry == -1) + continue; + } + catch (const ReadException& r) + { + m_logger->LogWarn("Fail to read pointer at %#" PRIx64 " while parsing section %s", + virtualReader.GetOffset(), autoSectionName.c_str()); + break; + } + + if (entry) + { + entry += imageBaseAdjustment; + Ref<Architecture> entryArch = entryPointArch->GetAssociatedArchitectureByAddress(entry); + if (entryArch != entryPointArch) + AddFunctionForAnalysis(platform->GetRelatedPlatform(entryArch), entry); + else + AddFunctionForAnalysis(platform, entry); + m_logger->LogDebug("Adding function start: %#" PRIx64 "\n", entry); + } + } + + } + } + + if (m_gotEntryLocations.size() > 0) + { + Ref<Section> got = GetSectionByName(".got"); + if (!got) + { + m_logger->LogWarn( + "ELF view did not find a .got section despite detected relocations; " + "attempting to create sections with appropriate semantics"); + + // Ensure the collected GOT entry locations include the program header specified GOT address. + if (gotStart) + { + m_gotEntryLocations.emplace(gotStart); + // A common setup observed is GOT[0] being the resolver, GOT[1] a constant, then pointers. + // If gotStart and a collected address sandwich a constant, include the constant. + if (m_gotEntryLocations.find(gotStart + 2 * m_addressSize) != m_gotEntryLocations.end()) + m_gotEntryLocations.emplace(gotStart + m_addressSize); + } + + map<uint64_t, size_t> gotSectionsToCreate; + + auto it = m_gotEntryLocations.begin(); + uint64_t start = *it; + uint64_t next = start + m_addressSize; + it++; + + while (true) + { + bool end = (it == m_gotEntryLocations.end()); + + if (end || (*it != next)) + { + gotSectionsToCreate[start] = next - start; + + if (end) + break; + + start = *it; + next = start; + } + + next += m_addressSize; + it++; + } + + for (auto& s : gotSectionsToCreate) + { + // Don't try creating a section if it starts in an already-created + // section. + if (GetSectionsAt(s.first).size() > 0) + continue; + + stringstream ss; + ss << ".got_recovered_" << std::hex << s.first; + AddAutoSection(ss.str(), s.first, s.second, ReadOnlyDataSectionSemantics); + } + } + } + + // 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 && (entryPointAddress != GetStart())) + { + auto func = GetAnalysisFunctionsForAddress(entryPointAddress); + if (func.size() == 1) + AddEntryPointForAnalysis(func[0]->GetPlatform(), entryPointAddress); + else + AddEntryPointForAnalysis(GetDefaultPlatform(), entryPointAddress); + } + + // Add a symbol for the entry point + if (entryPointAddress && (entryPointAddress != GetStart()) && !GetSymbolByAddress(entryPointAddress)) + DefineAutoSymbol(new Symbol(FunctionSymbol, "_start", entryPointAddress, GlobalBinding)); + + // Create type for ELF identification + const string structNamePrefix = (m_addressSize == 4) ? "Elf32_" : "Elf64_"; + + StructureBuilder identBuilder; + identBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 4), "signature"); + identBuilder.AddMember(Type::IntegerType(1, false), "file_class"); + identBuilder.AddMember(Type::IntegerType(1, false), "encoding"); + identBuilder.AddMember(Type::IntegerType(1, false), "version"); + identBuilder.AddMember(Type::IntegerType(1, false), "os"); + identBuilder.AddMember(Type::IntegerType(1, false), "abi_version"); + identBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 7), "pad"); + Ref<Structure> identStruct = identBuilder.Finalize(); + Ref<Type> identType = Type::StructureType(identStruct); + QualifiedName identStructName = structNamePrefix + "Ident"; + const string identStructTypeId = Type::GenerateAutoTypeId("elf", identStructName); + QualifiedName elfIdentStructName = DefineType(identStructTypeId, identStructName, identType); + QualifiedName rawIdentStructName = GetParentView()->DefineType(identStructTypeId, identStructName, identType); + + // Create enum for ELF header machine + EnumerationBuilder elfHeaderMachineBuilder; + elfHeaderMachineBuilder.AddMemberWithValue("EM_NONE", EM_NONE); + elfHeaderMachineBuilder.AddMemberWithValue("EM_M32", EM_M32); + elfHeaderMachineBuilder.AddMemberWithValue("EM_SPARC", EM_SPARC); + elfHeaderMachineBuilder.AddMemberWithValue("EM_386", EM_386); + elfHeaderMachineBuilder.AddMemberWithValue("EM_68K", EM_68K); + elfHeaderMachineBuilder.AddMemberWithValue("EM_88K", EM_88K); + elfHeaderMachineBuilder.AddMemberWithValue("EM_860", EM_860); + elfHeaderMachineBuilder.AddMemberWithValue("EM_MIPS", EM_MIPS); + elfHeaderMachineBuilder.AddMemberWithValue("EM_S370", EM_S370); + elfHeaderMachineBuilder.AddMemberWithValue("EM_MIPS_RS3_LE", EM_MIPS_RS3_LE); + elfHeaderMachineBuilder.AddMemberWithValue("EM_PARISC", EM_PARISC); + elfHeaderMachineBuilder.AddMemberWithValue("EM_VPP500", EM_VPP500); + elfHeaderMachineBuilder.AddMemberWithValue("EM_SPARC32PLUS", EM_SPARC32PLUS); + elfHeaderMachineBuilder.AddMemberWithValue("EM_960", EM_960); + elfHeaderMachineBuilder.AddMemberWithValue("EM_PPC", EM_PPC); + elfHeaderMachineBuilder.AddMemberWithValue("EM_PPC64", EM_PPC64); + elfHeaderMachineBuilder.AddMemberWithValue("EM_S390", EM_S390); + elfHeaderMachineBuilder.AddMemberWithValue("EM_V800", EM_V800); + elfHeaderMachineBuilder.AddMemberWithValue("EM_FR20", EM_FR20); + elfHeaderMachineBuilder.AddMemberWithValue("EM_RH32", EM_RH32); + elfHeaderMachineBuilder.AddMemberWithValue("EM_RCE", EM_RCE); + elfHeaderMachineBuilder.AddMemberWithValue("EM_ARM", EM_ARM); + elfHeaderMachineBuilder.AddMemberWithValue("EM_FAKE_ALPHA", EM_FAKE_ALPHA); + elfHeaderMachineBuilder.AddMemberWithValue("EM_SH", EM_SH); + elfHeaderMachineBuilder.AddMemberWithValue("EM_SPARCV9", EM_SPARCV9); + elfHeaderMachineBuilder.AddMemberWithValue("EM_TRICORE", EM_TRICORE); + elfHeaderMachineBuilder.AddMemberWithValue("EM_ARC", EM_ARC); + elfHeaderMachineBuilder.AddMemberWithValue("EM_H8_300", EM_H8_300); + elfHeaderMachineBuilder.AddMemberWithValue("EM_H8_300H", EM_H8_300H); + elfHeaderMachineBuilder.AddMemberWithValue("EM_H8S", EM_H8S); + elfHeaderMachineBuilder.AddMemberWithValue("EM_H8_500", EM_H8_500); + elfHeaderMachineBuilder.AddMemberWithValue("EM_IA_64", EM_IA_64); + elfHeaderMachineBuilder.AddMemberWithValue("EM_MIPS_X", EM_MIPS_X); + elfHeaderMachineBuilder.AddMemberWithValue("EM_COLDFIRE", EM_COLDFIRE); + elfHeaderMachineBuilder.AddMemberWithValue("EM_68HC12", EM_68HC12); + elfHeaderMachineBuilder.AddMemberWithValue("EM_MMA", EM_MMA); + elfHeaderMachineBuilder.AddMemberWithValue("EM_PCP", EM_PCP); + elfHeaderMachineBuilder.AddMemberWithValue("EM_NCPU", EM_NCPU); + elfHeaderMachineBuilder.AddMemberWithValue("EM_NDR1", EM_NDR1); + elfHeaderMachineBuilder.AddMemberWithValue("EM_STARCORE", EM_STARCORE); + elfHeaderMachineBuilder.AddMemberWithValue("EM_ME16", EM_ME16); + elfHeaderMachineBuilder.AddMemberWithValue("EM_ST100", EM_ST100); + elfHeaderMachineBuilder.AddMemberWithValue("EM_TINYJ", EM_TINYJ); + elfHeaderMachineBuilder.AddMemberWithValue("EM_X86_64", EM_X86_64); + elfHeaderMachineBuilder.AddMemberWithValue("EM_PDSP", EM_PDSP); + elfHeaderMachineBuilder.AddMemberWithValue("EM_FX66", EM_FX66); + elfHeaderMachineBuilder.AddMemberWithValue("EM_ST9PLUS", EM_ST9PLUS); + elfHeaderMachineBuilder.AddMemberWithValue("EM_ST7", EM_ST7); + elfHeaderMachineBuilder.AddMemberWithValue("EM_68HC16", EM_68HC16); + elfHeaderMachineBuilder.AddMemberWithValue("EM_68HC11", EM_68HC11); + elfHeaderMachineBuilder.AddMemberWithValue("EM_68HC08", EM_68HC08); + elfHeaderMachineBuilder.AddMemberWithValue("EM_68HC05", EM_68HC05); + elfHeaderMachineBuilder.AddMemberWithValue("EM_SVX", EM_SVX); + elfHeaderMachineBuilder.AddMemberWithValue("EM_ST19", EM_ST19); + elfHeaderMachineBuilder.AddMemberWithValue("EM_VAX", EM_VAX); + elfHeaderMachineBuilder.AddMemberWithValue("EM_CRIS", EM_CRIS); + elfHeaderMachineBuilder.AddMemberWithValue("EM_JAVELIN", EM_JAVELIN); + elfHeaderMachineBuilder.AddMemberWithValue("EM_FIREPATH", EM_FIREPATH); + elfHeaderMachineBuilder.AddMemberWithValue("EM_ZSP", EM_ZSP); + elfHeaderMachineBuilder.AddMemberWithValue("EM_MMIX", EM_MMIX); + elfHeaderMachineBuilder.AddMemberWithValue("EM_HUANY", EM_HUANY); + elfHeaderMachineBuilder.AddMemberWithValue("EM_PRISM", EM_PRISM); + elfHeaderMachineBuilder.AddMemberWithValue("EM_AVR", EM_AVR); + elfHeaderMachineBuilder.AddMemberWithValue("EM_FR30", EM_FR30); + elfHeaderMachineBuilder.AddMemberWithValue("EM_D10V", EM_D10V); + elfHeaderMachineBuilder.AddMemberWithValue("EM_D30V", EM_D30V); + elfHeaderMachineBuilder.AddMemberWithValue("EM_V850", EM_V850); + elfHeaderMachineBuilder.AddMemberWithValue("EM_M32R", EM_M32R); + elfHeaderMachineBuilder.AddMemberWithValue("EM_MN10300", EM_MN10300); + elfHeaderMachineBuilder.AddMemberWithValue("EM_MN10200", EM_MN10200); + elfHeaderMachineBuilder.AddMemberWithValue("EM_PJ", EM_PJ); + elfHeaderMachineBuilder.AddMemberWithValue("EM_OPENRISC", EM_OPENRISC); + elfHeaderMachineBuilder.AddMemberWithValue("EM_ARC_A5", EM_ARC_A5); + elfHeaderMachineBuilder.AddMemberWithValue("EM_XTENSA", EM_XTENSA); + elfHeaderMachineBuilder.AddMemberWithValue("EM_ALTERA_NIOS2", EM_ALTERA_NIOS2); + elfHeaderMachineBuilder.AddMemberWithValue("EM_AARCH64", EM_AARCH64); + elfHeaderMachineBuilder.AddMemberWithValue("EM_TILEPRO", EM_TILEPRO); + elfHeaderMachineBuilder.AddMemberWithValue("EM_MICROBLAZE", EM_MICROBLAZE); + elfHeaderMachineBuilder.AddMemberWithValue("EM_TILEGX", EM_TILEGX); + elfHeaderMachineBuilder.AddMemberWithValue("EM_NUM", EM_NUM); + + Ref<Enumeration> elfHeaderMachineEnum = elfHeaderMachineBuilder.Finalize(); + Ref<Type> elfHeaderMachineEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), elfHeaderMachineEnum, 2, false); + const string elfHeaderMachineEnumName = "e_machine"; + const string elfHeaderMachineEnumId = Type::GenerateAutoTypeId("elf", elfHeaderMachineEnumName); + QualifiedName elfElfHeaderMachineEnumName = DefineType(elfHeaderMachineEnumId, elfHeaderMachineEnumName, elfHeaderMachineEnumType); + QualifiedName rawElfHeaderMachineEnumName = GetParentView()->DefineType(elfHeaderMachineEnumId, elfHeaderMachineEnumName, elfHeaderMachineEnumType); + + // Create enum for ELF header type + EnumerationBuilder elfHeaderTypeBuilder; + elfHeaderTypeBuilder.AddMemberWithValue("ET_NONE", ET_NONE); + elfHeaderTypeBuilder.AddMemberWithValue("ET_REL", ET_REL); + elfHeaderTypeBuilder.AddMemberWithValue("ET_EXEC", ET_EXEC); + elfHeaderTypeBuilder.AddMemberWithValue("ET_DYN", ET_DYN); + elfHeaderTypeBuilder.AddMemberWithValue("ET_CORE", ET_CORE); + elfHeaderTypeBuilder.AddMemberWithValue("ET_NUM", ET_NUM); + + Ref<Enumeration> elfHeaderTypeEnum = elfHeaderTypeBuilder.Finalize(); + Ref<Type> elfHeaderTypeEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), elfHeaderTypeEnum, 2, false); + const string elfHeaderTypeEnumName = "e_type"; + const string elfHeaderTypeEnumId = Type::GenerateAutoTypeId("elf", elfHeaderTypeEnumName); + QualifiedName elfElfHeaderTypeEnumName = DefineType(elfHeaderTypeEnumId, elfHeaderTypeEnumName, elfHeaderTypeEnumType); + QualifiedName rawElfHeaderTypeEnumName = GetParentView()->DefineType(elfHeaderTypeEnumId, elfHeaderTypeEnumName, elfHeaderTypeEnumType); + + // Create type for ELF header + StructureBuilder elfHeaderBuilder; + elfHeaderBuilder.AddMember(Type::NamedType(this, elfIdentStructName), "ident"); + elfHeaderBuilder.AddMember(Type::NamedType(this, elfElfHeaderTypeEnumName), "type"); + elfHeaderBuilder.AddMember(Type::NamedType(this, elfElfHeaderMachineEnumName), "machine"); + elfHeaderBuilder.AddMember(Type::IntegerType(4, false), "version"); + + if (platform) + { + elfHeaderBuilder.AddMember(Type::PointerType(m_addressSize, Type::FunctionType(Type::VoidType(), + platform->GetDefaultCallingConvention(), vector<FunctionParameter>())), "entry"); + } + else + { + elfHeaderBuilder.AddMember(Type::PointerType(m_addressSize, Type::VoidType()), "entry"); + } + + elfHeaderBuilder.AddMember(Type::IntegerType(m_addressSize, false), "program_header_offset"); + elfHeaderBuilder.AddMember(Type::IntegerType(m_addressSize, false), "section_header_offset"); + elfHeaderBuilder.AddMember(Type::IntegerType(4, false), "flags"); + elfHeaderBuilder.AddMember(Type::IntegerType(2, false), "header_size"); + elfHeaderBuilder.AddMember(Type::IntegerType(2, false), "program_header_size"); + elfHeaderBuilder.AddMember(Type::IntegerType(2, false), "program_header_count"); + elfHeaderBuilder.AddMember(Type::IntegerType(2, false), "section_header_size"); + elfHeaderBuilder.AddMember(Type::IntegerType(2, false), "section_header_count"); + elfHeaderBuilder.AddMember(Type::IntegerType(2, false), "string_table"); + Ref<Structure> elfHeaderStruct = elfHeaderBuilder.Finalize(); + Ref<Type> elfHeaderType = Type::StructureType(elfHeaderStruct); + QualifiedName headerName = structNamePrefix + "Header"; + const string headerTypeId = Type::GenerateAutoTypeId("elf", headerName); + QualifiedName elfHeaderName = DefineType(headerTypeId, headerName, elfHeaderType); + + elfHeaderBuilder.ReplaceMember(0, Type::NamedType(GetParentView(), rawIdentStructName), "ident"); + elfHeaderBuilder.ReplaceMember(1, Type::NamedType(GetParentView(), rawElfHeaderTypeEnumName), "type"); + elfHeaderBuilder.ReplaceMember(2, Type::NamedType(GetParentView(), rawElfHeaderMachineEnumName), "machine"); + elfHeaderBuilder.ReplaceMember(4, Type::IntegerType(m_addressSize, false), "entry"); + Ref<Structure> rawElfHeaderStruct = elfHeaderBuilder.Finalize(); + Ref<Type> rawElfHeaderType = Type::StructureType(rawElfHeaderStruct); + QualifiedName rawHeaderName = GetParentView()->DefineType(headerTypeId, headerName, rawElfHeaderType); + + // Define variable for ELF header + uint64_t addr; + if (GetAddressForDataOffset(0, addr)) + { + DefineDataVariable(addr, Type::NamedType(this, elfHeaderName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__elf_header", addr, LocalBinding)); + } + GetParentView()->DefineDataVariable(0, Type::NamedType(GetParentView(), rawHeaderName)); + GetParentView()->DefineAutoSymbol(new Symbol(DataSymbol, "__elf_header", 0, LocalBinding)); + + + // Create enum for ELF program header type + EnumerationBuilder programHeaderTypeBuilder; + programHeaderTypeBuilder.AddMemberWithValue("PT_NULL", ELF_PT_NULL); + programHeaderTypeBuilder.AddMemberWithValue("PT_LOAD", ELF_PT_LOAD); + programHeaderTypeBuilder.AddMemberWithValue("PT_DYNAMIC", ELF_PT_DYNAMIC); + programHeaderTypeBuilder.AddMemberWithValue("PT_INTERP", ELF_PT_INTERP); + programHeaderTypeBuilder.AddMemberWithValue("PT_NOTE", ELF_PT_NOTE); + programHeaderTypeBuilder.AddMemberWithValue("PT_SHLIB", ELF_PT_SHLIB); + programHeaderTypeBuilder.AddMemberWithValue("PT_PHDR", ELF_PT_PHDR); + programHeaderTypeBuilder.AddMemberWithValue("PT_TLS", ELF_PT_TLS); + programHeaderTypeBuilder.AddMemberWithValue("PT_NUM", ELF_PT_NUM); + programHeaderTypeBuilder.AddMemberWithValue("PT_LOOS", ELF_PT_LOOS); + programHeaderTypeBuilder.AddMemberWithValue("PT_GNU_EH_FRAME", ELF_PT_GNU_EH_FRAME); + programHeaderTypeBuilder.AddMemberWithValue("PT_GNU_STACK", ELF_PT_GNU_STACK); + programHeaderTypeBuilder.AddMemberWithValue("PT_GNU_RELRO", ELF_PT_GNU_RELRO); + programHeaderTypeBuilder.AddMemberWithValue("PT_LOSUNW", ELF_PT_LOSUNW); + programHeaderTypeBuilder.AddMemberWithValue("PT_SUNWBSS", ELF_PT_SUNWBSS); + programHeaderTypeBuilder.AddMemberWithValue("PT_SUNWSTACK", ELF_PT_SUNWSTACK); + programHeaderTypeBuilder.AddMemberWithValue("PT_MIPS_REGINFO", ELF_PT_MIPS_REGINFO); + programHeaderTypeBuilder.AddMemberWithValue("PT_MIPS_RTPROC", ELF_PT_MIPS_RTPROC); + programHeaderTypeBuilder.AddMemberWithValue("PT_MIPS_OPTIONS", ELF_PT_MIPS_OPTIONS); + programHeaderTypeBuilder.AddMemberWithValue("PT_MIPS_ABIFLAGS", ELF_PT_MIPS_ABIFLAGS); + + Ref<Enumeration> programHeaderTypeEnum = programHeaderTypeBuilder.Finalize(); + Ref<Type> programHeaderTypeEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), programHeaderTypeEnum, 4, false); + const string programHeaderTypeEnumName = "p_type"; + const string programHeaderTypeEnumId = Type::GenerateAutoTypeId("elf", programHeaderTypeEnumName); + QualifiedName elfProgramHeaderTypeEnumName = DefineType(programHeaderTypeEnumId, programHeaderTypeEnumName, programHeaderTypeEnumType); + QualifiedName rawProgramHeaderTypeEnumName = GetParentView()->DefineType(programHeaderTypeEnumId, programHeaderTypeEnumName, programHeaderTypeEnumType); + + + // Create enum for ELF program header flags + EnumerationBuilder programHeaderFlagsBuilder; + programHeaderFlagsBuilder.AddMemberWithValue("PF_X", PF_X); + programHeaderFlagsBuilder.AddMemberWithValue("PF_W", PF_W); + programHeaderFlagsBuilder.AddMemberWithValue("PF_R", PF_R); + + Ref<Enumeration> programHeaderFlagsEnum = programHeaderFlagsBuilder.Finalize(); + Ref<Type> programHeaderFlagsEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), programHeaderFlagsEnum, 4, false); + const string programHeaderFlagsEnumName = "p_flags"; + const string programHeaderFlagsEnumId = Type::GenerateAutoTypeId("elf", programHeaderFlagsEnumName); + QualifiedName elfProgramHeaderFlagsEnumName = DefineType(programHeaderFlagsEnumId, programHeaderFlagsEnumName, programHeaderFlagsEnumType); + QualifiedName rawProgramHeaderFlagsEnumName = GetParentView()->DefineType(programHeaderFlagsEnumId, programHeaderFlagsEnumName, programHeaderFlagsEnumType); + + // Create type for ELF program headers + StructureBuilder programHeaderBuilder; + if (m_addressSize == 4) + { + programHeaderBuilder.AddMember(Type::NamedType(this, elfProgramHeaderTypeEnumName), "type"); + programHeaderBuilder.AddMember(Type::IntegerType(4, false), "offset"); + programHeaderBuilder.AddMember(Type::IntegerType(4, false), "virtual_address"); + programHeaderBuilder.AddMember(Type::IntegerType(4, false), "physical_address"); + programHeaderBuilder.AddMember(Type::IntegerType(4, false), "file_size"); + programHeaderBuilder.AddMember(Type::IntegerType(4, false), "memory_size"); + programHeaderBuilder.AddMember(Type::NamedType(this, elfProgramHeaderFlagsEnumName), "flags"); + programHeaderBuilder.AddMember(Type::IntegerType(4, false), "align"); + } + else + { + programHeaderBuilder.AddMember(Type::NamedType(this, elfProgramHeaderTypeEnumName), "type"); + programHeaderBuilder.AddMember(Type::NamedType(this, elfProgramHeaderFlagsEnumName), "flags"); + programHeaderBuilder.AddMember(Type::IntegerType(8, false), "offset"); + programHeaderBuilder.AddMember(Type::IntegerType(8, false), "virtual_address"); + programHeaderBuilder.AddMember(Type::IntegerType(8, false), "physical_address"); + programHeaderBuilder.AddMember(Type::IntegerType(8, false), "file_size"); + programHeaderBuilder.AddMember(Type::IntegerType(8, false), "memory_size"); + programHeaderBuilder.AddMember(Type::IntegerType(8, false), "align"); + } + Ref<Structure> programHeaderStruct = programHeaderBuilder.Finalize(); + Ref<Type> programHeaderType = Type::StructureType(programHeaderStruct); + QualifiedName programHeaderName = structNamePrefix + "ProgramHeader"; + const string programHeaderTypeId = Type::GenerateAutoTypeId("elf", programHeaderName); + QualifiedName elfProgramHeaderName = DefineType(programHeaderTypeId, programHeaderName, programHeaderType); + + if (m_addressSize == 4) + { + programHeaderBuilder.ReplaceMember(0, Type::NamedType(GetParentView(), rawProgramHeaderTypeEnumName), "type"); + programHeaderBuilder.ReplaceMember(6, Type::NamedType(GetParentView(), rawProgramHeaderFlagsEnumName), "flags"); + } + else + { + programHeaderBuilder.ReplaceMember(0, Type::NamedType(GetParentView(), rawProgramHeaderTypeEnumName), "type"); + programHeaderBuilder.ReplaceMember(1, Type::NamedType(GetParentView(), rawProgramHeaderFlagsEnumName), "flags"); + } + Ref<Structure> rawProgramHeaderStruct = programHeaderBuilder.Finalize(); + Ref<Type> rawProgramHeaderType = Type::StructureType(rawProgramHeaderStruct); + QualifiedName rawProgramHeaderName = GetParentView()->DefineType(programHeaderTypeId, programHeaderName, rawProgramHeaderType); + + // Define variable for ELF program headers + if (m_programHeaderCount != 0) + { + if (GetAddressForDataOffset(m_programHeaderOffset, addr)) + { + DefineDataVariable(addr, Type::ArrayType(Type::NamedType(this, elfProgramHeaderName), + m_programHeaderCount)); + DefineAutoSymbol(new Symbol(DataSymbol, "__elf_program_headers", addr, LocalBinding)); + } + GetParentView()->DefineDataVariable(m_programHeaderOffset, Type::ArrayType(Type::NamedType( + GetParentView(), rawProgramHeaderName), m_programHeaderCount)); + GetParentView()->DefineAutoSymbol(new Symbol(DataSymbol, "__elf_program_headers", m_programHeaderOffset, LocalBinding)); + } + + // Create enum for ELF section header type + EnumerationBuilder sectionHeaderTypeBuilder; + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_NULL", ELF_SHT_NULL); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_PROGBITS", ELF_SHT_PROGBITS); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_SYMTAB", ELF_SHT_SYMTAB); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_STRTAB", ELF_SHT_STRTAB); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_RELA", ELF_SHT_RELA); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_HASH", ELF_SHT_HASH); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_DYNAMIC", ELF_SHT_DYNAMIC); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_NOTE", ELF_SHT_NOTE); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_NOBITS", ELF_SHT_NOBITS); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_REL", ELF_SHT_REL); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_SHLIB", ELF_SHT_SHLIB); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_DYNSYM", ELF_SHT_DYNSYM); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_LOUSER", ELF_SHT_LOUSER); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_HIUSER", ELF_SHT_HIUSER); + + // Machine specific section header types + if (m_commonHeader.arch == EM_PARISC) + { + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_PARISC_EXT", ELF_SHT_EXT); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_PARISC_UNWIND", ELF_SHT_UNWIND); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_PARISC_DOC", ELF_SHT_PARISC_DOC); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_PARISC_ANNOT", ELF_SHT_PARISC_ANNOT); + } + else if (m_commonHeader.arch == EM_IA_64) + { + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_IA_64_EXT", ELF_SHT_EXT); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_IA_64_UNWIND", ELF_SHT_UNWIND); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_IA_64_LOPSREG", ELF_SHT_IA_64_LOPSREG); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_IA_64_HIPSREG", ELF_SHT_IA_64_HIPSREG); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_IA_64_PRIORITY_INIT", ELF_SHT_IA_64_PRIORITY_INIT); + } + else if (m_commonHeader.arch == EM_X86_64) + { + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_AMD64_UNWIND", ELF_SHT_UNWIND); + } + else + { + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_LOPROC", ELF_SHT_LOPROC); + sectionHeaderTypeBuilder.AddMemberWithValue("SHT_HIPROC", ELF_SHT_HIPROC); + } + + Ref<Enumeration> sectionHeaderTypeEnum = sectionHeaderTypeBuilder.Finalize(); + Ref<Type> sectionHeaderTypeEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), sectionHeaderTypeEnum, 4, false); + const string sectionHeaderTypeEnumName = "sh_type"; + const string sectionHeaderTypeEnumId = Type::GenerateAutoTypeId("elf", sectionHeaderTypeEnumName); + QualifiedName elfSectionHeaderTypeEnumName = DefineType(sectionHeaderTypeEnumId, sectionHeaderTypeEnumName, sectionHeaderTypeEnumType); + QualifiedName rawSectionHeaderTypeEnumName = GetParentView()->DefineType(sectionHeaderTypeEnumId, sectionHeaderTypeEnumName, sectionHeaderTypeEnumType); + + // Create enum for ELF section header flags + EnumerationBuilder sectionHeaderFlagsBuilder; + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_WRITE", ELF_SHF_WRITE); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_ALLOC", ELF_SHF_ALLOC); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_EXECINSTR", ELF_SHF_EXECINSTR); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_MERGE", ELF_SHF_MERGE); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_STRINGS", ELF_SHF_STRINGS); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_INFO_LINK", ELF_SHF_INFO_LINK); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_LINK_ORDER", ELF_SHF_LINK_ORDER); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_OS_NONCONFORMING", ELF_SHF_OS_NONCONFORMING); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_GROUP", ELF_SHF_GROUP); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_TLS", ELF_SHF_TLS); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_COMPRESSED", ELF_SHF_COMPRESSED); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_MASKOS", ELF_SHF_MASKOS); + + // Machine specific section header flags + if (m_commonHeader.arch == EM_PARISC) + { + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_PARISC_SHORT", ELF_SHF_PARISC_SHORT); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_PARISC_HUGE", ELF_SHF_PARISC_HUGE); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_PARISC_SBP", ELF_SHF_PARISC_SBP); + } + else if (m_commonHeader.arch == EM_IA_64) + { + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_IA_64_SHORT", ELF_SHF_IA_64_SHORT); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_IA_64_NORECOV", ELF_SHF_IA_64_NORECOV); + } + else if (m_commonHeader.arch == EM_X86_64) + { + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_AMD64_LARGE", ELF_SHF_AMD64_LARGE); + } + else if (In(m_arch->GetName(), {"aarch64", "armv7", "armv7eb", "thumb2", "thumb2eb"})) + { + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_ENTRYSECT", ELF_SHF_ENTRYSECT); + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_COMDEF", ELF_SHF_COMDEF); + } + else if (In(m_arch->GetName(), {"mipsel32", "mips32"})) + { + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_MIPS_GPREL", ELF_SHF_MIPS_GPREL); + } + else if (In(m_arch->GetName(), {"ppc", "ppc64", "ppc_le", "ppc64_le"})) + { + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_EXCLUDE", ELF_SHF_EXCLUDE); + } + else + { + sectionHeaderFlagsBuilder.AddMemberWithValue("SHF_MASKPROC", ELF_SHF_MASKPROC); + } + + Ref<Enumeration> sectionHeaderFlagsEnum = sectionHeaderFlagsBuilder.Finalize(); + Ref<Type> sectionHeaderFlagsEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), sectionHeaderFlagsEnum, m_addressSize, false); + const string sectionHeaderFlagsEnumName = "sh_flags"; + const string sectionHeaderFlagsEnumId = Type::GenerateAutoTypeId("elf", sectionHeaderFlagsEnumName); + QualifiedName elfSectionHeaderFlagsEnumName = DefineType(sectionHeaderFlagsEnumId, sectionHeaderFlagsEnumName, sectionHeaderFlagsEnumType); + QualifiedName rawSectionHeaderFlagsEnumName = GetParentView()->DefineType(sectionHeaderFlagsEnumId, sectionHeaderFlagsEnumName, sectionHeaderFlagsEnumType); + + // Create type for ELF section headers + StructureBuilder sectionHeaderBuilder; + sectionHeaderBuilder.AddMember(Type::IntegerType(4, false), "name"); + sectionHeaderBuilder.AddMember(Type::NamedType(this, elfSectionHeaderTypeEnumName), "type"); + sectionHeaderBuilder.AddMember(Type::NamedType(this, elfSectionHeaderFlagsEnumName), "flags"); + sectionHeaderBuilder.AddMember(Type::IntegerType(m_addressSize, false), "address"); + sectionHeaderBuilder.AddMember(Type::IntegerType(m_addressSize, false), "offset"); + sectionHeaderBuilder.AddMember(Type::IntegerType(m_addressSize, false), "size"); + sectionHeaderBuilder.AddMember(Type::IntegerType(4, false), "link"); + sectionHeaderBuilder.AddMember(Type::IntegerType(4, false), "info"); + sectionHeaderBuilder.AddMember(Type::IntegerType(m_addressSize, false), "align"); + sectionHeaderBuilder.AddMember(Type::IntegerType(m_addressSize, false), "entry_size"); + Ref<Structure> sectionHeaderStruct = sectionHeaderBuilder.Finalize(); + Ref<Type> sectionHeaderType = Type::StructureType(sectionHeaderStruct); + QualifiedName sectionHeaderName = structNamePrefix + "SectionHeader"; + const string sectionHeaderTypeId = Type::GenerateAutoTypeId("elf", sectionHeaderName); + QualifiedName elfSectionHeaderName = DefineType(sectionHeaderTypeId, sectionHeaderName, sectionHeaderType); + + sectionHeaderBuilder.ReplaceMember(1, Type::NamedType(GetParentView(), rawSectionHeaderTypeEnumName), "type"); + sectionHeaderBuilder.ReplaceMember(2, Type::NamedType(GetParentView(), rawSectionHeaderFlagsEnumName), "flags"); + Ref<Structure> rawSectionHeaderStruct = sectionHeaderBuilder.Finalize(); + Ref<Type> rawSectionHeaderType = Type::StructureType(rawSectionHeaderStruct); + QualifiedName rawSectionHeaderName = GetParentView()->DefineType(sectionHeaderTypeId, sectionHeaderName, rawSectionHeaderType); + + // Define variable for ELF program headers + if (m_sectionHeaderCount != 0) + { + uint64_t configuredSectionCount = 100; + if (viewSettings && viewSettings->Contains("files.elf.maxSectionHeaderCount")) + configuredSectionCount = viewSettings->Get<uint64_t>("files.elf.maxSectionHeaderCount", this); + uint64_t sectionCount = std::min<uint64_t>(m_sectionHeaderCount, configuredSectionCount); + if (GetAddressForDataOffset(m_sectionHeaderOffset, addr)) + { + DefineDataVariable(addr, Type::ArrayType(Type::NamedType(this, elfSectionHeaderName), sectionCount)); + DefineAutoSymbol(new Symbol(DataSymbol, "__elf_section_headers", addr, LocalBinding)); + } + GetParentView()->DefineDataVariable(m_sectionHeaderOffset, Type::ArrayType(Type::NamedType(GetParentView(), rawSectionHeaderName), sectionCount)); + GetParentView()->DefineAutoSymbol(new Symbol(DataSymbol, "__elf_section_headers", m_sectionHeaderOffset, LocalBinding)); + } + + // Add types for dynamic table + if (m_dynamicTable.virtualAddress && m_numDynamicTableEntries) + { + EnumerationBuilder dynTagEnumBuilder; + dynTagEnumBuilder.AddMemberWithValue("DT_NULL", ELF_DT_NULL); + dynTagEnumBuilder.AddMemberWithValue("DT_NEEDED", ELF_DT_NEEDED); + dynTagEnumBuilder.AddMemberWithValue("DT_PLTRELSZ", ELF_DT_PLTRELSZ); + dynTagEnumBuilder.AddMemberWithValue("DT_PLTGOT", ELF_DT_PLTGOT); + dynTagEnumBuilder.AddMemberWithValue("DT_HASH", ELF_DT_HASH); + dynTagEnumBuilder.AddMemberWithValue("DT_STRTAB", ELF_DT_STRTAB); + dynTagEnumBuilder.AddMemberWithValue("DT_SYMTAB", ELF_DT_SYMTAB); + dynTagEnumBuilder.AddMemberWithValue("DT_RELA", ELF_DT_RELA); + dynTagEnumBuilder.AddMemberWithValue("DT_RELASZ", ELF_DT_RELASZ); + dynTagEnumBuilder.AddMemberWithValue("DT_RELAENT", ELF_DT_RELAENT); + dynTagEnumBuilder.AddMemberWithValue("DT_STRSZ", ELF_DT_STRSZ); + //dynTagEnumBuilder.AddMemberWithValue("DT_DYNSYM", ELF_DT_DYNSYM); + dynTagEnumBuilder.AddMemberWithValue("DT_SYMENT", ELF_DT_SYMENT); + dynTagEnumBuilder.AddMemberWithValue("DT_INIT", ELF_DT_INIT); + dynTagEnumBuilder.AddMemberWithValue("DT_FINI", ELF_DT_FINI); + dynTagEnumBuilder.AddMemberWithValue("DT_SONAME", ELF_DT_SONAME); + dynTagEnumBuilder.AddMemberWithValue("DT_RPATH", ELF_DT_RPATH); + dynTagEnumBuilder.AddMemberWithValue("DT_SYMBOLIC", ELF_DT_SYMBOLIC); + dynTagEnumBuilder.AddMemberWithValue("DT_REL", ELF_DT_REL); + dynTagEnumBuilder.AddMemberWithValue("DT_RELSZ", ELF_DT_RELSZ); + dynTagEnumBuilder.AddMemberWithValue("DT_RELENT", ELF_DT_RELENT); + dynTagEnumBuilder.AddMemberWithValue("DT_PLTREL", ELF_DT_PLTREL); + dynTagEnumBuilder.AddMemberWithValue("DT_DEBUG", ELF_DT_DEBUG); + dynTagEnumBuilder.AddMemberWithValue("DT_TEXTREL", ELF_DT_TEXTREL); + dynTagEnumBuilder.AddMemberWithValue("DT_JMPREL", ELF_DT_JMPREL); + dynTagEnumBuilder.AddMemberWithValue("DT_BIND_NOW", ELF_DT_BIND_NOW); + dynTagEnumBuilder.AddMemberWithValue("DT_INIT_ARRAY", ELF_DT_INIT_ARRAY); + dynTagEnumBuilder.AddMemberWithValue("DT_FINI_ARRAY", ELF_DT_FINI_ARRAY); + dynTagEnumBuilder.AddMemberWithValue("DT_INIT_ARRAYSZ", ELF_DT_INIT_ARRAYSZ); + dynTagEnumBuilder.AddMemberWithValue("DT_FINI_ARRAYSZ", ELF_DT_FINI_ARRAYSZ); + dynTagEnumBuilder.AddMemberWithValue("DT_RUNPATH", ELF_DT_RUNPATH); + dynTagEnumBuilder.AddMemberWithValue("DT_FLAGS", ELF_DT_FLAGS); + dynTagEnumBuilder.AddMemberWithValue("DT_ENCODING", ELF_DT_ENCODING); + dynTagEnumBuilder.AddMemberWithValue("DT_PREINIT_ARRAY", ELF_DT_PREINIT_ARRAY); + dynTagEnumBuilder.AddMemberWithValue("DT_PREINIT_ARRAYSZ", ELF_DT_PREINIT_ARRAYSZ); + + dynTagEnumBuilder.AddMemberWithValue("DT_LOOS", ELF_DT_LOOS); + dynTagEnumBuilder.AddMemberWithValue("DT_SUNW_RTLDINF", ELF_DT_SUNW_RTLDINF); + dynTagEnumBuilder.AddMemberWithValue("DT_HIOS", ELF_DT_HIOS); + dynTagEnumBuilder.AddMemberWithValue("DT_VALRNGLO", ELF_DT_VALRNGLO); + dynTagEnumBuilder.AddMemberWithValue("DT_CHECKSUM", ELF_DT_CHECKSUM); + dynTagEnumBuilder.AddMemberWithValue("DT_PLTPADSZ", ELF_DT_PLTPADSZ); + dynTagEnumBuilder.AddMemberWithValue("DT_MOVEENT", ELF_DT_MOVEEN); + dynTagEnumBuilder.AddMemberWithValue("DT_MOVESZ", ELF_DT_MOVES); + dynTagEnumBuilder.AddMemberWithValue("DT_FEATURE_1", ELF_DT_FEATURE_1); + dynTagEnumBuilder.AddMemberWithValue("DT_POSFLAG_1", ELF_DT_POSFLAG_1); + dynTagEnumBuilder.AddMemberWithValue("DT_SYMINSZ", ELF_DT_SYMINSZ); + dynTagEnumBuilder.AddMemberWithValue("DT_SYMINENT", ELF_DT_SYMINENT); + dynTagEnumBuilder.AddMemberWithValue("DT_VALRNGHI", ELF_DT_VALRNGH); + dynTagEnumBuilder.AddMemberWithValue("DT_ADDRRNGLO", ELF_DT_ADDRRNGLO); + dynTagEnumBuilder.AddMemberWithValue("DT_GNU_HASH", ELF_DT_GNU_HASH); + dynTagEnumBuilder.AddMemberWithValue("DT_CONFIG", ELF_DT_CONFIG); + dynTagEnumBuilder.AddMemberWithValue("DT_DEPAUDIT", ELF_DT_DEPAUDIT); + dynTagEnumBuilder.AddMemberWithValue("DT_AUDIT", ELF_DT_AUDIT); + dynTagEnumBuilder.AddMemberWithValue("DT_PLTPAD", ELF_DT_PLTPAD); + dynTagEnumBuilder.AddMemberWithValue("DT_MOVETAB", ELF_DT_MOVETAB); + dynTagEnumBuilder.AddMemberWithValue("DT_SYMINFO", ELF_DT_SYMINFO); + dynTagEnumBuilder.AddMemberWithValue("DT_ADDRRNGHI", ELF_DT_ADDRRNGHI); + dynTagEnumBuilder.AddMemberWithValue("DT_RELACOUNT", ELF_DT_RELACOUNT); + dynTagEnumBuilder.AddMemberWithValue("DT_RELCOUNT", ELF_DT_RELCOUNT); + dynTagEnumBuilder.AddMemberWithValue("DT_FLAGS_1", ELF_DT_FLAGS_1); + dynTagEnumBuilder.AddMemberWithValue("DT_VERDEF", ELF_DT_VERDEF); + dynTagEnumBuilder.AddMemberWithValue("DT_VERDEFNUM", ELF_DT_VERDEFNUM); + dynTagEnumBuilder.AddMemberWithValue("DT_VERNEED", ELF_DT_VERNEED); + dynTagEnumBuilder.AddMemberWithValue("DT_VERNEEDNUM", ELF_DT_VERNEEDNUM); + dynTagEnumBuilder.AddMemberWithValue("DT_VERSYM", ELF_DT_VERSYM); + + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_RLD_VERSION", ELF_DT_MIPS_RLD_VERSION); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_TIME_STAMP", ELF_DT_MIPS_TIME_STAMP); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_ICHECKSUM", ELF_DT_MIPS_ICHECKSUM); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_IVERSION", ELF_DT_MIPS_IVERSION); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_FLAGS", ELF_DT_MIPS_FLAGS); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_BASE_ADDRESS", ELF_DT_MIPS_BASE_ADDRESS); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_CONFLICT", ELF_DT_MIPS_CONFLICT); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_LIBLIST", ELF_DT_MIPS_LIBLIST); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_LOCAL_GOTNO", ELF_DT_MIPS_LOCAL_GOTNO); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_CONFLICTNO", ELF_DT_MIPS_CONFLICTNO); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_LIBLISTNO", ELF_DT_MIPS_LIBLISTNO); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_SYMTABNO", ELF_DT_MIPS_SYMTABNO); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_UNREFEXTNO", ELF_DT_MIPS_UNREFEXTNO); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_GOTSYM", ELF_DT_MIPS_GOTSYM); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_HIPAGENO", ELF_DT_MIPS_HIPAGENO); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_RLD_MAP", ELF_DT_MIPS_RLD_MAP); + dynTagEnumBuilder.AddMemberWithValue("DT_MIPS_RLD_MAP_REL", ELF_DT_MIPS_RLD_MAP_REL); + + uint64_t entrySize = m_elf32 ? 8 : 16; + Ref<Enumeration> dynTagEnum = dynTagEnumBuilder.Finalize(); + Ref<Type> dynTagEnumType = Type::EnumerationType(GetParentView()->GetDefaultArchitecture(), dynTagEnum, entrySize / 2, false); + const string dynTagEnumName = "e_dyn_tag"; + const string dynTagEnumId = Type::GenerateAutoTypeId("elf", dynTagEnumName); + QualifiedName elfdynTagEnumName = DefineType(dynTagEnumId, dynTagEnumName, dynTagEnumType); + + StructureBuilder dynamicEntryBuilder; + dynamicEntryBuilder.AddMember(Type::NamedType(this, elfdynTagEnumName), "d_tag"); + dynamicEntryBuilder.AddMember(Type::IntegerType(entrySize / 2, false), "d_val"); + Ref<Structure> dynEntryStruct = dynamicEntryBuilder.Finalize(); + Ref<Type> dynEntryType = Type::StructureType(dynEntryStruct); + QualifiedName dynEntryName = m_elf32 ? string("Elf32_Dyn"): string("Elf64_Dyn"); + const string dynEntryTypeId = Type::GenerateAutoTypeId("elf", dynEntryName); + QualifiedName dynEntryTypeName = DefineType(dynEntryTypeId, dynEntryName, dynEntryType); + uint64_t adjustedVirtualAddr = m_dynamicTable.virtualAddress + imageBaseAdjustment; + DefineDataVariable(adjustedVirtualAddr, Type::ArrayType(Type::NamedType(this, dynEntryTypeName), m_numDynamicTableEntries)); + DefineAutoSymbol(new Symbol(DataSymbol, "__elf_dynamic_table", adjustedVirtualAddr, NoBinding)); + } + + // Add types for the dynamic symbol table + if (m_auxSymbolTable.size) + { + StructureBuilder symTableBuilder; + if (m_elf32) + { + symTableBuilder.AddMember(Type::IntegerType(4, false), "st_name"); + symTableBuilder.AddMember(Type::IntegerType(4, false), "st_value"); + symTableBuilder.AddMember(Type::IntegerType(4, false), "st_size"); + symTableBuilder.AddMember(Type::IntegerType(1, false), "st_info"); + symTableBuilder.AddMember(Type::IntegerType(1, false), "st_other"); + symTableBuilder.AddMember(Type::IntegerType(2, false), "st_shndx"); + } + else + { + symTableBuilder.AddMember(Type::IntegerType(4, false), "st_name"); + symTableBuilder.AddMember(Type::IntegerType(1, false), "st_info"); + symTableBuilder.AddMember(Type::IntegerType(1, false), "st_other"); + symTableBuilder.AddMember(Type::IntegerType(2, false), "st_shndx"); + symTableBuilder.AddMember(Type::IntegerType(8, false), "st_value"); + symTableBuilder.AddMember(Type::IntegerType(8, false), "st_size"); + } + Ref<Structure> symTableStruct = symTableBuilder.Finalize(); + Ref<Type> symTableType = Type::StructureType(symTableStruct); + QualifiedName symTableName = m_elf32 ? string("Elf32_Sym") : string("Elf64_Sym"); + const string symTableTypeId = Type::GenerateAutoTypeId("elf", symTableName); + QualifiedName symTableTypeName = DefineType(symTableTypeId, symTableName, symTableType); + DefineDataVariable(m_auxSymbolTable.offset, Type::ArrayType(Type::NamedType(this, symTableTypeName), m_auxSymbolTable.size / m_auxSymbolTableEntrySize)); + DefineAutoSymbol(new Symbol(DataSymbol, "__elf_symbol_table", m_auxSymbolTable.offset, NoBinding)); + } + + // In 32-bit mips with .got, add .extern symbol "RTL_Resolve" + if (gotStart && In(m_arch->GetName(), {"mips32", "mipsel32", "mips64", "nanomips"})) + { + const char *name = "RTL_Resolve"; + + /* create symbol for RTL_Resolve(), address will be auto-assigned after placement in .extern */ + Ref<Symbol> symbol = new Symbol( + ExternalSymbol, /* type */ + name, /* shortName */ + name, /* fullName */ + name, /* rawName */ + 0, /* byAddr */ + GlobalBinding, /* binding */ + GetExternalNameSpace() /* namespace */ + ); + + /* create type, associate it with RTL_Resolve */ + Ref<Type> ptr_type = Type::PointerType(m_arch, Type::VoidType())->WithConfidence(BN_FULL_CONFIDENCE); + + Ref<CallingConvention> cc = m_arch->GetCallingConventionByName("linux-rtlresolve"); + + Ref<Type> type = Type::FunctionType( + Type::VoidType(), /* returnValue */ + cc, /* callingConvention */ + { /* params */ + FunctionParameter("caller_ret_addr", ptr_type), + FunctionParameter("sym_idx", Type::IntegerType(4, true)), + }, + false, /* hasVariableArguments */ + false, /* canReturn */ + 0 + ); + + /* This BinaryView helper does: + 1) DefineAutoSymbol(symbol); + 2) m_externalTypes[name] = type; + ...so that upon BinaryView finalization, data variables are made. */ + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), symbol, type); + + /* Create relocation entry associated with this symbol so that reloc + servicing will overwrite GOT[0] with symbol's address. */ + BNRelocationInfo relocInfo; + memset(&relocInfo, 0, sizeof(BNRelocationInfo)); + relocInfo.base = gotStart; + relocInfo.address = gotStart; + relocInfo.size = m_arch->GetAddressSize(); + relocInfo.nativeType = m_arch->GetAddressSize() == 4 ? 2 /* R_MIPS_32 */ : 18 /* R_MIPS_64 */; + + DefineRelocation(m_arch, relocInfo, symbol, relocInfo.address); + } + + // Add type, data variables for TLS entries + for (auto offset : tlsModuleStarts) + { + /* All module ID's are set to 0. */ + DefineDataVariable(offset, Type::IntegerType(4, false)->WithConfidence(BN_FULL_CONFIDENCE)); + } + for (auto offset : tlsOffsets) + { + /* In runtime reality, these become the offsets of the variables within TLS data structures. + In static listing, we place a pointer to the variable for user convenience. */ + DefineDataVariable(offset, + Type::PointerType(m_arch, Type::VoidType())); + } + 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("ELF parsing took %.3f seconds\n", t); + m_stringTableCache.clear(); + return true; +} + + +void ElfView::DefineElfSymbol(BNSymbolType type, const string& incomingName, uint64_t addr, bool gotEntry, + BNSymbolBinding binding, size_t size, Ref<Type> typeObj) +{ + // Ensure symbol is within the executable + if (type != ExternalSymbol && !IsValidOffset(addr)) + return; + + string name = incomingName; + Ref<Type> symbolTypeRef; + if ((type == ExternalSymbol) || (type == ImportAddressSymbol) || (type == ImportedDataSymbol)) + { + QualifiedName n(name); + Ref<TypeLibrary> lib = nullptr; + symbolTypeRef = ImportTypeLibraryObject(lib, n); + if (symbolTypeRef) + { + m_logger->LogDebug("elf: type Library '%s' found hit for '%s'", lib->GetName().c_str(), name.c_str()); + if (type != ExternalSymbol || addr != 0) + { + RecordImportedObjectLibrary(GetDefaultPlatform(), addr, lib, n); + } + } + } + + auto pos = name.rfind("@@"); + if (type == ExternalSymbol && pos != string::npos) + { + name = name.substr(0, pos); + } + + pos = name.rfind("@GLIBC"); + if (type == ExternalSymbol && pos != string::npos) + { + name = name.substr(0, pos); + } + + // Deprioritize local label symbol names + if (type == DataSymbol && binding == LocalBinding && !name.empty() && name[0] == '.') + { + type = LocalLabelSymbol; + } + + // If name is empty, symbol is not valid + if (name.size() == 0) + return; + + if (!symbolTypeRef) + symbolTypeRef = typeObj; + + if (gotEntry) + m_gotEntryLocations.emplace(addr); + + auto process = [=]() { + NameSpace nameSpace = GetInternalNameSpace(); + if (type == ExternalSymbol) + { + nameSpace = GetExternalNameSpace(); + } + + // 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] == '@') || (name[0] == '.'))) + rawName = "_" + name; + + // Try to demangle any C++ symbols + string shortName = rawName; + string fullName = rawName; + Ref<Type> typeRef = symbolTypeRef; + if (m_arch && IsGNU3MangledString(rawName)) + { + QualifiedName varName; + Ref<Type> demangledType; + if (DemangleGNU3(m_arch, rawName, demangledType, varName, m_simplifyTemplates)) + { + shortName = varName.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()); + } + } + + if (!typeRef && (size > 0 && size <= 8)) + { + typeRef = Type::IntegerType(size, false); + } + + return std::pair<Ref<Symbol>, Ref<Type>>( + new Symbol(type, shortName, fullName, rawName, addr, binding, nameSpace), typeRef); + }; + + if (m_symbolQueue) + { + m_symbolQueue->Append(process, [this](Symbol* symbol, Type* type) { + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), symbol, type); + }); + } + else + { + auto result = process(); + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), result.first, result.second); + } +} + + +void ElfView::ApplyTypesToParentStringTable(const Elf64SectionHeader& section, const bool offset) +{ + m_logger->LogInfo("Found string table of size %p at offset %p", section.size, section.offset); + DataBuffer buffer = GetParentView()->ReadBuffer(section.offset, section.size); + if (buffer.GetLength() != section.size) + return; + unordered_map<size_t, Ref<Type>> cachedTypes; + for (size_t start_address = (offset ? 1 : 0); start_address < section.size; ++start_address) + { + size_t len; + char ch = 0; + for (len = 0; len < BN_MAX_STRING_LENGTH * 3 && start_address + len < section.size; ++len) + { + ch = buffer[start_address + len]; + if (ch == 0) + break; + } + + if (len != 0 && ch == 0) + { + auto i = cachedTypes.find(len); + Ref<Type> type; + if (i == cachedTypes.end()) + { + type = Type::ArrayType(Type::IntegerType(1, true), len + 1); + cachedTypes[len] = type; + } + else + { + type = i->second; + } + + GetParentView()->DefineDataVariable(section.offset + start_address, type); + } + + start_address += len; + } +} + + +void ElfView::ApplyTypesToStringTable(const Elf64SectionHeader& section, const int64_t imageBaseAdjustment, const bool offset) +{ + m_logger->LogInfo("Found string table of size %p at address %p", section.size, section.address); + DataVariable existing_var; + unordered_map<uint64_t, Ref<Type>> cachedTypes; + for (size_t start_address = section.offset + (offset ? 1 : 0); start_address < section.offset + section.size;) + { + if (!GetParentView()->GetDataVariableAtAddress(start_address, existing_var) || (existing_var.address != start_address)) + return; + + const uint64_t len = existing_var.type->GetElementCount(); + + auto i = cachedTypes.find(len); + Ref<Type> type; + if (i == cachedTypes.end()) + { + type = Type::ArrayType(Type::IntegerType(1, true), len); + cachedTypes[len] = type; + } + else + { + type = i->second; + } + DefineDataVariable(start_address - section.offset + section.address + imageBaseAdjustment, type); + + start_address += len; + } +} + + +string ElfView::ReadStringTable(BinaryReader& reader, const Elf64SectionHeader& section, uint64_t offset) +{ + if (offset == 0 || offset > section.size) + return ""; + + auto itr = m_stringTableCache.find(section.offset); + if (itr == m_stringTableCache.end()) + { + if (section.size > GetParentView()->GetLength()) + { + m_logger->LogError("Unable to read string table with section offset: 0x%" PRIx64 " size: 0x%" PRIx64, section.offset, section.size); + return ""; + } + + std::vector<char>& tableCache = m_stringTableCache[section.offset]; + tableCache.resize(section.size); + reader.Seek(section.offset); + reader.Read(tableCache.data(), section.size); + itr = m_stringTableCache.find(section.offset); + } + + const std::vector<char>& tableCache = itr->second; + return std::string(&tableCache[offset], strlen(tableCache.data() + offset)); +} + + +// http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#FUNC-DES +bool ElfView::DerefPpc64Descriptor(BinaryReader& reader, uint64_t addr, uint64_t& result) +{ + /* must be 64-bit ELF, arch PPC64 */ + if (m_elf32 || m_commonHeader.arch != EM_PPC64) + return false; + + /* .opd section must exist */ + if (!m_sectionOpd.size) + return false; + + /* addr must be within .opd section */ + if (addr < m_sectionOpd.address || addr >= m_sectionOpd.address + m_sectionOpd.size) + return false; + + /* dereference descriptor to get function entry */ + uint64_t saved = reader.GetOffset(); + reader.Seek(m_sectionOpd.offset + (addr - m_sectionOpd.address)); + bool read_success = reader.TryRead64(result);; + reader.Seek(saved); + return read_success; +} + + +vector<ElfSymbolTableEntry> ElfView::ParseSymbolTable(BinaryReader& reader, const Elf64SectionHeader& symbolSection, + const Elf64SectionHeader& stringSection, bool dynamic, size_t startEntry) +{ + size_t size = (size_t)symbolSection.size / (m_elf32 ? 16 : 24); + vector<ElfSymbolTableEntry> result; + for (size_t i = startEntry; i < size; i++) + { + ElfSymbolTableEntry entry; + if (!ParseSymbolTableEntry(reader, entry, i, symbolSection, stringSection, dynamic)) + break; + + /* TODO: PPC64 specific symbol handling to be moved to architecture extension for ELF */ + if (m_commonHeader.arch == EM_PPC64 && entry.type == ELF_STT_FUNC) + { + uint64_t func_start; + if (DerefPpc64Descriptor(reader, entry.value, func_start)) + { + if (entry.name[0] != '.') + { + /* new symbol with function entry as address */ + ElfSymbolTableEntry entry2 = entry; + entry2.name = "." + entry2.name; + entry2.value = func_start; + result.push_back(entry2); + + m_logger->LogDebug("PPC64 symbol %s=%016x to %s=%016x\n", entry.name.c_str(), entry.value, + entry2.name.c_str(), entry2.value); + + /* force the descriptor to a data symbol */ + entry.type = ELF_STT_OBJECT; + } + } + } + + result.push_back(entry); + } + + return result; +} + + +uint64_t ElfView::PerformGetEntryPoint() const +{ + return m_entryPoint; +} + + +BNEndianness ElfView::PerformGetDefaultEndianness() const +{ + return m_endian; +} + + +bool ElfView::PerformIsRelocatable() const +{ + return m_relocatable; +} + + +size_t ElfView::PerformGetAddressSize() const +{ + return m_addressSize; +} + + +ElfViewType::ElfViewType(): BinaryViewType("ELF", "ELF") +{ + m_logger = LogRegistry::CreateLogger("BinaryView.ElfViewType"); +} + + +Ref<BinaryView> ElfViewType::Create(BinaryView* data) +{ + try + { + return new ElfView(data); + } + catch (std::exception& e) + { + m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + + +Ref<BinaryView> ElfViewType::Parse(BinaryView* data) +{ + try + { + return new ElfView(data, true); + } + catch (std::exception& e) + { + m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + + +bool ElfViewType::IsTypeValidForData(BinaryView* data) +{ + DataBuffer sig = data->ReadBuffer(0, 4); + if (sig.GetLength() != 4) + return false; + if (memcmp(sig.GetData(), "\x7f" "ELF", 4) == 0) + return true; + + // Cyber Grand Challenge DECREE executables can be parsed as ELF + if (memcmp(sig.GetData(), "\x7f" "CGC", 4) == 0) + return true; + + return false; +} + + +uint64_t ElfViewType::ParseHeaders(BinaryView* data, ElfIdent& ident, ElfCommonHeader& commonHeader, Elf64Header& header, Ref<Architecture>* arch, Ref<Platform>* plat, string& errorMsg, BNEndianness& endianness) +{ + if (!IsTypeValidForData(data)) + { + errorMsg = "invalid signature"; + return 0; + } + + // parse ElfIdent + if (data->Read(&ident, 0, sizeof(ident)) != sizeof(ident)) + { + errorMsg = "unable to read header"; + return 0; + } + + BinaryReader reader(data); + if (ident.encoding <= 1) + endianness = LittleEndian; + else if (ident.encoding == 2) + endianness = BigEndian; + else + { + errorMsg = "invalid encoding"; + return 0; + } + + // parse ElfCommonHeader + reader.SetEndianness(endianness); + reader.Seek(sizeof(ident)); + commonHeader.type = reader.Read16(); + commonHeader.arch = reader.Read16(); + commonHeader.version = reader.Read32(); + + // Promote the file class to 64-bit + // TODO potentially add a setting to allow the user to override header interpretation + if ((commonHeader.type == ET_EXEC) && (commonHeader.arch == EM_X86_64) && (ident.fileClass == 1)) + { + ident.fileClass = 2; + m_logger->LogWarn( + "Executable file claims to be 32-bit but specifies a 64-bit architecture. It is likely malformed or " + "malicious. Treating it as 64-bit."); + } + + // parse Elf64Header + if (ident.fileClass == 1) // 32-bit ELF + { + header.entry = reader.Read32(); + header.programHeaderOffset = reader.Read32(); + header.sectionHeaderOffset = reader.Read32(); + header.flags = reader.Read32(); + header.headerSize = reader.Read16(); + header.programHeaderSize = reader.Read16(); + header.programHeaderCount = reader.Read16(); + header.sectionHeaderSize = reader.Read16(); + header.sectionHeaderCount = reader.Read16(); + header.stringTable = reader.Read16(); + } + else if (ident.fileClass == 2) // 64-bit ELF + { + header.entry = reader.Read64(); + header.programHeaderOffset = reader.Read64(); + header.sectionHeaderOffset = reader.Read64(); + header.flags = reader.Read32(); + header.headerSize = reader.Read16(); + header.programHeaderSize = reader.Read16(); + header.programHeaderCount = reader.Read16(); + header.sectionHeaderSize = reader.Read16(); + header.sectionHeaderCount = reader.Read16(); + header.stringTable = reader.Read16(); + } + else + { + errorMsg = "invalid file class"; + return 0; + } + + map<string, Ref<Metadata>> metadataMap = { + {"EI_CLASS", new Metadata((uint64_t) ident.fileClass)}, + {"EI_DATA", new Metadata((uint64_t) ident.encoding)}, + {"EI_OSABI", new Metadata((uint64_t) ident.os)}, + {"e_type", new Metadata((uint64_t) commonHeader.type)}, + {"e_machine", new Metadata((uint64_t) commonHeader.arch)}, + {"e_flags", new Metadata((uint64_t) header.flags)}, + }; + + Ref<Metadata> metadata = new Metadata(metadataMap); + // retrieve architecture + // FIXME: Architecture registration methods should perhaps be virtual and take the raw data, or some additional opaque information. + + endianness = header.flags & EF_ARM_BE8 ? BigEndian : endianness; + + /* for architectures where .e_machine field doesn't disambiguate between 32/64 (like MIPS), + form the conventional alternative id, including the .e_ident[EI_CLASS] field */ + uint32_t altArchId = (ident.fileClass << 16) | commonHeader.arch; + + Ref<Platform> recognizedPlatform = g_elfViewType->RecognizePlatform(commonHeader.arch, endianness, data, metadata); + + if (!recognizedPlatform) + { + /* second try with the alternative architecture identifier */ + recognizedPlatform = g_elfViewType->RecognizePlatform(altArchId, endianness, data, metadata); + } + + if (recognizedPlatform) + { + if (plat) + *plat = recognizedPlatform; + if (arch) + *arch = recognizedPlatform->GetArchitecture(); + } + else + { + BNEndianness codeEndianness = endianness; + if ((commonHeader.arch == EM_ARM) && (header.flags & EF_ARM_BE8)) + codeEndianness = LittleEndian; + + if (arch) + { + *arch = g_elfViewType->GetArchitecture(commonHeader.arch, codeEndianness); + + if (!*arch) + { + /* second try with the alternative architecture identifier */ + *arch = g_elfViewType->GetArchitecture(altArchId, codeEndianness); + } + } + } + + return reader.GetOffset(); +} + + +Ref<Settings> ElfViewType::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); + } + + + return settings; +} + + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + +#ifdef DEMO_VERSION + bool ElfPluginInit() +#else + BINARYNINJAPLUGIN bool CorePluginInit() +#endif + { + InitElfViewType(); + return true; + } +} diff --git a/view/elf/elfview.h b/view/elf/elfview.h new file mode 100644 index 00000000..7df78d01 --- /dev/null +++ b/view/elf/elfview.h @@ -0,0 +1,558 @@ +#pragma once + +#include "binaryninjaapi.h" +#include <exception> + +#define ELF_PT_NULL 0 +#define ELF_PT_LOAD 1 +#define ELF_PT_DYNAMIC 2 +#define ELF_PT_INTERP 3 +#define ELF_PT_NOTE 4 +#define ELF_PT_SHLIB 5 +#define ELF_PT_PHDR 6 +#define ELF_PT_TLS 7 /* Thread-local storage segment */ +#define ELF_PT_NUM 8 /* Number of defined types */ +#define ELF_PT_LOOS 0x60000000 /* Start of OS-specific */ +#define ELF_PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define ELF_PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define ELF_PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ + +#define ELF_PT_LOSUNW 0x6ffffffa +#define ELF_PT_SUNWBSS 0x6ffffffb +#define ELF_PT_SUNWSTACK 0x6ffffffa +#define ELF_PT_HISUNW 0x6fffffff +#define ELF_PT_LOPROC 0x70000000 +#define ELF_PT_HIPROC 0x7fffffff + +#define ELF_PT_MIPS_REGINFO 0x70000000 /* Register usage information */ +#define ELF_PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table */ +#define ELF_PT_MIPS_OPTIONS 0x70000002 +#define ELF_PT_MIPS_ABIFLAGS 0x70000003 /* FP mode requirement */ + +// x86-64 program header types. +// These all contain stack unwind tables. +#define ELF_PT_GNU_EH_FRAME 0x6474e550 +#define ELF_PT_SUNW_EH_FRAME 0x6474e550 +#define ELF_PT_SUNW_UNWIND 0x6464e550 +#define ELF_PT_GNU_STACK 0x6474e551 // Indicates stack executability. +#define ELF_PT_GNU_RELRO 0x6474e552 // Read-only after relocation. +#define ELF_PT_OPENBSD_RANDOMIZE 0x65a3dbe6 // Fill with random data. +#define ELF_PT_OPENBSD_WXNEEDED 0x65a3dbe7 // Program does W^X violations. +#define ELF_PT_OPENBSD_BOOTDATA 0x65a41be6 // Section for boot arguments. + // ARM program header types. +#define PT_ARM_ARCHEXT 0x70000000 // Platform architecture compatibility info + // These all contain stack unwind tables. +#define PT_ARM_EXIDX 0x70000001 +#define PT_ARM_UNWIND 0x70000001 + + // MIPS program header types. +#define PT_MIPS_REGINFO 0x70000000 // Register usage information. +#define PT_MIPS_RTPROC 0x70000001 // Runtime procedure table. +#define PT_MIPS_OPTIONS 0x70000002 // Options segment. +#define PT_MIPS_ABIFLAGS 0x70000003 // Abiflags segment. + + // WebAssembly program header types. + #define PT_WEBASSEMBLY_FUNCTIONS ELF_PT_LOPROC + 0 // Function definitions. + +#define ELF_DT_NULL 0 +#define ELF_DT_NEEDED 1 +#define ELF_DT_PLTRELSZ 2 +#define ELF_DT_PLTGOT 3 +#define ELF_DT_HASH 4 +#define ELF_DT_STRTAB 5 +#define ELF_DT_SYMTAB 6 +#define ELF_DT_RELA 7 +#define ELF_DT_RELASZ 8 +#define ELF_DT_RELAENT 9 +#define ELF_DT_STRSZ 10 +#define ELF_DT_DYNSYM 11 +#define ELF_DT_SYMENT 11 +#define ELF_DT_INIT 12 +#define ELF_DT_FINI 13 +#define ELF_DT_SONAME 14 +#define ELF_DT_RPATH 15 +#define ELF_DT_SYMBOLIC 16 +#define ELF_DT_REL 17 +#define ELF_DT_RELSZ 18 +#define ELF_DT_RELENT 19 +#define ELF_DT_PLTREL 20 +#define ELF_DT_DEBUG 21 +#define ELF_DT_TEXTREL 22 +#define ELF_DT_JMPREL 23 +#define ELF_DT_BIND_NOW 24 +#define ELF_DT_INIT_ARRAY 25 +#define ELF_DT_FINI_ARRAY 26 +#define ELF_DT_INIT_ARRAYSZ 27 +#define ELF_DT_FINI_ARRAYSZ 28 +#define ELF_DT_RUNPATH 29 +#define ELF_DT_FLAGS 30 +#define ELF_DT_ENCODING 31 +#define ELF_DT_PREINIT_ARRAY 32 +#define ELF_DT_PREINIT_ARRAYSZ 33 + +#define ELF_DT_LOOS 0x6000000D +#define ELF_DT_SUNW_RTLDINF 0x6000000E +#define ELF_DT_HIOS 0x6FFFF000 +#define ELF_DT_VALRNGLO 0x6FFFFD00 +#define ELF_DT_CHECKSUM 0x6FFFFDF8 +#define ELF_DT_PLTPADSZ 0x6FFFFDF9 +#define ELF_DT_MOVEEN 0x6FFFFDFA +#define ELF_DT_MOVES 0x6FFFFDFB +#define ELF_DT_FEATURE_1 0x6FFFFDFC +#define ELF_DT_POSFLAG_1 0x6FFFFDFD +#define ELF_DT_SYMINSZ 0x6FFFFDFE +#define ELF_DT_SYMINENT 0x6FFFFDFF +#define ELF_DT_VALRNGH 0x6FFFFDFF +#define ELF_DT_ADDRRNGLO 0x6FFFFE00 +#define ELF_DT_GNU_HASH 0x6FFFFEF5 +#define ELF_DT_CONFIG 0x6FFFFEFA +#define ELF_DT_DEPAUDIT 0x6FFFFEFB +#define ELF_DT_AUDIT 0x6FFFFEFC +#define ELF_DT_PLTPAD 0x6FFFFEFD +#define ELF_DT_MOVETAB 0x6FFFFEFE +#define ELF_DT_SYMINFO 0x6FFFFEFF +#define ELF_DT_ADDRRNGHI 0x6FFFFEFF +#define ELF_DT_RELACOUNT 0x6FFFFFF9 +#define ELF_DT_RELCOUNT 0x6FFFFFFA +#define ELF_DT_FLAGS_1 0x6FFFFFFB +#define ELF_DT_VERDEF 0x6FFFFFFC +#define ELF_DT_VERDEFNUM 0x6FFFFFFD +#define ELF_DT_VERNEED 0x6FFFFFFE +#define ELF_DT_VERNEEDNUM 0x6FFFFFFF +#define ELF_DT_VERSYM 0x6FFFFFF0 + +#define ELF_DT_LOPROC 0x70000000 + +#define ELF_DT_SPARC_REGISTER 0x70000001 + +#define ELF_DT_MIPS_RLD_VERSION 0x70000001 +#define ELF_DT_MIPS_TIME_STAMP 0x70000002 +#define ELF_DT_MIPS_ICHECKSUM 0x70000003 +#define ELF_DT_MIPS_IVERSION 0x70000004 +#define ELF_DT_MIPS_FLAGS 0x70000005 +#define ELF_DT_MIPS_BASE_ADDRESS 0x70000006 +#define ELF_DT_MIPS_CONFLICT 0x70000008 +#define ELF_DT_MIPS_LIBLIST 0x70000009 +#define ELF_DT_MIPS_LOCAL_GOTNO 0x7000000A +#define ELF_DT_MIPS_CONFLICTNO 0x7000000B +#define ELF_DT_MIPS_LIBLISTNO 0x70000010 +#define ELF_DT_MIPS_SYMTABNO 0x70000011 +#define ELF_DT_MIPS_UNREFEXTNO 0x70000012 +#define ELF_DT_MIPS_GOTSYM 0x70000013 +#define ELF_DT_MIPS_HIPAGENO 0x70000014 +#define ELF_DT_MIPS_RLD_MAP 0x70000016 +#define ELF_DT_MIPS_RLD_MAP_REL 0x70000035 + +#define ELF_DT_AUXILIARY 0x7FFFFFFD +#define ELF_DT_USED 0x7FFFFFFE +#define ELF_DT_FILTER 0x7FFFFFFF +#define ELF_DT_HIPROC 0x7FFFFFFF + +#define ELF_STT_NOTYPE 0 +#define ELF_STT_OBJECT 1 +#define ELF_STT_FUNC 2 +#define ELF_STT_SECTION 3 +#define ELF_STT_FILE 4 +#define ELF_STT_COMMON 5 +#define ELF_STT_TLS 6 +// GNU/Linux specific +#define ELF_STT_GNU_IFUNC 10 + +#define ELF_STB_LOCAL 0 +#define ELF_STB_GLOBAL 1 +#define ELF_STB_WEAK 2 +#define ELF_STB_LOPROC 13 +#define ELF_STB_HIPROC 15 + +#define ELF_ST_TYPE(t) ((t) & 0xf) +#define ELF_ST_BIND(b) ((b) >> 4) + +#define ELF_RELOC_COPY 5 +#define ELF_RELOC_GLOBAL 6 +#define ELF_RELOC_JUMP_SLOT 7 + +#define ET_NONE 0 // No file type +#define ET_REL 1 // Relocatable file +#define ET_EXEC 2 // Executable file +#define ET_DYN 3 // Shared object file +#define ET_CORE 4 // Core file +#define ET_NUM 5 // Number of defined types + +#define EM_NONE 0 // No machine +#define EM_M32 1 // AT&T WE 32100 +#define EM_SPARC 2 // SUN SPARC +#define EM_386 3 // Intel 80386 +#define EM_68K 4 // Motorola m68k family +#define EM_88K 5 // Motorola m88k family +#define EM_860 7 // Intel 80860 +#define EM_MIPS 8 // MIPS R3000 big-endian +#define EM_S370 9 // IBM System/370 +#define EM_MIPS_RS3_LE 10 // MIPS R3000 little-endian +#define EM_PARISC 15 // HPPA +#define EM_VPP500 17 // Fujitsu VPP500 +#define EM_SPARC32PLUS 18 // Sun's "v8plus" +#define EM_960 19 // Intel 80960 +#define EM_PPC 20 // PowerPC +#define EM_PPC64 21 // PowerPC 64-bit +#define EM_S390 22 // IBM S390 +#define EM_V800 36 // NEC V800 series +#define EM_FR20 37 // Fujitsu FR20 +#define EM_RH32 38 // TRW RH-32 +#define EM_RCE 39 // Motorola RCE +#define EM_ARM 40 // ARM +#define EM_FAKE_ALPHA 41 // Digital Alpha +#define EM_SH 42 // Hitachi SH +#define EM_SPARCV9 43 // SPARC v9 64-bit +#define EM_TRICORE 44 // Siemens Tricore +#define EM_ARC 45 // Argonaut RISC Core +#define EM_H8_300 46 // Hitachi H8/300 +#define EM_H8_300H 47 // Hitachi H8/300H +#define EM_H8S 48 // Hitachi H8S +#define EM_H8_500 49 // Hitachi H8/500 +#define EM_IA_64 50 // Intel Merced +#define EM_MIPS_X 51 // Stanford MIPS-X +#define EM_COLDFIRE 52 // Motorola Coldfire +#define EM_68HC12 53 // Motorola M68HC12 +#define EM_MMA 54 // Fujitsu MMA Multimedia Accelerator +#define EM_PCP 55 // Siemens PCP +#define EM_NCPU 56 // Sony nCPU embeeded RISC +#define EM_NDR1 57 // Denso NDR1 microprocessor +#define EM_STARCORE 58 // Motorola Start*Core processor +#define EM_ME16 59 // Toyota ME16 processor +#define EM_ST100 60 // STMicroelectronic ST100 processor +#define EM_TINYJ 61 // Advanced Logic Corp. Tinyj emb.fam +#define EM_X86_64 62 // AMD x86-64 architecture +#define EM_PDSP 63 // Sony DSP Processor +#define EM_FX66 66 // Siemens FX66 microcontroller +#define EM_ST9PLUS 67 // STMicroelectronics ST9+ 8/16 mc +#define EM_ST7 68 // STmicroelectronics ST7 8 bit mc +#define EM_68HC16 69 // Motorola MC68HC16 microcontroller +#define EM_68HC11 70 // Motorola MC68HC11 microcontroller +#define EM_68HC08 71 // Motorola MC68HC08 microcontroller +#define EM_68HC05 72 // Motorola MC68HC05 microcontroller +#define EM_SVX 73 // Silicon Graphics SVx +#define EM_ST19 74 // STMicroelectronics ST19 8 bit mc +#define EM_VAX 75 // Digital VAX +#define EM_CRIS 76 // Axis Communications 32-bit embedded processor +#define EM_JAVELIN 77 // Infineon Technologies 32-bit embedded processor +#define EM_FIREPATH 78 // Element 14 64-bit DSP Processor +#define EM_ZSP 79 // LSI Logic 16-bit DSP Processor +#define EM_MMIX 80 // Donald Knuth's educational 64-bit processor +#define EM_HUANY 81 // Harvard University machine-independent object files +#define EM_PRISM 82 // SiTera Prism +#define EM_AVR 83 // Atmel AVR 8-bit microcontroller +#define EM_FR30 84 // Fujitsu FR30 +#define EM_D10V 85 // Mitsubishi D10V +#define EM_D30V 86 // Mitsubishi D30V +#define EM_V850 87 // NEC v850 +#define EM_M32R 88 // Mitsubishi M32R +#define EM_MN10300 89 // Matsushita MN10300 +#define EM_MN10200 90 // Matsushita MN10200 +#define EM_PJ 91 // picoJava +#define EM_OPENRISC 92 // OpenRISC 32-bit embedded processor +#define EM_ARC_A5 93 // ARC Cores Tangent-A5 +#define EM_XTENSA 94 // Tensilica Xtensa Architecture +#define EM_ALTERA_NIOS2 113 // Altera Nios II +#define EM_AARCH64 183 // ARM AARCH64 +#define EM_TILEPRO 188 // Tilera TILEPro +#define EM_MICROBLAZE 189 // Xilinx MicroBlaze +#define EM_TILEGX 191 // Tilera TILE-Gx +#define EM_NUM 192 + +// Section Header Types +#define ELF_SHT_NULL 0 +#define ELF_SHT_PROGBITS 1 +#define ELF_SHT_SYMTAB 2 +#define ELF_SHT_STRTAB 3 +#define ELF_SHT_RELA 4 +#define ELF_SHT_HASH 5 +#define ELF_SHT_DYNAMIC 6 +#define ELF_SHT_NOTE 7 +#define ELF_SHT_NOBITS 8 +#define ELF_SHT_REL 9 +#define ELF_SHT_SHLIB 10 +#define ELF_SHT_DYNSYM 11 +#define ELF_SHT_LOPROC 0x70000000 // (take note that this only shows if none of the below are) +#define ELF_SHT_HIPROC 0x7fffffff // (take note that this only shows if none of the below are) +#define ELF_SHT_LOUSER 0x80000000 +#define ELF_SHT_HIUSER 0xffffffff + +#define ELF_SHT_EXT 0x70000000 // PA-RISC and Intel Itanium Only - Section contains product-specific extension bits +#define ELF_SHT_UNWIND 0x70000001 // PA-RISC, Intel Itanium, and AMD64 Only - Section contains unwind function table entries for stack unwinding +#define ELF_SHT_PARISC_DOC 0x70000002 // PA-RISC Only - Section contains debug information for optimized code +#define ELF_SHT_PARISC_ANNOT 0x70000003 // PA-RISC Only - Section contains code annotations Name Value Meaning +#define ELF_SHT_IA_64_LOPSREG 0x78000000 // Intel Itanium Only - Low section of operating system identification bits (and implimentation-specific types) +#define ELF_SHT_IA_64_HIPSREG 0x7fffffff // Intel Itanium Only - High section of operating system identification bits (and implimentation-specific types) +#define ELF_SHT_IA_64_PRIORITY_INIT 0x79000000 // Intel Itanium Only - Section contains priority initialization records + +// Section Header Flags +#define ELF_SHF_WRITE 0x1 // Section is writable +#define ELF_SHF_ALLOC 0x2 // Section occupies memory during execution (is a mapped segment) +#define ELF_SHF_EXECINSTR 0x4 // Section is executable +#define ELF_SHF_MASKOS 0x0ff00000 // OS-specific semantics +#define ELF_SHF_MASKPROC 0xf0000000 // Processor specific (take note that this only shows if none of the below are) + +// LLVM and GoLang Specific +#define ELF_SHF_MERGE 0x10 // Section may be merged +#define ELF_SHF_STRINGS 0x20 // Section contains strings +#define ELF_SHF_INFO_LINK 0x40 // sh_info holds section index +#define ELF_SHF_LINK_ORDER 0x80 // Special ordering requirements +#define ELF_SHF_OS_NONCONFORMING 0x100 // OS-specific processing required +#define ELF_SHF_GROUP 0x200 // Member of section group +#define ELF_SHF_TLS 0x400 // Section contains TLS data +#define ELF_SHF_COMPRESSED 0x800 // Section is compressed + +// Arch Specific +#define ELF_SHF_ENTRYSECT 0x10000000 // ARM only - Section contains an entrypoint +#define ELF_SHF_COMDEF 0x80000000 // ARM only - Section may be multiply defined in the input to a link step +#define ELF_SHF_AMD64_LARGE 0x10000000 // AMD64 Only - Section can be >2GB +#define ELF_SHF_IA_64_SHORT 0x10000000 // Intel Itanium Only - Section contains objects referenced by gp, so must be position near gp +#define ELF_SHF_IA_64_NORECOV 0x20000000 // Intel Itanium Only - Section contains speculative instructions without recovery code +#define ELF_SHF_MIPS_GPREL 0x10000000 // MIPS Only - Section contains global data +#define ELF_SHF_PARISC_SHORT 0x20000000 // PA-RISC Only - Section should be near gp +#define ELF_SHF_PARISC_HUGE 0x40000000 // PA-RISC Only - Section should be allocated far from gp +#define ELF_SHF_PARISC_SBP 0x80000000 // PA-RISC Only - Section contains code compiled for static branch prediction +#define ELF_SHF_EXCLUDE 0x80000000 // PPC Only - Section is excluded from executable and SO's when they're not to be further relocated + +#define EV_NONE 0 // Invalid ELF version +#define EV_CURRENT 1 // Current version +#define EV_NUM 2 + +#define EF_ARM_BE8 0x00800000 // ELF contains BE-8 code for ARMv6 processor + +#define PF_X (1 << 0) // Segment is executable +#define PF_W (1 << 1) // Segment is writable +#define PF_R (1 << 2) // Segment is readable + +#define ELF_SHN_UNDEF 0 +#define ELF_SHN_LORESERVE 0xff00 +#define ELF_SHN_LOPROC 0xff00 +#define ELF_SHN_HIPROC 0xff1f +#define ELF_SHN_ABS 0xfff1 +#define ELF_SHN_COMMON 0xfff2 +#define ELF_SHN_HIRESERVE 0xffff + +// TODO : Add these (MIPs Only): +// #define ELF_SHN_MIPS_ACOMMON 0xff00 +// #define ELF_SHN_MIPS_TEXT 0xff01 +// #define ELF_SHN_MIPS_DATA 0xff02 +// #define ELF_SHN_MIPS_SCOMMON 0xff03 +// #define ELF_SHN_MIPS_SUNDEFINED 0xff04 + +// ARM ONLY +#define R_ARM_TLS_DTPMOD32 0x11 +#define R_ARM_TLS_DTPOFF32 0x12 + +namespace BinaryNinja +{ + class ElfFormatException: public std::exception + { + std::string m_message; + + public: + ElfFormatException(const std::string& msg = "invalid format"): m_message(msg) {} + virtual const char* what() const NOEXCEPT { return m_message.c_str(); } + }; + + struct ElfIdent + { + char signature[4]; + uint8_t fileClass; + uint8_t encoding; + uint8_t version; + uint8_t os; + uint8_t abiVersion; + uint8_t pad[7]; + }; + + struct ElfCommonHeader + { + uint16_t type; + uint16_t arch; + uint32_t version; + }; + + struct Elf32Header + { + uint32_t entry; + uint32_t programHeaderOffset; + uint32_t sectionHeaderOffset; + uint32_t flags; + uint16_t headerSize; + uint16_t programHeaderSize; + uint16_t programHeaderCount; + uint16_t sectionHeaderSize; + uint16_t sectionHeaderCount; + uint16_t stringTable; + }; + + struct Elf32ProgramHeader + { + uint32_t type; + uint32_t offset; + uint32_t virtualAddress; + uint32_t physicalAddress; + uint32_t fileSize; + uint32_t memorySize; + uint32_t flags; + uint32_t align; + }; + + struct Elf32SectionHeader + { + uint32_t name; + uint32_t type; + uint32_t flags; + uint32_t address; + uint32_t offset; + uint32_t size; + uint32_t link; + uint32_t info; + uint32_t align; + uint32_t entrySize; + }; + + struct Elf64Header + { + uint64_t entry; + uint64_t programHeaderOffset; + uint64_t sectionHeaderOffset; + uint32_t flags; + uint16_t headerSize; + uint16_t programHeaderSize; + uint16_t programHeaderCount; + uint16_t sectionHeaderSize; + uint16_t sectionHeaderCount; + uint16_t stringTable; + }; + + struct Elf64ProgramHeader + { + uint32_t type; + uint32_t flags; + uint64_t offset; + uint64_t virtualAddress; + uint64_t physicalAddress; + uint64_t fileSize; + uint64_t memorySize; + uint64_t align; + }; + + struct Elf64SectionHeader + { + uint32_t name; + uint32_t type; + uint64_t flags; + uint64_t address; + uint64_t offset; + uint64_t size; + uint32_t link; + uint32_t info; + uint64_t align; + uint64_t entrySize; + }; + + struct ElfSymbolTableEntry + { + uint32_t nameOffset; + uint8_t type; + BNSymbolBinding binding; + uint8_t other; + uint16_t section; + uint64_t value; + uint64_t size; + std::string name; + bool dynamic; + }; + + struct ELFRelocEntry + { + uint64_t offset, sym, relocType, addend; + size_t sectionIdx; + bool implicit; + ELFRelocEntry(uint64_t o, uint64_t s, uint64_t r, uint64_t a, size_t idx, bool imp): offset(o), sym(s), relocType(r), + addend(a), sectionIdx(idx), implicit(imp) {} + }; + + class ElfView: public BinaryView + { + bool m_parseOnly, m_backedByDatabase; + uint64_t m_entryPoint; + uint64_t m_sectionHeaderOffset, m_programHeaderOffset; + size_t m_sectionHeaderCount, m_programHeaderCount; + uint64_t m_fileSize; + BNEndianness m_endian; + size_t m_addressSize; + ElfIdent m_ident; + ElfCommonHeader m_commonHeader; + Ref<Architecture> m_arch; + Ref<Platform> m_plat = nullptr; + uint32_t m_headerFlags; + bool m_elf32; + bool m_objectFile; + Ref<Logger> m_logger; + bool m_extractMangledTypes; + bool m_simplifyTemplates; + bool m_relocatable = false; + std::map<uint64_t, std::vector<char>> m_stringTableCache; + + // Section and program headers, internally use 64-bit form as it is a superset of 32-bit + std::vector<Elf64SectionHeader> m_elfSections; + std::vector<Elf64ProgramHeader> m_programHeaders; + Elf64SectionHeader m_symbolTableSection, m_dynamicSymbolTableSection; + Elf64SectionHeader m_auxSymbolTable, m_dynamicStringTable, m_sectionStringTable, m_sectionOpd; + Elf64ProgramHeader m_dynamicTable; + Elf64ProgramHeader m_tlsSegment; + std::map<uint64_t, uint64_t> m_localGotEntries; + std::set<uint64_t> m_gotEntryLocations; + std::vector<BNRelocationInfo> m_relocationInfo; + + size_t m_auxSymbolTableEntrySize = 0; + size_t m_numDynamicTableEntries = 0; + uint64_t m_hashHeader = 0; + uint64_t m_gnuHashHeader = 0; + + SymbolQueue* m_symbolQueue = nullptr; + + void DefineElfSymbol(BNSymbolType type, const std::string& name, uint64_t addr, bool gotEntry, + BNSymbolBinding binding, size_t size, Ref<Type> typeObj=nullptr); + + void ApplyTypesToParentStringTable(const Elf64SectionHeader& section, const bool offset = true); + void ApplyTypesToStringTable(const Elf64SectionHeader& section, const int64_t imageBaseAdjustment, const bool offset = true); + std::string ReadStringTable(BinaryReader& view, const Elf64SectionHeader& section, uint64_t offset); + bool ParseSymbolTableEntry(BinaryReader& reader, ElfSymbolTableEntry& entry, uint64_t sym, + const Elf64SectionHeader& symbolTable, const Elf64SectionHeader& stringTable, bool dynamic); + + std::vector<ElfSymbolTableEntry> ParseSymbolTable(BinaryReader& reader, const Elf64SectionHeader& symbolTableSection, + const Elf64SectionHeader& section, bool dynamic, size_t startEntry=0); + + virtual uint64_t PerformGetEntryPoint() const override; + + virtual bool PerformIsExecutable() const override { return true; } + virtual BNEndianness PerformGetDefaultEndianness() const override; + virtual bool PerformIsRelocatable() const override; + virtual size_t PerformGetAddressSize() const override; + void GetRelocEntries(BinaryReader& reader, const std::vector<Elf64SectionHeader>& sections, + bool implicit, std::vector<ELFRelocEntry>& result); + bool DerefPpc64Descriptor(BinaryReader& reader, uint64_t addr, uint64_t& result); + public: + ElfView(BinaryView* data, bool parseOnly = false); + ~ElfView(); + + virtual bool Init() override; + }; + + class ElfViewType: public BinaryViewType + { + Ref<Logger> m_logger; + public: + ElfViewType(); + virtual Ref<BinaryView> Create(BinaryView* data) override; + virtual Ref<BinaryView> Parse(BinaryView* data) override; + virtual bool IsTypeValidForData(BinaryView* data) override; + virtual uint64_t ParseHeaders(BinaryView* data, ElfIdent& ident, ElfCommonHeader& commonHeader, Elf64Header& header, Ref<Architecture>* arch, Ref<Platform>* plat, std::string& errorMsg, BNEndianness& endianness); + virtual Ref<Settings> GetLoadSettingsForData(BinaryView* data) override; + }; + + void InitElfViewType(); +} diff --git a/view/macho/.gitignore b/view/macho/.gitignore new file mode 100644 index 00000000..d4c4c4dc --- /dev/null +++ b/view/macho/.gitignore @@ -0,0 +1,8 @@ +CMakeFiles +CMakeCache.txt +cmake_install.cmake +Makefile +api +build +libview_* +view_*.dll diff --git a/view/macho/CMakeLists.txt b/view/macho/CMakeLists.txt new file mode 100644 index 00000000..6f89250a --- /dev/null +++ b/view/macho/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(view_macho) + +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_macho STATIC ${SOURCES}) +else() + add_library(view_macho SHARED ${SOURCES}) +endif() + +target_link_libraries(view_macho binaryninjaapi) + +set_target_properties(view_macho 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_macho) + set_target_properties(view_macho PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} + RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) +else() + bn_install_plugin(view_macho) +endif() diff --git a/view/macho/LICENSE b/view/macho/LICENSE new file mode 100644 index 00000000..265883c5 --- /dev/null +++ b/view/macho/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/macho/README.md b/view/macho/README.md new file mode 100644 index 00000000..b94ca4b3 --- /dev/null +++ b/view/macho/README.md @@ -0,0 +1,30 @@ +# view-macho +This is the Mach-O 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` (or `ninja`, depending on your default generator) in the build directory to compile the plugin. + +The plugin can be found in the root of the build directory as `libview_macho.so`, +`libview_macho.dylib` or `view_macho.dll` depending on your platform. + +To install the plugin, first launch Binary Ninja and uncheck the "Mach-O 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, run `make install` (or `ninja install`, depending on the generator used). + +You can also manually 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/macho/fatmachoview.cpp b/view/macho/fatmachoview.cpp new file mode 100644 index 00000000..de9b755b --- /dev/null +++ b/view/macho/fatmachoview.cpp @@ -0,0 +1,266 @@ +#include <stdlib.h> +#include <string.h> +#include <cstdint> +#include <map> +#include "fatmachoview.h" +#include "machoview.h" + +using namespace BinaryNinja; +using namespace std; + + +struct fat_type +{ + string name; + string long_name; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; +}; + + +static vector<fat_type>* g_recognizedFatTypes; + +static const map<cpu_type_t, cpu_subtype_t> g_allSubtypeMap +{ + {MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ALL}, + {MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_ALL}, + {MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_ALL}, + {MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_ALL}, + {MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_ALL}, + {MACHO_CPU_TYPE_POWERPC64, MACHO_CPU_SUBTYPE_POWERPC_ALL} +}; + + +FatMachoViewType::FatMachoViewType(const string& name, const string& long_name, cpu_type_t cputype, cpu_subtype_t cpusubtype) : BinaryViewType(name, long_name), m_cputype(cputype), m_cpusubtype(cpusubtype) +{ + +} + + +static bool FindMostSpecificCpuTypeSubtype(cpu_type_t cputype, cpu_subtype_t cpusubtype, cpu_subtype_t& outType) +{ + // Anything exact is most specific + for (const auto& type : *g_recognizedFatTypes) + { + if (type.cputype == cputype && type.cpusubtype == cpusubtype) + { + outType = type.cpusubtype; + return true; + } + } + // Then, any -all arch that matches + for (const auto& type : *g_recognizedFatTypes) + { + if (type.cputype == cputype && type.cpusubtype == g_allSubtypeMap.at(cputype)) + { + outType = type.cpusubtype; + return true; + } + } + // Else, no match + return false; +} + + +static bool ExtractFatArchForCPU(BinaryView* data, fat_arch_64& arch, cpu_type_t cputype, cpu_subtype_t cpusubtype) +{ + DataBuffer sig = data->ReadBuffer(0, 4); + if (sig.GetLength() != 4) + return false; + + uint32_t magic = ToBE32(*(uint32_t*)sig.GetData()); + if ((magic != FAT_MAGIC) && (magic != FAT_MAGIC_64)) + return false; + + bool fat64 = (magic == FAT_MAGIC_64); + + fat_header header; + BinaryReader reader(data); + + // According to docs, Fat files are always BigEndian + reader.SetEndianness(BigEndian); + header.magic = reader.Read32(); + header.nfat_arch = reader.Read32(); + + // Malformed header, too many archs + size_t expectSize = header.nfat_arch * (fat64 ? 32 /* sizeof(fat_arch_64) */ : 20 /* sizeof(fat_arch) */) + 8 /* sizeof(fat_header) */; + if (expectSize > data->GetLength()) + { + return false; + } + + // Because Mach-O files can "hide" extra archs after the end for weird legacy reasons, + // we shouldn't assume the header has the right number of archs + while (true) + { + // Just use a fat_arch_64 struct since we read manually and it can hold both sizes + fat_arch_64 iarch; + try + { + if (fat64) + { + iarch.cputype = reader.Read32(); + iarch.cpusubtype = reader.Read32(); + iarch.offset = reader.Read64(); + iarch.size = reader.Read64(); + iarch.align = reader.Read32(); + iarch.reserved = reader.Read32(); + } + else + { + iarch.cputype = reader.Read32(); + iarch.cpusubtype = reader.Read32(); + iarch.offset = reader.Read32(); + iarch.size = reader.Read32(); + iarch.align = reader.Read32(); + } + } + catch (ReadException &) + { + return false; + } + + if (iarch.cputype == 0) + { + // Probably the end + break; + } + + if (iarch.offset + iarch.size > data->GetLength()) + { + // Malformed + break; + } + + if (iarch.cputype != cputype) + { + continue; + } + + // Make sure the passed subtype is the most specific, otherwise two different views + // will try to display the same slice + cpu_subtype_t mostSpecific; + if (!FindMostSpecificCpuTypeSubtype(iarch.cputype, iarch.cpusubtype, mostSpecific)) + { + continue; + } + if (cpusubtype == mostSpecific) + { + arch = iarch; + return true; + } + } + + // Don't have this specific subtype + return false; +} + + +Ref<BinaryViewType> FatViewTypeForData(Ref<BinaryData> data) +{ + vector<Ref<BinaryViewType>> types = BinaryViewType::GetViewTypesForData(data); + for (Ref<BinaryViewType> type : types) + { + // Ignore raw binary data views + if (type->GetName() == "Raw") + { + continue; + } + + // Make sure that the slice is still itself a valid mach-o + if (!type->IsTypeValidForData(data)) + { + continue; + } + + return type; + } + return nullptr; +} + + +Ref<BinaryView> FatMachoViewType::Create(BinaryView* data) +{ + fat_arch_64 arch; + if (!ExtractFatArchForCPU(data, arch, m_cputype, m_cpusubtype)) + return nullptr; + + DataBuffer buffer = data->ReadBuffer(arch.offset, arch.size); + Ref<BinaryData> newData = new BinaryData(data->GetFile(), std::move(buffer)); + + try + { + // Update the view to this specific arch so it doesn't conflict when saving/loading + return new MachoView(GetName(), newData); + } + catch (std::exception&) + { + return nullptr; + } +} + + +bool FatMachoViewType::IsTypeValidForData(BinaryView* data) +{ + // Enable this BinaryViewType for existing databases only + if (!data->GetFile()->IsBackedByDatabase()) + return false; + + fat_arch_64 arch; + if (!ExtractFatArchForCPU(data, arch, m_cputype, m_cpusubtype)) + return false; + + // MachoView::IsMacho only considers the magic header, so we can load only that + DataBuffer buffer = data->ReadBuffer(arch.offset, 4); + Ref<BinaryData> newData = new BinaryData(data->GetFile(), std::move(buffer)); + Ref<BinaryViewType> attemptedViewType = FatViewTypeForData(newData); + + return attemptedViewType; +} + + +void BinaryNinja::InitFatMachoViewType() +{ + // Need a pointer because this is called from a constructor before g_recognizedFatTypes is loaded. + g_recognizedFatTypes = new vector<fat_type> + { + {"Fat Mach-O x86_64", "Fat Mach-O x86_64", MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_ALL}, + {"Fat Mach-O x86", "Fat Mach-O x86", MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ALL}, + + {"Fat Mach-O arm64e", "Fat Mach-O arm64e", MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64E}, + {"Fat Mach-O arm64v8", "Fat Mach-O arm64v8", MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_V8}, + {"Fat Mach-O arm64", "Fat Mach-O arm64", MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_ALL}, + {"Fat Mach-O arm64_32", "Fat Mach-O arm64_32", MACHO_CPU_TYPE_ARM64_32, MACHO_CPU_SUBTYPE_ARM64_32_V8}, + {"Fat Mach-O armv8", "Fat Mach-O armv8", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V8}, + {"Fat Mach-O armv7em", "Fat Mach-O armv7em", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7EM}, + {"Fat Mach-O armv7m", "Fat Mach-O armv7m", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7M}, + {"Fat Mach-O armv7s", "Fat Mach-O armv7s", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7S}, + {"Fat Mach-O armv7k", "Fat Mach-O armv7k", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7K}, + {"Fat Mach-O armv7", "Fat Mach-O armv7", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7}, + {"Fat Mach-O armv6m", "Fat Mach-O armv6m", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6M}, + {"Fat Mach-O armv6", "Fat Mach-O armv6", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6}, + {"Fat Mach-O armv5tej", "Fat Mach-O armv5tej", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V5TEJ}, + {"Fat Mach-O armv4t", "Fat Mach-O armv4t", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V4T}, + {"Fat Mach-O arm xscale", "Fat Mach-O arm xscale", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_XSCALE}, + {"Fat Mach-O arm", "Fat Mach-O arm", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_ALL}, + {"Fat Mach-O ppc64", "Fat Mach-O ppc64", MACHO_CPU_TYPE_POWERPC64, MACHO_CPU_SUBTYPE_POWERPC_ALL}, + {"Fat Mach-O ppc970", "Fat Mach-O ppc970", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_970}, + {"Fat Mach-O ppc7450", "Fat Mach-O ppc7450", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7450}, + {"Fat Mach-O ppc7400", "Fat Mach-O ppc7400", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7400}, + {"Fat Mach-O ppc750", "Fat Mach-O ppc750", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_750}, + {"Fat Mach-O ppc620", "Fat Mach-O ppc620", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_620}, + {"Fat Mach-O ppc604e", "Fat Mach-O ppc604e", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604e}, + {"Fat Mach-O ppc604", "Fat Mach-O ppc604", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604}, + {"Fat Mach-O ppc603ev", "Fat Mach-O ppc603ev", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603ev}, + {"Fat Mach-O ppc603e", "Fat Mach-O ppc603e", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603e}, + {"Fat Mach-O ppc603", "Fat Mach-O ppc603", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603}, + {"Fat Mach-O ppc602", "Fat Mach-O ppc602", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_602}, + {"Fat Mach-O ppc601", "Fat Mach-O ppc601", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_601}, + {"Fat Mach-O ppc", "Fat Mach-O ppc", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_ALL} + }; + + for (const auto& type : *g_recognizedFatTypes) + { + BinaryViewType::Register(new FatMachoViewType(type.name, type.long_name, type.cputype, type.cpusubtype)); + } +} diff --git a/view/macho/fatmachoview.h b/view/macho/fatmachoview.h new file mode 100644 index 00000000..575d3884 --- /dev/null +++ b/view/macho/fatmachoview.h @@ -0,0 +1,50 @@ +#pragma once + +#include <exception> +#include <vector> +#include <string.h> + +#include "binaryninjaapi.h" +#include "machoview.h" + +namespace BinaryNinja +{ + struct fat_header + { + uint32_t magic; + uint32_t nfat_arch; + }; + + struct fat_arch + { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t offset; + uint32_t size; + uint32_t align; + }; + + struct fat_arch_64 + { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint64_t offset; + uint64_t size; + uint32_t align; + uint32_t reserved; + }; + + class FatMachoViewType: public BinaryViewType + { + cpu_type_t m_cputype; + cpu_subtype_t m_cpusubtype; + + public: + FatMachoViewType(const std::string& name, const std::string& long_name, cpu_type_t cputype, cpu_subtype_t cpusubtype); + virtual Ref<BinaryView> Create(BinaryView* data) override; + virtual bool IsTypeValidForData(BinaryView* data) override; + virtual bool IsDeprecated() override { return true; } + }; + + void InitFatMachoViewType(); +} diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp new file mode 100644 index 00000000..cfdc0897 --- /dev/null +++ b/view/macho/machoview.cpp @@ -0,0 +1,3399 @@ +#include <algorithm> +#include <cstdint> +#include <sstream> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <tuple> +#ifndef _MSC_VER +#include <cxxabi.h> +#endif +#include "machoview.h" +#include "fatmachoview.h" +#include "universalview.h" +#include "lowlevelilinstruction.h" +#include "rapidjsonwrapper.h" + +enum { + N_STAB = 0xe0, + N_PEXT = 0x10, + N_TYPE = 0x0e, + N_EXT = 0x01 +}; + +using namespace BinaryNinja; +using namespace std; + + +static MachoViewType* g_machoViewType = nullptr; + +static string CommandToString(uint32_t lcCommand) +{ + switch(lcCommand) + { + case LC_REQ_DYLD: return "LC_REQ_DYLD"; + case LC_SEGMENT: return "LC_SEGMENT"; + case LC_SYMTAB: return "LC_SYMTAB"; + case LC_SYMSEG: return "LC_SYMSEG"; + case LC_THREAD: return "LC_THREAD"; + case LC_UNIXTHREAD: return "LC_UNIXTHREAD"; + case LC_LOADFVMLIB: return "LC_LOADFVMLIB"; + case LC_IDFVMLIB: return "LC_IDFVMLIB"; + case LC_IDENT: return "LC_IDENT"; + case LC_FVMFILE: return "LC_FVMFILE"; + case LC_PREPAGE: return "LC_PREPAGE"; + case LC_DYSYMTAB: return "LC_DYSYMTAB"; + case LC_LOAD_DYLIB: return "LC_LOAD_DYLIB"; + case LC_ID_DYLIB: return "LC_ID_DYLIB"; + case LC_LOAD_DYLINKER: return "LC_LOAD_DYLINKER"; + case LC_ID_DYLINKER: return "LC_ID_DYLINKER"; + case LC_PREBOUND_DYLIB: return "LC_PREBOUND_DYLIB"; + case LC_ROUTINES: return "LC_ROUTINES"; + case LC_SUB_FRAMEWORK: return "LC_SUB_FRAMEWORK"; + case LC_SUB_UMBRELLA: return "LC_SUB_UMBRELLA"; + case LC_SUB_CLIENT: return "LC_SUB_CLIENT"; + case LC_SUB_LIBRARY: return "LC_SUB_LIBRARY"; + case LC_TWOLEVEL_HINTS: return "LC_TWOLEVEL_HINTS"; + case LC_PREBIND_CKSUM: return "LC_PREBIND_CKSUM"; + case LC_LOAD_WEAK_DYLIB: return "LC_LOAD_WEAK_DYLIB"; + case LC_SEGMENT_64: return "LC_SEGMENT_64"; + case LC_ROUTINES_64: return "LC_ROUTINES_64"; + case LC_UUID: return "LC_UUID"; + case LC_RPATH: return "LC_RPATH"; + case LC_CODE_SIGNATURE: return "LC_CODE_SIGNATURE"; + case LC_SEGMENT_SPLIT_INFO: return "LC_SEGMENT_SPLIT_INFO"; + case LC_REEXPORT_DYLIB: return "LC_REEXPORT_DYLIB"; + case LC_LAZY_LOAD_DYLIB: return "LC_LAZY_LOAD_DYLIB"; + case LC_ENCRYPTION_INFO: return "LC_ENCRYPTION_INFO"; + case LC_DYLD_INFO: return "LC_DYLD_INFO"; + case LC_DYLD_INFO_ONLY: return "LC_DYLD_INFO_ONLY"; + case LC_LOAD_UPWARD_DYLIB: return "LC_LOAD_UPWARD_DYLIB"; + case LC_VERSION_MIN_MACOSX: return "LC_VERSION_MIN_MACOSX"; + case LC_VERSION_MIN_IPHONEOS: return "LC_VERSION_MIN_IPHONEOS"; + case LC_FUNCTION_STARTS: return "LC_FUNCTION_STARTS"; + case LC_DYLD_ENVIRONMENT: return "LC_DYLD_ENVIRONMENT"; + case LC_MAIN: return "LC_MAIN"; + case LC_DATA_IN_CODE: return "LC_DATA_IN_CODE"; + case LC_SOURCE_VERSION: return "LC_SOURCE_VERSION"; + case LC_DYLIB_CODE_SIGN_DRS: return "LC_DYLIB_CODE_SIGN_DRS"; + case _LC_ENCRYPTION_INFO_64: return "LC_ENCRYPTION_INFO_64"; + case _LC_LINKER_OPTION: return "LC_LINKER_OPTION"; + case _LC_LINKER_OPTIMIZATION_HINT: return "LC_LINKER_OPTIMIZATION_HINT"; + case _LC_VERSION_MIN_TVOS: return "LC_VERSION_MIN_TVOS"; + case LC_VERSION_MIN_WATCHOS: return "LC_VERSION_MIN_WATCHOS"; + case LC_NOTE: return "LC_NOTE"; + case LC_BUILD_VERSION: return "LC_BUILD_VERSION"; + case LC_DYLD_EXPORTS_TRIE: return "LC_DYLD_EXPORTS_TRIE"; + case LC_DYLD_CHAINED_FIXUPS: return "LC_DYLD_CHAINED_FIXUPS"; + default: + { + stringstream ss; + ss << "0x" << std::hex << lcCommand; + return ss.str(); + } + } +} + + +static string BuildPlatformToString(uint32_t platform) +{ + switch (platform) + { + case MACHO_PLATFORM_MACOS: return "macos"; + case MACHO_PLATFORM_IOS: return "ios"; + case MACHO_PLATFORM_TVOS: return "tvos"; + case MACHO_PLATFORM_WATCHOS: return "watchos"; + case MACHO_PLATFORM_BRIDGEOS: return "bridgeos"; + default: + { + stringstream ss; + ss << "0x" << std::hex << platform; + return ss.str(); + } + } +} + + +static string BuildToolToString(uint32_t tool) +{ + switch (tool) + { + case MACHO_TOOL_CLANG: return "clang"; + case MACHO_TOOL_SWIFT: return "swift"; + case MACHO_TOOL_LD: return "ld"; + default: + { + stringstream ss; + ss << "0x" << std::hex << tool; + return ss.str(); + } + } +} + + +static string BuildToolVersionToString(uint32_t version) +{ + uint32_t major = (version >> 16) & 0xffff; + uint32_t minor = (version >> 8) & 0xff; + uint32_t update = version & 0xff; + + stringstream ss; + ss << major << "." << minor; + if (update) + ss << "." << update; + return ss.str(); +} + + +void BinaryNinja::InitMachoViewType() +{ + static MachoViewType type; + BinaryViewType::Register(&type); + g_machoViewType = &type; +} + + +static int64_t readSLEB128(DataBuffer& buffer, size_t length, size_t &offset) +{ + uint8_t cur; + int64_t value = 0; + size_t shift = 0; + while (offset < length) + { + cur = buffer[offset++]; + value |= (cur & 0x7f) << shift; + shift += 7; + if ((cur & 0x80) == 0) + break; + } + value = (value << (64 - shift)) >> (64 - shift); + return value; +} + + +static uint64_t readLEB128(DataBuffer& p, size_t end, size_t &offset) +{ + uint64_t result = 0; + int bit = 0; + do { + if (offset >= end) + return -1; + + uint64_t slice = p[offset] & 0x7f; + + if (bit > 63) + return -1; + else { + result |= (slice << bit); + bit += 7; + } + } while (p[offset++] & 0x80); + return result; +} + + +uint64_t readValidULEB128(DataBuffer& buffer, size_t& cursor) +{ + uint64_t value = readLEB128(buffer, buffer.GetLength(), cursor); + if ((int64_t)value == -1) + throw ReadException(); + return value; +} + + +MachoView::MachoView(const string& typeName, BinaryView* data, bool parseOnly): BinaryView(typeName, data->GetFile(), data), + m_universalImageOffset(0), m_parseOnly(parseOnly) +{ + CreateLogger("BinaryView"); + m_logger = CreateLogger("BinaryView.MachoView"); + + m_backedByDatabase = data->GetFile()->IsBackedByDatabase(typeName); + + Ref<BinaryViewType> universalViewType = BinaryViewType::GetByName("Universal"); + bool isUniversal = (universalViewType && universalViewType->IsTypeValidForData(data)); + + Ref<Settings> viewSettings = Settings::Instance(); + m_extractMangledTypes = viewSettings->Get<bool>("analysis.extractTypesFromMangledNames", data); + m_simplifyTemplates = viewSettings->Get<bool>("analysis.types.templateSimplifier", data); + + Ref<Settings> settings = data->GetLoadSettings(typeName); + if (settings && settings->Contains("loader.macho.universalImageOffset")) + { + settings->SetResourceId(typeName); + m_universalImageOffset = settings->Get<uint64_t>("loader.macho.universalImageOffset", data); + } + else if (isUniversal) + { + Ref<Settings> loadSettings = universalViewType->GetLoadSettingsForData(data); + if (loadSettings && loadSettings->Contains("loader.universal.architectures")) + { + string json = loadSettings->Get<string>("loader.universal.architectures"); + rapidjson::Document jsonArchitectures; + jsonArchitectures.Parse(json.data(), json.size()); + if (!json.size() || jsonArchitectures.HasParseError()) + throw MachoFormatException("Mach-O view could not be created! Json parse error."); + if (!jsonArchitectures.IsArray() || jsonArchitectures.Empty()) + throw MachoFormatException("Mach-O view could not be created! Json data error."); + + // Select the object file based on architecture preference. + // Note: This code is duplicated in options.cpp + vector<string> architectures; + for (const auto& entry : jsonArchitectures.GetArray()) + architectures.push_back(entry["architecture"].GetString()); + auto archPref = Settings::Instance()->Get<vector<string>>("files.universal.architecturePreference"); + int archIndex = 0; + if (auto result = find_first_of(archPref.begin(), archPref.end(), architectures.begin(), architectures.end()); result != archPref.end()) + archIndex = std::find(architectures.begin(), architectures.end(), *result) - architectures.begin(); + + const auto& archEntry = jsonArchitectures[archIndex]; + loadSettings = Settings::Instance(GetUniqueIdentifierString()); + loadSettings->SetResourceId(typeName); + loadSettings->DeserializeSchema(archEntry["loadSchema"].GetString()); + data->SetLoadSettings(typeName, loadSettings); + if (!m_file->IsBackedByDatabase(typeName)) + { + auto defaultSettings = loadSettings->SerializeSettings(nullptr, SettingsDefaultScope); + loadSettings->DeserializeSettings(defaultSettings, data, SettingsResourceScope); + } + + m_universalImageOffset = loadSettings->Get<uint64_t>("loader.macho.universalImageOffset", data); + + // NOTE: this silences the 'Updated load schema detected' warning. There is some chicken/egg stuff here. + loadSettings->UpdateProperty("loader.macho.universalImageOffset", "default", m_universalImageOffset); + } + } + + m_header = HeaderForAddress(data, 0, true); +} + + +MachOHeader MachoView::HeaderForAddress(BinaryView* data, uint64_t address, bool isMainHeader, std::string identifierPrefix) +{ + MachOHeader header; + header.isMainHeader = isMainHeader; + + header.identifierPrefix = identifierPrefix; + header.stringList = new DataBuffer(); + + std::string errorMsg; + if (isMainHeader) { + header.loadCommandOffset = g_machoViewType->ParseHeaders(data, m_universalImageOffset + address, header.ident, &m_arch, &m_plat, errorMsg); + if (!header.loadCommandOffset) + throw MachoFormatException(errorMsg); + } + else + { + // address is a Raw file offset + BinaryReader subReader(data); + subReader.Seek(address); + + header.ident.magic = subReader.Read32(); + + BNEndianness endianness; + if (header.ident.magic == MH_MAGIC || header.ident.magic == MH_MAGIC_64) + endianness = LittleEndian; + else if (header.ident.magic == MH_CIGAM || header.ident.magic == MH_CIGAM_64) + endianness = BigEndian; + else + { + throw ReadException(); + } + + subReader.SetEndianness(endianness); + header.ident.cputype = subReader.Read32(); + header.ident.cpusubtype = subReader.Read32(); + header.ident.filetype = subReader.Read32(); + header.ident.ncmds = subReader.Read32(); + header.ident.sizeofcmds = subReader.Read32(); + header.ident.flags = subReader.Read32(); + if ((header.ident.cputype & MachOABIMask) == MachOABI64) // address size == 8 + { + header.ident.reserved = subReader.Read32(); + } + header.loadCommandOffset = subReader.GetOffset(); + } + + + if (isMainHeader) + { + if (header.ident.magic == MH_MAGIC || header.ident.magic == MH_MAGIC_64) + { + m_endian = LittleEndian; + m_logger->LogDebug("Recognized Little Endian Mach-O"); + } + else // (header.ident.magic == MH_CIGAM || header.ident.magic == MH_CIGAM_64) + { + m_endian = BigEndian; + m_logger->LogDebug("Recognized Big Endian Mach-O"); + } + } + + BinaryReader reader(data); + reader.SetEndianness(m_endian); + reader.SetVirtualBase(m_universalImageOffset); + reader.Seek(header.loadCommandOffset); + + if (isMainHeader) + { + m_objectFile = header.ident.filetype == MH_OBJECT; + m_dylibFile = header.ident.filetype == MH_DYLIB; + m_relocatable = ((header.ident.flags & MH_PIE) > 0) || m_objectFile || m_dylibFile; + m_archId = header.ident.cputype; + m_addressSize = (header.ident.cputype & MachOABIMask) == MachOABI64 ? 8 : 4; + } + + bool first = true; + // Parse segment commands + try + { + m_logger->LogDebug("ident.ncmds: %d\n", header.ident.ncmds); + for (size_t i = 0; i < header.ident.ncmds; i++) + { + load_command load; + segment_command_64 segment64; + section_64 sect; + memset(§, 0, sizeof(sect)); + size_t curOffset = reader.GetOffset(); + load.cmd = reader.Read32(); + load.cmdsize = reader.Read32(); + size_t nextOffset = curOffset + load.cmdsize; + m_logger->LogDebug("Segment cmd: %08x - cmdsize: %08x - ", load.cmd, load.cmdsize); + if (load.cmdsize < sizeof(load_command)) + throw MachoFormatException("unable to read header"); + + switch (load.cmd) + { + case LC_MAIN: + { + uint64_t entryPoint = reader.Read64(); + m_logger->LogDebug("LC_MAIN entryPoint: %#016" PRIx64, entryPoint); + header.entryPoints.push_back({entryPoint, true}); + (void)reader.Read64(); // Stack start + break; + } + case LC_SEGMENT: //map the 32bit version to 64 bits + m_logger->LogDebug("LC_SEGMENT\n"); + segment64.cmd = LC_SEGMENT_64; + reader.Read(&segment64.segname, 16); + segment64.vmaddr = reader.Read32(); + segment64.vmsize = reader.Read32(); + segment64.fileoff = reader.Read32() + m_universalImageOffset; + segment64.filesize = reader.Read32(); + segment64.maxprot = reader.Read32(); + segment64.initprot = reader.Read32(); + segment64.nsects = reader.Read32(); + segment64.flags = reader.Read32(); + if (first) + { + if (!((header.ident.flags & MH_SPLIT_SEGS) || header.ident.cputype == MACHO_CPU_TYPE_X86_64) + || (segment64.flags & MACHO_VM_PROT_WRITE)) + { + header.relocationBase = segment64.vmaddr; + first = false; + } + } + m_logger->LogDebug("\tName: %s\n" \ + "\tCmd: %#08" PRIx32 "\n" \ + "\tvmaddr: %#016" PRIx64 "\n" \ + "\tvmsize: %#016" PRIx64 "\n" \ + "\tfileoff: %#016" PRIx64 "\n" \ + "\tfilesize: %#016" PRIx64 "\n" \ + "\tmaxprot: %#08" PRIx32 "\n" \ + "\tinitprot: %#08" PRIx32 "\n" \ + "\tnsects: %#08" PRIx32 "\n" \ + "\tflags: %#08" PRIx32 "\n", + (char*)&segment64.segname, + segment64.cmd, + segment64.vmaddr, + segment64.vmsize, + segment64.fileoff, + segment64.filesize, + segment64.maxprot, + segment64.initprot, + segment64.nsects, + segment64.flags); + for (size_t j = 0; j < segment64.nsects; j++) + { + reader.Read(§.sectname, 16); + reader.Read(§.segname, 16); + sect.addr = reader.Read32(); + sect.size = reader.Read32(); + sect.offset = reader.Read32(); + sect.align = reader.Read32(); + sect.reloff = reader.Read32(); + sect.nreloc = reader.Read32(); + sect.flags = reader.Read32(); + sect.reserved1 = reader.Read32(); + sect.reserved2 = reader.Read32(); + // if the segment isn't mapped into virtual memory don't add the corresponding sections. + if (segment64.vmsize > 0) + { + header.sections.push_back(sect); + m_allSections.push_back(sect); + } + else + m_logger->LogInfo("Omitting section %16s at %#" PRIx64 " corresponding to segment %16s which is not mapped into memory", (char*)§.sectname, sect.addr, (char*)§.segname); + + m_logger->LogDebug("\t\tSegName: %16s\n" \ + "\t\tSectName: %16s\n" \ + "\t\tAddr: %#" PRIx64 "\n" \ + "\t\tSize: %#" PRIx64 "\n" \ + "\t\tOffset: %#" PRIx32 "\n" \ + "\t\tAlign: %#" PRIx32 "\n" \ + "\t\tReloff: %#" PRIx32 "\n" \ + "\t\tNReloc: %#" PRIx32 "\n" \ + "\t\tFlags: %#" PRIx32 "\n" \ + "\t\tReserved1:%#" PRIx32 "\n" \ + "\t\tReserved2:%#" PRIx32 "\n" \ + "\t\t------------------------\n", + (char*)§.segname, + (char*)§.sectname, + sect.addr, + sect.size, + sect.offset, + sect.align, + sect.reloff, + sect.nreloc, + sect.flags, + sect.reserved1, + sect.reserved2); + if (!strncmp(sect.sectname, "__mod_init_func", 15)) + header.moduleInitSections.push_back(sect); + if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) + header.symbolStubSections.push_back(sect); + if ((sect.flags & S_NON_LAZY_SYMBOL_POINTERS) == S_NON_LAZY_SYMBOL_POINTERS) + header.symbolPointerSections.push_back(sect); + if ((sect.flags & S_LAZY_SYMBOL_POINTERS) == S_LAZY_SYMBOL_POINTERS) + header.symbolPointerSections.push_back(sect); + } + header.segments.push_back(segment64); + m_allSegments.push_back(segment64); + break; + case LC_SEGMENT_64: + m_logger->LogDebug("LC_SEGMENT_64\n"); + segment64.cmd = LC_SEGMENT_64; + reader.Read(&segment64.segname, 16); + segment64.vmaddr = reader.Read64(); + segment64.vmsize = reader.Read64(); + segment64.fileoff = reader.Read64() + m_universalImageOffset; + segment64.filesize = reader.Read64(); + segment64.maxprot = reader.Read32(); + segment64.initprot = reader.Read32(); + segment64.nsects = reader.Read32(); + segment64.flags = reader.Read32(); + if (first) + { + if (!((header.ident.flags & MH_SPLIT_SEGS) || header.ident.cputype == MACHO_CPU_TYPE_X86_64) + || (segment64.flags & MACHO_VM_PROT_WRITE)) + { + header.relocationBase = segment64.vmaddr; + first = false; + } + } + m_logger->LogDebug( + "\tName: %s\n" \ + "\tCmd: %#08" PRIx32 "\n" \ + "\tvmaddr: %#016" PRIx64 "\n" \ + "\tvmsize: %#016" PRIx64 "\n" \ + "\tfileoff: %#016" PRIx64 "\n" \ + "\tfilesize: %#016" PRIx64 "\n" \ + "\tmaxprot: %#08" PRIx32 "\n" \ + "\tinitprot: %#08" PRIx32 "\n" \ + "\tnsects: %#08" PRIx32 "\n" \ + "\tflags: %#08" PRIx32 "\n", + (char*)&segment64.segname, + segment64.cmd, + segment64.vmaddr, + segment64.vmsize, + segment64.fileoff, + segment64.filesize, + segment64.maxprot, + segment64.initprot, + segment64.nsects, + segment64.flags); + m_logger->LogDebug("\t\t------------------------\n"); + for (size_t j = 0; j < segment64.nsects; j++) + { + reader.Read(§.sectname, 16); + reader.Read(§.segname, 16); + sect.addr = reader.Read64(); + sect.size = reader.Read64(); + sect.offset = reader.Read32(); + sect.align = reader.Read32(); + sect.reloff = reader.Read32(); + sect.nreloc = reader.Read32(); + sect.flags = reader.Read32(); + sect.reserved1 = reader.Read32(); + sect.reserved2 = reader.Read32(); + sect.reserved3 = reader.Read32(); + // if the segment isn't mapped into virtual memory don't add the corresponding sections. + if (segment64.vmsize > 0) + { + header.sections.push_back(sect); + m_allSections.push_back(sect); + } + else + m_logger->LogInfo("Omitting section %16s at %#" PRIx64 " corresponding to segment %16s which is not mapped into memory", (char*)§.sectname, sect.addr, (char*)§.segname); + m_logger->LogDebug( + "\t\tSegName: %16s\n" \ + "\t\tSectName: %16s\n" \ + "\t\tAddr: %#" PRIx64 "\n" \ + "\t\tSize: %#" PRIx64 "\n" \ + "\t\tOffset: %#" PRIx32 "\n" \ + "\t\tAlign: %#" PRIx32 "\n" \ + "\t\tReloff: %#" PRIx32 "\n" \ + "\t\tNReloc: %#" PRIx32 "\n" \ + "\t\tFlags: %#" PRIx32 "\n" \ + "\t\tReserved1:%#" PRIx32 "\n" \ + "\t\tReserved2:%#" PRIx32 "\n" \ + "\t\tReserved3:%#" PRIx32 "\n" \ + "\t\t------------------------\n", + (char*)§.segname, + (char*)§.sectname, + sect.addr, + sect.size, + sect.offset, + sect.align, + sect.reloff, + sect.nreloc, + sect.flags, + sect.reserved1, + sect.reserved2, + sect.reserved3); + if (!strncmp(sect.sectname, "__mod_init_func", 15)) + header.moduleInitSections.push_back(sect); + if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) + header.symbolStubSections.push_back(sect); + if ((sect.flags & S_NON_LAZY_SYMBOL_POINTERS) == S_NON_LAZY_SYMBOL_POINTERS) + header.symbolPointerSections.push_back(sect); + if ((sect.flags & S_LAZY_SYMBOL_POINTERS) == S_LAZY_SYMBOL_POINTERS) + header.symbolPointerSections.push_back(sect); + } + header.segments.push_back(segment64); + m_allSegments.push_back(segment64); + break; + case LC_ROUTINES: //map the 32bit version to 64bits + m_logger->LogDebug("LC_REOUTINES\n"); + header.routines64.cmd = LC_ROUTINES_64; + header.routines64.init_address = reader.Read32(); + header.routines64.init_module = reader.Read32(); + header.routines64.reserved1 = reader.Read32(); + header.routines64.reserved2 = reader.Read32(); + header.routines64.reserved3 = reader.Read32(); + header.routines64.reserved4 = reader.Read32(); + header.routines64.reserved5 = reader.Read32(); + header.routines64.reserved6 = reader.Read32(); + header.routinesPresent = true; + m_logger->LogDebug("\tinit_address: %016" PRIx64 "\n\tinit_module: %08" PRIx64 "\n", + header.routines64.init_address, header.routines64.init_module); + break; + case LC_ROUTINES_64: + m_logger->LogDebug("LC_REOUTINES_64\n"); + header.routines64.cmd = LC_ROUTINES_64; + header.routines64.init_address = reader.Read64(); + header.routines64.init_module = reader.Read64(); + header.routines64.reserved1 = reader.Read64(); + header.routines64.reserved2 = reader.Read64(); + header.routines64.reserved3 = reader.Read64(); + header.routines64.reserved4 = reader.Read64(); + header.routines64.reserved5 = reader.Read64(); + header.routines64.reserved6 = reader.Read64(); + header.routinesPresent = true; + m_logger->LogDebug("\tinit_address: %016" PRIx64 "\n\tinit_module: %08" PRIx64 "\n", + header.routines64.init_address, header.routines64.init_module); + break; + case LC_FUNCTION_STARTS: + m_logger->LogDebug("LC_FUNCTION_STARTS\n"); + header.functionStarts.funcoff = reader.Read32(); + header.functionStarts.funcsize = reader.Read32(); + header.functionStartsPresent = true; + m_logger->LogDebug("\tFunction Starts:\n\toffset: %#08" PRIx32 "\n\tsize: %#08" PRIx32 "\n", + header.functionStarts.funcoff, header.functionStarts.funcsize); + break; + case LC_SYMTAB: + m_logger->LogDebug("LC_SYMTAB\n"); + header.symtab.symoff = reader.Read32(); + header.symtab.nsyms = reader.Read32(); + header.symtab.stroff = reader.Read32(); + header.symtab.strsize = reader.Read32(); + reader.Seek(header.symtab.stroff); + header.stringList->Append(reader.Read(header.symtab.strsize)); + header.stringListSize = header.symtab.strsize; + m_logger->LogDebug("\tstrsize: %08x\n" \ + "\tstroff: %08x\n" \ + "\tnsyms: %08x\n", + header.symtab.strsize, + header.symtab.stroff, + header.symtab.nsyms); + break; + case LC_DYSYMTAB: + m_logger->LogDebug("LC_DYSYMTAB\n"); + header.dysymtab.ilocalsym = reader.Read32(); + header.dysymtab.nlocalsym = reader.Read32(); + header.dysymtab.iextdefsym = reader.Read32(); + header.dysymtab.nextdefsym = reader.Read32(); + header.dysymtab.iundefsym = reader.Read32(); + header.dysymtab.nundefsym = reader.Read32(); + header.dysymtab.tocoff = reader.Read32(); + header.dysymtab.ntoc = reader.Read32(); + header.dysymtab.modtaboff = reader.Read32(); + header.dysymtab.nmodtab = reader.Read32(); + header.dysymtab.extrefsymoff = reader.Read32(); + header.dysymtab.nextrefsyms = reader.Read32(); + header.dysymtab.indirectsymoff = reader.Read32(); + header.dysymtab.nindirectsyms = reader.Read32(); + header.dysymtab.extreloff = reader.Read32(); + header.dysymtab.nextrel = reader.Read32(); + header.dysymtab.locreloff = reader.Read32(); + header.dysymtab.nlocrel = reader.Read32(); + m_logger->LogDebug("\theader.dysymtab.ilocalsym 0x%08x\n"\ + "\theader.dysymtab.nlocalsym 0x%08x\n"\ + "\theader.dysymtab.iextdefsym 0x%08x\n"\ + "\theader.dysymtab.nextdefsym 0x%08x\n"\ + "\theader.dysymtab.iundefsym 0x%08x\n"\ + "\theader.dysymtab.nundefsym 0x%08x\n"\ + "\theader.dysymtab.tocoff 0x%08x\n"\ + "\theader.dysymtab.ntoc 0x%08x\n"\ + "\theader.dysymtab.modtaboff 0x%08x\n"\ + "\theader.dysymtab.nmodtab 0x%08x\n"\ + "\theader.dysymtab.extrefsymoff 0x%08x\n"\ + "\theader.dysymtab.nextrefsyms 0x%08x\n"\ + "\theader.dysymtab.indirectsymoff 0x%08x\n"\ + "\theader.dysymtab.nindirectsyms 0x%08x\n"\ + "\theader.dysymtab.extreloff 0x%08x\n"\ + "\theader.dysymtab.nextrel 0x%08x\n"\ + "\theader.dysymtab.locreloff 0x%08x\n"\ + "\theader.dysymtab.nlocrel 0x%08x\n", + header.dysymtab.ilocalsym, + header.dysymtab.nlocalsym, + header.dysymtab.iextdefsym, + header.dysymtab.nextdefsym, + header.dysymtab.iundefsym, + header.dysymtab.nundefsym, + header.dysymtab.tocoff, + header.dysymtab.ntoc, + header.dysymtab.modtaboff, + header.dysymtab.nmodtab, + header.dysymtab.extrefsymoff, + header.dysymtab.nextrefsyms, + header.dysymtab.indirectsymoff, + header.dysymtab.nindirectsyms, + header.dysymtab.extreloff, + header.dysymtab.nextrel, + header.dysymtab.locreloff, + header.dysymtab.nlocrel); + header.dysymPresent = true; + break; + case LC_DYLD_CHAINED_FIXUPS: + m_logger->LogDebug("LC_DYLD_CHAINED_FIXUPS\n"); + header.chainedFixups.dataoff = reader.Read32(); + header.chainedFixups.datasize = reader.Read32(); + header.chainedFixupsPresent = true; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + m_logger->LogDebug("LC_DYLD_INFO\n"); + header.dyldInfo.rebase_off = reader.Read32(); + header.dyldInfo.rebase_size = reader.Read32(); + header.dyldInfo.bind_off = reader.Read32(); + header.dyldInfo.bind_size = reader.Read32(); + header.dyldInfo.weak_bind_off = reader.Read32(); + header.dyldInfo.weak_bind_size = reader.Read32(); + header.dyldInfo.lazy_bind_off = reader.Read32(); + header.dyldInfo.lazy_bind_size = reader.Read32(); + header.dyldInfo.export_off = reader.Read32(); + header.dyldInfo.export_size = reader.Read32(); + header.exportTrie.dataoff = header.dyldInfo.export_off; + header.exportTrie.datasize = header.dyldInfo.export_size; + header.exportTriePresent = true; + header.dyldInfoPresent = true; + break; + case LC_DYLD_EXPORTS_TRIE: + m_logger->LogDebug("LC_DYLD_EXPORTS_TRIE\n"); + header.exportTrie.dataoff = reader.Read32(); + header.exportTrie.datasize = reader.Read32(); + header.exportTriePresent = true; + break; + case LC_THREAD: + case LC_UNIXTHREAD: + while (reader.GetOffset() < nextOffset) + { + thread_command thread; + thread.flavor = reader.Read32(); + thread.count = reader.Read32(); + m_logger->LogDebug("LC_THREAD\\LC_UNIXTHREAD\n"); + switch (m_archId) + { + case MachOx64: + m_logger->LogDebug("x86_64 Thread state\n"); + if (thread.flavor != X86_THREAD_STATE64) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + //This wont be big endian so we can just read the whole thing + reader.Read(&thread.statex64, sizeof(thread.statex64)); + header.entryPoints.push_back({thread.statex64.rip, false}); + break; + case MachOx86: + m_logger->LogDebug("x86 Thread state\n"); + if (thread.flavor != X86_THREAD_STATE32) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + //This wont be big endian so we can just read the whole thing + reader.Read(&thread.statex86, sizeof(thread.statex86)); + header.entryPoints.push_back({thread.statex86.eip, false}); + break; + case MachOArm: + m_logger->LogDebug("Arm Thread state\n"); + if (thread.flavor != _ARM_THREAD_STATE) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + //This wont be big endian so we can just read the whole thing + reader.Read(&thread.statearmv7, sizeof(thread.statearmv7)); + header.entryPoints.push_back({thread.statearmv7.r15, false}); + break; + case MachOAarch64: + case MachOAarch6432: + m_logger->LogDebug("Aarch64 Thread state\n"); + if (thread.flavor != _ARM_THREAD_STATE64) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + reader.Read(&thread.stateaarch64, sizeof(thread.stateaarch64)); + header.entryPoints.push_back({thread.stateaarch64.pc, false}); + break; + case MachOPPC: + m_logger->LogDebug("PPC Thread state\n"); + if (thread.flavor != PPC_THREAD_STATE) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + //Read individual entries for endian reasons + header.entryPoints.push_back({reader.Read32(), false}); + (void)reader.Read32(); + (void)reader.Read32(); + //Read the rest of the structure + (void)reader.Read(&thread.stateppc.r1, sizeof(thread.stateppc) - (3 * 4)); + break; + case MachOPPC64: + m_logger->LogDebug("PPC64 Thread state\n"); + if (thread.flavor != PPC_THREAD_STATE64) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + header.entryPoints.push_back({reader.Read64(), false}); + (void)reader.Read64(); + (void)reader.Read64(); // Stack start + (void)reader.Read(&thread.stateppc64.r1, sizeof(thread.stateppc64) - (3 * 8)); + break; + default: + m_logger->LogError("Unknown archid: %x", m_archId); + } + } + break; + case LC_LOAD_DYLIB: + { + uint32_t offset = reader.Read32(); + if (offset < nextOffset) + { + reader.Seek(curOffset + offset); + string libname = reader.ReadCString(); + header.dylibs.push_back(libname); + } + } + break; + case LC_BUILD_VERSION: + { + m_logger->LogDebug("LC_BUILD_VERSION:"); + header.buildVersion.platform = reader.Read32(); + header.buildVersion.minos = reader.Read32(); + header.buildVersion.sdk = reader.Read32(); + header.buildVersion.ntools = reader.Read32(); + m_logger->LogDebug("Platform: %s", BuildPlatformToString(header.buildVersion.platform).c_str()); + m_logger->LogDebug("MinOS: %s", BuildToolVersionToString(header.buildVersion.minos).c_str()); + m_logger->LogDebug("SDK: %s", BuildToolVersionToString(header.buildVersion.sdk).c_str()); + for (uint32_t i = 0; (i < header.buildVersion.ntools) && (i < 10); i++) + { + uint32_t tool = reader.Read32(); + uint32_t version = reader.Read32(); + header.buildToolVersions.push_back({tool, version}); + m_logger->LogDebug("Build Tool: %s: %s", BuildToolToString(tool).c_str(), BuildToolVersionToString(version).c_str()); + } + break; + } + case LC_FILESET_ENTRY: + { + if (isMainHeader) + { + uint64_t vmAddr = reader.Read64(); + uint64_t fAddr = reader.Read64(); + uint64_t strOff = reader.Read32() + curOffset; + reader.Seek(strOff); + auto identPrefix = reader.ReadCString(load.cmdsize - strOff); + MachOHeader subHeader = HeaderForAddress(data, fAddr, false, identPrefix); + subHeader.textBase = vmAddr; + m_subHeaders[vmAddr] = subHeader; + } + else { + throw ReadException(); + } + break; + } + default: + m_logger->LogDebug("Unhandled command: %s : %" PRIu32 "\n", CommandToString(load.cmd).c_str(), load.cmdsize); + break; + } + if (reader.GetOffset() != nextOffset) + { + m_logger->LogDebug("Didn't parse load command: %s fully %" PRIx64 ":%" PRIxPTR, CommandToString(load.cmd).c_str(), reader.GetOffset(), nextOffset); + } + reader.Seek(nextOffset); + } + } + catch (ReadException&) + { + throw MachoFormatException("Mach-O section headers invalid"); + } + + return header; +} + + +void MachoView::RebaseThreadStarts(BinaryReader& virtualReader, vector<uint32_t>& threadStarts, uint64_t stepMultiplier) +{ + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + + // create a dummy relocation to handle tagged pointers + BNRelocationInfo reloc; + memset(&reloc, 0, sizeof(BNRelocationInfo)); + reloc.type = StandardRelocationType; + reloc.size = 8; + reloc.nativeType = BINARYNINJA_MANUAL_RELOCATION; + + size_t numReg = 0; + size_t numAuth = 0; + uint64_t imageBase = GetStart(); + for (auto threadStart : threadStarts) + { + if (threadStart == 0xffffffff) + break; + + m_logger->LogDebug("Rebasing thread chain start: 0x%x", threadStart); + try + { + uint64_t curAddr = imageBase + threadStart; + virtualReader.Seek(curAddr); + while (true) + { + if (!IsOffsetBackedByFile(virtualReader.GetOffset())) + { + m_logger->LogError("Chained address: 0x%" PRIx64 " in thread start: 0x%x not backed by file!", virtualReader.GetOffset(), threadStart); + break; + } + + // read tagged pointer + uint64_t val = virtualReader.Read64(); + bool isAuthenticated = (val & (1ULL << 63)) != 0; + bool isRebase = (val & (1ULL << 62)) == 0; + if (isRebase) + { + // calculate new target value + uint64_t tgtVal; + if (isAuthenticated) + { + numAuth++; + tgtVal = (val & 0xFFFFFFFF) + imageBase; + } + else + { + numReg++; + uint64_t top8Bits = val & 0x0007F80000000000ULL; + uint64_t bottom43Bits = val & 0x000007FFFFFFFFFFULL; + tgtVal = (top8Bits << 13) | (((int64_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF); + tgtVal += m_imageBaseAdjustment; + } + + reloc.address = curAddr; + DefineRelocation(m_arch, reloc, tgtVal, reloc.address); + } + + // seek to next tagged pointer + val &= ~(1ULL << 62); // handle bind bit + uint64_t delta = (val & 0x3FF8000000000000) >> 51; + delta *= stepMultiplier; + if (!delta) + break; + + curAddr += delta; + virtualReader.Seek(curAddr); + } + } + catch (ReadException&) + { + m_logger->LogError("Failed rebasing thread start at: 0x%x", threadStart); + } + } + + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + double t = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / 1000.0; + m_logger->LogInfo("Rebasing thread starts took %.3f seconds. Updated %" PRIuPTR " pointers (authenticated: %" PRIuPTR ", regular: %" PRIuPTR ").", t, numAuth + numReg, numAuth, numReg); +} + + +bool MachoView::IsValidFunctionStart(uint64_t addr) +{ + uint8_t opcode[BN_MAX_INSTRUCTION_LENGTH]; + size_t opLen = Read(opcode, addr, m_arch->GetMaxInstructionLength()); + if (!opLen) + return false; + + Ref<LowLevelILFunction> ilFunc = new LowLevelILFunction(m_arch, nullptr); + ilFunc->SetCurrentAddress(m_arch, addr); + m_arch->GetInstructionLowLevelIL(opcode, addr, opLen, *ilFunc); + for (size_t i = 0; i < ilFunc->GetInstructionCount(); i++) + { + const auto& instr = ilFunc->GetInstruction(i); + if (instr.operation == LLIL_UNDEF) + return false; + } + + return true; +} + + +void MachoView::ParseFunctionStarts(Platform* platform, uint64_t textBase, function_starts_command functionStarts) +{ + BinaryReader reader(GetParentView()); + reader.SetEndianness(m_endian); + reader.SetVirtualBase(m_universalImageOffset); + try + { + if (m_header.ident.filetype == MH_DSYM) + { + m_logger->LogDebug("Skipping LC_FUNCTION_STARTS parsing"); + return; + } + + reader.Seek(functionStarts.funcoff); + DataBuffer buffer = reader.Read(functionStarts.funcsize); + size_t i = 0; + uint64_t curfunc = textBase; + uint64_t curOffset = 0; + + while (i < functionStarts.funcsize) + { + curOffset = readLEB128(buffer, functionStarts.funcsize, i); + if (curOffset == 0) + continue; + curfunc += curOffset; + uint64_t target = curfunc; + if (!IsValidFunctionStart(target)) + { + m_logger->LogWarn("Possible error processing LC_FUNCTION_STARTS! Not adding function at: 0x%" PRIx64 "\n", target); + continue; + } + Ref<Platform> targetPlatform = platform->GetAssociatedPlatformByAddress(target); + AddFunctionForAnalysis(targetPlatform, target); + m_logger->LogDebug("Adding function start: %#" PRIx64 "\n", curfunc); + } + } + catch (ReadException&) + { + m_logger->LogDebug("LC_FUNCTION_STARTS command invalid"); + } +} + + +bool MachoView::ParseRelocationEntry(const relocation_info& info, uint64_t start, BNRelocationInfo& result) +{ + // struct BNRelocationInfo + // { + // BNRelocationType type; // BinaryNinja Relocation Type + // bool pcRelative; // PC Relative or Absolute (subtract address from relocation) + // bool baseRelative; // Relative to start of module (Add module base to relocation) + // size_t size; // Size of the data to be written + // size_t truncateSize; // After addition/subtraction truncate to + // uint64_t nativeType; // Base type from relocation entry + // size_t addend; // Addend value from relocation entry + // bool hasSign; // Addend should be subtracted + // bool implicitAddend; // Addend should be read from the BinaryView + // bool external; // Relocation entry points to external symbol + // size_t symbolIndex; // Index into symbol table + // size_t sectionIndex; // Index into the section table + // uint64_t address; // Absolute address or segment offset + // }; + m_logger->LogDebug("\tr_address: %" PRIx32 " + %" PRIx64 " = %" PRIx64, info.r_address, start, info.r_address + start); + m_logger->LogDebug("\tr_symbolnum: %" PRIx32, info.r_symbolnum); + m_logger->LogDebug("\tr_pcrel: %" PRIx32, info.r_pcrel); + m_logger->LogDebug("\tr_length: %" PRIx32, info.r_length); + m_logger->LogDebug("\tr_extern: %" PRIx32, info.r_extern); + m_logger->LogDebug("\tr_type: %" PRIx32, info.r_type); + if (m_objectFile && (info.r_address & R_SCATTERED)) + { + m_logger->LogError("Scattered Relocations not currently supported"); + return false; + } + + switch (info.r_length) + { + case 0: result.size = 1; break; + case 1: result.size = 2; break; + case 2: result.size = 4; break; + case 3: result.size = 8; break; + } + result.address = start + info.r_address; + result.truncateSize = result.size; + result.pcRelative = info.r_pcrel; + result.baseRelative = start == GetStart(); + result.nativeType = info.r_type; + result.addend = 0; + result.hasSign = false; + result.implicitAddend = false; + result.external = info.r_extern; + if (result.external) + result.symbolIndex = info.r_symbolnum; + else + result.sectionIndex = info.r_symbolnum; + return true; +} + + +bool MachoView::Init() +{ + Ref<Settings> settings = GetLoadSettings(GetTypeName()); + std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now(); + BinaryReader reader(GetParentView()); + reader.SetEndianness(m_endian); + reader.SetVirtualBase(m_universalImageOffset); + BinaryReader virtualReader(this); + virtualReader.SetEndianness(m_endian); + + uint64_t initialImageBase = 0; + bool initialImageBaseSet = false; + string preferredImageBaseDesc; + for (const auto& i : m_header.segments) + { + if ((i.initprot == MACHO_VM_PROT_NONE) || (!i.vmsize)) + continue; + + if (!initialImageBaseSet) + { + initialImageBase = i.vmaddr; + initialImageBaseSet = true; + preferredImageBaseDesc = i.segname; + } + else if (i.vmaddr < initialImageBase) + { + initialImageBase = i.vmaddr; + preferredImageBaseDesc = i.segname; + } + } + + SetOriginalBase(initialImageBase); + uint64_t preferredImageBase = initialImageBase; + if (settings && settings->Contains("loader.imageBase") && settings->Contains("loader.architecture")) // handle overrides + { + preferredImageBase = 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; + } + + m_imageBaseAdjustment = 0; + if (!initialImageBase) + m_imageBaseAdjustment = preferredImageBase; + else if (initialImageBase <= preferredImageBase) + m_imageBaseAdjustment = preferredImageBase - initialImageBase; + else + m_imageBaseAdjustment = -(int64_t)(initialImageBase - preferredImageBase); + + // Add Mach-O file header type info + EnumerationBuilder cpuTypeBuilder; + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ANY", MACHO_CPU_TYPE_ANY); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_VAX", MACHO_CPU_TYPE_VAX); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC680x0", MACHO_CPU_TYPE_MC680x0); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_X86", MACHO_CPU_TYPE_X86); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_X86_64", MACHO_CPU_TYPE_X86_64); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MIPS", MACHO_CPU_TYPE_MIPS); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC98000", MACHO_CPU_TYPE_MC98000); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_HPPA", MACHO_CPU_TYPE_HPPA); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM", MACHO_CPU_TYPE_ARM); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM64", MACHO_CPU_TYPE_ARM64); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM64_32", MACHO_CPU_TYPE_ARM64_32); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC88000", MACHO_CPU_TYPE_MC88000); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_SPARC", MACHO_CPU_TYPE_SPARC); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_I860", MACHO_CPU_TYPE_I860); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ALPHA", MACHO_CPU_TYPE_ALPHA); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_POWERPC", MACHO_CPU_TYPE_POWERPC); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_POWERPC64", MACHO_CPU_TYPE_POWERPC64); + Ref<Enumeration> cpuTypeEnum = cpuTypeBuilder.Finalize(); + + Ref<Type> cpuTypeEnumType = Type::EnumerationType(nullptr, cpuTypeEnum, 4, false); + string cpuTypeEnumName = "cpu_type_t"; + string cpuTypeEnumId = Type::GenerateAutoTypeId("macho", cpuTypeEnumName); + m_typeNames.cpuTypeEnumQualName = DefineType(cpuTypeEnumId, cpuTypeEnumName, cpuTypeEnumType); + + EnumerationBuilder fileTypeBuilder; + fileTypeBuilder.AddMemberWithValue("MH_OBJECT", MH_OBJECT); + fileTypeBuilder.AddMemberWithValue("MH_EXECUTE", MH_EXECUTE); + fileTypeBuilder.AddMemberWithValue("MH_FVMLIB", MH_FVMLIB); + fileTypeBuilder.AddMemberWithValue("MH_CORE", MH_CORE); + fileTypeBuilder.AddMemberWithValue("MH_PRELOAD", MH_PRELOAD); + fileTypeBuilder.AddMemberWithValue("MH_DYLIB", MH_DYLIB); + fileTypeBuilder.AddMemberWithValue("MH_DYLINKER", MH_DYLINKER); + fileTypeBuilder.AddMemberWithValue("MH_BUNDLE", MH_BUNDLE); + fileTypeBuilder.AddMemberWithValue("MH_DYLIB_STUB", MH_DYLIB_STUB); + fileTypeBuilder.AddMemberWithValue("MH_DSYM", MH_DSYM); + fileTypeBuilder.AddMemberWithValue("MH_KEXT_BUNDLE", MH_KEXT_BUNDLE); + fileTypeBuilder.AddMemberWithValue("MH_FILESET", MH_FILESET); + Ref<Enumeration> fileTypeEnum = fileTypeBuilder.Finalize(); + + Ref<Type> fileTypeEnumType = Type::EnumerationType(nullptr, fileTypeEnum, 4, false); + string fileTypeEnumName = "file_type_t"; + string fileTypeEnumId = Type::GenerateAutoTypeId("macho", fileTypeEnumName); + m_typeNames.fileTypeEnumQualName = DefineType(fileTypeEnumId, fileTypeEnumName, fileTypeEnumType); + + EnumerationBuilder flagsTypeBuilder; + flagsTypeBuilder.AddMemberWithValue("MH_NOUNDEFS", MH_NOUNDEFS); + flagsTypeBuilder.AddMemberWithValue("MH_INCRLINK", MH_INCRLINK); + flagsTypeBuilder.AddMemberWithValue("MH_DYLDLINK", MH_DYLDLINK); + flagsTypeBuilder.AddMemberWithValue("MH_BINDATLOAD", MH_BINDATLOAD); + flagsTypeBuilder.AddMemberWithValue("MH_PREBOUND", MH_PREBOUND); + flagsTypeBuilder.AddMemberWithValue("MH_SPLIT_SEGS", MH_SPLIT_SEGS); + flagsTypeBuilder.AddMemberWithValue("MH_LAZY_INIT", MH_LAZY_INIT); + flagsTypeBuilder.AddMemberWithValue("MH_TWOLEVEL", MH_TWOLEVEL); + flagsTypeBuilder.AddMemberWithValue("MH_FORCE_FLAT", MH_FORCE_FLAT); + flagsTypeBuilder.AddMemberWithValue("MH_NOMULTIDEFS", MH_NOMULTIDEFS); + flagsTypeBuilder.AddMemberWithValue("MH_NOFIXPREBINDING", MH_NOFIXPREBINDING); + flagsTypeBuilder.AddMemberWithValue("MH_PREBINDABLE", MH_PREBINDABLE); + flagsTypeBuilder.AddMemberWithValue("MH_ALLMODSBOUND", MH_ALLMODSBOUND); + flagsTypeBuilder.AddMemberWithValue("MH_SUBSECTIONS_VIA_SYMBOLS", MH_SUBSECTIONS_VIA_SYMBOLS); + flagsTypeBuilder.AddMemberWithValue("MH_CANONICAL", MH_CANONICAL); + flagsTypeBuilder.AddMemberWithValue("MH_WEAK_DEFINES", MH_WEAK_DEFINES); + flagsTypeBuilder.AddMemberWithValue("MH_BINDS_TO_WEAK", MH_BINDS_TO_WEAK); + flagsTypeBuilder.AddMemberWithValue("MH_ALLOW_STACK_EXECUTION", MH_ALLOW_STACK_EXECUTION); + flagsTypeBuilder.AddMemberWithValue("MH_ROOT_SAFE", MH_ROOT_SAFE); + flagsTypeBuilder.AddMemberWithValue("MH_SETUID_SAFE", MH_SETUID_SAFE); + flagsTypeBuilder.AddMemberWithValue("MH_NO_REEXPORTED_DYLIBS", MH_NO_REEXPORTED_DYLIBS); + flagsTypeBuilder.AddMemberWithValue("MH_PIE", MH_PIE); + flagsTypeBuilder.AddMemberWithValue("MH_DEAD_STRIPPABLE_DYLIB", MH_DEAD_STRIPPABLE_DYLIB); + flagsTypeBuilder.AddMemberWithValue("MH_HAS_TLV_DESCRIPTORS", MH_HAS_TLV_DESCRIPTORS); + flagsTypeBuilder.AddMemberWithValue("MH_NO_HEAP_EXECUTION", MH_NO_HEAP_EXECUTION); + flagsTypeBuilder.AddMemberWithValue("MH_APP_EXTENSION_SAFE", _MH_APP_EXTENSION_SAFE); + flagsTypeBuilder.AddMemberWithValue("MH_NLIST_OUTOFSYNC_WITH_DYLDINFO", _MH_NLIST_OUTOFSYNC_WITH_DYLDINFO); + flagsTypeBuilder.AddMemberWithValue("MH_SIM_SUPPORT", _MH_SIM_SUPPORT); + flagsTypeBuilder.AddMemberWithValue("MH_DYLIB_IN_CACHE", _MH_DYLIB_IN_CACHE); + Ref<Enumeration> flagsTypeEnum = flagsTypeBuilder.Finalize(); + + Ref<Type> flagsTypeEnumType = Type::EnumerationType(nullptr, flagsTypeEnum, 4, false); + string flagsTypeEnumName = "flags_type_t"; + string flagsTypeEnumId = Type::GenerateAutoTypeId("macho", flagsTypeEnumName); + m_typeNames.flagsTypeEnumQualName = DefineType(flagsTypeEnumId, flagsTypeEnumName, flagsTypeEnumType); + + StructureBuilder machoHeaderBuilder; + machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "magic"); + machoHeaderBuilder.AddMember(Type::NamedType(this, m_typeNames.cpuTypeEnumQualName), "cputype"); + machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "cpusubtype"); + machoHeaderBuilder.AddMember(Type::NamedType(this, m_typeNames.fileTypeEnumQualName), "filetype"); + machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "ncmds"); + machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "sizeofcmds"); + machoHeaderBuilder.AddMember(Type::NamedType(this, m_typeNames.flagsTypeEnumQualName), "flags"); + if (m_addressSize == 8) + machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "reserved"); + Ref<Structure> machoHeaderStruct = machoHeaderBuilder.Finalize(); + QualifiedName headerName = (m_addressSize == 8) ? string("mach_header_64") : string("mach_header"); + + string headerTypeId = Type::GenerateAutoTypeId("macho", headerName); + Ref<Type> machoHeaderType = Type::StructureType(machoHeaderStruct); + m_typeNames.headerQualName = DefineType(headerTypeId, headerName, machoHeaderType); + + EnumerationBuilder cmdTypeBuilder; + cmdTypeBuilder.AddMemberWithValue("LC_REQ_DYLD", LC_REQ_DYLD); + cmdTypeBuilder.AddMemberWithValue("LC_SEGMENT", LC_SEGMENT); + cmdTypeBuilder.AddMemberWithValue("LC_SYMTAB", LC_SYMTAB); + cmdTypeBuilder.AddMemberWithValue("LC_SYMSEG",LC_SYMSEG); + cmdTypeBuilder.AddMemberWithValue("LC_THREAD", LC_THREAD); + cmdTypeBuilder.AddMemberWithValue("LC_UNIXTHREAD", LC_UNIXTHREAD); + cmdTypeBuilder.AddMemberWithValue("LC_LOADFVMLIB", LC_LOADFVMLIB); + cmdTypeBuilder.AddMemberWithValue("LC_IDFVMLIB", LC_IDFVMLIB); + cmdTypeBuilder.AddMemberWithValue("LC_IDENT", LC_IDENT); + cmdTypeBuilder.AddMemberWithValue("LC_FVMFILE", LC_FVMFILE); + cmdTypeBuilder.AddMemberWithValue("LC_PREPAGE", LC_PREPAGE); + cmdTypeBuilder.AddMemberWithValue("LC_DYSYMTAB", LC_DYSYMTAB); + cmdTypeBuilder.AddMemberWithValue("LC_LOAD_DYLIB", LC_LOAD_DYLIB); + cmdTypeBuilder.AddMemberWithValue("LC_ID_DYLIB", LC_ID_DYLIB); + cmdTypeBuilder.AddMemberWithValue("LC_LOAD_DYLINKER", LC_LOAD_DYLINKER); + cmdTypeBuilder.AddMemberWithValue("LC_ID_DYLINKER", LC_ID_DYLINKER); + cmdTypeBuilder.AddMemberWithValue("LC_PREBOUND_DYLIB", LC_PREBOUND_DYLIB); + cmdTypeBuilder.AddMemberWithValue("LC_ROUTINES", LC_ROUTINES); + cmdTypeBuilder.AddMemberWithValue("LC_SUB_FRAMEWORK", LC_SUB_FRAMEWORK); + cmdTypeBuilder.AddMemberWithValue("LC_SUB_UMBRELLA", LC_SUB_UMBRELLA); + cmdTypeBuilder.AddMemberWithValue("LC_SUB_CLIENT", LC_SUB_CLIENT); + cmdTypeBuilder.AddMemberWithValue("LC_SUB_LIBRARY", LC_SUB_LIBRARY); + cmdTypeBuilder.AddMemberWithValue("LC_TWOLEVEL_HINTS", LC_TWOLEVEL_HINTS); + cmdTypeBuilder.AddMemberWithValue("LC_PREBIND_CKSUM", LC_PREBIND_CKSUM); + cmdTypeBuilder.AddMemberWithValue("LC_LOAD_WEAK_DYLIB", LC_LOAD_WEAK_DYLIB);// (0x18 | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_SEGMENT_64", LC_SEGMENT_64); + cmdTypeBuilder.AddMemberWithValue("LC_ROUTINES_64", LC_ROUTINES_64); + cmdTypeBuilder.AddMemberWithValue("LC_UUID", LC_UUID); + cmdTypeBuilder.AddMemberWithValue("LC_RPATH", LC_RPATH);// (0x1c | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_CODE_SIGNATURE", LC_CODE_SIGNATURE); + cmdTypeBuilder.AddMemberWithValue("LC_SEGMENT_SPLIT_INFO", LC_SEGMENT_SPLIT_INFO); + cmdTypeBuilder.AddMemberWithValue("LC_REEXPORT_DYLIB", LC_REEXPORT_DYLIB);// (0x1f | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_LAZY_LOAD_DYLIB", LC_LAZY_LOAD_DYLIB); + cmdTypeBuilder.AddMemberWithValue("LC_ENCRYPTION_INFO", LC_ENCRYPTION_INFO); + cmdTypeBuilder.AddMemberWithValue("LC_DYLD_INFO", LC_DYLD_INFO); + cmdTypeBuilder.AddMemberWithValue("LC_DYLD_INFO_ONLY", LC_DYLD_INFO_ONLY);// (0x22 | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_LOAD_UPWARD_DYLIB", LC_LOAD_UPWARD_DYLIB);// (0x23 | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_MACOSX", LC_VERSION_MIN_MACOSX); + cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_IPHONEOS", LC_VERSION_MIN_IPHONEOS); + cmdTypeBuilder.AddMemberWithValue("LC_FUNCTION_STARTS", LC_FUNCTION_STARTS); + cmdTypeBuilder.AddMemberWithValue("LC_DYLD_ENVIRONMENT", LC_DYLD_ENVIRONMENT); + cmdTypeBuilder.AddMemberWithValue("LC_MAIN", LC_MAIN);// (0x28 | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_DATA_IN_CODE", LC_DATA_IN_CODE); + cmdTypeBuilder.AddMemberWithValue("LC_SOURCE_VERSION", LC_SOURCE_VERSION); + cmdTypeBuilder.AddMemberWithValue("LC_DYLIB_CODE_SIGN_DRS", LC_DYLIB_CODE_SIGN_DRS); + cmdTypeBuilder.AddMemberWithValue("LC_ENCRYPTION_INFO_64", _LC_ENCRYPTION_INFO_64); + cmdTypeBuilder.AddMemberWithValue("LC_LINKER_OPTION", _LC_LINKER_OPTION); + cmdTypeBuilder.AddMemberWithValue("LC_LINKER_OPTIMIZATION_HINT", _LC_LINKER_OPTIMIZATION_HINT); + cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_TVOS", _LC_VERSION_MIN_TVOS); + cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_WATCHOS", LC_VERSION_MIN_WATCHOS); + cmdTypeBuilder.AddMemberWithValue("LC_NOTE", LC_NOTE); + cmdTypeBuilder.AddMemberWithValue("LC_BUILD_VERSION", LC_BUILD_VERSION); + cmdTypeBuilder.AddMemberWithValue("LC_DYLD_EXPORTS_TRIE", LC_DYLD_EXPORTS_TRIE); + cmdTypeBuilder.AddMemberWithValue("LC_DYLD_CHAINED_FIXUPS", LC_DYLD_CHAINED_FIXUPS); + cmdTypeBuilder.AddMemberWithValue("LC_FILESET_ENTRY", LC_FILESET_ENTRY); + Ref<Enumeration> cmdTypeEnum = cmdTypeBuilder.Finalize(); + + Ref<Type> cmdTypeEnumType = Type::EnumerationType(nullptr, cmdTypeEnum, 4, false); + string cmdTypeEnumName = "load_command_type_t"; + string cmdTypeEnumId = Type::GenerateAutoTypeId("macho", cmdTypeEnumName); + m_typeNames.cmdTypeEnumQualName = DefineType(cmdTypeEnumId, cmdTypeEnumName, cmdTypeEnumType); + + StructureBuilder loadCommandBuilder; + loadCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + loadCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + Ref<Structure> loadCommandStruct = loadCommandBuilder.Finalize(); + QualifiedName loadCommandName = string("load_command"); + string loadCommandTypeId = Type::GenerateAutoTypeId("macho", loadCommandName); + Ref<Type> loadCommandType = Type::StructureType(loadCommandStruct); + m_typeNames.loadCommandQualName = DefineType(loadCommandTypeId, loadCommandName, loadCommandType); + + EnumerationBuilder protTypeBuilder; + protTypeBuilder.AddMemberWithValue("VM_PROT_NONE", MACHO_VM_PROT_NONE); + protTypeBuilder.AddMemberWithValue("VM_PROT_READ", MACHO_VM_PROT_READ); + protTypeBuilder.AddMemberWithValue("VM_PROT_WRITE", MACHO_VM_PROT_WRITE); + protTypeBuilder.AddMemberWithValue("VM_PROT_EXECUTE", MACHO_VM_PROT_EXECUTE); + // protTypeBuilder.AddMemberWithValue("VM_PROT_DEFAULT", MACHO_VM_PROT_DEFAULT); + // protTypeBuilder.AddMemberWithValue("VM_PROT_ALL", MACHO_VM_PROT_ALL); + protTypeBuilder.AddMemberWithValue("VM_PROT_NO_CHANGE", MACHO_VM_PROT_NO_CHANGE); + protTypeBuilder.AddMemberWithValue("VM_PROT_COPY_OR_WANTS_COPY", MACHO_VM_PROT_COPY); + //protTypeBuilder.AddMemberWithValue("VM_PROT_WANTS_COPY", MACHO_VM_PROT_WANTS_COPY); + Ref<Enumeration> protTypeEnum = protTypeBuilder.Finalize(); + + Ref<Type> protTypeEnumType = Type::EnumerationType(nullptr, protTypeEnum, 4, false); + string protTypeEnumName = "vm_prot_t"; + string protTypeEnumId = Type::GenerateAutoTypeId("macho", protTypeEnumName); + m_typeNames.protTypeEnumQualName = DefineType(protTypeEnumId, protTypeEnumName, protTypeEnumType); + + EnumerationBuilder segFlagsTypeBuilder; + segFlagsTypeBuilder.AddMemberWithValue("SG_HIGHVM", SG_HIGHVM); + segFlagsTypeBuilder.AddMemberWithValue("SG_FVMLIB", SG_FVMLIB); + segFlagsTypeBuilder.AddMemberWithValue("SG_NORELOC", SG_NORELOC); + segFlagsTypeBuilder.AddMemberWithValue("SG_PROTECTED_VERSION_1", SG_PROTECTED_VERSION_1); + Ref<Enumeration> segFlagsTypeEnum = segFlagsTypeBuilder.Finalize(); + + Ref<Type> segFlagsTypeEnumType = Type::EnumerationType(nullptr, segFlagsTypeEnum, 4, false); + string segFlagsTypeEnumName = "sg_flags_t"; + string segFlagsTypeEnumId = Type::GenerateAutoTypeId("macho", segFlagsTypeEnumName); + m_typeNames.segFlagsTypeEnumQualName = DefineType(segFlagsTypeEnumId, segFlagsTypeEnumName, segFlagsTypeEnumType); + + StructureBuilder loadSegmentCommandBuilder; + loadSegmentCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + loadSegmentCommandBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "vmaddr"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "vmsize"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "fileoff"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "filesize"); + loadSegmentCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.protTypeEnumQualName), "maxprot"); + loadSegmentCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.protTypeEnumQualName), "initprot"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "nsects"); + loadSegmentCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.segFlagsTypeEnumQualName), "flags"); + Ref<Structure> loadSegmentCommandStruct = loadSegmentCommandBuilder.Finalize(); + QualifiedName loadSegmentCommandName = string("segment_command"); + string loadSegmentCommandTypeId = Type::GenerateAutoTypeId("macho", loadSegmentCommandName); + Ref<Type> loadSegmentCommandType = Type::StructureType(loadSegmentCommandStruct); + m_typeNames.loadSegmentCommandQualName = DefineType(loadSegmentCommandTypeId, loadSegmentCommandName, loadSegmentCommandType); + + StructureBuilder loadSegmentCommand64Builder; + loadSegmentCommand64Builder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(4, false), "cmdsize"); + loadSegmentCommand64Builder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "vmaddr"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "vmsize"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "fileoff"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "filesize"); + loadSegmentCommand64Builder.AddMember(Type::NamedType(this, m_typeNames.protTypeEnumQualName), "maxprot"); + loadSegmentCommand64Builder.AddMember(Type::NamedType(this, m_typeNames.protTypeEnumQualName), "initprot"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(4, false), "nsects"); + loadSegmentCommand64Builder.AddMember(Type::NamedType(this, m_typeNames.segFlagsTypeEnumQualName), "flags"); + Ref<Structure> loadSegmentCommand64Struct = loadSegmentCommand64Builder.Finalize(); + QualifiedName loadSegment64CommandName = string("segment_command_64"); + string loadSegment64CommandTypeId = Type::GenerateAutoTypeId("macho", loadSegment64CommandName); + Ref<Type> loadSegment64CommandType = Type::StructureType(loadSegmentCommand64Struct); + m_typeNames.loadSegment64CommandQualName = DefineType(loadSegment64CommandTypeId, loadSegment64CommandName, loadSegment64CommandType); + + StructureBuilder sectionBuilder; + sectionBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "sectname"); + sectionBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "addr"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "size"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "offset"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "align"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "reloff"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "nreloc"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "flags"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "reserved1"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "reserved2"); + Ref<Structure> sectionStruct = sectionBuilder.Finalize(); + QualifiedName sectionName = string("section"); + string sectionTypeId = Type::GenerateAutoTypeId("macho", sectionName); + Ref<Type> sectionType = Type::StructureType(sectionStruct); + m_typeNames.sectionQualName = DefineType(sectionTypeId, sectionName, sectionType); + + StructureBuilder section64Builder; + section64Builder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "sectname"); + section64Builder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); + section64Builder.AddMember(Type::IntegerType(8, false), "addr"); + section64Builder.AddMember(Type::IntegerType(8, false), "size"); + section64Builder.AddMember(Type::IntegerType(4, false), "offset"); + section64Builder.AddMember(Type::IntegerType(4, false), "align"); + section64Builder.AddMember(Type::IntegerType(4, false), "reloff"); + section64Builder.AddMember(Type::IntegerType(4, false), "nreloc"); + section64Builder.AddMember(Type::IntegerType(4, false), "flags"); + section64Builder.AddMember(Type::IntegerType(4, false), "reserved1"); + section64Builder.AddMember(Type::IntegerType(4, false), "reserved2"); + section64Builder.AddMember(Type::IntegerType(4, false), "reserved3"); + Ref<Structure> section64Struct = section64Builder.Finalize(); + QualifiedName section64Name = string("section_64"); + string section64TypeId = Type::GenerateAutoTypeId("macho", section64Name); + Ref<Type> section64Type = Type::StructureType(section64Struct); + m_typeNames.section64QualName = DefineType(section64TypeId, section64Name, section64Type); + + StructureBuilder symtabBuilder; + symtabBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + symtabBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + symtabBuilder.AddMember(Type::IntegerType(4, false), "symoff"); + symtabBuilder.AddMember(Type::IntegerType(4, false), "nsyms"); + symtabBuilder.AddMember(Type::IntegerType(4, false), "stroff"); + symtabBuilder.AddMember(Type::IntegerType(4, false), "strsize"); + Ref<Structure> symtabStruct = symtabBuilder.Finalize(); + QualifiedName symtabName = string("symtab"); + string symtabTypeId = Type::GenerateAutoTypeId("macho", symtabName); + Ref<Type> symtabType = Type::StructureType(symtabStruct); + m_typeNames.symtabQualName = DefineType(symtabTypeId, symtabName, symtabType); + + StructureBuilder dynsymtabBuilder; + dynsymtabBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "ilocalsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nlocalsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "iextdefsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nextdefsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "iundefsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nundefsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "tocoff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "ntoc"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "modtaboff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nmodtab"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "extrefsymoff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nextrefsyms"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "indirectsymoff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nindirectsyms"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "extreloff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nextrel"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "locreloff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nlocrel"); + Ref<Structure> dynsymtabStruct = dynsymtabBuilder.Finalize(); + QualifiedName dynsymtabName = string("dynsymtab"); + string dynsymtabTypeId = Type::GenerateAutoTypeId("macho", dynsymtabName); + Ref<Type> dynsymtabType = Type::StructureType(dynsymtabStruct); + m_typeNames.dynsymtabQualName = DefineType(dynsymtabTypeId, dynsymtabName, dynsymtabType); + + StructureBuilder uuidBuilder; + uuidBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + uuidBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + uuidBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, false), 16), "uuid"); + Ref<Structure> uuidStruct = uuidBuilder.Finalize(); + QualifiedName uuidName = string("uuid"); + string uuidTypeId = Type::GenerateAutoTypeId("macho", uuidName); + Ref<Type> uuidType = Type::StructureType(uuidStruct); + m_typeNames.uuidQualName = DefineType(uuidTypeId, uuidName, uuidType); + + StructureBuilder linkeditDataBuilder; + linkeditDataBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + linkeditDataBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + linkeditDataBuilder.AddMember(Type::IntegerType(4, false), "dataoff"); + linkeditDataBuilder.AddMember(Type::IntegerType(4, false), "datasize"); + Ref<Structure> linkeditDataStruct = linkeditDataBuilder.Finalize(); + QualifiedName linkeditDataName = string("linkedit_data"); + string linkeditDataTypeId = Type::GenerateAutoTypeId("macho", linkeditDataName); + Ref<Type> linkeditDataType = Type::StructureType(linkeditDataStruct); + m_typeNames.linkeditDataQualName = DefineType(linkeditDataTypeId, linkeditDataName, linkeditDataType); + + StructureBuilder encryptionInfoBuilder; + encryptionInfoBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cryptoff"); + encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cryptsize"); + encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cryptid"); + Ref<Structure> encryptionInfoStruct = encryptionInfoBuilder.Finalize(); + QualifiedName encryptionInfoName = string("encryption_info"); + string encryptionInfoTypeId = Type::GenerateAutoTypeId("macho", encryptionInfoName); + Ref<Type> encryptionInfoType = Type::StructureType(encryptionInfoStruct); + m_typeNames.encryptionInfoQualName = DefineType(encryptionInfoTypeId, encryptionInfoName, encryptionInfoType); + + StructureBuilder versionMinBuilder; + versionMinBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + versionMinBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + versionMinBuilder.AddMember(Type::IntegerType(4, false), "version"); + versionMinBuilder.AddMember(Type::IntegerType(4, false), "sdk"); + Ref<Structure> versionMinStruct = versionMinBuilder.Finalize(); + QualifiedName versionMinName = string("version_min"); + string versionMinTypeId = Type::GenerateAutoTypeId("macho", versionMinName); + Ref<Type> versionMinType = Type::StructureType(versionMinStruct); + m_typeNames.versionMinQualName = DefineType(versionMinTypeId, versionMinName, versionMinType); + + StructureBuilder dyldInfoBuilder; + dyldInfoBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "rebase_off"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "rebase_size"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "bind_off"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "bind_size"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "weak_bind_off"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "weak_bind_size"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "lazy_bind_off"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "lazy_bind_size"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "export_off"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "export_size"); + Ref<Structure> dyldInfoStruct = dyldInfoBuilder.Finalize(); + QualifiedName dyldInfoName = string("dyld_info"); + string dyldInfoTypeId = Type::GenerateAutoTypeId("macho", dyldInfoName); + Ref<Type> dyldInfoType = Type::StructureType(dyldInfoStruct); + m_typeNames.dyldInfoQualName = DefineType(dyldInfoTypeId, dyldInfoName, dyldInfoType); + + StructureBuilder dylibBuilder; + dylibBuilder.AddMember(Type::IntegerType(4, false), "name"); + dylibBuilder.AddMember(Type::IntegerType(4, false), "timestamp"); + dylibBuilder.AddMember(Type::IntegerType(4, false), "current_version"); + dylibBuilder.AddMember(Type::IntegerType(4, false), "compatibility_version"); + Ref<Structure> dylibStruct = dylibBuilder.Finalize(); + QualifiedName dylibName = string("dylib"); + string dylibTypeId = Type::GenerateAutoTypeId("macho", dylibName); + Ref<Type> dylibType = Type::StructureType(dylibStruct); + m_typeNames.dylibQualName = DefineType(dylibTypeId, dylibName, dylibType); + + StructureBuilder dylibCommandBuilder; + dylibCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + dylibCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + dylibCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.dylibQualName), "dylib"); + Ref<Structure> dylibCommandStruct = dylibCommandBuilder.Finalize(); + QualifiedName dylibCommandName = string("dylib_command"); + string dylibCommandTypeId = Type::GenerateAutoTypeId("macho", dylibCommandName); + Ref<Type> dylibCommandType = Type::StructureType(dylibCommandStruct); + m_typeNames.dylibCommandQualName = DefineType(dylibCommandTypeId, dylibCommandName, dylibCommandType); + + StructureBuilder filesetEntryCommandBuilder; + filesetEntryCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + filesetEntryCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + filesetEntryCommandBuilder.AddMember(Type::IntegerType(8, false), "vmaddr"); + filesetEntryCommandBuilder.AddMember(Type::IntegerType(8, false), "fileoff"); + filesetEntryCommandBuilder.AddMember(Type::IntegerType(4, false), "entry_id"); + filesetEntryCommandBuilder.AddMember(Type::IntegerType(4, false), "reserved"); + Ref<Structure> filesetEntryCommandStruct = filesetEntryCommandBuilder.Finalize(); + QualifiedName filesetEntryCommandName = string("fileset_entry_command"); + string filesetEntryCommandTypeId = Type::GenerateAutoTypeId("macho", filesetEntryCommandName); + Ref<Type> filesetEntryCommandType = Type::StructureType(filesetEntryCommandStruct); + m_typeNames.filesetEntryCommandQualName = DefineType(filesetEntryCommandTypeId, filesetEntryCommandName, filesetEntryCommandType); + + if (!InitializeHeader(m_header, true, preferredImageBase, preferredImageBaseDesc)) + return false; + + for (auto& it : m_subHeaders) + { + if (!InitializeHeader(it.second, false, it.first, "")) + return false; + } + + 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("Mach-O parsing took %.3f seconds\n", t); + return true; +} + + +bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_t preferredImageBase, std::string preferredImageBaseDesc) +{ + Ref<Settings> settings = GetLoadSettings(GetTypeName()); + + for (auto& i : header.segments) + i.vmaddr += m_imageBaseAdjustment; + + for (auto& i : header.sections) + i.addr += m_imageBaseAdjustment; + + for (auto& i : header.symbolStubSections) + i.addr += m_imageBaseAdjustment; + + for (auto& i : header.symbolPointerSections) + i.addr += m_imageBaseAdjustment; + + for (auto& entryPoint : header.entryPoints) + entryPoint.first += (entryPoint.second ? 0 : m_imageBaseAdjustment); + + if (header.routinesPresent) + header.routines64.init_address += m_imageBaseAdjustment; + + for (auto& segment : header.segments) + { + if ((segment.initprot == MACHO_VM_PROT_NONE) || (!segment.vmsize)) + continue; + + if ((segment.initprot & MACHO_VM_PROT_EXECUTE) == MACHO_VM_PROT_EXECUTE) + { + if ((segment.fileoff == (0 + m_universalImageOffset)) && (segment.filesize != 0)) + header.textBase = segment.vmaddr; + for (auto& entryPoint : header.entryPoints) + { + uint64_t val = entryPoint.first + (entryPoint.second ? header.textBase : 0); + if (find(header.m_entryPoints.begin(), header.m_entryPoints.end(), val) == header.m_entryPoints.end()) + header.m_entryPoints.push_back(val); + } + } + } + + if (!(m_header.ident.filetype == MH_FILESET && isMainHeader)) \ + { + for (auto &segment: header.segments) { + if ((segment.initprot == MACHO_VM_PROT_NONE) || (!segment.vmsize)) + continue; + + uint32_t flags = 0; + if (segment.initprot & MACHO_VM_PROT_READ) + flags |= SegmentReadable; + if (segment.initprot & MACHO_VM_PROT_WRITE) + flags |= SegmentWritable; + if (segment.initprot & MACHO_VM_PROT_EXECUTE) + flags |= SegmentExecutable; + if (((segment.initprot & MACHO_VM_PROT_WRITE) == 0) && + ((segment.maxprot & MACHO_VM_PROT_WRITE) == 0)) + flags |= SegmentDenyWrite; + if (((segment.initprot & MACHO_VM_PROT_EXECUTE) == 0) && + ((segment.maxprot & MACHO_VM_PROT_EXECUTE) == 0)) + flags |= SegmentDenyExecute; + + // if we're positive we have an entry point for some reason, force the segment + // executable. this helps with kernel images. + for (auto &entryPoint: header.m_entryPoints) + if (segment.vmaddr <= entryPoint && (entryPoint < (segment.vmaddr + segment.filesize))) + flags |= SegmentExecutable; + + AddAutoSegment(segment.vmaddr, segment.vmsize, segment.fileoff, segment.filesize, flags); + } + for (auto& section : header.sections) + { + char sectionName[17]; + memcpy(sectionName, section.sectname, sizeof(section.sectname)); + sectionName[16] = 0; + if (header.identifierPrefix.empty()) + header.sectionNames.push_back(sectionName); + else + header.sectionNames.push_back(header.identifierPrefix + "::" + sectionName); + } + + header.sectionNames = GetUniqueSectionNames(header.sectionNames); + } + + for (size_t i = 0; i < header.sections.size(); i++) + { + if (!header.sections[i].size) + continue; + + string type; + BNSectionSemantics semantics = DefaultSectionSemantics; + switch (header.sections[i].flags & 0xff) + { + case S_REGULAR: + if (header.sections[i].flags & S_ATTR_PURE_INSTRUCTIONS) + { + type = "PURE_CODE"; + semantics = ReadOnlyCodeSectionSemantics; + } + else if (header.sections[i].flags & S_ATTR_SOME_INSTRUCTIONS) + { + type = "CODE"; + semantics = ReadOnlyCodeSectionSemantics; + } + else + { + type = "REGULAR"; + } + break; + case S_ZEROFILL: + type = "ZEROFILL"; + semantics = ReadWriteDataSectionSemantics; + break; + case S_CSTRING_LITERALS: + type = "CSTRING_LITERALS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_4BYTE_LITERALS: + type = "4BYTE_LITERALS"; + break; + case S_8BYTE_LITERALS: + type = "8BYTE_LITERALS"; + break; + case S_LITERAL_POINTERS: + type = "LITERAL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_NON_LAZY_SYMBOL_POINTERS: + type = "NON_LAZY_SYMBOL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_LAZY_SYMBOL_POINTERS: + type = "LAZY_SYMBOL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_SYMBOL_STUBS: + type = "SYMBOL_STUBS"; + semantics = ReadOnlyCodeSectionSemantics; + break; + case S_MOD_INIT_FUNC_POINTERS: + type = "MOD_INIT_FUNC_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_MOD_TERM_FUNC_POINTERS: + type = "MOD_TERM_FUNC_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_COALESCED: + type = "COALESCED"; + break; + case S_GB_ZEROFILL: + type = "GB_ZEROFILL"; + semantics = ReadWriteDataSectionSemantics; + break; + case S_INTERPOSING: + type = "INTERPOSING"; + break; + case S_16BYTE_LITERALS: + type = "16BYTE_LITERALS"; + break; + case S_DTRACE_DOF: + type = "DTRACE_DOF"; + break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: + type = "LAZY_DYLIB_SYMBOL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_THREAD_LOCAL_REGULAR: + type = "THREAD_LOCAL_REGULAR"; + break; + case S_THREAD_LOCAL_ZEROFILL: + type = "THREAD_LOCAL_ZEROFILL"; + break; + case S_THREAD_LOCAL_VARIABLES: + type = "THREAD_LOCAL_VARIABLES"; + break; + case S_THREAD_LOCAL_VARIABLE_POINTERS: + type = "THREAD_LOCAL_VARIABLE_POINTERS"; + break; + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + type = "THREAD_LOCAL_INIT_FUNCTION_POINTERS"; + break; + default: + type = "UNKNOWN"; + break; + } + if (i >= header.sectionNames.size()) + break; + if (strncmp(header.sections[i].sectname, "__text", sizeof(header.sections[i].sectname)) == 0) + semantics = ReadOnlyCodeSectionSemantics; + if (strncmp(header.sections[i].sectname, "__const", sizeof(header.sections[i].sectname)) == 0) + semantics = ReadOnlyDataSectionSemantics; + if (strncmp(header.sections[i].sectname, "__data", sizeof(header.sections[i].sectname)) == 0) + semantics = ReadWriteDataSectionSemantics; + if (strncmp(header.sections[i].segname, "__DATA_CONST", sizeof(header.sections[i].segname)) == 0) + semantics = ReadOnlyDataSectionSemantics; + + AddAutoSection(header.sectionNames[i], header.sections[i].addr, header.sections[i].size, semantics, type, header.sections[i].align); + } + if (isMainHeader) + { + // Validate architecture + if (!m_arch) + { + // Parse only mode returns true, even if no arch support + if (m_parseOnly) + return true; + + bool is64Bit; + string archName = UniversalViewType::ArchitectureToString(m_archId, 0, is64Bit); + if (!archName.empty()) + { + #ifdef DEMO_VERSION + if ((archName == "arm64") /* MACHO_CPU_TYPE_ARM64 */ + || (archName == "arm64v8") + || (archName == "arm64e") + || (archName == "arm64_32") /* MACHO_CPU_TYPE_ARM64_32 */ + || (archName == "arm64_32v8") + || (archName == "ppc") /* MACHO_CPU_TYPE_POWERPC */ + || (archName == "ppc601") + || (archName == "ppc602") + || (archName == "ppc603") + || (archName == "ppc603e") + || (archName == "ppc603ev") + || (archName == "ppc604") + || (archName == "ppc604e") + || (archName == "ppc620") + || (archName == "ppc750") + || (archName == "ppc7400") + || (archName == "ppc7450") + || (archName == "ppc970") + || (archName == "ppc64") /* MACHO_CPU_TYPE_POWERPC64 */ + ) + { + m_logger->LogError( + "Binary Ninja free does not support Mach-O architecture '%s'. " + "Purchase Binary Ninja to unlock all features.", + archName.c_str()); + return false; + } + #endif + + m_logger->LogError( + "Mach-O architecture '%s' is not explicitly supported. Try 'Open with Options' to manually select " + "a compatible architecture.", + archName.c_str()); + } + else + { + m_logger->LogError( + "Mach-O architecture 0x%x is not explicitly supported. Try 'Open with Options' to manually select " + "a compatible architecture.", + m_archId); + } + + return false; + } + + // Apply architecture and platform + Ref<Platform> platform = m_plat ? m_plat : g_machoViewType->GetPlatform(0, m_arch); + if (!platform) + platform = m_arch->GetStandalonePlatform(); + + if (header.m_entryPoints.size() > 0) + platform = platform->GetAssociatedPlatformByAddress(header.m_entryPoints[0]); + + 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()); + + // Finished for parse only mode + if (m_parseOnly) + return true; + } + + if (m_parseOnly) + return true; + + BinaryReader reader(GetParentView()); + reader.SetEndianness(m_endian); + reader.SetVirtualBase(m_universalImageOffset); + BinaryReader virtualReader(this); + virtualReader.SetEndianness(m_endian); + + Ref<Platform> platform = m_plat ? m_plat : g_machoViewType->GetPlatform(0, m_arch); + + + // parse thread starts section if available + bool rebaseThreadStarts = false; + auto theadStartSection = GetSectionByName("__thread_starts"); + vector<uint32_t> threadStarts; + uint64_t stepMultiplier; + if (theadStartSection) + { + size_t count = theadStartSection->GetLength() / 4; + threadStarts.reserve(count); + virtualReader.Seek(theadStartSection->GetStart()); + stepMultiplier = virtualReader.Read32() & 0x1 ? 8 : 4; + for (uint32_t i = 1; i < count; i++) + threadStarts.push_back(virtualReader.Read32()); + + rebaseThreadStarts = true; + if (settings && settings->Contains("loader.macho.rebaseThreadStarts")) + rebaseThreadStarts = settings->Get<bool>("loader.macho.rebaseThreadStarts", this); + } + + if (rebaseThreadStarts) + RebaseThreadStarts(virtualReader, threadStarts, stepMultiplier); + + // Add module Init functions if they exist + size_t modInitFuncCnt = 0; + for (const auto& moduleInitSection : header.moduleInitSections) + { + // ignore mod_init functions that are rebased as part of thread starts + if (find(threadStarts.begin(), threadStarts.end(), moduleInitSection.offset) != threadStarts.end()) + continue; + + // The mod_init section contains a list of function pointers called at initialization + // if we don't have a defined entrypoint then use the first one in the list as the entrypoint + size_t i = 0; + reader.Seek(moduleInitSection.offset); + for (; i < (moduleInitSection.size / m_addressSize); i++) + { + uint64_t target = (m_addressSize == 4) ? reader.Read32() : reader.Read64(); + target += m_imageBaseAdjustment; + Ref<Platform> targetPlatform = platform->GetAssociatedPlatformByAddress(target); + DefineMachoSymbol(FunctionSymbol, "mod_init_func_" + to_string(modInitFuncCnt++), target, GlobalBinding, false); + if (m_header.ident.filetype == MH_FILESET) + { + // FIXME: This isn't a super robust way of detagging, + // should look into xnu source and the tools used to build this cache (if they're public) + // and see if anything better can be done + + // mask out top 8 bits + uint64_t tag = 0xFFFFFFFF00000000 & header.textBase; + // and combine them with bottom 8 of the original entry + target = tag | (target & 0xFFFFFFFF); + } + AddEntryPointForAnalysis(targetPlatform, target); + } + } + + if (isMainHeader) + { + vector<Ref<Metadata>> libraries; + vector<Ref<Metadata>> libraryFound; + for (auto& libName : header.dylibs) + { + if (!GetExternalLibrary(libName)) + { + AddExternalLibrary(libName, {}, true); + } + libraries.push_back(new Metadata(string(libName))); + Ref<TypeLibrary> typeLib = GetTypeLibrary(libName); + if (!typeLib) + { + vector<Ref<TypeLibrary>> typeLibs = platform->GetTypeLibrariesByName(libName); + if (typeLibs.size()) + { + typeLib = typeLibs[0]; + AddTypeLibrary(typeLib); + + m_logger->LogDebug("mach-o: adding type library for '%s': %s (%s)", + libName.c_str(), typeLib->GetName().c_str(), typeLib->GetGuid().c_str()); + } + } + + if (typeLib) + libraryFound.push_back(new Metadata(typeLib->GetName())); + else + libraryFound.push_back(new Metadata(string(""))); + } + StoreMetadata("Libraries", new Metadata(libraries), true); + StoreMetadata("LibraryFound", new Metadata(libraryFound), true); + } + + bool first = true; + for (auto entry : header.m_entryPoints) + { + if (m_header.ident.filetype == MH_FILESET) + { + // FIXME: This isn't a super robust way of detagging, + // should look into xnu source and the tools used to build this cache (if they're public) + // and see if anything better can be done + + // mask out top 8 bits + uint64_t tag = 0xFFFFFFFF00000000 & header.textBase; + // and combine them with bottom 8 of the original entry + entry = tag | (entry & 0xFFFFFFFF); + } + AddEntryPointForAnalysis(platform, entry); + if (first) + { + first = false; + DefineAutoSymbol(new Symbol(FunctionSymbol, "_start", entry)); + } + } + + vector<uint32_t> indirectSymbols; + try + { + // Handle indirect symbols + if (header.dysymtab.nindirectsyms) + { + indirectSymbols.resize(header.dysymtab.nindirectsyms); + reader.Seek(header.dysymtab.indirectsymoff); + reader.Read(&indirectSymbols[0], header.dysymtab.nindirectsyms * sizeof(uint32_t)); + } + } + catch (ReadException&) + { + m_logger->LogError("Failed to read indirect symbol data"); + } + + bool parseFunctionStarts = true; + if (settings && settings->Contains("loader.macho.processFunctionStarts")) + parseFunctionStarts = settings->Get<bool>("loader.macho.processFunctionStarts", this); + + if (parseFunctionStarts) + { + m_logger->LogDebug("Parsing function starts\n"); + if (header.functionStartsPresent) + ParseFunctionStarts(platform, header.textBase, header.functionStarts); + } + + BeginBulkModifySymbols(); + m_symbolQueue = new SymbolQueue(); + + try + { + // Add functions for all function symbols + m_logger->LogDebug("Parsing symbol table\n"); + ParseSymbolTable(reader, header, header.symtab, indirectSymbols); + } + catch (std::exception&) + { + m_logger->LogError("Failed to parse symbol table!"); + } + + m_symbolQueue->Process(); + delete m_symbolQueue; + m_symbolQueue = nullptr; + + EndBulkModifySymbols(); + + auto relocationHandler = m_arch->GetRelocationHandler("Mach-O"); + if (relocationHandler) + { + // FIXME: this if statement block really needs to be a function + try + { + // For executables the relocations are attached to each of the sections + // In libraries these are zeroed out and collected in the dysymtab + vector<BNRelocationInfo> infoList; + for (auto& section : header.sections) + { + if (section.nreloc == 0) + continue; + + char sectionName[17]; + memcpy(sectionName, section.sectname, sizeof(section.sectname)); + sectionName[16] = 0; + + m_logger->LogDebug("Relocations for section %s", sectionName); + auto sec = GetSectionByName(sectionName); + if (!sec) + { + m_logger->LogError("Can't find section for %s", sectionName); + continue; + } + for (size_t i = 0; i < section.nreloc; i++) + { + relocation_info info; + reader.Seek(section.reloff + (i * sizeof(relocation_info))); + reader.Read(&info, sizeof(info)); + BNRelocationInfo result; + memset(&result, 0, sizeof(result)); + if (ParseRelocationEntry(info, sec->GetStart(), result)) + infoList.push_back(result); + } + } + + if (relocationHandler->GetRelocationInfo(this, m_arch, infoList)) + { + for (auto& reloc: infoList) + { + if (reloc.symbolIndex >= m_symbols.size()) + continue; + + // retrieve first symbol that is not a symbol relocation + auto symbols = GetSymbolsByName(m_symbols[reloc.symbolIndex]); + for (const auto& symbol : symbols) + { + if (symbol->GetAddress() == reloc.address) + continue; + DefineRelocation(m_arch, reloc, symbol, reloc.address); + break; + } + } + } + infoList.clear(); + + // Handle local relocations for dynamic libraries + for (size_t i = 0; i < header.dysymtab.nlocrel; i++) + { + relocation_info info; + reader.Seek(header.dysymtab.locreloff + (i * sizeof(relocation_info))); + reader.Read(&info, sizeof(info)); + BNRelocationInfo result; + memset(&result, 0, sizeof(result)); + if (ParseRelocationEntry(info, header.relocationBase, result)) + infoList.push_back(result); + } + + if (relocationHandler->GetRelocationInfo(this, m_arch, infoList)) + { + for (auto& reloc: infoList) + { + // TODO will matter once rebasing lands + if (!reloc.external) + continue; + + if (reloc.symbolIndex >= m_symbols.size()) + continue; + + // retrieve first symbol that is not a symbol relocation + auto symbols = GetSymbolsByName(m_symbols[reloc.symbolIndex]); + for (const auto& symbol : symbols) + { + if (symbol->GetAddress() == reloc.address) + continue; + DefineRelocation(m_arch, reloc, symbol, reloc.address); + break; + } + } + } + infoList.clear(); + + // Handle external relocations for dynamic libraries + for (size_t i = 0; i < header.dysymtab.nextrel; i++) + { + relocation_info info; + reader.Seek(header.dysymtab.extreloff + (i * sizeof(relocation_info))); + reader.Read(&info, sizeof(info)); + BNRelocationInfo result; + memset(&result, 0, sizeof(result)); + if (ParseRelocationEntry(info, header.relocationBase, result)) + infoList.push_back(result); + } + + if (relocationHandler->GetRelocationInfo(this, m_arch, infoList)) + { + for (auto& reloc: infoList) + { + // TODO will matter once rebasing lands + if (!reloc.external) + continue; + + if (reloc.symbolIndex >= m_symbols.size()) + continue; + + // retrieve first symbol that is not a symbol relocation + auto symbols = GetSymbolsByName(m_symbols[reloc.symbolIndex]); + for (const auto& symbol : symbols) + { + if (symbol->GetAddress() == reloc.address) + continue; + DefineRelocation(m_arch, reloc, symbol, reloc.address); + break; + } + } + } + infoList.clear(); + } + catch (ReadException&) + { + m_logger->LogError("Failed to read relocation data"); + } + } + + vector<std::tuple<uint64_t, string>> machoHeaderStarts; + if (isMainHeader) { + machoHeaderStarts.emplace_back(header.textBase, ""); + if (header.textBase != preferredImageBase) + machoHeaderStarts.emplace_back(preferredImageBase, preferredImageBaseDesc); + } + else { + machoHeaderStarts.emplace_back(preferredImageBase, preferredImageBaseDesc); + } + + // Apply Mach-O header types + for (auto [imageBase, imageDesc] : machoHeaderStarts) + { + string errorMsg; + mach_header_64 mappedIdent; + uint64_t loadCommandOffset; + loadCommandOffset = g_machoViewType->ParseHeaders(this, imageBase, mappedIdent, nullptr, nullptr, errorMsg); + if (!loadCommandOffset) + continue; + + DefineDataVariable(imageBase, Type::NamedType(this, m_typeNames.headerQualName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__macho_header" + imageDesc, imageBase, LocalBinding)); + + try + { + virtualReader.Seek(imageBase + loadCommandOffset); + size_t sectionNum = 0; + for (size_t i = 0; i < mappedIdent.ncmds; i++) + { + load_command load; + uint64_t curOffset = virtualReader.GetOffset(); + load.cmd = virtualReader.Read32(); + load.cmdsize = virtualReader.Read32(); + uint64_t nextOffset = curOffset + load.cmdsize; + switch (load.cmd) + { + case LC_SEGMENT: + { + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.loadSegmentCommandQualName)); + virtualReader.SeekRelative(5 * 8); + size_t numSections = virtualReader.Read32(); + virtualReader.SeekRelative(4); + for (size_t j = 0; j < numSections; j++) + { + DefineDataVariable(virtualReader.GetOffset(), Type::NamedType(this, m_typeNames.sectionQualName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__macho_section" + imageDesc + "_[" + to_string(sectionNum++) + "]", virtualReader.GetOffset(), LocalBinding)); + virtualReader.SeekRelative((8 * 8) + 4); + } + break; + } + case LC_SEGMENT_64: + { + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.loadSegment64CommandQualName)); + virtualReader.SeekRelative(7 * 8); + size_t numSections = virtualReader.Read32(); + virtualReader.SeekRelative(4); + for (size_t j = 0; j < numSections; j++) + { + DefineDataVariable(virtualReader.GetOffset(), Type::NamedType(this, m_typeNames.section64QualName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__macho_section_64" + imageDesc + "_[" + to_string(sectionNum++) + "]", virtualReader.GetOffset(), LocalBinding)); + virtualReader.SeekRelative(10 * 8); + } + break; + } + case LC_SYMTAB: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.symtabQualName)); + break; + case LC_DYSYMTAB: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.dynsymtabQualName)); + break; + case LC_UUID: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.uuidQualName)); + break; + case LC_ID_DYLIB: + case LC_LOAD_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.dylibCommandQualName)); + if (load.cmdsize-24 <= 150) + DefineDataVariable(curOffset + 24, Type::ArrayType(Type::IntegerType(1, true), load.cmdsize-24)); + break; + case LC_CODE_SIGNATURE: + case LC_SEGMENT_SPLIT_INFO: + case LC_FUNCTION_STARTS: + case LC_DATA_IN_CODE: + case LC_DYLIB_CODE_SIGN_DRS: + case LC_DYLD_EXPORTS_TRIE: + case LC_DYLD_CHAINED_FIXUPS: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.linkeditDataQualName)); + break; + case LC_ENCRYPTION_INFO: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.encryptionInfoQualName)); + break; + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.versionMinQualName)); + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.dyldInfoQualName)); + break; + case LC_FILESET_ENTRY: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.filesetEntryCommandQualName)); + if (load.cmdsize-0x20 <= 150) + DefineDataVariable(curOffset + 0x20, Type::ArrayType(Type::IntegerType(1, true), load.cmdsize-0x20)); + break; + default: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.loadCommandQualName)); + break; + } + + DefineAutoSymbol(new Symbol(DataSymbol, "__macho_load_command" + imageDesc + "_[" + to_string(i) + "]", curOffset, LocalBinding)); + virtualReader.Seek(nextOffset); + } + } + catch (ReadException&) + { + LogError("Error when applying Mach-O header types at %" PRIx64, imageBase); + } + } + + + return true; +} + + +Ref<Symbol> MachoView::DefineMachoSymbol( + BNSymbolType type, const string& name, uint64_t addr, BNSymbolBinding binding, bool deferred) +{ + Ref<Type> symbolTypeRef; + + // If name is empty, symbol is not valid + if (name.size() == 0) + return nullptr; + + if (type != ExternalSymbol) + { + // Don't create symbols that are present in the database snapshot now. If it is not a + // deferrable symbol, it may be used in relocations, so don't ignore those. + if (m_backedByDatabase && deferred) + return nullptr; + + // Ensure symbol is within the executable + bool ok = false; + for (auto& segment : m_header.segments) + { + if ((segment.initprot == MACHO_VM_PROT_NONE) || (!segment.vmsize)) + continue; + + if ((addr >= segment.vmaddr) && + (addr < (segment.vmaddr + segment.vmsize))) + { + ok = true; + break; + } + } + if (!ok) + return nullptr; + } + + if ((type == ExternalSymbol) || (type == ImportAddressSymbol) || (type == ImportedDataSymbol)) + { + QualifiedName n(name); + // TODO + Ref<TypeLibrary> appliedLib = nullptr; + symbolTypeRef = ImportTypeLibraryObject(appliedLib, n); + if (symbolTypeRef) + { + m_logger->LogDebug("mach-o: type Library '%s' found hit for '%s'", appliedLib->GetName().c_str(), name.c_str()); + RecordImportedObjectLibrary(GetDefaultPlatform(), addr, appliedLib, n); + } + } + + auto process = [=]() { + // 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; + + NameSpace nameSpace = GetInternalNameSpace(); + if (type == ExternalSymbol) + { + nameSpace = GetExternalNameSpace(); + } + + // Try to demangle any C++ symbols + string shortName = rawName; + string fullName = rawName; + Ref<Type> typeRef = symbolTypeRef; + + QualifiedName varName; + if (m_arch) + { + if (IsGNU3MangledString(rawName)) + { + Ref<Type> demangledType; + if (DemangleGNU3(m_arch, rawName, demangledType, varName, m_simplifyTemplates)) + { + shortName = varName.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()); + } + } + } + + return std::pair<Ref<Symbol>, Ref<Type>>( + new Symbol(type, shortName, fullName, rawName, addr, binding, nameSpace), typeRef); + }; + + if (deferred) + { + m_symbolQueue->Append(process, [this](Symbol* symbol, Type* type) { + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), symbol, type); + }); + return nullptr; + } + + auto result = process(); + return DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), result.first, result.second); +} + +bool MachoView::GetSegmentPermissions(MachOHeader& header, uint64_t address, uint32_t &flags) +{ + for (auto& segment : header.segments) + { + if ((segment.initprot == MACHO_VM_PROT_NONE) || (!segment.vmsize)) + continue; + + if (address >= segment.vmaddr && address < (segment.vmaddr + segment.vmsize)) + { + flags = segment.flags; + return true; + } + } + flags = 0; + return false; +} + +bool MachoView::GetSectionPermissions(MachOHeader& header, uint64_t address, uint32_t &flags) +{ + for (auto& section : header.sections) + { + if (!section.size) + continue; + + if (address >= section.addr && address < (section.addr + section.size)) + { + flags = section.flags; + return true; + } + } + flags = 0; + return false; +} + +void MachoView::ParseExportTrie(BinaryReader& reader, linkedit_data_command exportTrie) +{ + try { + uint32_t endGuard = exportTrie.datasize; + DataBuffer buffer = GetParentView()->ReadBuffer(m_universalImageOffset + exportTrie.dataoff, exportTrie.datasize); + + ReadExportNode(GetStart(), buffer, "", 0, endGuard); + } + catch (ReadException&) + { + m_logger->LogError("Error while parsing Export Trie"); + } +} + +void MachoView::ReadExportNode(uint64_t viewStart, DataBuffer& buffer, const std::string& currentText, size_t cursor, uint32_t endGuard) +{ + if (cursor > endGuard) + throw ReadException(); + + uint64_t terminalSize = readValidULEB128(buffer, cursor); + uint64_t childOffset = cursor + terminalSize; + if (terminalSize != 0) { + uint64_t imageOffset = 0; + uint64_t flags = readValidULEB128(buffer, cursor); + if (!(flags & EXPORT_SYMBOL_FLAGS_REEXPORT)) + { + imageOffset = readValidULEB128(buffer, cursor); + auto symbolType = GetAnalysisFunctionsForAddress(viewStart + imageOffset).size() ? FunctionSymbol : DataSymbol; + DefineMachoSymbol(symbolType, currentText, imageOffset + viewStart, GlobalBinding, true); + } + } + cursor = childOffset; + uint8_t childCount = buffer[cursor]; + cursor++; + if (cursor > endGuard) + throw ReadException(); + for (uint8_t i = 0; i < childCount; ++i) + { + std::string childText; + while (buffer[cursor] != 0 & cursor <= endGuard) + childText.push_back(buffer[cursor++]); + cursor++; + if (cursor > endGuard) + throw ReadException(); + auto next = readValidULEB128(buffer, cursor); + if (next == 0) + throw ReadException(); + ReadExportNode(viewStart, buffer, currentText + childText, next, endGuard); + } +} + +void MachoView::ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNSymbolType incomingType, uint32_t tableOffset, + uint32_t tableSize, BNSymbolBinding binding) +{ + try { + reader.Seek(tableOffset); + auto table = reader.Read(tableSize); + + BNSymbolType symtype = incomingType; + // uint64_t ordinal = 0; + // int64_t addend = 0; + uint64_t segmentIndex = 0; + uint64_t address = 0; + uint64_t offset = 0; + char* name = NULL; + // uint32_t flags = 0; + uint32_t type = 0; + size_t i = 0; + //bool done = false; + while (i < tableSize) + { + uint8_t opcode = table[i] & BindOpcodeMask; + uint8_t imm = table[i] & BindImmediateMask; + i++; + switch (opcode) + { + case BindOpcodeDone: + // ordinal = 0; + // addend = 0; + segmentIndex = 0; + address = 0; + offset = 0; + name = NULL; + // flags = 0; + type = 0; + symtype = incomingType; + break; + case BindOpcodeSetDylibOrdinalImmediate: /* ordinal = imm; */ break; + case BindOpcodeSetDylibOrdinalULEB: /* ordinal = */ readLEB128(table, tableSize, i); break; + case BindOpcodeSetDylibSpecialImmediate: /* ordinal = -imm; */ break; + case BindOpcodeSetSymbolTrailingFlagsImmediate: + /* flags = imm; */ + name = (char*)&table[i]; + while (i < tableSize && table[i++] != '\0') + {;} + break; + case BindOpcodeSetTypeImmediate: + type = imm; + if (type == 1) + symtype = ImportedDataSymbol; + break; + case BindOpcodeSetAddendSLEB: /* addend = */ readSLEB128(table, tableSize, i); break; + case BindOpcodeSetSegmentAndOffsetULEB: + segmentIndex = imm; + offset = readLEB128(table, tableSize, i); + if (segmentIndex >= header.segments.size()) + throw MachoFormatException(); + address = header.segments[segmentIndex].vmaddr + offset; + break; + case BindOpcodeAddAddressULEB: + address += readLEB128(table, tableSize, i); + break; + case BindOpcodeDoBind: + if (name == NULL) + throw MachoFormatException(); + + DefineMachoSymbol(symtype, string(name), address, binding, true); + address += m_addressSize; + break; + case BindOpcodeDoBindAddAddressULEB: + if (name == NULL) + throw MachoFormatException(); + + DefineMachoSymbol(symtype, string(name), address, binding, true); + address += m_addressSize; + address += readLEB128(table, tableSize, i); + break; + case BindOpcodeDoBindAddAddressImmediateScaled: + if (name == NULL) + throw MachoFormatException(); + + DefineMachoSymbol(symtype, string(name), address, binding, true); + address += m_addressSize; + address += (imm * m_addressSize); + break; + case BindOpcodeDoBindULEBTimesSkippingULEB: + { + if (name == NULL) + throw MachoFormatException(); + + uint64_t count = readLEB128(table, tableSize, i); + uint64_t skip = readLEB128(table, tableSize, i); + for (; count > 0; count--) + { + address += skip + m_addressSize; + DefineMachoSymbol(symtype, string(name), address, binding, true); + } + break; + } + // New Threaded Binding Opcode, not much info out about it. It encodes a ULEB after the op/imm. + case 0xD0: readLEB128(table, tableSize, i); break; + default: + break; + } + } + } + catch (ReadException&) + {;} +} + + +void MachoView::ParseSymbolTable(BinaryReader& reader, MachOHeader& header, const symtab_command& symtab, const vector<uint32_t>& indirectSymbols) +{ + if (header.ident.filetype == MH_DSYM) + { + m_logger->LogDebug("Skipping symbol parsing"); + return; + } + + try + { + //First parse the imports + if (header.dyldInfoPresent) + { + m_logger->LogDebug("Bind symbols"); + ParseDynamicTable(reader, header, ImportAddressSymbol, header.dyldInfo.bind_off, + header.dyldInfo.bind_size, GlobalBinding); + m_logger->LogDebug("Weak symbols"); + ParseDynamicTable(reader, header, ImportAddressSymbol, header.dyldInfo.weak_bind_off, + header.dyldInfo.weak_bind_size, WeakBinding); + m_logger->LogDebug("Lazy symbols"); + ParseDynamicTable(reader, header, ImportAddressSymbol, header.dyldInfo.lazy_bind_off, + header.dyldInfo.lazy_bind_size, GlobalBinding); + } + if (header.chainedFixupsPresent) + { + m_logger->LogDebug("Chained Fixups"); + ParseChainedFixups(header.chainedFixups); + } + if (header.exportTriePresent && header.isMainHeader) + ParseExportTrie(reader, header.exportTrie); + + //Then process the symtab + if (header.stringListSize == 0) + return; + reader.Seek(symtab.symoff); + + unordered_map<size_t, vector<std::pair<section_64*, size_t>>> stubSymbols; + for (auto& symbolStubs : header.symbolStubSections) + { + if (!symbolStubs.reserved2) + continue; + + size_t needed = symbolStubs.size / symbolStubs.reserved2; + for (size_t j = 0; (j < needed) && ((j + symbolStubs.reserved1) < indirectSymbols.size()); j++) + { + // Not exactly sure what the following 3 variables are for but this is the check done in + // Apple's source code + uint8_t sectionType = (symbolStubs.flags & SECTION_TYPE); + bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (symbolStubs.flags & S_ATTR_SELF_MODIFYING_CODE) && + (symbolStubs.reserved2 == 5) && (header.ident.cputype == MACHO_CPU_TYPE_X86); + auto symNum = indirectSymbols[j + symbolStubs.reserved1]; + if (symNum == INDIRECT_SYMBOL_ABS) + continue; + else if (symNum == INDIRECT_SYMBOL_LOCAL) + continue; + + stubSymbols[symNum].push_back( + std::pair<section_64*, size_t>(&symbolStubs, j)); + } + } + + unordered_map<size_t, vector<std::pair<section_64*, size_t>>> pointerSymbols; + for (auto& symbolPointerSection : header.symbolPointerSections) + { + // Not exactly sure what the following 3 variables are for but this is the check done in + // Apple's source code + uint8_t sectionType = (symbolPointerSection.flags & SECTION_TYPE); + bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (symbolPointerSection.flags & S_ATTR_SELF_MODIFYING_CODE) && + (symbolPointerSection.reserved2 == 5) && (header.ident.cputype == MACHO_CPU_TYPE_X86); + size_t needed = symbolPointerSection.size / m_addressSize; + for (size_t j = 0; (j < needed) && ((j + symbolPointerSection.reserved1) < indirectSymbols.size()); j++) + { + auto symNum = indirectSymbols[j + symbolPointerSection.reserved1]; + if (symNum == INDIRECT_SYMBOL_ABS) + continue; + else if (symNum == INDIRECT_SYMBOL_LOCAL) + continue; + pointerSymbols[symNum].push_back( + std::pair<section_64*, size_t>(&symbolPointerSection, j)); + } + } + + nlist_64 sym; + memset(&sym, 0, sizeof(sym)); + for (size_t i = 0; i < symtab.nsyms; i++) + { + sym.n_strx = reader.Read32(); + sym.n_type = reader.Read8(); + sym.n_sect = reader.Read8(); + sym.n_desc = reader.Read16(); + sym.n_value = (m_addressSize == 4) ? reader.Read32() : reader.Read64(); + if (sym.n_value) + sym.n_value += m_imageBaseAdjustment; + if (sym.n_strx >= symtab.strsize || ((sym.n_type & N_TYPE) == N_INDR)) + continue; + + string symbol((char*)header.stringList->GetDataAt(sym.n_strx)); + m_symbols.push_back(symbol); + //otool ignores symbols that end with ".o", startwith "ltmp" or are "gcc_compiled." so do we + if (symbol == "gcc_compiled." || + (symbol.length() > 2 && symbol.substr(symbol.length()-2, 2) == ".o") || + (symbol.length() > 4 && symbol.substr(0, 4) == "ltmp")) + { + m_logger->LogDebug("Skipping symbol: %s.", symbol.c_str()); + continue; + } + //N_TYPE is only set when N_SECT is the integer count of the section the symbol is in + //Note that we don't currently validate the section number, but just use the address. + //Very curious to see how other tools handle it. + BNSymbolType type = DataSymbol; + uint32_t flags; + if ((sym.n_type & N_TYPE) == N_SECT && sym.n_sect > 0 && (size_t)(sym.n_sect - 1) < header.sections.size()) + { + if (!GetSectionPermissions(header, sym.n_value, flags)) + { + if ((sym.n_type & N_EXT)) + { + if (!GetSegmentPermissions(header, sym.n_value, flags)) + { + m_logger->LogDebug("No valid segment for symbol %s. value:%" PRIx64, symbol.c_str(), sym.n_value); + continue; + } + } + else + { + m_logger->LogDebug("No valid section for symbol %s. value:%" PRIx64, symbol.c_str(), sym.n_value); + continue; + } + } + } + else if ((sym.n_type & N_TYPE) == N_ABS) + { + //N_ABS symbols do not have a section. Fall back to segment permissions. + if (!GetSegmentPermissions(header, sym.n_value, flags)) + { + m_logger->LogDebug("No valid segment for symbol %s. value:%" PRIx64, symbol.c_str(), sym.n_value); + continue; + } + } + else if ((sym.n_type & N_EXT)) + { + type = ExternalSymbol; + } + else + continue; + + if (type != ExternalSymbol) + { + if ((flags & S_ATTR_PURE_INSTRUCTIONS) == S_ATTR_PURE_INSTRUCTIONS || + (flags & S_ATTR_SOME_INSTRUCTIONS) == S_ATTR_SOME_INSTRUCTIONS) + type = FunctionSymbol; + else + type = DataSymbol; + } + if((sym.n_desc & N_ARM_THUMB_DEF) == N_ARM_THUMB_DEF) + sym.n_value++; + + auto stubSymbolIter = stubSymbols.find(i); + auto pointerSymbolIter = pointerSymbols.find(i); + bool deferred = stubSymbolIter == stubSymbols.end() && pointerSymbolIter == pointerSymbols.end(); + + Ref<Symbol> symbolObj; + if(header.dysymtab.nlocalsym && i >= header.dysymtab.ilocalsym && i < header.dysymtab.ilocalsym + header.dysymtab.nlocalsym) + { + symbolObj = DefineMachoSymbol(type, symbol, sym.n_value, LocalBinding, deferred); + } + else if (header.dysymtab.nextdefsym && i >= header.dysymtab.iextdefsym && i < header.dysymtab.iextdefsym + header.dysymtab.nextdefsym) + { + symbolObj = DefineMachoSymbol(type, symbol, sym.n_value, GlobalBinding, deferred); + } + else if (header.dysymtab.nundefsym && i >= header.dysymtab.iundefsym && i < header.dysymtab.iundefsym + header.dysymtab.nundefsym) + { + symbolObj = DefineMachoSymbol(type, symbol, sym.n_value, GlobalBinding, deferred); + } + else + { + symbolObj = DefineMachoSymbol(type, symbol, sym.n_value, GlobalBinding, deferred); + } + + if (!symbolObj) + { + continue; + } + + if (stubSymbolIter != stubSymbols.end()) + { + for (auto& j : stubSymbolIter->second) + { + // m_logger->LogError("STUB [%d] %llx - %s", i, j.first->addr + (j * j.first->reserved2), symbol.c_str()); + BNRelocationInfo info; + memset(&info, 0, sizeof(info)); + info.nativeType = -1; + info.size = j.first->reserved2; + info.pcRelative = true; + DefineRelocation(m_arch, info, symbolObj, j.first->addr + (j.second * j.first->reserved2)); + DefineMachoSymbol(ImportedFunctionSymbol, symbol, j.first->addr + (j.second * j.first->reserved2), + GlobalBinding, true); + } + } + + if (pointerSymbolIter != pointerSymbols.end()) + { + for (auto& j : pointerSymbolIter->second) + { + // m_logger->LogError("POINTER [%d] %llx - %s", i, j.first->addr + (j.second * m_addressSize), + // symbol.c_str()); + BNRelocationInfo info; + memset(&info, 0, sizeof(info)); + info.nativeType = BINARYNINJA_MANUAL_RELOCATION; + info.size = m_addressSize; + info.pcRelative = false; + DefineRelocation(m_arch, info, symbolObj, j.first->addr + (j.second * m_addressSize)); + DefineMachoSymbol( + ImportAddressSymbol, symbol, j.first->addr + (j.second * m_addressSize), GlobalBinding, true); + } + } + } + } + catch (ReadException&) + {;} + return; +} + + + + +void MachoView::ParseChainedFixups(linkedit_data_command chainedFixups) +{ + if (!chainedFixups.dataoff) + return; + + m_logger->LogDebug("Processing Chained Fixups"); + + // Dummy relocation + BNRelocationInfo reloc; + memset(&reloc, 0, sizeof(BNRelocationInfo)); + reloc.type = StandardRelocationType; + reloc.size = m_addressSize; + reloc.nativeType = BINARYNINJA_MANUAL_RELOCATION; + + bool processBinds = true; + + BinaryReader parentReader(GetParentView()); + BinaryReader mappedReader(this); + + try { + dyld_chained_fixups_header fixupsHeader {}; + uint64_t fixupHeaderAddress = m_universalImageOffset + chainedFixups.dataoff; + parentReader.Seek(fixupHeaderAddress); + fixupsHeader.fixups_version = parentReader.Read32(); + fixupsHeader.starts_offset = parentReader.Read32(); + fixupsHeader.imports_offset = parentReader.Read32(); + fixupsHeader.symbols_offset = parentReader.Read32(); + fixupsHeader.imports_count = parentReader.Read32(); + fixupsHeader.imports_format = parentReader.Read32(); + fixupsHeader.symbols_format = parentReader.Read32(); + + m_logger->LogDebug("Chained Fixups: Header @ %llx // Fixups version %lx", fixupHeaderAddress, fixupsHeader.fixups_version); + + size_t importsAddress = fixupHeaderAddress + fixupsHeader.imports_offset; + size_t importTableSize = sizeof(dyld_chained_import) * fixupsHeader.imports_count; + + if (fixupsHeader.fixups_version > 0) + { + m_logger->LogError("Chained Fixup parsing failed. Unknown Fixups Version"); + return; + } + + if (importTableSize > chainedFixups.datasize) + { + m_logger->LogError("Chained Fixup parsing failed. Binary is malformed"); + return; + } + + size_t symbolsAddress = fixupHeaderAddress + fixupsHeader.symbols_offset; + + // Pre-load the import table. We may re-access the same ordinal multiple times, this will be faster. + std::vector<import_entry> importTable; + parentReader.Seek(importsAddress); + + switch (fixupsHeader.imports_format) + { + case DYLD_CHAINED_IMPORT: + { + for (size_t i = 0; i < fixupsHeader.imports_count; i++) + { + uint32_t importEntry = parentReader.Read32(); + uint64_t nextEntryAddress = parentReader.GetOffset(); + + dyld_chained_import import = *(reinterpret_cast<dyld_chained_import*>(&importEntry)); + + import_entry entry; + + entry.lib_ordinal = (uint64_t)import.lib_ordinal; + entry.addend = 0; + entry.weak = (import.weak_import == 1); + + size_t symNameAddr = symbolsAddress + import.name_offset; + + parentReader.Seek(symNameAddr); + try { + string symbolName = parentReader.ReadCString(); + entry.name = symbolName; + } + catch (ReadException& ex) + { + entry.name = ""; + } + + importTable.push_back(entry); + parentReader.Seek(nextEntryAddress); + } + break; + } + case DYLD_CHAINED_IMPORT_ADDEND: + case DYLD_CHAINED_IMPORT_ADDEND64: + default: + { + m_logger->LogWarn("Chained Fixups: Unknown import binding format"); + processBinds = false; // We can still handle rebases. + break; + } + } + + m_logger->LogDebug("Chained Fixups: %llx import table entries", importTable.size()); + + uint64_t fixupStartsAddress = fixupHeaderAddress + fixupsHeader.starts_offset; + parentReader.Seek(fixupStartsAddress); + dyld_chained_starts_in_image segs {}; + segs.seg_count = parentReader.Read32(); + vector<uint32_t> segInfoOffsets {}; + for (size_t i = 0; i < segs.seg_count; i++) + { + segInfoOffsets.push_back(parentReader.Read32()); + } + for (auto offset : segInfoOffsets) + { + if (!offset) + continue; + + dyld_chained_starts_in_segment starts {}; + uint64_t startsAddr = fixupStartsAddress + offset; + parentReader.Seek(startsAddr); + starts.size = parentReader.Read32(); + starts.page_size = parentReader.Read16(); + starts.pointer_format = parentReader.Read16(); + starts.segment_offset = parentReader.Read64(); + starts.max_valid_pointer = parentReader.Read32(); + starts.page_count = parentReader.Read16(); + + uint8_t strideSize; + ChainedFixupPointerGeneric format; + + // Firmware formats will require digging up whatever place they're being used and reversing it. + // They are not handled by dyld. + switch (starts.pointer_format) { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + strideSize = 8; + format = GenericArm64eFixupFormat; + break; + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + strideSize = 4; + format = GenericArm64eFixupFormat; + break; + // case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: Unsupported. + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + strideSize = 4; + format = Generic64FixupFormat; + break; + case DYLD_CHAINED_PTR_32: + case DYLD_CHAINED_PTR_32_CACHE: + strideSize = 4; + format = Generic32FixupFormat; + break; + case DYLD_CHAINED_PTR_32_FIRMWARE: + strideSize = 4; + format = Firmware32FixupFormat; + break; + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + strideSize = 1; + format = Generic64FixupFormat; + break; + default: + { + m_logger->LogError("Chained Fixups: Unknown or unsupported pointer format %d, " + "unable to process chains for segment at @llx", starts.pointer_format, starts.segment_offset); + continue; + } + } + + uint16_t fmt = starts.pointer_format; + m_logger->LogDebug("Chained Fixups: Segment start @ %llx, fmt %d", starts.segment_offset, fmt); + + uint64_t pageStartsTableStartAddress = parentReader.GetOffset(); + vector<vector<uint16_t>> pageStartOffsets {}; + for (size_t i = 0; i < starts.page_count; i++) + { + // On armv7, Chained pointers here can have multiple starts. + // And if so, there's another table *overlapping* the table we're currently reading. + // dyld handles this through 'overflow indexing' + // This is technically supported on other archs however is not (currently) used. + parentReader.Seek(pageStartsTableStartAddress + (sizeof(uint16_t) * i)); + uint16_t start = parentReader.Read16(); + if ((start & DYLD_CHAINED_PTR_START_MULTI) && (start != DYLD_CHAINED_PTR_START_NONE)) + { + uint64_t overflowIndex = start & ~DYLD_CHAINED_PTR_START_MULTI; + vector<uint16_t> pageStartSubStarts; + parentReader.Seek(pageStartsTableStartAddress + (overflowIndex * sizeof(uint16_t))); + bool done = false; + while (!done) + { + uint16_t subPageStart = parentReader.Read16(); + if ((subPageStart & DYLD_CHAINED_PTR_START_LAST) == 0) + { + pageStartSubStarts.push_back(subPageStart); + } + else + { + pageStartSubStarts.push_back(subPageStart & ~DYLD_CHAINED_PTR_START_LAST); + done = true; + } + } + pageStartOffsets.push_back(pageStartSubStarts); + } + else + { + pageStartOffsets.push_back({start}); + } + } + + int i = -1; + for (auto pageStarts : pageStartOffsets) + { + i++; + uint64_t pageAddress = m_universalImageOffset + starts.segment_offset + (i * starts.page_size); + for (uint16_t start : pageStarts) + { + if (start == DYLD_CHAINED_PTR_START_NONE) + continue; + + uint64_t chainEntryAddress = pageAddress + start; + + bool fixupsDone = false; + + while (!fixupsDone) + { + ChainedFixupPointer pointer; + parentReader.Seek(chainEntryAddress); + mappedReader.Seek(chainEntryAddress - m_universalImageOffset + GetStart()); + if (format == Generic32FixupFormat || format == Firmware32FixupFormat) + pointer.raw32 = (uint32_t)(uintptr_t)mappedReader.Read32(); + else + pointer.raw64 = (uintptr_t)mappedReader.Read64(); + + bool bind = false; + uint64_t nextEntryStrideCount; + + switch (format) + { + case Generic32FixupFormat: + bind = pointer.generic32.bind.bind; + nextEntryStrideCount = pointer.generic32.rebase.next; + break; + case Generic64FixupFormat: + bind = pointer.generic64.bind.bind; + nextEntryStrideCount = pointer.generic64.rebase.next; + break; + case GenericArm64eFixupFormat: + bind = pointer.arm64e.bind.bind; + nextEntryStrideCount = pointer.arm64e.rebase.next; + break; + case Firmware32FixupFormat: + nextEntryStrideCount = pointer.firmware32.next; + bind = false; + break; + } + + m_logger->LogTrace("Chained Fixups: @ 0x%llx ( 0x%llx ) - %d 0x%llx", chainEntryAddress, + GetStart() + (chainEntryAddress - m_universalImageOffset), + bind, nextEntryStrideCount); + + if (bind && processBinds) + { + uint64_t ordinal; + + switch (starts.pointer_format) + { + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: + ordinal = pointer.generic64.bind.ordinal; + break; + // case DYLD_CHAINED_PTR_ARM64E_OFFSET: ; old _KERNEL name. + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + if (pointer.arm64e.bind.auth) + ordinal = starts.pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24 + ? pointer.arm64e.authBind24.ordinal : pointer.arm64e.authBind.ordinal; + else + ordinal = starts.pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24 + ? pointer.arm64e.bind24.ordinal : pointer.arm64e.bind.ordinal; + break; + case DYLD_CHAINED_PTR_32: + ordinal = pointer.generic32.bind.ordinal; + break; + default: + m_logger->LogWarn("Chained Fixups: Unknown Bind Pointer Format at %llx", + GetStart() + (chainEntryAddress - m_universalImageOffset)); + + chainEntryAddress += (nextEntryStrideCount * strideSize); + if (chainEntryAddress > pageAddress + starts.page_size) + { + m_logger->LogDebug("Chained Fixups: Pointer at %llx left page", + GetStart() + ((chainEntryAddress - (nextEntryStrideCount * strideSize))) - m_universalImageOffset); + fixupsDone = true; + } + if (nextEntryStrideCount == 0) + fixupsDone = true; + + continue; + } + + if (ordinal < importTable.size()) + { + import_entry entry = importTable.at(ordinal); + uint64_t targetAddress = GetStart() + (chainEntryAddress - m_universalImageOffset); + + if (!entry.name.empty()) + { + reloc.address = targetAddress; + DefineRelocation(m_arch, reloc, 0, reloc.address); + DefineMachoSymbol(ImportedDataSymbol, entry.name, + targetAddress, + entry.weak ? WeakBinding : GlobalBinding, true); + } + else + { + m_logger->LogWarn("Chained Fixups: Import Table entry %llx has no symbol; " + "Unable to bind item at %llx", ordinal, targetAddress); + } + } + } + else if (!bind) + { + uint64_t entryOffset; + switch (starts.pointer_format) + { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + { + if (pointer.arm64e.bind.auth) + entryOffset = pointer.arm64e.authRebase.target; + else + entryOffset = pointer.arm64e.rebase.target; + + if ( starts.pointer_format != DYLD_CHAINED_PTR_ARM64E || pointer.arm64e.bind.auth) + entryOffset += GetStart(); + + break; + } + case DYLD_CHAINED_PTR_64: + entryOffset = pointer.generic64.rebase.target; + break; + case DYLD_CHAINED_PTR_64_OFFSET: + entryOffset = pointer.generic64.rebase.target + GetStart(); + break; + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + entryOffset = pointer.kernel64.target; + break; + case DYLD_CHAINED_PTR_32: + case DYLD_CHAINED_PTR_32_CACHE: + entryOffset = pointer.generic32.rebase.target; + break; + case DYLD_CHAINED_PTR_32_FIRMWARE: + entryOffset = pointer.firmware32.target; + break; + } + + reloc.address = GetStart() + (chainEntryAddress - m_universalImageOffset); + DefineRelocation(m_arch, reloc, entryOffset, reloc.address); + } + + chainEntryAddress += (nextEntryStrideCount * strideSize); + + if (chainEntryAddress > pageAddress + starts.page_size) + { + // Something is seriously wrong here. likely malformed binary, or our parsing failed elsewhere. + // This will log the pointer in mapped memory. + m_logger->LogError("Chained Fixups: Pointer at %llx left page", + GetStart() + ((chainEntryAddress - (nextEntryStrideCount * strideSize))) - m_universalImageOffset); + fixupsDone = true; + } + + if (nextEntryStrideCount == 0) + fixupsDone = true; + } + } + } + } + } + catch (ReadException&) + { + m_logger->LogError("Chained Fixup parsing failed"); + } +} + + +uint64_t MachoView::PerformGetEntryPoint() const +{ + if (m_header.m_entryPoints.size() == 0) + return 0; + + return m_header.m_entryPoints[0]; +} + + +BNEndianness MachoView::PerformGetDefaultEndianness() const +{ + return LittleEndian; +} + + +bool MachoView::PerformIsRelocatable() const +{ + return m_relocatable; +} + + +size_t MachoView::PerformGetAddressSize() const +{ + return m_addressSize; +} + + +MachoViewType::MachoViewType(): BinaryViewType("Mach-O", "Mach-O") +{ + m_logger = LogRegistry::CreateLogger("BinaryView.MachoViewType"); +} + + +Ref<BinaryView> MachoViewType::Create(BinaryView* data) +{ + try + { + return new MachoView("Mach-O", data); + } + catch (std::exception& e) + { + m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + + +Ref<BinaryView> MachoViewType::Parse(BinaryView* data) +{ + try + { + return new MachoView("Mach-O", data, true); + } + catch (std::exception& e) + { + m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + + +bool MachoViewType::IsTypeValidForData(BinaryView* data) +{ + if (!data) + return false; + + DataBuffer sig = data->ReadBuffer(data->GetStart(), 4); + if (sig.GetLength() != 4) + return false; + + uint32_t magic = *(uint32_t*)sig.GetData(); + if (magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == MH_MAGIC || magic == MH_MAGIC_64) + return true; + magic = ToBE32(magic); + if ((magic == FAT_MAGIC) || (magic == FAT_MAGIC_64)) + return true; + + return data->GetLoadSettings(GetName()) ? true : false; +} + + +uint64_t MachoViewType::ParseHeaders(BinaryView* data, uint64_t imageOffset, mach_header_64& ident, Ref<Architecture>* arch, Ref<Platform>* plat, string& errorMsg) +{ + DataBuffer sig = data->ReadBuffer(imageOffset, 4); + uint32_t magic = *(uint32_t*)sig.GetData(); + if ((sig.GetLength() != 4) || !(magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == MH_MAGIC || magic == MH_MAGIC_64)) + { + errorMsg = "invalid signature"; + return 0; + } + + BinaryReader reader(data); + reader.SetVirtualBase(imageOffset); + ident.magic = reader.Read32(); + + BNEndianness endianness; + if (ident.magic == MH_MAGIC || ident.magic == MH_MAGIC_64) + endianness = LittleEndian; + else if (ident.magic == MH_CIGAM || ident.magic == MH_CIGAM_64) + endianness = BigEndian; + else + { + errorMsg = "invalid file class"; + return 0; + } + + reader.SetEndianness(endianness); + ident.cputype = reader.Read32(); + ident.cpusubtype = reader.Read32(); + ident.filetype = reader.Read32(); + ident.ncmds = reader.Read32(); + ident.sizeofcmds = reader.Read32(); + ident.flags = reader.Read32(); + + if ((ident.cputype & MachOABIMask) == MachOABI64) // address size == 8 + { + ident.reserved = reader.Read32(); + } + if (!(ident.filetype == MH_OBJECT || + ident.filetype == MH_EXECUTE || + ident.filetype == MH_DYLIB || + ident.filetype == MH_DYLINKER || + ident.filetype == MH_BUNDLE || + ident.filetype == MH_KEXT_BUNDLE || + ident.filetype == MH_CORE || + ident.filetype == MH_PRELOAD || + ident.filetype == MH_DSYM || + ident.filetype == MH_FILESET)) + { + m_logger->LogError("Unhandled Macho file class: 0x%x", ident.filetype); + errorMsg = "invalid file class"; + return 0; + } + + map<string, Ref<Metadata>> metadataMap = { + {"cputype", new Metadata((uint64_t) ident.cputype)}, + {"cpusubtype", new Metadata((uint64_t) ident.cpusubtype)}, + {"flags", new Metadata((uint64_t) ident.flags)}, + }; + + Ref<Metadata> metadata = new Metadata(metadataMap); + + // retrieve architecture + // FIXME: Architecture registration methods should perhaps be virtual and take the raw data, or some additional opaque information. + Ref<Platform> recognizedPlatform = g_machoViewType->RecognizePlatform(ident.cputype, endianness, data, metadata); + + if (recognizedPlatform) + { + if (plat) + *plat = recognizedPlatform; + if (arch) + *arch = recognizedPlatform->GetArchitecture(); + } + else if (arch) + { + *arch = g_machoViewType->GetArchitecture(ident.cputype, endianness); + } + + return reader.GetOffset(); +} + + +Ref<Settings> MachoViewType::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.macho.processFunctionStarts", + R"({ + "title" : "Process Mach-O Function Starts Table", + "type" : "boolean", + "default" : true, + "description" : "Add function starts sourced from the Function Starts table to the core for analysis." + })"); + + if (viewRef->GetSectionByName("__thread_starts")) + { + settings->RegisterSetting("loader.macho.rebaseThreadStarts", + R"({ + "title" : "Rebase Mach-O Kernelcache Thread Starts", + "type" : "boolean", + "default" : true, + "description" : "Add relocation entries to rebase chained thread starts located in the __thread_starts section." + })"); + } + + // Merge existing load settings if they exist. This allows for the selection of a specific object file from a Mach-O Universal file. + // The 'Universal' BinaryViewType generates a schema with 'loader.universal.architectures'. This schema contains an appropriate + // 'Mach-O' load schema for selecting a specific object file. The embedded schema contains 'loader.macho.universalImageOffset'. + Ref<Settings> loadSettings = viewRef->GetLoadSettings(GetName()); + if (loadSettings && !loadSettings->IsEmpty()) + settings->DeserializeSchema(loadSettings->SerializeSchema()); + + return settings; +} + + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + +#ifdef DEMO_VERSION + bool MachoPluginInit() +#else + BINARYNINJAPLUGIN bool CorePluginInit() +#endif + { + InitMachoViewType(); + InitFatMachoViewType(); + InitUniversalViewType(); + return true; + } +} diff --git a/view/macho/machoview.h b/view/macho/machoview.h new file mode 100644 index 00000000..20d2ea78 --- /dev/null +++ b/view/macho/machoview.h @@ -0,0 +1,1375 @@ +#pragma once + +#include <exception> +#include <vector> +#include <string.h> + +#include "binaryninjaapi.h" + +//These are laready defined in one of the osx headers we want to override +#undef CPU_SUBTYPE_INTEL +#undef CPU_SUBTYPE_I386_ALL +#undef CPU_SUBTYPE_POWERPC_ALL +#undef LC_MAIN +#undef LC_SOURCE_VERSION +#undef LC_DYLIB_CODE_SIGN_DRS +#undef LC_DYLD_INFO_ONLY + +#define MH_MAGIC 0xfeedface +#define MH_MAGIC_64 0xfeedfacf +#define MH_CIGAM 0xcefaedfe +#define MH_CIGAM_64 0xcffaedfe +#define FAT_MAGIC 0xcafebabe +#define FAT_MAGIC_64 0xcafebabf + +typedef int32_t cpu_type_t; +typedef int32_t cpu_subtype_t; +typedef int vm_prot_t; + +//Cpu types +#define MACHO_CPU_ARCH_ABI64 0x01000000 +#define MACHO_CPU_ARCH_ABI64_32 0x02000000 + +#define MACHO_CPU_TYPE_ANY ((cpu_type_t) -1) +#define MACHO_CPU_TYPE_VAX ((cpu_type_t) 1) +#define MACHO_CPU_TYPE_MC680x0 ((cpu_type_t) 6) +#define MACHO_CPU_TYPE_X86 ((cpu_type_t) 7) +#define MACHO_CPU_TYPE_X86_64 (MACHO_CPU_TYPE_X86 | MACHO_CPU_ARCH_ABI64) +#define MACHO_CPU_TYPE_MIPS ((cpu_type_t) 8) +#define MACHO_CPU_TYPE_MC98000 ((cpu_type_t) 10) +#define MACHO_CPU_TYPE_HPPA ((cpu_type_t) 11) +#define MACHO_CPU_TYPE_ARM ((cpu_type_t) 12) +#define MACHO_CPU_TYPE_ARM64 (MACHO_CPU_TYPE_ARM | MACHO_CPU_ARCH_ABI64) +#define MACHO_CPU_TYPE_ARM64_32 (MACHO_CPU_TYPE_ARM | MACHO_CPU_ARCH_ABI64_32) +#define MACHO_CPU_TYPE_MC88000 ((cpu_type_t) 13) +#define MACHO_CPU_TYPE_SPARC ((cpu_type_t) 14) +#define MACHO_CPU_TYPE_I860 ((cpu_type_t) 15) +#define MACHO_CPU_TYPE_ALPHA ((cpu_type_t) 16) +#define MACHO_CPU_TYPE_POWERPC ((cpu_type_t) 18) +#define MACHO_CPU_TYPE_POWERPC64 (MACHO_CPU_TYPE_POWERPC | MACHO_CPU_ARCH_ABI64) + +//Cpu subtype capability bits +#define MACHO_CPU_SUBTYPE_MASK ((cpu_subtype_t)0xff000000) +#define MACHO_CPU_SUBTYPE_LIB64 ((cpu_subtype_t)0x80000000) + +//Cpu subtypes +#define MACHO_CPU_SUBTYPE_INTEL(f,m) ((cpu_subtype_t) (f)+((m)<<4)) +#define MACHO_CPU_SUBTYPE_I386_ALL MACHO_CPU_SUBTYPE_INTEL(3,0) +#define MACHO_CPU_SUBTYPE_X86_ALL ((cpu_subtype_t) 3) +#define MACHO_CPU_SUBTYPE_X86_ARCH1 ((cpu_subtype_t) 4) +#define MACHO_CPU_SUBTYPE_X86_64_ALL ((cpu_subtype_t) 3) +#define MACHO_CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) + +#define MACHO_CPU_SUBTYPE_ARM_ALL ((cpu_subtype_t) 0) +#define MACHO_CPU_SUBTYPE_ARM_V4T ((cpu_subtype_t) 5) +#define MACHO_CPU_SUBTYPE_ARM_V6 ((cpu_subtype_t) 6) +#define MACHO_CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#define MACHO_CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#define MACHO_CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#define MACHO_CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) +#define MACHO_CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) +#define MACHO_CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) +#define MACHO_CPU_SUBTYPE_ARM_V8 ((cpu_subtype_t) 13) +#define MACHO_CPU_SUBTYPE_ARM_V6M ((cpu_subtype_t) 14) +#define MACHO_CPU_SUBTYPE_ARM_V7M ((cpu_subtype_t) 15) +#define MACHO_CPU_SUBTYPE_ARM_V7EM ((cpu_subtype_t) 16) + +#define MACHO_CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) +#define MACHO_CPU_SUBTYPE_ARM64_V8 ((cpu_subtype_t) 1) +#define MACHO_CPU_SUBTYPE_ARM64E ((cpu_subtype_t) 2) + +#define MACHO_CPU_SUBTYPE_ARM64_32_ALL ((cpu_subtype_t) 0) +#define MACHO_CPU_SUBTYPE_ARM64_32_V8 ((cpu_subtype_t) 1) + +#define MACHO_CPU_SUBTYPE_POWERPC_ALL ((cpu_subtype_t) 0) +#define MACHO_CPU_SUBTYPE_POWERPC_601 ((cpu_subtype_t) 1) +#define MACHO_CPU_SUBTYPE_POWERPC_602 ((cpu_subtype_t) 2) +#define MACHO_CPU_SUBTYPE_POWERPC_603 ((cpu_subtype_t) 3) +#define MACHO_CPU_SUBTYPE_POWERPC_603e ((cpu_subtype_t) 4) +#define MACHO_CPU_SUBTYPE_POWERPC_603ev ((cpu_subtype_t) 5) +#define MACHO_CPU_SUBTYPE_POWERPC_604 ((cpu_subtype_t) 6) +#define MACHO_CPU_SUBTYPE_POWERPC_604e ((cpu_subtype_t) 7) +#define MACHO_CPU_SUBTYPE_POWERPC_620 ((cpu_subtype_t) 8) +#define MACHO_CPU_SUBTYPE_POWERPC_750 ((cpu_subtype_t) 9) +#define MACHO_CPU_SUBTYPE_POWERPC_7400 ((cpu_subtype_t) 10) +#define MACHO_CPU_SUBTYPE_POWERPC_7450 ((cpu_subtype_t) 11) +#define MACHO_CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100) + +//Page protections +#define MACHO_VM_PROT_NONE ((vm_prot_t) 0x00) +#define MACHO_VM_PROT_READ ((vm_prot_t) 0x01) +#define MACHO_VM_PROT_WRITE ((vm_prot_t) 0x02) +#define MACHO_VM_PROT_EXECUTE ((vm_prot_t) 0x04) +#define MACHO_VM_PROT_DEFAULT (VM_PROT_READ|VM_PROT_WRITE) +#define MACHO_VM_PROT_ALL (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE) +#define MACHO_VM_PROT_NO_CHANGE ((vm_prot_t) 0x08) +#define MACHO_VM_PROT_COPY ((vm_prot_t) 0x10) +#define MACHO_VM_PROT_WANTS_COPY ((vm_prot_t) 0x10) + +//Segment flags +#define SG_HIGHVM 0x1 +#define SG_FVMLIB 0x2 +#define SG_NORELOC 0x4 +#define SG_PROTECTED_VERSION_1 0x8 + +//Section flags +#define S_ATTR_SOME_INSTRUCTIONS 0x00000400 +#define S_ATTR_PURE_INSTRUCTIONS 0x80000000 // section contains only true machine instructions +#define S_ATTR_NO_TOC 0x40000000 // section contains coalesced symbols that are not to be in a ranlib table of contents +#define S_ATTR_STRIP_STATIC_SYMS 0x20000000 // ok to strip static symbols in this section in files with the MH_DYLDLINK flag +#define S_ATTR_NO_DEAD_STRIP 0x10000000 // no dead stripping +#define S_ATTR_LIVE_SUPPORT 0x08000000 // blocks are live if they reference live blocks +#define S_ATTR_SELF_MODIFYING_CODE 0x04000000 // Used with i386 code stubs written on by dyld + +#define S_REGULAR 0x0 +#define S_ZEROFILL 0x1 +#define S_CSTRING_LITERALS 0x2 +#define S_4BYTE_LITERALS 0x3 +#define S_8BYTE_LITERALS 0x4 +#define S_LITERAL_POINTERS 0x5 +#define S_NON_LAZY_SYMBOL_POINTERS 0x6 +#define S_LAZY_SYMBOL_POINTERS 0x7 +#define S_SYMBOL_STUBS 0x8 +#define S_MOD_INIT_FUNC_POINTERS 0x9 +#define S_MOD_TERM_FUNC_POINTERS 0xa +#define S_COALESCED 0xb +#define S_GB_ZEROFILL 0xc +#define S_INTERPOSING 0xd +#define S_16BYTE_LITERALS 0xe +#define S_DTRACE_DOF 0xf +#define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 +#define S_THREAD_LOCAL_REGULAR 0x11 +#define S_THREAD_LOCAL_ZEROFILL 0x12 +#define S_THREAD_LOCAL_VARIABLES 0x13 +#define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 +#define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 + +//Mach-O Commands +#define LC_REQ_DYLD 0x80000000 +#define LC_SEGMENT 0x1 +#define LC_SYMTAB 0x2 +#define LC_SYMSEG 0x3 +#define LC_THREAD 0x4 +#define LC_UNIXTHREAD 0x5 +#define LC_LOADFVMLIB 0x6 +#define LC_IDFVMLIB 0x7 +#define LC_IDENT 0x8 +#define LC_FVMFILE 0x9 +#define LC_PREPAGE 0xa +#define LC_DYSYMTAB 0xb +#define LC_LOAD_DYLIB 0xc +#define LC_ID_DYLIB 0xd +#define LC_LOAD_DYLINKER 0xe +#define LC_ID_DYLINKER 0xf +#define LC_PREBOUND_DYLIB 0x10 +#define LC_ROUTINES 0x11 +#define LC_SUB_FRAMEWORK 0x12 +#define LC_SUB_UMBRELLA 0x13 +#define LC_SUB_CLIENT 0x14 +#define LC_SUB_LIBRARY 0x15 +#define LC_TWOLEVEL_HINTS 0x16 +#define LC_PREBIND_CKSUM 0x17 +#define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD) +#define LC_SEGMENT_64 0x19 +#define LC_ROUTINES_64 0x1a +#define LC_UUID 0x1b +#define LC_RPATH (0x1c | LC_REQ_DYLD) +#define LC_CODE_SIGNATURE 0x1d +#define LC_SEGMENT_SPLIT_INFO 0x1e +#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) +#define LC_LAZY_LOAD_DYLIB 0x20 +#define LC_ENCRYPTION_INFO 0x21 +#define LC_DYLD_INFO 0x22 +#define LC_DYLD_INFO_ONLY (0x22 | LC_REQ_DYLD) +#define LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) +#define LC_VERSION_MIN_MACOSX 0x24 +#define LC_VERSION_MIN_IPHONEOS 0x25 +#define LC_FUNCTION_STARTS 0x26 +#define LC_DYLD_ENVIRONMENT 0x27 +#define LC_MAIN (0x28 | LC_REQ_DYLD) +#define LC_DATA_IN_CODE 0x29 +#define LC_SOURCE_VERSION 0x2a +#define LC_DYLIB_CODE_SIGN_DRS 0x2b +#define _LC_ENCRYPTION_INFO_64 0x2c /* FIXME: warning defined in .../SDKs/MacOSX10.15.sdk/usr/include/mach-o/loader.h */ +#define _LC_LINKER_OPTION 0x2d /* FIXME: warning defined in .../SDKs/MacOSX10.15.sdk/usr/include/mach-o/loader.h */ +#define _LC_LINKER_OPTIMIZATION_HINT 0x2e /* FIXME: warning defined in .../SDKs/MacOSX10.15.sdk/usr/include/mach-o/loader.h */ +#define _LC_VERSION_MIN_TVOS 0x2f /* FIXME: warning defined in .../SDKs/MacOSX10.15.sdk/usr/include/mach-o/loader.h */ +#define LC_VERSION_MIN_WATCHOS 0x30 +#define LC_NOTE 0x31 +#define LC_BUILD_VERSION 0x32 +#define LC_DYLD_EXPORTS_TRIE (0x33 | LC_REQ_DYLD) +#define LC_DYLD_CHAINED_FIXUPS (0x34 | LC_REQ_DYLD) +#define LC_FILESET_ENTRY (0x35 | LC_REQ_DYLD) + +//Mach-O File types +#define MH_OBJECT 0x1 +#define MH_EXECUTE 0x2 +#define MH_FVMLIB 0x3 +#define MH_CORE 0x4 +#define MH_PRELOAD 0x5 +#define MH_DYLIB 0x6 +#define MH_DYLINKER 0x7 +#define MH_BUNDLE 0x8 +#define MH_DYLIB_STUB 0x9 +#define MH_DSYM 0xa +#define MH_KEXT_BUNDLE 0xb +#define MH_FILESET 0xc + +#define MH_NOUNDEFS 0x1 +#define MH_INCRLINK 0x2 +#define MH_DYLDLINK 0x4 +#define MH_BINDATLOAD 0x8 +#define MH_PREBOUND 0x10 +#define MH_SPLIT_SEGS 0x20 +#define MH_LAZY_INIT 0x40 +#define MH_TWOLEVEL 0x80 +#define MH_FORCE_FLAT 0x100 +#define MH_NOMULTIDEFS 0x200 +#define MH_NOFIXPREBINDING 0x400 +#define MH_PREBINDABLE 0x800 +#define MH_ALLMODSBOUND 0x1000 +#define MH_SUBSECTIONS_VIA_SYMBOLS 0x2000 +#define MH_CANONICAL 0x4000 +#define MH_WEAK_DEFINES 0x8000 +#define MH_BINDS_TO_WEAK 0x10000 +#define MH_ALLOW_STACK_EXECUTION 0x20000 +#define MH_ROOT_SAFE 0x40000 +#define MH_SETUID_SAFE 0x80000 +#define MH_NO_REEXPORTED_DYLIBS 0x100000 +#define MH_PIE 0x200000 +#define MH_DEAD_STRIPPABLE_DYLIB 0x400000 +#define MH_HAS_TLV_DESCRIPTORS 0x800000 +#define MH_NO_HEAP_EXECUTION 0x1000000 +#define _MH_APP_EXTENSION_SAFE 0x2000000 /* FIXME: warning defined in .../SDKs/MacOSX10.15.sdk/usr/include/mach-o/loader.h */ +#define _MH_NLIST_OUTOFSYNC_WITH_DYLDINFO 0x04000000u +#define _MH_SIM_SUPPORT 0x08000000u +#define _MH_DYLIB_IN_CACHE 0x80000000u + +//Segment Names +#define SEG_PAGEZERO "__PAGEZERO" +#define SEG_TEXT "__TEXT" +#define SECT_TEXT "__text" +#define SECT_FVMLIB_INIT0 "__fvmlib_init0" +#define SECT_FVMLIB_INIT1 "__fvmlib_init1" +#define SEG_DATA "__DATA" +#define SECT_DATA "__data" +#define SECT_BSS "__bss" +#define SECT_COMMON "__common" +#define SEG_OBJC "__OBJC" +#define SECT_OBJC_SYMBOLS "__symbol_table" +#define SECT_OBJC_MODULES "__module_info" +#define SECT_OBJC_STRINGS "__selector_strs" +#define SECT_OBJC_REFS "__selector_refs" +#define SEG_ICON "__ICON" +#define SECT_ICON_HEADER "__header" +#define SECT_ICON_TIFF "__tiff" +#define SEG_LINKEDIT "__LINKEDIT" +#define SEG_UNIXSTACK "__UNIXSTACK" +#define SEG_IMPORT "__IMPORT" + +//Symbol Types (N_TYPE) +#define N_UNDF 0x0 +#define N_ABS 0x2 +#define N_SECT 0xe +#define N_PBUD 0xc +#define N_INDR 0xa + +#define N_ARM_THUMB_DEF 0x0008 + +/* + * An indirect symbol table entry is simply a 32bit index into the symbol table + * to the symbol that the pointer or stub is referring to. Unless it is for a + * non-lazy symbol pointer section for a defined symbol which strip(1) as + * removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the + * symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that. + */ +#define INDIRECT_SYMBOL_ABS 0x40000000 +#define INDIRECT_SYMBOL_LOCAL 0x80000000 + +#define SECTION_TYPE 0x000000ff /* 256 section types */ +#define SECTION_ATTRIBUTES 0xffffff00 /* 24 section attributes */ + +#define BINARYNINJA_MANUAL_RELOCATION ((uint64_t)-2) + +enum MachoRelocationType +{ + GENERIC_RELOC_VANILLA = 0, + GENERIC_RELOC_PAIR = 1, + GENERIC_RELOC_SECTDIFF = 2, + GENERIC_RELOC_PB_LA_PTR = 3, + GENERIC_RELOC_LOCAL_SECTDIFF = 4, + GENERIC_RELOC_TLV = 5 +}; + + +namespace BinaryNinja +{ + class MachoFormatException: public std::exception + { + std::string m_message; + + public: + MachoFormatException(const std::string& msg = "invalid format"): m_message(msg) {} + virtual const char* what() const NOEXCEPT { return m_message.c_str(); } + }; + + struct FatHeader { + uint32_t magic; + uint32_t nfat_arch; + }; + + struct FatArch32 { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t offset; + uint32_t size; + uint32_t align; + }; + + struct FatArch64 { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint64_t offset; + uint64_t size; + uint32_t align; + uint32_t reserved; + }; + + struct mach_header { + uint32_t magic; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + }; + + struct mach_header_64 { + uint32_t magic; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + uint32_t reserved; + }; + + struct load_command { + uint32_t cmd; + uint32_t cmdsize; + }; + + struct segment_command { + uint32_t cmd; + uint32_t cmdsize; + char segname[16]; + uint32_t vmaddr; + uint32_t vmsize; + uint32_t fileoff; + uint32_t filesize; + vm_prot_t maxprot; + vm_prot_t initprot; + uint32_t nsects; + uint32_t flags; + }; + + struct section { // for 32-bit architectures + char sectname[16]; // name of this section + char segname[16]; // segment this section goes in + uint32_t addr; // memory address of this section + uint32_t size; // size in bytes of this section + uint32_t offset; // file offset of this section + uint32_t align; // section alignment (power of 2) + uint32_t reloff; // file offset of relocation entries + uint32_t nreloc; // number of relocation entries + uint32_t flags; // flags (section type and attributes) + uint32_t reserved1; // reserved (for offset or index) + uint32_t reserved2; // reserved (for count or sizeof) + }; + + struct section_64 { // for 64-bit architectures + char sectname[16]; // name of this section + char segname[16]; // segment this section goes in + uint64_t addr; // memory address of this section + uint64_t size; // size in bytes of this section + uint32_t offset; // file offset of this section + uint32_t align; // section alignment (power of 2) + uint32_t reloff; // file offset of relocation entries + uint32_t nreloc; // number of relocation entries + uint32_t flags; // flags (section type and attributes) + uint32_t reserved1; // reserved (for offset or index) + uint32_t reserved2; // reserved (for count or sizeof) + uint32_t reserved3; // reserved + }; + + struct segment_command_64 { + uint32_t cmd; + uint32_t cmdsize; + char segname[16]; + uint64_t vmaddr; + uint64_t vmsize; + uint64_t fileoff; + uint64_t filesize; + vm_prot_t maxprot; + vm_prot_t initprot; + uint32_t nsects; + uint32_t flags; + }; + + struct uuid_command { + uint32_t cmd; + uint32_t cmdsize; + char uuid[16]; + }; + + struct fvmlib_command { + uint32_t cmd; + uint32_t cmdsize; + char segname[16]; + uint32_t minor_version; + uint32_t header_addr; + }; + + struct dylib { + uint32_t name; + uint32_t timestamp; + uint32_t current_version; + uint32_t compatibility_version; + }; + + struct dylib_command { + uint32_t cmd; + uint32_t cmdsize; + struct dylib dylib; + }; + + struct sub_framework_command { + uint32_t cmd; // LC_SUB_FRAMEWORK + uint32_t cmdsize; // includes umbrella string + uint32_t umbrella; // the umbrella framework name + }; + + struct sub_client_command { + uint32_t cmd; // LC_SUB_CLIENT + uint32_t cmdsize; // includes client string + uint32_t client; // offset to the string + }; + + struct sub_umbrella_command { + uint32_t cmd; // LC_SUB_UMBRELLA + uint32_t cmdsize; // includes sub_umbrella string + uint32_t sub_umbrella; // the sub_umbrella framework name + }; + + struct sub_library_command { + uint32_t cmd; // LC_SUB_LIBRARY + uint32_t cmdsize; // includes sub_library string + uint32_t sub_library; // the sub_library name + }; + + struct prebound_dylib_command { + uint32_t cmd; // LC_PREBOUND_DYLIB + uint32_t cmdsize; // includes strings + uint32_t name; // library's path name + uint32_t nmodules; // number of modules in library + uint32_t linked_modules; // bit vector of linked modules + }; + + struct dylinker_command { + uint32_t cmd; // LC_ID_DYLINKER or LC_LOAD_DYLINKER + uint32_t cmdsize; // includes pathname string + uint32_t name; // dynamic linker's path name + }; + + struct x86_thread_state64 { + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint64_t cs; + uint64_t fs; + uint64_t gs; + }; + + struct x86_thread_state32 { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ss; + uint32_t eflags; + uint32_t eip; + uint32_t cs; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; + }; + + struct armv7_thread_state { + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t r7; + uint32_t r8; + uint32_t r9; + uint32_t r10; + uint32_t r11; + uint32_t r12; + uint32_t r13; + uint32_t r14; //SP + uint32_t r15; //PC + }; + + struct arm_thread_state64 { + uint64_t x[29]; // GPR x0-x28 + uint64_t fp; // x29 + uint64_t lr; // x30 + uint64_t sp; // x31 + uint64_t pc; // Program Counter + uint32_t cpsr; // Current program status register + }; + + struct ppc_thread_state { + uint32_t srr0; //Machine state register (PC) + uint32_t srr1; + uint32_t r0; + uint32_t r1; //Stack pointer + uint32_t r2; + uint32_t r3; + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t r7; + uint32_t r8; + uint32_t r9; + uint32_t r10; + uint32_t r11; + uint32_t r12; + uint32_t r13; + uint32_t r14; + uint32_t r15; + uint32_t r16; + uint32_t r17; + uint32_t r18; + uint32_t r19; + uint32_t r20; + uint32_t r21; + uint32_t r22; + uint32_t r23; + uint32_t r24; + uint32_t r25; + uint32_t r26; + uint32_t r27; + uint32_t r28; + uint32_t r29; + uint32_t r30; + uint32_t r31; + uint32_t cr; + uint32_t xer; + uint32_t lr; + uint32_t ctr; + uint32_t mq; + uint32_t vrsave; + }; + + #pragma pack(push, 4) + struct ppc_thread_state64 { + uint64_t srr0; //Machine state register (PC) + uint64_t srr1; + uint64_t r0; //Stack pointer + uint64_t r1; + uint64_t r2; + uint64_t r3; + uint64_t r4; + uint64_t r5; + uint64_t r6; + uint64_t r7; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t r16; + uint64_t r17; + uint64_t r18; + uint64_t r19; + uint64_t r20; + uint64_t r21; + uint64_t r22; + uint64_t r23; + uint64_t r24; + uint64_t r25; + uint64_t r26; + uint64_t r27; + uint64_t r28; + uint64_t r29; + uint64_t r30; + uint64_t r31; + uint32_t cr; // Condition register + uint64_t xer; // User's integer exception register + uint64_t lr; // Link register + uint64_t ctr; // Count register + uint32_t vrsave; // Vector Save Register + }; + #pragma pack(pop) + + struct thread_command { + uint32_t cmd; // LC_THREAD or LC_UNIXTHREAD + uint32_t cmdsize; // total size of this command + uint32_t flavor; // flavor of thread state + uint32_t count; //count of longs in thread state + union { + x86_thread_state64 statex64; + x86_thread_state32 statex86; + armv7_thread_state statearmv7; + arm_thread_state64 stateaarch64; + ppc_thread_state stateppc; + ppc_thread_state64 stateppc64; + }; + }; + + struct linkedit_data_command { + uint32_t cmd; + uint32_t cmdsize; + uint32_t dataoff; + uint32_t datasize; + }; + + + enum MachOArchitecture { + MachOABIMask = 0xff000000, + MachOABI64 = 0x01000000, // 64 bit ABI + MachOABI6432 = 0x02000000, // "ABI for 64-bit hardware with 32-bit types; LP32" + + // Constants for the cputype field. + MachOx86 = 7, + MachOx64 = MachOx86 | MachOABI64, + MachOArm = 0xc, + MachOAarch64 = MachOABI64 | MachOArm, + MachOAarch6432 = MachOABI6432 | MachOArm, + MachOSPARC = 0xe, + MachOPPC = 0x12, + MachOPPC64 = MachOABI64 | MachOPPC, + }; + + enum BindOpcode { + BindOpcodeMask = 0xF0u, // BIND_OPCODE_MASK + BindImmediateMask = 0x0Fu, // BIND_IMMEDIATE_MASK + BindOpcodeDone = 0x00u, // BIND_OPCODE_DONE + BindOpcodeSetDylibOrdinalImmediate = 0x10u, // BIND_OPCODE_SET_DYLIB_ORDINAL_IMM + BindOpcodeSetDylibOrdinalULEB = 0x20u, // BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB + BindOpcodeSetDylibSpecialImmediate = 0x30u, // BIND_OPCODE_SET_DYLIB_SPECIAL_IMM + BindOpcodeSetSymbolTrailingFlagsImmediate = 0x40u, // BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM + BindOpcodeSetTypeImmediate = 0x50u, // BIND_OPCODE_SET_TYPE_IMM + BindOpcodeSetAddendSLEB = 0x60u, // BIND_OPCODE_SET_ADDEND_SLEB + BindOpcodeSetSegmentAndOffsetULEB = 0x70u, // BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + BindOpcodeAddAddressULEB = 0x80u, // BIND_OPCODE_ADD_ADDR_ULEB + BindOpcodeDoBind = 0x90u, // BIND_OPCODE_DO_BIND + BindOpcodeDoBindAddAddressULEB = 0xA0u, // BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB + BindOpcodeDoBindAddAddressImmediateScaled = 0xB0u, // BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED + BindOpcodeDoBindULEBTimesSkippingULEB = 0xC0u, // BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB + }; + + enum X86ThreadFlavor { + X86_THREAD_STATE32 = 0x1, + X86_FLOAT_STATE32 = 0x2, + X86_EXCEPTION_STATE32 = 0x3, + X86_THREAD_STATE64 = 0x4, + X86_FLOAT_STATE64 = 0x5, + X86_EXCEPTION_STATE64 = 0x6, + X86_THREAD_STATE = 0x7, + X86_FLOAT_STATE = 0x8, + X86_EXCEPTION_STATE = 0x9, + X86_DEBUG_STATE32 = 0xA, + X86_DEBUG_STATE64 = 0xB, + X86_DEBUG_STATE = 0xC, + _THREAD_STATE_NONE = 0xD + }; + + enum PPCThreadFlavor { + PPC_THREAD_STATE = 0x1, + PPC_FLOAT_STATE = 0x2, + PPC_EXCEPTION_STATE = 0x3, + PPC_VECTOR_STATE = 0x4, + PPC_THREAD_STATE64 = 0x5, + PPC_EXCEPTION_STATE64 = 0x6 + }; + + enum ARMThreadFlavor + { + _ARM_THREAD_STATE = 1, + _ARM_VFP_STATE = 2, + _ARM_EXCEPTION_STATE = 3, + _ARM_DEBUG_STATE = 4, /* pre-armv8 */ + _ARM_THREAD_STATE64 = 6, + _ARM_EXCEPTION_STATE64 = 7 + }; + + struct routines_command { // for 32-bit architectures + uint32_t cmd; // LC_ROUTINES + uint32_t cmdsize; // total size of this command + uint32_t init_address; // address of initialization routine + uint32_t init_module; // index into the module table that the init routine is defined in + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + uint32_t reserved4; + uint32_t reserved5; + uint32_t reserved6; + }; + + struct routines_command_64 { // for 64-bit architectures + uint32_t cmd; // LC_ROUTINES_64 + uint32_t cmdsize; // total size of this command + uint64_t init_address; // address of initialization routine + uint64_t init_module; // index into the module table that the init routine is defined in + uint64_t reserved1; + uint64_t reserved2; + uint64_t reserved3; + uint64_t reserved4; + uint64_t reserved5; + uint64_t reserved6; + }; + + struct symtab_command { + uint32_t cmd; // LC_SYMTAB + uint32_t cmdsize; // sizeof(struct symtab_command) + uint32_t symoff; // symbol table offset + uint32_t nsyms; // number of symbol table entries + uint32_t stroff; // string table offset + uint32_t strsize; // string table size in bytes + }; + + // Lots of good information on how to use dysymtab for relocations + // https://opensource.apple.com/source/cctools/cctools-795/include/mach-o/loader.h + struct dysymtab_command { + uint32_t cmd; // LC_DYSYMTAB + uint32_t cmdsize; // sizeof(struct dysymtab_command) + uint32_t ilocalsym; // index to local symbols + uint32_t nlocalsym; // number of local symbols + uint32_t iextdefsym; // index to externally defined symbols + uint32_t nextdefsym; // number of externally defined symbols + uint32_t iundefsym; // index to undefined symbols + uint32_t nundefsym; // number of undefined symbols + uint32_t tocoff; // file offset to table of contents + uint32_t ntoc; // number of entries in table of contents + uint32_t modtaboff; // file offset to module table + uint32_t nmodtab; // number of module table entries + uint32_t extrefsymoff; // offset to referenced symbol table + uint32_t nextrefsyms; // number of referenced symbol table entries + uint32_t indirectsymoff; // file offset to the indirect symbol table + uint32_t nindirectsyms; // number of indirect symbol table entries + uint32_t extreloff; // offset to external relocation entries + uint32_t nextrel; // number of external relocation entries + uint32_t locreloff; // offset to local relocation entries + uint32_t nlocrel; // number of local relocation entries + }; + + struct twolevel_hints_command { + uint32_t cmd; // LC_TWOLEVEL_HINTS + uint32_t cmdsize; // sizeof(struct twolevel_hints_command) + uint32_t offset; // offset to the hint table + uint32_t nhints; // number of hints in the hint table + }; + + struct dyld_info_command { + uint32_t cmd; // LC_DYLD_INFO or LC_DYLD_INFO_ONLY + uint32_t cmdsize; // sizeof(struct dyld_info_command) + uint32_t rebase_off; // file offset to rebase info + uint32_t rebase_size; // size of rebase info + uint32_t bind_off; // file offset to binding info + uint32_t bind_size; // size of binding info + uint32_t weak_bind_off; // file offset to weak binding info + uint32_t weak_bind_size; // size of weak binding info + uint32_t lazy_bind_off; // file offset to lazy binding info + uint32_t lazy_bind_size; // size of lazy binding infs + uint32_t export_off; // file offset to lazy binding info + uint32_t export_size; // size of lazy binding infs + }; + + struct function_starts_command { + uint32_t cmd; //LC_FUNCTION_STARTS + uint32_t cmdsize; //sizeof(struct function_starts_command) + uint32_t funcoff; //offset to function starts list + uint32_t funcsize; //sizeof function starts list + }; + + enum MachoPlatform { + MACHO_PLATFORM_MACOS = 1, + MACHO_PLATFORM_IOS = 2, + MACHO_PLATFORM_TVOS = 3, + MACHO_PLATFORM_WATCHOS = 4, + MACHO_PLATFORM_BRIDGEOS = 5 + }; + + enum MachoBuildTool { + MACHO_TOOL_CLANG = 1, + MACHO_TOOL_SWIFT = 2, + MACHO_TOOL_LD = 3 + }; + + struct build_tool_version { + uint32_t tool; + uint32_t version; + }; + + struct build_version_command { + uint32_t cmd; // LC_BUILD_VERSION + uint32_t cmdsize; // sizeof(build_version_command) + (ntools * sizeof(build_tool_version) + uint32_t platform; // MachoPlatform + uint32_t minos; // X.Y.Z is encoded in nibbles xxxx.yy.zz + uint32_t sdk; // X.Y.Z is encoded in nibbles xxxx.yy.zz + uint32_t ntools; // number build_tool_version entries + }; + +#pragma pack(push, 1) + struct nlist { + int32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + int16_t n_desc; + uint32_t n_value; + }; + + struct nlist_64 { + uint32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + uint16_t n_desc; + uint64_t n_value; + }; +#pragma pack(pop) + +#define R_ABS 0 + struct relocation_info + { + int32_t r_address; + uint32_t r_symbolnum:24; + uint32_t r_pcrel:1; + uint32_t r_length:2; + uint32_t r_extern:1; + uint32_t r_type:4; + }; +#define R_SCATTERED 0x80000000 + struct scattered_relocation_info_be_64 { + uint32_t r_scattered:1; // 1=scattered, 0=non-scattered (see above) + uint32_t r_pcrel:1; // was relocated pc relative already + uint32_t r_length:2; // 0=byte, 1=word, 2=long + uint32_t r_type:4; // if not 0, machine specific relocation type + uint32_t r_address:24; // offset in the section to what is being relocated + int64_t r_value; // the value the item to be relocated is refering to (without any offset added) + }; + struct scattered_relocation_info_le_64 { + uint32_t r_address:24; // offset in the section to what is being relocated + uint32_t r_type:4; // if not 0, machine specific relocation type + uint32_t r_length:2; // 0=byte, 1=word, 2=long + uint32_t r_pcrel:1; // was relocated pc relative already + uint32_t r_scattered:1; // 1=scattered, 0=non-scattered (see above) + int64_t r_value; // the value the item to be relocated is refering to (without any offset added) + }; + struct scattered_relocation_info_be { + uint32_t r_scattered:1; // 1=scattered, 0=non-scattered (see above) + uint32_t r_pcrel:1; // was relocated pc relative already + uint32_t r_length:2; // 0=byte, 1=word, 2=long + uint32_t r_type:4; // if not 0, machine specific relocation type + uint32_t r_address:24; // offset in the section to what is being relocated + int32_t r_value; // the value the item to be relocated is refering to (without any offset added) + }; + struct scattered_relocation_info_le { + uint32_t r_address:24; // offset in the section to what is being relocated + uint32_t r_type:4; // if not 0, machine specific relocation type + uint32_t r_length:2; // 0=byte, 1=word, 2=long + uint32_t r_pcrel:1; // was relocated pc relative already + uint32_t r_scattered:1; // 1=scattered, 0=non-scattered (see above) + int32_t r_value; // the value the item to be relocated is refering to (without any offset added) + }; + + enum reloc_type_generic + { + GENERIC_RELOC_VANILLA, // generic relocation as discribed above + GENERIC_RELOC_PAIR, // Only follows a GENRIC_RELOC_SECTDIFF + GENERIC_RELOC_SECTDIFF, + GENERIC_RELOC_PB_LA_PTR // prebound lazy pointer + }; + + struct dyld_chained_fixups_header + { + uint32_t fixups_version; // 0 + uint32_t starts_offset; // offset of dyld_chained_starts_in_image in chain_data + uint32_t imports_offset; // offset of imports table in chain_data + uint32_t symbols_offset; // offset of symbol strings in chain_data + uint32_t imports_count; // number of imported symbol names + uint32_t imports_format; // DYLD_CHAINED_IMPORT* + uint32_t symbols_format; // 0 => uncompressed, 1 => zlib compressed + }; + + // This struct is embedded in LC_DYLD_CHAINED_FIXUPS payload + struct dyld_chained_starts_in_image + { + uint32_t seg_count; + uint32_t seg_info_offset[1]; // each entry is offset into this struct for that segment + // followed by pool of dyld_chain_starts_in_segment data + }; + + // This struct is embedded in dyld_chain_starts_in_image + // and passed down to the kernel for page-in linking + struct dyld_chained_starts_in_segment + { + uint32_t size; // size of this (amount kernel needs to copy) + uint16_t page_size; // 0x1000 or 0x4000 + uint16_t pointer_format; // DYLD_CHAINED_PTR_* + uint64_t segment_offset; // offset in memory to start of segment + uint32_t max_valid_pointer; // for 32-bit OS, any value beyond this is not a pointer + uint16_t page_count; // how many pages are in array + uint16_t page_start[1]; // each entry is offset in each page of first element in chain + // or DYLD_CHAINED_PTR_START_NONE if no fixups on page + + // uint16_t chain_starts[1]; // some 32-bit formats may require multiple starts per page. + // for those, if high bit is set in page_starts[], then it + // is index into chain_starts[] which is a list of starts + // the last of which has the high bit set + }; + + enum { + DYLD_CHAINED_PTR_START_NONE = 0xFFFF, // used in page_start[] to denote a page with no fixups + DYLD_CHAINED_PTR_START_MULTI = 0x8000, // used in page_start[] to denote a page which has multiple starts + DYLD_CHAINED_PTR_START_LAST = 0x8000, // used in chain_starts[] to denote last start in list for page + }; + + // This struct is embedded in __TEXT,__chain_starts section in firmware + struct dyld_chained_starts_offsets + { + uint32_t pointer_format; // DYLD_CHAINED_PTR_32_FIRMWARE + uint32_t starts_count; // number of starts in array + uint32_t chain_starts[1]; // array chain start offsets + }; + + // values for dyld_chained_starts_in_segment.pointer_format + enum { + DYLD_CHAINED_PTR_ARM64E = 1, // stride 8, unauth target is vmaddr + DYLD_CHAINED_PTR_64 = 2, // target is vmaddr + DYLD_CHAINED_PTR_32 = 3, + DYLD_CHAINED_PTR_32_CACHE = 4, + DYLD_CHAINED_PTR_32_FIRMWARE = 5, + DYLD_CHAINED_PTR_64_OFFSET = 6, // target is vm offset + DYLD_CHAINED_PTR_ARM64E_OFFSET = 7, // old name + DYLD_CHAINED_PTR_ARM64E_KERNEL = 7, // stride 4, unauth target is vm offset + DYLD_CHAINED_PTR_64_KERNEL_CACHE = 8, + DYLD_CHAINED_PTR_ARM64E_USERLAND = 9, // stride 8, unauth target is vm offset + DYLD_CHAINED_PTR_ARM64E_FIRMWARE = 10, // stride 4, unauth target is vmaddr + DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE = 11, // stride 1, x86_64 kernel caches + DYLD_CHAINED_PTR_ARM64E_USERLAND24 = 12, // stride 8, unauth target is vm offset, 24-bit bind + }; + + // DYLD_CHAINED_PTR_ARM64E + struct dyld_chained_ptr_arm64e_rebase + { + uint64_t target : 43, + high8 : 8, + next : 11, // 4 or 8-byte stide + bind : 1, // == 0 + auth : 1; // == 0 + }; + + // DYLD_CHAINED_PTR_ARM64E + struct dyld_chained_ptr_arm64e_bind + { + uint64_t ordinal : 16, + zero : 16, + addend : 19, // +/-256K + next : 11, // 4 or 8-byte stide + bind : 1, // == 1 + auth : 1; // == 0 + }; + + // DYLD_CHAINED_PTR_ARM64E + struct dyld_chained_ptr_arm64e_auth_rebase + { + uint64_t target : 32, // runtimeOffset + diversity : 16, + addrDiv : 1, + key : 2, + next : 11, // 4 or 8-byte stide + bind : 1, // == 0 + auth : 1; // == 1 + }; + + // DYLD_CHAINED_PTR_ARM64E + struct dyld_chained_ptr_arm64e_auth_bind + { + uint64_t ordinal : 16, + zero : 16, + diversity : 16, + addrDiv : 1, + key : 2, + next : 11, // 4 or 8-byte stide + bind : 1, // == 1 + auth : 1; // == 1 + }; + + // DYLD_CHAINED_PTR_64/DYLD_CHAINED_PTR_64_OFFSET + struct dyld_chained_ptr_64_rebase + { + uint64_t target : 36, // 64GB max image size (DYLD_CHAINED_PTR_64 => vmAddr, DYLD_CHAINED_PTR_64_OFFSET => runtimeOffset) + high8 : 8, // top 8 bits set to this (DYLD_CHAINED_PTR_64 => after slide added, DYLD_CHAINED_PTR_64_OFFSET => before slide added) + reserved : 7, // all zeros + next : 12, // 4-byte stride + bind : 1; // == 0 + }; + + + // DYLD_CHAINED_PTR_ARM64E_USERLAND24 + struct dyld_chained_ptr_arm64e_bind24 + { + uint64_t ordinal : 24, + zero : 8, + addend : 19, // +/-256K + next : 11, // 8-byte stide + bind : 1, // == 1 + auth : 1; // == 0 + }; + + // DYLD_CHAINED_PTR_ARM64E_USERLAND24 + struct dyld_chained_ptr_arm64e_auth_bind24 + { + uint64_t ordinal : 24, + zero : 8, + diversity : 16, + addrDiv : 1, + key : 2, + next : 11, // 8-byte stide + bind : 1, // == 1 + auth : 1; // == 1 + }; + + + // DYLD_CHAINED_PTR_64 + struct dyld_chained_ptr_64_bind + { + uint64_t ordinal : 24, + addend : 8, // 0 thru 255 + reserved : 19, // all zeros + next : 12, // 4-byte stride + bind : 1; // == 1 + }; + + // DYLD_CHAINED_PTR_64_KERNEL_CACHE, DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE + struct dyld_chained_ptr_64_kernel_cache_rebase + { + uint64_t target : 30, // basePointers[cacheLevel] + target + cacheLevel : 2, // what level of cache to bind to (indexes a mach_header array) + diversity : 16, + addrDiv : 1, + key : 2, + next : 12, // 1 or 4-byte stide + isAuth : 1; // 0 -> not authenticated. 1 -> authenticated + }; + + // DYLD_CHAINED_PTR_32 + // Note: for DYLD_CHAINED_PTR_32 some non-pointer values are co-opted into the chain + // as out of range rebases. If an entry in the chain is > max_valid_pointer, then it + // is not a pointer. To restore the value, subtract off the bias, which is + // (64MB+max_valid_pointer)/2. + struct dyld_chained_ptr_32_rebase + { + uint32_t target : 26, // vmaddr, 64MB max image size + next : 5, // 4-byte stride + bind : 1; // == 0 + }; + + // DYLD_CHAINED_PTR_32 + struct dyld_chained_ptr_32_bind + { + uint32_t ordinal : 20, + addend : 6, // 0 thru 63 + next : 5, // 4-byte stride + bind : 1; // == 1 + }; + + // DYLD_CHAINED_PTR_32_CACHE + struct dyld_chained_ptr_32_cache_rebase + { + uint32_t target : 30, // 1GB max dyld cache TEXT and DATA + next : 2; // 4-byte stride + }; + + + // DYLD_CHAINED_PTR_32_FIRMWARE + struct dyld_chained_ptr_32_firmware_rebase + { + uint32_t target : 26, // 64MB max firmware TEXT and DATA + next : 6; // 4-byte stride + }; + + enum ChainedFixupPointerGeneric { + GenericArm64eFixupFormat, + Generic64FixupFormat, + Generic32FixupFormat, + Firmware32FixupFormat + }; + + union Arm64e { + dyld_chained_ptr_arm64e_auth_rebase authRebase; + dyld_chained_ptr_arm64e_auth_bind authBind; + dyld_chained_ptr_arm64e_rebase rebase; + dyld_chained_ptr_arm64e_bind bind; + dyld_chained_ptr_arm64e_bind24 bind24; + dyld_chained_ptr_arm64e_auth_bind24 authBind24; + }; + + union Generic64 { + dyld_chained_ptr_64_rebase rebase; + dyld_chained_ptr_64_bind bind; + }; + + union Generic32 { + dyld_chained_ptr_32_rebase rebase; + dyld_chained_ptr_32_bind bind; + }; + + union ChainedFixupPointer + { + dyld_chained_ptr_64_kernel_cache_rebase kernel64; + dyld_chained_ptr_32_firmware_rebase firmware32; + + uint64_t raw64; + Arm64e arm64e; + Generic64 generic64; + + uint32_t raw32; + Generic32 generic32; + dyld_chained_ptr_32_cache_rebase cache32; + }; + + // values for dyld_chained_fixups_header.imports_format + enum { + DYLD_CHAINED_IMPORT = 1, + DYLD_CHAINED_IMPORT_ADDEND = 2, + DYLD_CHAINED_IMPORT_ADDEND64 = 3, + }; + + // DYLD_CHAINED_IMPORT + struct dyld_chained_import + { + uint32_t lib_ordinal : 8, + weak_import : 1, + name_offset : 23; + }; + + // DYLD_CHAINED_IMPORT_ADDEND + struct dyld_chained_import_addend + { + uint32_t lib_ordinal : 8, + weak_import : 1, + name_offset : 23; + int32_t addend; + }; + + // DYLD_CHAINED_IMPORT_ADDEND64 + struct dyld_chained_import_addend64 + { + uint64_t lib_ordinal : 16, + weak_import : 1, + reserved : 15, + name_offset : 32; + uint64_t addend; + }; + + // When we preload the table, we store them in this format. + struct import_entry + { + uint64_t lib_ordinal; + uint64_t addend; + bool weak; + std::string name; + }; + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_MASK + enum EXPORT_SYMBOL_FLAGS { + EXPORT_SYMBOL_FLAGS_KIND_MASK = 0x03u, ///< Mask to access to EXPORT_SYMBOL_KINDS + EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION = 0x04u, + EXPORT_SYMBOL_FLAGS_REEXPORT = 0x08u, + EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER = 0x10u + }; + + enum EXPORT_SYMBOL_KINDS { + EXPORT_SYMBOL_FLAGS_KIND_REGULAR = 0x00u, + EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL = 0x01u, + EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE = 0x02u + }; +#endif + + struct MachOHeader { + bool isMainHeader; + + uint64_t textBase = 0; + uint64_t loadCommandOffset = 0; + mach_header_64 ident; + std::string identifierPrefix; + + std::vector<std::pair<uint64_t, bool>> entryPoints; + std::vector<uint64_t> m_entryPoints; //list of entrypoints + + symtab_command symtab; + dysymtab_command dysymtab; + dyld_info_command dyldInfo; + routines_command_64 routines64; + function_starts_command functionStarts; + std::vector<section_64> moduleInitSections; + linkedit_data_command exportTrie; + linkedit_data_command chainedFixups {}; + + DataBuffer* stringList; + size_t stringListSize; + + uint64_t relocationBase; + // Section and program headers, internally use 64-bit form as it is a superset of 32-bit + std::vector<segment_command_64> segments; //only three types of sections __TEXT, __DATA, __IMPORT + std::vector<section_64> sections; + std::vector<std::string> sectionNames; + + std::vector<section_64> symbolStubSections; + std::vector<section_64> symbolPointerSections; + + std::vector<std::string> dylibs; + + build_version_command buildVersion; + std::vector<build_tool_version> buildToolVersions; + + bool dysymPresent = false; + bool dyldInfoPresent = false; + bool exportTriePresent = false; + bool chainedFixupsPresent = false; + bool routinesPresent = false; + bool functionStartsPresent = false; + bool relocatable = false; + }; + + class MachoView: public BinaryView + { + MachOHeader m_header; + std::map<uint64_t, MachOHeader> m_subHeaders; // Used for MH_FILESET entries. + + struct HeaderQualifiedNames { + QualifiedName cpuTypeEnumQualName; + QualifiedName fileTypeEnumQualName; + QualifiedName flagsTypeEnumQualName; + QualifiedName headerQualName; + QualifiedName cmdTypeEnumQualName; + QualifiedName loadCommandQualName; + QualifiedName protTypeEnumQualName; + QualifiedName segFlagsTypeEnumQualName; + QualifiedName loadSegmentCommandQualName; + QualifiedName loadSegment64CommandQualName; + QualifiedName sectionQualName; + QualifiedName section64QualName; + QualifiedName symtabQualName; + QualifiedName dynsymtabQualName; + QualifiedName uuidQualName; + QualifiedName linkeditDataQualName; + QualifiedName encryptionInfoQualName; + QualifiedName versionMinQualName; + QualifiedName dyldInfoQualName; + QualifiedName dylibQualName; + QualifiedName dylibCommandQualName; + QualifiedName filesetEntryCommandQualName; + } m_typeNames; + + uint64_t m_universalImageOffset; + bool m_parseOnly, m_backedByDatabase; + int64_t m_imageBaseAdjustment; + size_t m_addressSize; //Address size in bytes 4/8 + BNEndianness m_endian; + uint32_t m_archId; + Ref<Architecture> m_arch; + Ref<Platform> m_plat = nullptr; + bool m_dylibFile; + bool m_objectFile; + std::vector<std::string> m_symbols; + + bool m_relocatable = false; + + bool m_extractMangledTypes; + bool m_simplifyTemplates; + + SymbolQueue* m_symbolQueue = nullptr; + Ref<Logger> m_logger; + + std::vector<segment_command_64> m_allSegments; //only three types of sections __TEXT, __DATA, __IMPORT + std::vector<section_64> m_allSections; + + MachOHeader HeaderForAddress(BinaryView* data, uint64_t address, bool isMainHeader, std::string identifierPrefix = ""); + bool InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_t preferredImageBase, std::string preferredImageBaseDesc); + + void RebaseThreadStarts(BinaryReader& virtualReader, std::vector<uint32_t>& threadStarts, uint64_t stepMultiplier); + Ref<Symbol> DefineMachoSymbol( + BNSymbolType type, const std::string& name, uint64_t addr, BNSymbolBinding binding, bool deferred); + void ParseSymbolTable(BinaryReader& reader, MachOHeader& header, const symtab_command& symtab, const std::vector<uint32_t>& symbolStubsList); + bool IsValidFunctionStart(uint64_t addr); + void ParseFunctionStarts(Platform* platform, uint64_t textBase, function_starts_command functionStarts); + bool ParseRelocationEntry(const relocation_info& info, uint64_t start, BNRelocationInfo& result); + + void ParseExportTrie(BinaryReader& reader, linkedit_data_command exportTrie); + void ReadExportNode(uint64_t viewStart, DataBuffer& buffer, const std::string& currentText, + size_t cursor, uint32_t endGuard); + + void ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNSymbolType type, uint32_t tableOffset, uint32_t tableSize, + BNSymbolBinding binding); + bool GetSectionPermissions(MachOHeader& header, uint64_t address, uint32_t &flags); + bool GetSegmentPermissions(MachOHeader& header, uint64_t address, uint32_t &flags); + void ParseChainedFixups(linkedit_data_command chainedFixups); + + virtual uint64_t PerformGetEntryPoint() const override; + + virtual bool PerformIsExecutable() const override { return true; } + virtual BNEndianness PerformGetDefaultEndianness() const override; + virtual bool PerformIsRelocatable() const override; + virtual size_t PerformGetAddressSize() const override; + public: + MachoView(const std::string& typeName, BinaryView* data, bool parseOnly = false); + + virtual bool Init() override; + }; + + class MachoViewType: public BinaryViewType + { + Ref<Logger> m_logger; + public: + MachoViewType(); + virtual Ref<BinaryView> Create(BinaryView* data) override; + virtual Ref<BinaryView> Parse(BinaryView* data) override; + virtual bool IsTypeValidForData(BinaryView* data) override; + virtual uint64_t ParseHeaders(BinaryView* data, uint64_t imageOffset, mach_header_64& ident, Ref<Architecture>* arch, Ref<Platform>* platform, std::string& errorMsg); + virtual Ref<Settings> GetLoadSettingsForData(BinaryView* data) override; + }; + + void InitMachoViewType(); +} diff --git a/view/macho/universalview.cpp b/view/macho/universalview.cpp new file mode 100644 index 00000000..ac637c75 --- /dev/null +++ b/view/macho/universalview.cpp @@ -0,0 +1,369 @@ +#include <stdlib.h> +#include <string.h> +#include <cstdint> +#include <map> +#ifndef _MSC_VER +#include <cxxabi.h> +#endif +#include "universalview.h" +#include "machoview.h" +#include "rapidjsonwrapper.h" + +using namespace BinaryNinja; +using namespace std; + + +static UniversalViewType* g_universalViewType = nullptr; + + +const map<pair<cpu_type_t, cpu_subtype_t>, string>& UniversalViewType::GetArchitectures() +{ + static map<pair<cpu_type_t, cpu_subtype_t>, string> g_cpuArchNames = + { + {{MACHO_CPU_TYPE_VAX, 0}, "vax"}, + {{MACHO_CPU_TYPE_MC680x0, 0}, "mc680x0"}, + {{MACHO_CPU_TYPE_X86, 0}, "x86"}, + {{MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ALL}, "x86"}, + {{MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ARCH1}, "x86 (Arch1)"}, + {{MACHO_CPU_TYPE_X86_64, 0}, "x86_64"}, + {{MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_ALL}, "x86_64"}, + {{MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_H}, "x86_64 (Haswell)"}, + {{MACHO_CPU_TYPE_MIPS, 0}, "mips"}, + {{MACHO_CPU_TYPE_MC98000, 0}, "mc98000"}, + {{MACHO_CPU_TYPE_HPPA, 0}, "hppa"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_ALL}, "arm"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V4T}, "armv4t"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6}, "armv6"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V5TEJ}, "armv5tej"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_XSCALE}, "arm (XScale)"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7}, "armv7"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7F}, "armv7f"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7S}, "armv7s"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7K}, "armv7k"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V8}, "armv8"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6M}, "armv6m"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7M}, "armv7m"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7EM}, "armv7em"}, + {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_ALL}, "arm64"}, + {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_V8}, "arm64v8"}, + {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64E}, "arm64e"}, + {{MACHO_CPU_TYPE_ARM64_32, MACHO_CPU_SUBTYPE_ARM64_32_ALL}, "arm64_32"}, + {{MACHO_CPU_TYPE_ARM64_32, MACHO_CPU_SUBTYPE_ARM64_32_V8}, "arm64_32v8"}, + {{MACHO_CPU_TYPE_MC88000, 0}, "mc88000"}, + {{MACHO_CPU_TYPE_SPARC, 0}, "sparc"}, + {{MACHO_CPU_TYPE_I860, 0}, "i860"}, + {{MACHO_CPU_TYPE_ALPHA, 0}, "alpha"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_ALL}, "ppc"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_601}, "ppc601"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_602}, "ppc602"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603}, "ppc603"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603e}, "ppc603e"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603ev}, "ppc603ev"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604}, "ppc604"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604e}, "ppc604e"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_620}, "ppc620"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_750}, "ppc750"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7400}, "ppc7400"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7450}, "ppc7450"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_970}, "ppc970"}, + {{MACHO_CPU_TYPE_POWERPC64, 0}, "ppc64"} + }; + + return g_cpuArchNames; +} + + +string UniversalViewType::ArchitectureToString(cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool& is64Bit) +{ + const map<pair<cpu_type_t, cpu_subtype_t>, string>& cpuArchNames = GetArchitectures(); + + switch(cpuType) + { + case MACHO_CPU_TYPE_X86_64: + case MACHO_CPU_TYPE_ARM64: + case MACHO_CPU_TYPE_ARM64_32: + case MACHO_CPU_TYPE_POWERPC64: + is64Bit = true; + break; + default: + is64Bit = false; + break; + } + + auto itr = cpuArchNames.find({cpuType, cpuSubType}); + if (itr != cpuArchNames.end()) + return itr->second; + + itr = cpuArchNames.find({cpuType, 0}); + if (itr != cpuArchNames.end()) + return itr->second; + + return "Unknown"; +} + + +void BinaryNinja::InitUniversalViewType() +{ + static UniversalViewType type; + BinaryViewType::Register(&type); + g_universalViewType = &type; + + Ref<Settings> settings = Settings::Instance(); + settings->RegisterSetting("files.universal.architecturePreference", + R"({ + "title" : "Universal Mach-O Architecture Preference", + "type" : "array", + "elementType" : "string", + "sorted" : false, + "default" : [], + "description" : "Specify an architecture preference for automatic loading of a Mach-O file from a Universal archive. By default, the first object file in the listing is loaded.", + "ignore" : ["SettingsProjectScope", "SettingsResourceScope"] + })"); + + const map<pair<cpu_type_t, cpu_subtype_t>, string>& cpuArchNames = UniversalViewType::GetArchitectures(); + set<string> names; + for (const auto& [key, name] : cpuArchNames) + names.insert(name); + vector<string> archNames(names.begin(), names.end()); + settings->UpdateProperty("files.universal.architecturePreference", "enum", archNames); +} + + +Ref<BinaryView> UniversalViewType::Create(BinaryView* data) +{ + try + { + return new UniversalView(data); + } + catch (std::exception&) + { + return nullptr; + } +} + + +bool UniversalViewType::IsTypeValidForData(BinaryView* data) +{ + DataBuffer sig = data->ReadBuffer(0, 4); + if (sig.GetLength() != 4) + return false; + + uint32_t magic = ToBE32(*(uint32_t*)sig.GetData()); + if ((magic == FAT_MAGIC) || (magic == FAT_MAGIC_64)) + return true; + + return false; +} + + +bool UniversalViewType::ParseHeaders(BinaryView* data, FatHeader& fatHeader, vector<FatArch64>& fatArchEntries, bool& isFat64, string& errorMsg) +{ + if (!IsTypeValidForData(data)) + { + errorMsg = "Universal (Fat Mach-O): invalid signature"; + return false; + } + + BinaryReader reader(data); + reader.SetEndianness(BigEndian); + fatHeader.magic = reader.Read32(); + fatHeader.nfat_arch = reader.Read32(); + + isFat64 = (fatHeader.magic == FAT_MAGIC_64); + size_t requiredFatHeaderSize = fatHeader.nfat_arch * (isFat64 ? 32 : 20) + 8; + if (requiredFatHeaderSize > data->GetLength()) + { + errorMsg = "Universal (Fat Mach-O): header truncated"; + return false; + } + + for (size_t i = 0; i < fatHeader.nfat_arch; i++) + { + FatArch64 fatArch; + if (isFat64) + { + fatArch.cputype = reader.Read32(); + fatArch.cpusubtype = reader.Read32(); + fatArch.offset = reader.Read64(); + fatArch.size = reader.Read64(); + fatArch.align = reader.Read32(); + fatArch.reserved = reader.Read32(); + } + else + { + fatArch.cputype = reader.Read32(); + fatArch.cpusubtype = reader.Read32(); + fatArch.offset = reader.Read32(); + fatArch.size = reader.Read32(); + fatArch.align = reader.Read32(); + } + + // Mask away cpu subtype capability bits + fatArch.cpusubtype &= ~MACHO_CPU_SUBTYPE_MASK; + fatArchEntries.push_back(fatArch); + } + + return true; +} + + +Ref<Settings> UniversalViewType::GetLoadSettingsForData(BinaryView* data) +{ + FatHeader fatHeader; + vector<FatArch64> fatArchEntries; + bool isFat64; + string errorMsg; + if (!g_universalViewType->ParseHeaders(data, fatHeader, fatArchEntries, isFat64, errorMsg)) + return nullptr; + + if (!fatArchEntries.size()) // TODO other validation? + return nullptr; + + Ref<Settings> settings = Settings::Instance(GetUniqueIdentifierString()); + settings->RegisterGroup("loader", "Load Options"); + settings->RegisterSetting("loader.universal.architectures", + R"({ + "title" : "Universal Mach-O Multi-Architecture Binary Description", + "type" : "string", + "default" : "[]", + "description" : "Describes the available object files in the Universal Multi-Architecture binary.", + "readOnly" : true + })"); + + Ref<Settings> entrySettings = Settings::Instance(GetUniqueIdentifierString()); + entrySettings->RegisterGroup("loader", "Load Options"); + entrySettings->RegisterSetting("loader.macho.universalImageOffset", + R"({ + "title" : "Universal Mach-O Object File Offset", + "type" : "number", + "default" : 0, + "description" : "The offset to the object file within the Universal Mach-O file.", + "minValue" : 0, + "maxValue" : 18446744073709551615, + "readOnly" : true + })"); + + rapidjson::Document entries(rapidjson::kArrayType); + int count = 0; + for (const auto& entry : fatArchEntries) + { + rapidjson::Value result(rapidjson::kObjectType); + bool is64Bit; + string archDesc = ArchitectureToString(entry.cputype, entry.cpusubtype, is64Bit); + string bitDesc = is64Bit ? "64-bit" : "32-bit"; + result.AddMember("id", count++, entries.GetAllocator()); + result.AddMember("architecture", archDesc, entries.GetAllocator()); + result.AddMember("is64Bit", is64Bit, entries.GetAllocator()); + result.AddMember("binaryViewType", "Mach-O", entries.GetAllocator()); + result.AddMember("description", "Mach-O " + bitDesc + " executable " + archDesc, entries.GetAllocator()); + result.AddMember("offset", ToLE64(entry.offset), entries.GetAllocator()); + result.AddMember("size", ToLE64(entry.size), entries.GetAllocator()); + entrySettings->UpdateProperty("loader.macho.universalImageOffset", "default", ToLE64(entry.offset)); + result.AddMember("loadSchema", entrySettings->SerializeSchema(), entries.GetAllocator()); + entries.PushBack(result, entries.GetAllocator()); + } + + rapidjson::StringBuffer buffer; + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + entries.Accept(writer); + + settings->UpdateProperty("loader.universal.architectures", "default", buffer.GetString()); + + return settings; +} + + +UniversalView::UniversalView(BinaryView* data, bool parseOnly): BinaryView("Universal", data->GetFile(), data) +{ + FatHeader fatHeader; + vector<FatArch64> fatArchEntries; + bool isFat64; + string errorMsg; + if (!g_universalViewType->ParseHeaders(data, fatHeader, fatArchEntries, isFat64, errorMsg)) + throw MachoFormatException(errorMsg); + + if (parseOnly) + return; + + // Add Universal file header type info + StructureBuilder fatHeaderBuilder; + fatHeaderBuilder.AddMember(Type::IntegerType(4, false), "magic"); + fatHeaderBuilder.AddMember(Type::IntegerType(4, false), "nfat_arch"); + Ref<Structure> fatHeaderStruct = fatHeaderBuilder.Finalize(); + QualifiedName headerName = string("fat_header"); + string headerTypeId = Type::GenerateAutoTypeId("macho", headerName); + Ref<Type> fatHeaderType = Type::StructureType(fatHeaderStruct); + QualifiedName rawHeaderName = DefineType(headerTypeId, headerName, fatHeaderType); + DefineDataVariable(0, Type::NamedType(this, rawHeaderName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__fat_header", 0, LocalBinding)); + + EnumerationBuilder cpuTypeBuilder; + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ANY", MACHO_CPU_TYPE_ANY); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_VAX", MACHO_CPU_TYPE_VAX); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC680x0", MACHO_CPU_TYPE_MC680x0); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_X86", MACHO_CPU_TYPE_X86); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_X86_64", MACHO_CPU_TYPE_X86_64); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MIPS", MACHO_CPU_TYPE_MIPS); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC98000", MACHO_CPU_TYPE_MC98000); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_HPPA", MACHO_CPU_TYPE_HPPA); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM", MACHO_CPU_TYPE_ARM); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM64", MACHO_CPU_TYPE_ARM64); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM64_32", MACHO_CPU_TYPE_ARM64_32); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC88000", MACHO_CPU_TYPE_MC88000); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_SPARC", MACHO_CPU_TYPE_SPARC); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_I860", MACHO_CPU_TYPE_I860); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ALPHA", MACHO_CPU_TYPE_ALPHA); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_POWERPC", MACHO_CPU_TYPE_POWERPC); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_POWERPC64", MACHO_CPU_TYPE_POWERPC64); + Ref<Enumeration> cpuTypeEnum = cpuTypeBuilder.Finalize(); + + Ref<Type> cpuTypeEnumType = Type::EnumerationType(nullptr, cpuTypeEnum, 4, false); + string cpuTypeEnumName = "cpu_type_t"; + string cpuTypeEnumId = Type::GenerateAutoTypeId("macho", cpuTypeEnumName); + QualifiedName rawCpuTypeEnumName = DefineType(cpuTypeEnumId, cpuTypeEnumName, cpuTypeEnumType); + + StructureBuilder fatArchBuilder; + fatArchBuilder.AddMember(Type::NamedType(this, rawCpuTypeEnumName), "cputype"); + fatArchBuilder.AddMember(Type::IntegerType(4, false), "cpusubtype"); + if (isFat64) + { + fatArchBuilder.AddMember(Type::IntegerType(8, false), "offset"); + fatArchBuilder.AddMember(Type::IntegerType(8, false), "size"); + } + else + { + fatArchBuilder.AddMember(Type::IntegerType(4, false), "offset"); + fatArchBuilder.AddMember(Type::IntegerType(4, false), "size"); + } + fatArchBuilder.AddMember(Type::IntegerType(4, false), "align"); + if (isFat64) + fatArchBuilder.AddMember(Type::IntegerType(4, false), "reserved"); + Ref<Structure> fatArchStruct = fatArchBuilder.Finalize(); + QualifiedName fatArchName = isFat64 ? string("fat_arch_64") : string("fat_arch"); + string fatArchTypeId = Type::GenerateAutoTypeId("macho", fatArchName); + Ref<Type> fatArchType = Type::StructureType(fatArchStruct); + QualifiedName rawFatArchName = DefineType(fatArchTypeId, fatArchName, fatArchType); + DefineDataVariable(8, Type::ArrayType(Type::NamedType(this, rawFatArchName), fatHeader.nfat_arch)); + DefineAutoSymbol(new Symbol(DataSymbol, "__fat_arch_entries", 8, LocalBinding)); +} + + +UniversalView::~UniversalView() +{ +} + + +bool UniversalView::Init() +{ + // Disable analysis modules + // TODO: for now Raw and Universal views do this; possibly refactor + if (!m_file->IsBackedByDatabase(GetTypeName())) + { + Settings::Instance()->Set("analysis.linearSweep.autorun", false, this); + Settings::Instance()->Set("analysis.signatureMatcher.autorun", false, this); + Settings::Instance()->Set("analysis.pointerSweep.autorun", false, this); + } + + AddAutoSegment(0, GetParentView()->GetLength(), 0, GetParentView()->GetLength(), SegmentReadable | SegmentWritable); + return true; +} diff --git a/view/macho/universalview.h b/view/macho/universalview.h new file mode 100644 index 00000000..aaf483cc --- /dev/null +++ b/view/macho/universalview.h @@ -0,0 +1,40 @@ +#pragma once + +#include <exception> +#include <vector> +#include <string.h> + +#include "binaryninjaapi.h" +#include "fatmachoview.h" +#include "machoview.h" + + +namespace BinaryNinja +{ + void InitUniversalViewType(); + + class UniversalViewType: public BinaryViewType + { + public: + static const std::map<std::pair<cpu_type_t, cpu_subtype_t>, std::string>& GetArchitectures(); + static std::string ArchitectureToString(cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool& is64Bit); + + UniversalViewType(): BinaryViewType("Universal", "Universal") { } + virtual Ref<BinaryView> Create(BinaryView* data) override; + virtual bool IsTypeValidForData(BinaryView* data) override; + virtual bool ParseHeaders(BinaryView* data, FatHeader& fatHeader, std::vector<FatArch64>& fatArchEntries, bool& isFat64, std::string& errorMsg); + virtual Ref<Settings> GetLoadSettingsForData(BinaryView* data) override; + }; + + class UniversalView: public BinaryView + { + public: + UniversalView(BinaryView* data, bool parseOnly = false); + virtual ~UniversalView(); + + virtual bool Init() override; + virtual BNEndianness PerformGetDefaultEndianness() const override { return BigEndian; } + virtual bool PerformIsExecutable() const override { return false; } + virtual bool PerformIsRelocatable() const override { return false; }; + }; +} 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(); +} |
