summaryrefslogtreecommitdiff
path: root/view
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2024-03-06 19:20:34 -0500
committerRusty Wagner <rusty.wagner@gmail.com>2024-03-06 19:20:34 -0500
commit828fff839f4e4de3e9582405dfec567531000ebc (patch)
treeee0db0607edb0c76c653078672201c85e8a96d19 /view
parent47894a1ce20090849ad304c68e6c0b889665eda3 (diff)
Move view modules into the API repo
Diffstat (limited to 'view')
-rw-r--r--view/elf/.gitignore8
-rw-r--r--view/elf/CMakeLists.txt33
-rw-r--r--view/elf/LICENSE13
-rw-r--r--view/elf/README.md27
-rw-r--r--view/elf/elfview.cpp2772
-rw-r--r--view/elf/elfview.h558
-rw-r--r--view/macho/.gitignore8
-rw-r--r--view/macho/CMakeLists.txt35
-rw-r--r--view/macho/LICENSE13
-rw-r--r--view/macho/README.md30
-rw-r--r--view/macho/fatmachoview.cpp266
-rw-r--r--view/macho/fatmachoview.h50
-rw-r--r--view/macho/machoview.cpp3399
-rw-r--r--view/macho/machoview.h1375
-rw-r--r--view/macho/universalview.cpp369
-rw-r--r--view/macho/universalview.h40
-rw-r--r--view/pe/.gitignore8
-rw-r--r--view/pe/CMakeLists.txt33
-rw-r--r--view/pe/LICENSE13
-rw-r--r--view/pe/README.md27
-rw-r--r--view/pe/coffview.cpp1689
-rw-r--r--view/pe/coffview.h372
-rw-r--r--view/pe/peview.cpp2970
-rw-r--r--view/pe/peview.h479
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(&sect, 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(&sect.sectname, 16);
+ reader.Read(&sect.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*)&sect.sectname, sect.addr, (char*)&sect.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*)&sect.segname,
+ (char*)&sect.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(&sect.sectname, 16);
+ reader.Read(&sect.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*)&sect.sectname, sect.addr, (char*)&sect.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*)&sect.segname,
+ (char*)&sect.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();
+}