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