summaryrefslogtreecommitdiff
path: root/view/sharedcache/core
diff options
context:
space:
mode:
authorkat <kat@vector35.com>2024-10-23 21:56:29 -0400
committerkat <kat@vector35.com>2024-10-23 21:58:47 -0400
commit3006c68e108a18f9b8782bc937dc9ec7e2cb3b54 (patch)
treef589bb38909349142c09b6215244205aef0a57ac /view/sharedcache/core
parent80fcccec8f686e0e5c89626b60af121a97baf856 (diff)
Initial commit of the alpha dyld_shared_cache view API Plugin.
This is an early release of our DSC processing plugin. We're still hard at work improving this feature. You should be able to just drop in a dyld_shared_cache and use the 'Shared Cache Triage' view to load and analyze images.
Diffstat (limited to 'view/sharedcache/core')
-rw-r--r--view/sharedcache/core/CMakeLists.txt84
-rw-r--r--view/sharedcache/core/DSCView.cpp872
-rw-r--r--view/sharedcache/core/DSCView.h65
-rw-r--r--view/sharedcache/core/MetadataSerializable.hpp537
-rw-r--r--view/sharedcache/core/ObjC.cpp1524
-rw-r--r--view/sharedcache/core/ObjC.h233
-rw-r--r--view/sharedcache/core/SharedCache.cpp3286
-rw-r--r--view/sharedcache/core/SharedCache.h1197
-rw-r--r--view/sharedcache/core/VM.cpp793
-rw-r--r--view/sharedcache/core/VM.h330
10 files changed, 8921 insertions, 0 deletions
diff --git a/view/sharedcache/core/CMakeLists.txt b/view/sharedcache/core/CMakeLists.txt
new file mode 100644
index 00000000..89e89811
--- /dev/null
+++ b/view/sharedcache/core/CMakeLists.txt
@@ -0,0 +1,84 @@
+cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
+
+project(sharedcachecore)
+
+if((NOT BN_API_PATH) AND (NOT BN_INTERNAL_BUILD))
+ set(BN_API_PATH $ENV{BN_API_PATH})
+ if(NOT BN_API_PATH)
+ message(FATAL_ERROR "Provide path to Binary Ninja API source in BN_API_PATH")
+ endif()
+endif()
+file(GLOB COMMON_SOURCES
+ *.cpp
+ *.h
+ )
+set(SOURCES ${COMMON_SOURCES})
+
+add_library(sharedcachecore OBJECT ${SOURCES})
+
+function(get_recursive_include_dirs target result)
+ # Initialize an empty list to store include directories
+ set(include_dirs "")
+
+ # Get the include directories of the current target
+ get_target_property(current_target_includes ${target} INTERFACE_INCLUDE_DIRECTORIES)
+ if(current_target_includes)
+ list(APPEND include_dirs ${current_target_includes})
+ endif()
+
+ # Get the libraries that this target links to
+ get_target_property(linked_libraries ${target} INTERFACE_LINK_LIBRARIES)
+ if(linked_libraries)
+ foreach(linked_library IN LISTS linked_libraries)
+ # Skip plain library names (non-target libraries)
+ if(TARGET ${linked_library})
+ # Recursively get include directories from linked libraries
+ get_recursive_include_dirs(${linked_library} linked_library_includes)
+ list(APPEND include_dirs ${linked_library_includes})
+ endif()
+ endforeach()
+ endif()
+
+ # Set the result to the collected include directories
+ set(${result} ${include_dirs} PARENT_SCOPE)
+endfunction()
+
+get_recursive_include_dirs(binaryninjaapi INCLUDES)
+
+
+if (VIEW_NAME)
+ if (SLIDEINFO_DEBUG_TAGS)
+ target_compile_definitions(sharedcachecore PRIVATE VIEW_NAME="${VIEW_NAME}" SHAREDCACHE_LIBRARY SLIDEINFO_DEBUG_TAGS)
+ else()
+ target_compile_definitions(sharedcachecore PRIVATE VIEW_NAME="${VIEW_NAME}" SHAREDCACHE_LIBRARY)
+ endif()
+ message(STATUS "VIEW_NAME: ${VIEW_NAME}")
+else()
+ error("VIEW_NAME must be defined")
+endif()
+
+
+target_include_directories(sharedcachecore PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${INCLUDES})
+
+set_target_properties(sharedcachecore PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON
+ )
+
+if(BN_INTERNAL_BUILD)
+ set(LIBRARY_OUTPUT_DIRECTORY_PATH "${BN_CORE_PLUGIN_DIR}")
+else()
+ set(LIBRARY_OUTPUT_DIRECTORY_PATH "${CMAKE_BINARY_DIR}/out/plugins")
+endif()
+
+set_target_properties(sharedcachecore PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_DIRECTORY_PATH}
+ RUNTIME_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_DIRECTORY_PATH}
+ )
+
+target_compile_definitions(sharedcachecore PRIVATE SHAREDCACHE_CORE_LIBRARY)
+
+
diff --git a/view/sharedcache/core/DSCView.cpp b/view/sharedcache/core/DSCView.cpp
new file mode 100644
index 00000000..b065f1e6
--- /dev/null
+++ b/view/sharedcache/core/DSCView.cpp
@@ -0,0 +1,872 @@
+//
+// Created by kat on 5/23/23.
+//
+
+/*
+ * If you're here looking for the code to load caches, out of luck.
+ *
+ * The VIEW_NAME is essentially a blank slate that only knows how to deserialize and reserialize itself
+ * based on some metadata encoded in it.
+ *
+ * The actual controller logic that does _all_ of the image loading is invoked via API -> SharedCache.cpp
+ *
+ * */
+
+#include "DSCView.h"
+#include "view/macho/machoview.h"
+#include "SharedCache.h"
+
+using namespace BinaryNinja;
+
+/*
+ * DSCRawView is a "fake" parent view that the child view actually fills with data on init.
+ *
+ * This is the 'magic' that makes this sort of horrible "serialize the file headers and then refill the parent view"
+ * work.
+ *
+ * This throws errors on deser due to undo actions, but it still works.
+ * */
+DSCRawView::DSCRawView(const std::string& typeName, BinaryView* data, bool parseOnly) :
+ BinaryView(typeName, data->GetFile(), data)
+{
+ // This is going to load _only_ the dyld header of the loaded file.
+ // This written region will be immediately overwritten on image loading by SharedCache.cpp
+ GetFile()->SetFilename(data->GetFile()->GetOriginalFilename());
+ auto reader = new BinaryReader(GetParentView());
+ reader->Seek(16);
+ auto size = reader->Read32() + 0x8;
+ AddAutoSegment(0, size, 0, size, SegmentReadable);
+ GetParentView()->WriteBuffer(0, GetParentView()->ReadBuffer(0, size));
+}
+
+bool DSCRawView::Init()
+{
+ return true;
+}
+
+DSCRawViewType::DSCRawViewType() : BinaryViewType("DSCRaw", "DSCRaw") {}
+
+BinaryNinja::Ref<BinaryNinja::BinaryView> DSCRawViewType::Create(BinaryView* data)
+{
+ return new DSCRawView("DSCRaw", data, false);
+}
+
+BinaryNinja::Ref<BinaryNinja::BinaryView> DSCRawViewType::Parse(BinaryView* data)
+{
+ return new DSCRawView("DSCRaw", data, true);
+}
+
+bool DSCRawViewType::IsTypeValidForData(BinaryNinja::BinaryView* data)
+{
+ // Always return false here.
+ // This view pretty much exists to keep a bunch of internal core logic happy as it expects certain things
+ // from our view's parent that we need to control, and cannot control on a standard raw view.
+
+ // IIRC an example of this was the need to add more data past the end of the original file.
+
+ // in ios16 caches, the primary file can be like 100kb while a standard image will easily break 1MB
+
+ // I actually should check if the stuff related to non-file-backed-segments changes the need for this,
+ // but at the time it was created (2022) it was necessary.
+ return false;
+}
+
+
+DSCView::DSCView(const std::string& typeName, BinaryView* data, bool parseOnly) :
+ BinaryView(typeName, data->GetFile(), data), m_parseOnly(parseOnly)
+{
+ CreateLogger("SharedCache");
+ CreateLogger("SharedCache.ObjC");
+}
+
+DSCView::~DSCView()
+{
+ if (!m_parseOnly)
+ MMappedFileAccessor::CloseAll(GetFile()->GetSessionId());
+}
+
+enum DSCPlatform {
+ DSCPlatformMacOS = 1,
+ DSCPlatformiOS = 2,
+};
+
+bool DSCView::Init()
+{
+ uint32_t platform;
+ GetParentView()->Read(&platform, 0xd8, 4);
+ char magic[17];
+ GetParentView()->Read(&magic, 0, 16);
+ magic[16] = 0;
+ std::string os;
+ if (platform == DSCPlatformMacOS)
+ {
+ os = "mac";
+ }
+ else if (platform == DSCPlatformiOS)
+ {
+ os = "ios";
+ }
+ else
+ {
+ LogError("Unknown platform: %d", platform);
+ return false;
+ }
+ if (std::string(magic) == "dyld_v1 arm64" || std::string(magic) == "dyld_v1 arm64e")
+ {
+ SetDefaultPlatform(Platform::GetByName(os + "-aarch64"));
+ SetDefaultArchitecture(Architecture::GetByName("aarch64"));
+ }
+ else if (std::string(magic) == "dyld_v1 x86_64")
+ {
+ SetDefaultPlatform(Platform::GetByName(os + "-x86_64"));
+ SetDefaultArchitecture(Architecture::GetByName("x86_64"));
+ }
+ else
+ {
+ LogError("Unknown magic: %s", magic);
+ return false;
+ }
+ QualifiedNameAndType headerType;
+ std::string err;
+
+ ParseTypeString("\n"
+ "\tstruct dyld_cache_header\n"
+ "\t{\n"
+ "\t\tchar magic[16];\t\t\t\t\t // e.g. \"dyld_v0 i386\"\n"
+ "\t\tuint32_t mappingOffset;\t\t\t // file offset to first dyld_cache_mapping_info\n"
+ "\t\tuint32_t mappingCount;\t\t\t // number of dyld_cache_mapping_info entries\n"
+ "\t\tuint32_t imagesOffsetOld;\t\t // UNUSED: moved to imagesOffset to prevent older dsc_extarctors from crashing\n"
+ "\t\tuint32_t imagesCountOld;\t\t // UNUSED: moved to imagesCount to prevent older dsc_extarctors from crashing\n"
+ "\t\tuint64_t dyldBaseAddress;\t\t // base address of dyld when cache was built\n"
+ "\t\tuint64_t codeSignatureOffset;\t // file offset of code signature blob\n"
+ "\t\tuint64_t codeSignatureSize;\t\t // size of code signature blob (zero means to end of file)\n"
+ "\t\tuint64_t slideInfoOffsetUnused;\t // unused. Used to be file offset of kernel slid info\n"
+ "\t\tuint64_t slideInfoSizeUnused;\t // unused. Used to be size of kernel slid info\n"
+ "\t\tuint64_t localSymbolsOffset;\t // file offset of where local symbols are stored\n"
+ "\t\tuint64_t localSymbolsSize;\t\t // size of local symbols information\n"
+ "\t\tuint8_t uuid[16];\t\t\t\t // unique value for each shared cache file\n"
+ "\t\tuint64_t cacheType;\t\t\t\t // 0 for development, 1 for production // Kat: , 2 for iOS 16?\n"
+ "\t\tuint32_t branchPoolsOffset;\t\t // file offset to table of uint64_t pool addresses\n"
+ "\t\tuint32_t branchPoolsCount;\t\t // number of uint64_t entries\n"
+ "\t\tuint64_t accelerateInfoAddr;\t // (unslid) address of optimization info\n"
+ "\t\tuint64_t accelerateInfoSize;\t // size of optimization info\n"
+ "\t\tuint64_t imagesTextOffset;\t\t // file offset to first dyld_cache_image_text_info\n"
+ "\t\tuint64_t imagesTextCount;\t\t // number of dyld_cache_image_text_info entries\n"
+ "\t\tuint64_t patchInfoAddr;\t\t\t // (unslid) address of dyld_cache_patch_info\n"
+ "\t\tuint64_t patchInfoSize;\t // Size of all of the patch information pointed to via the dyld_cache_patch_info\n"
+ "\t\tuint64_t otherImageGroupAddrUnused;\t // unused\n"
+ "\t\tuint64_t otherImageGroupSizeUnused;\t // unused\n"
+ "\t\tuint64_t progClosuresAddr;\t\t\t // (unslid) address of list of program launch closures\n"
+ "\t\tuint64_t progClosuresSize;\t\t\t // size of list of program launch closures\n"
+ "\t\tuint64_t progClosuresTrieAddr;\t\t // (unslid) address of trie of indexes into program launch closures\n"
+ "\t\tuint64_t progClosuresTrieSize;\t\t // size of trie of indexes into program launch closures\n"
+ "\t\tuint32_t platform;\t\t\t\t\t // platform number (macOS=1, etc)\n"
+ "\t\tuint32_t formatVersion : 8,\t\t\t // dyld3::closure::kFormatVersion\n"
+ "\t\t\tdylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid\n"
+ "\t\t\tsimulator : 1,\t\t\t // for simulator of specified platform\n"
+ "\t\t\tlocallyBuiltCache : 1,\t // 0 for B&I built cache, 1 for locally built cache\n"
+ "\t\t\tbuiltFromChainedFixups : 1,\t // some dylib in cache was built using chained fixups, so patch tables must be used for overrides\n"
+ "\t\t\tpadding : 20;\t\t\t\t // TBD\n"
+ "\t\tuint64_t sharedRegionStart;\t\t // base load address of cache if not slid\n"
+ "\t\tuint64_t sharedRegionSize;\t\t // overall size required to map the cache and all subCaches, if any\n"
+ "\t\tuint64_t maxSlide;\t\t\t\t // runtime slide of cache can be between zero and this value\n"
+ "\t\tuint64_t dylibsImageArrayAddr;\t // (unslid) address of ImageArray for dylibs in this cache\n"
+ "\t\tuint64_t dylibsImageArraySize;\t // size of ImageArray for dylibs in this cache\n"
+ "\t\tuint64_t dylibsTrieAddr;\t\t // (unslid) address of trie of indexes of all cached dylibs\n"
+ "\t\tuint64_t dylibsTrieSize;\t\t // size of trie of cached dylib paths\n"
+ "\t\tuint64_t otherImageArrayAddr;\t // (unslid) address of ImageArray for dylibs and bundles with dlopen closures\n"
+ "\t\tuint64_t otherImageArraySize;\t // size of ImageArray for dylibs and bundles with dlopen closures\n"
+ "\t\tuint64_t otherTrieAddr;\t // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures\n"
+ "\t\tuint64_t otherTrieSize;\t // size of trie of dylibs and bundles with dlopen closures\n"
+ "\t\tuint32_t mappingWithSlideOffset;\t\t // file offset to first dyld_cache_mapping_and_slide_info\n"
+ "\t\tuint32_t mappingWithSlideCount;\t\t\t // number of dyld_cache_mapping_and_slide_info entries\n"
+ "\t\tuint64_t dylibsPBLStateArrayAddrUnused;\t // unused\n"
+ "\t\tuint64_t dylibsPBLSetAddr;\t\t\t\t // (unslid) address of PrebuiltLoaderSet of all cached dylibs\n"
+ "\t\tuint64_t programsPBLSetPoolAddr;\t\t // (unslid) address of pool of PrebuiltLoaderSet for each program\n"
+ "\t\tuint64_t programsPBLSetPoolSize;\t\t // size of pool of PrebuiltLoaderSet for each program\n"
+ "\t\tuint64_t programTrieAddr;\t\t\t\t // (unslid) address of trie mapping program path to PrebuiltLoaderSet\n"
+ "\t\tuint32_t programTrieSize;\n"
+ "\t\tuint32_t osVersion;\t\t\t\t// OS Version of dylibs in this cache for the main platform\n"
+ "\t\tuint32_t altPlatform;\t\t\t// e.g. iOSMac on macOS\n"
+ "\t\tuint32_t altOsVersion;\t\t\t// e.g. 14.0 for iOSMac\n"
+ "\t\tuint64_t swiftOptsOffset;\t\t// file offset to Swift optimizations header\n"
+ "\t\tuint64_t swiftOptsSize;\t\t\t// size of Swift optimizations header\n"
+ "\t\tuint32_t subCacheArrayOffset;\t// file offset to first dyld_subcache_entry\n"
+ "\t\tuint32_t subCacheArrayCount;\t// number of subCache entries\n"
+ "\t\tuint8_t symbolFileUUID[16];\t\t// unique value for the shared cache file containing unmapped local symbols\n"
+ "\t\tuint64_t rosettaReadOnlyAddr;\t// (unslid) address of the start of where Rosetta can add read-only/executable data\n"
+ "\t\tuint64_t rosettaReadOnlySize;\t// maximum size of the Rosetta read-only/executable region\n"
+ "\t\tuint64_t rosettaReadWriteAddr;\t// (unslid) address of the start of where Rosetta can add read-write data\n"
+ "\t\tuint64_t rosettaReadWriteSize;\t// maximum size of the Rosetta read-write region\n"
+ "\t\tuint32_t imagesOffset;\t\t\t// file offset to first dyld_cache_image_info\n"
+ "\t\tuint32_t imagesCount;\t\t\t// number of dyld_cache_image_info entries\n"
+ "\t};", headerType, err);
+
+ Ref<Settings> settings = GetLoadSettings(GetTypeName());
+
+ if (!settings)
+ {
+ Ref<Settings> programSettings = Settings::Instance();
+ programSettings->Set("workflows.enable", true, this);
+ programSettings->Set("workflows.functionWorkflow", "core.function.dsc", this);
+ }
+
+ // 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);
+ std::string cpuTypeEnumName = "cpu_type_t";
+ std::string cpuTypeEnumId = Type::GenerateAutoTypeId("macho", cpuTypeEnumName);
+ 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);
+ std::string fileTypeEnumName = "file_type_t";
+ std::string fileTypeEnumId = Type::GenerateAutoTypeId("macho", fileTypeEnumName);
+ 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);
+ std::string flagsTypeEnumName = "flags_type_t";
+ std::string flagsTypeEnumId = Type::GenerateAutoTypeId("macho", flagsTypeEnumName);
+ DefineType(flagsTypeEnumId, flagsTypeEnumName, flagsTypeEnumType);
+
+ StructureBuilder machoHeaderBuilder;
+ machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "magic");
+ machoHeaderBuilder.AddMember(Type::NamedType(this, QualifiedName("cpu_type_t")), "cputype");
+ machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "cpusubtype");
+ machoHeaderBuilder.AddMember(Type::NamedType(this, QualifiedName("file_type_t")), "filetype");
+ machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "ncmds");
+ machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "sizeofcmds");
+ machoHeaderBuilder.AddMember(Type::NamedType(this, QualifiedName("flags_type_t")), "flags");
+ if (GetAddressSize() == 8)
+ machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "reserved");
+ Ref<Structure> machoHeaderStruct = machoHeaderBuilder.Finalize();
+ QualifiedName headerName = (GetAddressSize() == 8) ? std::string("mach_header_64") : std::string("mach_header");
+
+ std::string headerTypeId = Type::GenerateAutoTypeId("macho", headerName);
+ Ref<Type> machoHeaderType = Type::StructureType(machoHeaderStruct);
+ 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);
+ std::string cmdTypeEnumName = "load_command_type_t";
+ std::string cmdTypeEnumId = Type::GenerateAutoTypeId("macho", cmdTypeEnumName);
+ DefineType(cmdTypeEnumId, cmdTypeEnumName, cmdTypeEnumType);
+
+ StructureBuilder loadCommandBuilder;
+ loadCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd");
+ loadCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize");
+ Ref<Structure> loadCommandStruct = loadCommandBuilder.Finalize();
+ QualifiedName loadCommandName = std::string("load_command");
+ std::string loadCommandTypeId = Type::GenerateAutoTypeId("macho", loadCommandName);
+ Ref<Type> loadCommandType = Type::StructureType(loadCommandStruct);
+ 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);
+ std::string protTypeEnumName = "vm_prot_t";
+ std::string protTypeEnumId = Type::GenerateAutoTypeId("macho", protTypeEnumName);
+ 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);
+ std::string segFlagsTypeEnumName = "sg_flags_t";
+ std::string segFlagsTypeEnumId = Type::GenerateAutoTypeId("macho", segFlagsTypeEnumName);
+ DefineType(segFlagsTypeEnumId, segFlagsTypeEnumName, segFlagsTypeEnumType);
+
+ StructureBuilder loadSegmentCommandBuilder;
+ loadSegmentCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "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, QualifiedName("vm_prot_t")), "maxprot");
+ loadSegmentCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("vm_prot_t")), "initprot");
+ loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "nsects");
+ loadSegmentCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("sg_flags_t")), "flags");
+ Ref<Structure> loadSegmentCommandStruct = loadSegmentCommandBuilder.Finalize();
+ QualifiedName loadSegmentCommandName = std::string("segment_command");
+ std::string loadSegmentCommandTypeId = Type::GenerateAutoTypeId("macho", loadSegmentCommandName);
+ Ref<Type> loadSegmentCommandType = Type::StructureType(loadSegmentCommandStruct);
+ DefineType(loadSegmentCommandTypeId, loadSegmentCommandName, loadSegmentCommandType);
+
+ StructureBuilder loadSegmentCommand64Builder;
+ loadSegmentCommand64Builder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "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, QualifiedName("vm_prot_t")), "maxprot");
+ loadSegmentCommand64Builder.AddMember(Type::NamedType(this, QualifiedName("vm_prot_t")), "initprot");
+ loadSegmentCommand64Builder.AddMember(Type::IntegerType(4, false), "nsects");
+ loadSegmentCommand64Builder.AddMember(Type::NamedType(this, QualifiedName("sg_flags_t")), "flags");
+ Ref<Structure> loadSegmentCommand64Struct = loadSegmentCommand64Builder.Finalize();
+ QualifiedName loadSegment64CommandName = std::string("segment_command_64");
+ std::string loadSegment64CommandTypeId = Type::GenerateAutoTypeId("macho", loadSegment64CommandName);
+ Ref<Type> loadSegment64CommandType = Type::StructureType(loadSegmentCommand64Struct);
+ 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 = std::string("section");
+ std::string sectionTypeId = Type::GenerateAutoTypeId("macho", sectionName);
+ Ref<Type> sectionType = Type::StructureType(sectionStruct);
+ 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 = std::string("section_64");
+ std::string section64TypeId = Type::GenerateAutoTypeId("macho", section64Name);
+ Ref<Type> section64Type = Type::StructureType(section64Struct);
+ DefineType(section64TypeId, section64Name, section64Type);
+
+ StructureBuilder symtabBuilder;
+ symtabBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "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 = std::string("symtab");
+ std::string symtabTypeId = Type::GenerateAutoTypeId("macho", symtabName);
+ Ref<Type> symtabType = Type::StructureType(symtabStruct);
+ DefineType(symtabTypeId, symtabName, symtabType);
+
+ StructureBuilder dynsymtabBuilder;
+ dynsymtabBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "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 = std::string("dynsymtab");
+ std::string dynsymtabTypeId = Type::GenerateAutoTypeId("macho", dynsymtabName);
+ Ref<Type> dynsymtabType = Type::StructureType(dynsymtabStruct);
+ DefineType(dynsymtabTypeId, dynsymtabName, dynsymtabType);
+
+ StructureBuilder uuidBuilder;
+ uuidBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "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 = std::string("uuid");
+ std::string uuidTypeId = Type::GenerateAutoTypeId("macho", uuidName);
+ Ref<Type> uuidType = Type::StructureType(uuidStruct);
+ DefineType(uuidTypeId, uuidName, uuidType);
+
+ StructureBuilder linkeditDataBuilder;
+ linkeditDataBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "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 = std::string("linkedit_data");
+ std::string linkeditDataTypeId = Type::GenerateAutoTypeId("macho", linkeditDataName);
+ Ref<Type> linkeditDataType = Type::StructureType(linkeditDataStruct);
+ DefineType(linkeditDataTypeId, linkeditDataName, linkeditDataType);
+
+ StructureBuilder encryptionInfoBuilder;
+ encryptionInfoBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "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 = std::string("encryption_info");
+ std::string encryptionInfoTypeId = Type::GenerateAutoTypeId("macho", encryptionInfoName);
+ Ref<Type> encryptionInfoType = Type::StructureType(encryptionInfoStruct);
+ DefineType(encryptionInfoTypeId, encryptionInfoName, encryptionInfoType);
+
+ StructureBuilder versionMinBuilder;
+ versionMinBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "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 = std::string("version_min");
+ std::string versionMinTypeId = Type::GenerateAutoTypeId("macho", versionMinName);
+ Ref<Type> versionMinType = Type::StructureType(versionMinStruct);
+ DefineType(versionMinTypeId, versionMinName, versionMinType);
+
+ StructureBuilder dyldInfoBuilder;
+ dyldInfoBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "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 = std::string("dyld_info");
+ std::string dyldInfoTypeId = Type::GenerateAutoTypeId("macho", dyldInfoName);
+ Ref<Type> dyldInfoType = Type::StructureType(dyldInfoStruct);
+ 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 = std::string("dylib");
+ std::string dylibTypeId = Type::GenerateAutoTypeId("macho", dylibName);
+ Ref<Type> dylibType = Type::StructureType(dylibStruct);
+ DefineType(dylibTypeId, dylibName, dylibType);
+
+ StructureBuilder dylibCommandBuilder;
+ dylibCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd");
+ dylibCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize");
+ dylibCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("dylib")), "dylib");
+ Ref<Structure> dylibCommandStruct = dylibCommandBuilder.Finalize();
+ QualifiedName dylibCommandName = std::string("dylib_command");
+ std::string dylibCommandTypeId = Type::GenerateAutoTypeId("macho", dylibCommandName);
+ Ref<Type> dylibCommandType = Type::StructureType(dylibCommandStruct);
+ DefineType(dylibCommandTypeId, dylibCommandName, dylibCommandType);
+
+ StructureBuilder filesetEntryCommandBuilder;
+ filesetEntryCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "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 = std::string("fileset_entry_command");
+ std::string filesetEntryCommandTypeId = Type::GenerateAutoTypeId("macho", filesetEntryCommandName);
+ Ref<Type> filesetEntryCommandType = Type::StructureType(filesetEntryCommandStruct);
+ DefineType(filesetEntryCommandTypeId, filesetEntryCommandName, filesetEntryCommandType);
+
+ std::vector<SharedCacheCore::MemoryRegion> regionsMappedIntoMemory;
+ if (auto meta = GetParentView()->GetParentView()->QueryMetadata(SharedCacheCore::SharedCacheMetadataTag))
+ {
+ std::string data = GetParentView()->GetParentView()->GetStringMetadata(SharedCacheCore::SharedCacheMetadataTag);
+ std::stringstream ss;
+ ss.str(data);
+ rapidjson::Document result(rapidjson::kObjectType);
+
+ result.Parse(data.c_str());
+ for (auto& imgV : result["regionsMappedIntoMemory"].GetArray())
+ {
+ SharedCacheCore::MemoryRegion region;
+ region.LoadFromValue(imgV);
+ regionsMappedIntoMemory.push_back(region);
+ }
+
+ std::unordered_map<uint64_t, std::string> imageStartToInstallName;
+ // key "m_imageStarts"
+ for (auto& imgV : result["m_imageStarts"].GetArray())
+ {
+ std::string name = imgV.GetArray()[0].GetString();
+ uint64_t addr = imgV.GetArray()[1].GetUint64();
+ imageStartToInstallName[addr] = name;
+ }
+
+ std::vector<std::pair<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>>> exportInfos;
+ for (auto& exportInfo : result["exportInfos"].GetArray())
+ {
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> exportInfoVec;
+ for (auto& exportInfoPair : exportInfo.GetArray())
+ {
+ exportInfoVec.push_back({exportInfoPair[0].GetUint64(), { (BNSymbolType)exportInfoPair[1].GetUint(), exportInfoPair[2].GetString()}});
+ }
+ exportInfos.push_back({exportInfo[0].GetUint64(), exportInfoVec});
+ }
+
+ std::vector<std::pair<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>>> symbolInfos;
+ for (auto& symbolInfo : result["symbolInfos"].GetArray())
+ {
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> symbolInfoVec;
+ for (auto& symbolInfoPair : symbolInfo.GetArray())
+ {
+ symbolInfoVec.push_back({symbolInfoPair[0].GetUint64(), { (BNSymbolType)symbolInfoPair[1].GetUint(), symbolInfoPair[2].GetString()}});
+ }
+ symbolInfos.push_back({symbolInfo[0].GetUint64(), symbolInfoVec});
+ }
+
+ // We need to re-map data located in the Raw (parent parent) viewtype to the DSCRaw (parent) viewtype.
+ for (auto region : regionsMappedIntoMemory)
+ {
+ GetParentView()->AddUserSegment(
+ region.rawViewOffsetIfLoaded, region.size, region.rawViewOffsetIfLoaded, region.size, region.flags);
+ GetParentView()->WriteBuffer(
+ region.rawViewOffsetIfLoaded, GetParentView()->GetParentView()->ReadBuffer(region.rawViewOffsetIfLoaded, region.size));
+ }
+
+ BeginBulkModifySymbols();
+ for (const auto & [imageBaseAddr, symbolList] : symbolInfos)
+ {
+ std::vector<Ref<Symbol>> symbolsList;
+ for (const auto & [symAddr, symTypeAndName] : symbolList)
+ {
+ symbolsList.push_back(new Symbol(symTypeAndName.first, symTypeAndName.second, symAddr));
+ }
+
+ auto typelib = GetTypeLibrary(imageStartToInstallName[imageBaseAddr]);
+
+ for (const auto& symbol : symbolsList)
+ {
+ if (typelib)
+ {
+ auto type = typelib->GetNamedObject(symbol->GetRawName());
+ if (type)
+ {
+ DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), symbol, type);
+ continue;
+ }
+ }
+ DefineAutoSymbol(symbol);
+ }
+ }
+
+ for (const auto & [imageBaseAddr, exportList] : exportInfos)
+ {
+ std::vector<Ref<Symbol>> symbolsList;
+ for (const auto & [exportAddr, exportTypeAndName] : exportList)
+ {
+ symbolsList.push_back(new Symbol(exportTypeAndName.first, exportTypeAndName.second, exportAddr));
+ }
+
+ auto typelib = GetTypeLibrary(imageStartToInstallName[imageBaseAddr]);
+
+ for (const auto& symbol : symbolsList)
+ {
+ if (typelib)
+ {
+ auto type = typelib->GetNamedObject(symbol->GetRawName());
+ if (type)
+ {
+ DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), symbol, type);
+ continue;
+ }
+ }
+ DefineAutoSymbol(symbol);
+ }
+ }
+ EndBulkModifySymbols();
+ }
+
+ // uint32_t at 0x10 is offset to mapping table.
+ // first mapping struct in that table is base of primary
+ // first uint64_t in that struct is the base address of the primary
+ // double gpv here because DSCRaw explicitly stops at the start of this mapping table
+ uint64_t basePointer = 0;
+ GetParentView()->GetParentView()->Read(&basePointer, 16, 4);
+ if (basePointer == 0)
+ {
+ LogError("Failed to read base pointer");
+ return false;
+ }
+ uint64_t primaryBase = 0;
+ GetParentView()->GetParentView()->Read(&primaryBase, basePointer, 8);
+ if (primaryBase == 0)
+ {
+ LogError("Failed to read primary base at 0x%llx", basePointer);
+ return false;
+ }
+
+ AddAutoSegment(primaryBase, 0x200, 0, 0x200, SegmentReadable);
+ DefineType("dyld_cache_header", headerType.name, headerType.type);
+ DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), new Symbol(DataSymbol, "primary_cache_header", primaryBase), headerType.type);
+
+ return true;
+}
+
+
+DSCViewType::DSCViewType() : BinaryViewType(VIEW_NAME, VIEW_NAME) {}
+
+BinaryNinja::Ref<BinaryNinja::BinaryView> DSCViewType::Create(BinaryNinja::BinaryView* data)
+{
+ return new DSCView(VIEW_NAME, new DSCRawView("DSCRawView", data, false), false);
+}
+
+
+Ref<Settings> DSCViewType::GetLoadSettingsForData(BinaryView* data)
+{
+ Ref<BinaryView> viewRef = Parse(data);
+ if (!viewRef || !viewRef->Init())
+ {
+ LogError("View type '%s' could not be created", GetName().c_str());
+ return nullptr;
+ }
+
+ Ref<Settings> settings = GetDefaultLoadSettingsForData(viewRef);
+
+ // specify default load settings that can be overridden
+ std::vector<std::string> overrides = {"loader.imageBase", "loader.platform"};
+ 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);
+ }
+
+ Ref<Settings> programSettings = Settings::Instance();
+ programSettings->Set("workflows.functionWorkflow", "core.function.dsc", viewRef);
+
+ settings->RegisterSetting("loader.dsc.processCFStrings",
+ R"({
+ "title" : "Process CFString Metadata",
+ "type" : "boolean",
+ "default" : true,
+ "description" : "Processes CoreFoundation strings, applying string values from encoded metadata"
+ })");
+
+ settings->RegisterSetting("loader.dsc.processObjC",
+ R"({
+ "title" : "Process Objective-C Metadata",
+ "type" : "boolean",
+ "default" : true,
+ "description" : "Processes Objective-C metadata, applying class and method names from encoded metadata"
+ })");
+
+ settings->RegisterSetting("loader.dsc.autoLoadObjCStubRequirements",
+ R"({
+ "title" : "Auto-Load Objective-C Stub Requirements",
+ "type" : "boolean",
+ "default" : true,
+ "description" : "Automatically loads segments required for inlining Objective-C stubs. Recommended you keep this on."
+ })");
+
+ settings->RegisterSetting("loader.dsc.autoLoadStubsAndDyldData",
+ R"({
+ "title" : "Auto-Load Stub Islands",
+ "type" : "boolean",
+ "default" : true,
+ "description" : "Automatically loads stub and dylddata regions that contain just branches and pointers. These are required for resolving stub names, and performance impact is minimal. Recommended you keep this on."
+ })");
+
+ settings->RegisterSetting("loader.dsc.allowLoadingLinkeditSegments",
+ R"({
+ "title" : "Allow Loading __LINKEDIT Segments",
+ "type" : "boolean",
+ "default" : false,
+ "description" : "Allow mapping __LINKEDIT segments. These are large regions of symbol data that are automatically processed by BinaryNinja without the need for mapping. On newer caches, __LINKEDIT for all images may end up merged and be >300MB in size. This will likely cause severe performance degradation with _zero_ benefit."
+ })");
+
+ settings->RegisterSetting("loader.dsc.processFunctionStarts",
+ R"({
+ "title" : "Process Mach-O Function Starts Tables",
+ "type" : "boolean",
+ "default" : true,
+ "description" : "Add function starts sourced from the Function Starts tables to the core for analysis."
+ })");
+
+ // 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;
+}
+
+
+BinaryNinja::Ref<BinaryNinja::BinaryView> DSCViewType::Parse(BinaryNinja::BinaryView* data)
+{
+ return new DSCView(VIEW_NAME, new DSCRawView("DSCRawView", data, true), true);
+}
+
+bool DSCViewType::IsTypeValidForData(BinaryNinja::BinaryView* data)
+{
+ if (!data)
+ return false;
+
+ DataBuffer sig = data->ReadBuffer(data->GetStart(), 4);
+ if (sig.GetLength() != 4)
+ return false;
+
+ const char* magic = (char*)sig.GetData();
+ if (strncmp(magic, "dyld", 4) == 0)
+ return true;
+
+ return false;
+}
diff --git a/view/sharedcache/core/DSCView.h b/view/sharedcache/core/DSCView.h
new file mode 100644
index 00000000..1b891e36
--- /dev/null
+++ b/view/sharedcache/core/DSCView.h
@@ -0,0 +1,65 @@
+//
+// Created by kat on 5/23/23.
+//
+
+#ifndef SHAREDCACHE_DSCVIEW_H
+#define SHAREDCACHE_DSCVIEW_H
+
+#include <binaryninjaapi.h>
+
+class DSCRawView : public BinaryNinja::BinaryView {
+ std::string m_filename;
+public:
+
+ DSCRawView(const std::string &typeName, BinaryView *data, bool parseOnly = false);
+
+ bool Init() override;
+};
+
+
+class DSCRawViewType : public BinaryNinja::BinaryViewType {
+
+public:
+ BinaryNinja::Ref<BinaryNinja::BinaryView> Create(BinaryNinja::BinaryView* data) override;
+ BinaryNinja::Ref<BinaryNinja::BinaryView> Parse(BinaryNinja::BinaryView* data) override;
+ bool IsTypeValidForData(BinaryNinja::BinaryView *data) override;
+
+ bool IsDeprecated() override { return false; }
+
+ BinaryNinja::Ref<BinaryNinja::Settings> GetLoadSettingsForData(BinaryNinja::BinaryView *data) override { return nullptr; }
+
+public:
+ DSCRawViewType();
+};
+
+
+class DSCView : public BinaryNinja::BinaryView {
+ bool m_parseOnly;
+public:
+
+ DSCView(const std::string &typeName, BinaryView *data, bool parseOnly = false);
+
+ ~DSCView() override;
+
+ bool Init() override;
+};
+
+
+class DSCViewType : public BinaryNinja::BinaryViewType {
+
+public:
+ DSCViewType();
+
+ BinaryNinja::Ref<BinaryNinja::BinaryView> Create(BinaryNinja::BinaryView *data) override;
+
+ BinaryNinja::Ref<BinaryNinja::BinaryView> Parse(BinaryNinja::BinaryView *data) override;
+
+ bool IsTypeValidForData(BinaryNinja::BinaryView *data) override;
+
+ bool IsDeprecated() override { return false; }
+
+ BinaryNinja::Ref<BinaryNinja::Settings> GetLoadSettingsForData(BinaryNinja::BinaryView *data) override;
+};
+
+
+#endif //SHAREDCACHE_DSCVIEW_H
diff --git a/view/sharedcache/core/MetadataSerializable.hpp b/view/sharedcache/core/MetadataSerializable.hpp
new file mode 100644
index 00000000..c49a9e8c
--- /dev/null
+++ b/view/sharedcache/core/MetadataSerializable.hpp
@@ -0,0 +1,537 @@
+//
+// Created by kat on 5/31/23.
+//
+
+/*
+ * Welcome to, this file.
+ *
+ * This is a metadata serialization helper.
+ *
+ * Have you ever wished turning a complex datastructure into a Metadata object was as easy in C++ as it is in python?
+ * Do you like macros and templates?
+ *
+ * Great news.
+ *
+ * Implement these on your `public MetadataSerializable` subclass:
+ * ```
+ void Store() override {
+ MSS(m_someVariable);
+ MSS(m_someOtherVariable);
+ }
+ void Load() override {
+ MSL(m_someVariable);
+ MSL(m_someOtherVariable);
+ }
+ ```
+ * Then, you can turn your object into a Metadata object with `AsMetadata()`, and load it back with
+ `LoadFromMetadata()`.
+ *
+ * Serialized fields will be automatically repopulated.
+ *
+ * Other ser/deser formats (rapidjson objects, strings) also exist. You can use these to achieve nesting, but probably
+ avoid that.
+ * */
+
+#include "binaryninjaapi.h"
+#include "rapidjson/document.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/prettywriter.h"
+
+#ifndef SHAREDCACHE_METADATASERIALIZABLE_HPP
+#define SHAREDCACHE_METADATASERIALIZABLE_HPP
+
+#define MSS(name) store(#name, name)
+#define MSS_CAST(name, type) store(#name, (type) name)
+#define MSS_SUBCLASS(name) Serialize(#name, name)
+#define MSL(name) name = load(#name, name)
+#define MSL_CAST(name, storedType, type) name = (type)load(#name, (storedType) name)
+#define MSL_SUBCLASS(name) Deserialize(#name, name)
+
+using namespace BinaryNinja;
+
+class MetadataSerializable
+{
+protected:
+ struct SerialContext
+ {
+ rapidjson::Document doc;
+ rapidjson::Document::AllocatorType allocator;
+ };
+ struct DeserContext
+ {
+ rapidjson::Document doc;
+ };
+
+ DeserContext m_activeDeserContext;
+ SerialContext m_activeContext;
+
+public:
+ MetadataSerializable()
+ {
+ m_activeContext.doc.SetObject();
+ m_activeContext.allocator = m_activeContext.doc.GetAllocator();
+ }
+
+ // copy constructor
+ MetadataSerializable(const MetadataSerializable& other)
+ {
+ m_activeContext.doc.CopyFrom(other.m_activeContext.doc, m_activeContext.doc.GetAllocator());
+ }
+
+ // copy assignment
+ MetadataSerializable& operator=(const MetadataSerializable& other)
+ {
+ m_activeContext.doc.CopyFrom(other.m_activeContext.doc, m_activeContext.doc.GetAllocator());
+ return *this;
+ }
+
+ virtual ~MetadataSerializable()
+ {
+ }
+
+ void SetupSerContext(rapidjson::Document::AllocatorType* alloc = nullptr)
+ {
+ m_activeContext.doc.SetObject();
+ m_activeContext.allocator = m_activeContext.doc.GetAllocator();
+ }
+ void S()
+ {
+ // fixme factor out
+ }
+ void Serialize(std::string& name, bool b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, b, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, bool& b) { b = m_activeDeserContext.doc[name.c_str()].GetBool(); }
+
+ void Serialize(std::string& name, uint8_t b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, b, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, uint8_t& b)
+ {
+ b = static_cast<uint8_t>(m_activeDeserContext.doc[name.c_str()].GetUint64());
+ }
+
+ void Serialize(std::string& name, uint16_t b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, b, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, uint16_t& b)
+ {
+ b = static_cast<uint16_t>(m_activeDeserContext.doc[name.c_str()].GetUint64());
+ }
+
+ void Serialize(std::string& name, uint32_t b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, b, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, uint32_t& b)
+ {
+ b = static_cast<uint32_t>(m_activeDeserContext.doc[name.c_str()].GetUint64());
+ }
+
+ void Serialize(std::string& name, uint64_t b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, b, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, uint64_t& b)
+ {
+ b = m_activeDeserContext.doc[name.c_str()].GetUint64();
+ }
+
+ void Serialize(std::string& name, int8_t b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, b, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, int8_t& b)
+ {
+ b = m_activeDeserContext.doc[name.c_str()].GetInt64();
+ }
+
+ void Serialize(std::string& name, int16_t b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, b, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, int16_t& b)
+ {
+ b = m_activeDeserContext.doc[name.c_str()].GetInt64();
+ }
+
+ void Serialize(std::string& name, int32_t b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, b, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, int32_t& b)
+ {
+ b = m_activeDeserContext.doc[name.c_str()].GetInt();
+ }
+
+ void Serialize(std::string& name, int64_t b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, b, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, int64_t& b)
+ {
+ b = m_activeDeserContext.doc[name.c_str()].GetInt64();
+ }
+
+ void Serialize(std::string& name, std::string b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value value(b.c_str(), m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, value, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, std::string& b)
+ {
+ b = m_activeDeserContext.doc[name.c_str()].GetString();
+ }
+
+ void Serialize(std::string& name, std::map<uint64_t, std::string> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& i : b)
+ {
+ rapidjson::Value p(rapidjson::kArrayType);
+ p.PushBack(i.first, m_activeContext.allocator);
+ rapidjson::Value value(i.second.c_str(), m_activeContext.allocator);
+ p.PushBack(value, m_activeContext.allocator);
+ bArr.PushBack(p, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, std::map<uint64_t, std::string>& b)
+ {
+ for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray())
+ b[i.GetArray()[0].GetUint64()] = i.GetArray()[1].GetString();
+ }
+
+ void Serialize(std::string& name, std::unordered_map<uint64_t, std::string> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& i : b)
+ {
+ rapidjson::Value p(rapidjson::kArrayType);
+ p.PushBack(i.first, m_activeContext.allocator);
+ rapidjson::Value value(i.second.c_str(), m_activeContext.allocator);
+ p.PushBack(value, m_activeContext.allocator);
+ bArr.PushBack(p, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Serialize(std::string& name, std::unordered_map<std::string, std::string> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& i : b)
+ {
+ rapidjson::Value p(rapidjson::kArrayType);
+ rapidjson::Value _key(i.first.c_str(), m_activeContext.allocator);
+ rapidjson::Value value(i.second.c_str(), m_activeContext.allocator);
+ p.PushBack(_key, m_activeContext.allocator);
+ p.PushBack(value, m_activeContext.allocator);
+ bArr.PushBack(p, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, std::unordered_map<uint64_t, std::string>& b)
+ {
+ for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray())
+ b[i.GetArray()[0].GetUint64()] = i.GetArray()[1].GetString();
+ }
+
+ void Serialize(std::string& name, std::unordered_map<uint64_t, uint64_t> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& i : b)
+ {
+ rapidjson::Value p(rapidjson::kArrayType);
+ p.PushBack(i.first, m_activeContext.allocator);
+ p.PushBack(i.second, m_activeContext.allocator);
+ bArr.PushBack(p, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, std::unordered_map<uint64_t, uint64_t>& b)
+ {
+ for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray())
+ b[i.GetArray()[0].GetUint64()] = i.GetArray()[1].GetUint64();
+ }
+
+ // std::unordered_map<std::string, std::unordered_map<uint64_t, uint64_t>>
+ void Serialize(std::string& name, std::unordered_map<std::string, std::unordered_map<uint64_t, uint64_t>> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value classes(rapidjson::kArrayType);
+ for (auto& i : b)
+ {
+ rapidjson::Value classArr(rapidjson::kArrayType);
+ rapidjson::Value classKey(i.first.c_str(), m_activeContext.allocator);
+ classArr.PushBack(classKey, m_activeContext.allocator);
+ rapidjson::Value membersArr(rapidjson::kArrayType);
+ for (auto& j : i.second)
+ {
+ rapidjson::Value member(rapidjson::kArrayType);
+ member.PushBack(j.first, m_activeContext.allocator);
+ member.PushBack(j.second, m_activeContext.allocator);
+ membersArr.PushBack(member, m_activeContext.allocator);
+ }
+ classArr.PushBack(membersArr, m_activeContext.allocator);
+ classes.PushBack(classArr, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, classes, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, std::unordered_map<std::string, std::unordered_map<uint64_t, uint64_t>>& b)
+ {
+ for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray())
+ {
+ std::string key = i.GetArray()[0].GetString();
+ std::unordered_map<uint64_t, uint64_t> memArray;
+ for (auto& member : i.GetArray()[1].GetArray())
+ {
+ memArray[member.GetArray()[0].GetUint64()] = member.GetArray()[1].GetUint64();
+ }
+ b[key] = memArray;
+ }
+ }
+
+ void Deserialize(std::string& name, std::unordered_map<std::string, std::string>& b)
+ {
+ for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray())
+ b[i.GetArray()[0].GetString()] = i.GetArray()[1].GetString();
+ }
+
+ void Serialize(std::string& name, std::vector<std::string> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (const auto& s : b)
+ {
+ rapidjson::Value value(s.c_str(), m_activeContext.allocator);
+ bArr.PushBack(value, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, std::vector<std::string>& b)
+ {
+ for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray())
+ b.emplace_back(i.GetString());
+ }
+
+ void Serialize(std::string& name, std::vector<std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& i : b)
+ {
+ rapidjson::Value segV(rapidjson::kArrayType);
+ segV.PushBack(i.first, m_activeContext.allocator);
+ segV.PushBack(i.second.first, m_activeContext.allocator);
+ segV.PushBack(i.second.second, m_activeContext.allocator);
+ bArr.PushBack(segV, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, std::vector<std::pair<uint64_t, std::pair<uint64_t, uint64_t>>>& b)
+ {
+ for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray())
+ {
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> j;
+ j.first = i.GetArray()[0].GetUint64();
+ j.second.first = i.GetArray()[1].GetUint64();
+ j.second.second = i.GetArray()[2].GetUint64();
+ b.push_back(j);
+ }
+ }
+
+ void Serialize(std::string& name, std::vector<std::pair<uint64_t, bool>> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& i : b)
+ {
+ rapidjson::Value segV(rapidjson::kArrayType);
+ segV.PushBack(i.first, m_activeContext.allocator);
+ segV.PushBack(i.second, m_activeContext.allocator);
+ bArr.PushBack(segV, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, std::vector<std::pair<uint64_t, bool>>& b)
+ {
+ for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray())
+ {
+ std::pair<uint64_t, bool> j;
+ j.first = i.GetArray()[0].GetUint64();
+ j.second = i.GetArray()[1].GetBool();
+ b.push_back(j);
+ }
+ }
+
+ void Serialize(std::string& name, std::vector<uint64_t> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& i : b)
+ {
+ bArr.PushBack(i, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, std::vector<uint64_t>& b)
+ {
+ for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray())
+ {
+ b.push_back(i.GetUint64());
+ }
+ }
+
+ // std::unordered_map<std::string, uint64_t>
+ void Serialize(std::string& name, std::unordered_map<std::string, uint64_t> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& i : b)
+ {
+ rapidjson::Value p(rapidjson::kArrayType);
+ rapidjson::Value _key(i.first.c_str(), m_activeContext.allocator);
+ p.PushBack(_key, m_activeContext.allocator);
+ p.PushBack(i.second, m_activeContext.allocator);
+ bArr.PushBack(p, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, std::unordered_map<std::string, uint64_t>& b)
+ {
+ for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray())
+ {
+ b[i.GetArray()[0].GetString()] = i.GetArray()[1].GetUint64();
+ }
+ }
+ // std::vector<std::pair<uint64_t, std::vector<std::pair<uint64_t, std::string>>>>
+ void Serialize(std::string& name, std::vector<std::pair<uint64_t, std::vector<std::pair<uint64_t, std::string>>>> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& i : b)
+ {
+ rapidjson::Value segV(rapidjson::kArrayType);
+ segV.PushBack(i.first, m_activeContext.allocator);
+ rapidjson::Value segArr(rapidjson::kArrayType);
+ for (auto& j : i.second)
+ {
+ rapidjson::Value segPair(rapidjson::kArrayType);
+ segPair.PushBack(j.first, m_activeContext.allocator);
+ rapidjson::Value segStr(j.second.c_str(), m_activeContext.allocator);
+ segPair.PushBack(segStr, m_activeContext.allocator);
+ segArr.PushBack(segPair, m_activeContext.allocator);
+ }
+ segV.PushBack(segArr, m_activeContext.allocator);
+ bArr.PushBack(segV, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+ void Deserialize(std::string& name, std::vector<std::pair<uint64_t, std::vector<std::pair<uint64_t, std::string>>>>& b)
+ {
+ for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray())
+ {
+ std::pair<uint64_t, std::vector<std::pair<uint64_t, std::string>>> j;
+ j.first = i.GetArray()[0].GetUint64();
+ for (auto& k : i.GetArray()[1].GetArray())
+ {
+ j.second.push_back({k.GetArray()[0].GetUint64(), k.GetArray()[1].GetString()});
+ }
+ b.push_back(j);
+ }
+ }
+
+ template <typename T>
+ void store(std::string x, T y)
+ {
+ Serialize(x, y);
+ }
+
+ template <typename T>
+ T load(std::string x, T y)
+ {
+ T val;
+ Deserialize(x, val);
+ return val;
+ }
+
+ rapidjson::Document& GetDoc()
+ {
+ S();
+ Store();
+ return m_activeContext.doc;
+ }
+
+public:
+ virtual void Store() = 0;
+ virtual void Load() = 0;
+
+ std::string AsString()
+ {
+ rapidjson::StringBuffer strbuf;
+ rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(strbuf);
+ GetDoc().Accept(writer);
+
+ std::string s = strbuf.GetString();
+ return s;
+ }
+ rapidjson::Document& AsDocument() { return GetDoc(); }
+ void LoadFromString(const std::string& s)
+ {
+ m_activeDeserContext.doc.Parse(s.c_str());
+ Load();
+ }
+ void LoadFromValue(rapidjson::Value& s)
+ {
+ m_activeDeserContext.doc.CopyFrom(s, m_activeDeserContext.doc.GetAllocator());
+ Load();
+ }
+ Ref<Metadata> AsMetadata() { return new Metadata(AsString()); }
+ bool LoadFromMetadata(const Ref<Metadata>& meta)
+ {
+ if (!meta->IsString())
+ return false;
+ LoadFromString(meta->GetString());
+ return true;
+ }
+};
+
+#endif // SHAREDCACHE_METADATASERIALIZABLE_HPP
diff --git a/view/sharedcache/core/ObjC.cpp b/view/sharedcache/core/ObjC.cpp
new file mode 100644
index 00000000..265506c2
--- /dev/null
+++ b/view/sharedcache/core/ObjC.cpp
@@ -0,0 +1,1524 @@
+#include "ObjC.h"
+#include "inttypes.h"
+#include "rapidjson/rapidjson.h"
+#include "rapidjson/document.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/prettywriter.h"
+
+using namespace BinaryNinja;
+using namespace DSCObjC;
+using namespace SharedCacheCore;
+
+Ref<Metadata> DSCObjCProcessor::SerializeMethod(uint64_t loc, const Method& method)
+{
+ std::map<std::string, Ref<Metadata>> methodMeta;
+
+ methodMeta["loc"] = new Metadata(loc);
+ methodMeta["name"] = new Metadata(method.name);
+ methodMeta["types"] = new Metadata(method.types);
+ methodMeta["imp"] = new Metadata(method.imp);
+
+ return new Metadata(methodMeta);
+}
+
+
+Ref<Metadata> DSCObjCProcessor::SerializeClass(uint64_t loc, const Class& cls)
+{
+ std::map<std::string, Ref<Metadata>> clsMeta;
+
+ clsMeta["loc"] = new Metadata(loc);
+ clsMeta["name"] = new Metadata(cls.name);
+ clsMeta["typeName"] = new Metadata(cls.associatedName.GetString());
+
+ std::vector<uint64_t> instanceMethods;
+ std::vector<uint64_t> classMethods;
+ instanceMethods.reserve(cls.instanceClass.methodList.size());
+ classMethods.reserve(cls.metaClass.methodList.size());
+ for (const auto& [location, _] : cls.instanceClass.methodList)
+ instanceMethods.push_back(location);
+
+ clsMeta["instanceMethods"] = new Metadata(instanceMethods);
+ clsMeta["classMethods"] = new Metadata(classMethods);
+
+ return new Metadata(clsMeta);
+}
+
+Ref<Metadata> DSCObjCProcessor::SerializeMetadata()
+{
+ std::map<std::string, Ref<Metadata>> viewMeta;
+ viewMeta["version"] = new Metadata((uint64_t)1);
+
+ std::vector<Ref<Metadata>> classes;
+ classes.reserve(m_classes.size());
+ std::vector<Ref<Metadata>> categories;
+ categories.reserve(m_categories.size());
+ std::vector<Ref<Metadata>> methods;
+ methods.reserve(m_localMethods.size());
+
+ for (const auto& [clsLoc, cls] : m_classes)
+ classes.push_back(SerializeClass(clsLoc, cls));
+ viewMeta["classes"] = new Metadata(classes);
+ for (const auto& [catLoc, cat] : m_categories)
+ categories.push_back(SerializeClass(catLoc, cat));
+ viewMeta["categories"] = new Metadata(categories);
+ for (const auto& [methodLoc, method] : m_localMethods)
+ methods.push_back(SerializeMethod(methodLoc, method));
+ viewMeta["methods"] = new Metadata(methods);
+
+ // Required for workflow_objc type guessing, should be removed when that is no longer a thing.
+ std::vector<Ref<Metadata>> selRefToImps;
+ selRefToImps.reserve(m_selRefToImplementations.size());
+ for (const auto& [selRef, imps] : m_selRefToImplementations)
+ {
+ std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(imps)};
+ Ref<Metadata> mapObject = new Metadata(mapBase);
+ selRefToImps.push_back(mapObject);
+ }
+ viewMeta["selRefImplementations"] = new Metadata(selRefToImps);
+
+ std::vector<Ref<Metadata>> selToImps;
+ selToImps.reserve(m_selToImplementations.size());
+ for (const auto& [selRef, imps] : m_selToImplementations)
+ {
+ std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(imps)};
+ Ref<Metadata> mapObject = new Metadata(mapBase);
+ selToImps.push_back(mapObject);
+ }
+ viewMeta["selImplementations"] = new Metadata(selToImps);
+
+ std::vector<Ref<Metadata>> selRefToName;
+ selRefToName.reserve(m_selRefToName.size());
+ for (const auto& [selRef, name] : m_selRefToName)
+ {
+ std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(name)};
+ Ref<Metadata> mapObject = new Metadata(mapBase);
+ selRefToName.push_back(mapObject);
+ }
+ viewMeta["selRefToName"] = new Metadata(selRefToName);
+ // ---
+
+
+ return new Metadata(viewMeta);
+}
+
+std::vector<DSCObjC::QualifiedNameOrType> DSCObjCProcessor::ParseEncodedType(const std::string& encodedType)
+{
+ std::vector<QualifiedNameOrType> result;
+ int pointerDepth = 0;
+
+ bool readingNamedType = false;
+ std::string namedType;
+ int readingStructDepth = 0;
+ std::string structType;
+ char last;
+
+ for (char c : encodedType)
+ {
+ if (readingNamedType && c != '"')
+ {
+ namedType.push_back(c);
+ last = c;
+ continue;
+ }
+ else if (readingStructDepth > 0 && c != '{' && c != '}')
+ {
+ structType.push_back(c);
+ last = c;
+ continue;
+ }
+
+ if (std::isdigit(c))
+ continue;
+
+ QualifiedNameOrType nameOrType;
+ std::string qualifiedName;
+
+ switch (c)
+ {
+ case '^':
+ pointerDepth++;
+ last = c;
+ continue;
+
+ case '"':
+ if (!readingNamedType)
+ {
+ readingNamedType = true;
+ if (last == '@')
+ result.pop_back(); // We added an 'id' in the last cycle, remove it
+ last = c;
+ continue;
+ }
+ else
+ {
+ readingNamedType = false;
+ nameOrType.name = QualifiedName(namedType);
+ nameOrType.ptrCount = 1;
+ break;
+ }
+ case '{':
+ readingStructDepth++;
+ last = c;
+ continue;
+ case '}':
+ readingStructDepth--;
+ if (readingStructDepth < 0)
+ return {}; // seriously malformed type.
+
+ if (readingStructDepth == 0)
+ {
+ // TODO: Emit real struct types
+ nameOrType.type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
+ break;
+ }
+ last = c;
+ continue;
+ case 'v':
+ nameOrType.type = Type::VoidType();
+ break;
+ case 'c':
+ nameOrType.type = Type::IntegerType(1, true);
+ break;
+ case 'A':
+ case 'C':
+ nameOrType.type = Type::IntegerType(1, false);
+ break;
+ case 's':
+ nameOrType.type = Type::IntegerType(2, true);
+ break;
+ case 'S':
+ nameOrType.type = Type::IntegerType(1, false);
+ break;
+ case 'i':
+ nameOrType.type = Type::IntegerType(4, true);
+ break;
+ case 'I':
+ nameOrType.type = Type::IntegerType(4, false);
+ break;
+ case 'l':
+ nameOrType.type = Type::IntegerType(8, true);
+ break;
+ case 'L':
+ nameOrType.type = Type::IntegerType(8, true);
+ break;
+ case 'f':
+ nameOrType.type = Type::IntegerType(4, true);
+ break;
+ case 'b':
+ case 'B':
+ nameOrType.type = Type::BoolType();
+ break;
+ case 'q':
+ qualifiedName = "NSInteger";
+ break;
+ case 'Q':
+ qualifiedName = "NSUInteger";
+ break;
+ case 'd':
+ qualifiedName = "CGFloat";
+ break;
+ case '*':
+ nameOrType.type = Type::PointerType(m_data->GetAddressSize(), Type::IntegerType(1, true));
+ break;
+ case '@':
+ qualifiedName = "id";
+ // There can be a type after this, like @"NSString", that overrides this
+ // The handler for " will catch it and drop this "id" entry.
+ break;
+ case ':':
+ qualifiedName = "SEL";
+ break;
+ case '#':
+ qualifiedName = "objc_class_t";
+ break;
+ case '?':
+ case 'T':
+ nameOrType.type = Type::PointerType(8, Type::VoidType());
+ break;
+ default:
+ // BNLogWarn("Unknown type specifier %c", c);
+ last = c;
+ continue;
+ }
+
+ while (pointerDepth)
+ {
+ if (nameOrType.type)
+ nameOrType.type = Type::PointerType(8, nameOrType.type);
+ else
+ nameOrType.ptrCount++;
+
+ pointerDepth--;
+ }
+
+ if (!qualifiedName.empty())
+ nameOrType.name = QualifiedName(qualifiedName);
+
+ if (nameOrType.type == nullptr && nameOrType.name.IsEmpty())
+ {
+ nameOrType.type = Type::VoidType();
+ }
+
+ result.push_back(nameOrType);
+ last = c;
+ }
+
+ return result;
+}
+
+void DSCObjCProcessor::DefineObjCSymbol(
+ BNSymbolType type, QualifiedName typeName, const std::string& name, uint64_t addr, bool deferred)
+{
+ DefineObjCSymbol(type, m_data->GetTypeByName(typeName), name, addr, deferred);
+}
+
+void DSCObjCProcessor::DefineObjCSymbol(
+ BNSymbolType type, Ref<Type> typeRef, const std::string& name, uint64_t addr, bool deferred)
+{
+ if (name.size() == 0 || addr == 0)
+ return;
+
+ auto process = [=]() {
+ NameSpace nameSpace = m_data->GetInternalNameSpace();
+ if (type == ExternalSymbol)
+ {
+ nameSpace = m_data->GetExternalNameSpace();
+ }
+
+ std::string shortName = name;
+ std::string fullName = name;
+
+ QualifiedName varName;
+
+ return std::pair<Ref<Symbol>, Ref<Type>>(
+ new Symbol(type, shortName, fullName, name, addr, GlobalBinding, nameSpace), typeRef);
+ };
+
+ if (deferred)
+ {
+ m_symbolQueue->Append(process, [this, addr = addr](Symbol* symbol, Type* type) {
+ // Armv7/Thumb: This will rewrite the symbol's address.
+ // e.g. We pass in 0xc001, it will rewrite it to 0xc000 and create the function w/ the "thumb2" arch.
+ if (Ref<Symbol> existingSymbol = m_data->GetSymbolByAddress(addr))
+ m_data->UndefineAutoSymbol(existingSymbol);
+ auto funcSym = m_data->DefineAutoSymbolAndVariableOrFunction(m_data->GetDefaultPlatform(), symbol, type);
+ if (funcSym->GetType() == FunctionSymbol)
+ {
+ uint64_t target = symbol->GetAddress();
+ Ref<Platform> targetPlatform =
+ m_data->GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); // rewrites target.
+ if (Ref<Function> targetFunction = m_data->GetAnalysisFunction(targetPlatform, target))
+ {
+ if (!m_isBackedByDatabase)
+ targetFunction->SetUserType(type);
+ }
+ }
+ });
+ return;
+ }
+
+ if (Ref<Symbol> existingSymbol = m_data->GetSymbolByAddress(addr))
+ m_data->UndefineAutoSymbol(existingSymbol);
+ auto result = process();
+ auto sym = m_data->DefineAutoSymbolAndVariableOrFunction(m_data->GetDefaultPlatform(), result.first, result.second);
+ if (sym->GetType() == FunctionSymbol)
+ {
+ uint64_t target = result.first->GetAddress();
+ Ref<Platform> targetPlatform = m_data->GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); // rewrites
+ // target.
+ if (Ref<Function> targetFunction = m_data->GetAnalysisFunction(targetPlatform, target))
+ {
+ if (!m_isBackedByDatabase)
+ targetFunction->SetUserType(result.second);
+ }
+ }
+}
+
+void DSCObjCProcessor::LoadClasses(VMReader* reader, Ref<Section> classPtrSection)
+{
+ if (!classPtrSection)
+ return;
+ auto size = classPtrSection->GetEnd() - classPtrSection->GetStart();
+ if (size == 0)
+ return;
+ auto ptrSize = m_data->GetAddressSize();
+ auto ptrCount = size / ptrSize;
+
+ auto classPtrSectionStart = classPtrSection->GetStart();
+ for (size_t i = 0; i < ptrCount; i++)
+ {
+ Class cls;
+
+ view_ptr_t classPtr;
+ class_t clsStruct;
+ class_ro_t classRO;
+
+ bool hasValidMetaClass = false;
+ bool hasValidMetaClassRO = false;
+ class_t metaClsStruct;
+ class_ro_t metaClassRO;
+
+ view_ptr_t classPointerLocation = classPtrSectionStart + (i * m_data->GetAddressSize());
+ reader->Seek(classPointerLocation);
+
+ classPtr = ReadPointerAccountingForRelocations(reader);
+ reader->Seek(classPtr);
+ try
+ {
+ clsStruct.isa = ReadPointerAccountingForRelocations(reader);
+ clsStruct.super = reader->ReadPointer();
+ clsStruct.cache = reader->ReadPointer();
+ clsStruct.vtable = reader->ReadPointer();
+ clsStruct.data = ReadPointerAccountingForRelocations(reader);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read class data at 0x%llx pointed to by @ 0x%llx", reader->GetOffset(),
+ classPointerLocation);
+ continue;
+ }
+ if (clsStruct.data & 1)
+ {
+ m_logger->LogInfo("Skipping class at 0x%llx as it contains swift types", classPtr);
+ continue;
+ }
+ // unset first two bits
+ view_ptr_t classROPtr = clsStruct.data & ~3;
+ reader->Seek(classROPtr);
+ try
+ {
+ classRO.flags = reader->Read32();
+ classRO.instanceStart = reader->Read32();
+ classRO.instanceSize = reader->Read32();
+ if (m_data->GetAddressSize() == 8)
+ classRO.reserved = reader->Read32();
+ classRO.ivarLayout = ReadPointerAccountingForRelocations(reader);
+ classRO.name = ReadPointerAccountingForRelocations(reader);
+ classRO.baseMethods = ReadPointerAccountingForRelocations(reader);
+ classRO.baseProtocols = ReadPointerAccountingForRelocations(reader);
+ classRO.ivars = ReadPointerAccountingForRelocations(reader);
+ classRO.weakIvarLayout = ReadPointerAccountingForRelocations(reader);
+ classRO.baseProperties = ReadPointerAccountingForRelocations(reader);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read class RO data at 0x%llx. 0x%llx, objc_class_t @ 0x%llx",
+ reader->GetOffset(), classPointerLocation, classROPtr);
+ continue;
+ }
+
+ auto namePtr = classRO.name;
+
+ std::string name;
+
+ reader->Seek(namePtr);
+ try
+ {
+ name = reader->ReadCString(namePtr);
+ }
+ catch (...)
+ {
+ m_logger->LogWarn(
+ "Failed to read class name at 0x%llx. Class has been given the placeholder name \"0x%llx\" ", namePtr,
+ classPtr);
+ char hexString[9];
+ hexString[8] = 0;
+ snprintf(hexString, sizeof(hexString), "%llx", classPtr);
+ name = "0x" + std::string(hexString);
+ }
+
+ cls.name = name;
+
+ DefineObjCSymbol(BNSymbolType::DataSymbol,
+ Type::PointerType(m_data->GetAddressSize(), m_data->GetTypeByName(m_typeNames.cls)), "clsPtr_" + name,
+ classPointerLocation, true);
+ DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.cls, "cls_" + name, classPtr, true);
+ DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.classRO, "cls_ro_" + name, classROPtr, true);
+ DefineObjCSymbol(BNSymbolType::DataSymbol, Type::ArrayType(Type::IntegerType(1, true), name.size() + 1),
+ "clsName_" + name, classRO.name, true);
+ if (0 && classRO.baseProtocols)
+ {
+ DefineObjCSymbol(BNSymbolType::DataSymbol, Type::NamedType(m_data, m_typeNames.protocolList),
+ "clsProtocols_" + name, classRO.baseProtocols, true);
+ reader->Seek(classRO.baseProtocols);
+ uint32_t count = reader->Read64();
+ view_ptr_t addr = reader->GetOffset();
+ for (uint32_t j = 0; j < count; j++)
+ {
+ m_data->DefineDataVariable(
+ addr, Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol)));
+ addr += ptrSize;
+ }
+ }
+
+ if (clsStruct.isa)
+ {
+ reader->Seek(clsStruct.isa);
+ try
+ {
+ metaClsStruct.isa = ReadPointerAccountingForRelocations(reader);
+ metaClsStruct.super = reader->ReadPointer();
+ metaClsStruct.cache = reader->ReadPointer();
+ metaClsStruct.vtable = reader->ReadPointer();
+ metaClsStruct.data = ReadPointerAccountingForRelocations(reader) & ~1;
+ DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.cls, "metacls_" + name, clsStruct.isa, true);
+ hasValidMetaClass = true;
+ }
+ catch (...)
+ {
+ m_logger->LogWarn("Failed to read metaclass data at 0x%llx pointed to by objc_class_t @ 0x%llx",
+ reader->GetOffset(), classPtr);
+ }
+ }
+ if (hasValidMetaClass && (metaClsStruct.data & 1))
+ {
+ m_logger->LogInfo("Skipping metaclass at 0x%llx as it contains swift types", classPtr);
+ hasValidMetaClass = false;
+ }
+ if (hasValidMetaClass)
+ {
+ reader->Seek(metaClsStruct.data);
+ try
+ {
+ metaClassRO.flags = reader->Read32();
+ metaClassRO.instanceStart = reader->Read32();
+ metaClassRO.instanceSize = reader->Read32();
+ if (m_data->GetAddressSize() == 8)
+ metaClassRO.reserved = reader->Read32();
+ metaClassRO.ivarLayout = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.name = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.baseMethods = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.baseProtocols = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.ivars = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.weakIvarLayout = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.baseProperties = ReadPointerAccountingForRelocations(reader);
+ DefineObjCSymbol(
+ BNSymbolType::DataSymbol, m_typeNames.classRO, "metacls_ro_" + name, metaClsStruct.data, true);
+ hasValidMetaClassRO = true;
+ }
+ catch (...)
+ {
+ m_logger->LogWarn("Failed to read metaclass RO data at 0x%llx pointed to by meta objc_class_t @ 0x%llx",
+ reader->GetOffset(), clsStruct.isa);
+ }
+ }
+
+ if (classRO.baseMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, cls.instanceClass, name, classRO.baseMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read the method list for class pointed to by 0x%llx", clsStruct.data);
+ }
+ }
+ if (hasValidMetaClassRO && metaClassRO.baseMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, cls.metaClass, name, metaClassRO.baseMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read the method list for metaclass pointed to by 0x%llx", clsStruct.data);
+ }
+ }
+
+ if (classRO.ivars)
+ {
+ try
+ {
+ ReadIvarList(reader, cls.instanceClass, name, classRO.ivars);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to process ivars for class at 0x%llx", clsStruct.data);
+ }
+ }
+ m_classes[classPtr] = cls;
+ }
+}
+
+void DSCObjCProcessor::LoadCategories(VMReader* reader, Ref<Section> classPtrSection)
+{
+ if (!classPtrSection)
+ return;
+ auto size = classPtrSection->GetEnd() - classPtrSection->GetStart();
+ if (size == 0)
+ return;
+ auto ptrSize = m_data->GetAddressSize();
+ auto ptrCount = size / m_data->GetAddressSize();
+
+ auto classPtrSectionStart = classPtrSection->GetStart();
+ auto classPtrSectionEnd = classPtrSection->GetEnd();
+
+ auto catType = Type::NamedType(m_data, m_typeNames.category);
+ auto ptrType = Type::PointerType(m_data->GetDefaultArchitecture(), catType);
+ for (size_t i = classPtrSectionStart; i < classPtrSectionEnd; i += ptrSize)
+ {
+ Class category;
+ category_t cat;
+
+ reader->Seek(i);
+ auto catLocation = ReadPointerAccountingForRelocations(reader);
+ reader->Seek(catLocation);
+
+ try
+ {
+ cat.name = ReadPointerAccountingForRelocations(reader);
+ cat.cls = ReadPointerAccountingForRelocations(reader);
+ cat.instanceMethods = ReadPointerAccountingForRelocations(reader);
+ cat.classMethods = ReadPointerAccountingForRelocations(reader);
+ cat.protocols = ReadPointerAccountingForRelocations(reader);
+ cat.instanceProperties = ReadPointerAccountingForRelocations(reader);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read category pointed to by 0x%llx", i);
+ continue;
+ }
+
+ std::string categoryAdditionsName;
+ std::string categoryBaseClassName;
+
+ if (const auto& it = m_classes.find(cat.cls); it != m_classes.end())
+ {
+ categoryBaseClassName = it->second.name;
+ category.associatedName = it->second.associatedName;
+ }
+ else if (auto symbol = m_data->GetSymbolByAddress(catLocation + m_data->GetAddressSize()))
+ {
+ if (symbol->GetType() == ImportedDataSymbol || symbol->GetType() == ImportAddressSymbol)
+ {
+ const auto& symbolName = symbol->GetFullName();
+ if (symbolName.size() > 14 && symbolName.rfind("_OBJC_CLASS_$_", 0) == 0)
+ categoryBaseClassName = symbolName.substr(14, symbolName.size() - 14);
+ }
+ }
+ if (categoryBaseClassName.empty())
+ {
+ m_logger->LogError(
+ "Failed to determine base classname for category at 0x%llx. Using base address as stand-in classname",
+ catLocation);
+ categoryBaseClassName = std::to_string(catLocation);
+ }
+ try
+ {
+ reader->Seek(cat.name);
+ categoryAdditionsName = reader->ReadCString(cat.name);
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read category name for category at 0x%llx. Using base address as stand-in category name",
+ catLocation);
+ categoryAdditionsName = std::to_string(catLocation);
+ }
+ category.name = categoryBaseClassName + " (" + categoryAdditionsName + ")";
+ DefineObjCSymbol(BNSymbolType::DataSymbol, ptrType, "categoryPtr_" + category.name, i, true);
+ DefineObjCSymbol(BNSymbolType::DataSymbol, catType, "category_" + category.name, catLocation, true);
+
+ if (cat.instanceMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, category.instanceClass, category.name, cat.instanceMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read the instance method list for category pointed to by 0x%llx", catLocation);
+ }
+ }
+ if (cat.classMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, category.metaClass, category.name, cat.classMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read the class method list for category pointed to by 0x%llx", catLocation);
+ }
+ }
+ m_categories[catLocation] = category;
+ }
+}
+
+void DSCObjCProcessor::LoadProtocols(VMReader* reader, Ref<Section> listSection)
+{
+ if (!listSection)
+ return;
+ auto size = listSection->GetEnd() - listSection->GetStart();
+ if (size == 0)
+ return;
+ auto ptrSize = m_data->GetAddressSize();
+
+ auto listSectionStart = listSection->GetStart();
+ auto listSectionEnd = listSection->GetEnd();
+
+ auto protocolType = Type::NamedType(m_data, m_typeNames.protocol);
+ auto ptrType = Type::PointerType(m_data->GetDefaultArchitecture(), protocolType);
+ for (size_t i = listSectionStart; i < listSectionEnd; i += ptrSize)
+ {
+ protocol_t protocol;
+ reader->Seek(i);
+ auto protocolLocation = ReadPointerAccountingForRelocations(reader);
+ reader->Seek(protocolLocation);
+
+ try
+ {
+ protocol.isa = ReadPointerAccountingForRelocations(reader);
+ protocol.mangledName = ReadPointerAccountingForRelocations(reader);
+ protocol.protocols = ReadPointerAccountingForRelocations(reader);
+ protocol.instanceMethods = ReadPointerAccountingForRelocations(reader);
+ protocol.classMethods = ReadPointerAccountingForRelocations(reader);
+ protocol.optionalInstanceMethods = ReadPointerAccountingForRelocations(reader);
+ protocol.optionalClassMethods = ReadPointerAccountingForRelocations(reader);
+ protocol.instanceProperties = ReadPointerAccountingForRelocations(reader);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read protocol pointed to by 0x%llx", i);
+ continue;
+ }
+
+ std::string protocolName;
+ try
+ {
+ reader->Seek(protocol.mangledName);
+ protocolName = reader->ReadCString(protocol.mangledName);
+ DefineObjCSymbol(BNSymbolType::DataSymbol,
+ Type::ArrayType(Type::IntegerType(1, true), protocolName.size() + 1), "protocolName_" + protocolName,
+ protocol.mangledName, true);
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read protocol name for protocol at 0x%llx. Using base address as stand-in protocol name",
+ protocolLocation);
+ protocolName = std::to_string(protocolLocation);
+ }
+
+ Protocol protocolClass;
+ protocolClass.name = protocolName;
+ DefineObjCSymbol(BNSymbolType::DataSymbol, ptrType, "protocolPtr_" + protocolName, i, true);
+ DefineObjCSymbol(BNSymbolType::DataSymbol, protocolType, "protocol_" + protocolName, protocolLocation, true);
+ if (protocol.protocols)
+ {
+ DefineObjCSymbol(BNSymbolType::DataSymbol, Type::NamedType(m_data, m_typeNames.protocolList),
+ "protoProtocols_" + protocolName, protocol.protocols, true);
+ reader->Seek(protocol.protocols);
+ uint32_t count = reader->Read64();
+ view_ptr_t addr = reader->GetOffset();
+ for (uint32_t j = 0; j < count; j++)
+ {
+ m_data->DefineDataVariable(
+ addr, Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol)));
+ addr += ptrSize;
+ }
+ }
+
+ if (protocol.instanceMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, protocolClass.instanceMethods, protocolName, protocol.instanceMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read the instance method list for protocol pointed to by 0x%llx", protocolLocation);
+ }
+ }
+ if (protocol.classMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, protocolClass.classMethods, protocolName, protocol.classMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read the class method list for protocol pointed to by 0x%llx", protocolLocation);
+ }
+ }
+ if (protocol.optionalInstanceMethods)
+ {
+ try
+ {
+ ReadMethodList(
+ reader, protocolClass.optionalInstanceMethods, protocolName, protocol.optionalInstanceMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read the optional instance method list for protocol pointed to by 0x%llx",
+ protocolLocation);
+ }
+ }
+ if (protocol.optionalClassMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, protocolClass.optionalClassMethods, protocolName, protocol.optionalClassMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read the optional class method list for protocol pointed to by 0x%llx",
+ protocolLocation);
+ }
+ }
+ m_protocols[protocolLocation] = protocolClass;
+ }
+}
+
+void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start)
+{
+ reader->Seek(start);
+ method_list_t head;
+ head.entsizeAndFlags = reader->Read32();
+ head.count = reader->Read32();
+ if (head.count > 0x1000)
+ {
+ m_logger->LogError("Method list at 0x%llx has an invalid count of 0x%x", start, head.count);
+ return;
+ }
+ uint64_t pointerSize = m_data->GetAddressSize();
+ bool relativeOffsets = (head.entsizeAndFlags & 0xFFFF0000) & 0x80000000;
+ bool directSelectors = (head.entsizeAndFlags & 0xFFFF0000) & 0x40000000;
+ auto methodSize = relativeOffsets ? 12 : pointerSize * 3;
+ DefineObjCSymbol(DataSymbol, m_typeNames.methodList, "method_list_" + name, start, true);
+
+ for (unsigned i = 0; i < head.count; i++)
+ {
+ try
+ {
+ Method method;
+ auto cursor = start + sizeof(method_list_t) + (i * methodSize);
+ reader->Seek(cursor);
+ method_t meth;
+ // workflow_objc support
+ uint64_t selRefAddr = 0;
+ uint64_t selAddr = 0;
+ // --
+ if (relativeOffsets)
+ {
+ if (m_customRelativeMethodSelectorBase.has_value())
+ {
+ meth.name = m_customRelativeMethodSelectorBase.value() + reader->ReadS32();
+ meth.types = reader->GetOffset() + reader->ReadS32();
+ meth.imp = reader->GetOffset() + reader->ReadS32();
+ }
+ else
+ {
+ meth.name = reader->GetOffset() + reader->ReadS32();
+ meth.types = reader->GetOffset() + reader->ReadS32();
+ meth.imp = reader->GetOffset() + reader->ReadS32();
+ }
+ }
+ else
+ {
+ meth.name = ReadPointerAccountingForRelocations(reader);
+ meth.types = ReadPointerAccountingForRelocations(reader);
+ meth.imp = ReadPointerAccountingForRelocations(reader);
+ }
+ if (!relativeOffsets || directSelectors)
+ {
+ selAddr = meth.name;
+ method.name = reader->ReadCString(meth.name);
+ reader->Seek(meth.types);
+ method.types = reader->ReadCString(meth.types);
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.name.size() + 1),
+ "sel_" + method.name, meth.name, true);
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.types.size() + 1),
+ "selTypes_" + method.name, meth.types, true);
+ }
+ else
+ {
+ std::string sel;
+ view_ptr_t selRef;
+ reader->Seek(meth.name);
+ selRefAddr = meth.name;
+ selRef = ReadPointerAccountingForRelocations(reader);
+ reader->Seek(meth.types);
+ method.types = reader->ReadCString(meth.types);
+ selAddr = selRef;
+ if (const auto& it = m_selectorCache.find(selRef); it != m_selectorCache.end())
+ method.name = it->second;
+ else
+ {
+ reader->Seek(selRef);
+ method.name = reader->ReadCString(selRef);
+ m_selectorCache[selRef] = method.name;
+ }
+ auto selType = Type::ArrayType(Type::IntegerType(1, true), method.name.size() + 1);
+ DefineObjCSymbol(DataSymbol, selType, "sel_" + method.name, selRef, true);
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.types.size() + 1),
+ "selTypes_" + method.name, meth.types, true);
+ DefineObjCSymbol(DataSymbol, Type::PointerType(m_data->GetAddressSize(), selType),
+ "selRef_" + method.name, meth.name, true);
+ }
+ // workflow objc support
+ if (selAddr)
+ m_selToImplementations[selAddr].push_back(meth.imp);
+ if (selRefAddr)
+ m_selRefToImplementations[selRefAddr].push_back(meth.imp);
+ // --
+
+ DefineObjCSymbol(DataSymbol, relativeOffsets ? m_typeNames.methodEntry : m_typeNames.method,
+ "method_" + method.name, cursor, true);
+ method.imp = meth.imp;
+ cls.methodList[cursor] = method;
+ m_localMethods[cursor] = method;
+ }
+ catch (...)
+ {
+ // m_logger->LogError("Failed to process a method at offset 0x%llx", start + sizeof(method_list_t) + (i * methodSize));
+ }
+ }
+}
+
+void DSCObjCProcessor::ReadIvarList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start)
+{
+ reader->Seek(start);
+ ivar_list_t head;
+ head.entsizeAndFlags = reader->Read32();
+ head.count = reader->Read32();
+ auto addressSize = m_data->GetAddressSize();
+ DefineObjCSymbol(DataSymbol, m_typeNames.ivarList, "ivar_list_" + name, start, true);
+ for (unsigned i = 0; i < head.count; i++)
+ {
+ try
+ {
+ Ivar ivar;
+ ivar_t ivarStruct;
+ uint64_t cursor = start + (sizeof(ivar_list_t)) + (i * ((addressSize * 3) + 8));
+ reader->Seek(cursor);
+ ivarStruct.offset = ReadPointerAccountingForRelocations(reader);
+ ivarStruct.name = ReadPointerAccountingForRelocations(reader);
+ ivarStruct.type = ReadPointerAccountingForRelocations(reader);
+ ivarStruct.alignmentRaw = reader->Read32();
+ ivarStruct.size = reader->Read32();
+
+ reader->Seek(ivarStruct.offset);
+ ivar.offset = reader->Read32();
+ reader->Seek(ivarStruct.name);
+ ivar.name = reader->ReadCString(ivarStruct.name);
+ reader->Seek(ivarStruct.type);
+ ivar.type = reader->ReadCString(ivarStruct.type);
+
+ DefineObjCSymbol(DataSymbol, m_typeNames.ivar, "ivar_" + ivar.name, cursor, true);
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), ivar.name.size() + 1),
+ "ivarName_" + ivar.name, ivarStruct.name, true);
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), ivar.type.size() + 1),
+ "ivarType_" + ivar.name, ivarStruct.type, true);
+
+ cls.ivarList[cursor] = ivar;
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to process an ivar at offset 0x%llx",
+ start + (sizeof(ivar_list_t)) + (i * ((addressSize * 3) + 8)));
+ }
+ }
+}
+
+
+std::pair<QualifiedName, Ref<Type>> finalizeStructureBuilder(
+ Ref<BinaryView> m_data, StructureBuilder sb, std::string name)
+{
+ auto classTypeStruct = sb.Finalize();
+
+ QualifiedName classTypeName(name);
+ auto classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
+ auto classType = Type::StructureType(classTypeStruct);
+ auto classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
+
+ return {classQualName, classType};
+}
+
+std::pair<QualifiedName, Ref<Type>> finalizeEnumerationBuilder(
+ Ref<BinaryView> m_data, EnumerationBuilder eb, uint64_t size, QualifiedName name)
+{
+ auto enumTypeStruct = eb.Finalize();
+
+ auto enumTypeId = Type::GenerateAutoTypeId("objc", name);
+ auto enumType = Type::EnumerationType(enumTypeStruct, size);
+ auto enumQualName = m_data->DefineType(enumTypeId, name, enumType);
+
+ return {enumQualName, enumType};
+}
+
+inline QualifiedName defineTypedef(Ref<BinaryView> m_data, const QualifiedName name, Ref<Type> type)
+{
+ auto typeID = Type::GenerateAutoTypeId("objc", name);
+ m_data->DefineType(typeID, name, type);
+ return m_data->GetTypeNameById(typeID);
+}
+
+void DSCObjCProcessor::GenerateClassTypes()
+{
+ for (auto& [_, cls] : m_classes)
+ {
+ QualifiedName typeName;
+ StructureBuilder classTypeBuilder;
+ bool failedToDecodeType = false;
+ for (const auto& [ivarLoc, ivar] : cls.instanceClass.ivarList)
+ {
+ auto encodedTypeList = ParseEncodedType(ivar.type);
+ if (encodedTypeList.empty())
+ {
+ failedToDecodeType = true;
+ break;
+ }
+ auto encodedType = encodedTypeList.at(0);
+
+ Ref<Type> type;
+
+ if (encodedType.type)
+ type = encodedType.type;
+ else
+ {
+ type = Type::NamedType(encodedType.name, Type::PointerType(m_data->GetAddressSize(), Type::VoidType()));
+ for (size_t i = encodedType.ptrCount; i > 0; i--)
+ type = Type::PointerType(m_data->GetAddressSize(), type);
+ }
+
+ if (!type)
+ type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
+
+ classTypeBuilder.AddMemberAtOffset(type, ivar.name, ivar.offset);
+ }
+ if (failedToDecodeType)
+ continue;
+ auto classTypeStruct = classTypeBuilder.Finalize();
+ QualifiedName classTypeName = cls.name;
+ std::string classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
+ Ref<Type> classType = Type::StructureType(classTypeStruct);
+ QualifiedName classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
+ cls.associatedName = classTypeName;
+ }
+}
+
+bool DSCObjCProcessor::ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod)
+{
+ std::stringstream r(method.name);
+
+ std::string token;
+ std::vector<std::string> selectorTokens;
+ while (std::getline(r, token, ':'))
+ selectorTokens.push_back(token);
+
+ std::vector<QualifiedNameOrType> typeTokens = ParseEncodedType(method.types);
+ if (typeTokens.empty())
+ return false;
+
+ auto typeForQualifiedNameOrType = [this](QualifiedNameOrType nameOrType) {
+ Ref<Type> type;
+
+ if (nameOrType.type)
+ {
+ type = nameOrType.type;
+ if (!type)
+ type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
+ }
+ else
+ {
+ type = Type::NamedType(nameOrType.name, Type::PointerType(m_data->GetAddressSize(), Type::VoidType()));
+ for (size_t i = nameOrType.ptrCount; i > 0; i--)
+ type = Type::PointerType(m_data->GetAddressSize(), type);
+ }
+
+ return type;
+ };
+
+ BinaryNinja::QualifiedNameAndType nameAndType;
+ std::set<BinaryNinja::QualifiedName> typesAllowRedefinition;
+
+ auto retType = typeForQualifiedNameOrType(typeTokens[0]);
+
+ std::vector<BinaryNinja::FunctionParameter> params;
+ auto cc = m_data->GetDefaultPlatform()->GetDefaultCallingConvention();
+
+ params.push_back({"self",
+ cls.associatedName.IsEmpty() ?
+ Type::NamedType(m_data, {"id"}) :
+ Type::PointerType(m_data->GetAddressSize(), Type::NamedType(m_data, cls.associatedName)),
+ true, BinaryNinja::Variable()});
+
+ params.push_back({"sel", Type::NamedType(m_data, {"SEL"}), true, BinaryNinja::Variable()});
+
+ for (size_t i = 3; i < typeTokens.size(); i++)
+ {
+ std::string suffix;
+
+ params.push_back({selectorTokens.size() > i - 3 ? selectorTokens[i - 3] : "arg",
+ typeForQualifiedNameOrType(typeTokens[i]), true, BinaryNinja::Variable()});
+ }
+
+ auto funcType = BinaryNinja::Type::FunctionType(retType, cc, params);
+
+ // Search for the method's implementation function; apply the type if found.
+ std::string prefix = isInstanceMethod ? "-" : "+";
+ auto name = prefix + "[" + cls.name + " " + method.name + "]";
+
+ DefineObjCSymbol(FunctionSymbol, funcType, name, method.imp, true);
+
+ return true;
+}
+
+void DSCObjCProcessor::ApplyMethodTypes(Class& cls)
+{
+ for (auto& [_, method] : cls.instanceClass.methodList)
+ {
+ ApplyMethodType(cls, method, true);
+ }
+ for (auto& [_, method] : cls.metaClass.methodList)
+ {
+ ApplyMethodType(cls, method, false);
+ }
+}
+
+void DSCObjCProcessor::PostProcessObjCSections(VMReader* reader)
+{
+ auto ptrSize = m_data->GetAddressSize();
+ if (auto imageInfo = m_data->GetSectionByName("__objc_imageinfo"))
+ {
+ auto start = imageInfo->GetStart();
+ auto type = Type::NamedType(m_data, m_typeNames.imageInfo);
+ m_data->DefineDataVariable(start, type);
+ }
+ if (auto selrefs = m_data->GetSectionByName("__objc_selrefs"))
+ {
+ auto start = selrefs->GetStart();
+ auto end = selrefs->GetEnd();
+ auto type = Type::PointerType(ptrSize, Type::IntegerType(1, false));
+ for (view_ptr_t i = start; i < end; i += ptrSize)
+ {
+ reader->Seek(i);
+ auto selLoc = ReadPointerAccountingForRelocations(reader);
+ std::string sel;
+ if (const auto& it = m_selectorCache.find(selLoc); it != m_selectorCache.end())
+ sel = it->second;
+ else
+ {
+ reader->Seek(selLoc);
+ sel = reader->ReadCString(selLoc);
+ m_selectorCache[selLoc] = sel;
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), sel.size() + 1), "sel_" + sel,
+ selLoc, true);
+ }
+ DefineObjCSymbol(DataSymbol, type, "selRef_" + sel, i, true);
+ }
+ }
+ if (auto superRefs = m_data->GetSectionByName("__objc_classrefs"))
+ {
+ auto start = superRefs->GetStart();
+ auto end = superRefs->GetEnd();
+ auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.cls));
+ for (view_ptr_t i = start; i < end; i += ptrSize)
+ {
+ reader->Seek(i);
+ auto clsLoc = ReadPointerAccountingForRelocations(reader);
+ if (const auto& it = m_classes.find(clsLoc); it != m_classes.end())
+ {
+ auto& cls = it->second;
+ std::string name = cls.name;
+ if (!name.empty())
+ DefineObjCSymbol(DataSymbol, type, "clsRef_" + name, i, true);
+ }
+ }
+ }
+ if (auto superRefs = m_data->GetSectionByName("__objc_superrefs"))
+ {
+ auto start = superRefs->GetStart();
+ auto end = superRefs->GetEnd();
+ auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.cls));
+ for (view_ptr_t i = start; i < end; i += ptrSize)
+ {
+ reader->Seek(i);
+ auto clsLoc = ReadPointerAccountingForRelocations(reader);
+ if (const auto& it = m_classes.find(clsLoc); it != m_classes.end())
+ {
+ auto& cls = it->second;
+ std::string name = cls.name;
+ if (!name.empty())
+ DefineObjCSymbol(DataSymbol, type, "superRef_" + name, i, true);
+ }
+ }
+ }
+ if (auto protoRefs = m_data->GetSectionByName("__objc_protorefs"))
+ {
+ auto start = protoRefs->GetStart();
+ auto end = protoRefs->GetEnd();
+ auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol));
+ for (view_ptr_t i = start; i < end; i += ptrSize)
+ {
+ reader->Seek(i);
+ auto protoLoc = ReadPointerAccountingForRelocations(reader);
+ if (const auto& it = m_protocols.find(protoLoc); it != m_protocols.end())
+ {
+ auto& proto = it->second;
+ std::string name = proto.name;
+ if (!name.empty())
+ DefineObjCSymbol(DataSymbol, type, "protoRef_" + name, i, true);
+ }
+ }
+ }
+ if (auto ivars = m_data->GetSectionByName("__objc_ivar"))
+ {
+ auto start = ivars->GetStart();
+ auto end = ivars->GetEnd();
+ auto ivarSectionEntryTypeBuilder = new TypeBuilder(Type::IntegerType(8, false));
+ ivarSectionEntryTypeBuilder->SetConst(true);
+ auto type = ivarSectionEntryTypeBuilder->Finalize();
+ for (view_ptr_t i = start; i < end; i += ptrSize)
+ {
+ m_data->DefineDataVariable(i, type);
+ }
+ }
+}
+
+uint64_t DSCObjCProcessor::ReadPointerAccountingForRelocations(VMReader* reader)
+{
+ if (auto it = m_relocationPointerRewrites.find(reader->GetOffset()); it != m_relocationPointerRewrites.end())
+ {
+ reader->SeekRelative(m_data->GetAddressSize());
+ return it->second;
+ }
+ // hack
+ return reader->ReadPointer();
+}
+
+
+DSCObjCProcessor::DSCObjCProcessor(BinaryNinja::BinaryView* data, SharedCache* cache, bool isBackedByDatabase) :
+ m_isBackedByDatabase(isBackedByDatabase), m_data(data), m_cache(cache)
+{
+ m_logger = LogRegistry::GetLogger("SharedCache.ObjC", m_data->GetFile()->GetSessionId());
+}
+
+void DSCObjCProcessor::ProcessObjCData(std::shared_ptr<VM> vm, std::string baseName)
+{
+ m_symbolQueue = new SymbolQueue();
+ auto addrSize = m_data->GetAddressSize();
+
+ m_typeNames.relativePtr = defineTypedef(m_data, {"rptr_t"}, Type::IntegerType(4, true));
+ auto rptr_t = Type::NamedType(m_data, m_typeNames.relativePtr);
+
+ m_typeNames.id = defineTypedef(m_data, {"id"}, Type::PointerType(addrSize, Type::VoidType()));
+ m_typeNames.sel = defineTypedef(m_data, {"SEL"}, Type::PointerType(addrSize, Type::IntegerType(1, false)));
+
+ m_typeNames.BOOL = defineTypedef(m_data, {"BOOL"}, Type::IntegerType(1, false));
+ m_typeNames.nsInteger = defineTypedef(m_data, {"NSInteger"}, Type::IntegerType(addrSize, true));
+ m_typeNames.nsuInteger = defineTypedef(m_data, {"NSUInteger"}, Type::IntegerType(addrSize, false));
+ m_typeNames.cgFloat = defineTypedef(m_data, {"CGFloat"}, Type::FloatType(addrSize));
+
+ // https://github.com/apple-oss-distributions/objc4/blob/196363c165b175ed925ef6b9b99f558717923c47/runtime/objc-abi.h
+ EnumerationBuilder imageInfoFlagBuilder;
+ imageInfoFlagBuilder.AddMemberWithValue("IsReplacement", 1 << 0);
+ imageInfoFlagBuilder.AddMemberWithValue("SupportsGC", 1 << 1);
+ imageInfoFlagBuilder.AddMemberWithValue("RequiresGC", 1 << 2);
+ imageInfoFlagBuilder.AddMemberWithValue("OptimizedByDyld", 1 << 3);
+ imageInfoFlagBuilder.AddMemberWithValue("CorrectedSynthesize", 1 << 4);
+ imageInfoFlagBuilder.AddMemberWithValue("IsSimulated", 1 << 5);
+ imageInfoFlagBuilder.AddMemberWithValue("HasCategoryClassProperties", 1 << 6);
+ imageInfoFlagBuilder.AddMemberWithValue("OptimizedByDyldClosure", 1 << 7);
+ imageInfoFlagBuilder.AddMemberWithValue("SwiftUnstableVersionMask", 0xff << 8);
+ imageInfoFlagBuilder.AddMemberWithValue("SwiftStableVersionMask", 0xFFFF << 16);
+ auto imageInfoFlagType = finalizeEnumerationBuilder(m_data, imageInfoFlagBuilder, 4, {"objc_image_info_flags"});
+ m_typeNames.imageInfoFlags = imageInfoFlagType.first;
+
+ EnumerationBuilder swiftVersionBuilder;
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion1", 1);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion1_2", 2);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion2", 3);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion3", 4);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion4", 5);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion4_1", 6); // [sic]
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion4_2", 6);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion5", 7);
+ auto swiftVersionType =
+ finalizeEnumerationBuilder(m_data, swiftVersionBuilder, 4, {"objc_image_info_swift_version"});
+ m_typeNames.imageInfoSwiftVersion = swiftVersionType.first;
+
+ StructureBuilder imageInfoBuilder;
+ imageInfoBuilder.AddMember(Type::IntegerType(4, false), "version");
+ imageInfoBuilder.AddMember(Type::NamedType(m_data, m_typeNames.imageInfoFlags), "flags");
+ auto imageInfoType = finalizeStructureBuilder(m_data, imageInfoBuilder, "objc_image_info_t");
+ m_typeNames.imageInfo = imageInfoType.first;
+
+ StructureBuilder methodEntry;
+ methodEntry.AddMember(rptr_t, "name");
+ methodEntry.AddMember(rptr_t, "types");
+ methodEntry.AddMember(rptr_t, "imp");
+ auto type = finalizeStructureBuilder(m_data, methodEntry, "objc_method_entry_t");
+ m_typeNames.methodEntry = type.first;
+
+ StructureBuilder method;
+ method.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
+ method.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "types");
+ method.AddMember(Type::PointerType(addrSize, Type::VoidType()), "imp");
+ type = finalizeStructureBuilder(m_data, method, "objc_method_t");
+ m_typeNames.method = type.first;
+
+ StructureBuilder methList;
+ methList.AddMember(Type::IntegerType(4, false), "obsolete");
+ methList.AddMember(Type::IntegerType(4, false), "count");
+ type = finalizeStructureBuilder(m_data, methList, "objc_method_list_t");
+ m_typeNames.methodList = type.first;
+
+ StructureBuilder ivarBuilder;
+ ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(4, false)), "offset");
+ ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
+ ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "type");
+ ivarBuilder.AddMember(Type::IntegerType(4, false), "alignment");
+ ivarBuilder.AddMember(Type::IntegerType(4, false), "size");
+ type = finalizeStructureBuilder(m_data, ivarBuilder, "objc_ivar_t");
+ m_typeNames.ivar = type.first;
+
+ StructureBuilder ivarList;
+ ivarList.AddMember(Type::IntegerType(4, false), "entsize");
+ ivarList.AddMember(Type::IntegerType(4, false), "count");
+ type = finalizeStructureBuilder(m_data, ivarList, "objc_ivar_list_t");
+ m_typeNames.ivarList = type.first;
+
+ StructureBuilder protocolListBuilder;
+ protocolListBuilder.AddMember(Type::IntegerType(addrSize, false), "count");
+ m_typeNames.protocolList = finalizeStructureBuilder(m_data, protocolListBuilder, "objc_protocol_list_t").first;
+
+ StructureBuilder classROBuilder;
+ classROBuilder.AddMember(Type::IntegerType(4, false), "flags");
+ classROBuilder.AddMember(Type::IntegerType(4, false), "start");
+ classROBuilder.AddMember(Type::IntegerType(4, false), "size");
+ if (addrSize == 8)
+ classROBuilder.AddMember(Type::IntegerType(4, false), "reserved");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "ivar_layout");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "methods");
+ classROBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.protocolList)), "protocols");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.ivarList)), "ivars");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "weak_ivar_layout");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "properties");
+ type = finalizeStructureBuilder(m_data, classROBuilder, "objc_class_ro_t");
+ m_typeNames.classRO = type.first;
+
+ QualifiedName classTypeName("objc_class_t");
+ auto classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
+ auto isaType = Type::PointerType(m_data->GetDefaultArchitecture(),
+ TypeBuilder::NamedType(
+ new NamedTypeReferenceBuilder(StructNamedTypeClass, "", classTypeName), m_data->GetAddressSize(), 4)
+ .Finalize());
+
+ StructureBuilder classBuilder;
+ classBuilder.AddMember(isaType, "isa");
+ classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "super");
+ classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "cache");
+ classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "vtable");
+ classBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.classRO)), "data");
+
+ auto classTypeStruct = classBuilder.Finalize();
+ auto classType = Type::StructureType(classTypeStruct);
+ auto classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
+
+ m_typeNames.cls = classQualName;
+
+ StructureBuilder categoryBuilder;
+ categoryBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "category_name");
+ categoryBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.cls)), "class");
+ categoryBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "inst_methods");
+ categoryBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "class_methods");
+ categoryBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "protocols");
+ categoryBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "properties");
+ m_typeNames.category = finalizeStructureBuilder(m_data, categoryBuilder, "objc_category_t").first;
+
+ StructureBuilder protocolBuilder;
+ protocolBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "isa");
+ protocolBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "mangledName");
+ protocolBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.protocolList)), "protocols");
+ protocolBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "instanceMethods");
+ protocolBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "classMethods");
+ protocolBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "optionalInstanceMethods");
+ protocolBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "optionalClassMethods");
+ protocolBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "instanceProperties");
+ protocolBuilder.AddMember(Type::IntegerType(4, false), "size");
+ protocolBuilder.AddMember(Type::IntegerType(4, false), "flags");
+ m_typeNames.protocol = finalizeStructureBuilder(m_data, protocolBuilder, "objc_protocol_t").first;
+
+ auto reader = VMReader(vm);
+
+ if (auto addr = m_cache->GetImageStart("/usr/lib/libobjc.A.dylib"))
+ {
+ auto header = m_cache->HeaderForAddress(addr.value());
+ uint64_t scoffs_addr = 0;
+ size_t scoffs_size = 0;
+
+ for (const auto& section : header->sections)
+ {
+ char name[17];
+ memcpy(name, section.sectname, 16);
+ name[16] = 0;
+ if (std::string(name) == "__objc_scoffs")
+ {
+ scoffs_addr = section.addr;
+ scoffs_size = section.size;
+ break;
+ }
+ }
+
+ if (scoffs_size && scoffs_addr)
+ {
+ if (scoffs_size == 0x20)
+ {
+ m_customRelativeMethodSelectorBase = reader.ReadULong(scoffs_addr);
+ }
+ else
+ {
+ m_customRelativeMethodSelectorBase = reader.ReadULong(scoffs_addr + 8);
+ }
+ m_logger->LogDebug("RelativeMethodSelector Base: 0x%llx", m_customRelativeMethodSelectorBase.value());
+ }
+ }
+
+
+ m_data->BeginBulkModifySymbols();
+ if (auto classList = m_data->GetSectionByName(baseName + "::__objc_classlist"))
+ LoadClasses(&reader, classList);
+ if (auto nonLazyClassList = m_data->GetSectionByName(baseName + "::__objc_nlclslist"))
+ LoadClasses(&reader, nonLazyClassList); // See: https://stackoverflow.com/a/15318325
+
+ GenerateClassTypes();
+ for (auto& [_, cls] : m_classes)
+ ApplyMethodTypes(cls);
+
+ if (auto catList = m_data->GetSectionByName(baseName + "::__objc_catlist")) // Do this after loading class type data.
+ LoadCategories(&reader, catList);
+ if (auto nonLazyCatList = m_data->GetSectionByName(baseName + "::__objc_nlcatlist")) // Do this after loading class type data.
+ LoadCategories(&reader, nonLazyCatList);
+ for (auto& [_, cat] : m_categories)
+ ApplyMethodTypes(cat);
+
+ if (auto protoList = m_data->GetSectionByName(baseName + "::__objc_protolist"))
+ LoadProtocols(&reader, protoList);
+
+ PostProcessObjCSections(&reader);
+
+ auto id = m_data->BeginUndoActions();
+ m_symbolQueue->Process();
+ m_data->EndBulkModifySymbols();
+ delete m_symbolQueue;
+ m_data->ForgetUndoActions(id);
+
+ auto meta = SerializeMetadata();
+ m_data->StoreMetadata("Objective-C", meta, true);
+
+ m_relocationPointerRewrites.clear();
+}
+
+
+void DSCObjCProcessor::ProcessCFStrings(std::shared_ptr<VM> vm, std::string baseName)
+{
+ m_symbolQueue = new SymbolQueue();
+ uint64_t ptrSize = m_data->GetAddressSize();
+ // https://github.com/apple/llvm-project/blob/next/clang/lib/CodeGen/CodeGenModule.cpp#L6129
+ // See also ASTContext.cpp ctrl+f __NSConstantString_tag
+
+ // The place these flags are used is unclear, along with any clear flag definitions, but they are useful for
+ // introspection
+ EnumerationBuilder __cfStringFlagBuilder;
+ __cfStringFlagBuilder.AddMemberWithValue("SwiftABI", 0b1);
+ __cfStringFlagBuilder.AddMemberWithValue("Swift4_1", 0b100);
+ // LLVM also sets 0x7c0 (0b11111000000) on both UTF8 and UTF16 strings however it is unclear what this denotes.
+ __cfStringFlagBuilder.AddMemberWithValue("UTF8", 0b1000);
+ __cfStringFlagBuilder.AddMemberWithValue("UTF16", 0b10000);
+ auto type = finalizeEnumerationBuilder(m_data, __cfStringFlagBuilder, ptrSize, {"CFStringFlag"});
+ m_typeNames.cfStringFlag = type.first;
+
+ StructureBuilder __cfStringStructBuilder;
+ __cfStringStructBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
+ __cfStringStructBuilder.AddMember(Type::NamedType(m_data, m_typeNames.cfStringFlag), "flags");
+ __cfStringStructBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(1, true)), "data");
+ __cfStringStructBuilder.AddMember(Type::IntegerType(ptrSize, false), "length");
+ type = finalizeStructureBuilder(m_data, __cfStringStructBuilder, "__NSConstantString");
+ m_typeNames.cfString = type.first;
+
+ StructureBuilder __cfStringUTF16StructBuilder;
+ __cfStringUTF16StructBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
+ __cfStringUTF16StructBuilder.AddMember(Type::NamedType(m_data, m_typeNames.cfStringFlag), "flags");
+ __cfStringUTF16StructBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(2, true)), "data");
+ __cfStringUTF16StructBuilder.AddMember(Type::IntegerType(ptrSize, false), "length");
+ type = finalizeStructureBuilder(m_data, __cfStringUTF16StructBuilder, "__NSConstantString_UTF16");
+ m_typeNames.cfStringUTF16 = type.first;
+
+ auto reader = VMReader(vm);
+ if (auto cfstrings = m_data->GetSectionByName(baseName + "::__cfstring"))
+ {
+ auto start = cfstrings->GetStart();
+ auto end = cfstrings->GetEnd();
+ auto typeWidth = Type::NamedType(m_data, m_typeNames.cfString)->GetWidth();
+ m_data->BeginBulkModifySymbols();
+ for (view_ptr_t i = start; i < end; i += typeWidth)
+ {
+ reader.Seek(i + ptrSize);
+ uint64_t flags = reader.ReadPointer();
+ auto strLoc = ReadPointerAccountingForRelocations(&reader);
+ auto size = reader.ReadPointer();
+ std::string str;
+ if (flags & 0b10000) // UTF16
+ {
+ auto data = m_data->ReadBuffer(strLoc, size * 2);
+
+ str = "";
+ for (uint64_t bufferOff = 0; bufferOff < size * 2; bufferOff += 2)
+ {
+ uint8_t* rawData = static_cast<uint8_t*>(data.GetData());
+ uint8_t* offsetAddress = rawData + bufferOff;
+ uint16_t c = *reinterpret_cast<uint16_t*>(offsetAddress);
+ if (c == 0x20)
+ str.push_back('_');
+ else if (c < 0x80)
+ str.push_back(c);
+ else
+ str.push_back('?');
+ }
+ DefineObjCSymbol(
+ DataSymbol, Type::ArrayType(Type::WideCharType(2), size + 1), "ustr_" + str, strLoc, true);
+ DefineObjCSymbol(
+ DataSymbol, Type::NamedType(m_data, m_typeNames.cfStringUTF16), "cfstr_" + str, i, true);
+ }
+ else // UTF8 / ASCII
+ {
+ reader.Seek(strLoc);
+ str = reader.ReadCString(strLoc);
+ for (auto& c : str)
+ {
+ if (c == ' ')
+ c = '_';
+ }
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), str.size() + 1), "cstr_" + str,
+ strLoc, true);
+ DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.cfString), "cfstr_" + str, i, true);
+ }
+ }
+ auto id = m_data->BeginUndoActions();
+ m_symbolQueue->Process();
+ m_data->EndBulkModifySymbols();
+ m_data->ForgetUndoActions(id);
+ }
+ delete m_symbolQueue;
+}
+
+void DSCObjCProcessor::AddRelocatedPointer(uint64_t location, uint64_t rewrite)
+{
+ m_relocationPointerRewrites[location] = rewrite;
+}
diff --git a/view/sharedcache/core/ObjC.h b/view/sharedcache/core/ObjC.h
new file mode 100644
index 00000000..40f2441b
--- /dev/null
+++ b/view/sharedcache/core/ObjC.h
@@ -0,0 +1,233 @@
+//
+// Created by kat on 5/23/23.
+//
+
+#ifndef SHAREDCACHE_OBJC_H
+#define SHAREDCACHE_OBJC_H
+
+#include <binaryninjaapi.h>
+#include "VM.h"
+#include "SharedCache.h"
+
+using namespace BinaryNinja;
+
+
+namespace DSCObjC {
+
+ // This set of structs is based on the objc4 source,
+ // however pointers have been replaced with view_ptr_t
+
+ // Used for pointers within BinaryView, primarily to make it far more clear in typedefs
+ // whether the size of a field can vary between architectures.
+ // These should _not_ be used in sizeof or direct Read() calls.
+ typedef uint64_t view_ptr_t;
+
+ typedef struct {
+ view_ptr_t name;
+ view_ptr_t types;
+ view_ptr_t imp;
+ } method_t;
+ typedef struct {
+ uint32_t name;
+ uint32_t types;
+ uint32_t imp;
+ } method_entry_t;
+ typedef struct {
+ view_ptr_t offset;
+ view_ptr_t name;
+ view_ptr_t type;
+ uint32_t alignmentRaw;
+ uint32_t size;
+ } ivar_t;
+ typedef struct {
+ view_ptr_t name;
+ view_ptr_t attributes;
+ } property_t;
+ typedef struct {
+ uint32_t entsizeAndFlags;
+ uint32_t count;
+ } method_list_t;
+ typedef struct {
+ uint32_t entsizeAndFlags;
+ uint32_t count;
+ } ivar_list_t;
+ typedef struct {
+ uint32_t entsizeAndFlags;
+ uint32_t count;
+ } property_list_t;
+ typedef struct {
+ uint64_t count;
+ } protocol_list_t;
+ typedef struct {
+ view_ptr_t isa;
+ view_ptr_t mangledName;
+ view_ptr_t protocols;
+ view_ptr_t instanceMethods;
+ view_ptr_t classMethods;
+ view_ptr_t optionalInstanceMethods;
+ view_ptr_t optionalClassMethods;
+ view_ptr_t instanceProperties;
+ uint32_t size;
+ uint32_t flags;
+ } protocol_t;
+ typedef struct {
+ uint32_t flags;
+ uint32_t instanceStart;
+ uint32_t instanceSize;
+ uint32_t reserved;
+ view_ptr_t ivarLayout;
+ view_ptr_t name;
+ view_ptr_t baseMethods;
+ view_ptr_t baseProtocols;
+ view_ptr_t ivars;
+ view_ptr_t weakIvarLayout;
+ view_ptr_t baseProperties;
+ } class_ro_t;
+ typedef struct {
+ view_ptr_t isa;
+ view_ptr_t super;
+ view_ptr_t cache;
+ view_ptr_t vtable;
+ view_ptr_t data;
+ } class_t;
+ typedef struct {
+ view_ptr_t name;
+ view_ptr_t cls;
+ view_ptr_t instanceMethods;
+ view_ptr_t classMethods;
+ view_ptr_t protocols;
+ view_ptr_t instanceProperties;
+ } category_t;
+ typedef struct {
+ view_ptr_t receiver;
+ view_ptr_t current_class;
+ } objc_super2;
+ typedef struct {
+ view_ptr_t imp;
+ view_ptr_t sel;
+ } message_ref_t;
+
+ struct Method {
+ std::string name;
+ std::string types;
+ view_ptr_t imp;
+ };
+
+ struct Ivar {
+ uint32_t offset;
+ std::string name;
+ std::string type;
+ uint32_t alignment;
+ uint32_t size;
+ };
+
+ struct Property {
+ std::string name;
+ std::string attributes;
+ };
+
+ struct ClassBase {
+ std::map<uint64_t, Method> methodList;
+ std::map<uint64_t, Ivar> ivarList;
+ };
+
+ struct Class {
+ std::string name;
+ ClassBase instanceClass;
+ ClassBase metaClass;
+
+ // Loaded by type processing
+ QualifiedName associatedName;
+ };
+
+ class Protocol {
+ public:
+ std::string name;
+ std::vector<QualifiedName> protocols;
+ ClassBase instanceMethods;
+ ClassBase classMethods;
+ ClassBase optionalInstanceMethods;
+ ClassBase optionalClassMethods;
+ };
+
+ struct QualifiedNameOrType {
+ BinaryNinja::Ref<BinaryNinja::Type> type = nullptr;
+ BinaryNinja::QualifiedName name;
+ size_t ptrCount = 0;
+ };
+
+ class DSCObjCProcessor {
+ struct Types {
+ QualifiedName relativePtr;
+ QualifiedName id;
+ QualifiedName sel;
+ QualifiedName BOOL;
+ QualifiedName nsInteger;
+ QualifiedName nsuInteger;
+ QualifiedName cgFloat;
+ QualifiedName cfStringFlag;
+ QualifiedName cfString;
+ QualifiedName cfStringUTF16;
+ QualifiedName imageInfoFlags;
+ QualifiedName imageInfoSwiftVersion;
+ QualifiedName imageInfo;
+ QualifiedName methodEntry;
+ QualifiedName method;
+ QualifiedName methodList;
+ QualifiedName classRO;
+ QualifiedName cls;
+ QualifiedName category;
+ QualifiedName protocol;
+ QualifiedName protocolList;
+ QualifiedName ivar;
+ QualifiedName ivarList;
+ } m_typeNames;
+
+ bool m_isBackedByDatabase;
+
+ BinaryView* m_data;
+ SymbolQueue* m_symbolQueue = nullptr;
+ Ref<Logger> m_logger;
+ std::map<uint64_t, Class> m_classes;
+ std::map<uint64_t, Class> m_categories;
+ std::map<uint64_t, Protocol> m_protocols;
+ std::unordered_map<uint64_t, std::string> m_selectorCache;
+ std::unordered_map<uint64_t, Method> m_localMethods;
+
+ // Required for workflow_objc type heuristics, should be removed when that is no longer a thing.
+ std::map<uint64_t, std::string> m_selRefToName;
+ std::map<uint64_t, std::vector<uint64_t>> m_selRefToImplementations;
+ std::map<uint64_t, std::vector<uint64_t>> m_selToImplementations;
+ // --
+
+
+ std::optional<uint64_t> m_customRelativeMethodSelectorBase = std::nullopt;
+ SharedCacheCore::SharedCache* m_cache;
+
+ uint64_t ReadPointerAccountingForRelocations(VMReader* reader);
+ std::unordered_map<uint64_t, uint64_t> m_relocationPointerRewrites;
+
+ static Ref<Metadata> SerializeMethod(uint64_t loc, const Method& method);
+ static Ref<Metadata> SerializeClass(uint64_t loc, const Class& cls);
+
+ Ref<Metadata> SerializeMetadata();
+ std::vector<QualifiedNameOrType> ParseEncodedType(const std::string& type);
+ void DefineObjCSymbol(BNSymbolType symbolType, QualifiedName typeName, const std::string& name, uint64_t addr, bool deferred);
+ void DefineObjCSymbol(BNSymbolType symbolType, Ref<Type> type, const std::string& name, uint64_t addr, bool deferred);
+ void ReadIvarList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start);
+ void ReadMethodList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start);
+ void LoadClasses(VMReader* reader, Ref<Section> listSection);
+ void LoadCategories(VMReader* reader, Ref<Section> listSection);
+ void LoadProtocols(VMReader* reader, Ref<Section> listSection);
+ void GenerateClassTypes();
+ bool ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod);
+ void ApplyMethodTypes(Class& cls);
+ void PostProcessObjCSections(VMReader* reader);
+ public:
+ DSCObjCProcessor(BinaryView* data, SharedCacheCore::SharedCache* cache, bool isBackedByDatabase);
+ void ProcessObjCData(std::shared_ptr<VM> vm, std::string baseName);
+ void ProcessCFStrings(std::shared_ptr<VM> vm, std::string baseName);
+ void AddRelocatedPointer(uint64_t location, uint64_t rewrite);
+ };
+}
+#endif //SHAREDCACHE_OBJC_H
diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp
new file mode 100644
index 00000000..d468ff9a
--- /dev/null
+++ b/view/sharedcache/core/SharedCache.cpp
@@ -0,0 +1,3286 @@
+//
+// Created by kat on 5/19/23.
+//
+
+#include "binaryninjaapi.h"
+
+/* ---
+ * This is the primary image loader logic for Shared Caches
+ *
+ * It is standalone code that operates on a DSCView.
+ *
+ * This has to recreate _all_ of the Mach-O View logic, but slightly differently, as everything is spicy and weird and
+ * different enough that it's not worth trying to make a shared base class.
+ *
+ * Essentially:
+ * 1. Make an API call through this class' FFI wrapper like SharedCache::GetAvailableImages(BinaryView* view)
+ * 2. That hops into core and makes a `new SharedCache(view)` core object (every time, yes) that holds a bv member
+ * variable
+ * 3. Deserializes information from that BinaryView.
+ * 4. Does this api call require reading the original cache? Then map VM pages
+ * 5. Do your stuff, save any changes to the BinaryView's metadata
+ * 6. Unmap VM pages (freeing file pointers, making sure this SharedCache object doesnt leak, etc)
+ * 7. Return
+ *
+ * Since we do everything properly (mmaped files, etc) this is not actually that slow. It can handle being done for
+ * several context menu items without visual lag.
+ *
+ *
+ * Strategy notes:
+ *
+ * While it is probably faster to map a file and read out metadata direct from disk,
+ * this results in an extreme amount of duplicate code.
+ *
+ * So, we try to pre-load a ton of metadata out of the cache and store it on the view, for developer sanity reasons.
+ *
+ * For performance reasons related to serdes speeds we cache this view metadata deserialized, per application lifetime.
+ * We could probably clear this when the file is closed?
+ *
+ * */
+
+#include "SharedCache.h"
+#include "ObjC.h"
+#include <filesystem>
+#include <utility>
+#include <fcntl.h>
+#include <memory>
+#include <chrono>
+#include <thread>
+
+
+using namespace BinaryNinja;
+using namespace SharedCacheCore;
+
+#ifdef _MSC_VER
+
+int count_trailing_zeros(uint64_t value) {
+ unsigned long index; // 32-bit long on Windows
+ if (_BitScanForward64(&index, value)) {
+ return index;
+ } else {
+ return 64; // If the value is 0, return 64.
+ }
+}
+#else
+int count_trailing_zeros(uint64_t value) {
+ return value == 0 ? 64 : __builtin_ctzll(value);
+}
+#endif
+
+struct ViewStateCacheStore {
+ SharedCache::SharedCacheFormat m_cacheFormat;
+
+ DSCViewState m_viewState;
+
+ std::unordered_map<std::string, uint64_t> m_imageStarts;
+ std::unordered_map<uint64_t, SharedCacheMachOHeader> m_headers;
+
+ std::vector<CacheImage> m_images;
+ std::vector<MemoryRegion> m_regionsMappedIntoMemory;
+
+ std::vector<BackingCache> m_backingCaches;
+ std::vector<MemoryRegion> m_stubIslandRegions; // TODO honestly both of these should be refactored into nonImageRegions. :p
+ std::vector<MemoryRegion> m_dyldDataRegions;
+ std::vector<MemoryRegion> m_nonImageRegions;
+
+ std::string m_baseFilePath;
+
+ std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>> m_exportInfos;
+ std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>> m_symbolInfos;
+};
+
+static std::recursive_mutex viewStateMutex;
+static std::unordered_map<uint64_t, ViewStateCacheStore> viewStateCache;
+
+std::mutex progressMutex;
+std::unordered_map<uint64_t, BNDSCViewLoadProgress> progressMap;
+
+struct ViewSpecificMutexes {
+ std::mutex viewOperationsThatInfluenceMetadataMutex;
+ std::mutex typeLibraryLookupAndApplicationMutex;
+};
+
+static std::unordered_map<uint64_t, ViewSpecificMutexes> viewSpecificMutexes;
+
+
+std::string base_name(std::string const& path)
+{
+ return path.substr(path.find_last_of("/\\") + 1);
+}
+
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+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;
+}
+#pragma clang diagnostic pop
+
+
+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;
+}
+
+
+uint64_t SharedCache::FastGetBackingCacheCount(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView)
+{
+ auto baseFile = MMappedFileAccessor::Open(dscView->GetFile()->GetSessionId(), dscView->GetFile()->GetOriginalFilename())->lock();
+
+ dyld_cache_header header {};
+ size_t header_size = baseFile->ReadUInt32(16);
+ baseFile->Read(&header, 0, std::min(header_size, sizeof(dyld_cache_header)));
+
+ SharedCacheFormat cacheFormat;
+
+ if (header.imagesCountOld != 0)
+ cacheFormat = RegularCacheFormat;
+
+ size_t subCacheOff = offsetof(struct dyld_cache_header, subCacheArrayOffset);
+ size_t headerEnd = header.mappingOffset;
+ if (headerEnd > subCacheOff)
+ {
+ if (header.cacheType != 2)
+ {
+ if (std::filesystem::exists(baseFile->Path() + ".01"))
+ cacheFormat = LargeCacheFormat;
+ else
+ cacheFormat = SplitCacheFormat;
+ }
+ else
+ cacheFormat = iOS16CacheFormat;
+ }
+
+ switch (cacheFormat)
+ {
+ case RegularCacheFormat:
+ {
+ return 1;
+ }
+ case LargeCacheFormat:
+ {
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = header.subCacheArrayCount;
+ return subCacheCount + 1;
+ }
+ case SplitCacheFormat:
+ {
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = header.subCacheArrayCount;
+ return subCacheCount + 2;
+ }
+ case iOS16CacheFormat:
+ {
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = header.subCacheArrayCount;
+ return subCacheCount + 2;
+ }
+ }
+}
+
+
+void SharedCache::PerformInitialLoad()
+{
+ m_logger->LogInfo("Performing initial load of Shared Cache");
+ auto path = m_dscView->GetFile()->GetOriginalFilename();
+ auto baseFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), path)->lock();
+
+ progressMutex.lock();
+ progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressLoadingCaches;
+ progressMutex.unlock();
+
+ m_baseFilePath = path;
+
+ DataBuffer sig = *baseFile->ReadBuffer(0, 4);
+ if (sig.GetLength() != 4)
+ abort();
+ const char* magic = (char*)sig.GetData();
+ if (strncmp(magic, "dyld", 4) != 0)
+ abort();
+
+ m_cacheFormat = RegularCacheFormat;
+
+ dyld_cache_header primaryCacheHeader {};
+ size_t header_size = baseFile->ReadUInt32(16);
+ baseFile->Read(&primaryCacheHeader, 0, std::min(header_size, sizeof(dyld_cache_header)));
+
+ if (primaryCacheHeader.imagesCountOld != 0)
+ m_cacheFormat = RegularCacheFormat;
+
+ size_t subCacheOff = offsetof(struct dyld_cache_header, subCacheArrayOffset);
+ size_t headerEnd = primaryCacheHeader.mappingOffset;
+ if (headerEnd > subCacheOff)
+ {
+ if (primaryCacheHeader.cacheType != 2)
+ {
+ if (std::filesystem::exists(baseFile->Path() + ".01"))
+ m_cacheFormat = LargeCacheFormat;
+ else
+ m_cacheFormat = SplitCacheFormat;
+ }
+ else
+ m_cacheFormat = iOS16CacheFormat;
+ }
+
+ switch (m_cacheFormat)
+ {
+ case RegularCacheFormat:
+ {
+ dyld_cache_mapping_info mapping {};
+ BackingCache cache;
+ cache.isPrimary = true;
+ cache.path = baseFile->Path();
+
+ for (size_t i = 0; i < primaryCacheHeader.mappingCount; i++)
+ {
+ baseFile->Read(&mapping, primaryCacheHeader.mappingOffset + (i * sizeof(mapping)), sizeof(mapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = mapping.fileOffset;
+ mapRawToAddrAndSize.second.first = mapping.address;
+ mapRawToAddrAndSize.second.second = mapping.size;
+ cache.mappings.push_back(mapRawToAddrAndSize);
+ }
+ m_backingCaches.push_back(cache);
+
+ dyld_cache_image_info img {};
+
+ for (size_t i = 0; i < primaryCacheHeader.imagesCountOld; i++)
+ {
+ baseFile->Read(&img, primaryCacheHeader.imagesOffsetOld + (i * sizeof(img)), sizeof(img));
+ auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
+ m_imageStarts[iname] = img.address;
+ }
+
+ m_logger->LogInfo("Found %d images in the shared cache", primaryCacheHeader.imagesCountOld);
+
+ if (primaryCacheHeader.branchPoolsCount)
+ {
+ std::vector<uint64_t> addresses;
+ for (size_t i = 0; i < primaryCacheHeader.branchPoolsCount; i++)
+ {
+ addresses.push_back(baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize())));
+ }
+ baseFile.reset(); // No longer needed, we're about to remap this file into VM space so we can load these.
+ uint64_t i = 0;
+ for (auto address : addresses)
+ {
+ i++;
+ auto vm = GetVMMap(true);
+ auto machoHeader = SharedCache::LoadHeaderForAddress(vm, address, "dyld_shared_cache_branch_islands_" + std::to_string(i));
+ if (machoHeader)
+ {
+ for (const auto& segment : machoHeader->segments)
+ {
+ MemoryRegion stubIslandRegion;
+ stubIslandRegion.start = segment.vmaddr;
+ stubIslandRegion.size = segment.filesize;
+ char segName[17];
+ memcpy(segName, segment.segname, 16);
+ segName[16] = 0;
+ std::string segNameStr = std::string(segName);
+ stubIslandRegion.prettyName = "dyld_shared_cache_branch_islands_" + std::to_string(i) + "::" + segNameStr;
+ stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
+ m_stubIslandRegions.push_back(stubIslandRegion);
+ }
+ }
+ }
+ }
+
+ m_logger->LogInfo("Found %d branch pools in the shared cache", primaryCacheHeader.branchPoolsCount);
+
+ break;
+ }
+ case LargeCacheFormat:
+ {
+ dyld_cache_mapping_info mapping {}; // We're going to reuse this for all of the mappings. We only need it
+ // briefly.
+
+ BackingCache cache;
+ cache.isPrimary = true;
+ cache.path = baseFile->Path();
+
+ for (size_t i = 0; i < primaryCacheHeader.mappingCount; i++)
+ {
+ baseFile->Read(&mapping, primaryCacheHeader.mappingOffset + (i * sizeof(mapping)), sizeof(mapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = mapping.fileOffset;
+ mapRawToAddrAndSize.second.first = mapping.address;
+ mapRawToAddrAndSize.second.second = mapping.size;
+ cache.mappings.push_back(mapRawToAddrAndSize);
+ }
+ m_backingCaches.push_back(cache);
+
+ dyld_cache_image_info img {};
+
+ for (size_t i = 0; i < primaryCacheHeader.imagesCount; i++)
+ {
+ baseFile->Read(&img, primaryCacheHeader.imagesOffset + (i * sizeof(img)), sizeof(img));
+ auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
+ m_imageStarts[iname] = img.address;
+ }
+
+ if (primaryCacheHeader.branchPoolsCount)
+ {
+ std::vector<uint64_t> pool {};
+ for (size_t i = 0; i < primaryCacheHeader.branchPoolsCount; i++)
+ {
+ m_imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] = baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
+ }
+ }
+
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = primaryCacheHeader.subCacheArrayCount;
+
+ dyld_subcache_entry2 _entry {};
+ std::vector<dyld_subcache_entry2> subCacheEntries;
+ for (size_t i = 0; i < subCacheCount; i++)
+ {
+ baseFile->Read(&_entry, primaryCacheHeader.subCacheArrayOffset + (i * sizeof(dyld_subcache_entry2)),
+ sizeof(dyld_subcache_entry2));
+ subCacheEntries.push_back(_entry);
+ }
+
+ baseFile.reset();
+ for (const auto& entry : subCacheEntries)
+ {
+ std::string subCachePath;
+ if (std::string(entry.fileExtension).find('.') != std::string::npos)
+ subCachePath = mainFileName + entry.fileExtension;
+ else
+ subCachePath = mainFileName + "." + entry.fileExtension;
+ auto subCacheFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), subCachePath)->lock();
+
+ dyld_cache_header subCacheHeader {};
+ uint64_t headerSize = subCacheFile->ReadUInt32(16);
+ if (headerSize > sizeof(dyld_cache_header))
+ {
+ m_logger->LogDebug("Header size is larger than expected (0x%llx), using default size (0x%llx)", headerSize,
+ sizeof(dyld_cache_header));
+ headerSize = sizeof(dyld_cache_header);
+ }
+ subCacheFile->Read(&subCacheHeader, 0, headerSize);
+
+ dyld_cache_mapping_info subCacheMapping {};
+ BackingCache subCache;
+ subCache.isPrimary = false;
+ subCache.path = subCachePath;
+
+ for (size_t j = 0; j < subCacheHeader.mappingCount; j++)
+ {
+ subCacheFile->Read(&subCacheMapping, subCacheHeader.mappingOffset + (j * sizeof(subCacheMapping)),
+ sizeof(subCacheMapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
+ mapRawToAddrAndSize.second.first = subCacheMapping.address;
+ mapRawToAddrAndSize.second.second = subCacheMapping.size;
+ subCache.mappings.push_back(mapRawToAddrAndSize);
+ }
+
+ if (subCacheHeader.mappingCount == 1 && subCacheHeader.imagesCountOld == 0 && subCacheHeader.imagesCount == 0
+ && subCacheHeader.imagesTextOffset == 0)
+ {
+ auto pathBasename = subCachePath.substr(subCachePath.find_last_of("/\\") + 1);
+ uint64_t address = subCacheMapping.address;
+ uint64_t size = subCacheMapping.size;
+ MemoryRegion stubIslandRegion;
+ stubIslandRegion.start = address;
+ stubIslandRegion.size = size;
+ stubIslandRegion.prettyName = pathBasename + "::_stubs";
+ stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
+ m_stubIslandRegions.push_back(stubIslandRegion);
+ }
+
+ m_backingCaches.push_back(subCache);
+ }
+ break;
+ }
+ case SplitCacheFormat:
+ {
+ dyld_cache_mapping_info mapping {}; // We're going to reuse this for all of the mappings. We only need it
+ // briefly.
+ BackingCache cache;
+ cache.isPrimary = true;
+ cache.path = baseFile->Path();
+
+ for (size_t i = 0; i < primaryCacheHeader.mappingCount; i++)
+ {
+ baseFile->Read(&mapping, primaryCacheHeader.mappingOffset + (i * sizeof(mapping)), sizeof(mapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = mapping.fileOffset;
+ mapRawToAddrAndSize.second.first = mapping.address;
+ mapRawToAddrAndSize.second.second = mapping.size;
+ cache.mappings.push_back(mapRawToAddrAndSize);
+ }
+ m_backingCaches.push_back(cache);
+
+ dyld_cache_image_info img {};
+
+ for (size_t i = 0; i < primaryCacheHeader.imagesCount; i++)
+ {
+ baseFile->Read(&img, primaryCacheHeader.imagesOffset + (i * sizeof(img)), sizeof(img));
+ auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
+ m_imageStarts[iname] = img.address;
+ }
+
+ if (primaryCacheHeader.branchPoolsCount)
+ {
+ std::vector<uint64_t> pool {};
+ for (size_t i = 0; i < primaryCacheHeader.branchPoolsCount; i++)
+ {
+ m_imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] = baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
+ }
+ }
+
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = primaryCacheHeader.subCacheArrayCount;
+
+ baseFile.reset();
+
+ for (size_t i = 1; i <= subCacheCount; i++)
+ {
+ auto subCachePath = mainFileName + "." + std::to_string(i);
+ auto subCacheFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), subCachePath)->lock();
+
+ dyld_cache_header subCacheHeader {};
+ uint64_t headerSize = subCacheFile->ReadUInt32(16);
+ if (headerSize > sizeof(dyld_cache_header))
+ {
+ m_logger->LogDebug("Header size is larger than expected (0x%llx), using default size (0x%llx)", headerSize,
+ sizeof(dyld_cache_header));
+ headerSize = sizeof(dyld_cache_header);
+ }
+ subCacheFile->Read(&subCacheHeader, 0, headerSize);
+
+ BackingCache subCache;
+ subCache.isPrimary = false;
+ subCache.path = subCachePath;
+
+ dyld_cache_mapping_info subCacheMapping {};
+
+ for (size_t j = 0; j < subCacheHeader.mappingCount; j++)
+ {
+ subCacheFile->Read(&subCacheMapping, subCacheHeader.mappingOffset + (j * sizeof(subCacheMapping)),
+ sizeof(subCacheMapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
+ mapRawToAddrAndSize.second.first = subCacheMapping.address;
+ mapRawToAddrAndSize.second.second = subCacheMapping.size;
+ subCache.mappings.push_back(mapRawToAddrAndSize);
+ }
+
+ m_backingCaches.push_back(subCache);
+
+ if (subCacheHeader.mappingCount == 1 && subCacheHeader.imagesCountOld == 0 && subCacheHeader.imagesCount == 0
+ && subCacheHeader.imagesTextOffset == 0)
+ {
+ auto pathBasename = subCachePath.substr(subCachePath.find_last_of("/\\") + 1);
+ uint64_t address = subCacheMapping.address;
+ uint64_t size = subCacheMapping.size;
+ MemoryRegion stubIslandRegion;
+ stubIslandRegion.start = address;
+ stubIslandRegion.size = size;
+ stubIslandRegion.prettyName = pathBasename + "::_stubs";
+ stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
+ m_stubIslandRegions.push_back(stubIslandRegion);
+ }
+ }
+
+ // Load .symbols subcache
+
+ auto subCachePath = mainFileName + ".symbols";
+ auto subCacheFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), subCachePath)->lock();
+
+ dyld_cache_header subCacheHeader {};
+ uint64_t headerSize = subCacheFile->ReadUInt32(16);
+ if (headerSize > sizeof(dyld_cache_header))
+ {
+ m_logger->LogDebug("Header size is larger than expected (0x%llx), using default size (0x%llx)", headerSize,
+ sizeof(dyld_cache_header));
+ headerSize = sizeof(dyld_cache_header);
+ }
+ subCacheFile->Read(&subCacheHeader, 0, headerSize);
+
+ dyld_cache_mapping_info subCacheMapping {};
+ BackingCache subCache;
+
+ for (size_t j = 0; j < subCacheHeader.mappingCount; j++)
+ {
+ subCacheFile->Read(&subCacheMapping, subCacheHeader.mappingOffset + (j * sizeof(subCacheMapping)),
+ sizeof(subCacheMapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
+ mapRawToAddrAndSize.second.first = subCacheMapping.address;
+ mapRawToAddrAndSize.second.second = subCacheMapping.size;
+ subCache.mappings.push_back(mapRawToAddrAndSize);
+ }
+
+ m_backingCaches.push_back(subCache);
+ break;
+ }
+ case iOS16CacheFormat:
+ {
+ dyld_cache_mapping_info mapping {};
+
+ BackingCache cache;
+ cache.isPrimary = true;
+ cache.path = baseFile->Path();
+
+ for (size_t i = 0; i < primaryCacheHeader.mappingCount; i++)
+ {
+ baseFile->Read(&mapping, primaryCacheHeader.mappingOffset + (i * sizeof(mapping)), sizeof(mapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = mapping.fileOffset;
+ mapRawToAddrAndSize.second.first = mapping.address;
+ mapRawToAddrAndSize.second.second = mapping.size;
+ cache.mappings.push_back(mapRawToAddrAndSize);
+ }
+
+ m_backingCaches.push_back(cache);
+
+ dyld_cache_image_info img {};
+
+ for (size_t i = 0; i < primaryCacheHeader.imagesCount; i++)
+ {
+ baseFile->Read(&img, primaryCacheHeader.imagesOffset + (i * sizeof(img)), sizeof(img));
+ auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
+ m_imageStarts[iname] = img.address;
+ }
+
+ if (primaryCacheHeader.branchPoolsCount)
+ {
+ std::vector<uint64_t> pool {};
+ for (size_t i = 0; i < primaryCacheHeader.branchPoolsCount; i++)
+ {
+ m_imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] = baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
+ }
+ }
+
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = primaryCacheHeader.subCacheArrayCount;
+
+ dyld_subcache_entry2 _entry {};
+
+ std::vector<dyld_subcache_entry2> subCacheEntries;
+ for (size_t i = 0; i < subCacheCount; i++)
+ {
+ baseFile->Read(&_entry, primaryCacheHeader.subCacheArrayOffset + (i * sizeof(dyld_subcache_entry2)),
+ sizeof(dyld_subcache_entry2));
+ subCacheEntries.push_back(_entry);
+ }
+
+ baseFile.reset();
+
+ for (const auto& entry : subCacheEntries)
+ {
+ std::string subCachePath;
+ if (std::string(entry.fileExtension).find('.') != std::string::npos)
+ subCachePath = mainFileName + entry.fileExtension;
+ else
+ subCachePath = mainFileName + "." + entry.fileExtension;
+
+ auto subCacheFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), subCachePath)->lock();
+
+ dyld_cache_header subCacheHeader {};
+ uint64_t headerSize = subCacheFile->ReadUInt32(16);
+ if (headerSize > sizeof(dyld_cache_header))
+ {
+ m_logger->LogDebug("Header size is larger than expected (0x%llx), using default size (0x%llx)", headerSize,
+ sizeof(dyld_cache_header));
+ headerSize = sizeof(dyld_cache_header);
+ }
+ subCacheFile->Read(&subCacheHeader, 0, headerSize);
+
+ dyld_cache_mapping_info subCacheMapping {};
+
+ BackingCache subCache;
+ subCache.isPrimary = false;
+ subCache.path = subCachePath;
+
+ for (size_t j = 0; j < subCacheHeader.mappingCount; j++)
+ {
+ subCacheFile->Read(&subCacheMapping, subCacheHeader.mappingOffset + (j * sizeof(subCacheMapping)),
+ sizeof(subCacheMapping));
+
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
+ mapRawToAddrAndSize.second.first = subCacheMapping.address;
+ mapRawToAddrAndSize.second.second = subCacheMapping.size;
+ subCache.mappings.push_back(mapRawToAddrAndSize);
+
+ if (subCachePath.find(".dylddata") != std::string::npos)
+ {
+ auto pathBasename = subCachePath.substr(subCachePath.find_last_of("/\\") + 1);
+ uint64_t address = subCacheMapping.address;
+ uint64_t size = subCacheMapping.size;
+ MemoryRegion dyldDataRegion;
+ dyldDataRegion.start = address;
+ dyldDataRegion.size = size;
+ dyldDataRegion.prettyName = pathBasename + "::_data" + std::to_string(j);
+ dyldDataRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable);
+ m_dyldDataRegions.push_back(dyldDataRegion);
+ }
+ }
+
+ m_backingCaches.push_back(subCache);
+
+ if (subCacheHeader.mappingCount == 1 && subCacheHeader.imagesCountOld == 0 && subCacheHeader.imagesCount == 0
+ && subCacheHeader.imagesTextOffset == 0)
+ {
+ auto pathBasename = subCachePath.substr(subCachePath.find_last_of("/\\") + 1);
+ uint64_t address = subCacheMapping.address;
+ uint64_t size = subCacheMapping.size;
+ MemoryRegion stubIslandRegion;
+ stubIslandRegion.start = address;
+ stubIslandRegion.size = size;
+ stubIslandRegion.prettyName = pathBasename + "::_stubs";
+ stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
+ m_stubIslandRegions.push_back(stubIslandRegion);
+ }
+ }
+
+ // Load .symbols subcache
+ try
+ {
+ auto subCachePath = mainFileName + ".symbols";
+ auto subCacheFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), subCachePath)->lock();
+ dyld_cache_header subCacheHeader {};
+ uint64_t headerSize = subCacheFile->ReadUInt32(16);
+ if (subCacheFile->ReadUInt32(16) > sizeof(dyld_cache_header))
+ {
+ m_logger->LogDebug("Header size is larger than expected, using default size");
+ headerSize = sizeof(dyld_cache_header);
+ }
+ subCacheFile->Read(&subCacheHeader, 0, headerSize);
+
+ BackingCache subCache;
+ subCache.isPrimary = false;
+ subCache.path = subCachePath;
+
+ dyld_cache_mapping_info subCacheMapping {};
+
+ for (size_t j = 0; j < subCacheHeader.mappingCount; j++)
+ {
+ subCacheFile->Read(&subCacheMapping, subCacheHeader.mappingOffset + (j * sizeof(subCacheMapping)),
+ sizeof(subCacheMapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
+ mapRawToAddrAndSize.second.first = subCacheMapping.address;
+ mapRawToAddrAndSize.second.second = subCacheMapping.size;
+ subCache.mappings.push_back(mapRawToAddrAndSize);
+ }
+
+ m_backingCaches.push_back(subCache);
+ }
+ catch (...)
+ {}
+ break;
+ }
+ }
+ baseFile.reset();
+ progressMutex.lock();
+ progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressLoadingImages;
+ progressMutex.unlock();
+
+ // We have set up enough metadata to map VM now.
+
+ auto vm = GetVMMap(true);
+ if (!vm)
+ {
+ m_logger->LogError("Failed to map VM pages for Shared Cache on initial load, this is fatal.");
+ return;
+ }
+ for (const auto &start : m_imageStarts)
+ {
+ try {
+ auto imageHeader = SharedCache::LoadHeaderForAddress(vm, start.second, start.first);
+ if (imageHeader)
+ {
+ if (imageHeader->linkeditPresent && vm->AddressIsMapped(imageHeader->linkeditSegment.vmaddr))
+ {
+ auto mapping = vm->MappingAtAddress(imageHeader->linkeditSegment.vmaddr);
+ imageHeader->exportTriePath = mapping.first.filePath;
+ }
+ m_headers[start.second] = imageHeader.value();
+ CacheImage image;
+ image.installName = start.first;
+ image.headerLocation = start.second;
+ for (const auto& segment : imageHeader->segments)
+ {
+ char segName[17];
+ memcpy(segName, segment.segname, 16);
+ segName[16] = 0;
+ MemoryRegion sectionRegion;
+ sectionRegion.prettyName = std::string(segName);
+ sectionRegion.start = segment.vmaddr;
+ sectionRegion.size = segment.vmsize;
+ 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: imageHeader->m_entryPoints)
+ if (segment.vmaddr <= entryPoint && (entryPoint < (segment.vmaddr + segment.filesize)))
+ flags |= SegmentExecutable;
+
+ sectionRegion.flags = (BNSegmentFlag)flags;
+ image.regions.push_back(sectionRegion);
+ }
+ m_images.push_back(image);
+ }
+ else
+ {
+ m_logger->LogError("Failed to load Mach-O header for %s", start.first.c_str());
+ }
+ }
+ catch (std::exception& ex)
+ {
+ m_logger->LogError("Failed to load Mach-O header for %s: %s", start.first.c_str(), ex.what());
+ }
+ }
+
+ m_logger->LogInfo("Loaded %d Mach-O headers", m_headers.size());
+
+ for (const auto& cache : m_backingCaches)
+ {
+ size_t i = 0;
+ for (const auto& mapping : cache.mappings)
+ {
+ MemoryRegion region;
+ region.start = mapping.second.first;
+ region.size = mapping.second.second;
+ region.prettyName = base_name(cache.path) + "::" + std::to_string(i);
+ // FIXME flags!!! BackingCache.mapping needs refactored to store this information!
+ region.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
+ m_nonImageRegions.push_back(region);
+ i++;
+ }
+ }
+
+ // Iterate through each Mach-O header
+ if (!m_dyldDataRegions.empty())
+ {
+ for (const auto& [headerKey, header] : m_headers)
+ {
+ // Iterate through each segment of the header
+ for (const auto& segment : header.segments)
+ {
+ uint64_t segmentStart = segment.vmaddr;
+ uint64_t segmentEnd = segmentStart + segment.vmsize;
+
+ // Iterate through each region in m_dyldDataRegions
+ for (auto it = m_dyldDataRegions.begin(); it != m_dyldDataRegions.end();)
+ {
+ uint64_t regionStart = it->start;
+ uint64_t regionSize = it->size;
+ uint64_t regionEnd = regionStart + regionSize;
+
+ // Check if the region overlaps with the segment
+ if (segmentStart < regionEnd && segmentEnd > regionStart)
+ {
+ // Split the region into two, removing the overlapped portion
+ std::vector<MemoryRegion> newRegions;
+
+ // Part before the overlap
+ if (regionStart < segmentStart)
+ {
+ MemoryRegion newRegion;
+ newRegion.start = regionStart;
+ newRegion.size = segmentStart - regionStart;
+ newRegion.prettyName = it->prettyName;
+ newRegions.push_back(newRegion);
+ }
+
+ // Part after the overlap
+ if (regionEnd > segmentEnd)
+ {
+ MemoryRegion newRegion;
+ newRegion.start = segmentEnd;
+ newRegion.size = regionEnd - segmentEnd;
+ newRegion.prettyName = it->prettyName;
+ newRegions.push_back(newRegion);
+ }
+
+ // Erase the original region
+ it = m_dyldDataRegions.erase(it);
+
+ // Insert the new regions (if any)
+ for (const auto& newRegion : newRegions)
+ {
+ it = m_dyldDataRegions.insert(it, newRegion);
+ ++it; // Move iterator to the next position
+ }
+ }
+ else
+ {
+ ++it; // No overlap, move to the next region
+ }
+ }
+ }
+ }
+ }
+
+ // Iterate through each Mach-O header
+ if (!m_nonImageRegions.empty())
+ {
+ for (const auto& [headerKey, header] : m_headers)
+ {
+ // Iterate through each segment of the header
+ for (const auto& segment : header.segments)
+ {
+ uint64_t segmentStart = segment.vmaddr;
+ uint64_t segmentEnd = segmentStart + segment.vmsize;
+
+ // Iterate through each region in m_dyldDataRegions
+ for (auto it = m_nonImageRegions.begin(); it != m_nonImageRegions.end();)
+ {
+ uint64_t regionStart = it->start;
+ uint64_t regionSize = it->size;
+ uint64_t regionEnd = regionStart + regionSize;
+
+ // Check if the region overlaps with the segment
+ if (segmentStart < regionEnd && segmentEnd > regionStart)
+ {
+ // Split the region into two, removing the overlapped portion
+ std::vector<MemoryRegion> newRegions;
+
+ // Part before the overlap
+ if (regionStart < segmentStart)
+ {
+ MemoryRegion newRegion;
+ newRegion.start = regionStart;
+ newRegion.size = segmentStart - regionStart;
+ newRegion.prettyName = it->prettyName;
+ newRegions.push_back(newRegion);
+ }
+
+ // Part after the overlap
+ if (regionEnd > segmentEnd)
+ {
+ MemoryRegion newRegion;
+ newRegion.start = segmentEnd;
+ newRegion.size = regionEnd - segmentEnd;
+ newRegion.prettyName = it->prettyName;
+ newRegions.push_back(newRegion);
+ }
+
+ // Erase the original region
+ it = m_nonImageRegions.erase(it);
+
+ // Insert the new regions (if any)
+ for (const auto& newRegion : newRegions)
+ {
+ it = m_nonImageRegions.insert(it, newRegion);
+ ++it; // Move iterator to the next position
+ }
+ }
+ else
+ {
+ ++it; // No overlap, move to the next region
+ }
+ }
+ }
+ }
+ }
+ SaveToDSCView();
+
+ m_logger->LogDebug("Finished initial load of Shared Cache");
+
+ progressMutex.lock();
+ progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressFinished;
+ progressMutex.unlock();
+}
+
+std::shared_ptr<VM> SharedCache::GetVMMap(bool mapPages)
+{
+ std::shared_ptr<VM> vm = std::make_shared<VM>(0x1000);
+
+ if (mapPages)
+ {
+ for (const auto& cache : m_backingCaches)
+ {
+ for (const auto& mapping : cache.mappings)
+ {
+ vm->MapPages(m_dscView->GetFile()->GetSessionId(), mapping.second.first, mapping.first, mapping.second.second, cache.path,
+ [this, vm=vm](std::shared_ptr<MMappedFileAccessor> mmap){
+ ParseAndApplySlideInfoForFile(mmap);
+ });
+ }
+ }
+ }
+
+ return vm;
+}
+
+
+void SharedCache::DeserializeFromRawView()
+{
+ if (m_dscView->QueryMetadata(SharedCacheMetadataTag))
+ {
+ std::unique_lock<std::recursive_mutex> viewStateCacheLock(viewStateMutex);
+ if (viewStateCache.find(m_dscView->GetFile()->GetSessionId()) != viewStateCache.end())
+ {
+ auto c = viewStateCache[m_dscView->GetFile()->GetSessionId()];
+ m_imageStarts = c.m_imageStarts;
+ m_cacheFormat = c.m_cacheFormat;
+ m_backingCaches = c.m_backingCaches;
+ m_viewState = c.m_viewState;
+ m_headers = c.m_headers;
+ m_images = c.m_images;
+ m_regionsMappedIntoMemory = c.m_regionsMappedIntoMemory;
+ m_stubIslandRegions = c.m_stubIslandRegions;
+ m_dyldDataRegions = c.m_dyldDataRegions;
+ m_nonImageRegions = c.m_nonImageRegions;
+ m_baseFilePath = c.m_baseFilePath;
+ m_exportInfos = c.m_exportInfos;
+ m_symbolInfos = c.m_symbolInfos;
+ }
+ else
+ {
+ LoadFromString(m_dscView->GetStringMetadata(SharedCacheMetadataTag));
+ }
+ }
+ else
+ {
+ m_viewState = DSCViewStateUnloaded;
+ m_images.clear(); // fixme ??
+ }
+}
+
+
+std::string to_hex_string(uint64_t value)
+{
+ std::stringstream ss;
+ ss << std::hex << value;
+ return ss.str();
+}
+
+
+void SharedCache::ParseAndApplySlideInfoForFile(std::shared_ptr<MMappedFileAccessor> file)
+{
+ if (file->SlideInfoWasApplied())
+ return;
+ std::vector<std::pair<uint64_t, uint64_t>> rewrites;
+
+ dyld_cache_header baseHeader;
+ file->Read(&baseHeader, 0, sizeof(dyld_cache_header));
+ uint64_t base = UINT64_MAX;
+ for (const auto& backingCache : m_backingCaches)
+ {
+ for (const auto& mapping : backingCache.mappings)
+ {
+ if (mapping.second.first < base)
+ {
+ base = mapping.second.first;
+ break;
+ }
+ }
+ }
+
+ std::vector<std::pair<uint64_t, MappingInfo>> mappings;
+
+ if (baseHeader.slideInfoOffsetUnused)
+ {
+ // Legacy
+
+ auto slideInfoOff = baseHeader.slideInfoOffsetUnused;
+ auto slideInfoVersion = file->ReadUInt32(slideInfoOff);
+ if (slideInfoVersion != 2 && slideInfoVersion != 3)
+ {
+ abort();
+ }
+
+ MappingInfo map;
+
+ file->Read(&map.mappingInfo, baseHeader.mappingOffset + sizeof(dyld_cache_mapping_info), sizeof(dyld_cache_mapping_info));
+ map.file = file;
+ map.slideInfoVersion = slideInfoVersion;
+ if (map.slideInfoVersion == 2)
+ file->Read(&map.slideInfoV2, slideInfoOff, sizeof(dyld_cache_slide_info_v2));
+ else if (map.slideInfoVersion == 3)
+ file->Read(&map.slideInfoV3, slideInfoOff, sizeof(dyld_cache_slide_info_v3));
+
+ mappings.emplace_back(slideInfoOff, map);
+ }
+ else
+ {
+ dyld_cache_header targetHeader;
+ file->Read(&targetHeader, 0, sizeof(dyld_cache_header));
+
+ if (targetHeader.mappingWithSlideCount == 0)
+ {
+ m_logger->LogDebug("No mappings with slide info found");
+ }
+
+ for (auto i = 0; i < targetHeader.mappingWithSlideCount; i++)
+ {
+ dyld_cache_mapping_and_slide_info mappingAndSlideInfo;
+ file->Read(&mappingAndSlideInfo, targetHeader.mappingWithSlideOffset + (i * sizeof(dyld_cache_mapping_and_slide_info)), sizeof(dyld_cache_mapping_and_slide_info));
+ if (mappingAndSlideInfo.slideInfoFileOffset)
+ {
+ MappingInfo map;
+ map.file = file;
+ if (mappingAndSlideInfo.size == 0)
+ continue;
+ map.slideInfoVersion = file->ReadUInt32(mappingAndSlideInfo.slideInfoFileOffset);
+ m_logger->LogDebug("Slide Info Version: %d", map.slideInfoVersion);
+ map.mappingInfo.address = mappingAndSlideInfo.address;
+ map.mappingInfo.size = mappingAndSlideInfo.size;
+ map.mappingInfo.fileOffset = mappingAndSlideInfo.fileOffset;
+ if (map.slideInfoVersion == 2)
+ {
+ file->Read(
+ &map.slideInfoV2, mappingAndSlideInfo.slideInfoFileOffset, sizeof(dyld_cache_slide_info_v2));
+ }
+ else if (map.slideInfoVersion == 3)
+ {
+ file->Read(
+ &map.slideInfoV3, mappingAndSlideInfo.slideInfoFileOffset, sizeof(dyld_cache_slide_info_v3));
+ map.slideInfoV3.auth_value_add = base;
+ }
+ else if (map.slideInfoVersion == 5)
+ {
+ file->Read(
+ &map.slideInfoV5, mappingAndSlideInfo.slideInfoFileOffset, sizeof(dyld_cache_slide_info5));
+ map.slideInfoV5.value_add = base;
+ }
+ else
+ {
+ m_logger->LogError("Unknown slide info version: %d", map.slideInfoVersion);
+ continue;
+ }
+
+ uint64_t slideInfoOffset = mappingAndSlideInfo.slideInfoFileOffset;
+ mappings.emplace_back(slideInfoOffset, map);
+ m_logger->LogDebug("Filename: %s", file->Path().c_str());
+ m_logger->LogDebug("Slide Info Offset: 0x%llx", slideInfoOffset);
+ m_logger->LogDebug("Mapping Address: 0x%llx", map.mappingInfo.address);
+ m_logger->LogDebug("Slide Info v", map.slideInfoVersion);
+ }
+ }
+ }
+
+ if (mappings.empty())
+ {
+ m_logger->LogDebug("No slide info found");
+ file->SetSlideInfoWasApplied(true);
+ return;
+ }
+
+ for (const auto& [off, mapping] : mappings)
+ {
+ m_logger->LogDebug("Slide Info Version: %d", mapping.slideInfoVersion);
+ uint64_t pageStartsOffset = off;
+ uint64_t pageStartCount;
+ uint64_t pageSize;
+ std::vector<uint64_t> pageStartFileOffsets;
+
+ if (mapping.slideInfoVersion == 2)
+ {
+ pageStartsOffset += mapping.slideInfoV2.page_starts_offset;
+ pageStartCount = mapping.slideInfoV2.page_starts_count;
+ pageSize = mapping.slideInfoV2.page_size;
+
+ pageStartFileOffsets.reserve(pageStartCount);
+ size_t cursor = pageStartsOffset;
+
+ for (size_t i = 0; i < pageStartCount; i++)
+ {
+ try
+ {
+ uint64_t pageStart = mapping.file->ReadUShort(cursor) * 4;
+ cursor += 2;
+ if (pageStart & 0x4000)
+ continue;
+ pageStartFileOffsets.push_back(mapping.mappingInfo.fileOffset + (pageSize * i) + pageStart);
+ }
+ catch (MappingReadException& ex)
+ {
+
+ }
+ }
+ }
+ else if (mapping.slideInfoVersion == 3) {
+ // Slide Info Version 3 Logic
+ pageStartsOffset += sizeof(dyld_cache_slide_info_v3);
+ pageStartCount = mapping.slideInfoV3.page_starts_count;
+ pageSize = mapping.slideInfoV3.page_size;
+
+ pageStartFileOffsets.reserve(pageStartCount);
+ size_t cursor = pageStartsOffset;
+
+ for (size_t i = 0; i < pageStartCount; i++)
+ {
+ try
+ {
+ uint64_t pageStart = mapping.file->ReadUShort(cursor);
+ cursor += 2;
+ if (pageStart & 0x4000)
+ continue;
+ pageStartFileOffsets.push_back(mapping.mappingInfo.fileOffset + (pageSize * i) + pageStart);
+ }
+ catch (MappingReadException& ex)
+ {
+ m_logger->LogError("Failed to read slide info at 0x%llx\n", cursor);
+ }
+ }
+ }
+ else if (mapping.slideInfoVersion == 5)
+ {
+ pageStartsOffset += sizeof(dyld_cache_slide_info5);
+ pageStartCount = mapping.slideInfoV5.page_starts_count;
+ m_logger->LogDebug("Page Start Count: %d", pageStartCount);
+ pageSize = mapping.slideInfoV5.page_size;
+ auto cursor = pageStartsOffset;
+ for (size_t i = 0; i < pageStartCount; i++)
+ {
+ try
+ {
+ uint64_t pageStart = mapping.file->ReadUShort(cursor);
+ cursor += 2;
+ if (pageStart == DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE)
+ continue;
+ pageStartFileOffsets.push_back(mapping.mappingInfo.fileOffset + (pageSize * i) + pageStart);
+ }
+ catch (MappingReadException& ex)
+ {
+ m_logger->LogError("Failed to read slide info at 0x%llx\n", cursor);
+ }
+ }
+ }
+ if (pageStartFileOffsets.empty())
+ {
+ m_logger->LogDebug("No page start file offsets found");
+ }
+ for (auto pageStart : pageStartFileOffsets)
+ {
+ if (mapping.slideInfoVersion == 2)
+ {
+ auto deltaMask = mapping.slideInfoV2.delta_mask;
+ auto valueMask = ~deltaMask;
+ auto valueAdd = mapping.slideInfoV2.value_add;
+
+ auto deltaShift = count_trailing_zeros(deltaMask) - 2;
+
+ uint64_t delta = 1;
+ uint64_t loc = pageStart;
+ while (delta != 0)
+ {
+ try {
+ uint64_t rawValue = file->ReadULong(loc);
+ delta = (rawValue & deltaMask) >> deltaShift;
+ uint64_t value = (rawValue & valueMask);
+ if (valueMask != 0)
+ {
+ value += valueAdd;
+ }
+ rewrites.emplace_back(loc, value);
+ }
+ catch (MappingReadException& ex)
+ {
+ m_logger->LogError("Failed to read slide info at 0x%llx\n", loc);
+ delta = 0;
+ }
+ loc += delta;
+ }
+ }
+ else if (mapping.slideInfoVersion == 3)
+ {
+ uint64_t loc = pageStart;
+ uint64_t delta = 1;
+ while (delta != 0)
+ {
+ dyld_cache_slide_pointer3 slideInfo;
+ try
+ {
+ file->Read(&slideInfo, loc, 8);
+ delta = slideInfo.plain.offsetToNextPointer * 8;
+
+ if (slideInfo.auth.authenticated)
+ {
+ uint64_t value = slideInfo.auth.offsetFromSharedCacheBase;
+ value += mapping.slideInfoV3.auth_value_add;
+ rewrites.emplace_back(loc, value);
+ }
+ else
+ {
+ uint64_t value51 = slideInfo.plain.pointerValue;
+ uint64_t top8Bits = value51 & 0x0007F80000000000;
+ uint64_t bottom43Bits = value51 & 0x000007FFFFFFFFFF;
+ uint64_t value = (uint64_t)top8Bits << 13 | bottom43Bits;
+ rewrites.emplace_back(loc, value);
+ }
+ loc += delta;
+ }
+ catch (MappingReadException& ex)
+ {
+ m_logger->LogError("Failed to read slide info at 0x%llx\n", loc);
+ delta = 0;
+ }
+ }
+ }
+ else if (mapping.slideInfoVersion == 5)
+ {
+ uint64_t loc = pageStart;
+ uint64_t delta = 1;
+ while (delta != 0)
+ {
+ dyld_cache_slide_pointer5 slideInfo;
+ try
+ {
+ file->Read(&slideInfo, loc, 8);
+ delta = slideInfo.regular.next * 8;
+ if (slideInfo.auth.auth)
+ {
+ uint64_t value = slideInfo.auth.runtimeOffset + mapping.slideInfoV5.value_add;
+ rewrites.emplace_back(loc, value);
+ }
+ else
+ {
+ uint64_t value = base + slideInfo.regular.runtimeOffset;
+ rewrites.emplace_back(loc, value);
+ }
+ loc += delta;
+ }
+ catch (MappingReadException& ex)
+ {
+ m_logger->LogError("Failed to read slide info at 0x%llx\n", loc);
+ delta = 0;
+ }
+ }
+ }
+ }
+ }
+ for (const auto& [loc, value] : rewrites)
+ {
+ file->WritePointer(loc, value);
+#ifdef SLIDEINFO_DEBUG_TAGS
+ uint64_t vmAddr = 0;
+ {
+ for (uint64_t off = baseHeader.mappingOffset; off < baseHeader.mappingOffset + baseHeader.mappingCount * sizeof(dyld_cache_mapping_info); off += sizeof(dyld_cache_mapping_info))
+ {
+ dyld_cache_mapping_info mapping;
+ file->Read(&mapping, off, sizeof(dyld_cache_mapping_info));
+ if (mapping.fileOffset <= loc && loc < mapping.fileOffset + mapping.size)
+ {
+ vmAddr = mapping.address + (loc - mapping.fileOffset);
+ break;
+ }
+ }
+ }
+ Ref<TagType> type = m_dscView->GetTagType("slideinfo");
+ if (!type)
+ {
+ m_dscView->AddTagType(new TagType(m_dscView, "slideinfo", "\xF0\x9F\x9A\x9E"));
+ type = m_dscView->GetTagType("slideinfo");
+ }
+ m_dscView->AddAutoDataTag(vmAddr, new Tag(type, "0x" + to_hex_string(file->ReadULong(loc)) + " => 0x" + to_hex_string(value)));
+#endif
+ }
+ m_logger->LogDebug("Applied slide info for %s (0x%llx rewrites)", file->Path().c_str(), rewrites.size());
+ file->SetSlideInfoWasApplied(true);
+}
+
+
+SharedCache::SharedCache(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView) : m_dscView(dscView)
+{
+ sharedCacheReferences++;
+ INIT_SHAREDCACHE_API_OBJECT()
+ m_logger = LogRegistry::GetLogger("SharedCache", dscView->GetFile()->GetSessionId());
+ DeserializeFromRawView();
+ if (m_viewState == DSCViewStateUnloaded)
+ {
+ if (m_viewState == DSCViewStateUnloaded)
+ {
+ // maybe we are getting called from UI thread. probably. do this on a separate thread just in case.
+ // TODO `wait` argument
+ WorkerEnqueue([this]() {
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+ try {
+ PerformInitialLoad();
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to perform initial load of Shared Cache");
+ }
+
+ for (const auto& [_, header] : m_headers)
+ {
+ if (header.installName.find("libsystem_c.dylib") != std::string::npos)
+ {
+ lock.unlock();
+ m_logger->LogInfo("Loading core libsystem_c.dylib library");
+ LoadImageWithInstallName(header.installName);
+ lock.lock();
+ break ;
+ }
+ }
+
+ m_viewState = DSCViewStateLoaded;
+ SaveToDSCView();
+ });
+ }
+ }
+ else
+ {
+ progressMutex.lock();
+ progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressFinished;
+ progressMutex.unlock();
+ }
+}
+
+SharedCache::~SharedCache() {
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+ sharedCacheReferences--;
+}
+
+SharedCache* SharedCache::GetFromDSCView(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView)
+{
+ try {
+ return new SharedCache(dscView);
+ }
+ catch (...)
+ {
+ return nullptr;
+ }
+}
+
+std::optional<uint64_t> SharedCache::GetImageStart(std::string installName)
+{
+ for (const auto& [name, start] : m_imageStarts)
+ {
+ if (name == installName)
+ {
+ return start;
+ }
+ }
+ return {};
+}
+
+std::optional<SharedCacheMachOHeader> SharedCache::HeaderForAddress(uint64_t address)
+{
+ // We _could_ mark each page with the image start? :grimacing emoji:
+ // But that'd require mapping pages :grimacing emoji: :grimacing emoji:
+ // There's not really any other hacks that could make this faster, that I can think of...
+ for (const auto& [start, header] : m_headers)
+ {
+ for (const auto& segment : header.segments)
+ {
+ if (segment.vmaddr <= address && segment.vmaddr + segment.vmsize > address)
+ {
+ return header;
+ }
+ }
+ }
+ return {};
+}
+
+std::string SharedCache::NameForAddress(uint64_t address)
+{
+ for (const auto& stubIsland : m_stubIslandRegions)
+ {
+ if (stubIsland.start <= address && stubIsland.start + stubIsland.size > address)
+ {
+ return stubIsland.prettyName;
+ }
+ }
+ for (const auto& dyldData : m_dyldDataRegions)
+ {
+ if (dyldData.start <= address && dyldData.start + dyldData.size > address)
+ {
+ return dyldData.prettyName;
+ }
+ }
+ for (const auto& nonImageRegion : m_nonImageRegions)
+ {
+ if (nonImageRegion.start <= address && nonImageRegion.start + nonImageRegion.size > address)
+ {
+ return nonImageRegion.prettyName;
+ }
+ }
+ if (auto header = HeaderForAddress(address))
+ {
+ for (const auto& section : header->sections)
+ {
+ if (section.addr <= address && section.addr + section.size > address)
+ {
+ char sectionName[17];
+ strncpy(sectionName, section.sectname, 16);
+ sectionName[16] = '\0';
+ return header->identifierPrefix + "::" + sectionName;
+ }
+ }
+ }
+ return "";
+}
+
+std::string SharedCache::ImageNameForAddress(uint64_t address)
+{
+ if (auto header = HeaderForAddress(address))
+ {
+ return header->identifierPrefix;
+ }
+ return "";
+}
+
+bool SharedCache::LoadImageContainingAddress(uint64_t address)
+{
+ for (const auto& [start, header] : m_headers)
+ {
+ for (const auto& segment : header.segments)
+ {
+ if (segment.vmaddr <= address && segment.vmaddr + segment.vmsize > address)
+ {
+ return LoadImageWithInstallName(header.installName);
+ }
+ }
+ }
+
+ return false;
+}
+
+bool SharedCache::LoadSectionAtAddress(uint64_t address)
+{
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+ DeserializeFromRawView();
+ auto vm = GetVMMap();
+ if (!vm)
+ {
+ m_logger->LogError("Failed to map VM pages for Shared Cache.");
+ return false;
+ }
+
+ SharedCacheMachOHeader targetHeader;
+ CacheImage* targetImage = nullptr;
+ MemoryRegion* targetSegment = nullptr;
+
+ for (auto& image : m_images)
+ {
+ for (auto& region : image.regions)
+ {
+ if (region.start <= address && region.start + region.size > address)
+ {
+ targetHeader = m_headers[image.headerLocation];
+ targetImage = &image;
+ targetSegment = &region;
+ break;
+ }
+ }
+ if (targetSegment)
+ break;
+ }
+ if (!targetSegment)
+ {
+ for (auto& stubIsland : m_stubIslandRegions)
+ {
+ if (stubIsland.start <= address && stubIsland.start + stubIsland.size > address)
+ {
+ if (stubIsland.loaded)
+ {
+ return true;
+ }
+ m_logger->LogInfo("Loading stub island %s @ 0x%llx", stubIsland.prettyName.c_str(), stubIsland.start);
+ auto targetFile = vm->MappingAtAddress(stubIsland.start).first.fileAccessor->lock();
+ ParseAndApplySlideInfoForFile(targetFile);
+ auto reader = VMReader(vm);
+ auto buff = reader.ReadBuffer(stubIsland.start, stubIsland.size);
+ auto rawViewEnd = m_dscView->GetParentView()->GetEnd();
+
+ auto name = stubIsland.prettyName;
+ m_dscView->GetParentView()->GetParentView()->WriteBuffer(
+ m_dscView->GetParentView()->GetParentView()->GetEnd(), *buff);
+ m_dscView->GetParentView()->AddAutoSegment(rawViewEnd, stubIsland.size, rawViewEnd, stubIsland.size,
+ SegmentReadable | SegmentExecutable);
+ m_dscView->AddUserSegment(stubIsland.start, stubIsland.size, rawViewEnd, stubIsland.size,
+ SegmentReadable | SegmentExecutable);
+ m_dscView->AddUserSection(name, stubIsland.start, stubIsland.size, ReadOnlyCodeSectionSemantics);
+ m_dscView->WriteBuffer(stubIsland.start, *buff);
+
+ stubIsland.loaded = true;
+
+ stubIsland.rawViewOffsetIfLoaded = rawViewEnd;
+
+ m_regionsMappedIntoMemory.push_back(stubIsland);
+
+ SaveToDSCView();
+
+ m_dscView->AddAnalysisOption("linearsweep");
+ m_dscView->UpdateAnalysis();
+
+ return true;
+ }
+ }
+
+ for (auto& dyldData : m_dyldDataRegions)
+ {
+ if (dyldData.start <= address && dyldData.start + dyldData.size > address)
+ {
+ if (dyldData.loaded)
+ {
+ return true;
+ }
+ m_logger->LogInfo("Loading dyld data %s", dyldData.prettyName.c_str());
+ auto targetFile = vm->MappingAtAddress(dyldData.start).first.fileAccessor->lock();
+ ParseAndApplySlideInfoForFile(targetFile);
+ auto reader = VMReader(vm);
+ auto buff = reader.ReadBuffer(dyldData.start, dyldData.size);
+ auto rawViewEnd = m_dscView->GetParentView()->GetEnd();
+
+ auto name = dyldData.prettyName;
+ m_dscView->GetParentView()->GetParentView()->WriteBuffer(
+ m_dscView->GetParentView()->GetParentView()->GetEnd(), *buff);
+ m_dscView->GetParentView()->WriteBuffer(rawViewEnd, *buff);
+ m_dscView->GetParentView()->AddAutoSegment(rawViewEnd, dyldData.size, rawViewEnd, dyldData.size,
+ SegmentReadable);
+ m_dscView->AddUserSegment(dyldData.start, dyldData.size, rawViewEnd, dyldData.size, SegmentReadable);
+ m_dscView->AddUserSection(name, dyldData.start, dyldData.size, ReadOnlyCodeSectionSemantics);
+ m_dscView->WriteBuffer(dyldData.start, *buff);
+
+ dyldData.loaded = true;
+ dyldData.rawViewOffsetIfLoaded = rawViewEnd;
+
+ m_regionsMappedIntoMemory.push_back(dyldData);
+
+ SaveToDSCView();
+
+ m_dscView->AddAnalysisOption("linearsweep");
+ m_dscView->UpdateAnalysis();
+
+ return true;
+ }
+ }
+
+ for (auto& region : m_nonImageRegions)
+ {
+ if (region.start <= address && region.start + region.size > address)
+ {
+ if (region.loaded)
+ {
+ return true;
+ }
+ m_logger->LogInfo("Loading non-image region %s", region.prettyName.c_str());
+ auto targetFile = vm->MappingAtAddress(region.start).first.fileAccessor->lock();
+ ParseAndApplySlideInfoForFile(targetFile);
+ auto reader = VMReader(vm);
+ auto buff = reader.ReadBuffer(region.start, region.size);
+ auto rawViewEnd = m_dscView->GetParentView()->GetEnd();
+
+ auto name = region.prettyName;
+ m_dscView->GetParentView()->GetParentView()->WriteBuffer(
+ m_dscView->GetParentView()->GetParentView()->GetEnd(), *buff);
+ m_dscView->GetParentView()->WriteBuffer(rawViewEnd, *buff);
+ m_dscView->GetParentView()->AddAutoSegment(rawViewEnd, region.size, rawViewEnd, region.size, region.flags);
+ m_dscView->AddUserSegment(region.start, region.size, rawViewEnd, region.size, region.flags);
+ m_dscView->AddUserSection(name, region.start, region.size, ReadOnlyCodeSectionSemantics);
+ m_dscView->WriteBuffer(region.start, *buff);
+
+ region.loaded = true;
+ region.rawViewOffsetIfLoaded = rawViewEnd;
+
+ m_regionsMappedIntoMemory.push_back(region);
+
+ SaveToDSCView();
+
+ m_dscView->AddAnalysisOption("linearsweep");
+ m_dscView->UpdateAnalysis();
+
+ return true;
+ }
+ }
+
+ m_logger->LogError("Failed to find a segment containing address 0x%llx", address);
+ return false;
+ }
+
+ auto id = m_dscView->BeginUndoActions();
+ auto rawViewEnd = m_dscView->GetParentView()->GetEnd();
+ auto reader = VMReader(vm);
+
+ m_logger->LogDebug("Partial loading image %s", targetHeader.installName.c_str());
+
+ auto targetFile = vm->MappingAtAddress(targetSegment->start).first.fileAccessor->lock();
+ ParseAndApplySlideInfoForFile(targetFile);
+ auto buff = reader.ReadBuffer(targetSegment->start, targetSegment->size);
+ m_dscView->GetParentView()->GetParentView()->WriteBuffer(
+ m_dscView->GetParentView()->GetParentView()->GetEnd(), *buff);
+ m_dscView->GetParentView()->WriteBuffer(rawViewEnd, *buff);
+ m_dscView->GetParentView()->AddAutoSegment(
+ rawViewEnd, targetSegment->size, rawViewEnd, targetSegment->size, SegmentReadable);
+ m_dscView->AddUserSegment(
+ targetSegment->start, targetSegment->size, rawViewEnd, targetSegment->size, targetSegment->flags);
+ m_dscView->WriteBuffer(targetSegment->start, *buff);
+
+ targetSegment->loaded = true;
+ targetSegment->rawViewOffsetIfLoaded = rawViewEnd;
+
+ m_regionsMappedIntoMemory.push_back(*targetSegment);
+
+ SaveToDSCView();
+
+ if (!targetSegment->headerInitialized)
+ {
+ SharedCache::InitializeHeader(m_dscView, vm.get(), targetHeader, {targetSegment});
+ }
+
+ m_dscView->AddAnalysisOption("linearsweep");
+ m_dscView->UpdateAnalysis();
+
+ m_dscView->CommitUndoActions(id);
+
+ return true;
+}
+
+bool SharedCache::LoadImageWithInstallName(std::string installName)
+{
+ auto settings = m_dscView->GetLoadSettings(VIEW_NAME);
+
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+
+ DeserializeFromRawView();
+ m_logger->LogInfo("Loading image %s", installName.c_str());
+
+ auto vm = GetVMMap();
+ CacheImage* targetImage = nullptr;
+
+ for (auto& cacheImage : m_images)
+ {
+ if (cacheImage.installName == installName)
+ {
+ targetImage = &cacheImage;
+ break;
+ }
+ }
+
+ auto header = m_headers[targetImage->headerLocation];
+
+ auto id = m_dscView->BeginUndoActions();
+ m_viewState = DSCViewStateLoadedWithImages;
+
+ auto reader = VMReader(vm);
+ reader.Seek(targetImage->headerLocation);
+
+ std::vector<MemoryRegion*> regionsToLoad;
+
+ for (auto& region : targetImage->regions)
+ {
+ bool allowLoadingLinkedit = false;
+ if (settings && settings->Contains("loader.dsc.allowLoadingLinkeditSegments"))
+ allowLoadingLinkedit = settings->Get<bool>("loader.dsc.allowLoadingLinkeditSegments", m_dscView);
+ if ((region.prettyName.find("__LINKEDIT") != std::string::npos) && !allowLoadingLinkedit)
+ continue;
+
+ if (region.loaded)
+ {
+ m_logger->LogDebug("Skipping region %s as it is already loaded.", region.prettyName.c_str());
+ continue;
+ }
+
+ auto targetFile = vm->MappingAtAddress(region.start).first.fileAccessor->lock();
+ ParseAndApplySlideInfoForFile(targetFile);
+
+ auto rawViewEnd = m_dscView->GetParentView()->GetEnd();
+
+ auto buff = reader.ReadBuffer(region.start, region.size);
+ m_dscView->GetParentView()->GetParentView()->WriteBuffer(rawViewEnd, *buff);
+ m_dscView->GetParentView()->WriteBuffer(rawViewEnd, *buff);
+
+ region.loaded = true;
+ region.rawViewOffsetIfLoaded = rawViewEnd;
+
+ m_regionsMappedIntoMemory.push_back(region);
+
+ m_dscView->GetParentView()->AddAutoSegment(rawViewEnd, region.size, rawViewEnd, region.size, region.flags);
+ m_dscView->AddUserSegment(region.start, region.size, rawViewEnd, region.size, region.flags);
+ m_dscView->WriteBuffer(region.start, *buff);
+
+ regionsToLoad.push_back(&region);
+ }
+
+ if (regionsToLoad.empty())
+ {
+ m_logger->LogWarn("No regions to load for image %s", installName.c_str());
+ return false;
+ }
+
+ std::unique_lock<std::mutex> typelibLock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].typeLibraryLookupAndApplicationMutex);
+ auto typeLib = m_dscView->GetTypeLibrary(header.installName);
+
+ if (!typeLib)
+ {
+ auto typeLibs = m_dscView->GetDefaultPlatform()->GetTypeLibrariesByName(header.installName);
+ if (!typeLibs.empty())
+ {
+ typeLib = typeLibs[0];
+ m_dscView->AddTypeLibrary(typeLib);
+ m_logger->LogInfo("shared-cache: adding type library for '%s': %s (%s)",
+ targetImage->installName.c_str(), typeLib->GetName().c_str(), typeLib->GetGuid().c_str());
+ }
+ }
+ typelibLock.unlock();
+
+ SaveToDSCView();
+
+ auto h = SharedCache::LoadHeaderForAddress(vm, targetImage->headerLocation, installName);
+ if (!h.has_value())
+ {
+ return false;
+ }
+
+ std::vector<MemoryRegion*> regions;
+ for (auto& region : regionsToLoad)
+ {
+ regions.push_back(region);
+ }
+
+ SharedCache::InitializeHeader(m_dscView, vm.get(), *h, regions);
+
+ try
+ {
+ auto objc = std::make_unique<DSCObjC::DSCObjCProcessor>(m_dscView, this, false);
+
+ bool processCFStrings = true;
+ bool processObjCMetadata = true;
+ if (settings && settings->Contains("loader.dsc.processCFStrings"))
+ processCFStrings = settings->Get<bool>("loader.dsc.processCFStrings", m_dscView);
+ if (settings && settings->Contains("loader.dsc.processObjC"))
+ processObjCMetadata = settings->Get<bool>("loader.dsc.processObjC", m_dscView);
+ if (processObjCMetadata)
+ objc->ProcessObjCData(vm, h->identifierPrefix);
+ if (processCFStrings)
+ objc->ProcessCFStrings(vm, h->identifierPrefix);
+ }
+ catch (const std::exception& ex)
+ {
+ m_logger->LogWarn("Error processing ObjC data: %s", ex.what());
+ }
+ catch (...)
+ {
+ m_logger->LogWarn("Error processing ObjC data");
+ }
+
+ m_dscView->AddAnalysisOption("linearsweep");
+ m_dscView->UpdateAnalysis();
+
+ m_dscView->CommitUndoActions(id);
+
+ return true;
+}
+
+std::optional<SharedCacheMachOHeader> SharedCache::LoadHeaderForAddress(std::shared_ptr<VM> vm, uint64_t address, std::string installName)
+{
+ SharedCacheMachOHeader header;
+
+ header.textBase = address;
+ header.installName = installName;
+ header.identifierPrefix = base_name(installName);
+
+ std::string errorMsg;
+ // address is a Raw file offset
+ VMReader reader(vm);
+ reader.Seek(address);
+
+ header.ident.magic = reader.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
+ {
+ return {};
+ }
+
+ reader.SetEndianness(endianness);
+ header.ident.cputype = reader.Read32();
+ header.ident.cpusubtype = reader.Read32();
+ header.ident.filetype = reader.Read32();
+ header.ident.ncmds = reader.Read32();
+ header.ident.sizeofcmds = reader.Read32();
+ header.ident.flags = reader.Read32();
+ if ((header.ident.cputype & MachOABIMask) == MachOABI64) // address size == 8
+ {
+ header.ident.reserved = reader.Read32();
+ }
+ header.loadCommandOffset = reader.GetOffset();
+
+ bool first = true;
+ // Parse segment commands
+ try
+ {
+ for (size_t i = 0; i < header.ident.ncmds; i++)
+ {
+ // BNLogInfo("of 0x%llx", reader.GetOffset());
+ 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;
+ if (load.cmdsize < sizeof(load_command))
+ return {};
+
+ switch (load.cmd)
+ {
+ case LC_MAIN:
+ {
+ uint64_t entryPoint = reader.Read64();
+ header.entryPoints.push_back({entryPoint, true});
+ (void)reader.Read64(); // Stack start
+ break;
+ }
+ case LC_SEGMENT: // map the 32bit version to 64 bits
+ segment64.cmd = LC_SEGMENT_64;
+ reader.Read(&segment64.segname, 16);
+ segment64.vmaddr = reader.Read32();
+ segment64.vmsize = reader.Read32();
+ segment64.fileoff = reader.Read32();
+ 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;
+ }
+ }
+ 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);
+ }
+ 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);
+ break;
+ case LC_SEGMENT_64:
+ segment64.cmd = LC_SEGMENT_64;
+ reader.Read(&segment64.segname, 16);
+ segment64.vmaddr = reader.Read64();
+ segment64.vmsize = reader.Read64();
+ segment64.fileoff = reader.Read64();
+ segment64.filesize = reader.Read64();
+ segment64.maxprot = reader.Read32();
+ segment64.initprot = reader.Read32();
+ segment64.nsects = reader.Read32();
+ segment64.flags = reader.Read32();
+ if (strncmp(segment64.segname, "__LINKEDIT", 10) == 0)
+ {
+ header.linkeditSegment = segment64;
+ header.linkeditPresent = true;
+ }
+ 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;
+ }
+ }
+ 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);
+ }
+
+ 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);
+ break;
+ case LC_ROUTINES: // map the 32bit version to 64bits
+ 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;
+ break;
+ case LC_ROUTINES_64:
+ 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;
+ break;
+ case LC_FUNCTION_STARTS:
+ header.functionStarts.funcoff = reader.Read32();
+ header.functionStarts.funcsize = reader.Read32();
+ header.functionStartsPresent = true;
+ break;
+ case LC_SYMTAB:
+ header.symtab.symoff = reader.Read32();
+ header.symtab.nsyms = reader.Read32();
+ header.symtab.stroff = reader.Read32();
+ header.symtab.strsize = reader.Read32();
+ break;
+ case LC_DYSYMTAB:
+ 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();
+ header.dysymPresent = true;
+ break;
+ case LC_DYLD_CHAINED_FIXUPS:
+ header.chainedFixups.dataoff = reader.Read32();
+ header.chainedFixups.datasize = reader.Read32();
+ header.chainedFixupsPresent = true;
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ 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:
+ 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();
+ 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);
+ std::string libname = reader.ReadCString(reader.GetOffset());
+ 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 j = 0; (i < header.buildVersion.ntools) && (j < 10); j++)
+ {
+ 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:
+ {
+ throw ReadException();
+ }
+ 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);
+ }
+
+ 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);
+ }
+ }
+ catch (ReadException&)
+ {
+ return {};
+ }
+
+ return header;
+}
+
+void SharedCache::InitializeHeader(
+ Ref<BinaryView> view, VM* vm, SharedCacheMachOHeader header, std::vector<MemoryRegion*> regionsToLoad)
+{
+
+ Ref<Settings> settings = view->GetLoadSettings(VIEW_NAME);
+ bool applyFunctionStarts = true;
+ if (settings && settings->Contains("loader.dsc.processFunctionStarts"))
+ applyFunctionStarts = settings->Get<bool>("loader.dsc.processFunctionStarts", view);
+
+ for (size_t i = 0; i < header.sections.size(); i++)
+ {
+ bool skip = false;
+ for (const auto& region : regionsToLoad)
+ {
+ if (header.sections[i].addr >= region->start && header.sections[i].addr < region->start + region->size)
+ {
+ if (region->headerInitialized)
+ {
+ skip = true;
+ }
+ break;
+ }
+ }
+ if (!header.sections[i].size || skip)
+ continue;
+
+ std::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;
+
+ view->AddUserSection(header.sectionNames[i], header.sections[i].addr, header.sections[i].size, semantics,
+ type, header.sections[i].align);
+ }
+
+ auto typeLib = view->GetTypeLibrary(header.installName);
+
+ BinaryReader virtualReader(view);
+
+ bool applyHeaderTypes = false;
+ for (const auto& region : regionsToLoad)
+ {
+ if (header.textBase >= region->start && header.textBase < region->start + region->size)
+ {
+ if (!region->headerInitialized)
+ applyHeaderTypes = true;
+ break;
+ }
+ }
+ if (applyHeaderTypes)
+ {
+ view->DefineDataVariable(header.textBase, Type::NamedType(view, QualifiedName("mach_header_64")));
+ view->DefineAutoSymbol(
+ new Symbol(DataSymbol, "__macho_header::" + header.identifierPrefix, header.textBase, LocalBinding));
+
+ try
+ {
+ virtualReader.Seek(header.textBase + sizeof(mach_header_64));
+ size_t sectionNum = 0;
+ for (size_t i = 0; i < header.ident.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:
+ {
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("segment_command")));
+ virtualReader.SeekRelative(5 * 8);
+ size_t numSections = virtualReader.Read32();
+ virtualReader.SeekRelative(4);
+ for (size_t j = 0; j < numSections; j++)
+ {
+ view->DefineDataVariable(
+ virtualReader.GetOffset(), Type::NamedType(view, QualifiedName("section")));
+ view->DefineUserSymbol(new Symbol(DataSymbol,
+ "__macho_section::" + header.identifierPrefix + "_[" + std::to_string(sectionNum++) + "]",
+ virtualReader.GetOffset(), LocalBinding));
+ virtualReader.SeekRelative((8 * 8) + 4);
+ }
+ break;
+ }
+ case LC_SEGMENT_64:
+ {
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("segment_command_64")));
+ virtualReader.SeekRelative(7 * 8);
+ size_t numSections = virtualReader.Read32();
+ virtualReader.SeekRelative(4);
+ for (size_t j = 0; j < numSections; j++)
+ {
+ view->DefineDataVariable(
+ virtualReader.GetOffset(), Type::NamedType(view, QualifiedName("section_64")));
+ view->DefineUserSymbol(new Symbol(DataSymbol,
+ "__macho_section_64::" + header.identifierPrefix + "_[" + std::to_string(sectionNum++) + "]",
+ virtualReader.GetOffset(), LocalBinding));
+ virtualReader.SeekRelative(10 * 8);
+ }
+ break;
+ }
+ case LC_SYMTAB:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("symtab")));
+ break;
+ case LC_DYSYMTAB:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("dysymtab")));
+ break;
+ case LC_UUID:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("uuid")));
+ break;
+ case LC_ID_DYLIB:
+ case LC_LOAD_DYLIB:
+ case LC_REEXPORT_DYLIB:
+ case LC_LOAD_WEAK_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("dylib_command")));
+ if (load.cmdsize - 24 <= 150)
+ view->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:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("linkedit_data")));
+ break;
+ case LC_ENCRYPTION_INFO:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("encryption_info")));
+ break;
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_IPHONEOS:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("version_min")));
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("dyld_info")));
+ break;
+ default:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("load_command")));
+ break;
+ }
+
+ view->DefineAutoSymbol(new Symbol(DataSymbol,
+ "__macho_load_command::" + header.identifierPrefix + "_[" + std::to_string(i) + "]", curOffset,
+ LocalBinding));
+ virtualReader.Seek(nextOffset);
+ }
+ }
+ catch (ReadException&)
+ {
+ LogError("Error when applying Mach-O header types at %" PRIx64, header.textBase);
+ }
+ }
+
+ if (applyFunctionStarts && header.functionStartsPresent && header.linkeditPresent && vm->AddressIsMapped(header.linkeditSegment.vmaddr))
+ {
+ auto funcStarts =
+ vm->MappingAtAddress(header.linkeditSegment.vmaddr)
+ .first.fileAccessor->lock()
+ ->ReadBuffer(header.functionStarts.funcoff, header.functionStarts.funcsize);
+ size_t i = 0;
+ uint64_t curfunc = header.textBase;
+ uint64_t curOffset;
+
+ while (i < header.functionStarts.funcsize)
+ {
+ curOffset = readLEB128(*funcStarts, header.functionStarts.funcsize, i);
+ bool addFunction = false;
+ for (const auto& region : regionsToLoad)
+ {
+ if (curfunc >= region->start && curfunc < region->start + region->size)
+ {
+ if (!region->headerInitialized)
+ addFunction = true;
+ }
+ }
+ // LogError("0x%llx, 0x%llx", header.textBase, curOffset);
+ if (curOffset == 0 || !addFunction)
+ continue;
+ curfunc += curOffset;
+ uint64_t target = curfunc;
+ Ref<Platform> targetPlatform = view->GetDefaultPlatform();
+ view->AddFunctionForAnalysis(targetPlatform, target);
+ }
+ }
+
+ view->BeginBulkModifySymbols();
+ if (header.symtab.symoff != 0 && header.linkeditPresent && vm->AddressIsMapped(header.linkeditSegment.vmaddr))
+ {
+ // Mach-O View symtab processing with
+ // a ton of stuff cut out so it can work
+
+ auto reader = vm->MappingAtAddress(header.linkeditSegment.vmaddr).first.fileAccessor->lock();
+ // auto symtab = reader->ReadBuffer(header.symtab.symoff, header.symtab.nsyms * sizeof(nlist_64));
+ auto strtab = reader->ReadBuffer(header.symtab.stroff, header.symtab.strsize);
+ nlist_64 sym;
+ memset(&sym, 0, sizeof(sym));
+ auto N_TYPE = 0xE; // idk
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> symbolInfos;
+ for (size_t i = 0; i < header.symtab.nsyms; i++)
+ {
+ reader->Read(&sym, header.symtab.symoff + i * sizeof(nlist_64), sizeof(nlist_64));
+ if (sym.n_strx >= header.symtab.strsize || ((sym.n_type & N_TYPE) == N_INDR))
+ continue;
+
+ std::string symbol((char*)strtab->GetDataAt(sym.n_strx));
+ // BNLogError("%s: 0x%llx", symbol.c_str(), sym.n_value);
+ if (symbol == "<redacted>")
+ continue;
+
+ 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())
+ {}
+ else if ((sym.n_type & N_TYPE) == N_ABS)
+ {}
+ else if ((sym.n_type & 0x1))
+ {
+ type = ExternalSymbol;
+ }
+ else
+ continue;
+
+ for (auto s : header.sections)
+ {
+ if (s.addr < sym.n_value)
+ {
+ if (s.addr + s.size > sym.n_value)
+ {
+ flags = s.flags;
+ }
+ }
+ }
+
+ 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 symbolObj = new Symbol(type, symbol, sym.n_value, GlobalBinding);
+ if (type == FunctionSymbol)
+ {
+ Ref<Platform> targetPlatform = view->GetDefaultPlatform();
+ view->AddFunctionForAnalysis(targetPlatform, sym.n_value);
+ }
+ if (typeLib)
+ {
+ auto _type = m_dscView->ImportTypeLibraryObject(typeLib, {symbolObj->GetFullName()});
+ if (_type)
+ {
+ view->DefineAutoSymbolAndVariableOrFunction(view->GetDefaultPlatform(), symbolObj, _type);
+ }
+ else
+ view->DefineAutoSymbol(symbolObj);
+ }
+ else
+ view->DefineAutoSymbol(symbolObj);
+ symbolInfos.push_back({sym.n_value, {type, symbol}});
+ }
+ m_symbolInfos[header.textBase] = symbolInfos;
+ }
+
+ if (header.exportTriePresent && header.linkeditPresent && vm->AddressIsMapped(header.linkeditSegment.vmaddr))
+ {
+ auto symbols = SharedCache::ParseExportTrie(vm->MappingAtAddress(header.linkeditSegment.vmaddr).first.fileAccessor->lock(), header);
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> exportMapping;
+ for (const auto& symbol : symbols)
+ {
+ exportMapping.push_back({symbol->GetAddress(), {symbol->GetType(), symbol->GetRawName()}});
+ if (typeLib)
+ {
+ auto type = m_dscView->ImportTypeLibraryObject(typeLib, {symbol->GetFullName()});
+
+ if (type)
+ {
+ view->DefineAutoSymbolAndVariableOrFunction(view->GetDefaultPlatform(), symbol, type);
+ }
+ else
+ view->DefineAutoSymbol(symbol);
+
+ if (view->GetAnalysisFunction(view->GetDefaultPlatform(), symbol->GetAddress()))
+ {
+ auto func = view->GetAnalysisFunction(view->GetDefaultPlatform(), symbol->GetAddress());
+ if (symbol->GetFullName() == "_objc_msgSend")
+ {
+ func->SetHasVariableArguments(false);
+ }
+ else if (symbol->GetFullName().find("_objc_retain_x") != std::string::npos || symbol->GetFullName().find("_objc_release_x") != std::string::npos)
+ {
+ auto x = symbol->GetFullName().rfind("x");
+ auto num = symbol->GetFullName().substr(x + 1);
+
+ std::vector<BinaryNinja::FunctionParameter> callTypeParams;
+ auto cc = m_dscView->GetDefaultArchitecture()->GetCallingConventionByName("apple-arm64-objc-fast-arc-" + num);
+
+ callTypeParams.push_back({"obj", m_dscView->GetTypeByName({ "id" }), true, BinaryNinja::Variable()});
+
+ auto funcType = BinaryNinja::Type::FunctionType(m_dscView->GetTypeByName({ "id" }), cc, callTypeParams);
+ func->SetUserType(funcType);
+ }
+ }
+ }
+ else
+ view->DefineAutoSymbol(symbol);
+ }
+ m_exportInfos[header.textBase] = exportMapping;
+ }
+ view->EndBulkModifySymbols();
+
+ for (auto region : regionsToLoad)
+ {
+ region->headerInitialized = true;
+ }
+}
+
+struct ExportNode
+{
+ std::string text;
+ uint64_t offset;
+ uint64_t flags;
+};
+
+
+void SharedCache::ReadExportNode(std::vector<Ref<Symbol>>& symbolList, SharedCacheMachOHeader& header, DataBuffer& buffer, uint64_t textBase,
+ 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 = m_dscView->GetAnalysisFunctionsForAddress(textBase + imageOffset).size() ? FunctionSymbol : DataSymbol;
+ {
+ if (!currentText.empty() && textBase + imageOffset)
+ {
+ uint32_t flags;
+ BNSymbolType type;
+ for (auto s : header.sections)
+ {
+ if (s.addr < textBase + imageOffset)
+ {
+ if (s.addr + s.size > textBase + imageOffset)
+ {
+ flags = s.flags;
+ }
+ }
+ }
+ 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 EXPORT_TRIE_DEBUG
+ // BNLogInfo("export: %s -> 0x%llx", n.text.c_str(), image.baseAddress + n.offset);
+#endif
+ auto sym = new Symbol(type, currentText, textBase + imageOffset);
+ symbolList.push_back(sym);
+ }
+ }
+ }
+ }
+ 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(symbolList, header, buffer, textBase, currentText + childText, next, endGuard);
+ }
+}
+
+
+std::vector<Ref<Symbol>> SharedCache::ParseExportTrie(std::shared_ptr<MMappedFileAccessor> linkeditFile, SharedCacheMachOHeader header)
+{
+ std::vector<Ref<Symbol>> symbols;
+ try
+ {
+ auto reader = linkeditFile;
+
+ std::vector<ExportNode> nodes;
+
+ DataBuffer* buffer = reader->ReadBuffer(header.exportTrie.dataoff, header.exportTrie.datasize);
+ ReadExportNode(symbols, header, *buffer, header.textBase, "", 0, header.exportTrie.datasize);
+ }
+ catch (std::exception& e)
+ {
+ BNLogError("Failed to load Export Trie");
+ }
+ return symbols;
+}
+
+std::vector<std::string> SharedCache::GetAvailableImages()
+{
+ std::vector<std::string> installNames;
+ for (const auto& header : m_headers)
+ {
+ installNames.push_back(header.second.installName);
+ }
+ return installNames;
+}
+
+
+std::vector<std::pair<std::string, Ref<Symbol>>> SharedCache::LoadAllSymbolsAndWait()
+{
+ std::unique_lock<std::mutex> initialLoadBlock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+
+ std::vector<std::pair<std::string, Ref<Symbol>>> symbols;
+ for (const auto& img : m_images)
+ {
+ auto header = HeaderForAddress(img.headerLocation);
+ std::shared_ptr<MMappedFileAccessor> mapping;
+ try {
+ mapping = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), header->exportTriePath)->lock();
+ }
+ catch (...)
+ {
+ m_logger->LogWarn("Serious Error: Failed to open export trie for %s", header->installName.c_str());
+ continue;
+ }
+ auto exportList = SharedCache::ParseExportTrie(mapping, *header);
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> exportMapping;
+ for (const auto& sym : exportList)
+ {
+ exportMapping.push_back({sym->GetAddress(), {sym->GetType(), sym->GetRawName()}});
+ symbols.push_back({img.installName, sym});
+ }
+ m_exportInfos[header->textBase] = exportMapping;
+ }
+
+ SaveToDSCView();
+
+ return symbols;
+}
+
+
+std::string SharedCache::SerializedImageHeaderForAddress(uint64_t address)
+{
+ auto header = HeaderForAddress(address);
+ if (header)
+ {
+ return header->AsString();
+ }
+ return "";
+}
+
+
+std::string SharedCache::SerializedImageHeaderForName(std::string name)
+{
+ auto header = HeaderForAddress(m_imageStarts[name]);
+ if (header)
+ {
+ return header->AsString();
+ }
+ return "";
+}
+
+
+void SharedCache::FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis)
+{
+ std::string prefix = "";
+ if (symbolLocation != targetLocation)
+ prefix = "j_";
+ if (auto preexistingSymbol = m_dscView->GetSymbolByAddress(targetLocation))
+ {
+ if (preexistingSymbol->GetFullName().find("j_") != std::string::npos)
+ return;
+ }
+ auto id = m_dscView->BeginUndoActions();
+ if (auto loadedSymbol = m_dscView->GetSymbolByAddress(symbolLocation))
+ {
+ if (m_dscView->GetAnalysisFunction(m_dscView->GetDefaultPlatform(), targetLocation))
+ m_dscView->DefineUserSymbol(new Symbol(FunctionSymbol, prefix + loadedSymbol->GetFullName(), targetLocation));
+ else
+ m_dscView->DefineUserSymbol(new Symbol(loadedSymbol->GetType(), prefix + loadedSymbol->GetFullName(), targetLocation));
+ }
+ else if (auto sym = m_dscView->GetSymbolByAddress(symbolLocation))
+ {
+ if (m_dscView->GetAnalysisFunction(m_dscView->GetDefaultPlatform(), targetLocation))
+ m_dscView->DefineUserSymbol(new Symbol(FunctionSymbol, prefix + sym->GetFullName(), targetLocation));
+ else
+ m_dscView->DefineUserSymbol(new Symbol(sym->GetType(), prefix + sym->GetFullName(), targetLocation));
+ }
+ m_dscView->ForgetUndoActions(id);
+ auto header = HeaderForAddress(symbolLocation);
+ if (header)
+ {
+ std::shared_ptr<MMappedFileAccessor> mapping;
+ try {
+ mapping = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), header->exportTriePath)->lock();
+ }
+ catch (...)
+ {
+ m_logger->LogWarn("Serious Error: Failed to open export trie for %s", header->installName.c_str());
+ return;
+ }
+ auto exportList = SharedCache::ParseExportTrie(mapping, *header);
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> exportMapping;
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].typeLibraryLookupAndApplicationMutex);
+ auto typeLib = m_dscView->GetTypeLibrary(header->installName);
+ if (!typeLib)
+ {
+ auto typeLibs = m_dscView->GetDefaultPlatform()->GetTypeLibrariesByName(header->installName);
+ if (!typeLibs.empty())
+ {
+ typeLib = typeLibs[0];
+ m_dscView->AddTypeLibrary(typeLib);
+ }
+ }
+ lock.unlock();
+ id = m_dscView->BeginUndoActions();
+ m_dscView->BeginBulkModifySymbols();
+ for (const auto& sym : exportList)
+ {
+ exportMapping.push_back({sym->GetAddress(), {sym->GetType(), sym->GetRawName()}});
+ if (sym->GetAddress() == symbolLocation)
+ {
+ if (auto func = m_dscView->GetAnalysisFunction(m_dscView->GetDefaultPlatform(), targetLocation))
+ {
+ m_dscView->DefineUserSymbol(
+ new Symbol(FunctionSymbol, prefix + sym->GetFullName(), targetLocation));
+
+ if (typeLib)
+ if (auto type = m_dscView->ImportTypeLibraryObject(typeLib, {sym->GetFullName()}))
+ func->SetUserType(type);
+ }
+ else
+ {
+ m_dscView->DefineUserSymbol(
+ new Symbol(sym->GetType(), prefix + sym->GetFullName(), targetLocation));
+
+ if (typeLib)
+ if (auto type = m_dscView->ImportTypeLibraryObject(typeLib, {sym->GetFullName()}))
+ m_dscView->DefineUserDataVariable(targetLocation, type);
+ }
+ if (triggerReanalysis)
+ {
+ auto func = m_dscView->GetAnalysisFunction(m_dscView->GetDefaultPlatform(), targetLocation);
+ if (func)
+ func->Reanalyze();
+ }
+ break;
+ }
+ }
+ {
+ std::unique_lock<std::mutex> _lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+ m_exportInfos[header->textBase] = exportMapping;
+ }
+ m_dscView->EndBulkModifySymbols();
+ m_dscView->ForgetUndoActions(id);
+ }
+}
+
+
+bool SharedCache::SaveToDSCView()
+{
+ if (m_dscView)
+ {
+ auto data = AsMetadata();
+ m_dscView->StoreMetadata(SharedCacheMetadataTag, data);
+ m_dscView->GetParentView()->GetParentView()->StoreMetadata(SharedCacheMetadataTag, data);
+ std::unique_lock<std::recursive_mutex> viewStateCacheLock(viewStateMutex);
+ ViewStateCacheStore c;
+ c.m_imageStarts = m_imageStarts;
+ c.m_cacheFormat = m_cacheFormat;
+ c.m_backingCaches = m_backingCaches;
+ c.m_viewState = m_viewState;
+ c.m_headers = m_headers;
+ c.m_images = m_images;
+ c.m_regionsMappedIntoMemory = m_regionsMappedIntoMemory;
+ c.m_stubIslandRegions = m_stubIslandRegions;
+ c.m_dyldDataRegions = m_dyldDataRegions;
+ c.m_nonImageRegions = m_nonImageRegions;
+ c.m_baseFilePath = m_baseFilePath;
+ c.m_exportInfos = m_exportInfos;
+ c.m_symbolInfos = m_symbolInfos;
+ viewStateCache[m_dscView->GetFile()->GetSessionId()] = c;
+
+ return true;
+ }
+ return false;
+}
+std::vector<MemoryRegion> SharedCache::GetMappedRegions() const
+{
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+ return m_regionsMappedIntoMemory;
+}
+
+extern "C"
+{
+ BNSharedCache* BNGetSharedCache(BNBinaryView* data)
+ {
+ if (!data)
+ return nullptr;
+
+ Ref<BinaryView> view = new BinaryView(BNNewViewReference(data));
+ if (auto cache = SharedCache::GetFromDSCView(view))
+ {
+ cache->AddAPIRef();
+ return cache->GetAPIObject();
+ }
+
+ return nullptr;
+ }
+
+ BNSharedCache* BNNewSharedCacheReference(BNSharedCache* cache)
+ {
+ if (!cache->object)
+ return nullptr;
+
+ cache->object->AddAPIRef();
+ return cache;
+ }
+
+ void BNFreeSharedCacheReference(BNSharedCache* cache)
+ {
+ if (!cache->object)
+ return;
+
+ cache->object->ReleaseAPIRef();
+ }
+
+ bool BNDSCViewLoadImageWithInstallName(BNSharedCache* cache, char* name)
+ {
+ std::string imageName = std::string(name);
+ BNFreeString(name);
+
+ if (cache->object)
+ return cache->object->LoadImageWithInstallName(imageName);
+
+ return false;
+ }
+
+ bool BNDSCViewLoadSectionAtAddress(BNSharedCache* cache, uint64_t addr)
+ {
+ if (cache->object)
+ {
+ return cache->object->LoadSectionAtAddress(addr);
+ }
+
+ return false;
+ }
+
+ bool BNDSCViewLoadImageContainingAddress(BNSharedCache* cache, uint64_t address)
+ {
+ if (cache->object)
+ {
+ return cache->object->LoadImageContainingAddress(address);
+ }
+
+ return false;
+ }
+
+ char** BNDSCViewGetInstallNames(BNSharedCache* cache, size_t* count)
+ {
+ if (cache->object)
+ {
+ auto value = cache->object->GetAvailableImages();
+ *count = value.size();
+
+ std::vector<const char*> cstrings;
+ for (size_t i = 0; i < value.size(); i++)
+ {
+ cstrings.push_back(value[i].c_str());
+ }
+ return BNAllocStringList(cstrings.data(), cstrings.size());
+ }
+ *count = 0;
+ return nullptr;
+ }
+
+ BNDSCSymbolRep* BNDSCViewLoadAllSymbolsAndWait(BNSharedCache* cache, size_t* count)
+ {
+ if (cache->object)
+ {
+ auto value = cache->object->LoadAllSymbolsAndWait();
+ *count = value.size();
+
+ BNDSCSymbolRep* symbols = (BNDSCSymbolRep*)malloc(sizeof(BNDSCSymbolRep) * value.size());
+ for (size_t i = 0; i < value.size(); i++)
+ {
+ symbols[i].address = value[i].second->GetAddress();
+ symbols[i].name = BNAllocString(value[i].second->GetRawName().c_str());
+ symbols[i].image = BNAllocString(value[i].first.c_str());
+ }
+ return symbols;
+ }
+ *count = 0;
+ return nullptr;
+ }
+
+ void BNDSCViewFreeSymbols(BNDSCSymbolRep* symbols, size_t count)
+ {
+ for (size_t i = 0; i < count; i++)
+ {
+ BNFreeString(symbols[i].name);
+ BNFreeString(symbols[i].image);
+ }
+ delete symbols;
+ }
+
+ char* BNDSCViewGetNameForAddress(BNSharedCache* cache, uint64_t address)
+ {
+ if (cache->object)
+ {
+ return BNAllocString(cache->object->NameForAddress(address).c_str());
+ }
+
+ return nullptr;
+ }
+
+ char* BNDSCViewGetImageNameForAddress(BNSharedCache* cache, uint64_t address)
+ {
+ if (cache->object)
+ {
+ return BNAllocString(cache->object->ImageNameForAddress(address).c_str());
+ }
+
+ return nullptr;
+ }
+
+ uint64_t BNDSCViewLoadedImageCount(BNSharedCache* cache)
+ {
+ // FIXME?
+ return 0;
+ }
+
+ BNDSCViewState BNDSCViewGetState(BNSharedCache* cache)
+ {
+ if (cache->object)
+ {
+ return (BNDSCViewState)cache->object->State();
+ }
+
+ return BNDSCViewState::Unloaded;
+ }
+
+
+ BNDSCMappedMemoryRegion* BNDSCViewGetLoadedRegions(BNSharedCache* cache, size_t* count)
+ {
+ if (cache->object)
+ {
+ auto regions = cache->object->GetMappedRegions();
+ *count = regions.size();
+ BNDSCMappedMemoryRegion* mappedRegions = (BNDSCMappedMemoryRegion*)malloc(sizeof(BNDSCMappedMemoryRegion) * regions.size());
+ for (size_t i = 0; i < regions.size(); i++)
+ {
+ mappedRegions[i].vmAddress = regions[i].start;
+ mappedRegions[i].size = regions[i].size;
+ mappedRegions[i].name = BNAllocString(regions[i].prettyName.c_str());
+ }
+ return mappedRegions;
+ }
+ *count = 0;
+ return nullptr;
+ }
+
+ void BNDSCViewFreeLoadedRegions(BNDSCMappedMemoryRegion* images, size_t count)
+ {
+ for (size_t i = 0; i < count; i++)
+ {
+ BNFreeString(images[i].name);
+ }
+ delete images;
+ }
+
+
+ BNDSCBackingCache* BNDSCViewGetBackingCaches(BNSharedCache* cache, size_t* count)
+ {
+ BNDSCBackingCache* caches = nullptr;
+
+ if (cache->object)
+ {
+ auto viewCaches = cache->object->BackingCaches();
+ *count = viewCaches.size();
+ caches = (BNDSCBackingCache*)malloc(sizeof(BNDSCBackingCache) * viewCaches.size());
+ for (size_t i = 0; i < viewCaches.size(); i++)
+ {
+ caches[i].path = BNAllocString(viewCaches[i].path.c_str());
+ caches[i].isPrimary = viewCaches[i].isPrimary;
+
+ BNDSCBackingCacheMapping* mappings;
+ mappings = (BNDSCBackingCacheMapping*)malloc(sizeof(BNDSCBackingCacheMapping) * viewCaches[i].mappings.size());
+
+ size_t j = 0;
+ for (const auto& [fileOffset, mapping] : viewCaches[i].mappings)
+ {
+ mappings[j].vmAddress = mapping.first;
+ mappings[j].size = mapping.second;
+ mappings[j].fileOffset = fileOffset;
+ j++;
+ }
+ caches[i].mappings = mappings;
+ caches[i].mappingCount = viewCaches[i].mappings.size();
+ }
+ }
+
+ return caches;
+ }
+
+ void BNDSCViewFreeBackingCaches(BNDSCBackingCache* caches, size_t count)
+ {
+ for (size_t i = 0; i < count; i++)
+ {
+ delete[] caches[i].mappings;
+ BNFreeString(caches[i].path);
+ }
+ delete[] caches;
+ }
+
+ void BNDSCFindSymbolAtAddressAndApplyToAddress(BNSharedCache* cache, uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis)
+ {
+ if (cache->object)
+ {
+ cache->object->FindSymbolAtAddrAndApplyToAddr(symbolLocation, targetLocation, triggerReanalysis);
+ }
+ }
+
+ BNDSCImage* BNDSCViewGetAllImages(BNSharedCache* cache, size_t* count)
+ {
+ if (cache->object)
+ {
+ auto vm = cache->object->GetVMMap(true);
+ auto viewImageHeaders = cache->object->AllImageHeaders();
+ *count = viewImageHeaders.size();
+ BNDSCImage* images = (BNDSCImage*)malloc(sizeof(BNDSCImage) * viewImageHeaders.size());
+ size_t i = 0;
+ for (const auto& [baseAddress, header] : viewImageHeaders)
+ {
+ images[i].name = BNAllocString(header.installName.c_str());
+ images[i].headerAddress = baseAddress;
+ images[i].mappingCount = header.sections.size();
+ images[i].mappings = (BNDSCImageMemoryMapping*)malloc(sizeof(BNDSCImageMemoryMapping) * header.sections.size());
+ for (size_t j = 0; j < header.sections.size(); j++)
+ {
+ images[i].mappings[j].rawViewOffset = header.sections[j].offset;
+ images[i].mappings[j].vmAddress = header.sections[j].addr;
+ images[i].mappings[j].size = header.sections[j].size;
+ images[i].mappings[j].name = BNAllocString(header.sectionNames[j].c_str());
+ images[i].mappings[j].filePath = BNAllocString(vm->MappingAtAddress(header.sections[j].addr).first.filePath.c_str());
+ }
+ i++;
+ }
+ return images;
+ }
+ *count = 0;
+ return nullptr;
+ }
+
+ void BNDSCViewFreeAllImages(BNDSCImage* images, size_t count)
+ {
+ for (size_t i = 0; i < count; i++)
+ {
+ for (size_t j = 0; j < images[i].mappingCount; j++)
+ {
+ BNFreeString(images[i].mappings[j].name);
+ BNFreeString(images[i].mappings[j].filePath);
+ }
+ delete[] images[i].mappings;
+ BNFreeString(images[i].name);
+ }
+ delete[] images;
+ }
+
+ char* BNDSCViewGetImageHeaderForAddress(BNSharedCache* cache, uint64_t address)
+ {
+ if (cache->object)
+ {
+ auto header = cache->object->SerializedImageHeaderForAddress(address);
+ return BNAllocString(header.c_str());
+ }
+
+ return nullptr;
+ }
+
+ char* BNDSCViewGetImageHeaderForName(BNSharedCache* cache, char* name)
+ {
+ std::string imageName = std::string(name);
+ BNFreeString(name);
+ if (cache->object)
+ {
+ auto header = cache->object->SerializedImageHeaderForName(imageName);
+ return BNAllocString(header.c_str());
+ }
+
+ return nullptr;
+ }
+
+ BNDSCMemoryUsageInfo BNDSCViewGetMemoryUsageInfo()
+ {
+ BNDSCMemoryUsageInfo info;
+ info.mmapRefs = mmapCount.load();
+ info.sharedCacheRefs = sharedCacheReferences.load();
+ return info;
+ }
+
+ BNDSCViewLoadProgress BNDSCViewGetLoadProgress(uint64_t sessionID)
+ {
+ progressMutex.lock();
+ if (progressMap.find(sessionID) == progressMap.end())
+ {
+ progressMutex.unlock();
+ return LoadProgressNotStarted;
+ }
+ auto progress = progressMap[sessionID];
+ progressMutex.unlock();
+ return progress;
+ }
+
+ uint64_t BNDSCViewFastGetBackingCacheCount(BNBinaryView* data)
+ {
+ Ref<BinaryView> view = new BinaryView(BNNewViewReference(data));
+ return SharedCache::FastGetBackingCacheCount(view);
+ }
+}
+
+[[maybe_unused]] DSCViewType* g_dscViewType;
+[[maybe_unused]] DSCRawViewType* g_dscRawViewType;
+
+void InitDSCViewType()
+{
+ MMappedFileAccessor::InitialVMSetup();
+ std::atexit(VMShutdown);
+
+ static DSCRawViewType rawType;
+ BinaryViewType::Register(&rawType);
+ static DSCViewType type;
+ BinaryViewType::Register(&type);
+ g_dscViewType = &type;
+ g_dscRawViewType = &rawType;
+}
diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h
new file mode 100644
index 00000000..2f099773
--- /dev/null
+++ b/view/sharedcache/core/SharedCache.h
@@ -0,0 +1,1197 @@
+//
+// Created by kat on 5/19/23.
+//
+
+#include <binaryninjaapi.h>
+#include "DSCView.h"
+#include "VM.h"
+#include "view/macho/machoview.h"
+#include "MetadataSerializable.hpp"
+#include "../api/sharedcachecore.h"
+
+#ifndef SHAREDCACHE_SHAREDCACHE_H
+#define SHAREDCACHE_SHAREDCACHE_H
+
+DECLARE_SHAREDCACHE_API_OBJECT(BNSharedCache, SharedCache);
+
+namespace SharedCacheCore {
+
+ enum DSCViewState
+ {
+ DSCViewStateUnloaded,
+ DSCViewStateLoaded,
+ DSCViewStateLoadedWithImages,
+ };
+
+
+ const std::string SharedCacheMetadataTag = "SHAREDCACHE-SharedCacheData";
+
+ struct MemoryRegion : public MetadataSerializable
+ {
+ std::string prettyName;
+ uint64_t start;
+ uint64_t size;
+ bool loaded = false;
+ uint64_t rawViewOffsetIfLoaded = 0;
+ bool headerInitialized = false;
+ BNSegmentFlag flags;
+
+ void Store() override
+ {
+ MSS(prettyName);
+ MSS(start);
+ MSS(size);
+ MSS(loaded);
+ MSS(rawViewOffsetIfLoaded);
+ MSS_CAST(flags, uint64_t);
+ }
+
+ void Load() override
+ {
+ MSL(prettyName);
+ MSL(start);
+ MSL(size);
+ MSL(loaded);
+ MSL(rawViewOffsetIfLoaded);
+ MSL_CAST(flags, uint64_t, BNSegmentFlag);
+ }
+ };
+
+ struct CacheImage : public MetadataSerializable
+ {
+ std::string installName;
+ uint64_t headerLocation;
+ std::vector<MemoryRegion> regions;
+
+ void Store() override
+ {
+ MSS(installName);
+ MSS(headerLocation);
+ rapidjson::Value key("regions", m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& region : regions)
+ {
+ bArr.PushBack(rapidjson::Value(region.AsString().c_str(), m_activeContext.allocator), m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Load() override
+ {
+ MSL(installName);
+ MSL(headerLocation);
+ auto bArr = m_activeDeserContext.doc["regions"].GetArray();
+ regions.clear();
+ for (auto& region : bArr)
+ {
+ MemoryRegion r;
+ r.LoadFromString(region.GetString());
+ regions.push_back(r);
+ }
+ }
+ };
+
+ struct BackingCache : public MetadataSerializable
+ {
+ std::string path;
+ bool isPrimary = false;
+ std::vector<std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> mappings;
+
+ void Store() override
+ {
+ MSS(path);
+ MSS(isPrimary);
+ MSS(mappings);
+ }
+ void Load() override
+ {
+ MSL(path);
+ MSL(isPrimary);
+ MSL(mappings);
+ }
+ };
+
+ #if defined(__GNUC__) || defined(__clang__)
+ #define PACKED_STRUCT __attribute__((packed))
+ #else
+ #define PACKED_STRUCT
+ #endif
+
+ #if defined(_MSC_VER)
+ #pragma pack(push, 1)
+ #else
+
+ #endif
+
+ struct PACKED_STRUCT dyld_cache_mapping_info
+ {
+ uint64_t address;
+ uint64_t size;
+ uint64_t fileOffset;
+ uint32_t maxProt;
+ uint32_t initProt;
+ };
+
+ struct LoadedMapping
+ {
+ std::shared_ptr<MMappedFileAccessor> backingFile;
+ dyld_cache_mapping_info mappingInfo;
+ };
+
+ struct PACKED_STRUCT dyld_cache_mapping_and_slide_info
+ {
+ uint64_t address;
+ uint64_t size;
+ uint64_t fileOffset;
+ uint64_t slideInfoFileOffset;
+ uint64_t slideInfoFileSize;
+ uint64_t flags;
+ uint32_t maxProt;
+ uint32_t initProt;
+ };
+
+ struct PACKED_STRUCT dyld_cache_slide_info_v2
+ {
+ uint32_t version;
+ uint32_t page_size;
+ uint32_t page_starts_offset;
+ uint32_t page_starts_count;
+ uint32_t page_extras_offset;
+ uint32_t page_extras_count;
+ uint64_t delta_mask;
+ uint64_t value_add;
+ };
+
+ struct PACKED_STRUCT dyld_cache_slide_info_v3
+ {
+ uint32_t version;
+ uint32_t page_size;
+ uint32_t page_starts_count;
+ uint32_t pad_i_guess;
+ uint64_t auth_value_add;
+ };
+
+
+ // DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE
+ struct dyld_chained_ptr_arm64e_shared_cache_rebase
+ {
+ uint64_t runtimeOffset : 34, // offset from the start of the shared cache
+ high8 : 8,
+ unused : 10,
+ next : 11, // 8-byte stide
+ auth : 1; // == 0
+ };
+
+ // DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE
+ struct dyld_chained_ptr_arm64e_shared_cache_auth_rebase
+ {
+ uint64_t runtimeOffset : 34, // offset from the start of the shared cache
+ diversity : 16,
+ addrDiv : 1,
+ keyIsData : 1, // implicitly always the 'A' key. 0 -> IA. 1 -> DA
+ next : 11, // 8-byte stide
+ auth : 1; // == 1
+ };
+
+ // dyld_cache_slide_info4 is used in watchOS which we are not close to supporting right now.
+
+ #define DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE 0xFFFF // page has no rebasing
+
+ struct PACKED_STRUCT dyld_cache_slide_info5
+ {
+ uint32_t version; // currently 5
+ uint32_t page_size; // currently 4096 (may also be 16384)
+ uint32_t page_starts_count;
+ uint64_t value_add;
+ // uint16_t page_starts[/* page_starts_count */];
+ };
+
+
+ struct PACKED_STRUCT dyld_cache_image_info
+ {
+ uint64_t address;
+ uint64_t modTime;
+ uint64_t inode;
+ uint32_t pathFileOffset;
+ uint32_t pad;
+ };
+
+ union dyld_cache_slide_pointer5
+ {
+ uint64_t raw;
+ struct dyld_chained_ptr_arm64e_shared_cache_rebase regular;
+ struct dyld_chained_ptr_arm64e_shared_cache_auth_rebase auth;
+ };
+
+
+ struct PACKED_STRUCT dyld_cache_local_symbols_info
+ {
+ uint32_t nlistOffset; // offset into this chunk of nlist entries
+ uint32_t nlistCount; // count of nlist entries
+ uint32_t stringsOffset; // offset into this chunk of string pool
+ uint32_t stringsSize; // byte count of string pool
+ uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry
+ uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array
+ };
+
+ struct PACKED_STRUCT dyld_cache_local_symbols_entry
+ {
+ uint32_t dylibOffset; // offset in cache file of start of dylib
+ uint32_t nlistStartIndex; // start index of locals for this dylib
+ uint32_t nlistCount; // number of local symbols for this dylib
+ };
+
+ struct PACKED_STRUCT dyld_cache_local_symbols_entry_64
+ {
+ uint64_t dylibOffset; // offset in cache buffer of start of dylib
+ uint32_t nlistStartIndex; // start index of locals for this dylib
+ uint32_t nlistCount; // number of local symbols for this dylib
+ };
+
+ union dyld_cache_slide_pointer3
+ {
+ uint64_t raw;
+ struct
+ {
+ uint64_t pointerValue : 51, offsetToNextPointer : 11, unused : 2;
+ } plain;
+
+ struct
+ {
+ uint64_t offsetFromSharedCacheBase : 32, diversityData : 16, hasAddressDiversity : 1, key : 2,
+ offsetToNextPointer : 11, unused : 1,
+ authenticated : 1; // = 1;
+ } auth;
+ };
+
+
+ struct PACKED_STRUCT dyld_cache_header
+ {
+ char magic[16]; // e.g. "dyld_v0 i386"
+ uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info
+ uint32_t mappingCount; // number of dyld_cache_mapping_info entries
+ uint32_t imagesOffsetOld; // UNUSED: moved to imagesOffset to prevent older dsc_extarctors from crashing
+ uint32_t imagesCountOld; // UNUSED: moved to imagesCount to prevent older dsc_extarctors from crashing
+ uint64_t dyldBaseAddress; // base address of dyld when cache was built
+ uint64_t codeSignatureOffset; // file offset of code signature blob
+ uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file)
+ uint64_t slideInfoOffsetUnused; // unused. Used to be file offset of kernel slid info
+ uint64_t slideInfoSizeUnused; // unused. Used to be size of kernel slid info
+ uint64_t localSymbolsOffset; // file offset of where local symbols are stored
+ uint64_t localSymbolsSize; // size of local symbols information
+ uint8_t uuid[16]; // unique value for each shared cache file
+ uint64_t cacheType; // 0 for development, 1 for production // Kat: , 2 for iOS 16?
+ uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses
+ uint32_t branchPoolsCount; // number of uint64_t entries
+ uint64_t accelerateInfoAddr; // (unslid) address of optimization info
+ uint64_t accelerateInfoSize; // size of optimization info
+ uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info
+ uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries
+ uint64_t patchInfoAddr; // (unslid) address of dyld_cache_patch_info
+ uint64_t patchInfoSize; // Size of all of the patch information pointed to via the dyld_cache_patch_info
+ uint64_t otherImageGroupAddrUnused; // unused
+ uint64_t otherImageGroupSizeUnused; // unused
+ uint64_t progClosuresAddr; // (unslid) address of list of program launch closures
+ uint64_t progClosuresSize; // size of list of program launch closures
+ uint64_t progClosuresTrieAddr; // (unslid) address of trie of indexes into program launch closures
+ uint64_t progClosuresTrieSize; // size of trie of indexes into program launch closures
+ uint32_t platform; // platform number (macOS=1, etc)
+ uint32_t formatVersion : 8, // dyld3::closure::kFormatVersion
+ dylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid
+ simulator : 1, // for simulator of specified platform
+ locallyBuiltCache : 1, // 0 for B&I built cache, 1 for locally built cache
+ builtFromChainedFixups : 1, // some dylib in cache was built using chained fixups, so patch tables must be used for overrides
+ padding : 20; // TBD
+ uint64_t sharedRegionStart; // base load address of cache if not slid
+ uint64_t sharedRegionSize; // overall size required to map the cache and all subCaches, if any
+ uint64_t maxSlide; // runtime slide of cache can be between zero and this value
+ uint64_t dylibsImageArrayAddr; // (unslid) address of ImageArray for dylibs in this cache
+ uint64_t dylibsImageArraySize; // size of ImageArray for dylibs in this cache
+ uint64_t dylibsTrieAddr; // (unslid) address of trie of indexes of all cached dylibs
+ uint64_t dylibsTrieSize; // size of trie of cached dylib paths
+ uint64_t otherImageArrayAddr; // (unslid) address of ImageArray for dylibs and bundles with dlopen closures
+ uint64_t otherImageArraySize; // size of ImageArray for dylibs and bundles with dlopen closures
+ uint64_t otherTrieAddr; // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures
+ uint64_t otherTrieSize; // size of trie of dylibs and bundles with dlopen closures
+ uint32_t mappingWithSlideOffset; // file offset to first dyld_cache_mapping_and_slide_info
+ uint32_t mappingWithSlideCount; // number of dyld_cache_mapping_and_slide_info entries
+ uint64_t dylibsPBLStateArrayAddrUnused; // unused
+ uint64_t dylibsPBLSetAddr; // (unslid) address of PrebuiltLoaderSet of all cached dylibs
+ uint64_t programsPBLSetPoolAddr; // (unslid) address of pool of PrebuiltLoaderSet for each program
+ uint64_t programsPBLSetPoolSize; // size of pool of PrebuiltLoaderSet for each program
+ uint64_t programTrieAddr; // (unslid) address of trie mapping program path to PrebuiltLoaderSet
+ uint32_t programTrieSize;
+ uint32_t osVersion; // OS Version of dylibs in this cache for the main platform
+ uint32_t altPlatform; // e.g. iOSMac on macOS
+ uint32_t altOsVersion; // e.g. 14.0 for iOSMac
+ uint64_t swiftOptsOffset; // file offset to Swift optimizations header
+ uint64_t swiftOptsSize; // size of Swift optimizations header
+ uint32_t subCacheArrayOffset; // file offset to first dyld_subcache_entry
+ uint32_t subCacheArrayCount; // number of subCache entries
+ uint8_t symbolFileUUID[16]; // unique value for the shared cache file containing unmapped local symbols
+ uint64_t rosettaReadOnlyAddr; // (unslid) address of the start of where Rosetta can add read-only/executable data
+ uint64_t rosettaReadOnlySize; // maximum size of the Rosetta read-only/executable region
+ uint64_t rosettaReadWriteAddr; // (unslid) address of the start of where Rosetta can add read-write data
+ uint64_t rosettaReadWriteSize; // maximum size of the Rosetta read-write region
+ uint32_t imagesOffset; // file offset to first dyld_cache_image_info
+ uint32_t imagesCount; // number of dyld_cache_image_info entries
+ };
+
+ struct PACKED_STRUCT dyld_subcache_entry
+ {
+ char uuid[16];
+ uint64_t address;
+ };
+
+ struct PACKED_STRUCT dyld_subcache_entry2
+ {
+ char uuid[16];
+ uint64_t address;
+ char fileExtension[32];
+ };
+
+ #if defined(_MSC_VER)
+ #pragma pack(pop)
+ #else
+
+ #endif
+
+ using namespace BinaryNinja;
+ struct SharedCacheMachOHeader : public MetadataSerializable
+ {
+ uint64_t textBase = 0;
+ uint64_t loadCommandOffset = 0;
+ mach_header_64 ident;
+ std::string identifierPrefix;
+ std::string installName;
+
+ 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 {};
+
+ 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
+ segment_command_64 linkeditSegment;
+ 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;
+
+ std::string exportTriePath;
+
+ bool linkeditPresent = false;
+ bool dysymPresent = false;
+ bool dyldInfoPresent = false;
+ bool exportTriePresent = false;
+ bool chainedFixupsPresent = false;
+ bool routinesPresent = false;
+ bool functionStartsPresent = false;
+ bool relocatable = false;
+ void Serialize(const std::string& name, mach_header_64 b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ bArr.PushBack(b.magic, m_activeContext.allocator);
+ bArr.PushBack(b.cputype, m_activeContext.allocator);
+ bArr.PushBack(b.cpusubtype, m_activeContext.allocator);
+ bArr.PushBack(b.filetype, m_activeContext.allocator);
+ bArr.PushBack(b.ncmds, m_activeContext.allocator);
+ bArr.PushBack(b.sizeofcmds, m_activeContext.allocator);
+ bArr.PushBack(b.flags, m_activeContext.allocator);
+ bArr.PushBack(b.reserved, m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, mach_header_64& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ b.magic = bArr[0].GetUint();
+ b.cputype = bArr[1].GetUint();
+ b.cpusubtype = bArr[2].GetUint();
+ b.filetype = bArr[3].GetUint();
+ b.ncmds = bArr[4].GetUint();
+ b.sizeofcmds = bArr[5].GetUint();
+ b.flags = bArr[6].GetUint();
+ b.reserved = bArr[7].GetUint();
+ }
+
+ void Serialize(const std::string& name, symtab_command b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ bArr.PushBack(b.cmd, m_activeContext.allocator);
+ bArr.PushBack(b.cmdsize, m_activeContext.allocator);
+ bArr.PushBack(b.symoff, m_activeContext.allocator);
+ bArr.PushBack(b.nsyms, m_activeContext.allocator);
+ bArr.PushBack(b.stroff, m_activeContext.allocator);
+ bArr.PushBack(b.strsize, m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, symtab_command& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ b.cmd = bArr[0].GetUint();
+ b.cmdsize = bArr[1].GetUint();
+ b.symoff = bArr[2].GetUint();
+ b.nsyms = bArr[3].GetUint();
+ b.stroff = bArr[4].GetUint();
+ b.strsize = bArr[5].GetUint();
+ }
+
+ void Serialize(const std::string& name, dysymtab_command b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ bArr.PushBack(b.cmd, m_activeContext.allocator);
+ bArr.PushBack(b.cmdsize, m_activeContext.allocator);
+ bArr.PushBack(b.ilocalsym, m_activeContext.allocator);
+ bArr.PushBack(b.nlocalsym, m_activeContext.allocator);
+ bArr.PushBack(b.iextdefsym, m_activeContext.allocator);
+ bArr.PushBack(b.nextdefsym, m_activeContext.allocator);
+ bArr.PushBack(b.iundefsym, m_activeContext.allocator);
+ bArr.PushBack(b.nundefsym, m_activeContext.allocator);
+ bArr.PushBack(b.tocoff, m_activeContext.allocator);
+ bArr.PushBack(b.ntoc, m_activeContext.allocator);
+ bArr.PushBack(b.modtaboff, m_activeContext.allocator);
+ bArr.PushBack(b.nmodtab, m_activeContext.allocator);
+ bArr.PushBack(b.extrefsymoff, m_activeContext.allocator);
+ bArr.PushBack(b.nextrefsyms, m_activeContext.allocator);
+ bArr.PushBack(b.indirectsymoff, m_activeContext.allocator);
+ bArr.PushBack(b.nindirectsyms, m_activeContext.allocator);
+ bArr.PushBack(b.extreloff, m_activeContext.allocator);
+ bArr.PushBack(b.nextrel, m_activeContext.allocator);
+ bArr.PushBack(b.locreloff, m_activeContext.allocator);
+ bArr.PushBack(b.nlocrel, m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, dysymtab_command& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ b.cmd = bArr[0].GetUint();
+ b.cmdsize = bArr[1].GetUint();
+ b.ilocalsym = bArr[2].GetUint();
+ b.nlocalsym = bArr[3].GetUint();
+ b.iextdefsym = bArr[4].GetUint();
+ b.nextdefsym = bArr[5].GetUint();
+ b.iundefsym = bArr[6].GetUint();
+ b.nundefsym = bArr[7].GetUint();
+ b.tocoff = bArr[8].GetUint();
+ b.ntoc = bArr[9].GetUint();
+ b.modtaboff = bArr[10].GetUint();
+ b.nmodtab = bArr[11].GetUint();
+ b.extrefsymoff = bArr[12].GetUint();
+ b.nextrefsyms = bArr[13].GetUint();
+ b.indirectsymoff = bArr[14].GetUint();
+ b.nindirectsyms = bArr[15].GetUint();
+ b.extreloff = bArr[16].GetUint();
+ b.nextrel = bArr[17].GetUint();
+ b.locreloff = bArr[18].GetUint();
+ b.nlocrel = bArr[19].GetUint();
+ }
+
+ void Serialize(const std::string& name, dyld_info_command b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ bArr.PushBack(b.cmd, m_activeContext.allocator);
+ bArr.PushBack(b.cmdsize, m_activeContext.allocator);
+ bArr.PushBack(b.rebase_off, m_activeContext.allocator);
+ bArr.PushBack(b.rebase_size, m_activeContext.allocator);
+ bArr.PushBack(b.bind_off, m_activeContext.allocator);
+ bArr.PushBack(b.bind_size, m_activeContext.allocator);
+ bArr.PushBack(b.weak_bind_off, m_activeContext.allocator);
+ bArr.PushBack(b.weak_bind_size, m_activeContext.allocator);
+ bArr.PushBack(b.lazy_bind_off, m_activeContext.allocator);
+ bArr.PushBack(b.lazy_bind_size, m_activeContext.allocator);
+ bArr.PushBack(b.export_off, m_activeContext.allocator);
+ bArr.PushBack(b.export_size, m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, dyld_info_command& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ b.cmd = bArr[0].GetUint();
+ b.cmdsize = bArr[1].GetUint();
+ b.rebase_off = bArr[2].GetUint();
+ b.rebase_size = bArr[3].GetUint();
+ b.bind_off = bArr[4].GetUint();
+ b.bind_size = bArr[5].GetUint();
+ b.weak_bind_off = bArr[6].GetUint();
+ b.weak_bind_size = bArr[7].GetUint();
+ b.lazy_bind_off = bArr[8].GetUint();
+ b.lazy_bind_size = bArr[9].GetUint();
+ b.export_off = bArr[10].GetUint();
+ b.export_size = bArr[11].GetUint();
+ }
+
+ void Serialize(const std::string& name, routines_command_64 b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ bArr.PushBack(b.cmd, m_activeContext.allocator);
+ bArr.PushBack(b.cmdsize, m_activeContext.allocator);
+ bArr.PushBack(b.init_address, m_activeContext.allocator);
+ bArr.PushBack(b.init_module, m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, routines_command_64& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ b.cmd = bArr[0].GetUint();
+ b.cmdsize = bArr[1].GetUint();
+ b.init_address = bArr[2].GetUint();
+ b.init_module = bArr[3].GetUint();
+ }
+
+ void Serialize(const std::string& name, function_starts_command b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ bArr.PushBack(b.cmd, m_activeContext.allocator);
+ bArr.PushBack(b.cmdsize, m_activeContext.allocator);
+ bArr.PushBack(b.funcoff, m_activeContext.allocator);
+ bArr.PushBack(b.funcsize, m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, function_starts_command& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ b.cmd = bArr[0].GetUint();
+ b.cmdsize = bArr[1].GetUint();
+ b.funcoff = bArr[2].GetUint();
+ b.funcsize = bArr[3].GetUint();
+ }
+
+ void Serialize(const std::string& name, std::vector<section_64> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& s : b)
+ {
+ rapidjson::Value sArr(rapidjson::kArrayType);
+ std::string sectNameStr;
+ char sectName[16];
+ memcpy(sectName, s.sectname, 16);
+ sectName[15] = 0;
+ sectNameStr = std::string(sectName);
+ sArr.PushBack(
+ rapidjson::Value(sectNameStr.c_str(), m_activeContext.allocator), m_activeContext.allocator);
+ std::string segNameStr;
+ char segName[16];
+ memcpy(segName, s.segname, 16);
+ segName[15] = 0;
+ segNameStr = std::string(segName);
+ sArr.PushBack(
+ rapidjson::Value(segNameStr.c_str(), m_activeContext.allocator), m_activeContext.allocator);
+ sArr.PushBack(s.addr, m_activeContext.allocator);
+ sArr.PushBack(s.size, m_activeContext.allocator);
+ sArr.PushBack(s.offset, m_activeContext.allocator);
+ sArr.PushBack(s.align, m_activeContext.allocator);
+ sArr.PushBack(s.reloff, m_activeContext.allocator);
+ sArr.PushBack(s.nreloc, m_activeContext.allocator);
+ sArr.PushBack(s.flags, m_activeContext.allocator);
+ sArr.PushBack(s.reserved1, m_activeContext.allocator);
+ sArr.PushBack(s.reserved2, m_activeContext.allocator);
+ sArr.PushBack(s.reserved3, m_activeContext.allocator);
+ bArr.PushBack(sArr, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, std::vector<section_64>& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ for (auto& s : bArr)
+ {
+ section_64 sec;
+ auto s2 = s.GetArray();
+ std::string sectNameStr = s2[0].GetString();
+ memset(sec.sectname, 0, 16);
+ memcpy(sec.sectname, sectNameStr.c_str(), sectNameStr.size());
+ std::string segNameStr = s2[1].GetString();
+ memset(sec.segname, 0, 16);
+ memcpy(sec.segname, segNameStr.c_str(), segNameStr.size());
+ sec.addr = s2[2].GetUint64();
+ sec.size = s2[3].GetUint64();
+ sec.offset = s2[4].GetUint();
+ sec.align = s2[5].GetUint();
+ sec.reloff = s2[6].GetUint();
+ sec.nreloc = s2[7].GetUint();
+ sec.flags = s2[8].GetUint();
+ sec.reserved1 = s2[9].GetUint();
+ sec.reserved2 = s2[10].GetUint();
+ sec.reserved3 = s2[11].GetUint();
+ b.push_back(sec);
+ }
+ }
+
+ void Serialize(const std::string& name, linkedit_data_command b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ bArr.PushBack(b.cmd, m_activeContext.allocator);
+ bArr.PushBack(b.cmdsize, m_activeContext.allocator);
+ bArr.PushBack(b.dataoff, m_activeContext.allocator);
+ bArr.PushBack(b.datasize, m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, linkedit_data_command& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ b.cmd = bArr[0].GetUint();
+ b.cmdsize = bArr[1].GetUint();
+ b.dataoff = bArr[2].GetUint();
+ b.datasize = bArr[3].GetUint();
+ }
+
+ void Serialize(const std::string& name, segment_command_64 b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ std::string segNameStr;
+ char segName[16];
+ memcpy(segName, b.segname, 16);
+ segName[15] = 0;
+ segNameStr = std::string(segName);
+ bArr.PushBack(rapidjson::Value(segNameStr.c_str(), m_activeContext.allocator), m_activeContext.allocator);
+ bArr.PushBack(b.vmaddr, m_activeContext.allocator);
+ bArr.PushBack(b.vmsize, m_activeContext.allocator);
+ bArr.PushBack(b.fileoff, m_activeContext.allocator);
+ bArr.PushBack(b.filesize, m_activeContext.allocator);
+ bArr.PushBack(b.maxprot, m_activeContext.allocator);
+ bArr.PushBack(b.initprot, m_activeContext.allocator);
+ bArr.PushBack(b.nsects, m_activeContext.allocator);
+ bArr.PushBack(b.flags, m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, segment_command_64& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ std::string segNameStr = bArr[0].GetString();
+ memset(b.segname, 0, 16);
+ memcpy(b.segname, segNameStr.c_str(), segNameStr.size());
+ b.vmaddr = bArr[1].GetUint64();
+ b.vmsize = bArr[2].GetUint64();
+ b.fileoff = bArr[3].GetUint64();
+ b.filesize = bArr[4].GetUint64();
+ b.maxprot = bArr[5].GetUint();
+ b.initprot = bArr[6].GetUint();
+ b.nsects = bArr[7].GetUint();
+ b.flags = bArr[8].GetUint();
+ }
+
+ void Serialize(const std::string& name, std::vector<segment_command_64> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& s : b)
+ {
+ rapidjson::Value sArr(rapidjson::kArrayType);
+ std::string segNameStr;
+ char segName[16];
+ memcpy(segName, s.segname, 16);
+ segName[15] = 0;
+ segNameStr = std::string(segName);
+ sArr.PushBack(
+ rapidjson::Value(segNameStr.c_str(), m_activeContext.allocator), m_activeContext.allocator);
+ sArr.PushBack(s.vmaddr, m_activeContext.allocator);
+ sArr.PushBack(s.vmsize, m_activeContext.allocator);
+ sArr.PushBack(s.fileoff, m_activeContext.allocator);
+ sArr.PushBack(s.filesize, m_activeContext.allocator);
+ sArr.PushBack(s.maxprot, m_activeContext.allocator);
+ sArr.PushBack(s.initprot, m_activeContext.allocator);
+ sArr.PushBack(s.nsects, m_activeContext.allocator);
+ sArr.PushBack(s.flags, m_activeContext.allocator);
+ bArr.PushBack(sArr, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, std::vector<segment_command_64>& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ for (auto& s : bArr)
+ {
+ segment_command_64 sec;
+ auto s2 = s.GetArray();
+ std::string segNameStr = s2[0].GetString();
+ memset(sec.segname, 0, 16);
+ memcpy(sec.segname, segNameStr.c_str(), segNameStr.size());
+ sec.vmaddr = s2[1].GetUint64();
+ sec.vmsize = s2[2].GetUint64();
+ sec.fileoff = s2[3].GetUint64();
+ sec.filesize = s2[4].GetUint64();
+ sec.maxprot = s2[5].GetUint();
+ sec.initprot = s2[6].GetUint();
+ sec.nsects = s2[7].GetUint();
+ sec.flags = s2[8].GetUint();
+ b.push_back(sec);
+ }
+ }
+
+ void Serialize(const std::string& name, build_version_command b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ bArr.PushBack(b.cmd, m_activeContext.allocator);
+ bArr.PushBack(b.cmdsize, m_activeContext.allocator);
+ bArr.PushBack(b.platform, m_activeContext.allocator);
+ bArr.PushBack(b.minos, m_activeContext.allocator);
+ bArr.PushBack(b.sdk, m_activeContext.allocator);
+ bArr.PushBack(b.ntools, m_activeContext.allocator);
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, build_version_command& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ b.cmd = bArr[0].GetUint();
+ b.cmdsize = bArr[1].GetUint();
+ b.platform = bArr[2].GetUint();
+ b.minos = bArr[3].GetUint();
+ b.sdk = bArr[4].GetUint();
+ b.ntools = bArr[5].GetUint();
+ }
+
+ void Serialize(const std::string& name, std::vector<build_tool_version> b)
+ {
+ S();
+ rapidjson::Value key(name.c_str(), m_activeContext.allocator);
+ rapidjson::Value bArr(rapidjson::kArrayType);
+ for (auto& s : b)
+ {
+ rapidjson::Value sArr(rapidjson::kArrayType);
+ sArr.PushBack(s.tool, m_activeContext.allocator);
+ sArr.PushBack(s.version, m_activeContext.allocator);
+ bArr.PushBack(sArr, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator);
+ }
+
+ void Deserialize(const std::string& name, std::vector<build_tool_version>& b)
+ {
+ auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray();
+ for (auto& s : bArr)
+ {
+ build_tool_version sec;
+ auto s2 = s.GetArray();
+ sec.tool = s2[0].GetUint();
+ sec.version = s2[1].GetUint();
+ b.push_back(sec);
+ }
+ }
+
+ void Store() override
+ {
+ MSS(textBase);
+ MSS(loadCommandOffset);
+ MSS_SUBCLASS(ident);
+ MSS(identifierPrefix);
+ MSS(installName);
+ MSS(entryPoints);
+ MSS(m_entryPoints);
+ MSS_SUBCLASS(symtab);
+ MSS_SUBCLASS(dysymtab);
+ MSS_SUBCLASS(dyldInfo);
+ // MSS_SUBCLASS(routines64);
+ MSS_SUBCLASS(functionStarts);
+ MSS_SUBCLASS(moduleInitSections);
+ MSS_SUBCLASS(exportTrie);
+ MSS_SUBCLASS(chainedFixups);
+ MSS(relocationBase);
+ MSS_SUBCLASS(segments);
+ MSS_SUBCLASS(linkeditSegment);
+ MSS_SUBCLASS(sections);
+ MSS(sectionNames);
+ MSS_SUBCLASS(symbolStubSections);
+ MSS_SUBCLASS(symbolPointerSections);
+ MSS(dylibs);
+ MSS_SUBCLASS(buildVersion);
+ MSS_SUBCLASS(buildToolVersions);
+ MSS(linkeditPresent);
+ MSS(exportTriePath);
+ MSS(dysymPresent);
+ MSS(dyldInfoPresent);
+ MSS(exportTriePresent);
+ MSS(chainedFixupsPresent);
+ MSS(routinesPresent);
+ MSS(functionStartsPresent);
+ MSS(relocatable);
+ }
+ void Load() override
+ {
+ MSL(textBase);
+ MSL(loadCommandOffset);
+ MSL_SUBCLASS(ident);
+ MSL(identifierPrefix);
+ MSL(installName);
+ MSL(entryPoints);
+ MSL(m_entryPoints);
+ MSL_SUBCLASS(symtab);
+ MSL_SUBCLASS(dysymtab);
+ MSL_SUBCLASS(dyldInfo);
+ // MSL_SUBCLASS(routines64); // FIXME CRASH but also do we even use this?
+ MSL_SUBCLASS(functionStarts);
+ MSL_SUBCLASS(moduleInitSections);
+ MSL_SUBCLASS(exportTrie);
+ MSL_SUBCLASS(chainedFixups);
+ MSL(relocationBase);
+ MSL_SUBCLASS(segments);
+ MSL_SUBCLASS(linkeditSegment);
+ MSL_SUBCLASS(sections);
+ MSL(sectionNames);
+ MSL_SUBCLASS(symbolStubSections);
+ MSL_SUBCLASS(symbolPointerSections);
+ MSL(dylibs);
+ MSL_SUBCLASS(buildVersion);
+ MSL_SUBCLASS(buildToolVersions);
+ MSL(linkeditPresent);
+ MSL(exportTriePath);
+ MSL(dysymPresent);
+ MSL(dyldInfoPresent);
+ MSL(exportTriePresent);
+ MSL(chainedFixupsPresent);
+ // MSL(routinesPresent);
+ MSL(functionStartsPresent);
+ MSL(relocatable);
+ }
+ };
+
+
+ struct MappingInfo
+ {
+ std::shared_ptr<MMappedFileAccessor> file;
+ dyld_cache_mapping_info mappingInfo;
+ uint32_t slideInfoVersion;
+ dyld_cache_slide_info_v2 slideInfoV2;
+ dyld_cache_slide_info_v3 slideInfoV3;
+ dyld_cache_slide_info5 slideInfoV5;
+ };
+
+
+ class ScopedVMMapSession;
+
+ static std::atomic<uint64_t> sharedCacheReferences = 0;
+
+ class SharedCache : public MetadataSerializable
+ {
+ IMPLEMENT_SHAREDCACHE_API_OBJECT(BNSharedCache);
+
+ std::atomic<int> m_refs = 0;
+
+ public:
+ virtual void AddRef() { m_refs.fetch_add(1); }
+
+ virtual void Release()
+ {
+ // undo actions will lock a file lock we hold and then wait for main thread
+ // so we need to release the ref later.
+ WorkerPriorityEnqueue([this]() {
+ if (m_refs.fetch_sub(1) == 1)
+ delete this;
+ });
+ }
+
+ virtual void AddAPIRef() { AddRef(); }
+
+ virtual void ReleaseAPIRef() { Release(); }
+
+ public:
+ enum SharedCacheFormat
+ {
+ RegularCacheFormat,
+ SplitCacheFormat,
+ LargeCacheFormat,
+ iOS16CacheFormat,
+ };
+
+ void Store() override
+ {
+ MSS(m_viewState);
+ MSS_CAST(m_cacheFormat, uint8_t);
+ MSS(m_imageStarts);
+ MSS(m_baseFilePath);
+ rapidjson::Value headers(rapidjson::kArrayType);
+ for (auto [k, v] : m_headers)
+ {
+ headers.PushBack(v.AsDocument(), m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember("headers", headers, m_activeContext.allocator);
+ // std::vector<std::pair<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>>> m_exportInfos
+ rapidjson::Value exportInfos(rapidjson::kArrayType);
+ for (auto& exportInfo : m_exportInfos)
+ {
+ rapidjson::Value exportInfoArr(rapidjson::kArrayType);
+ for (auto& exportInfoPair : exportInfo.second)
+ {
+ rapidjson::Value exportInfoPairArr(rapidjson::kArrayType);
+ exportInfoPairArr.PushBack(exportInfoPair.first, m_activeContext.allocator);
+ exportInfoPairArr.PushBack(exportInfoPair.second.first, m_activeContext.allocator);
+ exportInfoPairArr.PushBack(
+ rapidjson::Value(exportInfoPair.second.second.c_str(), m_activeContext.allocator),
+ m_activeContext.allocator);
+ exportInfoArr.PushBack(exportInfoPairArr, m_activeContext.allocator);
+ }
+ exportInfoArr.PushBack(exportInfoArr, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember("exportInfos", exportInfos, m_activeContext.allocator);
+ // std::vector<std::pair<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>>> m_symbolInfos
+ rapidjson::Value symbolInfos(rapidjson::kArrayType);
+ for (auto& symbolInfo : m_symbolInfos)
+ {
+ rapidjson::Value symbolInfoArr(rapidjson::kArrayType);
+ for (auto& symbolInfoPair : symbolInfo.second)
+ {
+ rapidjson::Value symbolInfoPairArr(rapidjson::kArrayType);
+ symbolInfoPairArr.PushBack(symbolInfoPair.first, m_activeContext.allocator);
+ symbolInfoPairArr.PushBack(symbolInfoPair.second.first, m_activeContext.allocator);
+ symbolInfoPairArr.PushBack(
+ rapidjson::Value(symbolInfoPair.second.second.c_str(), m_activeContext.allocator),
+ m_activeContext.allocator);
+ symbolInfoArr.PushBack(symbolInfoPairArr, m_activeContext.allocator);
+ }
+ symbolInfoArr.PushBack(symbolInfoArr, m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember("symbolInfos", symbolInfos, m_activeContext.allocator);
+
+ rapidjson::Value backingCaches(rapidjson::kArrayType);
+ for (auto bc : m_backingCaches)
+ {
+ backingCaches.PushBack(bc.AsDocument(), m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember("backingCaches", backingCaches, m_activeContext.allocator);
+ rapidjson::Value stubIslands(rapidjson::kArrayType);
+ for (auto si : m_stubIslandRegions)
+ {
+ stubIslands.PushBack(si.AsDocument(), m_activeContext.allocator);
+ }
+ rapidjson::Value images(rapidjson::kArrayType);
+ for (auto img : m_images)
+ {
+ images.PushBack(img.AsDocument(), m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember("images", images, m_activeContext.allocator);
+ rapidjson::Value regionsMappedIntoMemory(rapidjson::kArrayType);
+ for (auto r : m_regionsMappedIntoMemory)
+ {
+ regionsMappedIntoMemory.PushBack(r.AsDocument(), m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember("regionsMappedIntoMemory", regionsMappedIntoMemory, m_activeContext.allocator);
+ m_activeContext.doc.AddMember("stubIslands", stubIslands, m_activeContext.allocator);
+ rapidjson::Value dyldDataSections(rapidjson::kArrayType);
+ for (auto si : m_dyldDataRegions)
+ {
+ dyldDataSections.PushBack(si.AsDocument(), m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember("dyldDataSections", dyldDataSections, m_activeContext.allocator);
+ rapidjson::Value nonImageRegions(rapidjson::kArrayType);
+ for (auto si : m_nonImageRegions)
+ {
+ nonImageRegions.PushBack(si.AsDocument(), m_activeContext.allocator);
+ }
+ m_activeContext.doc.AddMember("nonImageRegions", nonImageRegions, m_activeContext.allocator);
+ }
+ void Load() override
+ {
+ m_viewState = MSL_CAST(m_viewState, uint8_t, DSCViewState);
+ m_cacheFormat = MSL_CAST(m_cacheFormat, uint8_t, SharedCacheFormat);
+ m_headers.clear();
+ for (auto& startAndHeader : m_activeDeserContext.doc["headers"].GetArray())
+ {
+ SharedCacheMachOHeader header;
+ header.LoadFromValue(startAndHeader);
+ m_headers[header.textBase] = header;
+ }
+ MSL(m_imageStarts);
+ MSL(m_baseFilePath);
+ m_exportInfos.clear();
+ for (auto& exportInfo : m_activeDeserContext.doc["exportInfos"].GetArray())
+ {
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> exportInfoVec;
+ for (auto& exportInfoPair : exportInfo.GetArray())
+ {
+ exportInfoVec.push_back({exportInfoPair[0].GetUint64(),
+ {(BNSymbolType)exportInfoPair[1].GetUint(), exportInfoPair[2].GetString()}});
+ }
+ m_exportInfos[exportInfo[0].GetUint64()] = exportInfoVec;
+ }
+ m_symbolInfos.clear();
+ for (auto& symbolInfo : m_activeDeserContext.doc["symbolInfos"].GetArray())
+ {
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> symbolInfoVec;
+ for (auto& symbolInfoPair : symbolInfo.GetArray())
+ {
+ symbolInfoVec.push_back({symbolInfoPair[0].GetUint64(),
+ {(BNSymbolType)symbolInfoPair[1].GetUint(), symbolInfoPair[2].GetString()}});
+ }
+ m_symbolInfos[symbolInfo[0].GetUint64()] = symbolInfoVec;
+ }
+ m_backingCaches.clear();
+ for (auto& bcV : m_activeDeserContext.doc["backingCaches"].GetArray())
+ {
+ BackingCache bc;
+ bc.LoadFromValue(bcV);
+ m_backingCaches.push_back(bc);
+ }
+ m_images.clear();
+ for (auto& imgV : m_activeDeserContext.doc["images"].GetArray())
+ {
+ CacheImage img;
+ img.LoadFromValue(imgV);
+ m_images.push_back(img);
+ }
+ m_regionsMappedIntoMemory.clear();
+ for (auto& rV : m_activeDeserContext.doc["regionsMappedIntoMemory"].GetArray())
+ {
+ MemoryRegion r;
+ r.LoadFromValue(rV);
+ m_regionsMappedIntoMemory.push_back(r);
+ }
+ m_stubIslandRegions.clear();
+ for (auto& siV : m_activeDeserContext.doc["stubIslands"].GetArray())
+ {
+ MemoryRegion si;
+ si.LoadFromValue(siV);
+ m_stubIslandRegions.push_back(si);
+ }
+ m_dyldDataRegions.clear();
+ for (auto& siV : m_activeDeserContext.doc["dyldDataSections"].GetArray())
+ {
+ MemoryRegion si;
+ si.LoadFromValue(siV);
+ m_dyldDataRegions.push_back(si);
+ }
+ m_nonImageRegions.clear();
+ for (auto& siV : m_activeDeserContext.doc["nonImageRegions"].GetArray())
+ {
+ MemoryRegion si;
+ si.LoadFromValue(siV);
+ m_nonImageRegions.push_back(si);
+ }
+ }
+
+ private:
+ Ref<Logger> m_logger;
+ /* VIEW STATE BEGIN -- SERIALIZE ALL OF THIS AND STORE IT IN RAW VIEW */
+
+ // Updated as the view is loaded further, more images are added, etc
+ DSCViewState m_viewState;
+ std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>>
+ m_exportInfos;
+ std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>>
+ m_symbolInfos;
+ // ---
+
+ // Serialized once by PerformInitialLoad and available after m_viewState == Loaded
+ std::string m_baseFilePath;
+ SharedCacheFormat m_cacheFormat;
+
+ std::unordered_map<std::string, uint64_t> m_imageStarts;
+ std::unordered_map<uint64_t, SharedCacheMachOHeader> m_headers;
+
+ std::vector<CacheImage> m_images;
+
+ std::vector<MemoryRegion> m_regionsMappedIntoMemory;
+
+ std::vector<BackingCache> m_backingCaches;
+
+ std::vector<MemoryRegion> m_stubIslandRegions;
+ std::vector<MemoryRegion> m_dyldDataRegions;
+ std::vector<MemoryRegion> m_nonImageRegions;
+
+ /* VIEWSTATE END -- NOTHING PAST THIS IS SERIALIZED */
+
+ /* API VIEW START */
+ BinaryNinja::Ref<BinaryNinja::BinaryView> m_dscView;
+ /* API VIEW END */
+
+ private:
+ void PerformInitialLoad();
+ void DeserializeFromRawView();
+
+ public:
+ std::shared_ptr<VM> GetVMMap(bool mapPages = true);
+
+ static SharedCache* GetFromDSCView(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView);
+ static uint64_t FastGetBackingCacheCount(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView);
+ bool SaveToDSCView();
+
+ void ParseAndApplySlideInfoForFile(std::shared_ptr<MMappedFileAccessor> file);
+ std::optional<uint64_t> GetImageStart(std::string installName);
+ std::optional<SharedCacheMachOHeader> HeaderForAddress(uint64_t);
+ bool LoadImageWithInstallName(std::string installName);
+ bool LoadSectionAtAddress(uint64_t address);
+ bool LoadImageContainingAddress(uint64_t address);
+ std::string NameForAddress(uint64_t address);
+ std::string ImageNameForAddress(uint64_t address);
+ std::vector<std::string> GetAvailableImages();
+
+ std::vector<MemoryRegion> GetMappedRegions() const;
+
+ std::vector<std::pair<std::string, Ref<Symbol>>> LoadAllSymbolsAndWait();
+
+ std::unordered_map<std::string, uint64_t> AllImageStarts() const { return m_imageStarts; }
+ std::unordered_map<uint64_t, SharedCacheMachOHeader> AllImageHeaders() const { return m_headers; }
+
+ std::string SerializedImageHeaderForAddress(uint64_t address);
+ std::string SerializedImageHeaderForName(std::string name);
+
+ void FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis);
+
+ std::vector<BackingCache> BackingCaches() const { return m_backingCaches; }
+
+ DSCViewState State() const { return m_viewState; }
+
+ explicit SharedCache(BinaryNinja::Ref<BinaryNinja::BinaryView> rawView);
+ ~SharedCache() override;
+
+ std::optional<SharedCacheMachOHeader> LoadHeaderForAddress(
+ std::shared_ptr<VM> vm, uint64_t address, std::string installName);
+ void InitializeHeader(
+ Ref<BinaryView> view, VM* vm, SharedCacheMachOHeader header, std::vector<MemoryRegion*> regionsToLoad);
+ void ReadExportNode(std::vector<Ref<Symbol>>& symbolList, SharedCacheMachOHeader& header, DataBuffer& buffer, uint64_t textBase,
+ const std::string& currentText, size_t cursor, uint32_t endGuard);
+ std::vector<Ref<Symbol>> ParseExportTrie(
+ std::shared_ptr<MMappedFileAccessor> linkeditFile, SharedCacheMachOHeader header);
+ };
+
+
+}
+
+void InitDSCViewType();
+
+#endif //SHAREDCACHE_SHAREDCACHE_H
+
diff --git a/view/sharedcache/core/VM.cpp b/view/sharedcache/core/VM.cpp
new file mode 100644
index 00000000..7fe01471
--- /dev/null
+++ b/view/sharedcache/core/VM.cpp
@@ -0,0 +1,793 @@
+//
+// Created by kat on 5/23/23.
+//
+
+/*
+ This is the cross-plat file buffering logic used for SharedCache processing.
+ This is used for reading large amounts of large files in a performant manner.
+
+ Here be no dragons, but this code is very complex, beware.
+
+ We in _all_ cases memory map the files, as we hardly ever need more than a few pages per file for most intensive operations.
+
+ Memory Map Implementation:
+ Of interest is that on several platforms we have to account for very low file pointer limits, and when mapping
+ 40+ files, these are trivially reachable.
+
+ We handle this with a "SelfAllocatingWeakPtr":
+ - Calling .lock() ALWAYS delivers a shared_ptr guaranteed to stay valid. This may block waiting for a free pointer
+ - As soon as that lock is released, that file pointer MAY be freed if another thread wants to open a new one, and we are at our limit.
+ - Calling .lock() again on this same theoretical object will then wait for another file pointer to be freeable.
+
+ VM Implementation:
+
+
+ Since the caches we're operating on are by nature page aligned, we are able to use nice optimizations under the hood to translate
+ "VM Addresses" to their actual in-memory counterparts.
+
+ We do this with a page table, which is a map of page -> file offset.
+
+ We also implement a "VMReader" here, which is a drop-in replacement for BinaryReader that operates on the VM.
+ see "ObjC.cpp" for where this is used.
+
+*/
+
+
+#include "VM.h"
+#include <utility>
+#include <memory>
+#include <cstring>
+#include <stdio.h>
+#include <binaryninjaapi.h>
+
+#ifdef _MSC_VER
+ #include <windows.h>
+#else
+ #include <sys/mman.h>
+ #include <fcntl.h>
+ #include <stdlib.h>
+ #include <sys/resource.h>
+#endif
+
+void VMShutdown()
+{
+ std::unique_lock<std::mutex> lock2(fileAccessorsMutex);
+ std::unique_lock<std::mutex> lock(fileAccessorDequeMutex);
+
+ // This will trigger the deallocation logic for these.
+ // It is background threaded to avoid a deadlock on exit.
+ fileAccessorReferenceHolder.clear();
+ fileAccessors.clear();
+}
+
+void MMAP::Map()
+{
+ if (mapped)
+ return;
+#ifdef _MSC_VER
+ LARGE_INTEGER fileSize;
+ if (!GetFileSizeEx(hFile, &fileSize))
+ {
+ // Handle error
+ CloseHandle(hFile);
+ return;
+ }
+ len = static_cast<size_t>(fileSize.QuadPart);
+
+ HANDLE hMapping = CreateFileMapping(
+ hFile, // file handle
+ NULL, // security attributes
+ PAGE_WRITECOPY, // protection
+ 0, // maximum size (high-order DWORD)
+ 0, // maximum size (low-order DWORD)
+ NULL); // name of the mapping object
+
+ if (hMapping == NULL)
+ {
+ // Handle error
+ CloseHandle(hFile);
+ return;
+ }
+
+ _mmap = MapViewOfFile(
+ hMapping, // handle to the file mapping object
+ FILE_MAP_COPY, // desired access
+ 0, // file offset (high-order DWORD)
+ 0, // file offset (low-order DWORD)
+ 0); // number of bytes to map (0 = entire file)
+
+ if (_mmap == nullptr)
+ {
+ // Handle error
+ CloseHandle(hMapping);
+ CloseHandle(hFile);
+ return;
+ }
+
+ mapped = true;
+
+ CloseHandle(hMapping);
+ CloseHandle(hFile);
+
+#else
+ fseek(fd, 0L, SEEK_END);
+ len = ftell(fd);
+ fseek(fd, 0L, SEEK_SET);
+
+ _mmap = mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(fd), 0u);
+ if (_mmap == MAP_FAILED)
+ {
+ // Handle error
+ return;
+ }
+
+ mapped = true;
+#endif
+}
+
+void MMAP::Unmap()
+{
+#ifdef _MSC_VER
+ if (_mmap)
+ {
+ UnmapViewOfFile(_mmap);
+ mapped = false;
+ }
+#else
+ if (mapped)
+ {
+ munmap(_mmap, len);
+ mapped = false;
+ }
+#endif
+}
+
+
+std::shared_ptr<SelfAllocatingWeakPtr<MMappedFileAccessor>> MMappedFileAccessor::Open(const uint64_t sessionID, const std::string &path, std::function<void(std::shared_ptr<MMappedFileAccessor>)> postAllocationRoutine)
+{
+ std::scoped_lock<std::mutex> lock(fileAccessorsMutex);
+ if (fileAccessors.count(path) == 0)
+ {
+ auto fileAcccessor = std::shared_ptr<SelfAllocatingWeakPtr<MMappedFileAccessor>>(new SelfAllocatingWeakPtr<MMappedFileAccessor>(
+ // Allocator logic for the SelfAllocatingWeakPtr
+ [path=path, sessionID=sessionID](){
+ std::unique_lock<std::mutex> _lock(fileAccessorDequeMutex);
+
+ // Iterate through held references and start removing them until we can get a file pointer
+ // FIXME: This could clear all currently used file pointers and still not get one. FIX!
+ // We should probably use a condition variable here to wait for a file pointer to be released!!!
+ for (auto& [_, fileAccessorDeque] : fileAccessorReferenceHolder)
+ {
+ if (fileAccessorSemaphore.try_acquire())
+ break;
+ fileAccessorDeque.pop_front();
+ }
+
+ mmapCount++;
+ _lock.unlock();
+ auto accessor = std::shared_ptr<MMappedFileAccessor>(new MMappedFileAccessor(path), [](MMappedFileAccessor* accessor){
+ // worker thread or we can deadlock on exit here.
+ BinaryNinja::WorkerEnqueue([accessor](){
+ fileAccessorSemaphore.release();
+ mmapCount--;
+ if (fileAccessors.count(accessor->m_path))
+ {
+ std::scoped_lock<std::mutex> lock(fileAccessorsMutex);
+ fileAccessors.erase(accessor->m_path);
+ }
+ delete accessor;
+ }, "MMappedFileAccessor Destructor");
+ });
+ _lock.lock();
+ // If some background thread has managed to try and open a file when the BV was already closed,
+ // we can still give them the file they want so they dont crash, but as soon as they let go it's gone.
+ if (!blockedSessionIDs.count(sessionID))
+ fileAccessorReferenceHolder[sessionID].push_back(accessor);
+ return accessor;
+ },
+ [postAllocationRoutine=postAllocationRoutine](std::shared_ptr<MMappedFileAccessor> accessor){
+ if (postAllocationRoutine)
+ postAllocationRoutine(accessor);
+ }));
+ fileAccessors.insert_or_assign(path, fileAcccessor);
+ }
+ return fileAccessors.at(path);
+}
+
+
+void MMappedFileAccessor::CloseAll(const uint64_t sessionID)
+{
+ blockedSessionIDs.insert(sessionID);
+ if (fileAccessorReferenceHolder.count(sessionID) == 0)
+ return;
+ fileAccessorReferenceHolder.erase(sessionID);
+}
+
+
+void MMappedFileAccessor::InitialVMSetup()
+{
+ // check for BN_SHAREDCACHE_FP_MAX
+ // if it exists, set maxFPLimit to that value
+ maxFPLimit = 0;
+ if (auto env = getenv("BN_SHAREDCACHE_FP_MAX"); env)
+ {
+ // FIXME behav on 0 here is unintuitive, '0123' will interpret as octal and be 83 according to manpage. meh.
+ maxFPLimit = strtoull(env, nullptr, 0);
+ if (maxFPLimit < 10)
+ {
+ BinaryNinja::LogWarn("BN_SHAREDCACHE_FP_MAX set to below 10. A value of at least 10 is recommended for performant analysis on SharedCache Binaries.");
+ }
+ if (maxFPLimit == 0)
+ {
+ BinaryNinja::LogError("BN_SHAREDCACHE_FP_MAX set to 0. Adjusting to 1");
+ maxFPLimit = 1;
+ }
+ }
+ else
+ {
+ if (maxFPLimit < 10) {
+#ifdef _MSC_VER
+ // It is not _super_ clear what the max file pointer limit is on windows,
+ // but to my understanding, we are using the windows API to map files,
+ // so we should have at least 2^24;
+ // kind of funny to me that windows would be the most effecient OS to
+ // parallelize sharedcache processing on in terms of FP usage concerns
+ maxFPLimit = 0x1000000;
+#else
+ // unix in comparison will likely have a very small limit, especially mac, necessitating all of this consideration
+ struct rlimit rlim;
+ getrlimit(RLIMIT_NOFILE, &rlim);
+ maxFPLimit = rlim.rlim_cur / 2;
+#endif
+ }
+ }
+ BinaryNinja::LogInfo("SharedCache processing initialized with a max file pointer limit of 0x%llx", maxFPLimit);
+ fileAccessorSemaphore.set_count(maxFPLimit);
+}
+
+
+MMappedFileAccessor::MMappedFileAccessor(const std::string& path) : m_path(path)
+{
+#ifdef _MSC_VER
+ m_mmap.hFile = CreateFile(
+ path.c_str(), // file name
+ GENERIC_READ, // desired access (read-only)
+ FILE_SHARE_READ, // share mode
+ NULL, // security attributes
+ OPEN_EXISTING, // creation disposition
+ FILE_ATTRIBUTE_NORMAL, // flags and attributes
+ NULL); // template file
+
+ if (m_mmap.hFile == INVALID_HANDLE_VALUE)
+ {
+ // BNLogInfo("Couldn't read file at %s", path.c_str());
+ throw MissingFileException();
+ }
+
+#else
+#ifdef ABORT_FAILURES
+ if (path.empty())
+ {
+ cerr << "Path is empty." << endl;
+ abort();
+ }
+#endif
+ m_mmap.fd = fopen(path.c_str(), "r");
+ if (m_mmap.fd == nullptr)
+ {
+ BNLogError("Serious VM Error: Couldn't read file at %s", path.c_str());
+
+#ifndef _MSC_VER
+ try {
+ throw BinaryNinja::ExceptionWithStackTrace("Unable to Read file");
+ }
+ catch (ExceptionWithStackTrace &ex)
+ {
+ BNLogError("%s", ex.m_stackTrace.c_str());
+ BNLogError("Error: %d (%s)", errno, strerror(errno));
+ }
+#endif
+ throw MissingFileException();
+ }
+#endif
+
+ m_mmap.Map();
+}
+
+MMappedFileAccessor::~MMappedFileAccessor()
+{
+ // BNLogInfo("Unmapping %s", m_path.c_str());
+ m_mmap.Unmap();
+
+#ifdef _MSC_VER
+ if (m_mmap.hFile != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(m_mmap.hFile);
+ }
+#else
+ if (m_mmap.fd != nullptr)
+ {
+ fclose(m_mmap.fd);
+ }
+#endif
+}
+
+void MMappedFileAccessor::WritePointer(size_t address, size_t pointer)
+{
+ ((size_t*)(&((uint8_t*)m_mmap._mmap)[address]))[0] = pointer;
+}
+
+std::string MMappedFileAccessor::ReadNullTermString(size_t address)
+{
+ if (address > m_mmap.len)
+ return "";
+ size_t max = m_mmap.len;
+ size_t i = address;
+ std::string str;
+ str.reserve(140);
+ while (i < max)
+ {
+ char c = ((char*)(&((uint8_t*)m_mmap._mmap)[i]))[0];
+ if (c == 0)
+ break;
+ str += c;
+ i++;
+ }
+ str.shrink_to_fit();
+ return str;
+}
+
+uint8_t MMappedFileAccessor::ReadUChar(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((uint8_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+int8_t MMappedFileAccessor::ReadChar(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((int8_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+uint16_t MMappedFileAccessor::ReadUShort(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((uint16_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+int16_t MMappedFileAccessor::ReadShort(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((int16_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+uint32_t MMappedFileAccessor::ReadUInt32(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((uint32_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+int32_t MMappedFileAccessor::ReadInt32(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((int32_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+uint64_t MMappedFileAccessor::ReadULong(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((uint64_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+int64_t MMappedFileAccessor::ReadLong(size_t address)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ return ((int64_t*)(&(((uint8_t*)m_mmap._mmap)[address])))[0];
+}
+
+BinaryNinja::DataBuffer* MMappedFileAccessor::ReadBuffer(size_t address, size_t length)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ if (address + length > m_mmap.len)
+ throw MappingReadException();
+ void* data = (void*)(&(((uint8_t*)m_mmap._mmap)[address]));
+ void* dataCopy = malloc(length);
+ memcpy(dataCopy, data, length);
+ return new BinaryNinja::DataBuffer(dataCopy, length);
+}
+
+void MMappedFileAccessor::Read(void* dest, size_t address, size_t length)
+{
+ if (address > m_mmap.len)
+ throw MappingReadException();
+ if (address + length > m_mmap.len)
+ throw MappingReadException();
+ memcpy(dest, (void*)&(((uint8_t*)m_mmap._mmap)[address]), length);
+}
+
+
+VM::VM(size_t pageSize, bool safe) : m_pageSize(pageSize), m_safe(safe)
+{
+ unsigned bits, var = (m_pageSize - 1 < 0) ? -(m_pageSize - 1) : m_pageSize - 1;
+ for (bits = 0; var != 0; ++bits)
+ var >>= 1;
+ m_pageSizeBits = bits;
+}
+
+VM::~VM()
+{
+}
+
+
+void VM::MapPages(uint64_t sessionID, size_t vm_address, size_t fileoff, size_t size, std::string filePath, std::function<void(std::shared_ptr<MMappedFileAccessor>)> postAllocationRoutine)
+{
+ // The mappings provided for shared caches will always be page aligned.
+ // We can use this to our advantage and gain considerable performance via page tables.
+ // This could probably be sped up if c++ were avoided?
+ // We want to create a map of page -> file offset
+
+ if (vm_address % m_pageSize != 0 || size % m_pageSize != 0)
+ {
+ throw MappingPageAlignmentException();
+ }
+
+ size_t pagesRemainingCount = size / m_pageSize;
+ for (size_t i = 0; i < size; i += m_pageSize)
+ {
+ // Our pages will be delimited by shifting off the page size
+ // So, 0x12345000 will become 0x12345 (assuming m_pageSize is 0x1000)
+ auto page = (vm_address + (i)) >> m_pageSizeBits;
+ if (m_map.count(page) != 0)
+ {
+ if (m_safe)
+ {
+ BNLogWarn("Remapping page 0x%lx (i == 0x%lx) (a: 0x%zx, f: 0x%zx)", page, i, vm_address, fileoff);
+ throw MappingCollisionException();
+ }
+ }
+ m_map.insert_or_assign(page, PageMapping(filePath, MMappedFileAccessor::Open(sessionID, filePath, postAllocationRoutine), i + fileoff));
+ }
+}
+
+std::pair<PageMapping, size_t> VM::MappingAtAddress(size_t address)
+{
+ // Get the page (e.g. 0x12345678 will become 0x12345 on 0x1000 aligned caches)
+ auto page = address >> m_pageSizeBits;
+ if (auto f = m_map.find(page); f != m_map.end())
+ {
+ // The PageMapping object returned contains the page, and more importantly, the file pointer (there can be
+ // multiple in newer caches) This is relevant for reading out the data in the rest of this file. The second item
+ // in this pair is created by taking the fileOffset (which will be a page but with the trailing bits (e.g.
+ // 0x12345000)
+ // and will add the "extra" bits lopped off when determining the page. (e.g. 0x12345678 -> 0x678)
+ return {f->second, f->second.fileOffset + (address & (m_pageSize - 1))};
+ }
+
+ throw MappingReadException();
+}
+
+
+bool VM::AddressIsMapped(uint64_t address)
+{
+ try
+ {
+ MappingAtAddress(address);
+ return true;
+ }
+ catch (...)
+ {}
+ return false;
+}
+
+
+uint64_t VMReader::ReadULEB128(size_t limit)
+{
+ uint64_t result = 0;
+ int bit = 0;
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ auto fileCursor = mapping.second;
+ auto fileLimit = fileCursor + (limit - m_cursor);
+ auto fa = mapping.first.fileAccessor->lock();
+ auto* fileBuff = (uint8_t*)fa->Data();
+ do
+ {
+ if (fileCursor >= fileLimit)
+ return -1;
+ uint64_t slice = ((uint64_t*)&((fileBuff)[fileCursor]))[0] & 0x7f;
+ if (bit > 63)
+ return -1;
+ else
+ {
+ result |= (slice << bit);
+ bit += 7;
+ }
+ } while (((uint64_t*)&(fileBuff[fileCursor++]))[0] & 0x80);
+ fa->Data(); // prevent deallocation of the fileAccessor as we're operating on the raw data buffer
+ return result;
+}
+
+
+int64_t VMReader::ReadSLEB128(size_t limit)
+{
+ uint8_t cur;
+ int64_t value = 0;
+ size_t shift = 0;
+
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ auto fileCursor = mapping.second;
+ auto fileLimit = fileCursor + (limit - m_cursor);
+ auto fa = mapping.first.fileAccessor->lock();
+ auto* fileBuff = (uint8_t*)fa->Data();
+
+ while (fileCursor < fileLimit)
+ {
+ cur = ((uint64_t*)&((fileBuff)[fileCursor]))[0];
+ fileCursor++;
+ value |= (cur & 0x7f) << shift;
+ shift += 7;
+ if ((cur & 0x80) == 0)
+ break;
+ }
+ value = (value << (64 - shift)) >> (64 - shift);
+ fa->Data(); // prevent deallocation of the fileAccessor as we're operating on the raw data buffer
+ return value;
+}
+
+std::string VM::ReadNullTermString(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadNullTermString(mapping.second);
+}
+
+uint8_t VM::ReadUChar(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadUChar(mapping.second);
+}
+
+int8_t VM::ReadChar(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadChar(mapping.second);
+}
+
+uint16_t VM::ReadUShort(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadUShort(mapping.second);
+}
+
+int16_t VM::ReadShort(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadShort(mapping.second);
+}
+
+uint32_t VM::ReadUInt32(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadUInt32(mapping.second);
+}
+
+int32_t VM::ReadInt32(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadInt32(mapping.second);
+}
+
+uint64_t VM::ReadULong(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadULong(mapping.second);
+}
+
+int64_t VM::ReadLong(size_t address)
+{
+ auto mapping = MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadLong(mapping.second);
+}
+
+BinaryNinja::DataBuffer* VM::ReadBuffer(size_t addr, size_t length)
+{
+ auto mapping = MappingAtAddress(addr);
+ return mapping.first.fileAccessor->lock()->ReadBuffer(mapping.second, length);
+}
+
+
+void VM::Read(void* dest, size_t addr, size_t length)
+{
+ auto mapping = MappingAtAddress(addr);
+ mapping.first.fileAccessor->lock()->Read(dest, mapping.second, length);
+}
+
+VMReader::VMReader(std::shared_ptr<VM> vm, size_t addressSize) : m_vm(vm), m_cursor(0), m_addressSize(addressSize) {}
+
+
+void VMReader::Seek(size_t address)
+{
+ m_cursor = address;
+}
+
+void VMReader::SeekRelative(size_t offset)
+{
+ m_cursor += offset;
+}
+
+std::string VMReader::ReadCString(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ return mapping.first.fileAccessor->lock()->ReadNullTermString(mapping.second);
+}
+
+uint8_t VMReader::ReadUChar(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 1;
+ return mapping.first.fileAccessor->lock()->ReadUChar(mapping.second);
+}
+
+int8_t VMReader::ReadChar(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 1;
+ return mapping.first.fileAccessor->lock()->ReadChar(mapping.second);
+}
+
+uint16_t VMReader::ReadUShort(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 2;
+ return mapping.first.fileAccessor->lock()->ReadUShort(mapping.second);
+}
+
+int16_t VMReader::ReadShort(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 2;
+ return mapping.first.fileAccessor->lock()->ReadShort(mapping.second);
+}
+
+uint32_t VMReader::ReadUInt32(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 4;
+ return mapping.first.fileAccessor->lock()->ReadUInt32(mapping.second);
+}
+
+int32_t VMReader::ReadInt32(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 4;
+ return mapping.first.fileAccessor->lock()->ReadInt32(mapping.second);
+}
+
+uint64_t VMReader::ReadULong(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 8;
+ return mapping.first.fileAccessor->lock()->ReadULong(mapping.second);
+}
+
+int64_t VMReader::ReadLong(size_t address)
+{
+ auto mapping = m_vm->MappingAtAddress(address);
+ m_cursor = address + 8;
+ return mapping.first.fileAccessor->lock()->ReadLong(mapping.second);
+}
+
+
+size_t VMReader::ReadPointer(size_t address)
+{
+ if (m_addressSize == 8)
+ return ReadULong(address);
+ else if (m_addressSize == 4)
+ return ReadUInt32(address);
+
+ // no idea what horrible arch we have, should probably die here.
+ return 0;
+}
+
+
+size_t VMReader::ReadPointer()
+{
+ if (m_addressSize == 8)
+ return Read64();
+ else if (m_addressSize == 4)
+ return Read32();
+
+ return 0;
+}
+
+BinaryNinja::DataBuffer* VMReader::ReadBuffer(size_t length)
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += length;
+ return mapping.first.fileAccessor->lock()->ReadBuffer(mapping.second, length);
+}
+
+BinaryNinja::DataBuffer* VMReader::ReadBuffer(size_t addr, size_t length)
+{
+ auto mapping = m_vm->MappingAtAddress(addr);
+ m_cursor = addr + length;
+ return mapping.first.fileAccessor->lock()->ReadBuffer(mapping.second, length);
+}
+
+void VMReader::Read(void* dest, size_t length)
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += length;
+ mapping.first.fileAccessor->lock()->Read(dest, mapping.second, length);
+}
+
+void VMReader::Read(void* dest, size_t addr, size_t length)
+{
+ auto mapping = m_vm->MappingAtAddress(addr);
+ m_cursor = addr + length;
+ mapping.first.fileAccessor->lock()->Read(dest, mapping.second, length);
+}
+
+
+uint8_t VMReader::Read8()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 1;
+ return mapping.first.fileAccessor->lock()->ReadUChar(mapping.second);
+}
+
+int8_t VMReader::ReadS8()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 1;
+ return mapping.first.fileAccessor->lock()->ReadChar(mapping.second);
+}
+
+uint16_t VMReader::Read16()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 2;
+ return mapping.first.fileAccessor->lock()->ReadUShort(mapping.second);
+}
+
+int16_t VMReader::ReadS16()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 2;
+ return mapping.first.fileAccessor->lock()->ReadShort(mapping.second);
+}
+
+uint32_t VMReader::Read32()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 4;
+ return mapping.first.fileAccessor->lock()->ReadUInt32(mapping.second);
+}
+
+int32_t VMReader::ReadS32()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 4;
+ return mapping.first.fileAccessor->lock()->ReadInt32(mapping.second);
+}
+
+uint64_t VMReader::Read64()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 8;
+ return mapping.first.fileAccessor->lock()->ReadULong(mapping.second);
+}
+
+int64_t VMReader::ReadS64()
+{
+ auto mapping = m_vm->MappingAtAddress(m_cursor);
+ m_cursor += 8;
+ return mapping.first.fileAccessor->lock()->ReadLong(mapping.second);
+}
diff --git a/view/sharedcache/core/VM.h b/view/sharedcache/core/VM.h
new file mode 100644
index 00000000..d85a766d
--- /dev/null
+++ b/view/sharedcache/core/VM.h
@@ -0,0 +1,330 @@
+//
+// Created by kat on 5/23/23.
+//
+
+#ifndef SHAREDCACHE_VM_H
+#define SHAREDCACHE_VM_H
+#include <binaryninjaapi.h>
+#include <condition_variable>
+
+void VMShutdown();
+
+class counting_semaphore {
+public:
+ explicit counting_semaphore(int count = 0) : count_(count) {}
+
+ void release(int update = 1) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ count_ += update;
+ cv_.notify_all();
+ }
+
+ void acquire() {
+ std::unique_lock<std::mutex> lock(mutex_);
+ cv_.wait(lock, [this]() { return count_ > 0; });
+ --count_;
+ }
+
+ bool try_acquire() {
+ std::unique_lock<std::mutex> lock(mutex_);
+ if (count_ > 0) {
+ --count_;
+ return true;
+ }
+ return false;
+ }
+
+ void set_count(int new_count) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ count_ = new_count;
+ cv_.notify_all();
+ }
+
+private:
+ std::mutex mutex_;
+ std::condition_variable cv_;
+ int count_;
+};
+
+
+template <typename T>
+class SelfAllocatingWeakPtr {
+public:
+ SelfAllocatingWeakPtr(std::function<std::shared_ptr<T>()> allocator, std::function<void(std::shared_ptr<T>)> postAlloc)
+ : allocator(allocator), postAlloc(postAlloc) {}
+
+ std::shared_ptr<T> lock() {
+ std::shared_ptr<T> sharedPtr = weakPtr.lock();
+ if (!sharedPtr) {
+ sharedPtr = allocator();
+ postAlloc(sharedPtr);
+ weakPtr = sharedPtr;
+ }
+ return sharedPtr;
+ }
+
+ std::shared_ptr<T> lock_no_allocate() {
+ return weakPtr.lock();
+ }
+
+private:
+ std::weak_ptr<T> weakPtr; // Weak reference to the object
+ std::function<std::shared_ptr<T>()> allocator; // Function to recreate the object
+ std::function<void(std::shared_ptr<T>)> postAlloc; // Function to call after the object is allocated
+};
+
+
+class MissingFileException : public std::exception
+{
+ virtual const char* what() const throw()
+ {
+ return "Missing File.";
+ }
+};
+
+class MMappedFileAccessor;
+
+class MMAP {
+ friend MMappedFileAccessor;
+
+ void *_mmap;
+ FILE *fd;
+ size_t len;
+
+#ifdef _MSC_VER
+ HANDLE hFile = INVALID_HANDLE_VALUE; // For Windows
+#endif
+
+ bool mapped = false;
+
+ void Map();
+
+ void Unmap();
+};
+
+static uint64_t maxFPLimit;
+static std::mutex fileAccessorDequeMutex;
+static std::unordered_map<uint64_t, std::deque<std::shared_ptr<MMappedFileAccessor>>> fileAccessorReferenceHolder;
+static std::set<uint64_t> blockedSessionIDs;
+static std::mutex fileAccessorsMutex;
+static std::unordered_map<std::string, std::shared_ptr<SelfAllocatingWeakPtr<MMappedFileAccessor>>> fileAccessors;
+static counting_semaphore fileAccessorSemaphore(0);
+
+static std::atomic<uint64_t> mmapCount = 0;
+
+class MMappedFileAccessor {
+ std::string m_path;
+ MMAP m_mmap;
+ bool m_slideInfoWasApplied = false;
+
+public:
+ MMappedFileAccessor(const std::string &path);
+ ~MMappedFileAccessor();
+
+ static std::shared_ptr<SelfAllocatingWeakPtr<MMappedFileAccessor>> Open(const uint64_t sessionID, const std::string &path, std::function<void(std::shared_ptr<MMappedFileAccessor>)> postAllocationRoutine = nullptr);
+
+ static void CloseAll(const uint64_t sessionID);
+
+ static void InitialVMSetup();
+
+ std::string Path() const { return m_path; };
+
+ size_t Length() const { return m_mmap.len; };
+
+ void *Data() const { return m_mmap._mmap; };
+
+ bool SlideInfoWasApplied() const { return m_slideInfoWasApplied; }
+
+ void SetSlideInfoWasApplied(bool slideInfoWasApplied) { m_slideInfoWasApplied = slideInfoWasApplied; }
+
+ /**
+ * Writes to files are implemented for performance reasons and should be treated with utmost care
+ *
+ * They _MAY_ disappear as _soon_ as you release the lock on the this file.
+ * They may also NOT disappear for the lifetime of the application.
+ *
+ * The former is more likely to occur when concurrent DSC processing is happening. The latter is the typical scenario.
+ *
+ * This is used explicitly for slide information in a locked scope and _NOTHING_ else. It should probably not be used for anything else.
+ *
+ * \param address
+ * \param pointer
+ */
+ void WritePointer(size_t address, size_t pointer);
+
+ std::string ReadNullTermString(size_t address);
+
+ uint8_t ReadUChar(size_t address);
+
+ int8_t ReadChar(size_t address);
+
+ uint16_t ReadUShort(size_t address);
+
+ int16_t ReadShort(size_t address);
+
+ uint32_t ReadUInt32(size_t address);
+
+ int32_t ReadInt32(size_t address);
+
+ uint64_t ReadULong(size_t address);
+
+ int64_t ReadLong(size_t address);
+
+ BinaryNinja::DataBuffer *ReadBuffer(size_t addr, size_t length);
+
+ void Read(void *dest, size_t addr, size_t length);
+};
+
+
+struct PageMapping {
+ std::string filePath;
+ std::shared_ptr<SelfAllocatingWeakPtr<MMappedFileAccessor>> fileAccessor;
+ size_t fileOffset;
+ PageMapping(std::string filePath, std::shared_ptr<SelfAllocatingWeakPtr<MMappedFileAccessor>> fileAccessor, size_t fileOffset)
+ : filePath(filePath), fileAccessor(fileAccessor), fileOffset(fileOffset) {}
+};
+
+
+class VMException : public std::exception {
+ virtual const char *what() const throw() {
+ return "Generic VM Exception";
+ }
+};
+
+class MappingPageAlignmentException : public VMException {
+ virtual const char *what() const throw() {
+ return "Tried to create a mapping not aligned to given page size";
+ }
+};
+
+class MappingReadException : VMException {
+ virtual const char *what() const throw() {
+ return "Tried to access unmapped page";
+ }
+};
+
+class MappingCollisionException : VMException {
+ virtual const char *what() const throw() {
+ return "Tried to remap a page";
+ }
+};
+
+class VMReader;
+
+
+class VM {
+ std::map<size_t, PageMapping> m_map;
+ size_t m_pageSize;
+ size_t m_pageSizeBits;
+ bool m_safe;
+
+ friend VMReader;
+
+public:
+
+ VM(size_t pageSize, bool safe = true);
+
+ ~VM();
+
+ void MapPages(uint64_t sessionID, size_t vm_address, size_t fileoff, size_t size, std::string filePath, std::function<void(std::shared_ptr<MMappedFileAccessor>)> postAllocationRoutine);
+
+ bool AddressIsMapped(uint64_t address);
+
+ std::pair<PageMapping, size_t> MappingAtAddress(size_t address);
+
+ std::string ReadNullTermString(size_t address);
+
+ uint8_t ReadUChar(size_t address);
+
+ int8_t ReadChar(size_t address);
+
+ uint16_t ReadUShort(size_t address);
+
+ int16_t ReadShort(size_t address);
+
+ uint32_t ReadUInt32(size_t address);
+
+ int32_t ReadInt32(size_t address);
+
+ uint64_t ReadULong(size_t address);
+
+ int64_t ReadLong(size_t address);
+
+ BinaryNinja::DataBuffer *ReadBuffer(size_t addr, size_t length);
+
+ void Read(void *dest, size_t addr, size_t length);
+};
+
+
+class VMReader {
+ std::shared_ptr<VM> m_vm;
+ size_t m_cursor;
+ size_t m_addressSize;
+
+ BNEndianness m_endianness = LittleEndian;
+
+public:
+ VMReader(std::shared_ptr<VM> vm, size_t addressSize = 8);
+
+ void SetEndianness(BNEndianness endianness) { m_endianness = endianness; }
+
+ BNEndianness GetEndianness() const { return m_endianness; }
+
+ void Seek(size_t address);
+
+ void SeekRelative(size_t offset);
+
+ [[nodiscard]] size_t GetOffset() const { return m_cursor; }
+
+ std::string ReadCString(size_t address);
+
+ uint64_t ReadULEB128(size_t cursorLimit);
+
+ int64_t ReadSLEB128(size_t cursorLimit);
+
+ uint8_t Read8();
+
+ int8_t ReadS8();
+
+ uint16_t Read16();
+
+ int16_t ReadS16();
+
+ uint32_t Read32();
+
+ int32_t ReadS32();
+
+ uint64_t Read64();
+
+ int64_t ReadS64();
+
+ size_t ReadPointer();
+
+ uint8_t ReadUChar(size_t address);
+
+ int8_t ReadChar(size_t address);
+
+ uint16_t ReadUShort(size_t address);
+
+ int16_t ReadShort(size_t address);
+
+ uint32_t ReadUInt32(size_t address);
+
+ int32_t ReadInt32(size_t address);
+
+ uint64_t ReadULong(size_t address);
+
+ int64_t ReadLong(size_t address);
+
+ size_t ReadPointer(size_t address);
+
+ BinaryNinja::DataBuffer *ReadBuffer(size_t length);
+
+ BinaryNinja::DataBuffer *ReadBuffer(size_t addr, size_t length);
+
+ void Read(void *dest, size_t length);
+
+ void Read(void *dest, size_t addr, size_t length);
+};
+
+#endif //SHAREDCACHE_VM_H