diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-06 19:20:34 -0500 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-06 19:20:34 -0500 |
| commit | 828fff839f4e4de3e9582405dfec567531000ebc (patch) | |
| tree | ee0db0607edb0c76c653078672201c85e8a96d19 /view/macho | |
| parent | 47894a1ce20090849ad304c68e6c0b889665eda3 (diff) | |
Move view modules into the API repo
Diffstat (limited to 'view/macho')
| -rw-r--r-- | view/macho/.gitignore | 8 | ||||
| -rw-r--r-- | view/macho/CMakeLists.txt | 35 | ||||
| -rw-r--r-- | view/macho/LICENSE | 13 | ||||
| -rw-r--r-- | view/macho/README.md | 30 | ||||
| -rw-r--r-- | view/macho/fatmachoview.cpp | 266 | ||||
| -rw-r--r-- | view/macho/fatmachoview.h | 50 | ||||
| -rw-r--r-- | view/macho/machoview.cpp | 3399 | ||||
| -rw-r--r-- | view/macho/machoview.h | 1375 | ||||
| -rw-r--r-- | view/macho/universalview.cpp | 369 | ||||
| -rw-r--r-- | view/macho/universalview.h | 40 |
10 files changed, 5585 insertions, 0 deletions
diff --git a/view/macho/.gitignore b/view/macho/.gitignore new file mode 100644 index 00000000..d4c4c4dc --- /dev/null +++ b/view/macho/.gitignore @@ -0,0 +1,8 @@ +CMakeFiles +CMakeCache.txt +cmake_install.cmake +Makefile +api +build +libview_* +view_*.dll diff --git a/view/macho/CMakeLists.txt b/view/macho/CMakeLists.txt new file mode 100644 index 00000000..6f89250a --- /dev/null +++ b/view/macho/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(view_macho) + +if(NOT BN_INTERNAL_BUILD) + add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api) +endif() + +file(GLOB SOURCES + *.cpp + *.h) + +if(DEMO) + add_library(view_macho STATIC ${SOURCES}) +else() + add_library(view_macho SHARED ${SOURCES}) +endif() + +target_link_libraries(view_macho binaryninjaapi) + +set_target_properties(view_macho PROPERTIES + CXX_STANDARD 17 + CXX_VISIBILITY_PRESET hidden + CXX_STANDARD_REQUIRED ON + VISIBILITY_INLINES_HIDDEN ON + POSITION_INDEPENDENT_CODE ON) + +if(BN_INTERNAL_BUILD) + plugin_rpath(view_macho) + set_target_properties(view_macho PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} + RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) +else() + bn_install_plugin(view_macho) +endif() diff --git a/view/macho/LICENSE b/view/macho/LICENSE new file mode 100644 index 00000000..265883c5 --- /dev/null +++ b/view/macho/LICENSE @@ -0,0 +1,13 @@ +Copyright 2021 Vector 35 Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/view/macho/README.md b/view/macho/README.md new file mode 100644 index 00000000..b94ca4b3 --- /dev/null +++ b/view/macho/README.md @@ -0,0 +1,30 @@ +# view-macho +This is the Mach-O view plugin that ships with Binary Ninja. + +## Issues + +Issues for this repository have been disabled. Please file an issue for this repository at https://github.com/Vector35/binaryninja-api/issues. All previously existing issues for this repository have been transferred there as well. + +## Building + +Building the architecture plugin requires `cmake` 3.9 or above. You will also need the +[Binary Ninja API source](https://github.com/Vector35/binaryninja-api). + +Run `cmake`. This can be done either from a separate build directory or from the source +directory. Once that is complete, run `make` (or `ninja`, depending on your default generator) in the build directory to compile the plugin. + +The plugin can be found in the root of the build directory as `libview_macho.so`, +`libview_macho.dylib` or `view_macho.dll` depending on your platform. + +To install the plugin, first launch Binary Ninja and uncheck the "Mach-O view plugin" +option in the "Core Plugins" section. This will cause Binary Ninja to stop loading the +bundled plugin so that its replacement can be loaded. + +Once this is complete, run `make install` (or `ninja install`, depending on the generator used). + +You can also manually copy the plugin into the user plugins directory (you can locate this by using the "Open Plugin Folder" +option in the Binary Ninja UI). + +**Do not replace the view plugin in the Binary Ninja install directory. This will be overwritten +every time there is a Binary Ninja update. Use the above process to ensure that updates do not +automatically uninstall your custom build.** diff --git a/view/macho/fatmachoview.cpp b/view/macho/fatmachoview.cpp new file mode 100644 index 00000000..de9b755b --- /dev/null +++ b/view/macho/fatmachoview.cpp @@ -0,0 +1,266 @@ +#include <stdlib.h> +#include <string.h> +#include <cstdint> +#include <map> +#include "fatmachoview.h" +#include "machoview.h" + +using namespace BinaryNinja; +using namespace std; + + +struct fat_type +{ + string name; + string long_name; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; +}; + + +static vector<fat_type>* g_recognizedFatTypes; + +static const map<cpu_type_t, cpu_subtype_t> g_allSubtypeMap +{ + {MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ALL}, + {MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_ALL}, + {MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_ALL}, + {MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_ALL}, + {MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_ALL}, + {MACHO_CPU_TYPE_POWERPC64, MACHO_CPU_SUBTYPE_POWERPC_ALL} +}; + + +FatMachoViewType::FatMachoViewType(const string& name, const string& long_name, cpu_type_t cputype, cpu_subtype_t cpusubtype) : BinaryViewType(name, long_name), m_cputype(cputype), m_cpusubtype(cpusubtype) +{ + +} + + +static bool FindMostSpecificCpuTypeSubtype(cpu_type_t cputype, cpu_subtype_t cpusubtype, cpu_subtype_t& outType) +{ + // Anything exact is most specific + for (const auto& type : *g_recognizedFatTypes) + { + if (type.cputype == cputype && type.cpusubtype == cpusubtype) + { + outType = type.cpusubtype; + return true; + } + } + // Then, any -all arch that matches + for (const auto& type : *g_recognizedFatTypes) + { + if (type.cputype == cputype && type.cpusubtype == g_allSubtypeMap.at(cputype)) + { + outType = type.cpusubtype; + return true; + } + } + // Else, no match + return false; +} + + +static bool ExtractFatArchForCPU(BinaryView* data, fat_arch_64& arch, cpu_type_t cputype, cpu_subtype_t cpusubtype) +{ + DataBuffer sig = data->ReadBuffer(0, 4); + if (sig.GetLength() != 4) + return false; + + uint32_t magic = ToBE32(*(uint32_t*)sig.GetData()); + if ((magic != FAT_MAGIC) && (magic != FAT_MAGIC_64)) + return false; + + bool fat64 = (magic == FAT_MAGIC_64); + + fat_header header; + BinaryReader reader(data); + + // According to docs, Fat files are always BigEndian + reader.SetEndianness(BigEndian); + header.magic = reader.Read32(); + header.nfat_arch = reader.Read32(); + + // Malformed header, too many archs + size_t expectSize = header.nfat_arch * (fat64 ? 32 /* sizeof(fat_arch_64) */ : 20 /* sizeof(fat_arch) */) + 8 /* sizeof(fat_header) */; + if (expectSize > data->GetLength()) + { + return false; + } + + // Because Mach-O files can "hide" extra archs after the end for weird legacy reasons, + // we shouldn't assume the header has the right number of archs + while (true) + { + // Just use a fat_arch_64 struct since we read manually and it can hold both sizes + fat_arch_64 iarch; + try + { + if (fat64) + { + iarch.cputype = reader.Read32(); + iarch.cpusubtype = reader.Read32(); + iarch.offset = reader.Read64(); + iarch.size = reader.Read64(); + iarch.align = reader.Read32(); + iarch.reserved = reader.Read32(); + } + else + { + iarch.cputype = reader.Read32(); + iarch.cpusubtype = reader.Read32(); + iarch.offset = reader.Read32(); + iarch.size = reader.Read32(); + iarch.align = reader.Read32(); + } + } + catch (ReadException &) + { + return false; + } + + if (iarch.cputype == 0) + { + // Probably the end + break; + } + + if (iarch.offset + iarch.size > data->GetLength()) + { + // Malformed + break; + } + + if (iarch.cputype != cputype) + { + continue; + } + + // Make sure the passed subtype is the most specific, otherwise two different views + // will try to display the same slice + cpu_subtype_t mostSpecific; + if (!FindMostSpecificCpuTypeSubtype(iarch.cputype, iarch.cpusubtype, mostSpecific)) + { + continue; + } + if (cpusubtype == mostSpecific) + { + arch = iarch; + return true; + } + } + + // Don't have this specific subtype + return false; +} + + +Ref<BinaryViewType> FatViewTypeForData(Ref<BinaryData> data) +{ + vector<Ref<BinaryViewType>> types = BinaryViewType::GetViewTypesForData(data); + for (Ref<BinaryViewType> type : types) + { + // Ignore raw binary data views + if (type->GetName() == "Raw") + { + continue; + } + + // Make sure that the slice is still itself a valid mach-o + if (!type->IsTypeValidForData(data)) + { + continue; + } + + return type; + } + return nullptr; +} + + +Ref<BinaryView> FatMachoViewType::Create(BinaryView* data) +{ + fat_arch_64 arch; + if (!ExtractFatArchForCPU(data, arch, m_cputype, m_cpusubtype)) + return nullptr; + + DataBuffer buffer = data->ReadBuffer(arch.offset, arch.size); + Ref<BinaryData> newData = new BinaryData(data->GetFile(), std::move(buffer)); + + try + { + // Update the view to this specific arch so it doesn't conflict when saving/loading + return new MachoView(GetName(), newData); + } + catch (std::exception&) + { + return nullptr; + } +} + + +bool FatMachoViewType::IsTypeValidForData(BinaryView* data) +{ + // Enable this BinaryViewType for existing databases only + if (!data->GetFile()->IsBackedByDatabase()) + return false; + + fat_arch_64 arch; + if (!ExtractFatArchForCPU(data, arch, m_cputype, m_cpusubtype)) + return false; + + // MachoView::IsMacho only considers the magic header, so we can load only that + DataBuffer buffer = data->ReadBuffer(arch.offset, 4); + Ref<BinaryData> newData = new BinaryData(data->GetFile(), std::move(buffer)); + Ref<BinaryViewType> attemptedViewType = FatViewTypeForData(newData); + + return attemptedViewType; +} + + +void BinaryNinja::InitFatMachoViewType() +{ + // Need a pointer because this is called from a constructor before g_recognizedFatTypes is loaded. + g_recognizedFatTypes = new vector<fat_type> + { + {"Fat Mach-O x86_64", "Fat Mach-O x86_64", MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_ALL}, + {"Fat Mach-O x86", "Fat Mach-O x86", MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ALL}, + + {"Fat Mach-O arm64e", "Fat Mach-O arm64e", MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64E}, + {"Fat Mach-O arm64v8", "Fat Mach-O arm64v8", MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_V8}, + {"Fat Mach-O arm64", "Fat Mach-O arm64", MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_ALL}, + {"Fat Mach-O arm64_32", "Fat Mach-O arm64_32", MACHO_CPU_TYPE_ARM64_32, MACHO_CPU_SUBTYPE_ARM64_32_V8}, + {"Fat Mach-O armv8", "Fat Mach-O armv8", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V8}, + {"Fat Mach-O armv7em", "Fat Mach-O armv7em", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7EM}, + {"Fat Mach-O armv7m", "Fat Mach-O armv7m", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7M}, + {"Fat Mach-O armv7s", "Fat Mach-O armv7s", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7S}, + {"Fat Mach-O armv7k", "Fat Mach-O armv7k", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7K}, + {"Fat Mach-O armv7", "Fat Mach-O armv7", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7}, + {"Fat Mach-O armv6m", "Fat Mach-O armv6m", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6M}, + {"Fat Mach-O armv6", "Fat Mach-O armv6", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6}, + {"Fat Mach-O armv5tej", "Fat Mach-O armv5tej", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V5TEJ}, + {"Fat Mach-O armv4t", "Fat Mach-O armv4t", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V4T}, + {"Fat Mach-O arm xscale", "Fat Mach-O arm xscale", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_XSCALE}, + {"Fat Mach-O arm", "Fat Mach-O arm", MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_ALL}, + {"Fat Mach-O ppc64", "Fat Mach-O ppc64", MACHO_CPU_TYPE_POWERPC64, MACHO_CPU_SUBTYPE_POWERPC_ALL}, + {"Fat Mach-O ppc970", "Fat Mach-O ppc970", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_970}, + {"Fat Mach-O ppc7450", "Fat Mach-O ppc7450", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7450}, + {"Fat Mach-O ppc7400", "Fat Mach-O ppc7400", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7400}, + {"Fat Mach-O ppc750", "Fat Mach-O ppc750", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_750}, + {"Fat Mach-O ppc620", "Fat Mach-O ppc620", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_620}, + {"Fat Mach-O ppc604e", "Fat Mach-O ppc604e", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604e}, + {"Fat Mach-O ppc604", "Fat Mach-O ppc604", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604}, + {"Fat Mach-O ppc603ev", "Fat Mach-O ppc603ev", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603ev}, + {"Fat Mach-O ppc603e", "Fat Mach-O ppc603e", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603e}, + {"Fat Mach-O ppc603", "Fat Mach-O ppc603", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603}, + {"Fat Mach-O ppc602", "Fat Mach-O ppc602", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_602}, + {"Fat Mach-O ppc601", "Fat Mach-O ppc601", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_601}, + {"Fat Mach-O ppc", "Fat Mach-O ppc", MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_ALL} + }; + + for (const auto& type : *g_recognizedFatTypes) + { + BinaryViewType::Register(new FatMachoViewType(type.name, type.long_name, type.cputype, type.cpusubtype)); + } +} diff --git a/view/macho/fatmachoview.h b/view/macho/fatmachoview.h new file mode 100644 index 00000000..575d3884 --- /dev/null +++ b/view/macho/fatmachoview.h @@ -0,0 +1,50 @@ +#pragma once + +#include <exception> +#include <vector> +#include <string.h> + +#include "binaryninjaapi.h" +#include "machoview.h" + +namespace BinaryNinja +{ + struct fat_header + { + uint32_t magic; + uint32_t nfat_arch; + }; + + struct fat_arch + { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t offset; + uint32_t size; + uint32_t align; + }; + + struct fat_arch_64 + { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint64_t offset; + uint64_t size; + uint32_t align; + uint32_t reserved; + }; + + class FatMachoViewType: public BinaryViewType + { + cpu_type_t m_cputype; + cpu_subtype_t m_cpusubtype; + + public: + FatMachoViewType(const std::string& name, const std::string& long_name, cpu_type_t cputype, cpu_subtype_t cpusubtype); + virtual Ref<BinaryView> Create(BinaryView* data) override; + virtual bool IsTypeValidForData(BinaryView* data) override; + virtual bool IsDeprecated() override { return true; } + }; + + void InitFatMachoViewType(); +} diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp new file mode 100644 index 00000000..cfdc0897 --- /dev/null +++ b/view/macho/machoview.cpp @@ -0,0 +1,3399 @@ +#include <algorithm> +#include <cstdint> +#include <sstream> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <tuple> +#ifndef _MSC_VER +#include <cxxabi.h> +#endif +#include "machoview.h" +#include "fatmachoview.h" +#include "universalview.h" +#include "lowlevelilinstruction.h" +#include "rapidjsonwrapper.h" + +enum { + N_STAB = 0xe0, + N_PEXT = 0x10, + N_TYPE = 0x0e, + N_EXT = 0x01 +}; + +using namespace BinaryNinja; +using namespace std; + + +static MachoViewType* g_machoViewType = nullptr; + +static string CommandToString(uint32_t lcCommand) +{ + switch(lcCommand) + { + case LC_REQ_DYLD: return "LC_REQ_DYLD"; + case LC_SEGMENT: return "LC_SEGMENT"; + case LC_SYMTAB: return "LC_SYMTAB"; + case LC_SYMSEG: return "LC_SYMSEG"; + case LC_THREAD: return "LC_THREAD"; + case LC_UNIXTHREAD: return "LC_UNIXTHREAD"; + case LC_LOADFVMLIB: return "LC_LOADFVMLIB"; + case LC_IDFVMLIB: return "LC_IDFVMLIB"; + case LC_IDENT: return "LC_IDENT"; + case LC_FVMFILE: return "LC_FVMFILE"; + case LC_PREPAGE: return "LC_PREPAGE"; + case LC_DYSYMTAB: return "LC_DYSYMTAB"; + case LC_LOAD_DYLIB: return "LC_LOAD_DYLIB"; + case LC_ID_DYLIB: return "LC_ID_DYLIB"; + case LC_LOAD_DYLINKER: return "LC_LOAD_DYLINKER"; + case LC_ID_DYLINKER: return "LC_ID_DYLINKER"; + case LC_PREBOUND_DYLIB: return "LC_PREBOUND_DYLIB"; + case LC_ROUTINES: return "LC_ROUTINES"; + case LC_SUB_FRAMEWORK: return "LC_SUB_FRAMEWORK"; + case LC_SUB_UMBRELLA: return "LC_SUB_UMBRELLA"; + case LC_SUB_CLIENT: return "LC_SUB_CLIENT"; + case LC_SUB_LIBRARY: return "LC_SUB_LIBRARY"; + case LC_TWOLEVEL_HINTS: return "LC_TWOLEVEL_HINTS"; + case LC_PREBIND_CKSUM: return "LC_PREBIND_CKSUM"; + case LC_LOAD_WEAK_DYLIB: return "LC_LOAD_WEAK_DYLIB"; + case LC_SEGMENT_64: return "LC_SEGMENT_64"; + case LC_ROUTINES_64: return "LC_ROUTINES_64"; + case LC_UUID: return "LC_UUID"; + case LC_RPATH: return "LC_RPATH"; + case LC_CODE_SIGNATURE: return "LC_CODE_SIGNATURE"; + case LC_SEGMENT_SPLIT_INFO: return "LC_SEGMENT_SPLIT_INFO"; + case LC_REEXPORT_DYLIB: return "LC_REEXPORT_DYLIB"; + case LC_LAZY_LOAD_DYLIB: return "LC_LAZY_LOAD_DYLIB"; + case LC_ENCRYPTION_INFO: return "LC_ENCRYPTION_INFO"; + case LC_DYLD_INFO: return "LC_DYLD_INFO"; + case LC_DYLD_INFO_ONLY: return "LC_DYLD_INFO_ONLY"; + case LC_LOAD_UPWARD_DYLIB: return "LC_LOAD_UPWARD_DYLIB"; + case LC_VERSION_MIN_MACOSX: return "LC_VERSION_MIN_MACOSX"; + case LC_VERSION_MIN_IPHONEOS: return "LC_VERSION_MIN_IPHONEOS"; + case LC_FUNCTION_STARTS: return "LC_FUNCTION_STARTS"; + case LC_DYLD_ENVIRONMENT: return "LC_DYLD_ENVIRONMENT"; + case LC_MAIN: return "LC_MAIN"; + case LC_DATA_IN_CODE: return "LC_DATA_IN_CODE"; + case LC_SOURCE_VERSION: return "LC_SOURCE_VERSION"; + case LC_DYLIB_CODE_SIGN_DRS: return "LC_DYLIB_CODE_SIGN_DRS"; + case _LC_ENCRYPTION_INFO_64: return "LC_ENCRYPTION_INFO_64"; + case _LC_LINKER_OPTION: return "LC_LINKER_OPTION"; + case _LC_LINKER_OPTIMIZATION_HINT: return "LC_LINKER_OPTIMIZATION_HINT"; + case _LC_VERSION_MIN_TVOS: return "LC_VERSION_MIN_TVOS"; + case LC_VERSION_MIN_WATCHOS: return "LC_VERSION_MIN_WATCHOS"; + case LC_NOTE: return "LC_NOTE"; + case LC_BUILD_VERSION: return "LC_BUILD_VERSION"; + case LC_DYLD_EXPORTS_TRIE: return "LC_DYLD_EXPORTS_TRIE"; + case LC_DYLD_CHAINED_FIXUPS: return "LC_DYLD_CHAINED_FIXUPS"; + default: + { + stringstream ss; + ss << "0x" << std::hex << lcCommand; + return ss.str(); + } + } +} + + +static string BuildPlatformToString(uint32_t platform) +{ + switch (platform) + { + case MACHO_PLATFORM_MACOS: return "macos"; + case MACHO_PLATFORM_IOS: return "ios"; + case MACHO_PLATFORM_TVOS: return "tvos"; + case MACHO_PLATFORM_WATCHOS: return "watchos"; + case MACHO_PLATFORM_BRIDGEOS: return "bridgeos"; + default: + { + stringstream ss; + ss << "0x" << std::hex << platform; + return ss.str(); + } + } +} + + +static string BuildToolToString(uint32_t tool) +{ + switch (tool) + { + case MACHO_TOOL_CLANG: return "clang"; + case MACHO_TOOL_SWIFT: return "swift"; + case MACHO_TOOL_LD: return "ld"; + default: + { + stringstream ss; + ss << "0x" << std::hex << tool; + return ss.str(); + } + } +} + + +static string BuildToolVersionToString(uint32_t version) +{ + uint32_t major = (version >> 16) & 0xffff; + uint32_t minor = (version >> 8) & 0xff; + uint32_t update = version & 0xff; + + stringstream ss; + ss << major << "." << minor; + if (update) + ss << "." << update; + return ss.str(); +} + + +void BinaryNinja::InitMachoViewType() +{ + static MachoViewType type; + BinaryViewType::Register(&type); + g_machoViewType = &type; +} + + +static int64_t readSLEB128(DataBuffer& buffer, size_t length, size_t &offset) +{ + uint8_t cur; + int64_t value = 0; + size_t shift = 0; + while (offset < length) + { + cur = buffer[offset++]; + value |= (cur & 0x7f) << shift; + shift += 7; + if ((cur & 0x80) == 0) + break; + } + value = (value << (64 - shift)) >> (64 - shift); + return value; +} + + +static uint64_t readLEB128(DataBuffer& p, size_t end, size_t &offset) +{ + uint64_t result = 0; + int bit = 0; + do { + if (offset >= end) + return -1; + + uint64_t slice = p[offset] & 0x7f; + + if (bit > 63) + return -1; + else { + result |= (slice << bit); + bit += 7; + } + } while (p[offset++] & 0x80); + return result; +} + + +uint64_t readValidULEB128(DataBuffer& buffer, size_t& cursor) +{ + uint64_t value = readLEB128(buffer, buffer.GetLength(), cursor); + if ((int64_t)value == -1) + throw ReadException(); + return value; +} + + +MachoView::MachoView(const string& typeName, BinaryView* data, bool parseOnly): BinaryView(typeName, data->GetFile(), data), + m_universalImageOffset(0), m_parseOnly(parseOnly) +{ + CreateLogger("BinaryView"); + m_logger = CreateLogger("BinaryView.MachoView"); + + m_backedByDatabase = data->GetFile()->IsBackedByDatabase(typeName); + + Ref<BinaryViewType> universalViewType = BinaryViewType::GetByName("Universal"); + bool isUniversal = (universalViewType && universalViewType->IsTypeValidForData(data)); + + Ref<Settings> viewSettings = Settings::Instance(); + m_extractMangledTypes = viewSettings->Get<bool>("analysis.extractTypesFromMangledNames", data); + m_simplifyTemplates = viewSettings->Get<bool>("analysis.types.templateSimplifier", data); + + Ref<Settings> settings = data->GetLoadSettings(typeName); + if (settings && settings->Contains("loader.macho.universalImageOffset")) + { + settings->SetResourceId(typeName); + m_universalImageOffset = settings->Get<uint64_t>("loader.macho.universalImageOffset", data); + } + else if (isUniversal) + { + Ref<Settings> loadSettings = universalViewType->GetLoadSettingsForData(data); + if (loadSettings && loadSettings->Contains("loader.universal.architectures")) + { + string json = loadSettings->Get<string>("loader.universal.architectures"); + rapidjson::Document jsonArchitectures; + jsonArchitectures.Parse(json.data(), json.size()); + if (!json.size() || jsonArchitectures.HasParseError()) + throw MachoFormatException("Mach-O view could not be created! Json parse error."); + if (!jsonArchitectures.IsArray() || jsonArchitectures.Empty()) + throw MachoFormatException("Mach-O view could not be created! Json data error."); + + // Select the object file based on architecture preference. + // Note: This code is duplicated in options.cpp + vector<string> architectures; + for (const auto& entry : jsonArchitectures.GetArray()) + architectures.push_back(entry["architecture"].GetString()); + auto archPref = Settings::Instance()->Get<vector<string>>("files.universal.architecturePreference"); + int archIndex = 0; + if (auto result = find_first_of(archPref.begin(), archPref.end(), architectures.begin(), architectures.end()); result != archPref.end()) + archIndex = std::find(architectures.begin(), architectures.end(), *result) - architectures.begin(); + + const auto& archEntry = jsonArchitectures[archIndex]; + loadSettings = Settings::Instance(GetUniqueIdentifierString()); + loadSettings->SetResourceId(typeName); + loadSettings->DeserializeSchema(archEntry["loadSchema"].GetString()); + data->SetLoadSettings(typeName, loadSettings); + if (!m_file->IsBackedByDatabase(typeName)) + { + auto defaultSettings = loadSettings->SerializeSettings(nullptr, SettingsDefaultScope); + loadSettings->DeserializeSettings(defaultSettings, data, SettingsResourceScope); + } + + m_universalImageOffset = loadSettings->Get<uint64_t>("loader.macho.universalImageOffset", data); + + // NOTE: this silences the 'Updated load schema detected' warning. There is some chicken/egg stuff here. + loadSettings->UpdateProperty("loader.macho.universalImageOffset", "default", m_universalImageOffset); + } + } + + m_header = HeaderForAddress(data, 0, true); +} + + +MachOHeader MachoView::HeaderForAddress(BinaryView* data, uint64_t address, bool isMainHeader, std::string identifierPrefix) +{ + MachOHeader header; + header.isMainHeader = isMainHeader; + + header.identifierPrefix = identifierPrefix; + header.stringList = new DataBuffer(); + + std::string errorMsg; + if (isMainHeader) { + header.loadCommandOffset = g_machoViewType->ParseHeaders(data, m_universalImageOffset + address, header.ident, &m_arch, &m_plat, errorMsg); + if (!header.loadCommandOffset) + throw MachoFormatException(errorMsg); + } + else + { + // address is a Raw file offset + BinaryReader subReader(data); + subReader.Seek(address); + + header.ident.magic = subReader.Read32(); + + BNEndianness endianness; + if (header.ident.magic == MH_MAGIC || header.ident.magic == MH_MAGIC_64) + endianness = LittleEndian; + else if (header.ident.magic == MH_CIGAM || header.ident.magic == MH_CIGAM_64) + endianness = BigEndian; + else + { + throw ReadException(); + } + + subReader.SetEndianness(endianness); + header.ident.cputype = subReader.Read32(); + header.ident.cpusubtype = subReader.Read32(); + header.ident.filetype = subReader.Read32(); + header.ident.ncmds = subReader.Read32(); + header.ident.sizeofcmds = subReader.Read32(); + header.ident.flags = subReader.Read32(); + if ((header.ident.cputype & MachOABIMask) == MachOABI64) // address size == 8 + { + header.ident.reserved = subReader.Read32(); + } + header.loadCommandOffset = subReader.GetOffset(); + } + + + if (isMainHeader) + { + if (header.ident.magic == MH_MAGIC || header.ident.magic == MH_MAGIC_64) + { + m_endian = LittleEndian; + m_logger->LogDebug("Recognized Little Endian Mach-O"); + } + else // (header.ident.magic == MH_CIGAM || header.ident.magic == MH_CIGAM_64) + { + m_endian = BigEndian; + m_logger->LogDebug("Recognized Big Endian Mach-O"); + } + } + + BinaryReader reader(data); + reader.SetEndianness(m_endian); + reader.SetVirtualBase(m_universalImageOffset); + reader.Seek(header.loadCommandOffset); + + if (isMainHeader) + { + m_objectFile = header.ident.filetype == MH_OBJECT; + m_dylibFile = header.ident.filetype == MH_DYLIB; + m_relocatable = ((header.ident.flags & MH_PIE) > 0) || m_objectFile || m_dylibFile; + m_archId = header.ident.cputype; + m_addressSize = (header.ident.cputype & MachOABIMask) == MachOABI64 ? 8 : 4; + } + + bool first = true; + // Parse segment commands + try + { + m_logger->LogDebug("ident.ncmds: %d\n", header.ident.ncmds); + for (size_t i = 0; i < header.ident.ncmds; i++) + { + load_command load; + segment_command_64 segment64; + section_64 sect; + memset(§, 0, sizeof(sect)); + size_t curOffset = reader.GetOffset(); + load.cmd = reader.Read32(); + load.cmdsize = reader.Read32(); + size_t nextOffset = curOffset + load.cmdsize; + m_logger->LogDebug("Segment cmd: %08x - cmdsize: %08x - ", load.cmd, load.cmdsize); + if (load.cmdsize < sizeof(load_command)) + throw MachoFormatException("unable to read header"); + + switch (load.cmd) + { + case LC_MAIN: + { + uint64_t entryPoint = reader.Read64(); + m_logger->LogDebug("LC_MAIN entryPoint: %#016" PRIx64, entryPoint); + header.entryPoints.push_back({entryPoint, true}); + (void)reader.Read64(); // Stack start + break; + } + case LC_SEGMENT: //map the 32bit version to 64 bits + m_logger->LogDebug("LC_SEGMENT\n"); + segment64.cmd = LC_SEGMENT_64; + reader.Read(&segment64.segname, 16); + segment64.vmaddr = reader.Read32(); + segment64.vmsize = reader.Read32(); + segment64.fileoff = reader.Read32() + m_universalImageOffset; + segment64.filesize = reader.Read32(); + segment64.maxprot = reader.Read32(); + segment64.initprot = reader.Read32(); + segment64.nsects = reader.Read32(); + segment64.flags = reader.Read32(); + if (first) + { + if (!((header.ident.flags & MH_SPLIT_SEGS) || header.ident.cputype == MACHO_CPU_TYPE_X86_64) + || (segment64.flags & MACHO_VM_PROT_WRITE)) + { + header.relocationBase = segment64.vmaddr; + first = false; + } + } + m_logger->LogDebug("\tName: %s\n" \ + "\tCmd: %#08" PRIx32 "\n" \ + "\tvmaddr: %#016" PRIx64 "\n" \ + "\tvmsize: %#016" PRIx64 "\n" \ + "\tfileoff: %#016" PRIx64 "\n" \ + "\tfilesize: %#016" PRIx64 "\n" \ + "\tmaxprot: %#08" PRIx32 "\n" \ + "\tinitprot: %#08" PRIx32 "\n" \ + "\tnsects: %#08" PRIx32 "\n" \ + "\tflags: %#08" PRIx32 "\n", + (char*)&segment64.segname, + segment64.cmd, + segment64.vmaddr, + segment64.vmsize, + segment64.fileoff, + segment64.filesize, + segment64.maxprot, + segment64.initprot, + segment64.nsects, + segment64.flags); + for (size_t j = 0; j < segment64.nsects; j++) + { + reader.Read(§.sectname, 16); + reader.Read(§.segname, 16); + sect.addr = reader.Read32(); + sect.size = reader.Read32(); + sect.offset = reader.Read32(); + sect.align = reader.Read32(); + sect.reloff = reader.Read32(); + sect.nreloc = reader.Read32(); + sect.flags = reader.Read32(); + sect.reserved1 = reader.Read32(); + sect.reserved2 = reader.Read32(); + // if the segment isn't mapped into virtual memory don't add the corresponding sections. + if (segment64.vmsize > 0) + { + header.sections.push_back(sect); + m_allSections.push_back(sect); + } + else + m_logger->LogInfo("Omitting section %16s at %#" PRIx64 " corresponding to segment %16s which is not mapped into memory", (char*)§.sectname, sect.addr, (char*)§.segname); + + m_logger->LogDebug("\t\tSegName: %16s\n" \ + "\t\tSectName: %16s\n" \ + "\t\tAddr: %#" PRIx64 "\n" \ + "\t\tSize: %#" PRIx64 "\n" \ + "\t\tOffset: %#" PRIx32 "\n" \ + "\t\tAlign: %#" PRIx32 "\n" \ + "\t\tReloff: %#" PRIx32 "\n" \ + "\t\tNReloc: %#" PRIx32 "\n" \ + "\t\tFlags: %#" PRIx32 "\n" \ + "\t\tReserved1:%#" PRIx32 "\n" \ + "\t\tReserved2:%#" PRIx32 "\n" \ + "\t\t------------------------\n", + (char*)§.segname, + (char*)§.sectname, + sect.addr, + sect.size, + sect.offset, + sect.align, + sect.reloff, + sect.nreloc, + sect.flags, + sect.reserved1, + sect.reserved2); + if (!strncmp(sect.sectname, "__mod_init_func", 15)) + header.moduleInitSections.push_back(sect); + if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) + header.symbolStubSections.push_back(sect); + if ((sect.flags & S_NON_LAZY_SYMBOL_POINTERS) == S_NON_LAZY_SYMBOL_POINTERS) + header.symbolPointerSections.push_back(sect); + if ((sect.flags & S_LAZY_SYMBOL_POINTERS) == S_LAZY_SYMBOL_POINTERS) + header.symbolPointerSections.push_back(sect); + } + header.segments.push_back(segment64); + m_allSegments.push_back(segment64); + break; + case LC_SEGMENT_64: + m_logger->LogDebug("LC_SEGMENT_64\n"); + segment64.cmd = LC_SEGMENT_64; + reader.Read(&segment64.segname, 16); + segment64.vmaddr = reader.Read64(); + segment64.vmsize = reader.Read64(); + segment64.fileoff = reader.Read64() + m_universalImageOffset; + segment64.filesize = reader.Read64(); + segment64.maxprot = reader.Read32(); + segment64.initprot = reader.Read32(); + segment64.nsects = reader.Read32(); + segment64.flags = reader.Read32(); + if (first) + { + if (!((header.ident.flags & MH_SPLIT_SEGS) || header.ident.cputype == MACHO_CPU_TYPE_X86_64) + || (segment64.flags & MACHO_VM_PROT_WRITE)) + { + header.relocationBase = segment64.vmaddr; + first = false; + } + } + m_logger->LogDebug( + "\tName: %s\n" \ + "\tCmd: %#08" PRIx32 "\n" \ + "\tvmaddr: %#016" PRIx64 "\n" \ + "\tvmsize: %#016" PRIx64 "\n" \ + "\tfileoff: %#016" PRIx64 "\n" \ + "\tfilesize: %#016" PRIx64 "\n" \ + "\tmaxprot: %#08" PRIx32 "\n" \ + "\tinitprot: %#08" PRIx32 "\n" \ + "\tnsects: %#08" PRIx32 "\n" \ + "\tflags: %#08" PRIx32 "\n", + (char*)&segment64.segname, + segment64.cmd, + segment64.vmaddr, + segment64.vmsize, + segment64.fileoff, + segment64.filesize, + segment64.maxprot, + segment64.initprot, + segment64.nsects, + segment64.flags); + m_logger->LogDebug("\t\t------------------------\n"); + for (size_t j = 0; j < segment64.nsects; j++) + { + reader.Read(§.sectname, 16); + reader.Read(§.segname, 16); + sect.addr = reader.Read64(); + sect.size = reader.Read64(); + sect.offset = reader.Read32(); + sect.align = reader.Read32(); + sect.reloff = reader.Read32(); + sect.nreloc = reader.Read32(); + sect.flags = reader.Read32(); + sect.reserved1 = reader.Read32(); + sect.reserved2 = reader.Read32(); + sect.reserved3 = reader.Read32(); + // if the segment isn't mapped into virtual memory don't add the corresponding sections. + if (segment64.vmsize > 0) + { + header.sections.push_back(sect); + m_allSections.push_back(sect); + } + else + m_logger->LogInfo("Omitting section %16s at %#" PRIx64 " corresponding to segment %16s which is not mapped into memory", (char*)§.sectname, sect.addr, (char*)§.segname); + m_logger->LogDebug( + "\t\tSegName: %16s\n" \ + "\t\tSectName: %16s\n" \ + "\t\tAddr: %#" PRIx64 "\n" \ + "\t\tSize: %#" PRIx64 "\n" \ + "\t\tOffset: %#" PRIx32 "\n" \ + "\t\tAlign: %#" PRIx32 "\n" \ + "\t\tReloff: %#" PRIx32 "\n" \ + "\t\tNReloc: %#" PRIx32 "\n" \ + "\t\tFlags: %#" PRIx32 "\n" \ + "\t\tReserved1:%#" PRIx32 "\n" \ + "\t\tReserved2:%#" PRIx32 "\n" \ + "\t\tReserved3:%#" PRIx32 "\n" \ + "\t\t------------------------\n", + (char*)§.segname, + (char*)§.sectname, + sect.addr, + sect.size, + sect.offset, + sect.align, + sect.reloff, + sect.nreloc, + sect.flags, + sect.reserved1, + sect.reserved2, + sect.reserved3); + if (!strncmp(sect.sectname, "__mod_init_func", 15)) + header.moduleInitSections.push_back(sect); + if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) + header.symbolStubSections.push_back(sect); + if ((sect.flags & S_NON_LAZY_SYMBOL_POINTERS) == S_NON_LAZY_SYMBOL_POINTERS) + header.symbolPointerSections.push_back(sect); + if ((sect.flags & S_LAZY_SYMBOL_POINTERS) == S_LAZY_SYMBOL_POINTERS) + header.symbolPointerSections.push_back(sect); + } + header.segments.push_back(segment64); + m_allSegments.push_back(segment64); + break; + case LC_ROUTINES: //map the 32bit version to 64bits + m_logger->LogDebug("LC_REOUTINES\n"); + header.routines64.cmd = LC_ROUTINES_64; + header.routines64.init_address = reader.Read32(); + header.routines64.init_module = reader.Read32(); + header.routines64.reserved1 = reader.Read32(); + header.routines64.reserved2 = reader.Read32(); + header.routines64.reserved3 = reader.Read32(); + header.routines64.reserved4 = reader.Read32(); + header.routines64.reserved5 = reader.Read32(); + header.routines64.reserved6 = reader.Read32(); + header.routinesPresent = true; + m_logger->LogDebug("\tinit_address: %016" PRIx64 "\n\tinit_module: %08" PRIx64 "\n", + header.routines64.init_address, header.routines64.init_module); + break; + case LC_ROUTINES_64: + m_logger->LogDebug("LC_REOUTINES_64\n"); + header.routines64.cmd = LC_ROUTINES_64; + header.routines64.init_address = reader.Read64(); + header.routines64.init_module = reader.Read64(); + header.routines64.reserved1 = reader.Read64(); + header.routines64.reserved2 = reader.Read64(); + header.routines64.reserved3 = reader.Read64(); + header.routines64.reserved4 = reader.Read64(); + header.routines64.reserved5 = reader.Read64(); + header.routines64.reserved6 = reader.Read64(); + header.routinesPresent = true; + m_logger->LogDebug("\tinit_address: %016" PRIx64 "\n\tinit_module: %08" PRIx64 "\n", + header.routines64.init_address, header.routines64.init_module); + break; + case LC_FUNCTION_STARTS: + m_logger->LogDebug("LC_FUNCTION_STARTS\n"); + header.functionStarts.funcoff = reader.Read32(); + header.functionStarts.funcsize = reader.Read32(); + header.functionStartsPresent = true; + m_logger->LogDebug("\tFunction Starts:\n\toffset: %#08" PRIx32 "\n\tsize: %#08" PRIx32 "\n", + header.functionStarts.funcoff, header.functionStarts.funcsize); + break; + case LC_SYMTAB: + m_logger->LogDebug("LC_SYMTAB\n"); + header.symtab.symoff = reader.Read32(); + header.symtab.nsyms = reader.Read32(); + header.symtab.stroff = reader.Read32(); + header.symtab.strsize = reader.Read32(); + reader.Seek(header.symtab.stroff); + header.stringList->Append(reader.Read(header.symtab.strsize)); + header.stringListSize = header.symtab.strsize; + m_logger->LogDebug("\tstrsize: %08x\n" \ + "\tstroff: %08x\n" \ + "\tnsyms: %08x\n", + header.symtab.strsize, + header.symtab.stroff, + header.symtab.nsyms); + break; + case LC_DYSYMTAB: + m_logger->LogDebug("LC_DYSYMTAB\n"); + header.dysymtab.ilocalsym = reader.Read32(); + header.dysymtab.nlocalsym = reader.Read32(); + header.dysymtab.iextdefsym = reader.Read32(); + header.dysymtab.nextdefsym = reader.Read32(); + header.dysymtab.iundefsym = reader.Read32(); + header.dysymtab.nundefsym = reader.Read32(); + header.dysymtab.tocoff = reader.Read32(); + header.dysymtab.ntoc = reader.Read32(); + header.dysymtab.modtaboff = reader.Read32(); + header.dysymtab.nmodtab = reader.Read32(); + header.dysymtab.extrefsymoff = reader.Read32(); + header.dysymtab.nextrefsyms = reader.Read32(); + header.dysymtab.indirectsymoff = reader.Read32(); + header.dysymtab.nindirectsyms = reader.Read32(); + header.dysymtab.extreloff = reader.Read32(); + header.dysymtab.nextrel = reader.Read32(); + header.dysymtab.locreloff = reader.Read32(); + header.dysymtab.nlocrel = reader.Read32(); + m_logger->LogDebug("\theader.dysymtab.ilocalsym 0x%08x\n"\ + "\theader.dysymtab.nlocalsym 0x%08x\n"\ + "\theader.dysymtab.iextdefsym 0x%08x\n"\ + "\theader.dysymtab.nextdefsym 0x%08x\n"\ + "\theader.dysymtab.iundefsym 0x%08x\n"\ + "\theader.dysymtab.nundefsym 0x%08x\n"\ + "\theader.dysymtab.tocoff 0x%08x\n"\ + "\theader.dysymtab.ntoc 0x%08x\n"\ + "\theader.dysymtab.modtaboff 0x%08x\n"\ + "\theader.dysymtab.nmodtab 0x%08x\n"\ + "\theader.dysymtab.extrefsymoff 0x%08x\n"\ + "\theader.dysymtab.nextrefsyms 0x%08x\n"\ + "\theader.dysymtab.indirectsymoff 0x%08x\n"\ + "\theader.dysymtab.nindirectsyms 0x%08x\n"\ + "\theader.dysymtab.extreloff 0x%08x\n"\ + "\theader.dysymtab.nextrel 0x%08x\n"\ + "\theader.dysymtab.locreloff 0x%08x\n"\ + "\theader.dysymtab.nlocrel 0x%08x\n", + header.dysymtab.ilocalsym, + header.dysymtab.nlocalsym, + header.dysymtab.iextdefsym, + header.dysymtab.nextdefsym, + header.dysymtab.iundefsym, + header.dysymtab.nundefsym, + header.dysymtab.tocoff, + header.dysymtab.ntoc, + header.dysymtab.modtaboff, + header.dysymtab.nmodtab, + header.dysymtab.extrefsymoff, + header.dysymtab.nextrefsyms, + header.dysymtab.indirectsymoff, + header.dysymtab.nindirectsyms, + header.dysymtab.extreloff, + header.dysymtab.nextrel, + header.dysymtab.locreloff, + header.dysymtab.nlocrel); + header.dysymPresent = true; + break; + case LC_DYLD_CHAINED_FIXUPS: + m_logger->LogDebug("LC_DYLD_CHAINED_FIXUPS\n"); + header.chainedFixups.dataoff = reader.Read32(); + header.chainedFixups.datasize = reader.Read32(); + header.chainedFixupsPresent = true; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + m_logger->LogDebug("LC_DYLD_INFO\n"); + header.dyldInfo.rebase_off = reader.Read32(); + header.dyldInfo.rebase_size = reader.Read32(); + header.dyldInfo.bind_off = reader.Read32(); + header.dyldInfo.bind_size = reader.Read32(); + header.dyldInfo.weak_bind_off = reader.Read32(); + header.dyldInfo.weak_bind_size = reader.Read32(); + header.dyldInfo.lazy_bind_off = reader.Read32(); + header.dyldInfo.lazy_bind_size = reader.Read32(); + header.dyldInfo.export_off = reader.Read32(); + header.dyldInfo.export_size = reader.Read32(); + header.exportTrie.dataoff = header.dyldInfo.export_off; + header.exportTrie.datasize = header.dyldInfo.export_size; + header.exportTriePresent = true; + header.dyldInfoPresent = true; + break; + case LC_DYLD_EXPORTS_TRIE: + m_logger->LogDebug("LC_DYLD_EXPORTS_TRIE\n"); + header.exportTrie.dataoff = reader.Read32(); + header.exportTrie.datasize = reader.Read32(); + header.exportTriePresent = true; + break; + case LC_THREAD: + case LC_UNIXTHREAD: + while (reader.GetOffset() < nextOffset) + { + thread_command thread; + thread.flavor = reader.Read32(); + thread.count = reader.Read32(); + m_logger->LogDebug("LC_THREAD\\LC_UNIXTHREAD\n"); + switch (m_archId) + { + case MachOx64: + m_logger->LogDebug("x86_64 Thread state\n"); + if (thread.flavor != X86_THREAD_STATE64) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + //This wont be big endian so we can just read the whole thing + reader.Read(&thread.statex64, sizeof(thread.statex64)); + header.entryPoints.push_back({thread.statex64.rip, false}); + break; + case MachOx86: + m_logger->LogDebug("x86 Thread state\n"); + if (thread.flavor != X86_THREAD_STATE32) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + //This wont be big endian so we can just read the whole thing + reader.Read(&thread.statex86, sizeof(thread.statex86)); + header.entryPoints.push_back({thread.statex86.eip, false}); + break; + case MachOArm: + m_logger->LogDebug("Arm Thread state\n"); + if (thread.flavor != _ARM_THREAD_STATE) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + //This wont be big endian so we can just read the whole thing + reader.Read(&thread.statearmv7, sizeof(thread.statearmv7)); + header.entryPoints.push_back({thread.statearmv7.r15, false}); + break; + case MachOAarch64: + case MachOAarch6432: + m_logger->LogDebug("Aarch64 Thread state\n"); + if (thread.flavor != _ARM_THREAD_STATE64) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + reader.Read(&thread.stateaarch64, sizeof(thread.stateaarch64)); + header.entryPoints.push_back({thread.stateaarch64.pc, false}); + break; + case MachOPPC: + m_logger->LogDebug("PPC Thread state\n"); + if (thread.flavor != PPC_THREAD_STATE) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + //Read individual entries for endian reasons + header.entryPoints.push_back({reader.Read32(), false}); + (void)reader.Read32(); + (void)reader.Read32(); + //Read the rest of the structure + (void)reader.Read(&thread.stateppc.r1, sizeof(thread.stateppc) - (3 * 4)); + break; + case MachOPPC64: + m_logger->LogDebug("PPC64 Thread state\n"); + if (thread.flavor != PPC_THREAD_STATE64) + { + reader.SeekRelative(thread.count * sizeof(uint32_t)); + break; + } + header.entryPoints.push_back({reader.Read64(), false}); + (void)reader.Read64(); + (void)reader.Read64(); // Stack start + (void)reader.Read(&thread.stateppc64.r1, sizeof(thread.stateppc64) - (3 * 8)); + break; + default: + m_logger->LogError("Unknown archid: %x", m_archId); + } + } + break; + case LC_LOAD_DYLIB: + { + uint32_t offset = reader.Read32(); + if (offset < nextOffset) + { + reader.Seek(curOffset + offset); + string libname = reader.ReadCString(); + header.dylibs.push_back(libname); + } + } + break; + case LC_BUILD_VERSION: + { + m_logger->LogDebug("LC_BUILD_VERSION:"); + header.buildVersion.platform = reader.Read32(); + header.buildVersion.minos = reader.Read32(); + header.buildVersion.sdk = reader.Read32(); + header.buildVersion.ntools = reader.Read32(); + m_logger->LogDebug("Platform: %s", BuildPlatformToString(header.buildVersion.platform).c_str()); + m_logger->LogDebug("MinOS: %s", BuildToolVersionToString(header.buildVersion.minos).c_str()); + m_logger->LogDebug("SDK: %s", BuildToolVersionToString(header.buildVersion.sdk).c_str()); + for (uint32_t i = 0; (i < header.buildVersion.ntools) && (i < 10); i++) + { + uint32_t tool = reader.Read32(); + uint32_t version = reader.Read32(); + header.buildToolVersions.push_back({tool, version}); + m_logger->LogDebug("Build Tool: %s: %s", BuildToolToString(tool).c_str(), BuildToolVersionToString(version).c_str()); + } + break; + } + case LC_FILESET_ENTRY: + { + if (isMainHeader) + { + uint64_t vmAddr = reader.Read64(); + uint64_t fAddr = reader.Read64(); + uint64_t strOff = reader.Read32() + curOffset; + reader.Seek(strOff); + auto identPrefix = reader.ReadCString(load.cmdsize - strOff); + MachOHeader subHeader = HeaderForAddress(data, fAddr, false, identPrefix); + subHeader.textBase = vmAddr; + m_subHeaders[vmAddr] = subHeader; + } + else { + throw ReadException(); + } + break; + } + default: + m_logger->LogDebug("Unhandled command: %s : %" PRIu32 "\n", CommandToString(load.cmd).c_str(), load.cmdsize); + break; + } + if (reader.GetOffset() != nextOffset) + { + m_logger->LogDebug("Didn't parse load command: %s fully %" PRIx64 ":%" PRIxPTR, CommandToString(load.cmd).c_str(), reader.GetOffset(), nextOffset); + } + reader.Seek(nextOffset); + } + } + catch (ReadException&) + { + throw MachoFormatException("Mach-O section headers invalid"); + } + + return header; +} + + +void MachoView::RebaseThreadStarts(BinaryReader& virtualReader, vector<uint32_t>& threadStarts, uint64_t stepMultiplier) +{ + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + + // create a dummy relocation to handle tagged pointers + BNRelocationInfo reloc; + memset(&reloc, 0, sizeof(BNRelocationInfo)); + reloc.type = StandardRelocationType; + reloc.size = 8; + reloc.nativeType = BINARYNINJA_MANUAL_RELOCATION; + + size_t numReg = 0; + size_t numAuth = 0; + uint64_t imageBase = GetStart(); + for (auto threadStart : threadStarts) + { + if (threadStart == 0xffffffff) + break; + + m_logger->LogDebug("Rebasing thread chain start: 0x%x", threadStart); + try + { + uint64_t curAddr = imageBase + threadStart; + virtualReader.Seek(curAddr); + while (true) + { + if (!IsOffsetBackedByFile(virtualReader.GetOffset())) + { + m_logger->LogError("Chained address: 0x%" PRIx64 " in thread start: 0x%x not backed by file!", virtualReader.GetOffset(), threadStart); + break; + } + + // read tagged pointer + uint64_t val = virtualReader.Read64(); + bool isAuthenticated = (val & (1ULL << 63)) != 0; + bool isRebase = (val & (1ULL << 62)) == 0; + if (isRebase) + { + // calculate new target value + uint64_t tgtVal; + if (isAuthenticated) + { + numAuth++; + tgtVal = (val & 0xFFFFFFFF) + imageBase; + } + else + { + numReg++; + uint64_t top8Bits = val & 0x0007F80000000000ULL; + uint64_t bottom43Bits = val & 0x000007FFFFFFFFFFULL; + tgtVal = (top8Bits << 13) | (((int64_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF); + tgtVal += m_imageBaseAdjustment; + } + + reloc.address = curAddr; + DefineRelocation(m_arch, reloc, tgtVal, reloc.address); + } + + // seek to next tagged pointer + val &= ~(1ULL << 62); // handle bind bit + uint64_t delta = (val & 0x3FF8000000000000) >> 51; + delta *= stepMultiplier; + if (!delta) + break; + + curAddr += delta; + virtualReader.Seek(curAddr); + } + } + catch (ReadException&) + { + m_logger->LogError("Failed rebasing thread start at: 0x%x", threadStart); + } + } + + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + double t = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / 1000.0; + m_logger->LogInfo("Rebasing thread starts took %.3f seconds. Updated %" PRIuPTR " pointers (authenticated: %" PRIuPTR ", regular: %" PRIuPTR ").", t, numAuth + numReg, numAuth, numReg); +} + + +bool MachoView::IsValidFunctionStart(uint64_t addr) +{ + uint8_t opcode[BN_MAX_INSTRUCTION_LENGTH]; + size_t opLen = Read(opcode, addr, m_arch->GetMaxInstructionLength()); + if (!opLen) + return false; + + Ref<LowLevelILFunction> ilFunc = new LowLevelILFunction(m_arch, nullptr); + ilFunc->SetCurrentAddress(m_arch, addr); + m_arch->GetInstructionLowLevelIL(opcode, addr, opLen, *ilFunc); + for (size_t i = 0; i < ilFunc->GetInstructionCount(); i++) + { + const auto& instr = ilFunc->GetInstruction(i); + if (instr.operation == LLIL_UNDEF) + return false; + } + + return true; +} + + +void MachoView::ParseFunctionStarts(Platform* platform, uint64_t textBase, function_starts_command functionStarts) +{ + BinaryReader reader(GetParentView()); + reader.SetEndianness(m_endian); + reader.SetVirtualBase(m_universalImageOffset); + try + { + if (m_header.ident.filetype == MH_DSYM) + { + m_logger->LogDebug("Skipping LC_FUNCTION_STARTS parsing"); + return; + } + + reader.Seek(functionStarts.funcoff); + DataBuffer buffer = reader.Read(functionStarts.funcsize); + size_t i = 0; + uint64_t curfunc = textBase; + uint64_t curOffset = 0; + + while (i < functionStarts.funcsize) + { + curOffset = readLEB128(buffer, functionStarts.funcsize, i); + if (curOffset == 0) + continue; + curfunc += curOffset; + uint64_t target = curfunc; + if (!IsValidFunctionStart(target)) + { + m_logger->LogWarn("Possible error processing LC_FUNCTION_STARTS! Not adding function at: 0x%" PRIx64 "\n", target); + continue; + } + Ref<Platform> targetPlatform = platform->GetAssociatedPlatformByAddress(target); + AddFunctionForAnalysis(targetPlatform, target); + m_logger->LogDebug("Adding function start: %#" PRIx64 "\n", curfunc); + } + } + catch (ReadException&) + { + m_logger->LogDebug("LC_FUNCTION_STARTS command invalid"); + } +} + + +bool MachoView::ParseRelocationEntry(const relocation_info& info, uint64_t start, BNRelocationInfo& result) +{ + // struct BNRelocationInfo + // { + // BNRelocationType type; // BinaryNinja Relocation Type + // bool pcRelative; // PC Relative or Absolute (subtract address from relocation) + // bool baseRelative; // Relative to start of module (Add module base to relocation) + // size_t size; // Size of the data to be written + // size_t truncateSize; // After addition/subtraction truncate to + // uint64_t nativeType; // Base type from relocation entry + // size_t addend; // Addend value from relocation entry + // bool hasSign; // Addend should be subtracted + // bool implicitAddend; // Addend should be read from the BinaryView + // bool external; // Relocation entry points to external symbol + // size_t symbolIndex; // Index into symbol table + // size_t sectionIndex; // Index into the section table + // uint64_t address; // Absolute address or segment offset + // }; + m_logger->LogDebug("\tr_address: %" PRIx32 " + %" PRIx64 " = %" PRIx64, info.r_address, start, info.r_address + start); + m_logger->LogDebug("\tr_symbolnum: %" PRIx32, info.r_symbolnum); + m_logger->LogDebug("\tr_pcrel: %" PRIx32, info.r_pcrel); + m_logger->LogDebug("\tr_length: %" PRIx32, info.r_length); + m_logger->LogDebug("\tr_extern: %" PRIx32, info.r_extern); + m_logger->LogDebug("\tr_type: %" PRIx32, info.r_type); + if (m_objectFile && (info.r_address & R_SCATTERED)) + { + m_logger->LogError("Scattered Relocations not currently supported"); + return false; + } + + switch (info.r_length) + { + case 0: result.size = 1; break; + case 1: result.size = 2; break; + case 2: result.size = 4; break; + case 3: result.size = 8; break; + } + result.address = start + info.r_address; + result.truncateSize = result.size; + result.pcRelative = info.r_pcrel; + result.baseRelative = start == GetStart(); + result.nativeType = info.r_type; + result.addend = 0; + result.hasSign = false; + result.implicitAddend = false; + result.external = info.r_extern; + if (result.external) + result.symbolIndex = info.r_symbolnum; + else + result.sectionIndex = info.r_symbolnum; + return true; +} + + +bool MachoView::Init() +{ + Ref<Settings> settings = GetLoadSettings(GetTypeName()); + std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now(); + BinaryReader reader(GetParentView()); + reader.SetEndianness(m_endian); + reader.SetVirtualBase(m_universalImageOffset); + BinaryReader virtualReader(this); + virtualReader.SetEndianness(m_endian); + + uint64_t initialImageBase = 0; + bool initialImageBaseSet = false; + string preferredImageBaseDesc; + for (const auto& i : m_header.segments) + { + if ((i.initprot == MACHO_VM_PROT_NONE) || (!i.vmsize)) + continue; + + if (!initialImageBaseSet) + { + initialImageBase = i.vmaddr; + initialImageBaseSet = true; + preferredImageBaseDesc = i.segname; + } + else if (i.vmaddr < initialImageBase) + { + initialImageBase = i.vmaddr; + preferredImageBaseDesc = i.segname; + } + } + + SetOriginalBase(initialImageBase); + uint64_t preferredImageBase = initialImageBase; + if (settings && settings->Contains("loader.imageBase") && settings->Contains("loader.architecture")) // handle overrides + { + preferredImageBase = settings->Get<uint64_t>("loader.imageBase", this); + + Ref<Architecture> arch = Architecture::GetByName(settings->Get<string>("loader.architecture", this)); + if (!m_arch || (arch && (arch->GetName() != m_arch->GetName()))) + m_arch = arch; + } + + m_imageBaseAdjustment = 0; + if (!initialImageBase) + m_imageBaseAdjustment = preferredImageBase; + else if (initialImageBase <= preferredImageBase) + m_imageBaseAdjustment = preferredImageBase - initialImageBase; + else + m_imageBaseAdjustment = -(int64_t)(initialImageBase - preferredImageBase); + + // Add Mach-O file header type info + EnumerationBuilder cpuTypeBuilder; + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ANY", MACHO_CPU_TYPE_ANY); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_VAX", MACHO_CPU_TYPE_VAX); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC680x0", MACHO_CPU_TYPE_MC680x0); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_X86", MACHO_CPU_TYPE_X86); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_X86_64", MACHO_CPU_TYPE_X86_64); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MIPS", MACHO_CPU_TYPE_MIPS); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC98000", MACHO_CPU_TYPE_MC98000); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_HPPA", MACHO_CPU_TYPE_HPPA); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM", MACHO_CPU_TYPE_ARM); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM64", MACHO_CPU_TYPE_ARM64); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM64_32", MACHO_CPU_TYPE_ARM64_32); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC88000", MACHO_CPU_TYPE_MC88000); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_SPARC", MACHO_CPU_TYPE_SPARC); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_I860", MACHO_CPU_TYPE_I860); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ALPHA", MACHO_CPU_TYPE_ALPHA); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_POWERPC", MACHO_CPU_TYPE_POWERPC); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_POWERPC64", MACHO_CPU_TYPE_POWERPC64); + Ref<Enumeration> cpuTypeEnum = cpuTypeBuilder.Finalize(); + + Ref<Type> cpuTypeEnumType = Type::EnumerationType(nullptr, cpuTypeEnum, 4, false); + string cpuTypeEnumName = "cpu_type_t"; + string cpuTypeEnumId = Type::GenerateAutoTypeId("macho", cpuTypeEnumName); + m_typeNames.cpuTypeEnumQualName = DefineType(cpuTypeEnumId, cpuTypeEnumName, cpuTypeEnumType); + + EnumerationBuilder fileTypeBuilder; + fileTypeBuilder.AddMemberWithValue("MH_OBJECT", MH_OBJECT); + fileTypeBuilder.AddMemberWithValue("MH_EXECUTE", MH_EXECUTE); + fileTypeBuilder.AddMemberWithValue("MH_FVMLIB", MH_FVMLIB); + fileTypeBuilder.AddMemberWithValue("MH_CORE", MH_CORE); + fileTypeBuilder.AddMemberWithValue("MH_PRELOAD", MH_PRELOAD); + fileTypeBuilder.AddMemberWithValue("MH_DYLIB", MH_DYLIB); + fileTypeBuilder.AddMemberWithValue("MH_DYLINKER", MH_DYLINKER); + fileTypeBuilder.AddMemberWithValue("MH_BUNDLE", MH_BUNDLE); + fileTypeBuilder.AddMemberWithValue("MH_DYLIB_STUB", MH_DYLIB_STUB); + fileTypeBuilder.AddMemberWithValue("MH_DSYM", MH_DSYM); + fileTypeBuilder.AddMemberWithValue("MH_KEXT_BUNDLE", MH_KEXT_BUNDLE); + fileTypeBuilder.AddMemberWithValue("MH_FILESET", MH_FILESET); + Ref<Enumeration> fileTypeEnum = fileTypeBuilder.Finalize(); + + Ref<Type> fileTypeEnumType = Type::EnumerationType(nullptr, fileTypeEnum, 4, false); + string fileTypeEnumName = "file_type_t"; + string fileTypeEnumId = Type::GenerateAutoTypeId("macho", fileTypeEnumName); + m_typeNames.fileTypeEnumQualName = DefineType(fileTypeEnumId, fileTypeEnumName, fileTypeEnumType); + + EnumerationBuilder flagsTypeBuilder; + flagsTypeBuilder.AddMemberWithValue("MH_NOUNDEFS", MH_NOUNDEFS); + flagsTypeBuilder.AddMemberWithValue("MH_INCRLINK", MH_INCRLINK); + flagsTypeBuilder.AddMemberWithValue("MH_DYLDLINK", MH_DYLDLINK); + flagsTypeBuilder.AddMemberWithValue("MH_BINDATLOAD", MH_BINDATLOAD); + flagsTypeBuilder.AddMemberWithValue("MH_PREBOUND", MH_PREBOUND); + flagsTypeBuilder.AddMemberWithValue("MH_SPLIT_SEGS", MH_SPLIT_SEGS); + flagsTypeBuilder.AddMemberWithValue("MH_LAZY_INIT", MH_LAZY_INIT); + flagsTypeBuilder.AddMemberWithValue("MH_TWOLEVEL", MH_TWOLEVEL); + flagsTypeBuilder.AddMemberWithValue("MH_FORCE_FLAT", MH_FORCE_FLAT); + flagsTypeBuilder.AddMemberWithValue("MH_NOMULTIDEFS", MH_NOMULTIDEFS); + flagsTypeBuilder.AddMemberWithValue("MH_NOFIXPREBINDING", MH_NOFIXPREBINDING); + flagsTypeBuilder.AddMemberWithValue("MH_PREBINDABLE", MH_PREBINDABLE); + flagsTypeBuilder.AddMemberWithValue("MH_ALLMODSBOUND", MH_ALLMODSBOUND); + flagsTypeBuilder.AddMemberWithValue("MH_SUBSECTIONS_VIA_SYMBOLS", MH_SUBSECTIONS_VIA_SYMBOLS); + flagsTypeBuilder.AddMemberWithValue("MH_CANONICAL", MH_CANONICAL); + flagsTypeBuilder.AddMemberWithValue("MH_WEAK_DEFINES", MH_WEAK_DEFINES); + flagsTypeBuilder.AddMemberWithValue("MH_BINDS_TO_WEAK", MH_BINDS_TO_WEAK); + flagsTypeBuilder.AddMemberWithValue("MH_ALLOW_STACK_EXECUTION", MH_ALLOW_STACK_EXECUTION); + flagsTypeBuilder.AddMemberWithValue("MH_ROOT_SAFE", MH_ROOT_SAFE); + flagsTypeBuilder.AddMemberWithValue("MH_SETUID_SAFE", MH_SETUID_SAFE); + flagsTypeBuilder.AddMemberWithValue("MH_NO_REEXPORTED_DYLIBS", MH_NO_REEXPORTED_DYLIBS); + flagsTypeBuilder.AddMemberWithValue("MH_PIE", MH_PIE); + flagsTypeBuilder.AddMemberWithValue("MH_DEAD_STRIPPABLE_DYLIB", MH_DEAD_STRIPPABLE_DYLIB); + flagsTypeBuilder.AddMemberWithValue("MH_HAS_TLV_DESCRIPTORS", MH_HAS_TLV_DESCRIPTORS); + flagsTypeBuilder.AddMemberWithValue("MH_NO_HEAP_EXECUTION", MH_NO_HEAP_EXECUTION); + flagsTypeBuilder.AddMemberWithValue("MH_APP_EXTENSION_SAFE", _MH_APP_EXTENSION_SAFE); + flagsTypeBuilder.AddMemberWithValue("MH_NLIST_OUTOFSYNC_WITH_DYLDINFO", _MH_NLIST_OUTOFSYNC_WITH_DYLDINFO); + flagsTypeBuilder.AddMemberWithValue("MH_SIM_SUPPORT", _MH_SIM_SUPPORT); + flagsTypeBuilder.AddMemberWithValue("MH_DYLIB_IN_CACHE", _MH_DYLIB_IN_CACHE); + Ref<Enumeration> flagsTypeEnum = flagsTypeBuilder.Finalize(); + + Ref<Type> flagsTypeEnumType = Type::EnumerationType(nullptr, flagsTypeEnum, 4, false); + string flagsTypeEnumName = "flags_type_t"; + string flagsTypeEnumId = Type::GenerateAutoTypeId("macho", flagsTypeEnumName); + m_typeNames.flagsTypeEnumQualName = DefineType(flagsTypeEnumId, flagsTypeEnumName, flagsTypeEnumType); + + StructureBuilder machoHeaderBuilder; + machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "magic"); + machoHeaderBuilder.AddMember(Type::NamedType(this, m_typeNames.cpuTypeEnumQualName), "cputype"); + machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "cpusubtype"); + machoHeaderBuilder.AddMember(Type::NamedType(this, m_typeNames.fileTypeEnumQualName), "filetype"); + machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "ncmds"); + machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "sizeofcmds"); + machoHeaderBuilder.AddMember(Type::NamedType(this, m_typeNames.flagsTypeEnumQualName), "flags"); + if (m_addressSize == 8) + machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "reserved"); + Ref<Structure> machoHeaderStruct = machoHeaderBuilder.Finalize(); + QualifiedName headerName = (m_addressSize == 8) ? string("mach_header_64") : string("mach_header"); + + string headerTypeId = Type::GenerateAutoTypeId("macho", headerName); + Ref<Type> machoHeaderType = Type::StructureType(machoHeaderStruct); + m_typeNames.headerQualName = DefineType(headerTypeId, headerName, machoHeaderType); + + EnumerationBuilder cmdTypeBuilder; + cmdTypeBuilder.AddMemberWithValue("LC_REQ_DYLD", LC_REQ_DYLD); + cmdTypeBuilder.AddMemberWithValue("LC_SEGMENT", LC_SEGMENT); + cmdTypeBuilder.AddMemberWithValue("LC_SYMTAB", LC_SYMTAB); + cmdTypeBuilder.AddMemberWithValue("LC_SYMSEG",LC_SYMSEG); + cmdTypeBuilder.AddMemberWithValue("LC_THREAD", LC_THREAD); + cmdTypeBuilder.AddMemberWithValue("LC_UNIXTHREAD", LC_UNIXTHREAD); + cmdTypeBuilder.AddMemberWithValue("LC_LOADFVMLIB", LC_LOADFVMLIB); + cmdTypeBuilder.AddMemberWithValue("LC_IDFVMLIB", LC_IDFVMLIB); + cmdTypeBuilder.AddMemberWithValue("LC_IDENT", LC_IDENT); + cmdTypeBuilder.AddMemberWithValue("LC_FVMFILE", LC_FVMFILE); + cmdTypeBuilder.AddMemberWithValue("LC_PREPAGE", LC_PREPAGE); + cmdTypeBuilder.AddMemberWithValue("LC_DYSYMTAB", LC_DYSYMTAB); + cmdTypeBuilder.AddMemberWithValue("LC_LOAD_DYLIB", LC_LOAD_DYLIB); + cmdTypeBuilder.AddMemberWithValue("LC_ID_DYLIB", LC_ID_DYLIB); + cmdTypeBuilder.AddMemberWithValue("LC_LOAD_DYLINKER", LC_LOAD_DYLINKER); + cmdTypeBuilder.AddMemberWithValue("LC_ID_DYLINKER", LC_ID_DYLINKER); + cmdTypeBuilder.AddMemberWithValue("LC_PREBOUND_DYLIB", LC_PREBOUND_DYLIB); + cmdTypeBuilder.AddMemberWithValue("LC_ROUTINES", LC_ROUTINES); + cmdTypeBuilder.AddMemberWithValue("LC_SUB_FRAMEWORK", LC_SUB_FRAMEWORK); + cmdTypeBuilder.AddMemberWithValue("LC_SUB_UMBRELLA", LC_SUB_UMBRELLA); + cmdTypeBuilder.AddMemberWithValue("LC_SUB_CLIENT", LC_SUB_CLIENT); + cmdTypeBuilder.AddMemberWithValue("LC_SUB_LIBRARY", LC_SUB_LIBRARY); + cmdTypeBuilder.AddMemberWithValue("LC_TWOLEVEL_HINTS", LC_TWOLEVEL_HINTS); + cmdTypeBuilder.AddMemberWithValue("LC_PREBIND_CKSUM", LC_PREBIND_CKSUM); + cmdTypeBuilder.AddMemberWithValue("LC_LOAD_WEAK_DYLIB", LC_LOAD_WEAK_DYLIB);// (0x18 | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_SEGMENT_64", LC_SEGMENT_64); + cmdTypeBuilder.AddMemberWithValue("LC_ROUTINES_64", LC_ROUTINES_64); + cmdTypeBuilder.AddMemberWithValue("LC_UUID", LC_UUID); + cmdTypeBuilder.AddMemberWithValue("LC_RPATH", LC_RPATH);// (0x1c | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_CODE_SIGNATURE", LC_CODE_SIGNATURE); + cmdTypeBuilder.AddMemberWithValue("LC_SEGMENT_SPLIT_INFO", LC_SEGMENT_SPLIT_INFO); + cmdTypeBuilder.AddMemberWithValue("LC_REEXPORT_DYLIB", LC_REEXPORT_DYLIB);// (0x1f | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_LAZY_LOAD_DYLIB", LC_LAZY_LOAD_DYLIB); + cmdTypeBuilder.AddMemberWithValue("LC_ENCRYPTION_INFO", LC_ENCRYPTION_INFO); + cmdTypeBuilder.AddMemberWithValue("LC_DYLD_INFO", LC_DYLD_INFO); + cmdTypeBuilder.AddMemberWithValue("LC_DYLD_INFO_ONLY", LC_DYLD_INFO_ONLY);// (0x22 | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_LOAD_UPWARD_DYLIB", LC_LOAD_UPWARD_DYLIB);// (0x23 | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_MACOSX", LC_VERSION_MIN_MACOSX); + cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_IPHONEOS", LC_VERSION_MIN_IPHONEOS); + cmdTypeBuilder.AddMemberWithValue("LC_FUNCTION_STARTS", LC_FUNCTION_STARTS); + cmdTypeBuilder.AddMemberWithValue("LC_DYLD_ENVIRONMENT", LC_DYLD_ENVIRONMENT); + cmdTypeBuilder.AddMemberWithValue("LC_MAIN", LC_MAIN);// (0x28 | LC_REQ_DYLD) + cmdTypeBuilder.AddMemberWithValue("LC_DATA_IN_CODE", LC_DATA_IN_CODE); + cmdTypeBuilder.AddMemberWithValue("LC_SOURCE_VERSION", LC_SOURCE_VERSION); + cmdTypeBuilder.AddMemberWithValue("LC_DYLIB_CODE_SIGN_DRS", LC_DYLIB_CODE_SIGN_DRS); + cmdTypeBuilder.AddMemberWithValue("LC_ENCRYPTION_INFO_64", _LC_ENCRYPTION_INFO_64); + cmdTypeBuilder.AddMemberWithValue("LC_LINKER_OPTION", _LC_LINKER_OPTION); + cmdTypeBuilder.AddMemberWithValue("LC_LINKER_OPTIMIZATION_HINT", _LC_LINKER_OPTIMIZATION_HINT); + cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_TVOS", _LC_VERSION_MIN_TVOS); + cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_WATCHOS", LC_VERSION_MIN_WATCHOS); + cmdTypeBuilder.AddMemberWithValue("LC_NOTE", LC_NOTE); + cmdTypeBuilder.AddMemberWithValue("LC_BUILD_VERSION", LC_BUILD_VERSION); + cmdTypeBuilder.AddMemberWithValue("LC_DYLD_EXPORTS_TRIE", LC_DYLD_EXPORTS_TRIE); + cmdTypeBuilder.AddMemberWithValue("LC_DYLD_CHAINED_FIXUPS", LC_DYLD_CHAINED_FIXUPS); + cmdTypeBuilder.AddMemberWithValue("LC_FILESET_ENTRY", LC_FILESET_ENTRY); + Ref<Enumeration> cmdTypeEnum = cmdTypeBuilder.Finalize(); + + Ref<Type> cmdTypeEnumType = Type::EnumerationType(nullptr, cmdTypeEnum, 4, false); + string cmdTypeEnumName = "load_command_type_t"; + string cmdTypeEnumId = Type::GenerateAutoTypeId("macho", cmdTypeEnumName); + m_typeNames.cmdTypeEnumQualName = DefineType(cmdTypeEnumId, cmdTypeEnumName, cmdTypeEnumType); + + StructureBuilder loadCommandBuilder; + loadCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + loadCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + Ref<Structure> loadCommandStruct = loadCommandBuilder.Finalize(); + QualifiedName loadCommandName = string("load_command"); + string loadCommandTypeId = Type::GenerateAutoTypeId("macho", loadCommandName); + Ref<Type> loadCommandType = Type::StructureType(loadCommandStruct); + m_typeNames.loadCommandQualName = DefineType(loadCommandTypeId, loadCommandName, loadCommandType); + + EnumerationBuilder protTypeBuilder; + protTypeBuilder.AddMemberWithValue("VM_PROT_NONE", MACHO_VM_PROT_NONE); + protTypeBuilder.AddMemberWithValue("VM_PROT_READ", MACHO_VM_PROT_READ); + protTypeBuilder.AddMemberWithValue("VM_PROT_WRITE", MACHO_VM_PROT_WRITE); + protTypeBuilder.AddMemberWithValue("VM_PROT_EXECUTE", MACHO_VM_PROT_EXECUTE); + // protTypeBuilder.AddMemberWithValue("VM_PROT_DEFAULT", MACHO_VM_PROT_DEFAULT); + // protTypeBuilder.AddMemberWithValue("VM_PROT_ALL", MACHO_VM_PROT_ALL); + protTypeBuilder.AddMemberWithValue("VM_PROT_NO_CHANGE", MACHO_VM_PROT_NO_CHANGE); + protTypeBuilder.AddMemberWithValue("VM_PROT_COPY_OR_WANTS_COPY", MACHO_VM_PROT_COPY); + //protTypeBuilder.AddMemberWithValue("VM_PROT_WANTS_COPY", MACHO_VM_PROT_WANTS_COPY); + Ref<Enumeration> protTypeEnum = protTypeBuilder.Finalize(); + + Ref<Type> protTypeEnumType = Type::EnumerationType(nullptr, protTypeEnum, 4, false); + string protTypeEnumName = "vm_prot_t"; + string protTypeEnumId = Type::GenerateAutoTypeId("macho", protTypeEnumName); + m_typeNames.protTypeEnumQualName = DefineType(protTypeEnumId, protTypeEnumName, protTypeEnumType); + + EnumerationBuilder segFlagsTypeBuilder; + segFlagsTypeBuilder.AddMemberWithValue("SG_HIGHVM", SG_HIGHVM); + segFlagsTypeBuilder.AddMemberWithValue("SG_FVMLIB", SG_FVMLIB); + segFlagsTypeBuilder.AddMemberWithValue("SG_NORELOC", SG_NORELOC); + segFlagsTypeBuilder.AddMemberWithValue("SG_PROTECTED_VERSION_1", SG_PROTECTED_VERSION_1); + Ref<Enumeration> segFlagsTypeEnum = segFlagsTypeBuilder.Finalize(); + + Ref<Type> segFlagsTypeEnumType = Type::EnumerationType(nullptr, segFlagsTypeEnum, 4, false); + string segFlagsTypeEnumName = "sg_flags_t"; + string segFlagsTypeEnumId = Type::GenerateAutoTypeId("macho", segFlagsTypeEnumName); + m_typeNames.segFlagsTypeEnumQualName = DefineType(segFlagsTypeEnumId, segFlagsTypeEnumName, segFlagsTypeEnumType); + + StructureBuilder loadSegmentCommandBuilder; + loadSegmentCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + loadSegmentCommandBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "vmaddr"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "vmsize"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "fileoff"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "filesize"); + loadSegmentCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.protTypeEnumQualName), "maxprot"); + loadSegmentCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.protTypeEnumQualName), "initprot"); + loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "nsects"); + loadSegmentCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.segFlagsTypeEnumQualName), "flags"); + Ref<Structure> loadSegmentCommandStruct = loadSegmentCommandBuilder.Finalize(); + QualifiedName loadSegmentCommandName = string("segment_command"); + string loadSegmentCommandTypeId = Type::GenerateAutoTypeId("macho", loadSegmentCommandName); + Ref<Type> loadSegmentCommandType = Type::StructureType(loadSegmentCommandStruct); + m_typeNames.loadSegmentCommandQualName = DefineType(loadSegmentCommandTypeId, loadSegmentCommandName, loadSegmentCommandType); + + StructureBuilder loadSegmentCommand64Builder; + loadSegmentCommand64Builder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(4, false), "cmdsize"); + loadSegmentCommand64Builder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "vmaddr"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "vmsize"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "fileoff"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "filesize"); + loadSegmentCommand64Builder.AddMember(Type::NamedType(this, m_typeNames.protTypeEnumQualName), "maxprot"); + loadSegmentCommand64Builder.AddMember(Type::NamedType(this, m_typeNames.protTypeEnumQualName), "initprot"); + loadSegmentCommand64Builder.AddMember(Type::IntegerType(4, false), "nsects"); + loadSegmentCommand64Builder.AddMember(Type::NamedType(this, m_typeNames.segFlagsTypeEnumQualName), "flags"); + Ref<Structure> loadSegmentCommand64Struct = loadSegmentCommand64Builder.Finalize(); + QualifiedName loadSegment64CommandName = string("segment_command_64"); + string loadSegment64CommandTypeId = Type::GenerateAutoTypeId("macho", loadSegment64CommandName); + Ref<Type> loadSegment64CommandType = Type::StructureType(loadSegmentCommand64Struct); + m_typeNames.loadSegment64CommandQualName = DefineType(loadSegment64CommandTypeId, loadSegment64CommandName, loadSegment64CommandType); + + StructureBuilder sectionBuilder; + sectionBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "sectname"); + sectionBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "addr"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "size"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "offset"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "align"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "reloff"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "nreloc"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "flags"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "reserved1"); + sectionBuilder.AddMember(Type::IntegerType(4, false), "reserved2"); + Ref<Structure> sectionStruct = sectionBuilder.Finalize(); + QualifiedName sectionName = string("section"); + string sectionTypeId = Type::GenerateAutoTypeId("macho", sectionName); + Ref<Type> sectionType = Type::StructureType(sectionStruct); + m_typeNames.sectionQualName = DefineType(sectionTypeId, sectionName, sectionType); + + StructureBuilder section64Builder; + section64Builder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "sectname"); + section64Builder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); + section64Builder.AddMember(Type::IntegerType(8, false), "addr"); + section64Builder.AddMember(Type::IntegerType(8, false), "size"); + section64Builder.AddMember(Type::IntegerType(4, false), "offset"); + section64Builder.AddMember(Type::IntegerType(4, false), "align"); + section64Builder.AddMember(Type::IntegerType(4, false), "reloff"); + section64Builder.AddMember(Type::IntegerType(4, false), "nreloc"); + section64Builder.AddMember(Type::IntegerType(4, false), "flags"); + section64Builder.AddMember(Type::IntegerType(4, false), "reserved1"); + section64Builder.AddMember(Type::IntegerType(4, false), "reserved2"); + section64Builder.AddMember(Type::IntegerType(4, false), "reserved3"); + Ref<Structure> section64Struct = section64Builder.Finalize(); + QualifiedName section64Name = string("section_64"); + string section64TypeId = Type::GenerateAutoTypeId("macho", section64Name); + Ref<Type> section64Type = Type::StructureType(section64Struct); + m_typeNames.section64QualName = DefineType(section64TypeId, section64Name, section64Type); + + StructureBuilder symtabBuilder; + symtabBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + symtabBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + symtabBuilder.AddMember(Type::IntegerType(4, false), "symoff"); + symtabBuilder.AddMember(Type::IntegerType(4, false), "nsyms"); + symtabBuilder.AddMember(Type::IntegerType(4, false), "stroff"); + symtabBuilder.AddMember(Type::IntegerType(4, false), "strsize"); + Ref<Structure> symtabStruct = symtabBuilder.Finalize(); + QualifiedName symtabName = string("symtab"); + string symtabTypeId = Type::GenerateAutoTypeId("macho", symtabName); + Ref<Type> symtabType = Type::StructureType(symtabStruct); + m_typeNames.symtabQualName = DefineType(symtabTypeId, symtabName, symtabType); + + StructureBuilder dynsymtabBuilder; + dynsymtabBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "ilocalsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nlocalsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "iextdefsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nextdefsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "iundefsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nundefsym"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "tocoff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "ntoc"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "modtaboff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nmodtab"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "extrefsymoff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nextrefsyms"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "indirectsymoff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nindirectsyms"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "extreloff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nextrel"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "locreloff"); + dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nlocrel"); + Ref<Structure> dynsymtabStruct = dynsymtabBuilder.Finalize(); + QualifiedName dynsymtabName = string("dynsymtab"); + string dynsymtabTypeId = Type::GenerateAutoTypeId("macho", dynsymtabName); + Ref<Type> dynsymtabType = Type::StructureType(dynsymtabStruct); + m_typeNames.dynsymtabQualName = DefineType(dynsymtabTypeId, dynsymtabName, dynsymtabType); + + StructureBuilder uuidBuilder; + uuidBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + uuidBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + uuidBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, false), 16), "uuid"); + Ref<Structure> uuidStruct = uuidBuilder.Finalize(); + QualifiedName uuidName = string("uuid"); + string uuidTypeId = Type::GenerateAutoTypeId("macho", uuidName); + Ref<Type> uuidType = Type::StructureType(uuidStruct); + m_typeNames.uuidQualName = DefineType(uuidTypeId, uuidName, uuidType); + + StructureBuilder linkeditDataBuilder; + linkeditDataBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + linkeditDataBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + linkeditDataBuilder.AddMember(Type::IntegerType(4, false), "dataoff"); + linkeditDataBuilder.AddMember(Type::IntegerType(4, false), "datasize"); + Ref<Structure> linkeditDataStruct = linkeditDataBuilder.Finalize(); + QualifiedName linkeditDataName = string("linkedit_data"); + string linkeditDataTypeId = Type::GenerateAutoTypeId("macho", linkeditDataName); + Ref<Type> linkeditDataType = Type::StructureType(linkeditDataStruct); + m_typeNames.linkeditDataQualName = DefineType(linkeditDataTypeId, linkeditDataName, linkeditDataType); + + StructureBuilder encryptionInfoBuilder; + encryptionInfoBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cryptoff"); + encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cryptsize"); + encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cryptid"); + Ref<Structure> encryptionInfoStruct = encryptionInfoBuilder.Finalize(); + QualifiedName encryptionInfoName = string("encryption_info"); + string encryptionInfoTypeId = Type::GenerateAutoTypeId("macho", encryptionInfoName); + Ref<Type> encryptionInfoType = Type::StructureType(encryptionInfoStruct); + m_typeNames.encryptionInfoQualName = DefineType(encryptionInfoTypeId, encryptionInfoName, encryptionInfoType); + + StructureBuilder versionMinBuilder; + versionMinBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + versionMinBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + versionMinBuilder.AddMember(Type::IntegerType(4, false), "version"); + versionMinBuilder.AddMember(Type::IntegerType(4, false), "sdk"); + Ref<Structure> versionMinStruct = versionMinBuilder.Finalize(); + QualifiedName versionMinName = string("version_min"); + string versionMinTypeId = Type::GenerateAutoTypeId("macho", versionMinName); + Ref<Type> versionMinType = Type::StructureType(versionMinStruct); + m_typeNames.versionMinQualName = DefineType(versionMinTypeId, versionMinName, versionMinType); + + StructureBuilder dyldInfoBuilder; + dyldInfoBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "rebase_off"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "rebase_size"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "bind_off"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "bind_size"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "weak_bind_off"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "weak_bind_size"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "lazy_bind_off"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "lazy_bind_size"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "export_off"); + dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "export_size"); + Ref<Structure> dyldInfoStruct = dyldInfoBuilder.Finalize(); + QualifiedName dyldInfoName = string("dyld_info"); + string dyldInfoTypeId = Type::GenerateAutoTypeId("macho", dyldInfoName); + Ref<Type> dyldInfoType = Type::StructureType(dyldInfoStruct); + m_typeNames.dyldInfoQualName = DefineType(dyldInfoTypeId, dyldInfoName, dyldInfoType); + + StructureBuilder dylibBuilder; + dylibBuilder.AddMember(Type::IntegerType(4, false), "name"); + dylibBuilder.AddMember(Type::IntegerType(4, false), "timestamp"); + dylibBuilder.AddMember(Type::IntegerType(4, false), "current_version"); + dylibBuilder.AddMember(Type::IntegerType(4, false), "compatibility_version"); + Ref<Structure> dylibStruct = dylibBuilder.Finalize(); + QualifiedName dylibName = string("dylib"); + string dylibTypeId = Type::GenerateAutoTypeId("macho", dylibName); + Ref<Type> dylibType = Type::StructureType(dylibStruct); + m_typeNames.dylibQualName = DefineType(dylibTypeId, dylibName, dylibType); + + StructureBuilder dylibCommandBuilder; + dylibCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + dylibCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + dylibCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.dylibQualName), "dylib"); + Ref<Structure> dylibCommandStruct = dylibCommandBuilder.Finalize(); + QualifiedName dylibCommandName = string("dylib_command"); + string dylibCommandTypeId = Type::GenerateAutoTypeId("macho", dylibCommandName); + Ref<Type> dylibCommandType = Type::StructureType(dylibCommandStruct); + m_typeNames.dylibCommandQualName = DefineType(dylibCommandTypeId, dylibCommandName, dylibCommandType); + + StructureBuilder filesetEntryCommandBuilder; + filesetEntryCommandBuilder.AddMember(Type::NamedType(this, m_typeNames.cmdTypeEnumQualName), "cmd"); + filesetEntryCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + filesetEntryCommandBuilder.AddMember(Type::IntegerType(8, false), "vmaddr"); + filesetEntryCommandBuilder.AddMember(Type::IntegerType(8, false), "fileoff"); + filesetEntryCommandBuilder.AddMember(Type::IntegerType(4, false), "entry_id"); + filesetEntryCommandBuilder.AddMember(Type::IntegerType(4, false), "reserved"); + Ref<Structure> filesetEntryCommandStruct = filesetEntryCommandBuilder.Finalize(); + QualifiedName filesetEntryCommandName = string("fileset_entry_command"); + string filesetEntryCommandTypeId = Type::GenerateAutoTypeId("macho", filesetEntryCommandName); + Ref<Type> filesetEntryCommandType = Type::StructureType(filesetEntryCommandStruct); + m_typeNames.filesetEntryCommandQualName = DefineType(filesetEntryCommandTypeId, filesetEntryCommandName, filesetEntryCommandType); + + if (!InitializeHeader(m_header, true, preferredImageBase, preferredImageBaseDesc)) + return false; + + for (auto& it : m_subHeaders) + { + if (!InitializeHeader(it.second, false, it.first, "")) + return false; + } + + std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now(); + double t = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count() / 1000.0; + m_logger->LogInfo("Mach-O parsing took %.3f seconds\n", t); + return true; +} + + +bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_t preferredImageBase, std::string preferredImageBaseDesc) +{ + Ref<Settings> settings = GetLoadSettings(GetTypeName()); + + for (auto& i : header.segments) + i.vmaddr += m_imageBaseAdjustment; + + for (auto& i : header.sections) + i.addr += m_imageBaseAdjustment; + + for (auto& i : header.symbolStubSections) + i.addr += m_imageBaseAdjustment; + + for (auto& i : header.symbolPointerSections) + i.addr += m_imageBaseAdjustment; + + for (auto& entryPoint : header.entryPoints) + entryPoint.first += (entryPoint.second ? 0 : m_imageBaseAdjustment); + + if (header.routinesPresent) + header.routines64.init_address += m_imageBaseAdjustment; + + for (auto& segment : header.segments) + { + if ((segment.initprot == MACHO_VM_PROT_NONE) || (!segment.vmsize)) + continue; + + if ((segment.initprot & MACHO_VM_PROT_EXECUTE) == MACHO_VM_PROT_EXECUTE) + { + if ((segment.fileoff == (0 + m_universalImageOffset)) && (segment.filesize != 0)) + header.textBase = segment.vmaddr; + for (auto& entryPoint : header.entryPoints) + { + uint64_t val = entryPoint.first + (entryPoint.second ? header.textBase : 0); + if (find(header.m_entryPoints.begin(), header.m_entryPoints.end(), val) == header.m_entryPoints.end()) + header.m_entryPoints.push_back(val); + } + } + } + + if (!(m_header.ident.filetype == MH_FILESET && isMainHeader)) \ + { + for (auto &segment: header.segments) { + if ((segment.initprot == MACHO_VM_PROT_NONE) || (!segment.vmsize)) + continue; + + uint32_t flags = 0; + if (segment.initprot & MACHO_VM_PROT_READ) + flags |= SegmentReadable; + if (segment.initprot & MACHO_VM_PROT_WRITE) + flags |= SegmentWritable; + if (segment.initprot & MACHO_VM_PROT_EXECUTE) + flags |= SegmentExecutable; + if (((segment.initprot & MACHO_VM_PROT_WRITE) == 0) && + ((segment.maxprot & MACHO_VM_PROT_WRITE) == 0)) + flags |= SegmentDenyWrite; + if (((segment.initprot & MACHO_VM_PROT_EXECUTE) == 0) && + ((segment.maxprot & MACHO_VM_PROT_EXECUTE) == 0)) + flags |= SegmentDenyExecute; + + // if we're positive we have an entry point for some reason, force the segment + // executable. this helps with kernel images. + for (auto &entryPoint: header.m_entryPoints) + if (segment.vmaddr <= entryPoint && (entryPoint < (segment.vmaddr + segment.filesize))) + flags |= SegmentExecutable; + + AddAutoSegment(segment.vmaddr, segment.vmsize, segment.fileoff, segment.filesize, flags); + } + for (auto& section : header.sections) + { + char sectionName[17]; + memcpy(sectionName, section.sectname, sizeof(section.sectname)); + sectionName[16] = 0; + if (header.identifierPrefix.empty()) + header.sectionNames.push_back(sectionName); + else + header.sectionNames.push_back(header.identifierPrefix + "::" + sectionName); + } + + header.sectionNames = GetUniqueSectionNames(header.sectionNames); + } + + for (size_t i = 0; i < header.sections.size(); i++) + { + if (!header.sections[i].size) + continue; + + string type; + BNSectionSemantics semantics = DefaultSectionSemantics; + switch (header.sections[i].flags & 0xff) + { + case S_REGULAR: + if (header.sections[i].flags & S_ATTR_PURE_INSTRUCTIONS) + { + type = "PURE_CODE"; + semantics = ReadOnlyCodeSectionSemantics; + } + else if (header.sections[i].flags & S_ATTR_SOME_INSTRUCTIONS) + { + type = "CODE"; + semantics = ReadOnlyCodeSectionSemantics; + } + else + { + type = "REGULAR"; + } + break; + case S_ZEROFILL: + type = "ZEROFILL"; + semantics = ReadWriteDataSectionSemantics; + break; + case S_CSTRING_LITERALS: + type = "CSTRING_LITERALS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_4BYTE_LITERALS: + type = "4BYTE_LITERALS"; + break; + case S_8BYTE_LITERALS: + type = "8BYTE_LITERALS"; + break; + case S_LITERAL_POINTERS: + type = "LITERAL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_NON_LAZY_SYMBOL_POINTERS: + type = "NON_LAZY_SYMBOL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_LAZY_SYMBOL_POINTERS: + type = "LAZY_SYMBOL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_SYMBOL_STUBS: + type = "SYMBOL_STUBS"; + semantics = ReadOnlyCodeSectionSemantics; + break; + case S_MOD_INIT_FUNC_POINTERS: + type = "MOD_INIT_FUNC_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_MOD_TERM_FUNC_POINTERS: + type = "MOD_TERM_FUNC_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_COALESCED: + type = "COALESCED"; + break; + case S_GB_ZEROFILL: + type = "GB_ZEROFILL"; + semantics = ReadWriteDataSectionSemantics; + break; + case S_INTERPOSING: + type = "INTERPOSING"; + break; + case S_16BYTE_LITERALS: + type = "16BYTE_LITERALS"; + break; + case S_DTRACE_DOF: + type = "DTRACE_DOF"; + break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: + type = "LAZY_DYLIB_SYMBOL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_THREAD_LOCAL_REGULAR: + type = "THREAD_LOCAL_REGULAR"; + break; + case S_THREAD_LOCAL_ZEROFILL: + type = "THREAD_LOCAL_ZEROFILL"; + break; + case S_THREAD_LOCAL_VARIABLES: + type = "THREAD_LOCAL_VARIABLES"; + break; + case S_THREAD_LOCAL_VARIABLE_POINTERS: + type = "THREAD_LOCAL_VARIABLE_POINTERS"; + break; + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + type = "THREAD_LOCAL_INIT_FUNCTION_POINTERS"; + break; + default: + type = "UNKNOWN"; + break; + } + if (i >= header.sectionNames.size()) + break; + if (strncmp(header.sections[i].sectname, "__text", sizeof(header.sections[i].sectname)) == 0) + semantics = ReadOnlyCodeSectionSemantics; + if (strncmp(header.sections[i].sectname, "__const", sizeof(header.sections[i].sectname)) == 0) + semantics = ReadOnlyDataSectionSemantics; + if (strncmp(header.sections[i].sectname, "__data", sizeof(header.sections[i].sectname)) == 0) + semantics = ReadWriteDataSectionSemantics; + if (strncmp(header.sections[i].segname, "__DATA_CONST", sizeof(header.sections[i].segname)) == 0) + semantics = ReadOnlyDataSectionSemantics; + + AddAutoSection(header.sectionNames[i], header.sections[i].addr, header.sections[i].size, semantics, type, header.sections[i].align); + } + if (isMainHeader) + { + // Validate architecture + if (!m_arch) + { + // Parse only mode returns true, even if no arch support + if (m_parseOnly) + return true; + + bool is64Bit; + string archName = UniversalViewType::ArchitectureToString(m_archId, 0, is64Bit); + if (!archName.empty()) + { + #ifdef DEMO_VERSION + if ((archName == "arm64") /* MACHO_CPU_TYPE_ARM64 */ + || (archName == "arm64v8") + || (archName == "arm64e") + || (archName == "arm64_32") /* MACHO_CPU_TYPE_ARM64_32 */ + || (archName == "arm64_32v8") + || (archName == "ppc") /* MACHO_CPU_TYPE_POWERPC */ + || (archName == "ppc601") + || (archName == "ppc602") + || (archName == "ppc603") + || (archName == "ppc603e") + || (archName == "ppc603ev") + || (archName == "ppc604") + || (archName == "ppc604e") + || (archName == "ppc620") + || (archName == "ppc750") + || (archName == "ppc7400") + || (archName == "ppc7450") + || (archName == "ppc970") + || (archName == "ppc64") /* MACHO_CPU_TYPE_POWERPC64 */ + ) + { + m_logger->LogError( + "Binary Ninja free does not support Mach-O architecture '%s'. " + "Purchase Binary Ninja to unlock all features.", + archName.c_str()); + return false; + } + #endif + + m_logger->LogError( + "Mach-O architecture '%s' is not explicitly supported. Try 'Open with Options' to manually select " + "a compatible architecture.", + archName.c_str()); + } + else + { + m_logger->LogError( + "Mach-O architecture 0x%x is not explicitly supported. Try 'Open with Options' to manually select " + "a compatible architecture.", + m_archId); + } + + return false; + } + + // Apply architecture and platform + Ref<Platform> platform = m_plat ? m_plat : g_machoViewType->GetPlatform(0, m_arch); + if (!platform) + platform = m_arch->GetStandalonePlatform(); + + if (header.m_entryPoints.size() > 0) + platform = platform->GetAssociatedPlatformByAddress(header.m_entryPoints[0]); + + if (settings && settings->Contains("loader.platform")) // handle overrides + { + Ref<Platform> platformOverride = Platform::GetByName(settings->Get<string>("loader.platform", this)); + if (platformOverride) + platform = platformOverride; + } + + SetDefaultPlatform(platform); + SetDefaultArchitecture(platform->GetArchitecture()); + + // Finished for parse only mode + if (m_parseOnly) + return true; + } + + if (m_parseOnly) + return true; + + BinaryReader reader(GetParentView()); + reader.SetEndianness(m_endian); + reader.SetVirtualBase(m_universalImageOffset); + BinaryReader virtualReader(this); + virtualReader.SetEndianness(m_endian); + + Ref<Platform> platform = m_plat ? m_plat : g_machoViewType->GetPlatform(0, m_arch); + + + // parse thread starts section if available + bool rebaseThreadStarts = false; + auto theadStartSection = GetSectionByName("__thread_starts"); + vector<uint32_t> threadStarts; + uint64_t stepMultiplier; + if (theadStartSection) + { + size_t count = theadStartSection->GetLength() / 4; + threadStarts.reserve(count); + virtualReader.Seek(theadStartSection->GetStart()); + stepMultiplier = virtualReader.Read32() & 0x1 ? 8 : 4; + for (uint32_t i = 1; i < count; i++) + threadStarts.push_back(virtualReader.Read32()); + + rebaseThreadStarts = true; + if (settings && settings->Contains("loader.macho.rebaseThreadStarts")) + rebaseThreadStarts = settings->Get<bool>("loader.macho.rebaseThreadStarts", this); + } + + if (rebaseThreadStarts) + RebaseThreadStarts(virtualReader, threadStarts, stepMultiplier); + + // Add module Init functions if they exist + size_t modInitFuncCnt = 0; + for (const auto& moduleInitSection : header.moduleInitSections) + { + // ignore mod_init functions that are rebased as part of thread starts + if (find(threadStarts.begin(), threadStarts.end(), moduleInitSection.offset) != threadStarts.end()) + continue; + + // The mod_init section contains a list of function pointers called at initialization + // if we don't have a defined entrypoint then use the first one in the list as the entrypoint + size_t i = 0; + reader.Seek(moduleInitSection.offset); + for (; i < (moduleInitSection.size / m_addressSize); i++) + { + uint64_t target = (m_addressSize == 4) ? reader.Read32() : reader.Read64(); + target += m_imageBaseAdjustment; + Ref<Platform> targetPlatform = platform->GetAssociatedPlatformByAddress(target); + DefineMachoSymbol(FunctionSymbol, "mod_init_func_" + to_string(modInitFuncCnt++), target, GlobalBinding, false); + if (m_header.ident.filetype == MH_FILESET) + { + // FIXME: This isn't a super robust way of detagging, + // should look into xnu source and the tools used to build this cache (if they're public) + // and see if anything better can be done + + // mask out top 8 bits + uint64_t tag = 0xFFFFFFFF00000000 & header.textBase; + // and combine them with bottom 8 of the original entry + target = tag | (target & 0xFFFFFFFF); + } + AddEntryPointForAnalysis(targetPlatform, target); + } + } + + if (isMainHeader) + { + vector<Ref<Metadata>> libraries; + vector<Ref<Metadata>> libraryFound; + for (auto& libName : header.dylibs) + { + if (!GetExternalLibrary(libName)) + { + AddExternalLibrary(libName, {}, true); + } + libraries.push_back(new Metadata(string(libName))); + Ref<TypeLibrary> typeLib = GetTypeLibrary(libName); + if (!typeLib) + { + vector<Ref<TypeLibrary>> typeLibs = platform->GetTypeLibrariesByName(libName); + if (typeLibs.size()) + { + typeLib = typeLibs[0]; + AddTypeLibrary(typeLib); + + m_logger->LogDebug("mach-o: adding type library for '%s': %s (%s)", + libName.c_str(), typeLib->GetName().c_str(), typeLib->GetGuid().c_str()); + } + } + + if (typeLib) + libraryFound.push_back(new Metadata(typeLib->GetName())); + else + libraryFound.push_back(new Metadata(string(""))); + } + StoreMetadata("Libraries", new Metadata(libraries), true); + StoreMetadata("LibraryFound", new Metadata(libraryFound), true); + } + + bool first = true; + for (auto entry : header.m_entryPoints) + { + if (m_header.ident.filetype == MH_FILESET) + { + // FIXME: This isn't a super robust way of detagging, + // should look into xnu source and the tools used to build this cache (if they're public) + // and see if anything better can be done + + // mask out top 8 bits + uint64_t tag = 0xFFFFFFFF00000000 & header.textBase; + // and combine them with bottom 8 of the original entry + entry = tag | (entry & 0xFFFFFFFF); + } + AddEntryPointForAnalysis(platform, entry); + if (first) + { + first = false; + DefineAutoSymbol(new Symbol(FunctionSymbol, "_start", entry)); + } + } + + vector<uint32_t> indirectSymbols; + try + { + // Handle indirect symbols + if (header.dysymtab.nindirectsyms) + { + indirectSymbols.resize(header.dysymtab.nindirectsyms); + reader.Seek(header.dysymtab.indirectsymoff); + reader.Read(&indirectSymbols[0], header.dysymtab.nindirectsyms * sizeof(uint32_t)); + } + } + catch (ReadException&) + { + m_logger->LogError("Failed to read indirect symbol data"); + } + + bool parseFunctionStarts = true; + if (settings && settings->Contains("loader.macho.processFunctionStarts")) + parseFunctionStarts = settings->Get<bool>("loader.macho.processFunctionStarts", this); + + if (parseFunctionStarts) + { + m_logger->LogDebug("Parsing function starts\n"); + if (header.functionStartsPresent) + ParseFunctionStarts(platform, header.textBase, header.functionStarts); + } + + BeginBulkModifySymbols(); + m_symbolQueue = new SymbolQueue(); + + try + { + // Add functions for all function symbols + m_logger->LogDebug("Parsing symbol table\n"); + ParseSymbolTable(reader, header, header.symtab, indirectSymbols); + } + catch (std::exception&) + { + m_logger->LogError("Failed to parse symbol table!"); + } + + m_symbolQueue->Process(); + delete m_symbolQueue; + m_symbolQueue = nullptr; + + EndBulkModifySymbols(); + + auto relocationHandler = m_arch->GetRelocationHandler("Mach-O"); + if (relocationHandler) + { + // FIXME: this if statement block really needs to be a function + try + { + // For executables the relocations are attached to each of the sections + // In libraries these are zeroed out and collected in the dysymtab + vector<BNRelocationInfo> infoList; + for (auto& section : header.sections) + { + if (section.nreloc == 0) + continue; + + char sectionName[17]; + memcpy(sectionName, section.sectname, sizeof(section.sectname)); + sectionName[16] = 0; + + m_logger->LogDebug("Relocations for section %s", sectionName); + auto sec = GetSectionByName(sectionName); + if (!sec) + { + m_logger->LogError("Can't find section for %s", sectionName); + continue; + } + for (size_t i = 0; i < section.nreloc; i++) + { + relocation_info info; + reader.Seek(section.reloff + (i * sizeof(relocation_info))); + reader.Read(&info, sizeof(info)); + BNRelocationInfo result; + memset(&result, 0, sizeof(result)); + if (ParseRelocationEntry(info, sec->GetStart(), result)) + infoList.push_back(result); + } + } + + if (relocationHandler->GetRelocationInfo(this, m_arch, infoList)) + { + for (auto& reloc: infoList) + { + if (reloc.symbolIndex >= m_symbols.size()) + continue; + + // retrieve first symbol that is not a symbol relocation + auto symbols = GetSymbolsByName(m_symbols[reloc.symbolIndex]); + for (const auto& symbol : symbols) + { + if (symbol->GetAddress() == reloc.address) + continue; + DefineRelocation(m_arch, reloc, symbol, reloc.address); + break; + } + } + } + infoList.clear(); + + // Handle local relocations for dynamic libraries + for (size_t i = 0; i < header.dysymtab.nlocrel; i++) + { + relocation_info info; + reader.Seek(header.dysymtab.locreloff + (i * sizeof(relocation_info))); + reader.Read(&info, sizeof(info)); + BNRelocationInfo result; + memset(&result, 0, sizeof(result)); + if (ParseRelocationEntry(info, header.relocationBase, result)) + infoList.push_back(result); + } + + if (relocationHandler->GetRelocationInfo(this, m_arch, infoList)) + { + for (auto& reloc: infoList) + { + // TODO will matter once rebasing lands + if (!reloc.external) + continue; + + if (reloc.symbolIndex >= m_symbols.size()) + continue; + + // retrieve first symbol that is not a symbol relocation + auto symbols = GetSymbolsByName(m_symbols[reloc.symbolIndex]); + for (const auto& symbol : symbols) + { + if (symbol->GetAddress() == reloc.address) + continue; + DefineRelocation(m_arch, reloc, symbol, reloc.address); + break; + } + } + } + infoList.clear(); + + // Handle external relocations for dynamic libraries + for (size_t i = 0; i < header.dysymtab.nextrel; i++) + { + relocation_info info; + reader.Seek(header.dysymtab.extreloff + (i * sizeof(relocation_info))); + reader.Read(&info, sizeof(info)); + BNRelocationInfo result; + memset(&result, 0, sizeof(result)); + if (ParseRelocationEntry(info, header.relocationBase, result)) + infoList.push_back(result); + } + + if (relocationHandler->GetRelocationInfo(this, m_arch, infoList)) + { + for (auto& reloc: infoList) + { + // TODO will matter once rebasing lands + if (!reloc.external) + continue; + + if (reloc.symbolIndex >= m_symbols.size()) + continue; + + // retrieve first symbol that is not a symbol relocation + auto symbols = GetSymbolsByName(m_symbols[reloc.symbolIndex]); + for (const auto& symbol : symbols) + { + if (symbol->GetAddress() == reloc.address) + continue; + DefineRelocation(m_arch, reloc, symbol, reloc.address); + break; + } + } + } + infoList.clear(); + } + catch (ReadException&) + { + m_logger->LogError("Failed to read relocation data"); + } + } + + vector<std::tuple<uint64_t, string>> machoHeaderStarts; + if (isMainHeader) { + machoHeaderStarts.emplace_back(header.textBase, ""); + if (header.textBase != preferredImageBase) + machoHeaderStarts.emplace_back(preferredImageBase, preferredImageBaseDesc); + } + else { + machoHeaderStarts.emplace_back(preferredImageBase, preferredImageBaseDesc); + } + + // Apply Mach-O header types + for (auto [imageBase, imageDesc] : machoHeaderStarts) + { + string errorMsg; + mach_header_64 mappedIdent; + uint64_t loadCommandOffset; + loadCommandOffset = g_machoViewType->ParseHeaders(this, imageBase, mappedIdent, nullptr, nullptr, errorMsg); + if (!loadCommandOffset) + continue; + + DefineDataVariable(imageBase, Type::NamedType(this, m_typeNames.headerQualName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__macho_header" + imageDesc, imageBase, LocalBinding)); + + try + { + virtualReader.Seek(imageBase + loadCommandOffset); + size_t sectionNum = 0; + for (size_t i = 0; i < mappedIdent.ncmds; i++) + { + load_command load; + uint64_t curOffset = virtualReader.GetOffset(); + load.cmd = virtualReader.Read32(); + load.cmdsize = virtualReader.Read32(); + uint64_t nextOffset = curOffset + load.cmdsize; + switch (load.cmd) + { + case LC_SEGMENT: + { + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.loadSegmentCommandQualName)); + virtualReader.SeekRelative(5 * 8); + size_t numSections = virtualReader.Read32(); + virtualReader.SeekRelative(4); + for (size_t j = 0; j < numSections; j++) + { + DefineDataVariable(virtualReader.GetOffset(), Type::NamedType(this, m_typeNames.sectionQualName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__macho_section" + imageDesc + "_[" + to_string(sectionNum++) + "]", virtualReader.GetOffset(), LocalBinding)); + virtualReader.SeekRelative((8 * 8) + 4); + } + break; + } + case LC_SEGMENT_64: + { + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.loadSegment64CommandQualName)); + virtualReader.SeekRelative(7 * 8); + size_t numSections = virtualReader.Read32(); + virtualReader.SeekRelative(4); + for (size_t j = 0; j < numSections; j++) + { + DefineDataVariable(virtualReader.GetOffset(), Type::NamedType(this, m_typeNames.section64QualName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__macho_section_64" + imageDesc + "_[" + to_string(sectionNum++) + "]", virtualReader.GetOffset(), LocalBinding)); + virtualReader.SeekRelative(10 * 8); + } + break; + } + case LC_SYMTAB: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.symtabQualName)); + break; + case LC_DYSYMTAB: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.dynsymtabQualName)); + break; + case LC_UUID: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.uuidQualName)); + break; + case LC_ID_DYLIB: + case LC_LOAD_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.dylibCommandQualName)); + if (load.cmdsize-24 <= 150) + DefineDataVariable(curOffset + 24, Type::ArrayType(Type::IntegerType(1, true), load.cmdsize-24)); + break; + case LC_CODE_SIGNATURE: + case LC_SEGMENT_SPLIT_INFO: + case LC_FUNCTION_STARTS: + case LC_DATA_IN_CODE: + case LC_DYLIB_CODE_SIGN_DRS: + case LC_DYLD_EXPORTS_TRIE: + case LC_DYLD_CHAINED_FIXUPS: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.linkeditDataQualName)); + break; + case LC_ENCRYPTION_INFO: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.encryptionInfoQualName)); + break; + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.versionMinQualName)); + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.dyldInfoQualName)); + break; + case LC_FILESET_ENTRY: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.filesetEntryCommandQualName)); + if (load.cmdsize-0x20 <= 150) + DefineDataVariable(curOffset + 0x20, Type::ArrayType(Type::IntegerType(1, true), load.cmdsize-0x20)); + break; + default: + DefineDataVariable(curOffset, Type::NamedType(this, m_typeNames.loadCommandQualName)); + break; + } + + DefineAutoSymbol(new Symbol(DataSymbol, "__macho_load_command" + imageDesc + "_[" + to_string(i) + "]", curOffset, LocalBinding)); + virtualReader.Seek(nextOffset); + } + } + catch (ReadException&) + { + LogError("Error when applying Mach-O header types at %" PRIx64, imageBase); + } + } + + + return true; +} + + +Ref<Symbol> MachoView::DefineMachoSymbol( + BNSymbolType type, const string& name, uint64_t addr, BNSymbolBinding binding, bool deferred) +{ + Ref<Type> symbolTypeRef; + + // If name is empty, symbol is not valid + if (name.size() == 0) + return nullptr; + + if (type != ExternalSymbol) + { + // Don't create symbols that are present in the database snapshot now. If it is not a + // deferrable symbol, it may be used in relocations, so don't ignore those. + if (m_backedByDatabase && deferred) + return nullptr; + + // Ensure symbol is within the executable + bool ok = false; + for (auto& segment : m_header.segments) + { + if ((segment.initprot == MACHO_VM_PROT_NONE) || (!segment.vmsize)) + continue; + + if ((addr >= segment.vmaddr) && + (addr < (segment.vmaddr + segment.vmsize))) + { + ok = true; + break; + } + } + if (!ok) + return nullptr; + } + + if ((type == ExternalSymbol) || (type == ImportAddressSymbol) || (type == ImportedDataSymbol)) + { + QualifiedName n(name); + // TODO + Ref<TypeLibrary> appliedLib = nullptr; + symbolTypeRef = ImportTypeLibraryObject(appliedLib, n); + if (symbolTypeRef) + { + m_logger->LogDebug("mach-o: type Library '%s' found hit for '%s'", appliedLib->GetName().c_str(), name.c_str()); + RecordImportedObjectLibrary(GetDefaultPlatform(), addr, appliedLib, n); + } + } + + auto process = [=]() { + // If name does not start with alphabetic character or symbol, prepend an underscore + string rawName = name; + if (!(((name[0] >= 'A') && (name[0] <= 'Z')) || ((name[0] >= 'a') && (name[0] <= 'z')) || (name[0] == '_') + || (name[0] == '?') || (name[0] == '$') || (name[0] == '@'))) + rawName = "_" + name; + + NameSpace nameSpace = GetInternalNameSpace(); + if (type == ExternalSymbol) + { + nameSpace = GetExternalNameSpace(); + } + + // Try to demangle any C++ symbols + string shortName = rawName; + string fullName = rawName; + Ref<Type> typeRef = symbolTypeRef; + + QualifiedName varName; + if (m_arch) + { + if (IsGNU3MangledString(rawName)) + { + Ref<Type> demangledType; + if (DemangleGNU3(m_arch, rawName, demangledType, varName, m_simplifyTemplates)) + { + shortName = varName.GetString(); + fullName = shortName; + if (demangledType) + fullName += demangledType->GetStringAfterName(); + if (!typeRef && m_extractMangledTypes && !GetDefaultPlatform()->GetFunctionByName(rawName)) + typeRef = demangledType; + } + else + { + m_logger->LogDebug("Failed to demangle name: '%s'\n", rawName.c_str()); + } + } + } + + return std::pair<Ref<Symbol>, Ref<Type>>( + new Symbol(type, shortName, fullName, rawName, addr, binding, nameSpace), typeRef); + }; + + if (deferred) + { + m_symbolQueue->Append(process, [this](Symbol* symbol, Type* type) { + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), symbol, type); + }); + return nullptr; + } + + auto result = process(); + return DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), result.first, result.second); +} + +bool MachoView::GetSegmentPermissions(MachOHeader& header, uint64_t address, uint32_t &flags) +{ + for (auto& segment : header.segments) + { + if ((segment.initprot == MACHO_VM_PROT_NONE) || (!segment.vmsize)) + continue; + + if (address >= segment.vmaddr && address < (segment.vmaddr + segment.vmsize)) + { + flags = segment.flags; + return true; + } + } + flags = 0; + return false; +} + +bool MachoView::GetSectionPermissions(MachOHeader& header, uint64_t address, uint32_t &flags) +{ + for (auto& section : header.sections) + { + if (!section.size) + continue; + + if (address >= section.addr && address < (section.addr + section.size)) + { + flags = section.flags; + return true; + } + } + flags = 0; + return false; +} + +void MachoView::ParseExportTrie(BinaryReader& reader, linkedit_data_command exportTrie) +{ + try { + uint32_t endGuard = exportTrie.datasize; + DataBuffer buffer = GetParentView()->ReadBuffer(m_universalImageOffset + exportTrie.dataoff, exportTrie.datasize); + + ReadExportNode(GetStart(), buffer, "", 0, endGuard); + } + catch (ReadException&) + { + m_logger->LogError("Error while parsing Export Trie"); + } +} + +void MachoView::ReadExportNode(uint64_t viewStart, DataBuffer& buffer, const std::string& currentText, size_t cursor, uint32_t endGuard) +{ + if (cursor > endGuard) + throw ReadException(); + + uint64_t terminalSize = readValidULEB128(buffer, cursor); + uint64_t childOffset = cursor + terminalSize; + if (terminalSize != 0) { + uint64_t imageOffset = 0; + uint64_t flags = readValidULEB128(buffer, cursor); + if (!(flags & EXPORT_SYMBOL_FLAGS_REEXPORT)) + { + imageOffset = readValidULEB128(buffer, cursor); + auto symbolType = GetAnalysisFunctionsForAddress(viewStart + imageOffset).size() ? FunctionSymbol : DataSymbol; + DefineMachoSymbol(symbolType, currentText, imageOffset + viewStart, GlobalBinding, true); + } + } + cursor = childOffset; + uint8_t childCount = buffer[cursor]; + cursor++; + if (cursor > endGuard) + throw ReadException(); + for (uint8_t i = 0; i < childCount; ++i) + { + std::string childText; + while (buffer[cursor] != 0 & cursor <= endGuard) + childText.push_back(buffer[cursor++]); + cursor++; + if (cursor > endGuard) + throw ReadException(); + auto next = readValidULEB128(buffer, cursor); + if (next == 0) + throw ReadException(); + ReadExportNode(viewStart, buffer, currentText + childText, next, endGuard); + } +} + +void MachoView::ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNSymbolType incomingType, uint32_t tableOffset, + uint32_t tableSize, BNSymbolBinding binding) +{ + try { + reader.Seek(tableOffset); + auto table = reader.Read(tableSize); + + BNSymbolType symtype = incomingType; + // uint64_t ordinal = 0; + // int64_t addend = 0; + uint64_t segmentIndex = 0; + uint64_t address = 0; + uint64_t offset = 0; + char* name = NULL; + // uint32_t flags = 0; + uint32_t type = 0; + size_t i = 0; + //bool done = false; + while (i < tableSize) + { + uint8_t opcode = table[i] & BindOpcodeMask; + uint8_t imm = table[i] & BindImmediateMask; + i++; + switch (opcode) + { + case BindOpcodeDone: + // ordinal = 0; + // addend = 0; + segmentIndex = 0; + address = 0; + offset = 0; + name = NULL; + // flags = 0; + type = 0; + symtype = incomingType; + break; + case BindOpcodeSetDylibOrdinalImmediate: /* ordinal = imm; */ break; + case BindOpcodeSetDylibOrdinalULEB: /* ordinal = */ readLEB128(table, tableSize, i); break; + case BindOpcodeSetDylibSpecialImmediate: /* ordinal = -imm; */ break; + case BindOpcodeSetSymbolTrailingFlagsImmediate: + /* flags = imm; */ + name = (char*)&table[i]; + while (i < tableSize && table[i++] != '\0') + {;} + break; + case BindOpcodeSetTypeImmediate: + type = imm; + if (type == 1) + symtype = ImportedDataSymbol; + break; + case BindOpcodeSetAddendSLEB: /* addend = */ readSLEB128(table, tableSize, i); break; + case BindOpcodeSetSegmentAndOffsetULEB: + segmentIndex = imm; + offset = readLEB128(table, tableSize, i); + if (segmentIndex >= header.segments.size()) + throw MachoFormatException(); + address = header.segments[segmentIndex].vmaddr + offset; + break; + case BindOpcodeAddAddressULEB: + address += readLEB128(table, tableSize, i); + break; + case BindOpcodeDoBind: + if (name == NULL) + throw MachoFormatException(); + + DefineMachoSymbol(symtype, string(name), address, binding, true); + address += m_addressSize; + break; + case BindOpcodeDoBindAddAddressULEB: + if (name == NULL) + throw MachoFormatException(); + + DefineMachoSymbol(symtype, string(name), address, binding, true); + address += m_addressSize; + address += readLEB128(table, tableSize, i); + break; + case BindOpcodeDoBindAddAddressImmediateScaled: + if (name == NULL) + throw MachoFormatException(); + + DefineMachoSymbol(symtype, string(name), address, binding, true); + address += m_addressSize; + address += (imm * m_addressSize); + break; + case BindOpcodeDoBindULEBTimesSkippingULEB: + { + if (name == NULL) + throw MachoFormatException(); + + uint64_t count = readLEB128(table, tableSize, i); + uint64_t skip = readLEB128(table, tableSize, i); + for (; count > 0; count--) + { + address += skip + m_addressSize; + DefineMachoSymbol(symtype, string(name), address, binding, true); + } + break; + } + // New Threaded Binding Opcode, not much info out about it. It encodes a ULEB after the op/imm. + case 0xD0: readLEB128(table, tableSize, i); break; + default: + break; + } + } + } + catch (ReadException&) + {;} +} + + +void MachoView::ParseSymbolTable(BinaryReader& reader, MachOHeader& header, const symtab_command& symtab, const vector<uint32_t>& indirectSymbols) +{ + if (header.ident.filetype == MH_DSYM) + { + m_logger->LogDebug("Skipping symbol parsing"); + return; + } + + try + { + //First parse the imports + if (header.dyldInfoPresent) + { + m_logger->LogDebug("Bind symbols"); + ParseDynamicTable(reader, header, ImportAddressSymbol, header.dyldInfo.bind_off, + header.dyldInfo.bind_size, GlobalBinding); + m_logger->LogDebug("Weak symbols"); + ParseDynamicTable(reader, header, ImportAddressSymbol, header.dyldInfo.weak_bind_off, + header.dyldInfo.weak_bind_size, WeakBinding); + m_logger->LogDebug("Lazy symbols"); + ParseDynamicTable(reader, header, ImportAddressSymbol, header.dyldInfo.lazy_bind_off, + header.dyldInfo.lazy_bind_size, GlobalBinding); + } + if (header.chainedFixupsPresent) + { + m_logger->LogDebug("Chained Fixups"); + ParseChainedFixups(header.chainedFixups); + } + if (header.exportTriePresent && header.isMainHeader) + ParseExportTrie(reader, header.exportTrie); + + //Then process the symtab + if (header.stringListSize == 0) + return; + reader.Seek(symtab.symoff); + + unordered_map<size_t, vector<std::pair<section_64*, size_t>>> stubSymbols; + for (auto& symbolStubs : header.symbolStubSections) + { + if (!symbolStubs.reserved2) + continue; + + size_t needed = symbolStubs.size / symbolStubs.reserved2; + for (size_t j = 0; (j < needed) && ((j + symbolStubs.reserved1) < indirectSymbols.size()); j++) + { + // Not exactly sure what the following 3 variables are for but this is the check done in + // Apple's source code + uint8_t sectionType = (symbolStubs.flags & SECTION_TYPE); + bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (symbolStubs.flags & S_ATTR_SELF_MODIFYING_CODE) && + (symbolStubs.reserved2 == 5) && (header.ident.cputype == MACHO_CPU_TYPE_X86); + auto symNum = indirectSymbols[j + symbolStubs.reserved1]; + if (symNum == INDIRECT_SYMBOL_ABS) + continue; + else if (symNum == INDIRECT_SYMBOL_LOCAL) + continue; + + stubSymbols[symNum].push_back( + std::pair<section_64*, size_t>(&symbolStubs, j)); + } + } + + unordered_map<size_t, vector<std::pair<section_64*, size_t>>> pointerSymbols; + for (auto& symbolPointerSection : header.symbolPointerSections) + { + // Not exactly sure what the following 3 variables are for but this is the check done in + // Apple's source code + uint8_t sectionType = (symbolPointerSection.flags & SECTION_TYPE); + bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (symbolPointerSection.flags & S_ATTR_SELF_MODIFYING_CODE) && + (symbolPointerSection.reserved2 == 5) && (header.ident.cputype == MACHO_CPU_TYPE_X86); + size_t needed = symbolPointerSection.size / m_addressSize; + for (size_t j = 0; (j < needed) && ((j + symbolPointerSection.reserved1) < indirectSymbols.size()); j++) + { + auto symNum = indirectSymbols[j + symbolPointerSection.reserved1]; + if (symNum == INDIRECT_SYMBOL_ABS) + continue; + else if (symNum == INDIRECT_SYMBOL_LOCAL) + continue; + pointerSymbols[symNum].push_back( + std::pair<section_64*, size_t>(&symbolPointerSection, j)); + } + } + + nlist_64 sym; + memset(&sym, 0, sizeof(sym)); + for (size_t i = 0; i < symtab.nsyms; i++) + { + sym.n_strx = reader.Read32(); + sym.n_type = reader.Read8(); + sym.n_sect = reader.Read8(); + sym.n_desc = reader.Read16(); + sym.n_value = (m_addressSize == 4) ? reader.Read32() : reader.Read64(); + if (sym.n_value) + sym.n_value += m_imageBaseAdjustment; + if (sym.n_strx >= symtab.strsize || ((sym.n_type & N_TYPE) == N_INDR)) + continue; + + string symbol((char*)header.stringList->GetDataAt(sym.n_strx)); + m_symbols.push_back(symbol); + //otool ignores symbols that end with ".o", startwith "ltmp" or are "gcc_compiled." so do we + if (symbol == "gcc_compiled." || + (symbol.length() > 2 && symbol.substr(symbol.length()-2, 2) == ".o") || + (symbol.length() > 4 && symbol.substr(0, 4) == "ltmp")) + { + m_logger->LogDebug("Skipping symbol: %s.", symbol.c_str()); + continue; + } + //N_TYPE is only set when N_SECT is the integer count of the section the symbol is in + //Note that we don't currently validate the section number, but just use the address. + //Very curious to see how other tools handle it. + BNSymbolType type = DataSymbol; + uint32_t flags; + if ((sym.n_type & N_TYPE) == N_SECT && sym.n_sect > 0 && (size_t)(sym.n_sect - 1) < header.sections.size()) + { + if (!GetSectionPermissions(header, sym.n_value, flags)) + { + if ((sym.n_type & N_EXT)) + { + if (!GetSegmentPermissions(header, sym.n_value, flags)) + { + m_logger->LogDebug("No valid segment for symbol %s. value:%" PRIx64, symbol.c_str(), sym.n_value); + continue; + } + } + else + { + m_logger->LogDebug("No valid section for symbol %s. value:%" PRIx64, symbol.c_str(), sym.n_value); + continue; + } + } + } + else if ((sym.n_type & N_TYPE) == N_ABS) + { + //N_ABS symbols do not have a section. Fall back to segment permissions. + if (!GetSegmentPermissions(header, sym.n_value, flags)) + { + m_logger->LogDebug("No valid segment for symbol %s. value:%" PRIx64, symbol.c_str(), sym.n_value); + continue; + } + } + else if ((sym.n_type & N_EXT)) + { + type = ExternalSymbol; + } + else + continue; + + if (type != ExternalSymbol) + { + if ((flags & S_ATTR_PURE_INSTRUCTIONS) == S_ATTR_PURE_INSTRUCTIONS || + (flags & S_ATTR_SOME_INSTRUCTIONS) == S_ATTR_SOME_INSTRUCTIONS) + type = FunctionSymbol; + else + type = DataSymbol; + } + if((sym.n_desc & N_ARM_THUMB_DEF) == N_ARM_THUMB_DEF) + sym.n_value++; + + auto stubSymbolIter = stubSymbols.find(i); + auto pointerSymbolIter = pointerSymbols.find(i); + bool deferred = stubSymbolIter == stubSymbols.end() && pointerSymbolIter == pointerSymbols.end(); + + Ref<Symbol> symbolObj; + if(header.dysymtab.nlocalsym && i >= header.dysymtab.ilocalsym && i < header.dysymtab.ilocalsym + header.dysymtab.nlocalsym) + { + symbolObj = DefineMachoSymbol(type, symbol, sym.n_value, LocalBinding, deferred); + } + else if (header.dysymtab.nextdefsym && i >= header.dysymtab.iextdefsym && i < header.dysymtab.iextdefsym + header.dysymtab.nextdefsym) + { + symbolObj = DefineMachoSymbol(type, symbol, sym.n_value, GlobalBinding, deferred); + } + else if (header.dysymtab.nundefsym && i >= header.dysymtab.iundefsym && i < header.dysymtab.iundefsym + header.dysymtab.nundefsym) + { + symbolObj = DefineMachoSymbol(type, symbol, sym.n_value, GlobalBinding, deferred); + } + else + { + symbolObj = DefineMachoSymbol(type, symbol, sym.n_value, GlobalBinding, deferred); + } + + if (!symbolObj) + { + continue; + } + + if (stubSymbolIter != stubSymbols.end()) + { + for (auto& j : stubSymbolIter->second) + { + // m_logger->LogError("STUB [%d] %llx - %s", i, j.first->addr + (j * j.first->reserved2), symbol.c_str()); + BNRelocationInfo info; + memset(&info, 0, sizeof(info)); + info.nativeType = -1; + info.size = j.first->reserved2; + info.pcRelative = true; + DefineRelocation(m_arch, info, symbolObj, j.first->addr + (j.second * j.first->reserved2)); + DefineMachoSymbol(ImportedFunctionSymbol, symbol, j.first->addr + (j.second * j.first->reserved2), + GlobalBinding, true); + } + } + + if (pointerSymbolIter != pointerSymbols.end()) + { + for (auto& j : pointerSymbolIter->second) + { + // m_logger->LogError("POINTER [%d] %llx - %s", i, j.first->addr + (j.second * m_addressSize), + // symbol.c_str()); + BNRelocationInfo info; + memset(&info, 0, sizeof(info)); + info.nativeType = BINARYNINJA_MANUAL_RELOCATION; + info.size = m_addressSize; + info.pcRelative = false; + DefineRelocation(m_arch, info, symbolObj, j.first->addr + (j.second * m_addressSize)); + DefineMachoSymbol( + ImportAddressSymbol, symbol, j.first->addr + (j.second * m_addressSize), GlobalBinding, true); + } + } + } + } + catch (ReadException&) + {;} + return; +} + + + + +void MachoView::ParseChainedFixups(linkedit_data_command chainedFixups) +{ + if (!chainedFixups.dataoff) + return; + + m_logger->LogDebug("Processing Chained Fixups"); + + // Dummy relocation + BNRelocationInfo reloc; + memset(&reloc, 0, sizeof(BNRelocationInfo)); + reloc.type = StandardRelocationType; + reloc.size = m_addressSize; + reloc.nativeType = BINARYNINJA_MANUAL_RELOCATION; + + bool processBinds = true; + + BinaryReader parentReader(GetParentView()); + BinaryReader mappedReader(this); + + try { + dyld_chained_fixups_header fixupsHeader {}; + uint64_t fixupHeaderAddress = m_universalImageOffset + chainedFixups.dataoff; + parentReader.Seek(fixupHeaderAddress); + fixupsHeader.fixups_version = parentReader.Read32(); + fixupsHeader.starts_offset = parentReader.Read32(); + fixupsHeader.imports_offset = parentReader.Read32(); + fixupsHeader.symbols_offset = parentReader.Read32(); + fixupsHeader.imports_count = parentReader.Read32(); + fixupsHeader.imports_format = parentReader.Read32(); + fixupsHeader.symbols_format = parentReader.Read32(); + + m_logger->LogDebug("Chained Fixups: Header @ %llx // Fixups version %lx", fixupHeaderAddress, fixupsHeader.fixups_version); + + size_t importsAddress = fixupHeaderAddress + fixupsHeader.imports_offset; + size_t importTableSize = sizeof(dyld_chained_import) * fixupsHeader.imports_count; + + if (fixupsHeader.fixups_version > 0) + { + m_logger->LogError("Chained Fixup parsing failed. Unknown Fixups Version"); + return; + } + + if (importTableSize > chainedFixups.datasize) + { + m_logger->LogError("Chained Fixup parsing failed. Binary is malformed"); + return; + } + + size_t symbolsAddress = fixupHeaderAddress + fixupsHeader.symbols_offset; + + // Pre-load the import table. We may re-access the same ordinal multiple times, this will be faster. + std::vector<import_entry> importTable; + parentReader.Seek(importsAddress); + + switch (fixupsHeader.imports_format) + { + case DYLD_CHAINED_IMPORT: + { + for (size_t i = 0; i < fixupsHeader.imports_count; i++) + { + uint32_t importEntry = parentReader.Read32(); + uint64_t nextEntryAddress = parentReader.GetOffset(); + + dyld_chained_import import = *(reinterpret_cast<dyld_chained_import*>(&importEntry)); + + import_entry entry; + + entry.lib_ordinal = (uint64_t)import.lib_ordinal; + entry.addend = 0; + entry.weak = (import.weak_import == 1); + + size_t symNameAddr = symbolsAddress + import.name_offset; + + parentReader.Seek(symNameAddr); + try { + string symbolName = parentReader.ReadCString(); + entry.name = symbolName; + } + catch (ReadException& ex) + { + entry.name = ""; + } + + importTable.push_back(entry); + parentReader.Seek(nextEntryAddress); + } + break; + } + case DYLD_CHAINED_IMPORT_ADDEND: + case DYLD_CHAINED_IMPORT_ADDEND64: + default: + { + m_logger->LogWarn("Chained Fixups: Unknown import binding format"); + processBinds = false; // We can still handle rebases. + break; + } + } + + m_logger->LogDebug("Chained Fixups: %llx import table entries", importTable.size()); + + uint64_t fixupStartsAddress = fixupHeaderAddress + fixupsHeader.starts_offset; + parentReader.Seek(fixupStartsAddress); + dyld_chained_starts_in_image segs {}; + segs.seg_count = parentReader.Read32(); + vector<uint32_t> segInfoOffsets {}; + for (size_t i = 0; i < segs.seg_count; i++) + { + segInfoOffsets.push_back(parentReader.Read32()); + } + for (auto offset : segInfoOffsets) + { + if (!offset) + continue; + + dyld_chained_starts_in_segment starts {}; + uint64_t startsAddr = fixupStartsAddress + offset; + parentReader.Seek(startsAddr); + starts.size = parentReader.Read32(); + starts.page_size = parentReader.Read16(); + starts.pointer_format = parentReader.Read16(); + starts.segment_offset = parentReader.Read64(); + starts.max_valid_pointer = parentReader.Read32(); + starts.page_count = parentReader.Read16(); + + uint8_t strideSize; + ChainedFixupPointerGeneric format; + + // Firmware formats will require digging up whatever place they're being used and reversing it. + // They are not handled by dyld. + switch (starts.pointer_format) { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + strideSize = 8; + format = GenericArm64eFixupFormat; + break; + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + strideSize = 4; + format = GenericArm64eFixupFormat; + break; + // case DYLD_CHAINED_PTR_ARM64E_FIRMWARE: Unsupported. + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + strideSize = 4; + format = Generic64FixupFormat; + break; + case DYLD_CHAINED_PTR_32: + case DYLD_CHAINED_PTR_32_CACHE: + strideSize = 4; + format = Generic32FixupFormat; + break; + case DYLD_CHAINED_PTR_32_FIRMWARE: + strideSize = 4; + format = Firmware32FixupFormat; + break; + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + strideSize = 1; + format = Generic64FixupFormat; + break; + default: + { + m_logger->LogError("Chained Fixups: Unknown or unsupported pointer format %d, " + "unable to process chains for segment at @llx", starts.pointer_format, starts.segment_offset); + continue; + } + } + + uint16_t fmt = starts.pointer_format; + m_logger->LogDebug("Chained Fixups: Segment start @ %llx, fmt %d", starts.segment_offset, fmt); + + uint64_t pageStartsTableStartAddress = parentReader.GetOffset(); + vector<vector<uint16_t>> pageStartOffsets {}; + for (size_t i = 0; i < starts.page_count; i++) + { + // On armv7, Chained pointers here can have multiple starts. + // And if so, there's another table *overlapping* the table we're currently reading. + // dyld handles this through 'overflow indexing' + // This is technically supported on other archs however is not (currently) used. + parentReader.Seek(pageStartsTableStartAddress + (sizeof(uint16_t) * i)); + uint16_t start = parentReader.Read16(); + if ((start & DYLD_CHAINED_PTR_START_MULTI) && (start != DYLD_CHAINED_PTR_START_NONE)) + { + uint64_t overflowIndex = start & ~DYLD_CHAINED_PTR_START_MULTI; + vector<uint16_t> pageStartSubStarts; + parentReader.Seek(pageStartsTableStartAddress + (overflowIndex * sizeof(uint16_t))); + bool done = false; + while (!done) + { + uint16_t subPageStart = parentReader.Read16(); + if ((subPageStart & DYLD_CHAINED_PTR_START_LAST) == 0) + { + pageStartSubStarts.push_back(subPageStart); + } + else + { + pageStartSubStarts.push_back(subPageStart & ~DYLD_CHAINED_PTR_START_LAST); + done = true; + } + } + pageStartOffsets.push_back(pageStartSubStarts); + } + else + { + pageStartOffsets.push_back({start}); + } + } + + int i = -1; + for (auto pageStarts : pageStartOffsets) + { + i++; + uint64_t pageAddress = m_universalImageOffset + starts.segment_offset + (i * starts.page_size); + for (uint16_t start : pageStarts) + { + if (start == DYLD_CHAINED_PTR_START_NONE) + continue; + + uint64_t chainEntryAddress = pageAddress + start; + + bool fixupsDone = false; + + while (!fixupsDone) + { + ChainedFixupPointer pointer; + parentReader.Seek(chainEntryAddress); + mappedReader.Seek(chainEntryAddress - m_universalImageOffset + GetStart()); + if (format == Generic32FixupFormat || format == Firmware32FixupFormat) + pointer.raw32 = (uint32_t)(uintptr_t)mappedReader.Read32(); + else + pointer.raw64 = (uintptr_t)mappedReader.Read64(); + + bool bind = false; + uint64_t nextEntryStrideCount; + + switch (format) + { + case Generic32FixupFormat: + bind = pointer.generic32.bind.bind; + nextEntryStrideCount = pointer.generic32.rebase.next; + break; + case Generic64FixupFormat: + bind = pointer.generic64.bind.bind; + nextEntryStrideCount = pointer.generic64.rebase.next; + break; + case GenericArm64eFixupFormat: + bind = pointer.arm64e.bind.bind; + nextEntryStrideCount = pointer.arm64e.rebase.next; + break; + case Firmware32FixupFormat: + nextEntryStrideCount = pointer.firmware32.next; + bind = false; + break; + } + + m_logger->LogTrace("Chained Fixups: @ 0x%llx ( 0x%llx ) - %d 0x%llx", chainEntryAddress, + GetStart() + (chainEntryAddress - m_universalImageOffset), + bind, nextEntryStrideCount); + + if (bind && processBinds) + { + uint64_t ordinal; + + switch (starts.pointer_format) + { + case DYLD_CHAINED_PTR_64: + case DYLD_CHAINED_PTR_64_OFFSET: + ordinal = pointer.generic64.bind.ordinal; + break; + // case DYLD_CHAINED_PTR_ARM64E_OFFSET: ; old _KERNEL name. + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + if (pointer.arm64e.bind.auth) + ordinal = starts.pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24 + ? pointer.arm64e.authBind24.ordinal : pointer.arm64e.authBind.ordinal; + else + ordinal = starts.pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24 + ? pointer.arm64e.bind24.ordinal : pointer.arm64e.bind.ordinal; + break; + case DYLD_CHAINED_PTR_32: + ordinal = pointer.generic32.bind.ordinal; + break; + default: + m_logger->LogWarn("Chained Fixups: Unknown Bind Pointer Format at %llx", + GetStart() + (chainEntryAddress - m_universalImageOffset)); + + chainEntryAddress += (nextEntryStrideCount * strideSize); + if (chainEntryAddress > pageAddress + starts.page_size) + { + m_logger->LogDebug("Chained Fixups: Pointer at %llx left page", + GetStart() + ((chainEntryAddress - (nextEntryStrideCount * strideSize))) - m_universalImageOffset); + fixupsDone = true; + } + if (nextEntryStrideCount == 0) + fixupsDone = true; + + continue; + } + + if (ordinal < importTable.size()) + { + import_entry entry = importTable.at(ordinal); + uint64_t targetAddress = GetStart() + (chainEntryAddress - m_universalImageOffset); + + if (!entry.name.empty()) + { + reloc.address = targetAddress; + DefineRelocation(m_arch, reloc, 0, reloc.address); + DefineMachoSymbol(ImportedDataSymbol, entry.name, + targetAddress, + entry.weak ? WeakBinding : GlobalBinding, true); + } + else + { + m_logger->LogWarn("Chained Fixups: Import Table entry %llx has no symbol; " + "Unable to bind item at %llx", ordinal, targetAddress); + } + } + } + else if (!bind) + { + uint64_t entryOffset; + switch (starts.pointer_format) + { + case DYLD_CHAINED_PTR_ARM64E: + case DYLD_CHAINED_PTR_ARM64E_KERNEL: + case DYLD_CHAINED_PTR_ARM64E_USERLAND: + case DYLD_CHAINED_PTR_ARM64E_USERLAND24: + { + if (pointer.arm64e.bind.auth) + entryOffset = pointer.arm64e.authRebase.target; + else + entryOffset = pointer.arm64e.rebase.target; + + if ( starts.pointer_format != DYLD_CHAINED_PTR_ARM64E || pointer.arm64e.bind.auth) + entryOffset += GetStart(); + + break; + } + case DYLD_CHAINED_PTR_64: + entryOffset = pointer.generic64.rebase.target; + break; + case DYLD_CHAINED_PTR_64_OFFSET: + entryOffset = pointer.generic64.rebase.target + GetStart(); + break; + case DYLD_CHAINED_PTR_64_KERNEL_CACHE: + case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: + entryOffset = pointer.kernel64.target; + break; + case DYLD_CHAINED_PTR_32: + case DYLD_CHAINED_PTR_32_CACHE: + entryOffset = pointer.generic32.rebase.target; + break; + case DYLD_CHAINED_PTR_32_FIRMWARE: + entryOffset = pointer.firmware32.target; + break; + } + + reloc.address = GetStart() + (chainEntryAddress - m_universalImageOffset); + DefineRelocation(m_arch, reloc, entryOffset, reloc.address); + } + + chainEntryAddress += (nextEntryStrideCount * strideSize); + + if (chainEntryAddress > pageAddress + starts.page_size) + { + // Something is seriously wrong here. likely malformed binary, or our parsing failed elsewhere. + // This will log the pointer in mapped memory. + m_logger->LogError("Chained Fixups: Pointer at %llx left page", + GetStart() + ((chainEntryAddress - (nextEntryStrideCount * strideSize))) - m_universalImageOffset); + fixupsDone = true; + } + + if (nextEntryStrideCount == 0) + fixupsDone = true; + } + } + } + } + } + catch (ReadException&) + { + m_logger->LogError("Chained Fixup parsing failed"); + } +} + + +uint64_t MachoView::PerformGetEntryPoint() const +{ + if (m_header.m_entryPoints.size() == 0) + return 0; + + return m_header.m_entryPoints[0]; +} + + +BNEndianness MachoView::PerformGetDefaultEndianness() const +{ + return LittleEndian; +} + + +bool MachoView::PerformIsRelocatable() const +{ + return m_relocatable; +} + + +size_t MachoView::PerformGetAddressSize() const +{ + return m_addressSize; +} + + +MachoViewType::MachoViewType(): BinaryViewType("Mach-O", "Mach-O") +{ + m_logger = LogRegistry::CreateLogger("BinaryView.MachoViewType"); +} + + +Ref<BinaryView> MachoViewType::Create(BinaryView* data) +{ + try + { + return new MachoView("Mach-O", data); + } + catch (std::exception& e) + { + m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + + +Ref<BinaryView> MachoViewType::Parse(BinaryView* data) +{ + try + { + return new MachoView("Mach-O", data, true); + } + catch (std::exception& e) + { + m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + + +bool MachoViewType::IsTypeValidForData(BinaryView* data) +{ + if (!data) + return false; + + DataBuffer sig = data->ReadBuffer(data->GetStart(), 4); + if (sig.GetLength() != 4) + return false; + + uint32_t magic = *(uint32_t*)sig.GetData(); + if (magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == MH_MAGIC || magic == MH_MAGIC_64) + return true; + magic = ToBE32(magic); + if ((magic == FAT_MAGIC) || (magic == FAT_MAGIC_64)) + return true; + + return data->GetLoadSettings(GetName()) ? true : false; +} + + +uint64_t MachoViewType::ParseHeaders(BinaryView* data, uint64_t imageOffset, mach_header_64& ident, Ref<Architecture>* arch, Ref<Platform>* plat, string& errorMsg) +{ + DataBuffer sig = data->ReadBuffer(imageOffset, 4); + uint32_t magic = *(uint32_t*)sig.GetData(); + if ((sig.GetLength() != 4) || !(magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == MH_MAGIC || magic == MH_MAGIC_64)) + { + errorMsg = "invalid signature"; + return 0; + } + + BinaryReader reader(data); + reader.SetVirtualBase(imageOffset); + ident.magic = reader.Read32(); + + BNEndianness endianness; + if (ident.magic == MH_MAGIC || ident.magic == MH_MAGIC_64) + endianness = LittleEndian; + else if (ident.magic == MH_CIGAM || ident.magic == MH_CIGAM_64) + endianness = BigEndian; + else + { + errorMsg = "invalid file class"; + return 0; + } + + reader.SetEndianness(endianness); + ident.cputype = reader.Read32(); + ident.cpusubtype = reader.Read32(); + ident.filetype = reader.Read32(); + ident.ncmds = reader.Read32(); + ident.sizeofcmds = reader.Read32(); + ident.flags = reader.Read32(); + + if ((ident.cputype & MachOABIMask) == MachOABI64) // address size == 8 + { + ident.reserved = reader.Read32(); + } + if (!(ident.filetype == MH_OBJECT || + ident.filetype == MH_EXECUTE || + ident.filetype == MH_DYLIB || + ident.filetype == MH_DYLINKER || + ident.filetype == MH_BUNDLE || + ident.filetype == MH_KEXT_BUNDLE || + ident.filetype == MH_CORE || + ident.filetype == MH_PRELOAD || + ident.filetype == MH_DSYM || + ident.filetype == MH_FILESET)) + { + m_logger->LogError("Unhandled Macho file class: 0x%x", ident.filetype); + errorMsg = "invalid file class"; + return 0; + } + + map<string, Ref<Metadata>> metadataMap = { + {"cputype", new Metadata((uint64_t) ident.cputype)}, + {"cpusubtype", new Metadata((uint64_t) ident.cpusubtype)}, + {"flags", new Metadata((uint64_t) ident.flags)}, + }; + + Ref<Metadata> metadata = new Metadata(metadataMap); + + // retrieve architecture + // FIXME: Architecture registration methods should perhaps be virtual and take the raw data, or some additional opaque information. + Ref<Platform> recognizedPlatform = g_machoViewType->RecognizePlatform(ident.cputype, endianness, data, metadata); + + if (recognizedPlatform) + { + if (plat) + *plat = recognizedPlatform; + if (arch) + *arch = recognizedPlatform->GetArchitecture(); + } + else if (arch) + { + *arch = g_machoViewType->GetArchitecture(ident.cputype, endianness); + } + + return reader.GetOffset(); +} + + +Ref<Settings> MachoViewType::GetLoadSettingsForData(BinaryView* data) +{ + Ref<BinaryView> viewRef = Parse(data); + if (!viewRef || !viewRef->Init()) + { + m_logger->LogError("View type '%s' could not be created", GetName().c_str()); + return nullptr; + } + + Ref<Settings> settings = GetDefaultLoadSettingsForData(viewRef); + + // specify default load settings that can be overridden + vector<string> overrides = {"loader.architecture", "loader.imageBase", "loader.platform"}; + if (!viewRef->IsRelocatable()) + settings->UpdateProperty("loader.imageBase", "message", "Note: File indicates image is not relocatable."); + + for (const auto& override : overrides) + { + if (settings->Contains(override)) + settings->UpdateProperty(override, "readOnly", false); + } + + // register additional settings + settings->RegisterSetting("loader.macho.processFunctionStarts", + R"({ + "title" : "Process Mach-O Function Starts Table", + "type" : "boolean", + "default" : true, + "description" : "Add function starts sourced from the Function Starts table to the core for analysis." + })"); + + if (viewRef->GetSectionByName("__thread_starts")) + { + settings->RegisterSetting("loader.macho.rebaseThreadStarts", + R"({ + "title" : "Rebase Mach-O Kernelcache Thread Starts", + "type" : "boolean", + "default" : true, + "description" : "Add relocation entries to rebase chained thread starts located in the __thread_starts section." + })"); + } + + // Merge existing load settings if they exist. This allows for the selection of a specific object file from a Mach-O Universal file. + // The 'Universal' BinaryViewType generates a schema with 'loader.universal.architectures'. This schema contains an appropriate + // 'Mach-O' load schema for selecting a specific object file. The embedded schema contains 'loader.macho.universalImageOffset'. + Ref<Settings> loadSettings = viewRef->GetLoadSettings(GetName()); + if (loadSettings && !loadSettings->IsEmpty()) + settings->DeserializeSchema(loadSettings->SerializeSchema()); + + return settings; +} + + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + +#ifdef DEMO_VERSION + bool MachoPluginInit() +#else + BINARYNINJAPLUGIN bool CorePluginInit() +#endif + { + InitMachoViewType(); + InitFatMachoViewType(); + InitUniversalViewType(); + return true; + } +} diff --git a/view/macho/machoview.h b/view/macho/machoview.h new file mode 100644 index 00000000..20d2ea78 --- /dev/null +++ b/view/macho/machoview.h @@ -0,0 +1,1375 @@ +#pragma once + +#include <exception> +#include <vector> +#include <string.h> + +#include "binaryninjaapi.h" + +//These are laready defined in one of the osx headers we want to override +#undef CPU_SUBTYPE_INTEL +#undef CPU_SUBTYPE_I386_ALL +#undef CPU_SUBTYPE_POWERPC_ALL +#undef LC_MAIN +#undef LC_SOURCE_VERSION +#undef LC_DYLIB_CODE_SIGN_DRS +#undef LC_DYLD_INFO_ONLY + +#define MH_MAGIC 0xfeedface +#define MH_MAGIC_64 0xfeedfacf +#define MH_CIGAM 0xcefaedfe +#define MH_CIGAM_64 0xcffaedfe +#define FAT_MAGIC 0xcafebabe +#define FAT_MAGIC_64 0xcafebabf + +typedef int32_t cpu_type_t; +typedef int32_t cpu_subtype_t; +typedef int vm_prot_t; + +//Cpu types +#define MACHO_CPU_ARCH_ABI64 0x01000000 +#define MACHO_CPU_ARCH_ABI64_32 0x02000000 + +#define MACHO_CPU_TYPE_ANY ((cpu_type_t) -1) +#define MACHO_CPU_TYPE_VAX ((cpu_type_t) 1) +#define MACHO_CPU_TYPE_MC680x0 ((cpu_type_t) 6) +#define MACHO_CPU_TYPE_X86 ((cpu_type_t) 7) +#define MACHO_CPU_TYPE_X86_64 (MACHO_CPU_TYPE_X86 | MACHO_CPU_ARCH_ABI64) +#define MACHO_CPU_TYPE_MIPS ((cpu_type_t) 8) +#define MACHO_CPU_TYPE_MC98000 ((cpu_type_t) 10) +#define MACHO_CPU_TYPE_HPPA ((cpu_type_t) 11) +#define MACHO_CPU_TYPE_ARM ((cpu_type_t) 12) +#define MACHO_CPU_TYPE_ARM64 (MACHO_CPU_TYPE_ARM | MACHO_CPU_ARCH_ABI64) +#define MACHO_CPU_TYPE_ARM64_32 (MACHO_CPU_TYPE_ARM | MACHO_CPU_ARCH_ABI64_32) +#define MACHO_CPU_TYPE_MC88000 ((cpu_type_t) 13) +#define MACHO_CPU_TYPE_SPARC ((cpu_type_t) 14) +#define MACHO_CPU_TYPE_I860 ((cpu_type_t) 15) +#define MACHO_CPU_TYPE_ALPHA ((cpu_type_t) 16) +#define MACHO_CPU_TYPE_POWERPC ((cpu_type_t) 18) +#define MACHO_CPU_TYPE_POWERPC64 (MACHO_CPU_TYPE_POWERPC | MACHO_CPU_ARCH_ABI64) + +//Cpu subtype capability bits +#define MACHO_CPU_SUBTYPE_MASK ((cpu_subtype_t)0xff000000) +#define MACHO_CPU_SUBTYPE_LIB64 ((cpu_subtype_t)0x80000000) + +//Cpu subtypes +#define MACHO_CPU_SUBTYPE_INTEL(f,m) ((cpu_subtype_t) (f)+((m)<<4)) +#define MACHO_CPU_SUBTYPE_I386_ALL MACHO_CPU_SUBTYPE_INTEL(3,0) +#define MACHO_CPU_SUBTYPE_X86_ALL ((cpu_subtype_t) 3) +#define MACHO_CPU_SUBTYPE_X86_ARCH1 ((cpu_subtype_t) 4) +#define MACHO_CPU_SUBTYPE_X86_64_ALL ((cpu_subtype_t) 3) +#define MACHO_CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) + +#define MACHO_CPU_SUBTYPE_ARM_ALL ((cpu_subtype_t) 0) +#define MACHO_CPU_SUBTYPE_ARM_V4T ((cpu_subtype_t) 5) +#define MACHO_CPU_SUBTYPE_ARM_V6 ((cpu_subtype_t) 6) +#define MACHO_CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#define MACHO_CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#define MACHO_CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#define MACHO_CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) +#define MACHO_CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) +#define MACHO_CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) +#define MACHO_CPU_SUBTYPE_ARM_V8 ((cpu_subtype_t) 13) +#define MACHO_CPU_SUBTYPE_ARM_V6M ((cpu_subtype_t) 14) +#define MACHO_CPU_SUBTYPE_ARM_V7M ((cpu_subtype_t) 15) +#define MACHO_CPU_SUBTYPE_ARM_V7EM ((cpu_subtype_t) 16) + +#define MACHO_CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) +#define MACHO_CPU_SUBTYPE_ARM64_V8 ((cpu_subtype_t) 1) +#define MACHO_CPU_SUBTYPE_ARM64E ((cpu_subtype_t) 2) + +#define MACHO_CPU_SUBTYPE_ARM64_32_ALL ((cpu_subtype_t) 0) +#define MACHO_CPU_SUBTYPE_ARM64_32_V8 ((cpu_subtype_t) 1) + +#define MACHO_CPU_SUBTYPE_POWERPC_ALL ((cpu_subtype_t) 0) +#define MACHO_CPU_SUBTYPE_POWERPC_601 ((cpu_subtype_t) 1) +#define MACHO_CPU_SUBTYPE_POWERPC_602 ((cpu_subtype_t) 2) +#define MACHO_CPU_SUBTYPE_POWERPC_603 ((cpu_subtype_t) 3) +#define MACHO_CPU_SUBTYPE_POWERPC_603e ((cpu_subtype_t) 4) +#define MACHO_CPU_SUBTYPE_POWERPC_603ev ((cpu_subtype_t) 5) +#define MACHO_CPU_SUBTYPE_POWERPC_604 ((cpu_subtype_t) 6) +#define MACHO_CPU_SUBTYPE_POWERPC_604e ((cpu_subtype_t) 7) +#define MACHO_CPU_SUBTYPE_POWERPC_620 ((cpu_subtype_t) 8) +#define MACHO_CPU_SUBTYPE_POWERPC_750 ((cpu_subtype_t) 9) +#define MACHO_CPU_SUBTYPE_POWERPC_7400 ((cpu_subtype_t) 10) +#define MACHO_CPU_SUBTYPE_POWERPC_7450 ((cpu_subtype_t) 11) +#define MACHO_CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100) + +//Page protections +#define MACHO_VM_PROT_NONE ((vm_prot_t) 0x00) +#define MACHO_VM_PROT_READ ((vm_prot_t) 0x01) +#define MACHO_VM_PROT_WRITE ((vm_prot_t) 0x02) +#define MACHO_VM_PROT_EXECUTE ((vm_prot_t) 0x04) +#define MACHO_VM_PROT_DEFAULT (VM_PROT_READ|VM_PROT_WRITE) +#define MACHO_VM_PROT_ALL (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE) +#define MACHO_VM_PROT_NO_CHANGE ((vm_prot_t) 0x08) +#define MACHO_VM_PROT_COPY ((vm_prot_t) 0x10) +#define MACHO_VM_PROT_WANTS_COPY ((vm_prot_t) 0x10) + +//Segment flags +#define SG_HIGHVM 0x1 +#define SG_FVMLIB 0x2 +#define SG_NORELOC 0x4 +#define SG_PROTECTED_VERSION_1 0x8 + +//Section flags +#define S_ATTR_SOME_INSTRUCTIONS 0x00000400 +#define S_ATTR_PURE_INSTRUCTIONS 0x80000000 // section contains only true machine instructions +#define S_ATTR_NO_TOC 0x40000000 // section contains coalesced symbols that are not to be in a ranlib table of contents +#define S_ATTR_STRIP_STATIC_SYMS 0x20000000 // ok to strip static symbols in this section in files with the MH_DYLDLINK flag +#define S_ATTR_NO_DEAD_STRIP 0x10000000 // no dead stripping +#define S_ATTR_LIVE_SUPPORT 0x08000000 // blocks are live if they reference live blocks +#define S_ATTR_SELF_MODIFYING_CODE 0x04000000 // Used with i386 code stubs written on by dyld + +#define S_REGULAR 0x0 +#define S_ZEROFILL 0x1 +#define S_CSTRING_LITERALS 0x2 +#define S_4BYTE_LITERALS 0x3 +#define S_8BYTE_LITERALS 0x4 +#define S_LITERAL_POINTERS 0x5 +#define S_NON_LAZY_SYMBOL_POINTERS 0x6 +#define S_LAZY_SYMBOL_POINTERS 0x7 +#define S_SYMBOL_STUBS 0x8 +#define S_MOD_INIT_FUNC_POINTERS 0x9 +#define S_MOD_TERM_FUNC_POINTERS 0xa +#define S_COALESCED 0xb +#define S_GB_ZEROFILL 0xc +#define S_INTERPOSING 0xd +#define S_16BYTE_LITERALS 0xe +#define S_DTRACE_DOF 0xf +#define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 +#define S_THREAD_LOCAL_REGULAR 0x11 +#define S_THREAD_LOCAL_ZEROFILL 0x12 +#define S_THREAD_LOCAL_VARIABLES 0x13 +#define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 +#define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 + +//Mach-O Commands +#define LC_REQ_DYLD 0x80000000 +#define LC_SEGMENT 0x1 +#define LC_SYMTAB 0x2 +#define LC_SYMSEG 0x3 +#define LC_THREAD 0x4 +#define LC_UNIXTHREAD 0x5 +#define LC_LOADFVMLIB 0x6 +#define LC_IDFVMLIB 0x7 +#define LC_IDENT 0x8 +#define LC_FVMFILE 0x9 +#define LC_PREPAGE 0xa +#define LC_DYSYMTAB 0xb +#define LC_LOAD_DYLIB 0xc +#define LC_ID_DYLIB 0xd +#define LC_LOAD_DYLINKER 0xe +#define LC_ID_DYLINKER 0xf +#define LC_PREBOUND_DYLIB 0x10 +#define LC_ROUTINES 0x11 +#define LC_SUB_FRAMEWORK 0x12 +#define LC_SUB_UMBRELLA 0x13 +#define LC_SUB_CLIENT 0x14 +#define LC_SUB_LIBRARY 0x15 +#define LC_TWOLEVEL_HINTS 0x16 +#define LC_PREBIND_CKSUM 0x17 +#define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD) +#define LC_SEGMENT_64 0x19 +#define LC_ROUTINES_64 0x1a +#define LC_UUID 0x1b +#define LC_RPATH (0x1c | LC_REQ_DYLD) +#define LC_CODE_SIGNATURE 0x1d +#define LC_SEGMENT_SPLIT_INFO 0x1e +#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) +#define LC_LAZY_LOAD_DYLIB 0x20 +#define LC_ENCRYPTION_INFO 0x21 +#define LC_DYLD_INFO 0x22 +#define LC_DYLD_INFO_ONLY (0x22 | LC_REQ_DYLD) +#define LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) +#define LC_VERSION_MIN_MACOSX 0x24 +#define LC_VERSION_MIN_IPHONEOS 0x25 +#define LC_FUNCTION_STARTS 0x26 +#define LC_DYLD_ENVIRONMENT 0x27 +#define LC_MAIN (0x28 | LC_REQ_DYLD) +#define LC_DATA_IN_CODE 0x29 +#define LC_SOURCE_VERSION 0x2a +#define LC_DYLIB_CODE_SIGN_DRS 0x2b +#define _LC_ENCRYPTION_INFO_64 0x2c /* FIXME: warning defined in .../SDKs/MacOSX10.15.sdk/usr/include/mach-o/loader.h */ +#define _LC_LINKER_OPTION 0x2d /* FIXME: warning defined in .../SDKs/MacOSX10.15.sdk/usr/include/mach-o/loader.h */ +#define _LC_LINKER_OPTIMIZATION_HINT 0x2e /* FIXME: warning defined in .../SDKs/MacOSX10.15.sdk/usr/include/mach-o/loader.h */ +#define _LC_VERSION_MIN_TVOS 0x2f /* FIXME: warning defined in .../SDKs/MacOSX10.15.sdk/usr/include/mach-o/loader.h */ +#define LC_VERSION_MIN_WATCHOS 0x30 +#define LC_NOTE 0x31 +#define LC_BUILD_VERSION 0x32 +#define LC_DYLD_EXPORTS_TRIE (0x33 | LC_REQ_DYLD) +#define LC_DYLD_CHAINED_FIXUPS (0x34 | LC_REQ_DYLD) +#define LC_FILESET_ENTRY (0x35 | LC_REQ_DYLD) + +//Mach-O File types +#define MH_OBJECT 0x1 +#define MH_EXECUTE 0x2 +#define MH_FVMLIB 0x3 +#define MH_CORE 0x4 +#define MH_PRELOAD 0x5 +#define MH_DYLIB 0x6 +#define MH_DYLINKER 0x7 +#define MH_BUNDLE 0x8 +#define MH_DYLIB_STUB 0x9 +#define MH_DSYM 0xa +#define MH_KEXT_BUNDLE 0xb +#define MH_FILESET 0xc + +#define MH_NOUNDEFS 0x1 +#define MH_INCRLINK 0x2 +#define MH_DYLDLINK 0x4 +#define MH_BINDATLOAD 0x8 +#define MH_PREBOUND 0x10 +#define MH_SPLIT_SEGS 0x20 +#define MH_LAZY_INIT 0x40 +#define MH_TWOLEVEL 0x80 +#define MH_FORCE_FLAT 0x100 +#define MH_NOMULTIDEFS 0x200 +#define MH_NOFIXPREBINDING 0x400 +#define MH_PREBINDABLE 0x800 +#define MH_ALLMODSBOUND 0x1000 +#define MH_SUBSECTIONS_VIA_SYMBOLS 0x2000 +#define MH_CANONICAL 0x4000 +#define MH_WEAK_DEFINES 0x8000 +#define MH_BINDS_TO_WEAK 0x10000 +#define MH_ALLOW_STACK_EXECUTION 0x20000 +#define MH_ROOT_SAFE 0x40000 +#define MH_SETUID_SAFE 0x80000 +#define MH_NO_REEXPORTED_DYLIBS 0x100000 +#define MH_PIE 0x200000 +#define MH_DEAD_STRIPPABLE_DYLIB 0x400000 +#define MH_HAS_TLV_DESCRIPTORS 0x800000 +#define MH_NO_HEAP_EXECUTION 0x1000000 +#define _MH_APP_EXTENSION_SAFE 0x2000000 /* FIXME: warning defined in .../SDKs/MacOSX10.15.sdk/usr/include/mach-o/loader.h */ +#define _MH_NLIST_OUTOFSYNC_WITH_DYLDINFO 0x04000000u +#define _MH_SIM_SUPPORT 0x08000000u +#define _MH_DYLIB_IN_CACHE 0x80000000u + +//Segment Names +#define SEG_PAGEZERO "__PAGEZERO" +#define SEG_TEXT "__TEXT" +#define SECT_TEXT "__text" +#define SECT_FVMLIB_INIT0 "__fvmlib_init0" +#define SECT_FVMLIB_INIT1 "__fvmlib_init1" +#define SEG_DATA "__DATA" +#define SECT_DATA "__data" +#define SECT_BSS "__bss" +#define SECT_COMMON "__common" +#define SEG_OBJC "__OBJC" +#define SECT_OBJC_SYMBOLS "__symbol_table" +#define SECT_OBJC_MODULES "__module_info" +#define SECT_OBJC_STRINGS "__selector_strs" +#define SECT_OBJC_REFS "__selector_refs" +#define SEG_ICON "__ICON" +#define SECT_ICON_HEADER "__header" +#define SECT_ICON_TIFF "__tiff" +#define SEG_LINKEDIT "__LINKEDIT" +#define SEG_UNIXSTACK "__UNIXSTACK" +#define SEG_IMPORT "__IMPORT" + +//Symbol Types (N_TYPE) +#define N_UNDF 0x0 +#define N_ABS 0x2 +#define N_SECT 0xe +#define N_PBUD 0xc +#define N_INDR 0xa + +#define N_ARM_THUMB_DEF 0x0008 + +/* + * An indirect symbol table entry is simply a 32bit index into the symbol table + * to the symbol that the pointer or stub is referring to. Unless it is for a + * non-lazy symbol pointer section for a defined symbol which strip(1) as + * removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the + * symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that. + */ +#define INDIRECT_SYMBOL_ABS 0x40000000 +#define INDIRECT_SYMBOL_LOCAL 0x80000000 + +#define SECTION_TYPE 0x000000ff /* 256 section types */ +#define SECTION_ATTRIBUTES 0xffffff00 /* 24 section attributes */ + +#define BINARYNINJA_MANUAL_RELOCATION ((uint64_t)-2) + +enum MachoRelocationType +{ + GENERIC_RELOC_VANILLA = 0, + GENERIC_RELOC_PAIR = 1, + GENERIC_RELOC_SECTDIFF = 2, + GENERIC_RELOC_PB_LA_PTR = 3, + GENERIC_RELOC_LOCAL_SECTDIFF = 4, + GENERIC_RELOC_TLV = 5 +}; + + +namespace BinaryNinja +{ + class MachoFormatException: public std::exception + { + std::string m_message; + + public: + MachoFormatException(const std::string& msg = "invalid format"): m_message(msg) {} + virtual const char* what() const NOEXCEPT { return m_message.c_str(); } + }; + + struct FatHeader { + uint32_t magic; + uint32_t nfat_arch; + }; + + struct FatArch32 { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t offset; + uint32_t size; + uint32_t align; + }; + + struct FatArch64 { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint64_t offset; + uint64_t size; + uint32_t align; + uint32_t reserved; + }; + + struct mach_header { + uint32_t magic; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + }; + + struct mach_header_64 { + uint32_t magic; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + uint32_t reserved; + }; + + struct load_command { + uint32_t cmd; + uint32_t cmdsize; + }; + + struct segment_command { + uint32_t cmd; + uint32_t cmdsize; + char segname[16]; + uint32_t vmaddr; + uint32_t vmsize; + uint32_t fileoff; + uint32_t filesize; + vm_prot_t maxprot; + vm_prot_t initprot; + uint32_t nsects; + uint32_t flags; + }; + + struct section { // for 32-bit architectures + char sectname[16]; // name of this section + char segname[16]; // segment this section goes in + uint32_t addr; // memory address of this section + uint32_t size; // size in bytes of this section + uint32_t offset; // file offset of this section + uint32_t align; // section alignment (power of 2) + uint32_t reloff; // file offset of relocation entries + uint32_t nreloc; // number of relocation entries + uint32_t flags; // flags (section type and attributes) + uint32_t reserved1; // reserved (for offset or index) + uint32_t reserved2; // reserved (for count or sizeof) + }; + + struct section_64 { // for 64-bit architectures + char sectname[16]; // name of this section + char segname[16]; // segment this section goes in + uint64_t addr; // memory address of this section + uint64_t size; // size in bytes of this section + uint32_t offset; // file offset of this section + uint32_t align; // section alignment (power of 2) + uint32_t reloff; // file offset of relocation entries + uint32_t nreloc; // number of relocation entries + uint32_t flags; // flags (section type and attributes) + uint32_t reserved1; // reserved (for offset or index) + uint32_t reserved2; // reserved (for count or sizeof) + uint32_t reserved3; // reserved + }; + + struct segment_command_64 { + uint32_t cmd; + uint32_t cmdsize; + char segname[16]; + uint64_t vmaddr; + uint64_t vmsize; + uint64_t fileoff; + uint64_t filesize; + vm_prot_t maxprot; + vm_prot_t initprot; + uint32_t nsects; + uint32_t flags; + }; + + struct uuid_command { + uint32_t cmd; + uint32_t cmdsize; + char uuid[16]; + }; + + struct fvmlib_command { + uint32_t cmd; + uint32_t cmdsize; + char segname[16]; + uint32_t minor_version; + uint32_t header_addr; + }; + + struct dylib { + uint32_t name; + uint32_t timestamp; + uint32_t current_version; + uint32_t compatibility_version; + }; + + struct dylib_command { + uint32_t cmd; + uint32_t cmdsize; + struct dylib dylib; + }; + + struct sub_framework_command { + uint32_t cmd; // LC_SUB_FRAMEWORK + uint32_t cmdsize; // includes umbrella string + uint32_t umbrella; // the umbrella framework name + }; + + struct sub_client_command { + uint32_t cmd; // LC_SUB_CLIENT + uint32_t cmdsize; // includes client string + uint32_t client; // offset to the string + }; + + struct sub_umbrella_command { + uint32_t cmd; // LC_SUB_UMBRELLA + uint32_t cmdsize; // includes sub_umbrella string + uint32_t sub_umbrella; // the sub_umbrella framework name + }; + + struct sub_library_command { + uint32_t cmd; // LC_SUB_LIBRARY + uint32_t cmdsize; // includes sub_library string + uint32_t sub_library; // the sub_library name + }; + + struct prebound_dylib_command { + uint32_t cmd; // LC_PREBOUND_DYLIB + uint32_t cmdsize; // includes strings + uint32_t name; // library's path name + uint32_t nmodules; // number of modules in library + uint32_t linked_modules; // bit vector of linked modules + }; + + struct dylinker_command { + uint32_t cmd; // LC_ID_DYLINKER or LC_LOAD_DYLINKER + uint32_t cmdsize; // includes pathname string + uint32_t name; // dynamic linker's path name + }; + + struct x86_thread_state64 { + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint64_t cs; + uint64_t fs; + uint64_t gs; + }; + + struct x86_thread_state32 { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ss; + uint32_t eflags; + uint32_t eip; + uint32_t cs; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; + }; + + struct armv7_thread_state { + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t r7; + uint32_t r8; + uint32_t r9; + uint32_t r10; + uint32_t r11; + uint32_t r12; + uint32_t r13; + uint32_t r14; //SP + uint32_t r15; //PC + }; + + struct arm_thread_state64 { + uint64_t x[29]; // GPR x0-x28 + uint64_t fp; // x29 + uint64_t lr; // x30 + uint64_t sp; // x31 + uint64_t pc; // Program Counter + uint32_t cpsr; // Current program status register + }; + + struct ppc_thread_state { + uint32_t srr0; //Machine state register (PC) + uint32_t srr1; + uint32_t r0; + uint32_t r1; //Stack pointer + uint32_t r2; + uint32_t r3; + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t r7; + uint32_t r8; + uint32_t r9; + uint32_t r10; + uint32_t r11; + uint32_t r12; + uint32_t r13; + uint32_t r14; + uint32_t r15; + uint32_t r16; + uint32_t r17; + uint32_t r18; + uint32_t r19; + uint32_t r20; + uint32_t r21; + uint32_t r22; + uint32_t r23; + uint32_t r24; + uint32_t r25; + uint32_t r26; + uint32_t r27; + uint32_t r28; + uint32_t r29; + uint32_t r30; + uint32_t r31; + uint32_t cr; + uint32_t xer; + uint32_t lr; + uint32_t ctr; + uint32_t mq; + uint32_t vrsave; + }; + + #pragma pack(push, 4) + struct ppc_thread_state64 { + uint64_t srr0; //Machine state register (PC) + uint64_t srr1; + uint64_t r0; //Stack pointer + uint64_t r1; + uint64_t r2; + uint64_t r3; + uint64_t r4; + uint64_t r5; + uint64_t r6; + uint64_t r7; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t r16; + uint64_t r17; + uint64_t r18; + uint64_t r19; + uint64_t r20; + uint64_t r21; + uint64_t r22; + uint64_t r23; + uint64_t r24; + uint64_t r25; + uint64_t r26; + uint64_t r27; + uint64_t r28; + uint64_t r29; + uint64_t r30; + uint64_t r31; + uint32_t cr; // Condition register + uint64_t xer; // User's integer exception register + uint64_t lr; // Link register + uint64_t ctr; // Count register + uint32_t vrsave; // Vector Save Register + }; + #pragma pack(pop) + + struct thread_command { + uint32_t cmd; // LC_THREAD or LC_UNIXTHREAD + uint32_t cmdsize; // total size of this command + uint32_t flavor; // flavor of thread state + uint32_t count; //count of longs in thread state + union { + x86_thread_state64 statex64; + x86_thread_state32 statex86; + armv7_thread_state statearmv7; + arm_thread_state64 stateaarch64; + ppc_thread_state stateppc; + ppc_thread_state64 stateppc64; + }; + }; + + struct linkedit_data_command { + uint32_t cmd; + uint32_t cmdsize; + uint32_t dataoff; + uint32_t datasize; + }; + + + enum MachOArchitecture { + MachOABIMask = 0xff000000, + MachOABI64 = 0x01000000, // 64 bit ABI + MachOABI6432 = 0x02000000, // "ABI for 64-bit hardware with 32-bit types; LP32" + + // Constants for the cputype field. + MachOx86 = 7, + MachOx64 = MachOx86 | MachOABI64, + MachOArm = 0xc, + MachOAarch64 = MachOABI64 | MachOArm, + MachOAarch6432 = MachOABI6432 | MachOArm, + MachOSPARC = 0xe, + MachOPPC = 0x12, + MachOPPC64 = MachOABI64 | MachOPPC, + }; + + enum BindOpcode { + BindOpcodeMask = 0xF0u, // BIND_OPCODE_MASK + BindImmediateMask = 0x0Fu, // BIND_IMMEDIATE_MASK + BindOpcodeDone = 0x00u, // BIND_OPCODE_DONE + BindOpcodeSetDylibOrdinalImmediate = 0x10u, // BIND_OPCODE_SET_DYLIB_ORDINAL_IMM + BindOpcodeSetDylibOrdinalULEB = 0x20u, // BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB + BindOpcodeSetDylibSpecialImmediate = 0x30u, // BIND_OPCODE_SET_DYLIB_SPECIAL_IMM + BindOpcodeSetSymbolTrailingFlagsImmediate = 0x40u, // BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM + BindOpcodeSetTypeImmediate = 0x50u, // BIND_OPCODE_SET_TYPE_IMM + BindOpcodeSetAddendSLEB = 0x60u, // BIND_OPCODE_SET_ADDEND_SLEB + BindOpcodeSetSegmentAndOffsetULEB = 0x70u, // BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + BindOpcodeAddAddressULEB = 0x80u, // BIND_OPCODE_ADD_ADDR_ULEB + BindOpcodeDoBind = 0x90u, // BIND_OPCODE_DO_BIND + BindOpcodeDoBindAddAddressULEB = 0xA0u, // BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB + BindOpcodeDoBindAddAddressImmediateScaled = 0xB0u, // BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED + BindOpcodeDoBindULEBTimesSkippingULEB = 0xC0u, // BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB + }; + + enum X86ThreadFlavor { + X86_THREAD_STATE32 = 0x1, + X86_FLOAT_STATE32 = 0x2, + X86_EXCEPTION_STATE32 = 0x3, + X86_THREAD_STATE64 = 0x4, + X86_FLOAT_STATE64 = 0x5, + X86_EXCEPTION_STATE64 = 0x6, + X86_THREAD_STATE = 0x7, + X86_FLOAT_STATE = 0x8, + X86_EXCEPTION_STATE = 0x9, + X86_DEBUG_STATE32 = 0xA, + X86_DEBUG_STATE64 = 0xB, + X86_DEBUG_STATE = 0xC, + _THREAD_STATE_NONE = 0xD + }; + + enum PPCThreadFlavor { + PPC_THREAD_STATE = 0x1, + PPC_FLOAT_STATE = 0x2, + PPC_EXCEPTION_STATE = 0x3, + PPC_VECTOR_STATE = 0x4, + PPC_THREAD_STATE64 = 0x5, + PPC_EXCEPTION_STATE64 = 0x6 + }; + + enum ARMThreadFlavor + { + _ARM_THREAD_STATE = 1, + _ARM_VFP_STATE = 2, + _ARM_EXCEPTION_STATE = 3, + _ARM_DEBUG_STATE = 4, /* pre-armv8 */ + _ARM_THREAD_STATE64 = 6, + _ARM_EXCEPTION_STATE64 = 7 + }; + + struct routines_command { // for 32-bit architectures + uint32_t cmd; // LC_ROUTINES + uint32_t cmdsize; // total size of this command + uint32_t init_address; // address of initialization routine + uint32_t init_module; // index into the module table that the init routine is defined in + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + uint32_t reserved4; + uint32_t reserved5; + uint32_t reserved6; + }; + + struct routines_command_64 { // for 64-bit architectures + uint32_t cmd; // LC_ROUTINES_64 + uint32_t cmdsize; // total size of this command + uint64_t init_address; // address of initialization routine + uint64_t init_module; // index into the module table that the init routine is defined in + uint64_t reserved1; + uint64_t reserved2; + uint64_t reserved3; + uint64_t reserved4; + uint64_t reserved5; + uint64_t reserved6; + }; + + struct symtab_command { + uint32_t cmd; // LC_SYMTAB + uint32_t cmdsize; // sizeof(struct symtab_command) + uint32_t symoff; // symbol table offset + uint32_t nsyms; // number of symbol table entries + uint32_t stroff; // string table offset + uint32_t strsize; // string table size in bytes + }; + + // Lots of good information on how to use dysymtab for relocations + // https://opensource.apple.com/source/cctools/cctools-795/include/mach-o/loader.h + struct dysymtab_command { + uint32_t cmd; // LC_DYSYMTAB + uint32_t cmdsize; // sizeof(struct dysymtab_command) + uint32_t ilocalsym; // index to local symbols + uint32_t nlocalsym; // number of local symbols + uint32_t iextdefsym; // index to externally defined symbols + uint32_t nextdefsym; // number of externally defined symbols + uint32_t iundefsym; // index to undefined symbols + uint32_t nundefsym; // number of undefined symbols + uint32_t tocoff; // file offset to table of contents + uint32_t ntoc; // number of entries in table of contents + uint32_t modtaboff; // file offset to module table + uint32_t nmodtab; // number of module table entries + uint32_t extrefsymoff; // offset to referenced symbol table + uint32_t nextrefsyms; // number of referenced symbol table entries + uint32_t indirectsymoff; // file offset to the indirect symbol table + uint32_t nindirectsyms; // number of indirect symbol table entries + uint32_t extreloff; // offset to external relocation entries + uint32_t nextrel; // number of external relocation entries + uint32_t locreloff; // offset to local relocation entries + uint32_t nlocrel; // number of local relocation entries + }; + + struct twolevel_hints_command { + uint32_t cmd; // LC_TWOLEVEL_HINTS + uint32_t cmdsize; // sizeof(struct twolevel_hints_command) + uint32_t offset; // offset to the hint table + uint32_t nhints; // number of hints in the hint table + }; + + struct dyld_info_command { + uint32_t cmd; // LC_DYLD_INFO or LC_DYLD_INFO_ONLY + uint32_t cmdsize; // sizeof(struct dyld_info_command) + uint32_t rebase_off; // file offset to rebase info + uint32_t rebase_size; // size of rebase info + uint32_t bind_off; // file offset to binding info + uint32_t bind_size; // size of binding info + uint32_t weak_bind_off; // file offset to weak binding info + uint32_t weak_bind_size; // size of weak binding info + uint32_t lazy_bind_off; // file offset to lazy binding info + uint32_t lazy_bind_size; // size of lazy binding infs + uint32_t export_off; // file offset to lazy binding info + uint32_t export_size; // size of lazy binding infs + }; + + struct function_starts_command { + uint32_t cmd; //LC_FUNCTION_STARTS + uint32_t cmdsize; //sizeof(struct function_starts_command) + uint32_t funcoff; //offset to function starts list + uint32_t funcsize; //sizeof function starts list + }; + + enum MachoPlatform { + MACHO_PLATFORM_MACOS = 1, + MACHO_PLATFORM_IOS = 2, + MACHO_PLATFORM_TVOS = 3, + MACHO_PLATFORM_WATCHOS = 4, + MACHO_PLATFORM_BRIDGEOS = 5 + }; + + enum MachoBuildTool { + MACHO_TOOL_CLANG = 1, + MACHO_TOOL_SWIFT = 2, + MACHO_TOOL_LD = 3 + }; + + struct build_tool_version { + uint32_t tool; + uint32_t version; + }; + + struct build_version_command { + uint32_t cmd; // LC_BUILD_VERSION + uint32_t cmdsize; // sizeof(build_version_command) + (ntools * sizeof(build_tool_version) + uint32_t platform; // MachoPlatform + uint32_t minos; // X.Y.Z is encoded in nibbles xxxx.yy.zz + uint32_t sdk; // X.Y.Z is encoded in nibbles xxxx.yy.zz + uint32_t ntools; // number build_tool_version entries + }; + +#pragma pack(push, 1) + struct nlist { + int32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + int16_t n_desc; + uint32_t n_value; + }; + + struct nlist_64 { + uint32_t n_strx; + uint8_t n_type; + uint8_t n_sect; + uint16_t n_desc; + uint64_t n_value; + }; +#pragma pack(pop) + +#define R_ABS 0 + struct relocation_info + { + int32_t r_address; + uint32_t r_symbolnum:24; + uint32_t r_pcrel:1; + uint32_t r_length:2; + uint32_t r_extern:1; + uint32_t r_type:4; + }; +#define R_SCATTERED 0x80000000 + struct scattered_relocation_info_be_64 { + uint32_t r_scattered:1; // 1=scattered, 0=non-scattered (see above) + uint32_t r_pcrel:1; // was relocated pc relative already + uint32_t r_length:2; // 0=byte, 1=word, 2=long + uint32_t r_type:4; // if not 0, machine specific relocation type + uint32_t r_address:24; // offset in the section to what is being relocated + int64_t r_value; // the value the item to be relocated is refering to (without any offset added) + }; + struct scattered_relocation_info_le_64 { + uint32_t r_address:24; // offset in the section to what is being relocated + uint32_t r_type:4; // if not 0, machine specific relocation type + uint32_t r_length:2; // 0=byte, 1=word, 2=long + uint32_t r_pcrel:1; // was relocated pc relative already + uint32_t r_scattered:1; // 1=scattered, 0=non-scattered (see above) + int64_t r_value; // the value the item to be relocated is refering to (without any offset added) + }; + struct scattered_relocation_info_be { + uint32_t r_scattered:1; // 1=scattered, 0=non-scattered (see above) + uint32_t r_pcrel:1; // was relocated pc relative already + uint32_t r_length:2; // 0=byte, 1=word, 2=long + uint32_t r_type:4; // if not 0, machine specific relocation type + uint32_t r_address:24; // offset in the section to what is being relocated + int32_t r_value; // the value the item to be relocated is refering to (without any offset added) + }; + struct scattered_relocation_info_le { + uint32_t r_address:24; // offset in the section to what is being relocated + uint32_t r_type:4; // if not 0, machine specific relocation type + uint32_t r_length:2; // 0=byte, 1=word, 2=long + uint32_t r_pcrel:1; // was relocated pc relative already + uint32_t r_scattered:1; // 1=scattered, 0=non-scattered (see above) + int32_t r_value; // the value the item to be relocated is refering to (without any offset added) + }; + + enum reloc_type_generic + { + GENERIC_RELOC_VANILLA, // generic relocation as discribed above + GENERIC_RELOC_PAIR, // Only follows a GENRIC_RELOC_SECTDIFF + GENERIC_RELOC_SECTDIFF, + GENERIC_RELOC_PB_LA_PTR // prebound lazy pointer + }; + + struct dyld_chained_fixups_header + { + uint32_t fixups_version; // 0 + uint32_t starts_offset; // offset of dyld_chained_starts_in_image in chain_data + uint32_t imports_offset; // offset of imports table in chain_data + uint32_t symbols_offset; // offset of symbol strings in chain_data + uint32_t imports_count; // number of imported symbol names + uint32_t imports_format; // DYLD_CHAINED_IMPORT* + uint32_t symbols_format; // 0 => uncompressed, 1 => zlib compressed + }; + + // This struct is embedded in LC_DYLD_CHAINED_FIXUPS payload + struct dyld_chained_starts_in_image + { + uint32_t seg_count; + uint32_t seg_info_offset[1]; // each entry is offset into this struct for that segment + // followed by pool of dyld_chain_starts_in_segment data + }; + + // This struct is embedded in dyld_chain_starts_in_image + // and passed down to the kernel for page-in linking + struct dyld_chained_starts_in_segment + { + uint32_t size; // size of this (amount kernel needs to copy) + uint16_t page_size; // 0x1000 or 0x4000 + uint16_t pointer_format; // DYLD_CHAINED_PTR_* + uint64_t segment_offset; // offset in memory to start of segment + uint32_t max_valid_pointer; // for 32-bit OS, any value beyond this is not a pointer + uint16_t page_count; // how many pages are in array + uint16_t page_start[1]; // each entry is offset in each page of first element in chain + // or DYLD_CHAINED_PTR_START_NONE if no fixups on page + + // uint16_t chain_starts[1]; // some 32-bit formats may require multiple starts per page. + // for those, if high bit is set in page_starts[], then it + // is index into chain_starts[] which is a list of starts + // the last of which has the high bit set + }; + + enum { + DYLD_CHAINED_PTR_START_NONE = 0xFFFF, // used in page_start[] to denote a page with no fixups + DYLD_CHAINED_PTR_START_MULTI = 0x8000, // used in page_start[] to denote a page which has multiple starts + DYLD_CHAINED_PTR_START_LAST = 0x8000, // used in chain_starts[] to denote last start in list for page + }; + + // This struct is embedded in __TEXT,__chain_starts section in firmware + struct dyld_chained_starts_offsets + { + uint32_t pointer_format; // DYLD_CHAINED_PTR_32_FIRMWARE + uint32_t starts_count; // number of starts in array + uint32_t chain_starts[1]; // array chain start offsets + }; + + // values for dyld_chained_starts_in_segment.pointer_format + enum { + DYLD_CHAINED_PTR_ARM64E = 1, // stride 8, unauth target is vmaddr + DYLD_CHAINED_PTR_64 = 2, // target is vmaddr + DYLD_CHAINED_PTR_32 = 3, + DYLD_CHAINED_PTR_32_CACHE = 4, + DYLD_CHAINED_PTR_32_FIRMWARE = 5, + DYLD_CHAINED_PTR_64_OFFSET = 6, // target is vm offset + DYLD_CHAINED_PTR_ARM64E_OFFSET = 7, // old name + DYLD_CHAINED_PTR_ARM64E_KERNEL = 7, // stride 4, unauth target is vm offset + DYLD_CHAINED_PTR_64_KERNEL_CACHE = 8, + DYLD_CHAINED_PTR_ARM64E_USERLAND = 9, // stride 8, unauth target is vm offset + DYLD_CHAINED_PTR_ARM64E_FIRMWARE = 10, // stride 4, unauth target is vmaddr + DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE = 11, // stride 1, x86_64 kernel caches + DYLD_CHAINED_PTR_ARM64E_USERLAND24 = 12, // stride 8, unauth target is vm offset, 24-bit bind + }; + + // DYLD_CHAINED_PTR_ARM64E + struct dyld_chained_ptr_arm64e_rebase + { + uint64_t target : 43, + high8 : 8, + next : 11, // 4 or 8-byte stide + bind : 1, // == 0 + auth : 1; // == 0 + }; + + // DYLD_CHAINED_PTR_ARM64E + struct dyld_chained_ptr_arm64e_bind + { + uint64_t ordinal : 16, + zero : 16, + addend : 19, // +/-256K + next : 11, // 4 or 8-byte stide + bind : 1, // == 1 + auth : 1; // == 0 + }; + + // DYLD_CHAINED_PTR_ARM64E + struct dyld_chained_ptr_arm64e_auth_rebase + { + uint64_t target : 32, // runtimeOffset + diversity : 16, + addrDiv : 1, + key : 2, + next : 11, // 4 or 8-byte stide + bind : 1, // == 0 + auth : 1; // == 1 + }; + + // DYLD_CHAINED_PTR_ARM64E + struct dyld_chained_ptr_arm64e_auth_bind + { + uint64_t ordinal : 16, + zero : 16, + diversity : 16, + addrDiv : 1, + key : 2, + next : 11, // 4 or 8-byte stide + bind : 1, // == 1 + auth : 1; // == 1 + }; + + // DYLD_CHAINED_PTR_64/DYLD_CHAINED_PTR_64_OFFSET + struct dyld_chained_ptr_64_rebase + { + uint64_t target : 36, // 64GB max image size (DYLD_CHAINED_PTR_64 => vmAddr, DYLD_CHAINED_PTR_64_OFFSET => runtimeOffset) + high8 : 8, // top 8 bits set to this (DYLD_CHAINED_PTR_64 => after slide added, DYLD_CHAINED_PTR_64_OFFSET => before slide added) + reserved : 7, // all zeros + next : 12, // 4-byte stride + bind : 1; // == 0 + }; + + + // DYLD_CHAINED_PTR_ARM64E_USERLAND24 + struct dyld_chained_ptr_arm64e_bind24 + { + uint64_t ordinal : 24, + zero : 8, + addend : 19, // +/-256K + next : 11, // 8-byte stide + bind : 1, // == 1 + auth : 1; // == 0 + }; + + // DYLD_CHAINED_PTR_ARM64E_USERLAND24 + struct dyld_chained_ptr_arm64e_auth_bind24 + { + uint64_t ordinal : 24, + zero : 8, + diversity : 16, + addrDiv : 1, + key : 2, + next : 11, // 8-byte stide + bind : 1, // == 1 + auth : 1; // == 1 + }; + + + // DYLD_CHAINED_PTR_64 + struct dyld_chained_ptr_64_bind + { + uint64_t ordinal : 24, + addend : 8, // 0 thru 255 + reserved : 19, // all zeros + next : 12, // 4-byte stride + bind : 1; // == 1 + }; + + // DYLD_CHAINED_PTR_64_KERNEL_CACHE, DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE + struct dyld_chained_ptr_64_kernel_cache_rebase + { + uint64_t target : 30, // basePointers[cacheLevel] + target + cacheLevel : 2, // what level of cache to bind to (indexes a mach_header array) + diversity : 16, + addrDiv : 1, + key : 2, + next : 12, // 1 or 4-byte stide + isAuth : 1; // 0 -> not authenticated. 1 -> authenticated + }; + + // DYLD_CHAINED_PTR_32 + // Note: for DYLD_CHAINED_PTR_32 some non-pointer values are co-opted into the chain + // as out of range rebases. If an entry in the chain is > max_valid_pointer, then it + // is not a pointer. To restore the value, subtract off the bias, which is + // (64MB+max_valid_pointer)/2. + struct dyld_chained_ptr_32_rebase + { + uint32_t target : 26, // vmaddr, 64MB max image size + next : 5, // 4-byte stride + bind : 1; // == 0 + }; + + // DYLD_CHAINED_PTR_32 + struct dyld_chained_ptr_32_bind + { + uint32_t ordinal : 20, + addend : 6, // 0 thru 63 + next : 5, // 4-byte stride + bind : 1; // == 1 + }; + + // DYLD_CHAINED_PTR_32_CACHE + struct dyld_chained_ptr_32_cache_rebase + { + uint32_t target : 30, // 1GB max dyld cache TEXT and DATA + next : 2; // 4-byte stride + }; + + + // DYLD_CHAINED_PTR_32_FIRMWARE + struct dyld_chained_ptr_32_firmware_rebase + { + uint32_t target : 26, // 64MB max firmware TEXT and DATA + next : 6; // 4-byte stride + }; + + enum ChainedFixupPointerGeneric { + GenericArm64eFixupFormat, + Generic64FixupFormat, + Generic32FixupFormat, + Firmware32FixupFormat + }; + + union Arm64e { + dyld_chained_ptr_arm64e_auth_rebase authRebase; + dyld_chained_ptr_arm64e_auth_bind authBind; + dyld_chained_ptr_arm64e_rebase rebase; + dyld_chained_ptr_arm64e_bind bind; + dyld_chained_ptr_arm64e_bind24 bind24; + dyld_chained_ptr_arm64e_auth_bind24 authBind24; + }; + + union Generic64 { + dyld_chained_ptr_64_rebase rebase; + dyld_chained_ptr_64_bind bind; + }; + + union Generic32 { + dyld_chained_ptr_32_rebase rebase; + dyld_chained_ptr_32_bind bind; + }; + + union ChainedFixupPointer + { + dyld_chained_ptr_64_kernel_cache_rebase kernel64; + dyld_chained_ptr_32_firmware_rebase firmware32; + + uint64_t raw64; + Arm64e arm64e; + Generic64 generic64; + + uint32_t raw32; + Generic32 generic32; + dyld_chained_ptr_32_cache_rebase cache32; + }; + + // values for dyld_chained_fixups_header.imports_format + enum { + DYLD_CHAINED_IMPORT = 1, + DYLD_CHAINED_IMPORT_ADDEND = 2, + DYLD_CHAINED_IMPORT_ADDEND64 = 3, + }; + + // DYLD_CHAINED_IMPORT + struct dyld_chained_import + { + uint32_t lib_ordinal : 8, + weak_import : 1, + name_offset : 23; + }; + + // DYLD_CHAINED_IMPORT_ADDEND + struct dyld_chained_import_addend + { + uint32_t lib_ordinal : 8, + weak_import : 1, + name_offset : 23; + int32_t addend; + }; + + // DYLD_CHAINED_IMPORT_ADDEND64 + struct dyld_chained_import_addend64 + { + uint64_t lib_ordinal : 16, + weak_import : 1, + reserved : 15, + name_offset : 32; + uint64_t addend; + }; + + // When we preload the table, we store them in this format. + struct import_entry + { + uint64_t lib_ordinal; + uint64_t addend; + bool weak; + std::string name; + }; + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_MASK + enum EXPORT_SYMBOL_FLAGS { + EXPORT_SYMBOL_FLAGS_KIND_MASK = 0x03u, ///< Mask to access to EXPORT_SYMBOL_KINDS + EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION = 0x04u, + EXPORT_SYMBOL_FLAGS_REEXPORT = 0x08u, + EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER = 0x10u + }; + + enum EXPORT_SYMBOL_KINDS { + EXPORT_SYMBOL_FLAGS_KIND_REGULAR = 0x00u, + EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL = 0x01u, + EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE = 0x02u + }; +#endif + + struct MachOHeader { + bool isMainHeader; + + uint64_t textBase = 0; + uint64_t loadCommandOffset = 0; + mach_header_64 ident; + std::string identifierPrefix; + + std::vector<std::pair<uint64_t, bool>> entryPoints; + std::vector<uint64_t> m_entryPoints; //list of entrypoints + + symtab_command symtab; + dysymtab_command dysymtab; + dyld_info_command dyldInfo; + routines_command_64 routines64; + function_starts_command functionStarts; + std::vector<section_64> moduleInitSections; + linkedit_data_command exportTrie; + linkedit_data_command chainedFixups {}; + + DataBuffer* stringList; + size_t stringListSize; + + uint64_t relocationBase; + // Section and program headers, internally use 64-bit form as it is a superset of 32-bit + std::vector<segment_command_64> segments; //only three types of sections __TEXT, __DATA, __IMPORT + std::vector<section_64> sections; + std::vector<std::string> sectionNames; + + std::vector<section_64> symbolStubSections; + std::vector<section_64> symbolPointerSections; + + std::vector<std::string> dylibs; + + build_version_command buildVersion; + std::vector<build_tool_version> buildToolVersions; + + bool dysymPresent = false; + bool dyldInfoPresent = false; + bool exportTriePresent = false; + bool chainedFixupsPresent = false; + bool routinesPresent = false; + bool functionStartsPresent = false; + bool relocatable = false; + }; + + class MachoView: public BinaryView + { + MachOHeader m_header; + std::map<uint64_t, MachOHeader> m_subHeaders; // Used for MH_FILESET entries. + + struct HeaderQualifiedNames { + QualifiedName cpuTypeEnumQualName; + QualifiedName fileTypeEnumQualName; + QualifiedName flagsTypeEnumQualName; + QualifiedName headerQualName; + QualifiedName cmdTypeEnumQualName; + QualifiedName loadCommandQualName; + QualifiedName protTypeEnumQualName; + QualifiedName segFlagsTypeEnumQualName; + QualifiedName loadSegmentCommandQualName; + QualifiedName loadSegment64CommandQualName; + QualifiedName sectionQualName; + QualifiedName section64QualName; + QualifiedName symtabQualName; + QualifiedName dynsymtabQualName; + QualifiedName uuidQualName; + QualifiedName linkeditDataQualName; + QualifiedName encryptionInfoQualName; + QualifiedName versionMinQualName; + QualifiedName dyldInfoQualName; + QualifiedName dylibQualName; + QualifiedName dylibCommandQualName; + QualifiedName filesetEntryCommandQualName; + } m_typeNames; + + uint64_t m_universalImageOffset; + bool m_parseOnly, m_backedByDatabase; + int64_t m_imageBaseAdjustment; + size_t m_addressSize; //Address size in bytes 4/8 + BNEndianness m_endian; + uint32_t m_archId; + Ref<Architecture> m_arch; + Ref<Platform> m_plat = nullptr; + bool m_dylibFile; + bool m_objectFile; + std::vector<std::string> m_symbols; + + bool m_relocatable = false; + + bool m_extractMangledTypes; + bool m_simplifyTemplates; + + SymbolQueue* m_symbolQueue = nullptr; + Ref<Logger> m_logger; + + std::vector<segment_command_64> m_allSegments; //only three types of sections __TEXT, __DATA, __IMPORT + std::vector<section_64> m_allSections; + + MachOHeader HeaderForAddress(BinaryView* data, uint64_t address, bool isMainHeader, std::string identifierPrefix = ""); + bool InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_t preferredImageBase, std::string preferredImageBaseDesc); + + void RebaseThreadStarts(BinaryReader& virtualReader, std::vector<uint32_t>& threadStarts, uint64_t stepMultiplier); + Ref<Symbol> DefineMachoSymbol( + BNSymbolType type, const std::string& name, uint64_t addr, BNSymbolBinding binding, bool deferred); + void ParseSymbolTable(BinaryReader& reader, MachOHeader& header, const symtab_command& symtab, const std::vector<uint32_t>& symbolStubsList); + bool IsValidFunctionStart(uint64_t addr); + void ParseFunctionStarts(Platform* platform, uint64_t textBase, function_starts_command functionStarts); + bool ParseRelocationEntry(const relocation_info& info, uint64_t start, BNRelocationInfo& result); + + void ParseExportTrie(BinaryReader& reader, linkedit_data_command exportTrie); + void ReadExportNode(uint64_t viewStart, DataBuffer& buffer, const std::string& currentText, + size_t cursor, uint32_t endGuard); + + void ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNSymbolType type, uint32_t tableOffset, uint32_t tableSize, + BNSymbolBinding binding); + bool GetSectionPermissions(MachOHeader& header, uint64_t address, uint32_t &flags); + bool GetSegmentPermissions(MachOHeader& header, uint64_t address, uint32_t &flags); + void ParseChainedFixups(linkedit_data_command chainedFixups); + + virtual uint64_t PerformGetEntryPoint() const override; + + virtual bool PerformIsExecutable() const override { return true; } + virtual BNEndianness PerformGetDefaultEndianness() const override; + virtual bool PerformIsRelocatable() const override; + virtual size_t PerformGetAddressSize() const override; + public: + MachoView(const std::string& typeName, BinaryView* data, bool parseOnly = false); + + virtual bool Init() override; + }; + + class MachoViewType: public BinaryViewType + { + Ref<Logger> m_logger; + public: + MachoViewType(); + virtual Ref<BinaryView> Create(BinaryView* data) override; + virtual Ref<BinaryView> Parse(BinaryView* data) override; + virtual bool IsTypeValidForData(BinaryView* data) override; + virtual uint64_t ParseHeaders(BinaryView* data, uint64_t imageOffset, mach_header_64& ident, Ref<Architecture>* arch, Ref<Platform>* platform, std::string& errorMsg); + virtual Ref<Settings> GetLoadSettingsForData(BinaryView* data) override; + }; + + void InitMachoViewType(); +} diff --git a/view/macho/universalview.cpp b/view/macho/universalview.cpp new file mode 100644 index 00000000..ac637c75 --- /dev/null +++ b/view/macho/universalview.cpp @@ -0,0 +1,369 @@ +#include <stdlib.h> +#include <string.h> +#include <cstdint> +#include <map> +#ifndef _MSC_VER +#include <cxxabi.h> +#endif +#include "universalview.h" +#include "machoview.h" +#include "rapidjsonwrapper.h" + +using namespace BinaryNinja; +using namespace std; + + +static UniversalViewType* g_universalViewType = nullptr; + + +const map<pair<cpu_type_t, cpu_subtype_t>, string>& UniversalViewType::GetArchitectures() +{ + static map<pair<cpu_type_t, cpu_subtype_t>, string> g_cpuArchNames = + { + {{MACHO_CPU_TYPE_VAX, 0}, "vax"}, + {{MACHO_CPU_TYPE_MC680x0, 0}, "mc680x0"}, + {{MACHO_CPU_TYPE_X86, 0}, "x86"}, + {{MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ALL}, "x86"}, + {{MACHO_CPU_TYPE_X86, MACHO_CPU_SUBTYPE_X86_ARCH1}, "x86 (Arch1)"}, + {{MACHO_CPU_TYPE_X86_64, 0}, "x86_64"}, + {{MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_ALL}, "x86_64"}, + {{MACHO_CPU_TYPE_X86_64, MACHO_CPU_SUBTYPE_X86_64_H}, "x86_64 (Haswell)"}, + {{MACHO_CPU_TYPE_MIPS, 0}, "mips"}, + {{MACHO_CPU_TYPE_MC98000, 0}, "mc98000"}, + {{MACHO_CPU_TYPE_HPPA, 0}, "hppa"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_ALL}, "arm"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V4T}, "armv4t"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6}, "armv6"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V5TEJ}, "armv5tej"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_XSCALE}, "arm (XScale)"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7}, "armv7"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7F}, "armv7f"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7S}, "armv7s"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7K}, "armv7k"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V8}, "armv8"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V6M}, "armv6m"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7M}, "armv7m"}, + {{MACHO_CPU_TYPE_ARM, MACHO_CPU_SUBTYPE_ARM_V7EM}, "armv7em"}, + {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_ALL}, "arm64"}, + {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64_V8}, "arm64v8"}, + {{MACHO_CPU_TYPE_ARM64, MACHO_CPU_SUBTYPE_ARM64E}, "arm64e"}, + {{MACHO_CPU_TYPE_ARM64_32, MACHO_CPU_SUBTYPE_ARM64_32_ALL}, "arm64_32"}, + {{MACHO_CPU_TYPE_ARM64_32, MACHO_CPU_SUBTYPE_ARM64_32_V8}, "arm64_32v8"}, + {{MACHO_CPU_TYPE_MC88000, 0}, "mc88000"}, + {{MACHO_CPU_TYPE_SPARC, 0}, "sparc"}, + {{MACHO_CPU_TYPE_I860, 0}, "i860"}, + {{MACHO_CPU_TYPE_ALPHA, 0}, "alpha"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_ALL}, "ppc"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_601}, "ppc601"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_602}, "ppc602"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603}, "ppc603"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603e}, "ppc603e"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_603ev}, "ppc603ev"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604}, "ppc604"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_604e}, "ppc604e"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_620}, "ppc620"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_750}, "ppc750"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7400}, "ppc7400"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_7450}, "ppc7450"}, + {{MACHO_CPU_TYPE_POWERPC, MACHO_CPU_SUBTYPE_POWERPC_970}, "ppc970"}, + {{MACHO_CPU_TYPE_POWERPC64, 0}, "ppc64"} + }; + + return g_cpuArchNames; +} + + +string UniversalViewType::ArchitectureToString(cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool& is64Bit) +{ + const map<pair<cpu_type_t, cpu_subtype_t>, string>& cpuArchNames = GetArchitectures(); + + switch(cpuType) + { + case MACHO_CPU_TYPE_X86_64: + case MACHO_CPU_TYPE_ARM64: + case MACHO_CPU_TYPE_ARM64_32: + case MACHO_CPU_TYPE_POWERPC64: + is64Bit = true; + break; + default: + is64Bit = false; + break; + } + + auto itr = cpuArchNames.find({cpuType, cpuSubType}); + if (itr != cpuArchNames.end()) + return itr->second; + + itr = cpuArchNames.find({cpuType, 0}); + if (itr != cpuArchNames.end()) + return itr->second; + + return "Unknown"; +} + + +void BinaryNinja::InitUniversalViewType() +{ + static UniversalViewType type; + BinaryViewType::Register(&type); + g_universalViewType = &type; + + Ref<Settings> settings = Settings::Instance(); + settings->RegisterSetting("files.universal.architecturePreference", + R"({ + "title" : "Universal Mach-O Architecture Preference", + "type" : "array", + "elementType" : "string", + "sorted" : false, + "default" : [], + "description" : "Specify an architecture preference for automatic loading of a Mach-O file from a Universal archive. By default, the first object file in the listing is loaded.", + "ignore" : ["SettingsProjectScope", "SettingsResourceScope"] + })"); + + const map<pair<cpu_type_t, cpu_subtype_t>, string>& cpuArchNames = UniversalViewType::GetArchitectures(); + set<string> names; + for (const auto& [key, name] : cpuArchNames) + names.insert(name); + vector<string> archNames(names.begin(), names.end()); + settings->UpdateProperty("files.universal.architecturePreference", "enum", archNames); +} + + +Ref<BinaryView> UniversalViewType::Create(BinaryView* data) +{ + try + { + return new UniversalView(data); + } + catch (std::exception&) + { + return nullptr; + } +} + + +bool UniversalViewType::IsTypeValidForData(BinaryView* data) +{ + DataBuffer sig = data->ReadBuffer(0, 4); + if (sig.GetLength() != 4) + return false; + + uint32_t magic = ToBE32(*(uint32_t*)sig.GetData()); + if ((magic == FAT_MAGIC) || (magic == FAT_MAGIC_64)) + return true; + + return false; +} + + +bool UniversalViewType::ParseHeaders(BinaryView* data, FatHeader& fatHeader, vector<FatArch64>& fatArchEntries, bool& isFat64, string& errorMsg) +{ + if (!IsTypeValidForData(data)) + { + errorMsg = "Universal (Fat Mach-O): invalid signature"; + return false; + } + + BinaryReader reader(data); + reader.SetEndianness(BigEndian); + fatHeader.magic = reader.Read32(); + fatHeader.nfat_arch = reader.Read32(); + + isFat64 = (fatHeader.magic == FAT_MAGIC_64); + size_t requiredFatHeaderSize = fatHeader.nfat_arch * (isFat64 ? 32 : 20) + 8; + if (requiredFatHeaderSize > data->GetLength()) + { + errorMsg = "Universal (Fat Mach-O): header truncated"; + return false; + } + + for (size_t i = 0; i < fatHeader.nfat_arch; i++) + { + FatArch64 fatArch; + if (isFat64) + { + fatArch.cputype = reader.Read32(); + fatArch.cpusubtype = reader.Read32(); + fatArch.offset = reader.Read64(); + fatArch.size = reader.Read64(); + fatArch.align = reader.Read32(); + fatArch.reserved = reader.Read32(); + } + else + { + fatArch.cputype = reader.Read32(); + fatArch.cpusubtype = reader.Read32(); + fatArch.offset = reader.Read32(); + fatArch.size = reader.Read32(); + fatArch.align = reader.Read32(); + } + + // Mask away cpu subtype capability bits + fatArch.cpusubtype &= ~MACHO_CPU_SUBTYPE_MASK; + fatArchEntries.push_back(fatArch); + } + + return true; +} + + +Ref<Settings> UniversalViewType::GetLoadSettingsForData(BinaryView* data) +{ + FatHeader fatHeader; + vector<FatArch64> fatArchEntries; + bool isFat64; + string errorMsg; + if (!g_universalViewType->ParseHeaders(data, fatHeader, fatArchEntries, isFat64, errorMsg)) + return nullptr; + + if (!fatArchEntries.size()) // TODO other validation? + return nullptr; + + Ref<Settings> settings = Settings::Instance(GetUniqueIdentifierString()); + settings->RegisterGroup("loader", "Load Options"); + settings->RegisterSetting("loader.universal.architectures", + R"({ + "title" : "Universal Mach-O Multi-Architecture Binary Description", + "type" : "string", + "default" : "[]", + "description" : "Describes the available object files in the Universal Multi-Architecture binary.", + "readOnly" : true + })"); + + Ref<Settings> entrySettings = Settings::Instance(GetUniqueIdentifierString()); + entrySettings->RegisterGroup("loader", "Load Options"); + entrySettings->RegisterSetting("loader.macho.universalImageOffset", + R"({ + "title" : "Universal Mach-O Object File Offset", + "type" : "number", + "default" : 0, + "description" : "The offset to the object file within the Universal Mach-O file.", + "minValue" : 0, + "maxValue" : 18446744073709551615, + "readOnly" : true + })"); + + rapidjson::Document entries(rapidjson::kArrayType); + int count = 0; + for (const auto& entry : fatArchEntries) + { + rapidjson::Value result(rapidjson::kObjectType); + bool is64Bit; + string archDesc = ArchitectureToString(entry.cputype, entry.cpusubtype, is64Bit); + string bitDesc = is64Bit ? "64-bit" : "32-bit"; + result.AddMember("id", count++, entries.GetAllocator()); + result.AddMember("architecture", archDesc, entries.GetAllocator()); + result.AddMember("is64Bit", is64Bit, entries.GetAllocator()); + result.AddMember("binaryViewType", "Mach-O", entries.GetAllocator()); + result.AddMember("description", "Mach-O " + bitDesc + " executable " + archDesc, entries.GetAllocator()); + result.AddMember("offset", ToLE64(entry.offset), entries.GetAllocator()); + result.AddMember("size", ToLE64(entry.size), entries.GetAllocator()); + entrySettings->UpdateProperty("loader.macho.universalImageOffset", "default", ToLE64(entry.offset)); + result.AddMember("loadSchema", entrySettings->SerializeSchema(), entries.GetAllocator()); + entries.PushBack(result, entries.GetAllocator()); + } + + rapidjson::StringBuffer buffer; + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + entries.Accept(writer); + + settings->UpdateProperty("loader.universal.architectures", "default", buffer.GetString()); + + return settings; +} + + +UniversalView::UniversalView(BinaryView* data, bool parseOnly): BinaryView("Universal", data->GetFile(), data) +{ + FatHeader fatHeader; + vector<FatArch64> fatArchEntries; + bool isFat64; + string errorMsg; + if (!g_universalViewType->ParseHeaders(data, fatHeader, fatArchEntries, isFat64, errorMsg)) + throw MachoFormatException(errorMsg); + + if (parseOnly) + return; + + // Add Universal file header type info + StructureBuilder fatHeaderBuilder; + fatHeaderBuilder.AddMember(Type::IntegerType(4, false), "magic"); + fatHeaderBuilder.AddMember(Type::IntegerType(4, false), "nfat_arch"); + Ref<Structure> fatHeaderStruct = fatHeaderBuilder.Finalize(); + QualifiedName headerName = string("fat_header"); + string headerTypeId = Type::GenerateAutoTypeId("macho", headerName); + Ref<Type> fatHeaderType = Type::StructureType(fatHeaderStruct); + QualifiedName rawHeaderName = DefineType(headerTypeId, headerName, fatHeaderType); + DefineDataVariable(0, Type::NamedType(this, rawHeaderName)); + DefineAutoSymbol(new Symbol(DataSymbol, "__fat_header", 0, LocalBinding)); + + EnumerationBuilder cpuTypeBuilder; + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ANY", MACHO_CPU_TYPE_ANY); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_VAX", MACHO_CPU_TYPE_VAX); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC680x0", MACHO_CPU_TYPE_MC680x0); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_X86", MACHO_CPU_TYPE_X86); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_X86_64", MACHO_CPU_TYPE_X86_64); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MIPS", MACHO_CPU_TYPE_MIPS); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC98000", MACHO_CPU_TYPE_MC98000); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_HPPA", MACHO_CPU_TYPE_HPPA); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM", MACHO_CPU_TYPE_ARM); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM64", MACHO_CPU_TYPE_ARM64); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM64_32", MACHO_CPU_TYPE_ARM64_32); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC88000", MACHO_CPU_TYPE_MC88000); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_SPARC", MACHO_CPU_TYPE_SPARC); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_I860", MACHO_CPU_TYPE_I860); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ALPHA", MACHO_CPU_TYPE_ALPHA); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_POWERPC", MACHO_CPU_TYPE_POWERPC); + cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_POWERPC64", MACHO_CPU_TYPE_POWERPC64); + Ref<Enumeration> cpuTypeEnum = cpuTypeBuilder.Finalize(); + + Ref<Type> cpuTypeEnumType = Type::EnumerationType(nullptr, cpuTypeEnum, 4, false); + string cpuTypeEnumName = "cpu_type_t"; + string cpuTypeEnumId = Type::GenerateAutoTypeId("macho", cpuTypeEnumName); + QualifiedName rawCpuTypeEnumName = DefineType(cpuTypeEnumId, cpuTypeEnumName, cpuTypeEnumType); + + StructureBuilder fatArchBuilder; + fatArchBuilder.AddMember(Type::NamedType(this, rawCpuTypeEnumName), "cputype"); + fatArchBuilder.AddMember(Type::IntegerType(4, false), "cpusubtype"); + if (isFat64) + { + fatArchBuilder.AddMember(Type::IntegerType(8, false), "offset"); + fatArchBuilder.AddMember(Type::IntegerType(8, false), "size"); + } + else + { + fatArchBuilder.AddMember(Type::IntegerType(4, false), "offset"); + fatArchBuilder.AddMember(Type::IntegerType(4, false), "size"); + } + fatArchBuilder.AddMember(Type::IntegerType(4, false), "align"); + if (isFat64) + fatArchBuilder.AddMember(Type::IntegerType(4, false), "reserved"); + Ref<Structure> fatArchStruct = fatArchBuilder.Finalize(); + QualifiedName fatArchName = isFat64 ? string("fat_arch_64") : string("fat_arch"); + string fatArchTypeId = Type::GenerateAutoTypeId("macho", fatArchName); + Ref<Type> fatArchType = Type::StructureType(fatArchStruct); + QualifiedName rawFatArchName = DefineType(fatArchTypeId, fatArchName, fatArchType); + DefineDataVariable(8, Type::ArrayType(Type::NamedType(this, rawFatArchName), fatHeader.nfat_arch)); + DefineAutoSymbol(new Symbol(DataSymbol, "__fat_arch_entries", 8, LocalBinding)); +} + + +UniversalView::~UniversalView() +{ +} + + +bool UniversalView::Init() +{ + // Disable analysis modules + // TODO: for now Raw and Universal views do this; possibly refactor + if (!m_file->IsBackedByDatabase(GetTypeName())) + { + Settings::Instance()->Set("analysis.linearSweep.autorun", false, this); + Settings::Instance()->Set("analysis.signatureMatcher.autorun", false, this); + Settings::Instance()->Set("analysis.pointerSweep.autorun", false, this); + } + + AddAutoSegment(0, GetParentView()->GetLength(), 0, GetParentView()->GetLength(), SegmentReadable | SegmentWritable); + return true; +} diff --git a/view/macho/universalview.h b/view/macho/universalview.h new file mode 100644 index 00000000..aaf483cc --- /dev/null +++ b/view/macho/universalview.h @@ -0,0 +1,40 @@ +#pragma once + +#include <exception> +#include <vector> +#include <string.h> + +#include "binaryninjaapi.h" +#include "fatmachoview.h" +#include "machoview.h" + + +namespace BinaryNinja +{ + void InitUniversalViewType(); + + class UniversalViewType: public BinaryViewType + { + public: + static const std::map<std::pair<cpu_type_t, cpu_subtype_t>, std::string>& GetArchitectures(); + static std::string ArchitectureToString(cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool& is64Bit); + + UniversalViewType(): BinaryViewType("Universal", "Universal") { } + virtual Ref<BinaryView> Create(BinaryView* data) override; + virtual bool IsTypeValidForData(BinaryView* data) override; + virtual bool ParseHeaders(BinaryView* data, FatHeader& fatHeader, std::vector<FatArch64>& fatArchEntries, bool& isFat64, std::string& errorMsg); + virtual Ref<Settings> GetLoadSettingsForData(BinaryView* data) override; + }; + + class UniversalView: public BinaryView + { + public: + UniversalView(BinaryView* data, bool parseOnly = false); + virtual ~UniversalView(); + + virtual bool Init() override; + virtual BNEndianness PerformGetDefaultEndianness() const override { return BigEndian; } + virtual bool PerformIsExecutable() const override { return false; } + virtual bool PerformIsRelocatable() const override { return false; }; + }; +} |
