summaryrefslogtreecommitdiff
path: root/view/sharedcache
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
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')
-rw-r--r--view/sharedcache/.clang-format86
-rw-r--r--view/sharedcache/CMakeLists.txt88
-rw-r--r--view/sharedcache/HeadlessPlugin.cpp23
-rw-r--r--view/sharedcache/api/CMakeLists.txt55
-rw-r--r--view/sharedcache/api/MetadataSerializable.hpp500
-rw-r--r--view/sharedcache/api/python/CMakeLists.txt50
-rw-r--r--view/sharedcache/api/python/__init__.py8
-rw-r--r--view/sharedcache/api/python/_sharedcachecore.py659
-rw-r--r--view/sharedcache/api/python/_sharedcachecore_template.py43
-rw-r--r--view/sharedcache/api/python/generator.cpp617
-rw-r--r--view/sharedcache/api/python/sharedcache.py260
-rw-r--r--view/sharedcache/api/python/sharedcache_enums.py14
-rw-r--r--view/sharedcache/api/sharedcache.cpp224
-rw-r--r--view/sharedcache/api/sharedcacheapi.h820
-rw-r--r--view/sharedcache/api/sharedcachecore.h156
-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
-rw-r--r--view/sharedcache/ui/CMakeLists.txt72
-rw-r--r--view/sharedcache/ui/Plugin.cpp25
-rw-r--r--view/sharedcache/ui/SharedCacheBDNotifications.cpp69
-rw-r--r--view/sharedcache/ui/SharedCacheBDNotifications.h20
-rw-r--r--view/sharedcache/ui/SharedCacheUINotifications.cpp143
-rw-r--r--view/sharedcache/ui/SharedCacheUINotifications.h23
-rw-r--r--view/sharedcache/ui/dscpicker.cpp42
-rw-r--r--view/sharedcache/ui/dscpicker.h12
-rw-r--r--view/sharedcache/ui/dsctriage.cpp1012
-rw-r--r--view/sharedcache/ui/dsctriage.h297
-rw-r--r--view/sharedcache/ui/dscwidget.cpp437
-rw-r--r--view/sharedcache/ui/dscwidget.h190
-rw-r--r--view/sharedcache/workflow/CMakeLists.txt52
-rw-r--r--view/sharedcache/workflow/SharedCacheWorkflow.cpp496
-rw-r--r--view/sharedcache/workflow/SharedCacheWorkflow.h27
40 files changed, 15441 insertions, 0 deletions
diff --git a/view/sharedcache/.clang-format b/view/sharedcache/.clang-format
new file mode 100644
index 00000000..ba1ac018
--- /dev/null
+++ b/view/sharedcache/.clang-format
@@ -0,0 +1,86 @@
+---
+AccessModifierOffset: -4
+AlignAfterOpenBracket: DontAlign
+AlignConsecutiveMacros: 'true'
+AlignConsecutiveAssignments: 'false'
+AlignConsecutiveDeclarations: 'false'
+AlignEscapedNewlines: DontAlign
+AlignOperands: 'false'
+AlignTrailingComments: 'true'
+AllowAllArgumentsOnNextLine: 'true'
+AllowAllConstructorInitializersOnNextLine: 'false'
+AllowAllParametersOfDeclarationOnNextLine: 'false'
+AllowShortBlocksOnASingleLine: 'false'
+AllowShortCaseLabelsOnASingleLine: 'false'
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLambdasOnASingleLine: Inline
+AllowShortLoopsOnASingleLine: 'false'
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: 'true'
+AlwaysBreakTemplateDeclarations: 'Yes'
+BinPackArguments: 'true'
+BinPackParameters: 'true'
+BreakBeforeBraces: Custom
+BraceWrapping:
+ AfterCaseLabel: 'true'
+ AfterClass: 'true'
+ AfterControlStatement: 'true'
+ AfterEnum: 'true'
+ AfterFunction: 'true'
+ AfterNamespace: 'false'
+ AfterObjCDeclaration: 'true'
+ AfterStruct: 'true'
+ AfterUnion: 'true'
+ AfterExternBlock: 'true'
+ BeforeCatch: 'true'
+ BeforeElse: 'true'
+ IndentBraces: 'false'
+ SplitEmptyFunction: 'false'
+ SplitEmptyRecord: 'false'
+ SplitEmptyNamespace: 'false'
+BreakBeforeBinaryOperators: NonAssignment
+BreakBeforeTernaryOperators: 'false'
+BreakConstructorInitializers: AfterColon
+BreakInheritanceList: AfterColon
+ColumnLimit: '120'
+CompactNamespaces: 'true'
+ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
+Cpp11BracedListStyle: 'true'
+DerivePointerAlignment: 'false'
+DisableFormat: 'false'
+EmptyLineBeforeAccessModifier: Always
+FixNamespaceComments: 'true'
+IncludeBlocks: Preserve
+IndentCaseLabels: 'false'
+IndentPPDirectives: BeforeHash
+IndentWrappedFunctionNames: 'true'
+KeepEmptyLinesAtTheStartOfBlocks: 'false'
+MaxEmptyLinesToKeep: '2'
+NamespaceIndentation: All
+PenaltyIndentedWhitespace: 100
+PointerAlignment: Left
+SortIncludes: 'false'
+SortUsingDeclarations: 'true'
+SpaceAfterCStyleCast: 'false'
+SpaceAfterLogicalNot: 'false'
+SpaceAfterTemplateKeyword: 'true'
+SpaceBeforeAssignmentOperators: 'true'
+SpaceBeforeCpp11BracedList: 'true'
+SpaceBeforeCtorInitializerColon: 'true'
+SpaceBeforeInheritanceColon: 'true'
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: 'true'
+SpaceInEmptyParentheses: 'false'
+SpacesBeforeTrailingComments: '2'
+SpacesInAngles: 'false'
+SpacesInCStyleCastParentheses: 'false'
+SpacesInContainerLiterals: 'false'
+SpacesInParentheses: 'false'
+SpacesInSquareBrackets: 'false'
+IndentWidth: '4'
+TabWidth: '4'
+UseTab: Always
+
+...
diff --git a/view/sharedcache/CMakeLists.txt b/view/sharedcache/CMakeLists.txt
new file mode 100644
index 00000000..83ff4a57
--- /dev/null
+++ b/view/sharedcache/CMakeLists.txt
@@ -0,0 +1,88 @@
+cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
+
+project(sharedcache)
+
+if((NOT BN_API_PATH) AND (NOT BN_INTERNAL_BUILD))
+ set(BN_API_PATH $ENV{BN_API_PATH} CACHE STRING "Path to Binary Ninja API source")
+ if(NOT BN_API_PATH)
+ message(FATAL_ERROR "Provide path to Binary Ninja API source in BN_API_PATH")
+ endif()
+endif()
+
+if (NOT BN_INTERNAL_BUILD)
+ if(WIN32)
+ set(MSVC_VERSION msvc2022_64 CACHE STRING "Version of MSVC Qt is built with" )
+ endif()
+ set(QT_VERSION 6.7.2 CACHE STRING "Version of Qt to use")
+
+ if(NOT CMAKE_PREFIX_PATH)
+ if(APPLE)
+ set(CMAKE_PREFIX_PATH $ENV{HOME}/Qt/${QT_VERSION}/clang_64/lib/cmake)
+ elseif(WIN32)
+ set(CMAKE_PREFIX_PATH $ENV{HOMEDRIVE}$ENV{HOMEPATH}/Qt/${QT_VERSION}/${MSVC_VERSION}/lib/cmake)
+ else()
+ set(CMAKE_PREFIX_PATH $ENV{HOME}/Qt/${QT_VERSION}/gcc_64/lib/cmake)
+ endif()
+ endif()
+ message("CMAKE_PREFIX_PATH is: ${CMAKE_PREFIX_PATH}")
+endif()
+
+set(HARD_FAIL_MODE OFF CACHE BOOL "Enable hard fail mode")
+set(SLIDEINFO_DEBUG_TAGS OFF CACHE BOOL "Enable debug tags in slideinfo")
+set(VIEW_NAME "DSCViewAlpha" CACHE STRING "Name of the view")
+
+add_subdirectory(core)
+add_subdirectory(api)
+add_subdirectory(workflow)
+
+add_library(sharedcache SHARED
+ HeadlessPlugin.cpp)
+
+
+if(BN_INTERNAL_BUILD)
+ set_target_properties(sharedcache PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
+else()
+ set_target_properties(sharedcache PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/plugins
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/plugins
+ )
+endif()
+
+set_target_properties(sharedcache PROPERTIES
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+ CXX_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON
+ )
+
+target_include_directories(sharedcache PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/core ${CMAKE_CURRENT_SOURCE_DIR}/api ${CMAKE_CURRENT_SOURCE_DIR}/workflow)
+
+target_link_libraries(sharedcache PUBLIC sharedcacheapi binaryninjaapi sharedcachecore sharedcacheworkflow)
+
+if (HARD_FAIL_MODE)
+ target_compile_definitions(sharedcache PRIVATE ABORT_FAILURES)
+endif()
+
+if (SLIDEINFO_DEBUG_TAGS)
+ target_compile_definitions(sharedcache PRIVATE SLIDEINFO_DEBUG_TAGS)
+endif()
+
+target_compile_definitions(sharedcache PRIVATE VIEW_NAME="${VIEW_NAME}")
+
+if(NOT HEADLESS)
+ add_subdirectory(ui)
+endif()
+
+message("
+▓█████▄ ██████ ▄████▄
+▒██▀ ██▌ ▒██ ▒ ▒██▀ ▀█ Shared Cache Plugin
+░██ █▌ ░ ▓██▄ ▒▓█ ▄
+░▓█▄ █▌ ▒ ██▒ ▒▓▓▄ ▄██▒ CMake Prefix Path: ${CMAKE_PREFIX_PATH}
+░▒████▓ ▒██████▒▒▒ ▓███▀ ░ Qt Version: ${QT_VERSION}
+ ▒▒▓ ▒ ▒ ▒▓▒ ▒ ░░ ░▒ ▒ ░ Crash on Failure: ${HARD_FAIL_MODE}
+ ░ ▒ ▒ ░ ░▒ ░ ░ ░ ▒ Slideinfo Debug Tags: ${SLIDEINFO_DEBUG_TAGS}
+ ░ ░ ░ ░ ░ ░ ░
+") \ No newline at end of file
diff --git a/view/sharedcache/HeadlessPlugin.cpp b/view/sharedcache/HeadlessPlugin.cpp
new file mode 100644
index 00000000..c49ace5a
--- /dev/null
+++ b/view/sharedcache/HeadlessPlugin.cpp
@@ -0,0 +1,23 @@
+#include <binaryninjaapi.h>
+#include "DSCView.h"
+#include "SharedCache.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern void RegisterSharedCacheWorkflow();
+#ifdef __cplusplus
+}
+#endif
+
+extern "C"
+{
+ BN_DECLARE_CORE_ABI_VERSION
+
+ BINARYNINJAPLUGIN bool CorePluginInit()
+ {
+ InitDSCViewType();
+ RegisterSharedCacheWorkflow();
+ return true;
+ }
+} \ No newline at end of file
diff --git a/view/sharedcache/api/CMakeLists.txt b/view/sharedcache/api/CMakeLists.txt
new file mode 100644
index 00000000..b09ae321
--- /dev/null
+++ b/view/sharedcache/api/CMakeLists.txt
@@ -0,0 +1,55 @@
+cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
+
+project(sharedcacheapi)
+file(GLOB BN_MACHO_API_SOURCES *.cpp *.h)
+add_library(sharedcacheapi OBJECT ${BN_MACHO_API_SOURCES})
+
+if (VIEW_NAME)
+ target_compile_definitions(sharedcacheapi PRIVATE VIEW_NAME="${VIEW_NAME}")
+else()
+ error("VIEW_NAME must be defined")
+endif()
+
+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)
+
+target_include_directories(sharedcacheapi
+ PUBLIC ${PROJECT_SOURCE_DIR} ${INCLUDES})
+
+set_target_properties(sharedcacheapi PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON
+ ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/out)
+
+if (NOT DEMO)
+ add_subdirectory(python)
+endif() \ No newline at end of file
diff --git a/view/sharedcache/api/MetadataSerializable.hpp b/view/sharedcache/api/MetadataSerializable.hpp
new file mode 100644
index 00000000..dd3951ea
--- /dev/null
+++ b/view/sharedcache/api/MetadataSerializable.hpp
@@ -0,0 +1,500 @@
+//
+// 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();
+ }
+ }
+
+ 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/api/python/CMakeLists.txt b/view/sharedcache/api/python/CMakeLists.txt
new file mode 100644
index 00000000..c04a57ff
--- /dev/null
+++ b/view/sharedcache/api/python/CMakeLists.txt
@@ -0,0 +1,50 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(sharedcache-python-api)
+
+file(GLOB PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/*.py)
+list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_sharedcachecore.py)
+list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/enums.py)
+
+add_executable(sharedcache_generator
+ ${PROJECT_SOURCE_DIR}/generator.cpp)
+target_link_libraries(sharedcache_generator binaryninjaapi)
+target_include_directories(sharedcache_generator PUBLIC {PROJECT_SOURCE_DIR}/../../api)
+
+set_target_properties(sharedcache_generator PROPERTIES
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+ BUILD_WITH_INSTALL_RPATH OFF
+ RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+
+if(BN_INTERNAL_BUILD)
+ set(PYTHON_OUTPUT_DIRECTORY ${BN_RESOURCE_DIR}/python/binaryninja/sharedcache/)
+else()
+ set(PYTHON_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/plugins/sharedcache/)
+endif()
+
+if(WIN32)
+ if (BN_INTERNAL_BUILD)
+ add_custom_command(TARGET sharedcache_generator PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${BN_CORE_OUTPUT_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/)
+ else()
+ add_custom_command(TARGET sharedcache_generator PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${BN_INSTALL_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/)
+ endif()
+endif()
+
+add_custom_target(sharedcache_generator_copy ALL
+ BYPRODUCTS ${PROJECT_SOURCE_DIR}/_sharedcachecore.py ${PROJECT_SOURCE_DIR}/enums.py
+ DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../sharedcachecore.h $<TARGET_FILE:sharedcache_generator>
+ COMMAND ${CMAKE_COMMAND} -E echo "Copying SharedCache Python Sources"
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_OUTPUT_DIRECTORY}
+ COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:sharedcache_generator>
+ ${PROJECT_SOURCE_DIR}/../sharedcachecore.h
+ ${PROJECT_SOURCE_DIR}/_sharedcachecore.py
+ ${PROJECT_SOURCE_DIR}/_sharedcachecore_template.py
+ ${PROJECT_SOURCE_DIR}/sharedcache_enums.py
+
+ COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_SOURCES} ${PYTHON_OUTPUT_DIRECTORY}
+ COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_sharedcachecore.py ${PYTHON_OUTPUT_DIRECTORY}
+ COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/sharedcache_enums.py ${PYTHON_OUTPUT_DIRECTORY})
+
diff --git a/view/sharedcache/api/python/__init__.py b/view/sharedcache/api/python/__init__.py
new file mode 100644
index 00000000..1f65af93
--- /dev/null
+++ b/view/sharedcache/api/python/__init__.py
@@ -0,0 +1,8 @@
+import os
+
+from binaryninja._binaryninjacore import BNGetUserPluginDirectory
+user_plugin_dir = os.path.realpath(BNGetUserPluginDirectory())
+current_path = os.path.realpath(__file__)
+
+from .sharedcache import *
+
diff --git a/view/sharedcache/api/python/_sharedcachecore.py b/view/sharedcache/api/python/_sharedcachecore.py
new file mode 100644
index 00000000..93228468
--- /dev/null
+++ b/view/sharedcache/api/python/_sharedcachecore.py
@@ -0,0 +1,659 @@
+import binaryninja
+import ctypes, os
+
+from typing import Optional
+from . import sharedcache_enums
+# Load core module
+import platform
+core = None
+core_platform = platform.system()
+
+# By the time the debugger is loaded, binaryninja has not fully initialized.
+# So we cannot call binaryninja.bundled_plugin_path()
+from binaryninja._binaryninjacore import BNGetBundledPluginDirectory, BNFreeString
+if core_platform == "Darwin":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.dylib"))
+
+elif core_platform == "Linux":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.so"))
+
+elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0):
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "sharedcache.dll"))
+else:
+ raise Exception("OS not supported")
+
+def cstr(var) -> Optional[ctypes.c_char_p]:
+ if var is None:
+ return None
+ if isinstance(var, bytes):
+ return var
+ return var.encode("utf-8")
+
+def pyNativeStr(arg):
+ if isinstance(arg, str):
+ return arg
+ else:
+ return arg.decode('utf8')
+
+def free_string(value:ctypes.c_char_p) -> None:
+ BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte)))
+
+# Type definitions
+from binaryninja._binaryninjacore import BNBinaryView, BNBinaryViewHandle
+class BNDSCBackingCache(ctypes.Structure):
+ @property
+ def path(self):
+ return pyNativeStr(self._path)
+ @path.setter
+ def path(self, value):
+ self._path = cstr(value)
+BNDSCBackingCacheHandle = ctypes.POINTER(BNDSCBackingCache)
+class BNDSCBackingCacheMapping(ctypes.Structure):
+ pass
+BNDSCBackingCacheMappingHandle = ctypes.POINTER(BNDSCBackingCacheMapping)
+class BNDSCImage(ctypes.Structure):
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+BNDSCImageHandle = ctypes.POINTER(BNDSCImage)
+class BNDSCImageMemoryMapping(ctypes.Structure):
+ @property
+ def filePath(self):
+ return pyNativeStr(self._filePath)
+ @filePath.setter
+ def filePath(self, value):
+ self._filePath = cstr(value)
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+BNDSCImageMemoryMappingHandle = ctypes.POINTER(BNDSCImageMemoryMapping)
+class BNDSCMappedMemoryRegion(ctypes.Structure):
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+BNDSCMappedMemoryRegionHandle = ctypes.POINTER(BNDSCMappedMemoryRegion)
+class BNDSCMemoryUsageInfo(ctypes.Structure):
+ pass
+BNDSCMemoryUsageInfoHandle = ctypes.POINTER(BNDSCMemoryUsageInfo)
+class BNDSCSymbolRep(ctypes.Structure):
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+ @property
+ def image(self):
+ return pyNativeStr(self._image)
+ @image.setter
+ def image(self, value):
+ self._image = cstr(value)
+BNDSCSymbolRepHandle = ctypes.POINTER(BNDSCSymbolRep)
+DSCViewLoadProgressEnum = ctypes.c_int
+DSCViewStateEnum = ctypes.c_int
+class BNSharedCache(ctypes.Structure):
+ pass
+BNSharedCacheHandle = ctypes.POINTER(BNSharedCache)
+
+# Structure definitions
+BNDSCBackingCache._fields_ = [
+ ("_path", ctypes.c_char_p),
+ ("isPrimary", ctypes.c_bool),
+ ("mappings", ctypes.POINTER(BNDSCBackingCacheMapping)),
+ ("mappingCount", ctypes.c_ulonglong),
+ ]
+BNDSCBackingCacheMapping._fields_ = [
+ ("vmAddress", ctypes.c_ulonglong),
+ ("size", ctypes.c_ulonglong),
+ ("fileOffset", ctypes.c_ulonglong),
+ ]
+BNDSCImage._fields_ = [
+ ("_name", ctypes.c_char_p),
+ ("headerAddress", ctypes.c_ulonglong),
+ ("mappings", ctypes.POINTER(BNDSCImageMemoryMapping)),
+ ("mappingCount", ctypes.c_ulonglong),
+ ]
+BNDSCImageMemoryMapping._fields_ = [
+ ("_filePath", ctypes.c_char_p),
+ ("_name", ctypes.c_char_p),
+ ("vmAddress", ctypes.c_ulonglong),
+ ("size", ctypes.c_ulonglong),
+ ("loaded", ctypes.c_bool),
+ ("rawViewOffset", ctypes.c_ulonglong),
+ ]
+BNDSCMappedMemoryRegion._fields_ = [
+ ("vmAddress", ctypes.c_ulonglong),
+ ("size", ctypes.c_ulonglong),
+ ("_name", ctypes.c_char_p),
+ ]
+BNDSCMemoryUsageInfo._fields_ = [
+ ("sharedCacheRefs", ctypes.c_ulonglong),
+ ("mmapRefs", ctypes.c_ulonglong),
+ ]
+BNDSCSymbolRep._fields_ = [
+ ("address", ctypes.c_ulonglong),
+ ("_name", ctypes.c_char_p),
+ ("_image", ctypes.c_char_p),
+ ]
+
+# Function definitions
+# -------------------------------------------------------
+# _BNDSCFindSymbolAtAddressAndApplyToAddress
+
+_BNDSCFindSymbolAtAddressAndApplyToAddress = core.BNDSCFindSymbolAtAddressAndApplyToAddress
+_BNDSCFindSymbolAtAddressAndApplyToAddress.restype = None
+_BNDSCFindSymbolAtAddressAndApplyToAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ctypes.c_ulonglong,
+ ctypes.c_bool,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCFindSymbolAtAddressAndApplyToAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ symbolLocation: int,
+ targetLocation: int,
+ triggerReanalysis: bool
+ ) -> None:
+ return _BNDSCFindSymbolAtAddressAndApplyToAddress(cache, symbolLocation, targetLocation, triggerReanalysis)
+
+
+# -------------------------------------------------------
+# _BNDSCViewFastGetBackingCacheCount
+
+_BNDSCViewFastGetBackingCacheCount = core.BNDSCViewFastGetBackingCacheCount
+_BNDSCViewFastGetBackingCacheCount.restype = ctypes.c_ulonglong
+_BNDSCViewFastGetBackingCacheCount.argtypes = [
+ ctypes.POINTER(BNBinaryView),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewFastGetBackingCacheCount(
+ view: ctypes.POINTER(BNBinaryView)
+ ) -> int:
+ return _BNDSCViewFastGetBackingCacheCount(view)
+
+
+# -------------------------------------------------------
+# _BNDSCViewFreeAllImages
+
+_BNDSCViewFreeAllImages = core.BNDSCViewFreeAllImages
+_BNDSCViewFreeAllImages.restype = None
+_BNDSCViewFreeAllImages.argtypes = [
+ ctypes.POINTER(BNDSCImage),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewFreeAllImages(
+ images: ctypes.POINTER(BNDSCImage),
+ count: int
+ ) -> None:
+ return _BNDSCViewFreeAllImages(images, count)
+
+
+# -------------------------------------------------------
+# _BNDSCViewFreeBackingCaches
+
+_BNDSCViewFreeBackingCaches = core.BNDSCViewFreeBackingCaches
+_BNDSCViewFreeBackingCaches.restype = None
+_BNDSCViewFreeBackingCaches.argtypes = [
+ ctypes.POINTER(BNDSCBackingCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewFreeBackingCaches(
+ caches: ctypes.POINTER(BNDSCBackingCache),
+ count: int
+ ) -> None:
+ return _BNDSCViewFreeBackingCaches(caches, count)
+
+
+# -------------------------------------------------------
+# _BNDSCViewFreeLoadedRegions
+
+_BNDSCViewFreeLoadedRegions = core.BNDSCViewFreeLoadedRegions
+_BNDSCViewFreeLoadedRegions.restype = None
+_BNDSCViewFreeLoadedRegions.argtypes = [
+ ctypes.POINTER(BNDSCMappedMemoryRegion),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewFreeLoadedRegions(
+ images: ctypes.POINTER(BNDSCMappedMemoryRegion),
+ count: int
+ ) -> None:
+ return _BNDSCViewFreeLoadedRegions(images, count)
+
+
+# -------------------------------------------------------
+# _BNDSCViewFreeSymbols
+
+_BNDSCViewFreeSymbols = core.BNDSCViewFreeSymbols
+_BNDSCViewFreeSymbols.restype = None
+_BNDSCViewFreeSymbols.argtypes = [
+ ctypes.POINTER(BNDSCSymbolRep),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewFreeSymbols(
+ symbols: ctypes.POINTER(BNDSCSymbolRep),
+ count: int
+ ) -> None:
+ return _BNDSCViewFreeSymbols(symbols, count)
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetAllImages
+
+_BNDSCViewGetAllImages = core.BNDSCViewGetAllImages
+_BNDSCViewGetAllImages.restype = ctypes.POINTER(BNDSCImage)
+_BNDSCViewGetAllImages.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetAllImages(
+ cache: ctypes.POINTER(BNSharedCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(BNDSCImage)]:
+ result = _BNDSCViewGetAllImages(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetBackingCaches
+
+_BNDSCViewGetBackingCaches = core.BNDSCViewGetBackingCaches
+_BNDSCViewGetBackingCaches.restype = ctypes.POINTER(BNDSCBackingCache)
+_BNDSCViewGetBackingCaches.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetBackingCaches(
+ cache: ctypes.POINTER(BNSharedCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(BNDSCBackingCache)]:
+ result = _BNDSCViewGetBackingCaches(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetImageHeaderForAddress
+
+_BNDSCViewGetImageHeaderForAddress = core.BNDSCViewGetImageHeaderForAddress
+_BNDSCViewGetImageHeaderForAddress.restype = ctypes.POINTER(ctypes.c_byte)
+_BNDSCViewGetImageHeaderForAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetImageHeaderForAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ address: int
+ ) -> Optional[Optional[str]]:
+ result = _BNDSCViewGetImageHeaderForAddress(cache, address)
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetImageHeaderForName
+
+_BNDSCViewGetImageHeaderForName = core.BNDSCViewGetImageHeaderForName
+_BNDSCViewGetImageHeaderForName.restype = ctypes.POINTER(ctypes.c_byte)
+_BNDSCViewGetImageHeaderForName.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_char_p,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetImageHeaderForName(
+ cache: ctypes.POINTER(BNSharedCache),
+ name: Optional[str]
+ ) -> Optional[Optional[str]]:
+ result = _BNDSCViewGetImageHeaderForName(cache, cstr(name))
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetImageNameForAddress
+
+_BNDSCViewGetImageNameForAddress = core.BNDSCViewGetImageNameForAddress
+_BNDSCViewGetImageNameForAddress.restype = ctypes.POINTER(ctypes.c_byte)
+_BNDSCViewGetImageNameForAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetImageNameForAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ address: int
+ ) -> Optional[Optional[str]]:
+ result = _BNDSCViewGetImageNameForAddress(cache, address)
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetInstallNames
+
+_BNDSCViewGetInstallNames = core.BNDSCViewGetInstallNames
+_BNDSCViewGetInstallNames.restype = ctypes.POINTER(ctypes.c_char_p)
+_BNDSCViewGetInstallNames.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetInstallNames(
+ cache: ctypes.POINTER(BNSharedCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(ctypes.c_char_p)]:
+ result = _BNDSCViewGetInstallNames(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetLoadProgress
+
+_BNDSCViewGetLoadProgress = core.BNDSCViewGetLoadProgress
+_BNDSCViewGetLoadProgress.restype = DSCViewLoadProgressEnum
+_BNDSCViewGetLoadProgress.argtypes = [
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetLoadProgress(
+ sessionID: int
+ ) -> DSCViewLoadProgressEnum:
+ return _BNDSCViewGetLoadProgress(sessionID)
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetLoadedRegions
+
+_BNDSCViewGetLoadedRegions = core.BNDSCViewGetLoadedRegions
+_BNDSCViewGetLoadedRegions.restype = ctypes.POINTER(BNDSCMappedMemoryRegion)
+_BNDSCViewGetLoadedRegions.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetLoadedRegions(
+ cache: ctypes.POINTER(BNSharedCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(BNDSCMappedMemoryRegion)]:
+ result = _BNDSCViewGetLoadedRegions(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetMemoryUsageInfo
+
+_BNDSCViewGetMemoryUsageInfo = core.BNDSCViewGetMemoryUsageInfo
+_BNDSCViewGetMemoryUsageInfo.restype = BNDSCMemoryUsageInfo
+_BNDSCViewGetMemoryUsageInfo.argtypes = [
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetMemoryUsageInfo(
+ ) -> BNDSCMemoryUsageInfo:
+ return _BNDSCViewGetMemoryUsageInfo()
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetNameForAddress
+
+_BNDSCViewGetNameForAddress = core.BNDSCViewGetNameForAddress
+_BNDSCViewGetNameForAddress.restype = ctypes.POINTER(ctypes.c_byte)
+_BNDSCViewGetNameForAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetNameForAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ address: int
+ ) -> Optional[Optional[str]]:
+ result = _BNDSCViewGetNameForAddress(cache, address)
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNDSCViewGetState
+
+_BNDSCViewGetState = core.BNDSCViewGetState
+_BNDSCViewGetState.restype = DSCViewStateEnum
+_BNDSCViewGetState.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewGetState(
+ cache: ctypes.POINTER(BNSharedCache)
+ ) -> DSCViewStateEnum:
+ return _BNDSCViewGetState(cache)
+
+
+# -------------------------------------------------------
+# _BNDSCViewLoadAllSymbolsAndWait
+
+_BNDSCViewLoadAllSymbolsAndWait = core.BNDSCViewLoadAllSymbolsAndWait
+_BNDSCViewLoadAllSymbolsAndWait.restype = ctypes.POINTER(BNDSCSymbolRep)
+_BNDSCViewLoadAllSymbolsAndWait.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewLoadAllSymbolsAndWait(
+ cache: ctypes.POINTER(BNSharedCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(BNDSCSymbolRep)]:
+ result = _BNDSCViewLoadAllSymbolsAndWait(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNDSCViewLoadImageContainingAddress
+
+_BNDSCViewLoadImageContainingAddress = core.BNDSCViewLoadImageContainingAddress
+_BNDSCViewLoadImageContainingAddress.restype = ctypes.c_bool
+_BNDSCViewLoadImageContainingAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewLoadImageContainingAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ address: int
+ ) -> bool:
+ return _BNDSCViewLoadImageContainingAddress(cache, address)
+
+
+# -------------------------------------------------------
+# _BNDSCViewLoadImageWithInstallName
+
+_BNDSCViewLoadImageWithInstallName = core.BNDSCViewLoadImageWithInstallName
+_BNDSCViewLoadImageWithInstallName.restype = ctypes.c_bool
+_BNDSCViewLoadImageWithInstallName.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_char_p,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewLoadImageWithInstallName(
+ cache: ctypes.POINTER(BNSharedCache),
+ name: Optional[str]
+ ) -> bool:
+ return _BNDSCViewLoadImageWithInstallName(cache, cstr(name))
+
+
+# -------------------------------------------------------
+# _BNDSCViewLoadSectionAtAddress
+
+_BNDSCViewLoadSectionAtAddress = core.BNDSCViewLoadSectionAtAddress
+_BNDSCViewLoadSectionAtAddress.restype = ctypes.c_bool
+_BNDSCViewLoadSectionAtAddress.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewLoadSectionAtAddress(
+ cache: ctypes.POINTER(BNSharedCache),
+ name: int
+ ) -> bool:
+ return _BNDSCViewLoadSectionAtAddress(cache, name)
+
+
+# -------------------------------------------------------
+# _BNDSCViewLoadedImageCount
+
+_BNDSCViewLoadedImageCount = core.BNDSCViewLoadedImageCount
+_BNDSCViewLoadedImageCount.restype = ctypes.c_ulonglong
+_BNDSCViewLoadedImageCount.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNDSCViewLoadedImageCount(
+ cache: ctypes.POINTER(BNSharedCache)
+ ) -> int:
+ return _BNDSCViewLoadedImageCount(cache)
+
+
+# -------------------------------------------------------
+# _BNFreeSharedCacheReference
+
+_BNFreeSharedCacheReference = core.BNFreeSharedCacheReference
+_BNFreeSharedCacheReference.restype = None
+_BNFreeSharedCacheReference.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNFreeSharedCacheReference(
+ cache: ctypes.POINTER(BNSharedCache)
+ ) -> None:
+ return _BNFreeSharedCacheReference(cache)
+
+
+# -------------------------------------------------------
+# _BNGetSharedCache
+
+_BNGetSharedCache = core.BNGetSharedCache
+_BNGetSharedCache.restype = ctypes.POINTER(BNSharedCache)
+_BNGetSharedCache.argtypes = [
+ ctypes.POINTER(BNBinaryView),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNGetSharedCache(
+ data: ctypes.POINTER(BNBinaryView)
+ ) -> Optional[ctypes.POINTER(BNSharedCache)]:
+ result = _BNGetSharedCache(data)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNNewSharedCacheReference
+
+_BNNewSharedCacheReference = core.BNNewSharedCacheReference
+_BNNewSharedCacheReference.restype = ctypes.POINTER(BNSharedCache)
+_BNNewSharedCacheReference.argtypes = [
+ ctypes.POINTER(BNSharedCache),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNNewSharedCacheReference(
+ cache: ctypes.POINTER(BNSharedCache)
+ ) -> Optional[ctypes.POINTER(BNSharedCache)]:
+ result = _BNNewSharedCacheReference(cache)
+ if not result:
+ return None
+ return result
+
+
+
+# Helper functions
+def handle_of_type(value, handle_type):
+ if isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):
+ return ctypes.cast(value, ctypes.POINTER(handle_type))
+ raise ValueError('expected pointer to %s' % str(handle_type))
diff --git a/view/sharedcache/api/python/_sharedcachecore_template.py b/view/sharedcache/api/python/_sharedcachecore_template.py
new file mode 100644
index 00000000..6b5b174f
--- /dev/null
+++ b/view/sharedcache/api/python/_sharedcachecore_template.py
@@ -0,0 +1,43 @@
+import binaryninja
+import ctypes, os
+
+from typing import Optional
+from . import sharedcache_enums
+# Load core module
+import platform
+core = None
+core_platform = platform.system()
+
+# By the time the debugger is loaded, binaryninja has not fully initialized.
+# So we cannot call binaryninja.bundled_plugin_path()
+from binaryninja._binaryninjacore import BNGetBundledPluginDirectory, BNFreeString
+if core_platform == "Darwin":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.dylib"))
+
+elif core_platform == "Linux":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.so"))
+
+elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0):
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "sharedcache.dll"))
+else:
+ raise Exception("OS not supported")
+
+def cstr(var) -> Optional[ctypes.c_char_p]:
+ if var is None:
+ return None
+ if isinstance(var, bytes):
+ return var
+ return var.encode("utf-8")
+
+def pyNativeStr(arg):
+ if isinstance(arg, str):
+ return arg
+ else:
+ return arg.decode('utf8')
+
+def free_string(value:ctypes.c_char_p) -> None:
+ BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte)))
+
diff --git a/view/sharedcache/api/python/generator.cpp b/view/sharedcache/api/python/generator.cpp
new file mode 100644
index 00000000..91ff88c0
--- /dev/null
+++ b/view/sharedcache/api/python/generator.cpp
@@ -0,0 +1,617 @@
+/*
+Copyright 2020-2024 Vector 35 Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+
+#include <stdio.h>
+#include <inttypes.h>
+#include "binaryninjaapi.h"
+
+using namespace BinaryNinja;
+using namespace std;
+
+
+map<string, string> g_pythonKeywordReplacements = {
+ {"False", "False_"},
+ {"True", "True_"},
+ {"None", "None_"},
+ {"and", "and_"},
+ {"as", "as_"},
+ {"assert", "assert_"},
+ {"async", "async_"},
+ {"await", "await_"},
+ {"break", "break_"},
+ {"class", "class_"},
+ {"continue", "continue_"},
+ {"def", "def_"},
+ {"del", "del_"},
+ {"elif", "elif_"},
+ {"else", "else_"},
+ {"except", "except_"},
+ {"finally", "finally_"},
+ {"for", "for_"},
+ {"from", "from_"},
+ {"global", "global_"},
+ {"if", "if_"},
+ {"import", "import_"},
+ {"in", "in_"},
+ {"is", "is_"},
+ {"lambda", "lambda_"},
+ {"nonlocal", "nonlocal_"},
+ {"not", "not_"},
+ {"or", "or_"},
+ {"pass", "pass_"},
+ {"raise", "raise_"},
+ {"return", "return_"},
+ {"try", "try_"},
+ {"while", "while_"},
+ {"with", "with_"},
+ {"yield", "yield_"},
+};
+
+
+void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallback = false)
+{
+ switch (type->GetClass())
+ {
+ case BoolTypeClass:
+ fprintf(out, "ctypes.c_bool");
+ break;
+ case IntegerTypeClass:
+ switch (type->GetWidth())
+ {
+ case 1:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_byte");
+ else
+ fprintf(out, "ctypes.c_ubyte");
+ break;
+ case 2:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_short");
+ else
+ fprintf(out, "ctypes.c_ushort");
+ break;
+ case 4:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_int");
+ else
+ fprintf(out, "ctypes.c_uint");
+ break;
+ default:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_longlong");
+ else
+ fprintf(out, "ctypes.c_ulonglong");
+ break;
+ }
+ break;
+ case FloatTypeClass:
+ if (type->GetWidth() == 4)
+ fprintf(out, "ctypes.c_float");
+ else
+ fprintf(out, "ctypes.c_double");
+ break;
+ case NamedTypeReferenceClass:
+ if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass)
+ {
+ string name = type->GetNamedTypeReference()->GetName().GetString();
+ if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger")
+ name = name.substr(3);
+ else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger")
+ name = name.substr(2);
+ else if (name.size() > 15 && name.substr(0, 7) == "BNDebug")
+ name = name.substr(2);
+ else if (name.size() > 2 && name.substr(0, 2) == "BN")
+ name = name.substr(2);
+ fprintf(out, "%sEnum", name.c_str());
+ }
+ else
+ {
+ fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str());
+ }
+ break;
+ case PointerTypeClass:
+ if (isCallback || (type->GetChildType()->GetClass() == VoidTypeClass))
+ {
+ fprintf(out, "ctypes.c_void_p");
+ break;
+ }
+ else if ((type->GetChildType()->GetClass() == IntegerTypeClass) &&
+ (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned()))
+ {
+ if (isReturnType)
+ fprintf(out, "ctypes.POINTER(ctypes.c_byte)");
+ else
+ fprintf(out, "ctypes.c_char_p");
+ break;
+ }
+ else if (type->GetChildType()->GetClass() == FunctionTypeClass)
+ {
+ fprintf(out, "ctypes.CFUNCTYPE(");
+ OutputType(out, type->GetChildType()->GetChildType(), true, true);
+ for (auto& i : type->GetChildType()->GetParameters())
+ {
+ fprintf(out, ", ");
+ OutputType(out, i.type);
+ }
+ fprintf(out, ")");
+ break;
+ }
+ fprintf(out, "ctypes.POINTER(");
+ OutputType(out, type->GetChildType());
+ fprintf(out, ")");
+ break;
+ case ArrayTypeClass:
+ OutputType(out, type->GetChildType());
+ fprintf(out, " * %" PRId64, type->GetElementCount());
+ break;
+ default:
+ fprintf(out, "None");
+ break;
+ }
+}
+
+
+void OutputSwizzledType(FILE* out, Type* type)
+{
+ switch (type->GetClass())
+ {
+ case BoolTypeClass:
+ fprintf(out, "bool");
+ break;
+ case IntegerTypeClass:
+ fprintf(out, "int");
+ break;
+ case FloatTypeClass:
+ fprintf(out, "float");
+ break;
+ case NamedTypeReferenceClass:
+ if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass)
+ {
+ string name = type->GetNamedTypeReference()->GetName().GetString();
+ if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger")
+ name = name.substr(3);
+ else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger")
+ name = name.substr(2);
+ else if (name.size() > 15 && name.substr(0, 7) == "BNDebug")
+ name = name.substr(2);
+ else if (name.size() > 2 && name.substr(0, 2) == "BN")
+ name = name.substr(2);
+ fprintf(out, "%sEnum", name.c_str());
+ }
+ else
+ {
+ fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str());
+ }
+ break;
+ case PointerTypeClass:
+ if (type->GetChildType()->GetClass() == VoidTypeClass)
+ {
+ fprintf(out, "Optional[ctypes.c_void_p]");
+ break;
+ }
+ else if ((type->GetChildType()->GetClass() == IntegerTypeClass) &&
+ (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned()))
+ {
+ fprintf(out, "Optional[str]");
+ break;
+ }
+ else if (type->GetChildType()->GetClass() == FunctionTypeClass)
+ {
+ fprintf(out, "ctypes.CFUNCTYPE(");
+ OutputType(out, type->GetChildType()->GetChildType(), true, true);
+ for (auto& i : type->GetChildType()->GetParameters())
+ {
+ fprintf(out, ", ");
+ OutputType(out, i.type);
+ }
+ fprintf(out, ")");
+ break;
+ }
+ fprintf(out, "ctypes.POINTER(");
+ OutputType(out, type->GetChildType());
+ fprintf(out, ")");
+ break;
+ case ArrayTypeClass:
+ OutputType(out, type->GetChildType());
+ fprintf(out, " * %" PRId64, type->GetElementCount());
+ break;
+ default:
+ fprintf(out, "None");
+ break;
+ }
+}
+
+
+int main(int argc, char* argv[])
+{
+ if (argc < 5)
+ {
+ fprintf(stderr, "Usage: generator <header> <output> <output_template> <output_enum>\n");
+ return 1;
+ }
+
+ // Parse API header to get type and function information
+ map<QualifiedName, Ref<Type>> types, vars, funcs;
+ string errors;
+ auto arch = new CoreArchitecture(BNGetNativeTypeParserArchitecture());
+
+ // Enable ephemeral settings
+ Settings::Instance()->LoadSettingsFile("");
+ Settings::Instance()->Set("analysis.types.parserName", "ClangTypeParser");
+ bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors);
+
+ if (!ok)
+ {
+ fprintf(stderr, "Errors: %s\n", errors.c_str());
+ return 1;
+ }
+
+ FILE* out = fopen(argv[2], "w");
+ FILE* out_template = fopen(argv[3], "r");
+ FILE* enums = fopen(argv[4], "w");
+
+ fprintf(enums, "import enum\n");
+
+ // Copy the content of the template to the output file
+ int c;
+ while((c = fgetc(out_template)) != EOF)
+ fputc(c, out);
+
+ // Create type objects
+ fprintf(out, "# Type definitions\n");
+ for (auto& i : types)
+ {
+ string name;
+ if (i.first.size() != 1)
+ continue;
+ name = i.first[0];
+ if (name == "BNBinaryView")
+ {
+ fprintf(out, "from binaryninja._binaryninjacore import BNBinaryView, BNBinaryViewHandle\n");
+ continue;
+ }
+ if (i.second->GetClass() == StructureTypeClass)
+ {
+ fprintf(out, "class %s(ctypes.Structure):\n", name.c_str());
+
+ // python uses str's, C uses byte-arrays
+ bool stringField = false;
+ for (auto& arg : i.second->GetStructure()->GetMembers())
+ {
+ if ((arg.type->GetClass() == PointerTypeClass) &&
+ (arg.type->GetChildType()->GetWidth() == 1) &&
+ (arg.type->GetChildType()->IsSigned()))
+ {
+ fprintf(out, "\t@property\n\tdef %s(self):\n\t\treturn pyNativeStr(self._%s)\n", arg.name.c_str(), arg.name.c_str());
+ fprintf(out, "\t@%s.setter\n\tdef %s(self, value):\n\t\tself._%s = cstr(value)\n", arg.name.c_str(), arg.name.c_str(), arg.name.c_str());
+ stringField = true;
+ }
+ }
+
+ if (!stringField)
+ fprintf(out, "\tpass\n");
+
+ fprintf(out, "%sHandle = ctypes.POINTER(%s)\n", name.c_str(), name.c_str());
+ }
+ else if (i.second->GetClass() == EnumerationTypeClass)
+ {
+ bool isBNAPIEnum = false;
+ if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger")
+ name = name.substr(3);
+ else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger")
+ name = name.substr(2);
+ else if (name.size() > 15 && name.substr(0, 7) == "BNDebug")
+ name = name.substr(2);
+ else if (name.size() > 2 && name.substr(0, 2) == "BN")
+ {
+ name = name.substr(2);
+ isBNAPIEnum = false;
+ }
+ else
+ continue;
+
+ if (isBNAPIEnum)
+ {
+ fprintf(out, "from binaryninja._binaryninjacore import %sEnum\n", name.c_str());
+ continue;
+ }
+
+ fprintf(out, "%sEnum = ctypes.c_int\n", name.c_str());
+
+ fprintf(enums, "\n\nclass %s(enum.IntEnum):\n", name.c_str());
+ for (auto& j : i.second->GetEnumeration()->GetMembers())
+ {
+ fprintf(enums, "\t%s = %" PRId64 "\n", j.name.c_str(), j.value);
+ }
+ }
+ else if ((i.second->GetClass() == BoolTypeClass) || (i.second->GetClass() == IntegerTypeClass) ||
+ (i.second->GetClass() == FloatTypeClass) || (i.second->GetClass() == ArrayTypeClass))
+ {
+ fprintf(out, "%s = ", name.c_str());
+ OutputType(out, i.second);
+ fprintf(out, "\n");
+ }
+ }
+
+
+ fprintf(out, "\n# Structure definitions\n");
+ set<QualifiedName> structsToProcess;
+ set<QualifiedName> finishedStructs;
+ for (auto& i : types)
+ structsToProcess.insert(i.first);
+ while (structsToProcess.size() != 0)
+ {
+ set<QualifiedName> currentStructList = structsToProcess;
+ structsToProcess.clear();
+ bool processedSome = false;
+ for (auto& i : currentStructList)
+ {
+ string name;
+ if (i.size() != 1)
+ continue;
+ Ref<Type> type = types[i];
+ name = i[0];
+ if ((type->GetClass() == StructureTypeClass) && (type->GetStructure()->GetMembers().size() != 0))
+ {
+ bool requiresDependency = false;
+ for (auto& j : type->GetStructure()->GetMembers())
+ {
+ if ((j.type->GetClass() == NamedTypeReferenceClass) &&
+ (types[j.type->GetNamedTypeReference()->GetName()]->GetClass() == StructureTypeClass) &&
+ (finishedStructs.count(j.type->GetNamedTypeReference()->GetName()) == 0))
+ {
+ // This structure needs another structure that isn't fully defined yet, need to wait
+ // for the dependencies to be defined
+ structsToProcess.insert(i);
+ requiresDependency = true;
+ break;
+ }
+ }
+
+ if (requiresDependency)
+ continue;
+ fprintf(out, "%s._fields_ = [\n", name.c_str());
+ for (auto& j : type->GetStructure()->GetMembers())
+ {
+ // To help the python->C wrappers
+ if ((j.type->GetClass() == PointerTypeClass) &&
+ (j.type->GetChildType()->GetWidth() == 1) &&
+ (j.type->GetChildType()->IsSigned()))
+ {
+ fprintf(out, "\t\t(\"_%s\", ", j.name.c_str());
+ }
+ else
+ fprintf(out, "\t\t(\"%s\", ", j.name.c_str());
+ OutputType(out, j.type);
+ fprintf(out, "),\n");
+ }
+ fprintf(out, "\t]\n");
+ finishedStructs.insert(i);
+ processedSome = true;
+ }
+ else if (type->GetClass() == NamedTypeReferenceClass)
+ {
+ if (type->GetNamedTypeReference()->GetTypeReferenceClass() == StructNamedTypeClass)
+ {
+ fprintf(out, "%s = %s\n", name.c_str(), type->GetNamedTypeReference()->GetName().GetString().c_str());
+ fprintf(out, "%sHandle = %sHandle\n", name.c_str(), type->GetNamedTypeReference()->GetName().GetString().c_str());
+ }
+ else if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass)
+ {
+ fprintf(out, "%s = ctypes.c_int\n", name.c_str());
+ }
+ finishedStructs.insert(i);
+ processedSome = true;
+ }
+ }
+
+ if (!processedSome && structsToProcess.size() != 0)
+ {
+ fprintf(stderr, "Detected dependency cycle in structures\n");
+ for (auto& i : structsToProcess)
+ fprintf(stderr, "%s\n", i.GetString().c_str());
+ return 1;
+ }
+ }
+
+ fprintf(out, "\n# Function definitions\n");
+ for (auto& i : funcs)
+ {
+ string name;
+ if (i.first.size() != 1)
+ continue;
+ name = i.first[0];
+
+ // Check for a string result, these will be automatically wrapped to free the string
+ // memory and return a Python string
+ bool stringResult = (i.second->GetChildType()->GetClass() == PointerTypeClass) &&
+ (i.second->GetChildType()->GetChildType()->GetWidth() == 1) &&
+ (i.second->GetChildType()->GetChildType()->IsSigned());
+ // Pointer returns will be automatically wrapped to return None on null pointer
+ bool pointerResult = (i.second->GetChildType()->GetClass() == PointerTypeClass);
+
+ // From python -> C python3 requires str -> str.encode('charmap')
+ bool swizzleArgs = true;
+ if (name == "BNFreeString")
+ swizzleArgs = false;
+
+ bool callbackConvention = false;
+ if (name == "BNAllocString")
+ {
+ // Don't perform automatic wrapping of string allocation, and return a void
+ // pointer so that callback functions (which is the only valid use of BNDebuggerAllocString)
+ // can properly return the result
+ stringResult = false;
+ callbackConvention = true;
+ swizzleArgs = false;
+ }
+
+ string funcName = string("_") + name;
+
+ fprintf(out, "# -------------------------------------------------------\n");
+ fprintf(out, "# %s\n\n", funcName.c_str());
+ fprintf(out, "%s = core.%s\n", funcName.c_str(), name.c_str());
+ fprintf(out, "%s.restype = ", funcName.c_str());
+ OutputType(out, i.second->GetChildType(), true, callbackConvention);
+ fprintf(out, "\n");
+ if (!i.second->HasVariableArguments())
+ {
+ fprintf(out, "%s.argtypes = [\n", funcName.c_str());
+ for (auto& j : i.second->GetParameters())
+ {
+ fprintf(out, "\t\t");
+ if (name == "BNFreeString")
+ {
+ // BNDebuggerFreeString expects a pointer to a string allocated by the core, so do not use
+ // a c_char_p here, as that would be allocated by the Python runtime. This can
+ // be enforced by outputting like a return value.
+ OutputType(out, j.type, true);
+ }
+ else
+ {
+ OutputType(out, j.type);
+ }
+ fprintf(out, ",\n");
+ }
+ fprintf(out, "\t]");
+ }
+ else
+ {
+ // As of writing this, only BNLog's have variable instruction lengths, but in an attempt not to break in the future:
+ if (funcName.compare(0, 6, "_BNLog") == 0)
+ {
+ if (funcName != "_BNLog")
+ {
+ fprintf(out, "def %s(*args):\n", name.c_str());
+ fprintf(out, "\treturn %s(*[cstr(arg) for arg in args])\n\n", funcName.c_str());
+ continue;
+ }
+ else
+ {
+ fprintf(out, "def %s(level, *args):\n", name.c_str());
+ fprintf(out, "\treturn %s(level, *[cstr(arg) for arg in args])\n\n", funcName.c_str());
+ continue;
+ }
+ }
+ }
+ fprintf(out, "\n\n\n# noinspection PyPep8Naming\n");
+ fprintf(out, "def %s(", name.c_str());
+ if (!i.second->HasVariableArguments())
+ {
+ size_t argN = 0;
+ for (auto& arg: i.second->GetParameters())
+ {
+ string argName = arg.name;
+ if (g_pythonKeywordReplacements.find(argName) != g_pythonKeywordReplacements.end())
+ argName = g_pythonKeywordReplacements[argName];
+
+ if (argName.empty())
+ argName = "arg" + to_string(argN);
+
+ if (argN > 0)
+ fprintf(out, ", ");
+ fprintf(out, "\n\t\t");
+ fprintf(out, "%s: ", argName.c_str());
+ if (swizzleArgs)
+ OutputSwizzledType(out, arg.type);
+ else
+ OutputType(out, arg.type);
+ argN ++;
+ }
+ }
+ fprintf(out, "\n\t\t) -> ");
+ if (swizzleArgs)
+ {
+ if (stringResult || pointerResult)
+ fprintf(out, "Optional[");
+ OutputSwizzledType(out, i.second->GetChildType());
+ if (stringResult || pointerResult)
+ fprintf(out, "]");
+ }
+ else
+ {
+ OutputType(out, i.second->GetChildType());
+ }
+ fprintf(out, ":\n");
+
+ string stringArgFuncCall = funcName + "(";
+ size_t argN = 0;
+ for (auto& arg : i.second->GetParameters())
+ {
+ string argName = arg.name;
+ if (g_pythonKeywordReplacements.find(argName) != g_pythonKeywordReplacements.end())
+ argName = g_pythonKeywordReplacements[argName];
+
+ if (argName.empty())
+ argName = "arg" + to_string(argN);
+
+ if (swizzleArgs && (arg.type->GetClass() == PointerTypeClass) &&
+ (arg.type->GetChildType()->GetClass() == IntegerTypeClass) &&
+ (arg.type->GetChildType()->GetWidth() == 1) &&
+ (arg.type->GetChildType()->IsSigned()))
+ {
+ stringArgFuncCall += string("cstr(") + argName + "), ";
+ }
+ else
+ {
+ stringArgFuncCall += argName + ", ";
+ }
+ argN++;
+ }
+ if (argN > 0)
+ stringArgFuncCall = stringArgFuncCall.substr(0, stringArgFuncCall.size()-2);
+ stringArgFuncCall += ")";
+
+ if (stringResult)
+ {
+ // Emit wrapper to get Python string and free native memory
+ fprintf(out, "\tresult = ");
+ fprintf(out, "%s\n", stringArgFuncCall.c_str());
+ fprintf(out, "\tif not result:\n");
+ fprintf(out, "\t\treturn None\n");
+ fprintf(out, "\tstring = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))\n");
+ fprintf(out, "\tBNFreeString(result)\n");
+ fprintf(out, "\treturn string\n");
+ }
+ else if (pointerResult)
+ {
+ // Emit wrapper to return None on null pointer
+ fprintf(out, "\tresult = ");
+ fprintf(out, "%s\n", stringArgFuncCall.c_str());
+ fprintf(out, "\tif not result:\n");
+ fprintf(out, "\t\treturn None\n");
+ fprintf(out, "\treturn result\n");
+ }
+ else
+ {
+ fprintf(out, "\treturn ");
+ fprintf(out, "%s\n", stringArgFuncCall.c_str());
+ }
+ fprintf(out, "\n\n");
+ }
+
+ fprintf(out, "\n# Helper functions\n");
+ fprintf(out, "def handle_of_type(value, handle_type):\n");
+ fprintf(out, "\tif isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):\n");
+ fprintf(out, "\t\treturn ctypes.cast(value, ctypes.POINTER(handle_type))\n");
+ fprintf(out, "\traise ValueError('expected pointer to %%s' %% str(handle_type))\n");
+
+ fclose(out);
+ fclose(enums);
+ return 0;
+}
diff --git a/view/sharedcache/api/python/sharedcache.py b/view/sharedcache/api/python/sharedcache.py
new file mode 100644
index 00000000..ca031e03
--- /dev/null
+++ b/view/sharedcache/api/python/sharedcache.py
@@ -0,0 +1,260 @@
+import os
+import ctypes
+import dataclasses
+import traceback
+
+import binaryninja
+from binaryninja._binaryninjacore import BNFreeStringList, BNAllocString, BNFreeString
+
+from . import _sharedcachecore as sccore
+from .sharedcache_enums import *
+
+
+@dataclasses.dataclass
+class DSCMemoryMapping:
+ filePath: str
+ name: str
+ vmAddress: int
+ rawViewOffset: int
+ size: int
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCMemoryMapping '{self.name}' {os.path.basename(self.filePath)} raw<{self.rawViewOffset:x}>: {self.vmAddress:x}+{self.size:x}>"
+
+
+@dataclasses.dataclass
+class LoadedRegion:
+ name: str
+ headerAddress: int
+ mappings: list[DSCMemoryMapping]
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<LoadedRegion {self.name} @ {self.headerAddress:x}>"
+
+
+@dataclasses.dataclass
+class DSCBackingCacheMapping:
+ vmAddress: int
+ size: int
+ fileOffset: int
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCBackingCacheMapping {self.vmAddress:x}+{self.size:x} @ {self.fileOffset:x}"
+
+
+@dataclasses.dataclass
+class DSCBackingCache:
+ path: str
+ isPrimary: bool
+ mappings: list[DSCBackingCacheMapping]
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCBackingCache {self.path} {'Primary' if self.isPrimary else 'Secondary'} | {len(self.mappings)} mappings>"
+
+
+@dataclasses.dataclass
+class DSCImageMemoryMapping:
+ filePath: str
+ name: str
+ vmAddress: int
+ size: int
+ loaded: bool
+ rawViewOffset: int
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCImageMemoryMapping '{self.name}' {os.path.basename(self.filePath)} raw<{self.rawViewOffset:x}>: {self.vmAddress:x}+{self.size:x}>"
+
+
+@dataclasses.dataclass
+class DSCImage:
+ name: str
+ headerAddress: int
+ mappings: list[DSCImageMemoryMapping]
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCImage {self.name} @ {self.headerAddress:x}>"
+
+
+@dataclasses.dataclass
+class DSCSymbol:
+ name: str
+ image: str
+ address: int
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<DSCSymbol {self.name} @ {self.address:x} ({self.image}>"
+
+
+class SharedCache:
+ def __init__(self, view):
+ self.handle = sccore.BNGetSharedCache(view.handle)
+
+ def load_image_with_install_name(self, installName):
+ str = BNAllocString(installName.encode('utf-8'))
+ return sccore.BNDSCViewLoadImageWithInstallName(self.handle, str)
+
+ def load_section_at_address(self, addr):
+ return sccore.BNDSCViewLoadSectionAtAddress(self.handle, addr)
+
+ def load_image_containing_address(self, addr):
+ return sccore.BNDSCViewLoadImageContainingAddress(self.handle, addr)
+
+ @property
+ def caches(self):
+ count = ctypes.c_ulonglong()
+ value = sccore.BNDSCViewGetBackingCaches(self.handle, count)
+ if value is None:
+ return []
+
+ result = []
+ for i in range(count.value):
+ mappings = []
+ for j in range(value[i].mappingCount):
+ mapping = DSCBackingCacheMapping(
+ value[i].mappings[j].vmAddress,
+ value[i].mappings[j].size,
+ value[i].mappings[j].fileOffset
+ )
+ mappings.append(mapping)
+ result.append(DSCBackingCache(
+ value[i].path,
+ value[i].isPrimary,
+ mappings
+ ))
+
+ sccore.BNDSCViewFreeBackingCaches(value, count)
+ return result
+
+ @property
+ def images(self):
+ count = ctypes.c_ulonglong()
+ value = sccore.BNDSCViewGetAllImages(self.handle, count)
+ if value is None:
+ return []
+
+ result = []
+ for i in range(count.value):
+ mappings = []
+ for j in range(value[i].mappingCount):
+ mapping = DSCImageMemoryMapping(
+ value[i].mappings[j].filePath,
+ value[i].mappings[j].name,
+ value[i].mappings[j].vmAddress,
+ value[i].mappings[j].size,
+ value[i].mappings[j].loaded,
+ value[i].mappings[j].rawViewOffset
+ )
+ mappings.append(mapping)
+ result.append(DSCImage(
+ value[i].name,
+ value[i].headerAddress,
+ mappings
+ ))
+
+ sccore.BNDSCViewFreeAllImages(value, count)
+ return result
+
+ @property
+ def loaded_images(self):
+ count = ctypes.c_ulonglong()
+ value = sccore.BNDSCViewGetLoadedImages(self.handle, count)
+ if value is None:
+ return []
+
+ result = []
+ for i in range(count.value):
+ mappings = []
+ for j in range(value[i].mappingCount):
+ mapping = DSCMemoryMapping(
+ value[i].mappings[j].filePath,
+ value[i].mappings[j].name,
+ value[i].mappings[j].vmAddress,
+ value[i].mappings[j].rawViewOffset,
+ value[i].mappings[j].size
+ )
+ mappings.append(mapping)
+ result.append(LoadedRegion(
+ value[i].name,
+ value[i].headerAddress,
+ mappings
+ ))
+
+ sccore.BNDSCViewFreeLoadedImages(value, count)
+ return result
+
+ def load_all_symbols_and_wait(self):
+ count = ctypes.c_ulonglong()
+ value = sccore.BNDSCViewLoadAllSymbolsAndWait(self.handle, count)
+ if value is None:
+ return []
+ result = []
+ for i in range(count.value):
+ sym = DSCSymbol(
+ value[i].name,
+ value[i].image,
+ value[i].address
+ )
+ result.append(sym)
+
+ sccore.BNDSCViewFreeSymbols(value, count)
+ return result
+
+ @property
+ def image_names(self):
+ count = ctypes.c_ulonglong()
+ value = sccore.BNDSCViewGetInstallNames(self.handle, count)
+ if value is None:
+ return []
+
+ result = []
+ for i in range(count.value):
+ result.append(value[i].decode('utf-8'))
+
+ BNFreeStringList(value, count)
+ return result
+
+ @property
+ def image_count(self):
+ return sccore.BNDSCViewLoadedImageCount(self.handle)
+
+ @property
+ def state(self):
+ return DSCViewState(sccore.BNDSCViewGetState(self.handle))
+
+ def get_name_for_address(self, address):
+ name = sccore.BNDSCViewGetNameForAddress(self.handle, address)
+ if name is None:
+ return ""
+ result = name
+ return result
+
+ def get_image_name_for_address(self, address):
+ name = sccore.BNDSCViewGetImageNameForAddress(self.handle, address)
+ if name is None:
+ return ""
+ result = name
+ return result
+
+ def find_symbol_at_addr_and_apply_to_addr(self, symbolAddress, targetAddress, triggerReanalysis) -> None:
+ sccore.BNDSCFindSymbolAtAddressAndApplyToAddress(self.handle, symbolAddress, targetAddress, triggerReanalysis)
diff --git a/view/sharedcache/api/python/sharedcache_enums.py b/view/sharedcache/api/python/sharedcache_enums.py
new file mode 100644
index 00000000..34668424
--- /dev/null
+++ b/view/sharedcache/api/python/sharedcache_enums.py
@@ -0,0 +1,14 @@
+import enum
+
+
+class DSCViewLoadProgress(enum.IntEnum):
+ LoadProgressNotStarted = 0
+ LoadProgressLoadingCaches = 1
+ LoadProgressLoadingImages = 2
+ LoadProgressFinished = 3
+
+
+class DSCViewState(enum.IntEnum):
+ Unloaded = 0
+ Loaded = 1
+ LoadedWithImages = 2
diff --git a/view/sharedcache/api/sharedcache.cpp b/view/sharedcache/api/sharedcache.cpp
new file mode 100644
index 00000000..5ca7c480
--- /dev/null
+++ b/view/sharedcache/api/sharedcache.cpp
@@ -0,0 +1,224 @@
+//
+// Created by kat on 5/21/23.
+//
+
+#include "sharedcacheapi.h"
+
+namespace SharedCacheAPI {
+
+ SharedCache::SharedCache(Ref<BinaryView> view) {
+ m_object = BNGetSharedCache(view->GetObject());
+ }
+
+ BNDSCViewLoadProgress SharedCache::GetLoadProgress(Ref<BinaryView> view)
+ {
+ return BNDSCViewGetLoadProgress(view->GetFile()->GetSessionId());
+ }
+
+ uint64_t SharedCache::FastGetBackingCacheCount(Ref<BinaryView> view)
+ {
+ return BNDSCViewFastGetBackingCacheCount(view->GetObject());
+ }
+
+ bool SharedCache::LoadImageWithInstallName(std::string installName)
+ {
+ char* str = BNAllocString(installName.c_str());
+ return BNDSCViewLoadImageWithInstallName(m_object, str);
+ }
+
+ bool SharedCache::LoadSectionAtAddress(uint64_t addr)
+ {
+ return BNDSCViewLoadSectionAtAddress(m_object, addr);
+ }
+
+ bool SharedCache::LoadImageContainingAddress(uint64_t addr)
+ {
+ return BNDSCViewLoadImageContainingAddress(m_object, addr);
+ }
+
+ std::vector<std::string> SharedCache::GetAvailableImages()
+ {
+ size_t count;
+ char** value = BNDSCViewGetInstallNames(m_object, &count);
+ if (value == nullptr)
+ {
+ return {};
+ }
+
+ std::vector<std::string> result;
+ for (size_t i = 0; i < count; i++)
+ {
+ result.push_back(value[i]);
+ }
+
+ BNFreeStringList(value, count);
+ return result;
+ }
+
+ std::vector<DSCMemoryRegion> SharedCache::GetLoadedMemoryRegions()
+ {
+ size_t count;
+ BNDSCMappedMemoryRegion* value = BNDSCViewGetLoadedRegions(m_object, &count);
+ if (value == nullptr)
+ {
+ return {};
+ }
+
+ std::vector<DSCMemoryRegion> result;
+ for (size_t i = 0; i < count; i++)
+ {
+ DSCMemoryRegion region;
+ region.vmAddress = value[i].vmAddress;
+ region.size = value[i].size;
+ region.prettyName = value[i].name;
+ result.push_back(region);
+ }
+
+ BNDSCViewFreeLoadedRegions(value, count);
+ return result;
+ }
+ std::vector<BackingCache> SharedCache::GetBackingCaches()
+ {
+ size_t count;
+ BNDSCBackingCache* value = BNDSCViewGetBackingCaches(m_object, &count);
+ if (value == nullptr)
+ {
+ return {};
+ }
+
+ std::vector<BackingCache> result;
+ for (size_t i = 0; i < count; i++)
+ {
+ BackingCache cache;
+ cache.path = value[i].path;
+ cache.isPrimary = value[i].isPrimary;
+ for (size_t j = 0; j < value[i].mappingCount; j++)
+ {
+ BackingCacheMapping mapping;
+ mapping.vmAddress = value[i].mappings[j].vmAddress;
+ mapping.size = value[i].mappings[j].size;
+ mapping.fileOffset = value[i].mappings[j].fileOffset;
+ cache.mappings.push_back(mapping);
+ }
+ result.push_back(cache);
+ }
+
+ BNDSCViewFreeBackingCaches(value, count);
+ return result;
+ }
+
+ std::vector<DSCImage> SharedCache::GetImages()
+ {
+ size_t count;
+ BNDSCImage* value = BNDSCViewGetAllImages(m_object, &count);
+ if (value == nullptr)
+ {
+ return {};
+ }
+
+ std::vector<DSCImage> result;
+ for (size_t i = 0; i < count; i++)
+ {
+ DSCImage img;
+ img.name = value[i].name;
+ img.headerAddress = value[i].headerAddress;
+ for (size_t j = 0; j < value[i].mappingCount; j++)
+ {
+ DSCImageMemoryMapping mapping;
+ mapping.filePath = value[i].mappings[j].filePath;
+ mapping.name = value[i].mappings[j].name;
+ mapping.vmAddress = value[i].mappings[j].vmAddress;
+ mapping.rawViewOffset = value[i].mappings[j].rawViewOffset;
+ mapping.size = value[i].mappings[j].size;
+ mapping.loaded = value[i].mappings[j].loaded;
+ img.mappings.push_back(mapping);
+ }
+ result.push_back(img);
+ }
+
+ BNDSCViewFreeAllImages(value, count);
+ return result;
+ }
+
+ std::vector<DSCSymbol> SharedCache::LoadAllSymbolsAndWait()
+ {
+ size_t count;
+ BNDSCSymbolRep* value = BNDSCViewLoadAllSymbolsAndWait(m_object, &count);
+ if (value == nullptr)
+ {
+ return {};
+ }
+
+ std::vector<DSCSymbol> result;
+ for (size_t i = 0; i < count; i++)
+ {
+ DSCSymbol sym;
+ sym.address = value[i].address;
+ sym.name = value[i].name;
+ sym.image = value[i].image;
+ result.push_back(sym);
+ }
+
+ BNDSCViewFreeSymbols(value, count);
+ return result;
+ }
+
+ std::string SharedCache::GetNameForAddress(uint64_t address)
+ {
+ char* name = BNDSCViewGetNameForAddress(m_object, address);
+ if (name == nullptr)
+ return {};
+ std::string result = name;
+ BNFreeString(name);
+ return result;
+ }
+
+ std::string SharedCache::GetImageNameForAddress(uint64_t address)
+ {
+ char* name = BNDSCViewGetImageNameForAddress(m_object, address);
+ if (name == nullptr)
+ return {};
+ std::string result = name;
+ BNFreeString(name);
+ return result;
+ }
+
+ std::optional<SharedCacheMachOHeader> SharedCache::GetMachOHeaderForImage(std::string name)
+ {
+ char* str = BNAllocString(name.c_str());
+ char* outputStr = BNDSCViewGetImageHeaderForName(m_object, str);
+ if (outputStr == nullptr)
+ return {};
+ std::string output = outputStr;
+ BNFreeString(outputStr);
+ if (output.empty())
+ return {};
+ SharedCacheMachOHeader header;
+ header.LoadFromString(output);
+ return header;
+ }
+
+ std::optional<SharedCacheMachOHeader> SharedCache::GetMachOHeaderForAddress(uint64_t address)
+ {
+ char* outputStr = BNDSCViewGetImageHeaderForAddress(m_object, address);
+ if (outputStr == nullptr)
+ return {};
+ std::string output = outputStr;
+ BNFreeString(outputStr);
+ if (output.empty())
+ return {};
+ SharedCacheMachOHeader header;
+ header.LoadFromString(output);
+ return header;
+ }
+
+ BNDSCViewState SharedCache::GetState()
+ {
+ return BNDSCViewGetState(m_object);
+ }
+
+ void SharedCache::FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis) const
+ {
+ BNDSCFindSymbolAtAddressAndApplyToAddress(m_object, symbolLocation, targetLocation, triggerReanalysis);
+ }
+} // namespace SharedCacheAPI
diff --git a/view/sharedcache/api/sharedcacheapi.h b/view/sharedcache/api/sharedcacheapi.h
new file mode 100644
index 00000000..efd38532
--- /dev/null
+++ b/view/sharedcache/api/sharedcacheapi.h
@@ -0,0 +1,820 @@
+#pragma once
+
+#include <binaryninjaapi.h>
+#include "MetadataSerializable.hpp"
+#include "view/macho/machoview.h"
+#include "sharedcachecore.h"
+
+using namespace BinaryNinja;
+
+namespace SharedCacheAPI {
+ template<class T>
+ class SCRefCountObject {
+ void AddRefInternal() { m_refs.fetch_add(1); }
+
+ void ReleaseInternal() {
+ if (m_refs.fetch_sub(1) == 1)
+ delete this;
+ }
+
+ public:
+ std::atomic<int> m_refs;
+ T *m_object;
+
+ SCRefCountObject() : m_refs(0), m_object(nullptr) {}
+
+ virtual ~SCRefCountObject() {}
+
+ T *GetObject() const { return m_object; }
+
+ static T *GetObject(SCRefCountObject *obj) {
+ if (!obj)
+ return nullptr;
+ return obj->GetObject();
+ }
+
+ void AddRef() { AddRefInternal(); }
+
+ void Release() { ReleaseInternal(); }
+
+ void AddRefForRegistration() { AddRefInternal(); }
+ };
+
+
+ template<class T, T *(*AddObjectReference)(T *), void (*FreeObjectReference)(T *)>
+ class SCCoreRefCountObject {
+ void AddRefInternal() { m_refs.fetch_add(1); }
+
+ void ReleaseInternal() {
+ if (m_refs.fetch_sub(1) == 1) {
+ if (!m_registeredRef)
+ delete this;
+ }
+ }
+
+ public:
+ std::atomic<int> m_refs;
+ bool m_registeredRef = false;
+ T *m_object;
+
+ SCCoreRefCountObject() : m_refs(0), m_object(nullptr) {}
+
+ virtual ~SCCoreRefCountObject() {}
+
+ T *GetObject() const { return m_object; }
+
+ static T *GetObject(SCCoreRefCountObject *obj) {
+ if (!obj)
+ return nullptr;
+ return obj->GetObject();
+ }
+
+ void AddRef() {
+ if (m_object && (m_refs != 0))
+ AddObjectReference(m_object);
+ AddRefInternal();
+ }
+
+ void Release() {
+ if (m_object)
+ FreeObjectReference(m_object);
+ ReleaseInternal();
+ }
+
+ void AddRefForRegistration() { m_registeredRef = true; }
+
+ void ReleaseForRegistration() {
+ m_object = nullptr;
+ m_registeredRef = false;
+ if (m_refs == 0)
+ delete this;
+ }
+ };
+
+
+ template<class T>
+ class SCRef {
+ T *m_obj;
+#ifdef BN_REF_COUNT_DEBUG
+ void* m_assignmentTrace = nullptr;
+#endif
+
+ public:
+ SCRef<T>() : m_obj(NULL) {}
+
+ SCRef<T>(T *obj) : m_obj(obj) {
+ if (m_obj) {
+ m_obj->AddRef();
+#ifdef BN_REF_COUNT_DEBUG
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
+ }
+ }
+
+ SCRef<T>(const SCRef<T> &obj) : m_obj(obj.m_obj) {
+ if (m_obj) {
+ m_obj->AddRef();
+#ifdef BN_REF_COUNT_DEBUG
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
+ }
+ }
+
+ SCRef<T>(SCRef<T> &&other) : m_obj(other.m_obj) {
+ other.m_obj = 0;
+#ifdef BN_REF_COUNT_DEBUG
+ m_assignmentTrace = other.m_assignmentTrace;
+#endif
+ }
+
+ ~SCRef<T>() {
+ if (m_obj) {
+ m_obj->Release();
+#ifdef BN_REF_COUNT_DEBUG
+ BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+#endif
+ }
+ }
+
+ SCRef<T> &operator=(const Ref<T> &obj) {
+#ifdef BN_REF_COUNT_DEBUG
+ if (m_obj)
+ BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+ if (obj.m_obj)
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
+ T *oldObj = m_obj;
+ m_obj = obj.m_obj;
+ if (m_obj)
+ m_obj->AddRef();
+ if (oldObj)
+ oldObj->Release();
+ return *this;
+ }
+
+ SCRef<T> &operator=(SCRef<T> &&other) {
+ if (m_obj) {
+#ifdef BN_REF_COUNT_DEBUG
+ BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+#endif
+ m_obj->Release();
+ }
+ m_obj = other.m_obj;
+ other.m_obj = 0;
+#ifdef BN_REF_COUNT_DEBUG
+ m_assignmentTrace = other.m_assignmentTrace;
+#endif
+ return *this;
+ }
+
+ SCRef<T> &operator=(T *obj) {
+#ifdef BN_REF_COUNT_DEBUG
+ if (m_obj)
+ BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+ if (obj)
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
+ T *oldObj = m_obj;
+ m_obj = obj;
+ if (m_obj)
+ m_obj->AddRef();
+ if (oldObj)
+ oldObj->Release();
+ return *this;
+ }
+
+ operator T *() const {
+ return m_obj;
+ }
+
+ T *operator->() const {
+ return m_obj;
+ }
+
+ T &operator*() const {
+ return *m_obj;
+ }
+
+ bool operator!() const {
+ return m_obj == NULL;
+ }
+
+ bool operator==(const T *obj) const {
+ return T::GetObject(m_obj) == T::GetObject(obj);
+ }
+
+ bool operator==(const SCRef<T> &obj) const {
+ return T::GetObject(m_obj) == T::GetObject(obj.m_obj);
+ }
+
+ bool operator!=(const T *obj) const {
+ return T::GetObject(m_obj) != T::GetObject(obj);
+ }
+
+ bool operator!=(const SCRef<T> &obj) const {
+ return T::GetObject(m_obj) != T::GetObject(obj.m_obj);
+ }
+
+ bool operator<(const T *obj) const {
+ return T::GetObject(m_obj) < T::GetObject(obj);
+ }
+
+ bool operator<(const SCRef<T> &obj) const {
+ return T::GetObject(m_obj) < T::GetObject(obj.m_obj);
+ }
+
+ T *GetPtr() const {
+ return m_obj;
+ }
+ };
+
+ struct DSCMemoryRegion {
+ uint64_t vmAddress;
+ uint64_t size;
+ std::string prettyName;
+ };
+
+ struct BackingCacheMapping {
+ uint64_t vmAddress;
+ uint64_t size;
+ uint64_t fileOffset;
+ };
+
+ struct BackingCache {
+ std::string path;
+ bool isPrimary;
+ std::vector<BackingCacheMapping> mappings;
+ };
+
+ struct DSCImageMemoryMapping {
+ std::string filePath;
+ std::string name;
+ uint64_t vmAddress;
+ uint64_t size;
+ bool loaded;
+ uint64_t rawViewOffset;
+ };
+
+ struct DSCImage {
+ std::string name;
+ uint64_t headerAddress;
+ std::vector<DSCImageMemoryMapping> mappings;
+ };
+
+ struct DSCSymbol {
+ uint64_t address;
+ std::string name;
+ std::string image;
+ };
+
+ 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 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].GetInt64();
+ b.cputype = bArr[1].GetInt64();
+ b.cpusubtype = bArr[2].GetInt64();
+ b.filetype = bArr[3].GetInt64();
+ b.ncmds = bArr[4].GetInt64();
+ b.sizeofcmds = bArr[5].GetInt64();
+ b.flags = bArr[6].GetInt64();
+ b.reserved = bArr[7].GetInt64();
+ }
+
+ 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();
+ memcpy(sec.sectname, sectNameStr.c_str(), sectNameStr.size());
+ std::string segNameStr = s2[1].GetString();
+ 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();
+ 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();
+ 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(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(exportTriePath);
+ MSL(dysymPresent);
+ MSL(dyldInfoPresent);
+ MSL(exportTriePresent);
+ MSL(chainedFixupsPresent);
+ // MSL(routinesPresent);
+ MSL(functionStartsPresent);
+ MSL(relocatable);
+ }
+ };
+
+
+ class SharedCache : public SCCoreRefCountObject<BNSharedCache, BNNewSharedCacheReference, BNFreeSharedCacheReference> {
+ public:
+ SharedCache(Ref<BinaryView> view);
+
+ BNDSCViewState GetState();
+ static BNDSCViewLoadProgress GetLoadProgress(Ref<BinaryView> view);
+ static uint64_t FastGetBackingCacheCount(Ref<BinaryView> view);
+
+ bool LoadImageWithInstallName(std::string installName);
+ bool LoadSectionAtAddress(uint64_t addr);
+ bool LoadImageContainingAddress(uint64_t addr);
+ std::vector<std::string> GetAvailableImages();
+
+ std::vector<DSCSymbol> LoadAllSymbolsAndWait();
+
+ std::string GetNameForAddress(uint64_t address);
+ std::string GetImageNameForAddress(uint64_t address);
+
+ std::vector<BackingCache> GetBackingCaches();
+ std::vector<DSCImage> GetImages();
+
+ std::optional<SharedCacheMachOHeader> GetMachOHeaderForImage(std::string name);
+ std::optional<SharedCacheMachOHeader> GetMachOHeaderForAddress(uint64_t address);
+
+ std::vector<DSCMemoryRegion> GetLoadedMemoryRegions();
+
+ void FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis = true) const;
+ };
+} \ No newline at end of file
diff --git a/view/sharedcache/api/sharedcachecore.h b/view/sharedcache/api/sharedcachecore.h
new file mode 100644
index 00000000..1c382e48
--- /dev/null
+++ b/view/sharedcache/api/sharedcachecore.h
@@ -0,0 +1,156 @@
+#pragma once
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef __GNUC__
+ #ifdef SHAREDCACHE_LIBRARY
+ #define SHAREDCACHE_FFI_API __attribute__((visibility("default")))
+ #else // SHAREDCACHE_LIBRARY
+ #define SHAREDCACHE_FFI_API
+ #endif // SHAREDCACHE_LIBRARY
+#else // __GNUC__
+ #ifdef _MSC_VER
+ #ifndef DEMO_VERSION
+ #ifdef SHAREDCACHE_LIBRARY
+ #define SHAREDCACHE_FFI_API __declspec(dllexport)
+ #else // SHAREDCACHE_LIBRARY
+ #define SHAREDCACHE_FFI_API __declspec(dllimport)
+ #endif // SHAREDCACHE_LIBRARY
+ #else
+ #define SHAREDCACHE_FFI_API
+ #endif
+ #else // _MSC_VER
+ #define SHAREDCACHE_FFI_API
+ #endif // _MSC_VER
+#endif // __GNUC__C
+
+#define CORE_ALLOCATED_STRUCT(T)
+
+#define CORE_ALLOCATED_CLASS(T) \
+ public: \
+ CORE_ALLOCATED_STRUCT(T) \
+ private:
+
+#define DECLARE_SHAREDCACHE_API_OBJECT_INTERNAL(handle, cls, ns) \
+ namespace ns { class cls; } struct handle { ns::cls* object; }
+
+#define DECLARE_SHAREDCACHE_API_OBJECT(handle, cls) DECLARE_SHAREDCACHE_API_OBJECT_INTERNAL(handle, cls, SharedCacheCore)
+
+#define IMPLEMENT_SHAREDCACHE_API_OBJECT(handle) \
+ CORE_ALLOCATED_CLASS(handle) \
+ private: \
+ handle m_apiObject; \
+ public: \
+ typedef handle* APIHandle; \
+ handle* GetAPIObject() { return &m_apiObject; } \
+ private:
+#define INIT_SHAREDCACHE_API_OBJECT() \
+ m_apiObject.object = this;
+
+ typedef enum BNDSCViewState {
+ Unloaded,
+ Loaded,
+ LoadedWithImages,
+ } BNDSCViewState;
+
+ typedef enum BNDSCViewLoadProgress {
+ LoadProgressNotStarted,
+ LoadProgressLoadingCaches,
+ LoadProgressLoadingImages,
+ LoadProgressFinished,
+ } BNDSCViewLoadProgress;
+
+ typedef struct BNBinaryView BNBinaryView;
+ typedef struct BNSharedCache BNSharedCache;
+
+ typedef struct BNDSCImageMemoryMapping {
+ char* filePath;
+ char* name;
+ uint64_t vmAddress;
+ uint64_t size;
+ bool loaded;
+ uint64_t rawViewOffset;
+ } BNDSCImageMemoryMapping;
+
+ typedef struct BNDSCImage {
+ char* name;
+ uint64_t headerAddress;
+ BNDSCImageMemoryMapping* mappings;
+ size_t mappingCount;
+ } BNDSCImage;
+
+ typedef struct BNDSCMappedMemoryRegion {
+ uint64_t vmAddress;
+ uint64_t size;
+ char* name;
+ } BNDSCMappedMemoryRegion;
+
+ typedef struct BNDSCBackingCacheMapping {
+ uint64_t vmAddress;
+ uint64_t size;
+ uint64_t fileOffset;
+ } BNDSCBackingCacheMapping;
+
+ typedef struct BNDSCBackingCache {
+ char* path;
+ bool isPrimary;
+ BNDSCBackingCacheMapping* mappings;
+ size_t mappingCount;
+ } BNDSCBackingCache;
+
+ typedef struct BNDSCMemoryUsageInfo {
+ uint64_t sharedCacheRefs;
+ uint64_t mmapRefs;
+ } BNDSCMemoryUsageInfo;
+
+ typedef struct BNDSCSymbolRep {
+ uint64_t address;
+ char* name;
+ char* image;
+ } BNDSCSymbolRep;
+
+ SHAREDCACHE_FFI_API BNSharedCache* BNGetSharedCache(BNBinaryView* data);
+
+ SHAREDCACHE_FFI_API BNSharedCache* BNNewSharedCacheReference(BNSharedCache* cache);
+ SHAREDCACHE_FFI_API void BNFreeSharedCacheReference(BNSharedCache* cache);
+
+ SHAREDCACHE_FFI_API char** BNDSCViewGetInstallNames(BNSharedCache* cache, size_t* count);
+ SHAREDCACHE_FFI_API uint64_t BNDSCViewLoadedImageCount(BNSharedCache* cache);
+
+ SHAREDCACHE_FFI_API bool BNDSCViewLoadImageWithInstallName(BNSharedCache* cache, char* name);
+ SHAREDCACHE_FFI_API bool BNDSCViewLoadSectionAtAddress(BNSharedCache* cache, uint64_t name);
+ SHAREDCACHE_FFI_API bool BNDSCViewLoadImageContainingAddress(BNSharedCache* cache, uint64_t address);
+
+ SHAREDCACHE_FFI_API char* BNDSCViewGetNameForAddress(BNSharedCache* cache, uint64_t address);
+ SHAREDCACHE_FFI_API char* BNDSCViewGetImageNameForAddress(BNSharedCache* cache, uint64_t address);
+
+ SHAREDCACHE_FFI_API BNDSCViewState BNDSCViewGetState(BNSharedCache* cache);
+ SHAREDCACHE_FFI_API BNDSCViewLoadProgress BNDSCViewGetLoadProgress(uint64_t sessionID);
+ SHAREDCACHE_FFI_API uint64_t BNDSCViewFastGetBackingCacheCount(BNBinaryView* view);
+
+ SHAREDCACHE_FFI_API BNDSCSymbolRep* BNDSCViewLoadAllSymbolsAndWait(BNSharedCache* cache, size_t* count);
+ SHAREDCACHE_FFI_API void BNDSCViewFreeSymbols(BNDSCSymbolRep* symbols, size_t count);
+
+ SHAREDCACHE_FFI_API BNDSCMappedMemoryRegion* BNDSCViewGetLoadedRegions(BNSharedCache* cache, size_t* count);
+ SHAREDCACHE_FFI_API void BNDSCViewFreeLoadedRegions(BNDSCMappedMemoryRegion* images, size_t count);
+
+ SHAREDCACHE_FFI_API BNDSCImage* BNDSCViewGetAllImages(BNSharedCache* cache, size_t* count);
+ SHAREDCACHE_FFI_API void BNDSCViewFreeAllImages(BNDSCImage* images, size_t count);
+
+ SHAREDCACHE_FFI_API BNDSCBackingCache* BNDSCViewGetBackingCaches(BNSharedCache* cache, size_t* count);
+ SHAREDCACHE_FFI_API void BNDSCViewFreeBackingCaches(BNDSCBackingCache* caches, size_t count);
+
+ SHAREDCACHE_FFI_API void BNDSCFindSymbolAtAddressAndApplyToAddress(BNSharedCache* cache, uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis);
+
+ SHAREDCACHE_FFI_API char* BNDSCViewGetImageHeaderForAddress(BNSharedCache* cache, uint64_t address);
+ SHAREDCACHE_FFI_API char* BNDSCViewGetImageHeaderForName(BNSharedCache* cache, char* name);
+
+ [[maybe_unused]] SHAREDCACHE_FFI_API BNDSCMemoryUsageInfo BNDSCViewGetMemoryUsageInfo();
+
+#ifdef __cplusplus
+}
+#endif
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
diff --git a/view/sharedcache/ui/CMakeLists.txt b/view/sharedcache/ui/CMakeLists.txt
new file mode 100644
index 00000000..04e2f3cb
--- /dev/null
+++ b/view/sharedcache/ui/CMakeLists.txt
@@ -0,0 +1,72 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(sharedcacheui)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+find_package(Qt6 COMPONENTS Core Gui Widgets REQUIRED)
+
+file(GLOB SOURCES *.cpp *.h)
+list(FILTER SOURCES EXCLUDE REGEX moc_.*)
+list(FILTER SOURCES EXCLUDE REGEX qrc_.*)
+
+add_library(sharedcacheui SHARED ${SOURCES})
+
+if (VIEW_NAME)
+ target_compile_definitions(sharedcacheui PRIVATE VIEW_NAME="${VIEW_NAME}")
+else()
+ error("VIEW_NAME must be defined")
+endif()
+
+if(BN_INTERNAL_BUILD)
+ set_target_properties(sharedcacheui PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR})
+else()
+ set_target_properties(sharedcacheui PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/plugins
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/plugins
+ )
+endif()
+
+set_target_properties(sharedcacheui PROPERTIES
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+ CXX_VISIBILITY_PRESET hidden
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON
+ )
+
+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(sharedcacheapi INCLUDES)
+
+target_include_directories(sharedcacheui PRIVATE ${INCLUDES})
+
+target_link_libraries(sharedcacheui sharedcacheapi sharedcache binaryninjaui Qt6::Core Qt6::Gui Qt6::Widgets)
+
diff --git a/view/sharedcache/ui/Plugin.cpp b/view/sharedcache/ui/Plugin.cpp
new file mode 100644
index 00000000..1d52fd6a
--- /dev/null
+++ b/view/sharedcache/ui/Plugin.cpp
@@ -0,0 +1,25 @@
+//
+// Created by kat on 8/6/24.
+//
+#include <binaryninjaapi.h>
+#include "SharedCacheUINotifications.h"
+#include "dsctriage.h"
+
+extern "C"
+{
+ BN_DECLARE_CORE_ABI_VERSION
+ BN_DECLARE_UI_ABI_VERSION
+
+ BINARYNINJAPLUGIN bool UIPluginInit()
+ {
+ UINotifications::init();
+ UIAction::registerAction("Load Image by Name");
+ UIAction::registerAction("Load Section by Address");
+ UIAction::registerAction("Load ADDRHERE");
+ UIAction::registerAction("Load IMGHERE");
+
+ DSCTriageViewType::Register();
+
+ return true;
+ }
+} \ No newline at end of file
diff --git a/view/sharedcache/ui/SharedCacheBDNotifications.cpp b/view/sharedcache/ui/SharedCacheBDNotifications.cpp
new file mode 100644
index 00000000..f59f313c
--- /dev/null
+++ b/view/sharedcache/ui/SharedCacheBDNotifications.cpp
@@ -0,0 +1,69 @@
+//
+// Created by kat on 8/22/24.
+//
+
+#include "SharedCacheBDNotifications.h"
+
+
+SharedCacheBDNotifications::SharedCacheBDNotifications(Ref<BinaryView> view)
+ : BinaryDataNotification(FunctionUpdates | DataVariableUpdates)
+{
+}
+
+void SharedCacheBDNotifications::OnAnalysisFunctionAdded(BinaryView* view, Function* func)
+{
+ //
+ // We just cannot do this until one of:
+ // "Component::AddAutoFunction"
+ // BinaryView::BeginIgnoredUndoActions
+ // some similar fix
+
+ /*
+ if (view->GetTypeName() == VIEW_NAME)
+ {
+ auto sections = view->GetSectionsAt(func->GetStart());
+ if (sections.size() > 0)
+ {
+ auto section = sections[0];
+ auto imageName = section->GetName().substr(0, section->GetName().find("::"));
+ auto id = view->BeginUndoActions();
+ auto comp = view->GetComponentByPath(imageName);
+ if (!comp)
+ {
+ comp = view->CreateComponentWithName(imageName);
+ }
+ comp.value()->AddFunction(func);
+ view->ForgetUndoActions(id);
+ }
+ }
+ */
+}
+
+
+void SharedCacheBDNotifications::OnSectionAdded(BinaryView* data, Section* section)
+{
+
+}
+
+
+void SharedCacheBDNotifications::OnDataVariableAdded(BinaryView* view, const DataVariable& var)
+{
+ /*
+ if (view->GetTypeName() == VIEW_NAME)
+ {
+ auto sections = view->GetSectionsAt(var.address);
+ if (sections.size() > 0)
+ {
+ auto section = sections[0];
+ auto imageName = section->GetName().substr(0, section->GetName().find("::"));
+ auto comp = view->GetComponentByPath(imageName);
+ auto id = view->BeginUndoActions();
+ if (!comp)
+ {
+ comp = view->CreateComponentWithName(imageName);
+ }
+ comp.value()->AddDataVariable(var);
+ view->ForgetUndoActions(id);
+ }
+ }*/
+}
diff --git a/view/sharedcache/ui/SharedCacheBDNotifications.h b/view/sharedcache/ui/SharedCacheBDNotifications.h
new file mode 100644
index 00000000..632fca28
--- /dev/null
+++ b/view/sharedcache/ui/SharedCacheBDNotifications.h
@@ -0,0 +1,20 @@
+//
+// Created by kat on 8/22/24.
+//
+
+#pragma once
+
+#include <binaryninjaapi.h>
+#include "ui/uicontext.h"
+#include "SharedCacheUINotifications.h"
+
+using namespace BinaryNinja;
+
+class SharedCacheBDNotifications : public BinaryDataNotification
+{
+public:
+ SharedCacheBDNotifications(Ref<BinaryView> view);
+ void OnAnalysisFunctionAdded(BinaryView* view, Function* func) override;
+ void OnDataVariableAdded(BinaryView* view, const DataVariable& var) override;
+ void OnSectionAdded(BinaryView* data, Section* section) override;
+};
diff --git a/view/sharedcache/ui/SharedCacheUINotifications.cpp b/view/sharedcache/ui/SharedCacheUINotifications.cpp
new file mode 100644
index 00000000..6c64c9ef
--- /dev/null
+++ b/view/sharedcache/ui/SharedCacheUINotifications.cpp
@@ -0,0 +1,143 @@
+//
+// Created by kat on 5/8/23.
+//
+
+#include "SharedCacheUINotifications.h"
+#include <QLayout>
+#include <sharedcacheapi.h>
+#include "ui/sidebar.h"
+#include "ui/linearview.h"
+#include "ui/viewframe.h"
+#include "dscpicker.h"
+#include "progresstask.h"
+#include "SharedCacheBDNotifications.h"
+
+UINotifications* UINotifications::m_instance = nullptr;
+
+void UINotifications::init()
+{
+ m_instance = new UINotifications;
+ UIContext::registerNotification(m_instance);
+}
+
+
+void UINotifications::OnViewChange(UIContext* context, ViewFrame* frame, const QString& type)
+{
+ if (!frame)
+ return;
+
+ // FIXME there is a bv func for this
+ static std::function<bool(Ref<BinaryView>, uint64_t)> isAddrMapped = [](Ref<BinaryView> view, uint64_t addr) {
+ if (view && view->GetTypeName() == VIEW_NAME)
+ {
+ for (const auto& seg : view->GetSegments())
+ {
+ if (seg->GetStart() <= addr && seg->GetEnd() > addr)
+ return true;
+ }
+ }
+ return false;
+ };
+
+ auto view = frame->getCurrentBinaryView();
+ if (view && view->GetTypeName() == VIEW_NAME)
+ {
+ if (auto viewInt = frame->getCurrentViewInterface())
+ {
+ auto ah = viewInt->actionHandler();
+ if (!ah->isBoundAction("Load Image by Name"))
+ {
+ ah->bindAction("Load Image by Name", UIAction([view = view](const UIActionContext& ctx) {
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache = new SharedCacheAPI::SharedCache(view);
+ DisplayDSCPicker(ctx.context, view);
+ }));
+ ah->bindAction("Load Section by Address", UIAction([view = view](const UIActionContext& ctx) {
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache = new SharedCacheAPI::SharedCache(ctx.binaryView);
+ uint64_t addr = 0;
+ bool gotAddr = GetAddressInput(addr, "Address", "Address");
+ if (gotAddr)
+ {
+ BackgroundThread::create(ctx.context->mainWindow())->thenBackground(
+ [cache=cache, addr=addr]() {
+ cache->LoadSectionAtAddress(addr);
+ })->start();
+ }
+ }));
+ ah->bindAction("Load ADDRHERE",
+ UIAction(
+ [](const UIActionContext& ctx) {
+ Ref<BinaryView> view = ctx.binaryView;
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache = new SharedCacheAPI::SharedCache(ctx.binaryView);
+ uint64_t addr = ctx.token.token.value;
+ if (addr)
+ {
+ BackgroundThread::create(ctx.context->mainWindow())->thenBackground(
+ [cache=cache, addr=addr]() {
+ cache->LoadSectionAtAddress(addr);
+ })->start();
+ }
+ },
+ [](const UIActionContext& ctx) {
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache = new SharedCacheAPI::SharedCache(ctx.binaryView);
+ uint64_t addr = ctx.token.token.value;
+ if (isAddrMapped(ctx.binaryView, addr))
+ return false;
+ return addr && cache->GetNameForAddress(addr) != ""; // bool
+ }));
+ ah->bindAction("Load IMGHERE",
+ UIAction(
+ [](const UIActionContext& ctx) {
+ Ref<BinaryView> view = ctx.binaryView;
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache = new SharedCacheAPI::SharedCache(view);
+ uint64_t addr = ctx.token.token.value;
+ if (addr)
+ {
+ BackgroundThread::create(ctx.context->mainWindow())->thenBackground(
+ [cache=cache, addr=addr]() {
+ cache->LoadImageContainingAddress(addr);
+ })->start();
+ }
+ },
+ [](const UIActionContext& ctx) {
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache = new SharedCacheAPI::SharedCache(ctx.binaryView);
+ uint64_t addr = ctx.token.token.value;
+ if (isAddrMapped(ctx.binaryView, addr))
+ return false;
+ return addr && cache->GetImageNameForAddress(addr) != ""; // bool
+ }));
+ ah->setActionDisplayName("Load ADDRHERE", [](const UIActionContext& ctx) {
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache = new SharedCacheAPI::SharedCache(ctx.binaryView);
+ uint64_t addr = ctx.token.token.value;
+ if (addr)
+ return QString("Load ") + cache->GetNameForAddress(addr).c_str();
+ return QString("Error");
+ });
+ ah->setActionDisplayName("Load IMGHERE", [](const UIActionContext& ctx) {
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache = new SharedCacheAPI::SharedCache(ctx.binaryView);
+ uint64_t addr = ctx.token.token.value;
+ if (addr)
+ return QString("Load ") + cache->GetImageNameForAddress(addr).c_str();
+ return QString("Error");
+ });
+ if (auto linearView = qobject_cast<LinearView*>(viewInt->widget()))
+ {
+ linearView->contextMenu().addAction("Load ADDRHERE", VIEW_NAME);
+ linearView->contextMenu().addAction("Load IMGHERE", VIEW_NAME);
+ linearView->contextMenu().addAction("Load Image by Name", "DSCView2");
+ linearView->contextMenu().addAction("Load Section by Address", "DSCView2");
+ linearView->contextMenu().setGroupOrdering(VIEW_NAME, 0);
+ linearView->contextMenu().setGroupOrdering("DSCView2", 1);
+ }
+ }
+ }
+ }
+}
+void UINotifications::OnAfterOpenFile(UIContext* context, FileContext* file, ViewFrame* frame)
+{
+ if (frame->getCurrentBinaryView())
+ {
+ auto listener = new SharedCacheBDNotifications(frame->getCurrentBinaryView());
+ frame->getCurrentBinaryView()->RegisterNotification(listener);
+ }
+ UIContextNotification::OnAfterOpenFile(context, file, frame);
+}
diff --git a/view/sharedcache/ui/SharedCacheUINotifications.h b/view/sharedcache/ui/SharedCacheUINotifications.h
new file mode 100644
index 00000000..9ea4b50d
--- /dev/null
+++ b/view/sharedcache/ui/SharedCacheUINotifications.h
@@ -0,0 +1,23 @@
+//
+// Created by kat on 5/8/23.
+//
+#include "ui/uicontext.h"
+
+#ifndef SHAREDCACHE_NOTIFICATIONS_H
+#define SHAREDCACHE_NOTIFICATIONS_H
+
+class UINotifications : public UIContextNotification {
+ static UINotifications* m_instance;
+
+ std::vector<size_t> m_sessionsAlreadyDisplayedPickerFor;
+
+public:
+ virtual void OnViewChange(UIContext *context, ViewFrame *frame, const QString &type) override;
+ // bool OnAfterOpenDatabase(UIContext* context, FileMetadataRef metadata, BinaryViewRef data) override;
+ void OnAfterOpenFile(UIContext* context, FileContext* file, ViewFrame* frame) override;
+
+ static void init();
+};
+
+
+#endif //SHAREDCACHE_NOTIFICATIONS_H
diff --git a/view/sharedcache/ui/dscpicker.cpp b/view/sharedcache/ui/dscpicker.cpp
new file mode 100644
index 00000000..33877675
--- /dev/null
+++ b/view/sharedcache/ui/dscpicker.cpp
@@ -0,0 +1,42 @@
+//
+// Created by kat on 5/22/23.
+//
+
+#include "dscpicker.h"
+#include <sharedcacheapi.h>
+#include "progresstask.h"
+
+#include <utility>
+
+using namespace BinaryNinja;
+
+void DisplayDSCPicker(UIContext* ctx, Ref<BinaryView> dscView)
+{
+ BackgroundThread::create(ctx ? ctx->mainWindow() : nullptr)->thenBackground(
+ [dscView=dscView](QVariant var) {
+ QStringList entries;
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache = new SharedCacheAPI::SharedCache(dscView);
+
+ for (const auto& img : cache->GetAvailableImages())
+ entries.push_back(QString::fromStdString(img));
+
+ return entries;
+ })->thenMainThread([ctx](QVariant var){
+ QStringList entries = var.toStringList();
+
+ auto choiceDialog = new MetadataChoiceDialog(ctx ? ctx->mainWindow() : nullptr, "Pick Image", "Select", entries);
+ choiceDialog->AddWidthRequiredByItem(ctx, 300);
+ choiceDialog->AddHeightRequiredByItem(ctx, 150);
+ choiceDialog->exec();
+
+ if (choiceDialog->GetChosenEntry().has_value())
+ return QVariant(QString::fromStdString(entries.at((qsizetype)choiceDialog->GetChosenEntry().value().idx).toStdString()));
+ else
+ return QVariant("");
+ })->thenBackground([dscView=dscView](QVariant var){
+ if (var.toString().isEmpty())
+ return;
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache = new SharedCacheAPI::SharedCache(dscView);
+ cache->LoadImageWithInstallName(var.toString().toStdString());
+ })->start();
+}
diff --git a/view/sharedcache/ui/dscpicker.h b/view/sharedcache/ui/dscpicker.h
new file mode 100644
index 00000000..6c4b15b5
--- /dev/null
+++ b/view/sharedcache/ui/dscpicker.h
@@ -0,0 +1,12 @@
+//
+// Created by kat on 5/22/23.
+//
+
+#ifndef SHAREDCACHE_DSCPICKER_H
+#define SHAREDCACHE_DSCPICKER_H
+
+#include <binaryninjaapi.h>
+#include <ui/metadatachoicedialog.h>
+void DisplayDSCPicker(UIContext* ctx = nullptr, BinaryNinja::Ref<BinaryNinja::BinaryView> dscView = nullptr);
+
+#endif //SHAREDCACHE_DSCPICKER_H
diff --git a/view/sharedcache/ui/dsctriage.cpp b/view/sharedcache/ui/dsctriage.cpp
new file mode 100644
index 00000000..0dca96bf
--- /dev/null
+++ b/view/sharedcache/ui/dsctriage.cpp
@@ -0,0 +1,1012 @@
+//
+// Created by kat on 8/15/24.
+//
+
+#include "dsctriage.h"
+#include "ui/fontsettings.h"
+#include <QPainter>
+#include <QTextBrowser>
+#include "tabwidget.h"
+#include "globalarea.h"
+#include "progresstask.h"
+
+#include <cmath>
+#include <QMessageBox>
+
+
+#define QSETTINGS_KEY_SELECTED_TAB "DSCTriage-SelectedTab"
+#define QSETTINGS_KEY_TAB_LAYOUT "DSCTriage-TabLayout"
+#define QSETTINGS_KEY_IMAGELOAD_TAB_LAYOUT "DSCTriage-ImageLoadTabLayout"
+#define QSETTINGS_KEY_ALPHA_POPUP_SEEN "DSCTriage-AlphaPopupSeen"
+
+
+DSCCacheBlocksView::DSCCacheBlocksView(QWidget* parent, BinaryViewRef data, SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache)
+ : QWidget(parent), m_data(data), m_cache(cache)
+{
+ setMouseTracking(true);
+ m_backingCacheCount = SharedCacheAPI::SharedCache::FastGetBackingCacheCount(data);
+ m_blockLuminance.resize(m_backingCacheCount, 128);
+ m_blockSizeRatios.resize(m_backingCacheCount, 1);
+ m_currentProgress = m_cache->GetLoadProgress(data);
+ m_targetBlockSizeForAnimation.resize(m_backingCacheCount, 0);
+
+ m_blockWaveAnimation = Animation::create(this)
+ ->withDuration(1200)
+ ->withEasingCurve(QEasingCurve::Linear)
+ ->thenOnValueChanged([this](double v)
+ {
+ for (size_t i = 0; i < m_backingCacheCount; i++)
+ {
+ // Create a wave effect.
+ // We use sine to create the initial wave effect, and then cube it to make it more pronounced.
+ m_blockLuminance[i] = 128 + 95 * (pow((sin(v * 2 * M_PI + i * M_PI / m_backingCacheCount) + 1) / 2, 3));
+ }
+ update();
+ })
+ ->thenOnEnd([this](QAbstractAnimation::Direction)
+ {
+ m_currentProgress = m_cache->GetLoadProgress(m_data);
+ if (m_currentProgress == BNDSCViewLoadProgress::LoadProgressFinished)
+ {
+ m_backingCaches = m_cache->GetBackingCaches();
+ m_blockExpandAnimation->start();
+ }
+ else
+ {
+ m_blockWaveAnimation->start();
+ }
+ });
+ m_blockExpandAnimation = Animation::create(this)
+ ->withDuration(600)
+ ->withEasingCurve(QEasingCurve::InOutCirc)
+ ->thenOnStart([this](QAbstractAnimation::Direction)
+ {
+ uint64_t totalSize = 0;
+ uint64_t sumCountForAvg = 0;
+ for (size_t i = 0; i < m_backingCacheCount; i++)
+ {
+ const auto& backingCache = m_backingCaches[i];
+ double sizeSum = 0.0;
+
+ for (const auto& mapping : backingCache.mappings)
+ {
+ sizeSum += mapping.size;
+ }
+ m_targetBlockSizeForAnimation[i] = sizeSum;
+ totalSize += sizeSum;
+ sumCountForAvg++;
+ }
+
+ uint64_t avgSize = totalSize / sumCountForAvg;
+
+ for (size_t i = 0; i < m_backingCacheCount; i++)
+ {
+ m_blockSizeRatios[i] = avgSize;
+ }
+
+ m_averageBlockSizeForAnimationInterp = avgSize;
+ })
+ ->thenOnValueChanged([this](double v)
+ {
+ for (size_t i = 0; i < m_backingCacheCount; i++)
+ {
+ m_blockSizeRatios[i] = m_averageBlockSizeForAnimationInterp + (v/2) * (m_targetBlockSizeForAnimation[i] - ((1.0 - (v/2)) * m_averageBlockSizeForAnimationInterp));
+
+ // Adjust luminance based on animation progress
+ m_blockLuminance[i] = 128 + (63 * v);
+ }
+ update();
+ })
+ ->thenOnEnd([this](QAbstractAnimation::Direction)
+ {
+ std::fill(m_blockLuminance.begin(), m_blockLuminance.end(), 191);
+ update();
+ // wait 300, somehow
+ emit loadDone();
+ m_selectedBlock = 0;
+ m_blockAutoselectAnimation->start();
+ });
+
+ m_blockAutoselectAnimation = Animation::create(this)
+ ->withDuration(100)
+ ->withEasingCurve(QEasingCurve::InOutCirc)
+ ->thenOnValueChanged([this](double v){
+ m_blockLuminance[0] = 191 + (64 * v);
+ update();
+ })
+ ->thenOnEnd([this](QAbstractAnimation::Direction)
+ {
+ emit selectionChanged(m_backingCaches[0], true);
+ });
+
+ m_blockWaveAnimation->setDirection(QAbstractAnimation::Backward);
+ m_blockWaveAnimation->start();
+
+}
+
+DSCCacheBlocksView::~DSCCacheBlocksView()
+{
+
+}
+
+void DSCCacheBlocksView::mousePressEvent(QMouseEvent* event)
+{
+ if (m_currentProgress != BNDSCViewLoadProgress::LoadProgressFinished
+ || m_selectedBlock == -1)
+ {
+ return;
+ }
+ int blockIndex = getBlockIndexAtPosition(event->pos());
+ blockSelected(blockIndex);
+ QWidget::mousePressEvent(event);
+}
+
+
+void DSCCacheBlocksView::mouseReleaseEvent(QMouseEvent* event)
+{
+ QWidget::mouseReleaseEvent(event);
+}
+
+
+void DSCCacheBlocksView::mouseDoubleClickEvent(QMouseEvent* event)
+{
+ QWidget::mouseDoubleClickEvent(event);
+}
+
+
+void DSCCacheBlocksView::mouseMoveEvent(QMouseEvent* event)
+{
+ if (m_selectedBlock == -1)
+ {
+ return;
+ }
+ uint64_t hoveredIndex = getBlockIndexAtPosition(event->pos());
+ std::fill(m_blockLuminance.begin(), m_blockLuminance.end(), 191);
+ if (hoveredIndex != -1)
+ {
+ m_blockLuminance[hoveredIndex] = 255 - 32;
+ }
+ m_blockLuminance[m_selectedBlock] = 255;
+ update();
+}
+
+
+void DSCCacheBlocksView::keyPressEvent(QKeyEvent* event)
+{
+ QWidget::keyPressEvent(event);
+}
+
+
+void DSCCacheBlocksView::keyReleaseEvent(QKeyEvent* event)
+{
+ QWidget::keyReleaseEvent(event);
+ if (m_selectedBlock == -1)
+ {
+ return;
+ }
+
+ // left/right arrows, inc/dec m_selectedBlock
+ if (event->key() == Qt::Key_Left)
+ {
+ if (m_selectedBlock > 0)
+ {
+ blockSelected(m_selectedBlock - 1);
+ }
+ }
+ else if (event->key() == Qt::Key_Right)
+ {
+ if (m_selectedBlock < m_backingCacheCount - 1)
+ {
+ blockSelected(m_selectedBlock + 1);
+ }
+ }
+}
+
+
+void DSCCacheBlocksView::focusInEvent(QFocusEvent* event)
+{
+ QWidget::focusInEvent(event);
+}
+
+
+void DSCCacheBlocksView::focusOutEvent(QFocusEvent* event)
+{
+ QWidget::focusOutEvent(event);
+}
+
+
+void DSCCacheBlocksView::enterEvent(QEnterEvent* event)
+{
+ QWidget::enterEvent(event);
+}
+
+
+void DSCCacheBlocksView::leaveEvent(QEvent* event)
+{
+ QWidget::leaveEvent(event);
+}
+
+void DSCCacheBlocksView::paintEvent(QPaintEvent* event)
+{
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+
+ // Initial X position and total width of the widget
+ int totalWidth = this->width();
+ int totalHeight = 30; // Height of the rectangles
+ int totalSpacing = (m_blockSizeRatios.size() - 1) * 5;
+ int availableWidth = totalWidth - (50 * 2) - totalSpacing; // availableWidth minus the initial padding
+
+ // Calculate the total ratio of block sizes
+ uint64_t totalRatio = 0;
+ for (const auto& ratio : m_blockSizeRatios) {
+ totalRatio += ratio;
+ }
+
+ std::vector<int> originalWidths;
+ originalWidths.resize(m_blockSizeRatios.size(), (availableWidth / m_blockSizeRatios.size()));
+
+
+ // Calculate center points for each block
+ std::vector<int> centers;
+ centers.reserve(m_blockSizeRatios.size());
+ int currentX = 50;
+ for (size_t i = 0; i < originalWidths.size(); ++i) {
+ centers.push_back(currentX + (originalWidths[i] / 2)); // Store the center point
+ currentX += originalWidths[i] + 5; // Update currentX for the next block
+ }
+
+ // Now draw the blocks, adjusting the position to keep the center point constant
+ currentX = 50;
+ uint64_t lastBlockEnd = currentX - 5;
+ for (size_t i = 0; i < m_blockSizeRatios.size(); ++i) {
+ // Recalculate the width during animation
+ uint64_t adjustedAvailableWidth = availableWidth * m_blockSizeRatios[i];
+ int blockWidth = std::max(10, static_cast<int>(adjustedAvailableWidth / totalRatio));
+
+ // Calculate the new X position to maintain the center
+ int newX = centers[i] - (blockWidth / 2);
+ if (newX > lastBlockEnd + 5)
+ {
+ int diff = newX - (lastBlockEnd + 5);
+ newX -= diff;
+ blockWidth += diff;
+ }
+ if (newX < lastBlockEnd + 5)
+ {
+ int diff = (lastBlockEnd + 5) - newX;
+ newX += diff;
+ blockWidth -= diff;
+ }
+ lastBlockEnd = newX + blockWidth;
+
+ QRect blockRect(newX, (height() - totalHeight) / 2, blockWidth, totalHeight);
+ QColor blockColor(m_blockLuminance[i], m_blockLuminance[i], m_blockLuminance[i]);
+ painter.setBrush(blockColor);
+ painter.setPen(blockColor);
+ painter.drawRect(blockRect);
+
+ currentX += blockWidth + 5; // Move to the next block's position
+ }
+}
+
+
+int DSCCacheBlocksView::getBlockIndexAtPosition(const QPoint& clickPosition)
+{
+ // Initial X position and total width of the widget
+ int totalWidth = this->width();
+ int totalHeight = 50; // Height of the rectangles
+ int totalSpacing = (m_blockSizeRatios.size() - 1) * 5;
+ int availableWidth = totalWidth - (50 * 2) - totalSpacing; // availableWidth minus the initial padding
+
+ // Calculate the total ratio of block sizes
+ uint64_t totalRatio = 0;
+ for (const auto& ratio : m_blockSizeRatios)
+ {
+ totalRatio += ratio;
+ }
+
+ // Calculate center points for each block
+ std::vector<int> originalWidths;
+ originalWidths.resize(m_blockSizeRatios.size(), (availableWidth / m_blockSizeRatios.size()));
+
+ std::vector<int> centers;
+ centers.reserve(m_blockSizeRatios.size());
+ int currentX = 50;
+ for (size_t i = 0; i < originalWidths.size(); ++i)
+ {
+ centers.push_back(currentX + (originalWidths[i] / 2)); // Store the center point
+ currentX += originalWidths[i] + 5; // Update currentX for the next block
+ }
+
+ // Now find the block that contains the click
+ currentX = 50;
+ uint64_t lastBlockEnd = currentX - 5;
+ for (size_t i = 0; i < m_blockSizeRatios.size(); ++i)
+ {
+ // Recalculate the width during animation
+ uint64_t adjustedAvailableWidth = availableWidth * m_blockSizeRatios[i];
+ int blockWidth = std::max(10, static_cast<int>(adjustedAvailableWidth / totalRatio));
+
+ // Calculate the new X position to maintain the center
+ int newX = centers[i] - (blockWidth / 2);
+ if (newX > lastBlockEnd + 5)
+ {
+ int diff = newX - (lastBlockEnd + 5);
+ newX -= diff;
+ blockWidth += diff;
+ }
+ if (newX < lastBlockEnd + 5)
+ {
+ int diff = (lastBlockEnd + 5) - newX;
+ newX += diff;
+ blockWidth -= diff;
+ }
+ lastBlockEnd = newX + blockWidth;
+
+ // Check if the clickPosition is inside the current block's rectangle
+ QRect blockRect(newX, (height() - totalHeight) / 2, blockWidth, totalHeight);
+ if (blockRect.contains(clickPosition))
+ {
+ return static_cast<int>(i); // Return the index of the clicked block
+ }
+
+ currentX += blockWidth + 5; // Move to the next block's position
+ }
+
+ return -1; // Return -1 if no block was clicked
+}
+
+
+void DSCCacheBlocksView::blockSelected(int index)
+{
+ std::fill(m_blockLuminance.begin(), m_blockLuminance.end(), 191);
+ m_selectedBlock = index;
+ if (index != -1)
+ m_blockLuminance[index] = 255;
+ update();
+ if (index != -1)
+ emit selectionChanged(m_backingCaches[index], false);
+}
+
+
+void DSCCacheBlocksView::resizeEvent(QResizeEvent* event)
+{
+ QWidget::resizeEvent(event);
+}
+
+
+QSize DSCCacheBlocksView::sizeHint() const
+{
+ return QWidget::sizeHint();
+}
+
+
+QSize DSCCacheBlocksView::minimumSizeHint() const
+{
+ return QWidget::minimumSizeHint();
+}
+
+
+SymbolTableModel::SymbolTableModel(SymbolTableView* parent)
+ : QAbstractTableModel(parent), m_parent(parent) {
+}
+
+int SymbolTableModel::rowCount(const QModelIndex& parent) const {
+ Q_UNUSED(parent);
+ return static_cast<int>(m_symbols.size());
+}
+
+int SymbolTableModel::columnCount(const QModelIndex& parent) const {
+ Q_UNUSED(parent);
+ // We have 3 columns: Address, Name, and Image
+ return 3;
+}
+
+QVariant SymbolTableModel::data(const QModelIndex& index, int role) const {
+ if (!index.isValid() || role != Qt::DisplayRole) {
+ return QVariant();
+ }
+
+ const SharedCacheAPI::DSCSymbol& symbol = m_symbols.at(index.row());
+
+ switch (index.column()) {
+ case 0: // Address column
+ return QString("0x%1").arg(symbol.address, 0, 16); // Display address as hexadecimal
+ case 1: // Name column
+ return QString::fromStdString(symbol.name);
+ case 2: // Image column
+ return QString::fromStdString(symbol.image);
+ default:
+ return QVariant();
+ }
+}
+
+QVariant SymbolTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
+ if (role != Qt::DisplayRole || orientation != Qt::Horizontal) {
+ return QVariant();
+ }
+
+ switch (section) {
+ case 0:
+ return QString("Address");
+ case 1:
+ return QString("Name");
+ case 2:
+ return QString("Image");
+ default:
+ return QVariant();
+ }
+}
+
+void SymbolTableModel::updateSymbols() {
+ m_symbols = m_parent->m_symbols;
+ setFilter(m_filter);
+}
+
+const SharedCacheAPI::DSCSymbol& SymbolTableModel::symbolAt(int row) const {
+ return m_symbols.at(row);
+}
+
+
+void SymbolTableModel::setFilter(std::string text)
+{
+ beginResetModel();
+
+ m_filter = text;
+ m_symbols.clear();
+
+ if (m_filter.empty())
+ {
+ m_symbols = m_parent->m_symbols;
+ }
+ else
+ {
+ m_symbols.reserve(m_parent->m_symbols.size());
+ for (const auto& symbol : m_parent->m_symbols)
+ {
+ if (symbol.name.find(m_filter) != std::string::npos)
+ {
+ m_symbols.push_back(symbol);
+ }
+ }
+ m_symbols.shrink_to_fit();
+ }
+
+ endResetModel();
+}
+
+
+SymbolTableView::SymbolTableView(QWidget* parent, SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache)
+ : m_model(new SymbolTableModel(this)){
+
+ // Set up the filter model
+ setModel(m_model);
+
+ // Configure view settings
+ horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ setSelectionMode(QAbstractItemView::SingleSelection);
+
+ BackgroundThread::create(this)->thenBackground([this, cache=cache](){
+ // LogInfo("Symbol Search: Loading symbols...");
+ m_symbols = cache->LoadAllSymbolsAndWait();
+ // LogInfo("Symbol Search: Loaded 0x%zx symbols", m_symbols.size());
+ })->thenMainThread([this](){
+ m_model->updateSymbols();
+ })->start();
+}
+
+SymbolTableView::~SymbolTableView() {
+ delete m_model;
+}
+
+void SymbolTableView::setFilter(const std::string& filter) {
+ m_model->setFilter(filter);
+}
+
+
+DSCTriageView::DSCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(parent), View(), m_data(data), m_cache(new SharedCacheAPI::SharedCache(data))
+{
+ setBinaryDataNavigable(false);
+ setupView(this);
+
+ m_triageCollection = new DockableTabCollection();
+ m_triageTabs = new SplitTabWidget(m_triageCollection);
+
+ auto triageTabStyle = new GlobalAreaTabStyle();
+ m_triageTabs->setTabStyle(triageTabStyle);
+
+ auto cacheInfoWidget = new QWidget;
+ auto cacheInfoLayout = new QVBoxLayout(cacheInfoWidget);
+
+ QSplitter* containerWidget = new QSplitter;
+ containerWidget->setOrientation(Qt::Vertical);
+
+ DSCCacheBlocksView* cacheBlocksView = new DSCCacheBlocksView(containerWidget, data, m_cache);
+ cacheBlocksView->setMinimumHeight(60);
+
+ auto cacheInfo = new CollapsibleSection(this);
+ cacheInfo->setTitle(QString::fromStdString(data->GetFile()->GetOriginalFilename().substr(data->GetFile()->GetOriginalFilename().find_last_of('/') + 1)));
+
+ auto cacheInfoSubwidget = new QWidget;
+
+ auto mappingTable = new QTableView(cacheInfoSubwidget);
+ auto mappingModel = new QStandardItemModel(0, 3, mappingTable);
+ mappingModel->setHorizontalHeaderLabels({"VM Address", "File Address", "Size"});
+
+ mappingTable->setModel(mappingModel);
+
+ mappingTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+ mappingTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+ mappingTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
+
+ auto sectionTable = new QTableView(cacheInfoSubwidget);
+ auto sectionModel = new QStandardItemModel(0, 3, sectionTable);
+ sectionModel->setHorizontalHeaderLabels({"Name", "VM Address", "Size"});
+
+ sectionTable->setModel(sectionModel);
+
+ sectionTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
+ sectionTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+ sectionTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
+
+ auto mappingLabel = new QLabel("Mappings");
+ auto sectionLabel = new QLabel("Sections");
+
+ auto mappingLayout = new QVBoxLayout;
+ mappingLayout->addWidget(mappingLabel);
+ mappingLayout->addWidget(mappingTable);
+
+ auto sectionLayout = new QVBoxLayout;
+ sectionLayout->addWidget(sectionLabel);
+ sectionLayout->addWidget(sectionTable);
+
+ cacheInfoLayout->addLayout(mappingLayout);
+ cacheInfoLayout->addLayout(sectionLayout);
+
+ cacheInfo->setContentWidget(cacheInfoSubwidget);
+
+ cacheInfo->setMinimumHeight(170);
+
+ connect(cacheBlocksView, &DSCCacheBlocksView::selectionChanged, [this, sectionModel, cacheInfo, cacheInfoWidget, mappingModel](const SharedCacheAPI::BackingCache& index, bool _auto)
+ {
+ if (!_auto)
+ m_triageTabs->selectWidget(cacheInfoWidget);
+ mappingModel->removeRows(0, mappingModel->rowCount());
+ sectionModel->removeRows(0, sectionModel->rowCount());
+ auto basename = index.path.substr(index.path.find_last_of('/') + 1);
+ cacheInfo->setTitle(QString::fromStdString(basename));
+ size_t sizeInBits = 0;
+ for (const auto& mapping : index.mappings)
+ {
+ sizeInBits += mapping.size;
+ mappingModel->appendRow({
+ new QStandardItem(QString("0x%1").arg(mapping.vmAddress, 0, 16)),
+ new QStandardItem(QString("0x%1").arg(mapping.fileOffset, 0, 16)),
+ new QStandardItem(QString("0x%1").arg(mapping.size, 0, 16))});
+ }
+
+ for (const auto& header : m_headers)
+ {
+ uint64_t i = 0;
+ for (const auto& section : header.sections)
+ {
+ for (const auto& mapping : index.mappings)
+ {
+ if (section.addr >= mapping.vmAddress && section.addr < mapping.vmAddress + mapping.size)
+ {
+ sectionModel->appendRow({
+ new QStandardItem(QString::fromStdString(header.sectionNames[i])),
+ new QStandardItem(QString("0x%1").arg(section.addr, 0, 16)),
+ new QStandardItem(QString("0x%1").arg(section.size, 0, 16))});
+ break;
+ }
+ }
+ i++;
+ }
+ continue;
+ }
+
+ std::string sizeStr;
+ if (sizeInBits < 1024)
+ {
+ sizeStr = std::to_string(sizeInBits) + " B";
+ }
+ else if (sizeInBits < 1024 * 1024)
+ {
+ sizeStr = std::to_string(sizeInBits / 1024) + " KB";
+ }
+ else if (sizeInBits < 1024 * 1024 * 1024)
+ {
+ sizeStr = std::to_string(sizeInBits / (1024 * 1024)) + " MB";
+ }
+ else
+ {
+ sizeStr = std::to_string(sizeInBits / (1024 * 1024 * 1024)) + " GB";
+ }
+
+ cacheInfo->setSubtitleRight(QString::fromStdString(sizeStr));
+ });
+
+ containerWidget->addWidget(cacheInfo);
+
+ QWidget* defaultWidget;
+
+ // check for alpha popup qsetting
+ QSettings settings;
+ if (!(settings.contains(QSETTINGS_KEY_ALPHA_POPUP_SEEN) && settings.value(QSETTINGS_KEY_ALPHA_POPUP_SEEN).toBool()))
+ {
+
+ QTextBrowser *tb = new QTextBrowser(this);
+ {
+ tb->setOpenExternalLinks(true);
+ auto alphaHtml =
+ R"(
+<br>
+<h1>Shared Cache Alpha</h1>
+
+<p> This is the alpha release of the sharedcache viewer! We are hard at work improving this and adding features, but we wanted
+to make it available for users to play with as soon as possible. </p>
+
+<h2> Supported Platforms </h2>
+<ul>
+ <li> iOS 11-17 (full) </li>
+ <li> iOS 18 (partial, Objective-C optimization parsing is not implemented yet.) </li>
+ <li> macOS x86/arm64e (partial) </li>
+</ul>
+
+<p> iOS parsing should work well for now. macOS parsing should be usable, but is still a work in progress. </p>
+
+<h2> Getting the latest version of the plugin </h2>
+
+<p> We frequently release "dev" builds which will contain the latest version of the SharedCache plugin (and many other things).
+
+You can find instructions on how to install these builds <a href="https://docs.binary.ninja/guide/index.html#development-branch">here</a>. </p>
+
+<h3> Reading / building the source </h3>
+<p>You can read the source and find instructions for building it <a href="https://github.com/Vector35/binaryninja-api/tree/dev/view/sharedcache">here</a>.
+
+Contributions are always welcome! </p>
+)";
+ tb->setHtml(alphaHtml);
+
+ m_triageTabs->addTab(tb, "Shared Cache Alpha");
+
+ }
+ settings.setValue(QSETTINGS_KEY_ALPHA_POPUP_SEEN, true);
+ defaultWidget = tb;
+ }
+
+ m_bottomRegionCollection = new DockableTabCollection();
+ m_bottomRegionTabs = new SplitTabWidget(m_bottomRegionCollection);
+ m_bottomRegionTabs->setTabStyle(new GlobalAreaTabStyle());
+
+ auto loadImageTable = new FilterableTableView;
+ {
+ auto loadImageModel = new QStandardItemModel(0, 2, loadImageTable);
+ {
+ connect(
+ cacheBlocksView, &DSCCacheBlocksView::loadDone, [this, loadImageModel, cacheInfo]()
+ {
+ for (const auto& img : m_cache->GetImages())
+ {
+ if (auto header = m_cache->GetMachOHeaderForAddress(img.headerAddress); header)
+ {
+ m_headers.push_back(*header);
+ }
+ loadImageModel->appendRow({
+ new QStandardItem(QString::fromStdString(img.name)),
+ new QStandardItem(QString("0x%1").arg(img.headerAddress, 0, 16))});
+ }
+ });
+ loadImageModel->setHorizontalHeaderLabels({"Name", "VM Address"});
+ } // loadImageModel
+
+ auto loadImageButton = new CustomStyleFlatPushButton();
+ {
+ connect(loadImageButton, &QPushButton::clicked,
+ [this, loadImageTable, cacheInfo, mappingModel, sectionModel](bool) {
+ auto selected = loadImageTable->selectionModel()->selectedRows();
+ if (selected.size() == 0)
+ {
+ return;
+ }
+
+ auto name = selected[0].data().toString().toStdString();
+ WorkerPriorityEnqueue([this, name]() { m_cache->LoadImageWithInstallName(name); });
+ });
+ loadImageButton->setText("Load");
+
+ loadImageButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ loadImageButton->setMinimumWidth(100);
+ loadImageButton->setMinimumHeight(30);
+
+ } // loadImageButton
+ loadImageTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+
+ auto loadImageFilterEdit = new FilterEdit(loadImageTable);
+ {
+ connect(loadImageFilterEdit, &FilterEdit::textChanged, [loadImageTable](const QString& filter) {
+ loadImageTable->setFilter(filter.toStdString());
+ });
+ } // loadImageFilterEdit
+
+ connect(loadImageTable, &FilterableTableView::activated, this, [=](const QModelIndex& index)
+ {
+ auto name = loadImageModel->item(index.row(), 0)->text().toStdString();
+ WorkerPriorityEnqueue([this, name]()
+ {
+ m_cache->LoadImageWithInstallName(name);
+ });
+ });
+ connect(loadImageTable, &FilterableTableView::doubleClicked, this, [=](const QModelIndex& index)
+ {
+ auto name = loadImageModel->item(index.row(), 0)->text().toStdString();
+ WorkerPriorityEnqueue([this, name]()
+ {
+ m_cache->LoadImageWithInstallName(name);
+ });
+ });
+
+ auto loadImageLayout = new QVBoxLayout;
+ loadImageLayout->addWidget(loadImageFilterEdit);
+ loadImageLayout->addWidget(loadImageTable);
+ loadImageLayout->addWidget(loadImageButton);
+
+ auto loadImageWidget = new QWidget;
+ loadImageWidget->setLayout(loadImageLayout);
+
+ m_bottomRegionTabs->addTab(loadImageWidget, "Load an Image");
+
+ loadImageTable->setModel(loadImageModel);
+
+ loadImageTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
+ loadImageTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+
+ loadImageTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ loadImageTable->setSelectionMode(QAbstractItemView::SingleSelection);
+
+ m_triageTabs->addTab(loadImageWidget, "Images");
+ if (!defaultWidget)
+ defaultWidget = loadImageWidget;
+ m_triageTabs->setCanCloseTab(loadImageWidget, false);
+ } // loadImageTable
+
+ auto symbolSearch = new SymbolTableView(this, m_cache);
+ {
+ auto symbolFilterEdit = new FilterEdit(symbolSearch);
+ {
+ connect(symbolFilterEdit, &FilterEdit::textChanged, [symbolSearch](const QString& filter) {
+ symbolSearch->setFilter(filter.toStdString());
+ });
+ }
+
+ auto symbolLayout = new QVBoxLayout;
+ symbolLayout->addWidget(symbolFilterEdit);
+ symbolLayout->addWidget(symbolSearch);
+
+ auto symbolWidget = new QWidget;
+ symbolWidget->setLayout(symbolLayout);
+
+ symbolSearch->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); // Address
+ symbolSearch->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); // Name
+ symbolSearch->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); // Image
+
+ symbolSearch->setSelectionBehavior(QAbstractItemView::SelectRows);
+ symbolSearch->setSelectionMode(QAbstractItemView::SingleSelection);
+
+ connect(symbolSearch, &SymbolTableView::activated, this, [=](const QModelIndex& index)
+ {
+ auto symbol = symbolSearch->getSymbolAtRow(index.row());
+ auto dialog = new QMessageBox(this);
+ dialog->setText("Load " + QString::fromStdString(symbol.image) + "?");
+ dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+
+ connect(dialog, &QMessageBox::buttonClicked, this, [=](QAbstractButton* button)
+ {
+ if (button == dialog->button(QMessageBox::Yes))
+ {
+ WorkerPriorityEnqueue([this, symbol]()
+ {
+ m_cache->LoadImageWithInstallName(symbol.image);
+ });
+ }
+ });
+ dialog->exec();
+ });
+
+ m_triageTabs->addTab(symbolWidget, "Symbol Search");
+ m_triageTabs->setCanCloseTab(symbolWidget, false);
+ } // symbolSearch
+
+ auto loadedRegions = new QTreeView;
+ {
+ auto loadedRegionsModel = new QStandardItemModel(0, 3, loadedRegions);
+ loadedRegionsModel->setHorizontalHeaderLabels({"VM Address", "Size", "Pretty Name"});
+
+ auto loadedRegionsLayout = new QVBoxLayout;
+ loadedRegionsLayout->addWidget(loadedRegions);
+
+ auto loadedRegionsWidget = new QWidget;
+ loadedRegionsWidget->setLayout(loadedRegionsLayout);
+
+ loadedRegions->setModel(loadedRegionsModel);
+
+ loadedRegions->header()->setSectionResizeMode(QHeaderView::Stretch);
+
+ loadedRegions->setSelectionBehavior(QAbstractItemView::SelectRows);
+ loadedRegions->setSelectionMode(QAbstractItemView::SingleSelection);
+
+ connect(loadedRegions, &QTreeView::doubleClicked, this, [=](const QModelIndex& index)
+ {
+ auto addr = loadedRegionsModel->item(index.row(), 0)->text().toULongLong(nullptr, 16);
+ });
+
+ connect(loadedRegions, &QTreeView::activated, this, [=](const QModelIndex& index)
+ {
+ auto addr = loadedRegionsModel->item(index.row(), 0)->text().toULongLong(nullptr, 16);
+ });
+
+ // m_triageTabs->addTab(loadedRegionsWidget, "Loaded Regions");
+ } // loadedRegions
+
+ containerWidget->addWidget(m_bottomRegionTabs);
+
+ m_triageTabs->addTab(cacheInfoWidget, "Cache Info");
+ m_triageTabs->setCanCloseTab(cacheInfoWidget, false);
+
+ m_layout = new QVBoxLayout(this);
+ m_layout->addWidget(cacheBlocksView);
+ m_layout->addWidget(m_triageTabs);
+ setLayout(m_layout);
+
+ m_triageTabs->selectWidget(defaultWidget);
+}
+
+
+DSCTriageView::~DSCTriageView() {}
+
+
+QFont DSCTriageView::getFont()
+{
+ return getMonospaceFont(this);
+}
+
+
+BinaryViewRef DSCTriageView::getData()
+{
+ return m_data;
+}
+
+
+bool DSCTriageView::navigate(uint64_t offset)
+{
+ return true;
+}
+
+
+uint64_t DSCTriageView::getCurrentOffset()
+{
+ return 0;
+}
+
+
+CollapsibleSection::CollapsibleSection(QWidget* parent)
+ : QWidget(parent)
+{
+ auto layout = new QVBoxLayout(this);
+ {
+ layout->setContentsMargins(0, 0, 0, 0);
+
+ auto hLayout = new QHBoxLayout;
+ {
+ hLayout->setContentsMargins(0, 0, 0, 0);
+
+ m_titleLabel = new QLabel;
+ m_titleLabel->setStyleSheet("font-weight: bold; font-size: 16px;");
+ hLayout->addWidget(m_titleLabel, 1);
+
+ m_subtitleRightLabel = new QLabel;
+ m_subtitleRightLabel->setStyleSheet("font-size: 12px;");
+ hLayout->addWidget(m_subtitleRightLabel);
+
+ m_collapseButton = new CustomStyleFlatPushButton;
+ m_collapseButton->setFlat(true);
+ m_collapseButton->setCheckable(true);
+ }
+
+ layout->addLayout(hLayout);
+ }
+
+ m_contentWidgetContainer = new QWidget;
+ {
+ layout->addWidget(m_contentWidgetContainer);
+ new QVBoxLayout(m_contentWidgetContainer);
+ }
+
+}
+
+
+void CollapsibleSection::setTitle(const QString& title)
+{
+ m_titleLabel->setText(title);
+}
+
+
+void CollapsibleSection::setSubtitleRight(const QString& subtitle)
+{
+ m_subtitleRightLabel->setVisible(subtitle != "");
+ m_subtitleRightLabel->setText(subtitle);
+}
+
+
+void CollapsibleSection::setContentWidget(QWidget* contentWidget)
+{
+ m_contentWidget = contentWidget;
+ m_contentWidgetContainer->layout()->addWidget(contentWidget);
+}
+
+
+QSize CollapsibleSection::sizeHint() const
+{
+ return QWidget::sizeHint();
+}
+
+
+void CollapsibleSection::setCollapsed(bool collapsed, bool animated)
+{
+ if (collapsed == m_collapsed)
+ {
+ return;
+ }
+
+ m_collapsed = collapsed;
+
+ if (m_collapsed)
+ {
+ m_contentWidget->hide();
+ }
+ else
+ {
+ m_contentWidget->show();
+ }
+
+ if (animated)
+ {
+ m_onContentAddedAnimation->start();
+ }
+}
+
+
+DSCTriageViewType::DSCTriageViewType()
+ : ViewType("DSCTriage", "Shared Cache Triage")
+{
+
+}
+
+
+int DSCTriageViewType::getPriority(BinaryViewRef data, const QString& filename)
+{
+ if (data->GetTypeName() == VIEW_NAME)
+ {
+ return 100;
+ }
+ return 1;
+}
+
+
+QWidget* DSCTriageViewType::create(BinaryViewRef data, ViewFrame* viewFrame)
+{
+ if (data->GetTypeName() != VIEW_NAME)
+ {
+ return nullptr;
+ }
+ return new DSCTriageView(viewFrame, data);
+}
+
+
+void DSCTriageViewType::Register()
+{
+ ViewType::registerViewType(new DSCTriageViewType());
+}
diff --git a/view/sharedcache/ui/dsctriage.h b/view/sharedcache/ui/dsctriage.h
new file mode 100644
index 00000000..96099a53
--- /dev/null
+++ b/view/sharedcache/ui/dsctriage.h
@@ -0,0 +1,297 @@
+//
+// Created by kat on 8/15/24.
+//
+
+#include <sharedcacheapi.h>
+#include <binaryninjaapi.h>
+#include "uitypes.h"
+#include "viewframe.h"
+#include "animation.h"
+#include "uicontext.h"
+
+#include <QTableView>
+#include <QStandardItemModel>
+#include <QSortFilterProxyModel>
+#include <QHeaderView>
+#include "filter.h"
+
+#ifndef BINARYNINJA_DSCTRIAGE_H
+#define BINARYNINJA_DSCTRIAGE_H
+
+
+class DSCCacheBlocksView : public QWidget
+{
+ Q_OBJECT
+
+ BinaryViewRef m_data;
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> m_cache;
+
+ uint64_t m_backingCacheCount = 0;
+ std::vector<SharedCacheAPI::BackingCache> m_backingCaches;
+
+ std::atomic<BNDSCViewLoadProgress> m_currentProgress;
+ std::vector<uint64_t> m_blockSizeRatios;
+ std::vector<uint64_t> m_targetBlockSizeForAnimation;
+ uint64_t m_averageBlockSizeForAnimationInterp = 0;
+ std::vector<uint64_t> m_blockLuminance;
+ Animation* m_blockWaveAnimation;
+ Animation* m_blockExpandAnimation;
+ Animation* m_blockAutoselectAnimation;
+
+ int m_selectedBlock = -1;
+
+ int getBlockIndexAtPosition(const QPoint& clickPosition);
+
+ void blockSelected(int index);
+
+public:
+ DSCCacheBlocksView(QWidget* parent, BinaryViewRef data, SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache);
+ virtual ~DSCCacheBlocksView() override;
+
+protected:
+ void mousePressEvent(QMouseEvent* event) override;
+ void mouseReleaseEvent(QMouseEvent* event) override;
+ void mouseDoubleClickEvent(QMouseEvent* event) override;
+ void mouseMoveEvent(QMouseEvent* event) override;
+ void keyPressEvent(QKeyEvent* event) override;
+ void keyReleaseEvent(QKeyEvent* event) override;
+ void focusInEvent(QFocusEvent* event) override;
+ void focusOutEvent(QFocusEvent* event) override;
+ void enterEvent(QEnterEvent* event) override;
+ void leaveEvent(QEvent* event) override;
+ void paintEvent(QPaintEvent* event) override;
+ void resizeEvent(QResizeEvent* event) override;
+
+public:
+ QSize sizeHint() const override;
+ QSize minimumSizeHint() const override;
+
+signals:
+ void loadDone();
+ void selectionChanged(const SharedCacheAPI::BackingCache& index, bool automatic);
+};
+
+
+class CollapsibleSection : public QWidget
+{
+ Q_OBJECT
+
+ QLabel* m_titleLabel;
+ QLabel* m_subtitleRightLabel;
+ QPushButton* m_collapseButton;
+
+ bool m_collapsed = true;
+
+ Animation* m_onContentAddedAnimation;
+
+ QWidget* m_contentWidgetContainer;
+ QWidget* m_contentWidget;
+
+protected:
+ QSize sizeHint() const override;
+
+public:
+ CollapsibleSection(QWidget* parent);
+ void setTitle(const QString& title);
+ void setSubtitleRight(const QString& subtitle);
+
+ void setContentWidget(QWidget* contentWidget);
+
+ void setCollapsed(bool collapsed, bool animated = true);
+ bool isCollapsed() const { return m_collapsed; }
+};
+
+
+class FilterableTableView : public QTableView, public FilterTarget {
+ Q_OBJECT
+
+ bool m_filterByHiding;
+
+public:
+ FilterableTableView(QWidget* parent = nullptr, bool filterByHiding = true)
+ : QTableView(parent), m_filterByHiding(filterByHiding) {
+ viewport()->installEventFilter(this);
+ }
+
+ ~FilterableTableView() override {}
+
+ void setFilter(const std::string& filter) override {
+ if (!m_filterByHiding)
+ {
+ emit filterTextChanged(QString::fromStdString(filter));
+ return;
+ }
+ QString qFilter = QString::fromStdString(filter);
+ for (int row = 0; row < model()->rowCount(); ++row) {
+ bool match = false;
+ for (int col = 0; col < model()->columnCount(); ++col) {
+ QModelIndex index = model()->index(row, col);
+ QString data = model()->data(index).toString();
+ if (data.contains(qFilter, Qt::CaseInsensitive)) {
+ match = true;
+ break;
+ }
+ }
+ setRowHidden(row, !match);
+ }
+ }
+
+ void scrollToFirstItem() override {
+ if (model()->rowCount() > 0) {
+ scrollTo(model()->index(0, 0));
+ }
+ }
+
+ void scrollToCurrentItem() override {
+ QModelIndex currentIndex = selectionModel()->currentIndex();
+ if (currentIndex.isValid()) {
+ scrollTo(currentIndex);
+ }
+ }
+
+ void selectFirstItem() override {
+ if (model()->rowCount() > 0) {
+ QModelIndex firstIndex = model()->index(0, 0);
+ selectionModel()->select(firstIndex, QItemSelectionModel::ClearAndSelect);
+ }
+ }
+
+ void activateFirstItem() override {
+ if (model()->rowCount() > 0) {
+ QModelIndex firstIndex = model()->index(0, 0);
+ setCurrentIndex(firstIndex);
+ emit activated(firstIndex);
+ }
+ }
+
+ bool eventFilter(QObject* obj, QEvent* event) override {
+ if (event->type() == QEvent::KeyPress) {
+ QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
+ if (keyEvent->key() == Qt::Key_Escape) {
+ clearSelection();
+ return true;
+ }
+ if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) {
+ emit activated(currentIndex());
+ return true;
+ }
+ }
+ return QTableView::eventFilter(obj, event);
+ }
+
+signals:
+ void filterTextChanged(const QString& text);
+};
+
+class SymbolTableView;
+
+class SymbolTableModel : public QAbstractTableModel {
+ Q_OBJECT
+
+ SymbolTableView* m_parent;
+ std::string m_filter;
+ std::vector<SharedCacheAPI::DSCSymbol> m_symbols;
+
+public:
+ explicit SymbolTableModel(SymbolTableView* parent);
+
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+
+ void updateSymbols();
+
+ void setFilter(std::string text);
+
+ const SharedCacheAPI::DSCSymbol& symbolAt(int row) const;
+};
+
+
+class SymbolTableView : public QTableView, public FilterTarget
+{
+ Q_OBJECT
+ friend class SymbolTableModel;
+
+ std::vector<SharedCacheAPI::DSCSymbol> m_symbols;
+
+ SymbolTableModel* m_model;
+
+public:
+ SymbolTableView(QWidget* parent, SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache);
+ virtual ~SymbolTableView() override;
+
+ void scrollToFirstItem() override {
+ if (model()->rowCount() > 0) {
+ scrollTo(model()->index(0, 0));
+ }
+ }
+
+ void scrollToCurrentItem() override {
+ QModelIndex currentIndex = selectionModel()->currentIndex();
+ if (currentIndex.isValid()) {
+ scrollTo(currentIndex);
+ }
+ }
+
+ void selectFirstItem() override {
+ if (model()->rowCount() > 0) {
+ QModelIndex firstIndex = model()->index(0, 0);
+ selectionModel()->select(firstIndex, QItemSelectionModel::ClearAndSelect);
+ }
+ }
+
+ void activateFirstItem() override {
+ if (model()->rowCount() > 0) {
+ QModelIndex firstIndex = model()->index(0, 0);
+ setCurrentIndex(firstIndex);
+ emit activated(firstIndex);
+ }
+ }
+
+ SharedCacheAPI::DSCSymbol getSymbolAtRow(int row) const
+ {
+ return m_model->symbolAt(row);
+ }
+
+ void setFilter(const std::string& filter) override;
+};
+
+
+class DSCTriageView : public QWidget, public View
+{
+ BinaryViewRef m_data;
+ QVBoxLayout* m_layout;
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> m_cache;
+
+ SplitTabWidget* m_triageTabs;
+ DockableTabCollection* m_triageCollection;
+
+ SplitTabWidget* m_bottomRegionTabs;
+ QTimer* m_tabLayoutTimer;
+ DockableTabCollection* m_bottomRegionCollection;
+
+ std::vector<SharedCacheAPI::SharedCacheMachOHeader> m_headers;
+
+public:
+ DSCTriageView(QWidget* parent, BinaryViewRef data);
+ virtual ~DSCTriageView() override;
+ BinaryViewRef getData() override;
+ void setSelectionOffsets(BNAddressRange range) override {};
+ QFont getFont() override;
+ bool navigate(uint64_t offset) override;
+ uint64_t getCurrentOffset() override;
+};
+
+
+class DSCTriageViewType : public ViewType
+{
+public:
+ DSCTriageViewType();
+ int getPriority(BinaryViewRef data, const QString& filename) override;
+ QWidget* create(BinaryViewRef data, ViewFrame* viewFrame) override;
+ static void Register();
+};
+
+
+#endif // BINARYNINJA_DSCTRIAGE_H
diff --git a/view/sharedcache/ui/dscwidget.cpp b/view/sharedcache/ui/dscwidget.cpp
new file mode 100644
index 00000000..2483a26e
--- /dev/null
+++ b/view/sharedcache/ui/dscwidget.cpp
@@ -0,0 +1,437 @@
+//
+// by kat // 9/15/22.
+//
+
+// CURRENTLY UNUSED CODE
+
+#include "dscwidget.h"
+
+#include "ui/viewframe.h"
+#include "ui/progresstask.h"
+
+#include <QtCore/QMimeData>
+#include <QtWidgets/QHeaderView>
+#include <QtWidgets/QVBoxLayout>
+#include <filesystem>
+#include <QtWidgets>
+
+namespace fs = std::filesystem;
+
+
+/// Format an address as hexadecimal. Does not include leading '0x' prefix.
+QString formatAddress(uint64_t address)
+{
+ return QString::number(address, 16).rightJustified(8, '0');
+};
+
+//===-- DSCContentsModelItem ------------------------------------------------===//
+
+DSCContentsModelItem::DSCContentsModelItem(DSCContentsModelItem* parent) : DSCContentsModelItem(nullptr, {}, {}, parent)
+{}
+
+DSCContentsModelItem::DSCContentsModelItem(
+ BinaryViewRef view, std::string name, std::string installName, DSCContentsModelItem* parent) :
+ m_bv(view),
+ m_name(name), m_installName(installName), m_parent(parent)
+{
+ if (!installName.empty())
+ m_type = ImageModelItem;
+ else
+ m_type = FolderModelItem;
+}
+
+QString DSCContentsModelItem::displayName() const
+{
+ return QString::fromStdString(m_name);
+}
+
+size_t DSCContentsModelItem::childCount() const
+{
+ return m_children.size();
+}
+
+DSCContentsModelItem* DSCContentsModelItem::child(size_t index)
+{
+ if (index < 0 || index >= m_children.size())
+ return nullptr;
+
+ return m_children[index];
+}
+
+void DSCContentsModelItem::addChild(DSCContentsModelItem* item)
+{
+ item->m_parent = this;
+ m_children.push_back(item);
+}
+
+DSCContentsModelItem* DSCContentsModelItem::parent() const
+{
+ return m_parent;
+}
+
+size_t DSCContentsModelItem::row() const
+{
+ if (!m_parent)
+ return 0;
+ auto it = std::find(m_parent->m_children.begin(), m_parent->m_children.end(), this);
+ return it - m_parent->m_children.begin();
+}
+
+QVariant DSCContentsModelItem::data(int column) const
+{
+ switch (column)
+ {
+ case DSCContentsModel::NameColumn:
+ return displayName();
+
+ default:
+ return QVariant();
+ }
+}
+
+QImage DSCContentsModelItem::icon() const
+{
+ auto kind = data(DSCContentsModel::KindColumn).toString();
+ auto icon = QImage(":/icons/images/ComponentTree_" + kind + ".png");
+
+ return icon.scaled(16, 16, Qt::KeepAspectRatio);
+}
+
+//===-- DSCContentsModel ----------------------------------------------------===//
+
+DSCContentsModel::DSCContentsModel(BinaryViewRef bv, QObject* parent) : QAbstractItemModel(parent), m_bv(bv)
+{
+ m_cache = new SharedCacheAPI::SharedCache(bv);
+ refresh();
+}
+
+struct ItemNode
+{
+ ItemNode* parent = nullptr;
+ std::string fullPath;
+ std::string path;
+ DSCContentsModelItem* assignedModelItem = nullptr;
+ std::unordered_map<std::string, ItemNode*> edges {};
+};
+
+std::vector<std::string> split(std::string str, std::string token)
+{
+ std::vector<std::string> result;
+ while (str.size())
+ {
+ int index = str.find(token);
+ if (index != std::string::npos)
+ {
+ result.push_back(str.substr(0, index));
+ str = str.substr(index + token.size());
+ if (str.size() == 0)
+ result.push_back(str);
+ }
+ else
+ {
+ result.push_back(str);
+ str = "";
+ }
+ }
+ return result;
+}
+
+void DSCContentsModel::refresh()
+{
+ std::scoped_lock<std::mutex> lock(m_updateMutex);
+
+ // Using `{begin,end}ResetModel` here is not ideal and is a temporary
+ // hack at best. Actual model indices should be updated. That requires
+ // more work and will be implemented after more important things have
+ // been taken care of.
+ beginResetModel();
+
+ auto inames = m_cache->GetAvailableImages();
+
+ m_root = new DSCContentsModelItem();
+
+ std::unordered_map<std::string, DSCContentsModelItem*> folders {};
+ folders["/"] = m_root;
+ for (const auto& iname : inames)
+ {
+ auto pathItems = split(iname, "/");
+ pathItems.pop_back(); // skip filenames
+ std::string fullPath = "/";
+
+ for (const auto& item : pathItems)
+ {
+ if (item.empty())
+ continue;
+ auto parentPath = fullPath;
+ fullPath += item + "/";
+ if (folders.count(fullPath) == 0)
+ {
+ auto pnode = folders.at(parentPath);
+ auto* nnode = new DSCContentsModelItem(m_bv, item, "", pnode);
+ pnode->addChild(nnode);
+ folders[fullPath] = nnode;
+ }
+ }
+ }
+
+ // Ok, all our folders are in place. Put files in them.
+
+ for (const auto& iname : inames)
+ {
+ auto file = fs::path(iname).filename().string();
+ auto folderName = fs::path(iname).parent_path().string() + "/";
+ if (auto folder = folders.find(folderName); folder != folders.end())
+ {
+ auto* nnode = new DSCContentsModelItem(m_bv, file, iname, folder->second);
+ folder->second->addChild(nnode);
+ }
+ else
+ BNLogError("DSCView Sidebar Logic Error: Couldn't find folder for %s %s %s", iname.c_str(), file.c_str(),
+ folderName.c_str());
+ }
+
+ endResetModel();
+}
+
+QModelIndex DSCContentsModel::index(int row, int column, const QModelIndex& parentIndex) const
+{
+ if (!hasIndex(row, column, parentIndex))
+ return QModelIndex();
+
+ // Use the parent index's item if it is valid, otherwise use the root.
+ DSCContentsModelItem* parent = nullptr;
+ if (parentIndex.isValid())
+ parent = static_cast<DSCContentsModelItem*>(parentIndex.internalPointer());
+ else
+ parent = m_root;
+
+ // If the child is found, create an index for it; use an invalid index otherwise.
+ auto item = parent->child(row);
+ if (item)
+ return createIndex(row, column, item);
+
+ return QModelIndex();
+}
+
+QModelIndex DSCContentsModel::parent(const QModelIndex& index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ auto child = static_cast<DSCContentsModelItem*>(index.internalPointer());
+ auto parent = child->parent();
+ if (parent == m_root || parent == nullptr)
+ return QModelIndex();
+
+ return createIndex(parent->row(), 0, parent);
+}
+
+QVariant DSCContentsModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ {
+ switch (section)
+ {
+ case DSCContentsModel::NameColumn:
+ return "Name";
+ default:
+ return "";
+ }
+ }
+
+ return QAbstractItemModel::headerData(section, orientation, role);
+}
+
+constexpr int ComponentGuidDataRole = 64;
+
+QVariant DSCContentsModel::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ auto item = static_cast<DSCContentsModelItem*>(index.internalPointer());
+ if (!item)
+ return {};
+
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ return item->data(index.column());
+ default:
+ return {};
+ }
+}
+
+bool DSCContentsModel::setData(const QModelIndex& index, const QVariant& value, int role)
+{
+ return false;
+}
+
+Qt::ItemFlags DSCContentsModel::flags(const QModelIndex& index) const
+{
+ if (!index.isValid())
+ return Qt::ItemIsDropEnabled; // Root node
+
+ Qt::ItemFlags flags = QAbstractItemModel::flags(index);
+
+ return flags;
+}
+
+
+int DSCContentsModel::rowCount(const QModelIndex& parent) const
+{
+ DSCContentsModelItem* item;
+ if (!parent.isValid())
+ item = m_root;
+ else
+ item = static_cast<DSCContentsModelItem*>(parent.internalPointer());
+
+ return item->childCount();
+}
+
+int DSCContentsModel::columnCount(const QModelIndex& parent) const
+{
+ return 1;
+}
+
+Qt::DropActions DSCContentsModel::supportedDropActions() const
+{
+ return Qt::IgnoreAction;
+}
+
+
+//===-- ComponentFilterModel ----------------------------------------------===//
+
+DSCFilterModel::DSCFilterModel(BinaryViewRef data, QObject* parent) :
+ QSortFilterProxyModel(parent), m_model(new DSCContentsModel(data))
+{
+ setSourceModel(m_model);
+}
+
+bool DSCFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
+{
+ auto index = sourceModel()->index(sourceRow, 0, sourceParent);
+ if (!index.isValid())
+ return false;
+
+ return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
+}
+
+DSCSidebarView::DSCSidebarView(ViewFrame* frame, BinaryViewRef data, QWidget* parent) :
+ QTreeView(parent), m_data(data), m_frame(frame), m_parent(parent)
+{
+ connect(this, &DSCSidebarView::doubleClicked, this, &DSCSidebarView::navigateToIndex);
+
+ setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(this, &DSCSidebarView::customContextMenuRequested, [this](const QPoint& p) {
+ auto menu = createContextMenu();
+ menu->popup(viewport()->mapToGlobal(p));
+ });
+}
+
+
+void DSCSidebarView::navigateToIndex(const QModelIndex& index)
+{
+ auto filterParent = static_cast<DSCSidebarWidget*>(m_parent);
+ if (!filterParent)
+ return;
+ auto modelItem = static_cast<DSCContentsModelItem*>(filterParent->m_model->mapToSource(index).internalPointer());
+
+ if (modelItem->m_installName.empty())
+ return;
+
+ QMessageBox::StandardButton reply;
+ reply = QMessageBox::question(this, "Load Image", "Load " + QString::fromStdString(modelItem->m_name) + "?",
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (reply == QMessageBox::Yes)
+ {
+ SharedCacheAPI::SharedCache* cache = new SharedCacheAPI::SharedCache(m_data);
+ cache->LoadImageWithInstallName(modelItem->m_installName);
+ m_data->UpdateAnalysis();
+ }
+}
+
+QMenu* DSCSidebarView::createContextMenu()
+{
+ auto menu = new QMenu();
+
+ return menu;
+}
+
+//===-- ComponentTree -----------------------------------------------------===//
+
+DSCSidebarWidget::DSCSidebarWidget(ViewFrame* frame, BinaryViewRef data) :
+ SidebarWidget("dyld_shared_cache"), m_data(data), m_frame(frame), m_header(new QWidget)
+{
+ auto view = data;
+ m_tree = new DSCSidebarView(frame, view, this);
+ m_model = new DSCFilterModel(view);
+ m_tree->setDragDropMode(QAbstractItemView::DragDrop);
+ m_tree->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ m_tree->setDragEnabled(true);
+ m_tree->setAcceptDrops(true);
+ m_tree->setDropIndicatorShown(true);
+ m_tree->header()->setSectionsMovable(false);
+
+ m_tree->setModel(m_model);
+ m_model->setRecursiveFilteringEnabled(true);
+
+ m_filterEdit = new FilterEdit(this);
+ m_filterView = new FilteredView(this, m_tree, this, m_filterEdit);
+ m_filterView->setFilterPlaceholderText("Search Shared Cache Files");
+
+ auto headerLayout = new QHBoxLayout(m_header);
+ headerLayout->setContentsMargins(0, 0, 0, 0);
+ headerLayout->addWidget(m_filterEdit);
+
+ auto layout = new QVBoxLayout(this);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addWidget(m_filterView);
+}
+
+//===-- ComponentTree - FilterTarget --------------------------------------===//
+
+void DSCSidebarWidget::setFilter(const std::string& filter)
+{
+ m_model->setFilterFixedString(QString::fromStdString(filter));
+}
+
+void DSCSidebarWidget::scrollToFirstItem() {}
+
+void DSCSidebarWidget::scrollToCurrentItem() {}
+
+void DSCSidebarWidget::selectFirstItem() {}
+
+void DSCSidebarWidget::activateFirstItem() {}
+
+//===-- DSCSidebarWidget - SidebarWidget -------------------------------------===//
+
+QWidget* DSCSidebarWidget::headerWidget()
+{
+ return m_header;
+}
+
+void DSCSidebarWidget::focus() {}
+
+QImage temporaryIcon()
+{
+ QImage icon(56, 56, QImage::Format_RGB32);
+ icon.fill(0);
+
+ QPainter p;
+ p.begin(&icon);
+ p.setFont({"Inter", 16});
+ p.setPen({255, 255, 255, 255});
+ p.drawText(QRectF {0, 0, 56, 56}, Qt::AlignCenter, "DSC");
+ p.end();
+
+ return icon;
+}
+
+DSCSidebarWidgetType::DSCSidebarWidgetType() : SidebarWidgetType(temporaryIcon(), "Shared Cache") {}
+
+SidebarWidget* DSCSidebarWidgetType::createWidget(ViewFrame* frame, BinaryViewRef data)
+{
+ return new DSCSidebarWidget(frame, data);
+}
diff --git a/view/sharedcache/ui/dscwidget.h b/view/sharedcache/ui/dscwidget.h
new file mode 100644
index 00000000..0d5b9e0b
--- /dev/null
+++ b/view/sharedcache/ui/dscwidget.h
@@ -0,0 +1,190 @@
+//
+// by kat // 9/15/22.
+//
+
+#ifndef SHAREDCACHE_DSCSIDEBARWIDGET_H
+#define SHAREDCACHE_DSCSIDEBARWIDGET_H
+
+#include <QtCore/QAbstractItemModel>
+#include <QtCore/QSortFilterProxyModel>
+#include <QtWidgets/QTreeView>
+
+#include "ui/filter.h"
+#include "ui/sidebar.h"
+#include "ui/uitypes.h"
+#include <binaryninjaapi.h>
+#include <sharedcacheapi.h>
+
+#include <mutex>
+
+
+class DSCContentsModel;
+
+class DSCFilterModel;
+
+class DSCSidebarView;
+
+enum ModelItemType {
+ FolderModelItem,
+ ImageModelItem
+};
+
+class DSCContentsModelItem {
+ friend class ComponentModel;
+
+ friend class ComponentFilterModel;
+
+ friend class DSCSidebarView;
+
+ ModelItemType m_type;
+
+ DSCContentsModelItem *m_parent;
+ std::vector<DSCContentsModelItem *> m_children;
+
+ BinaryViewRef m_bv;
+
+ std::string m_name;
+ std::string m_installName; // only set on images, not dirs
+
+ bool m_hasDataVar = false;
+ BinaryNinja::DataVariable m_dataVar;
+
+public:
+ explicit DSCContentsModelItem(DSCContentsModelItem *parent = nullptr);
+
+ explicit DSCContentsModelItem(BinaryViewRef, std::string, std::string, DSCContentsModelItem *parent = nullptr);
+
+ /// Get the "name" that should be displayed for an item.
+ QString displayName() const;
+
+ size_t childCount() const;
+
+ DSCContentsModelItem *child(size_t);
+
+ void addChild(DSCContentsModelItem *);
+
+ DSCContentsModelItem *parent() const;
+
+ size_t row() const;
+
+ QVariant data(int column) const;
+
+ QImage icon() const;
+};
+
+class DSCContentsModel : public QAbstractItemModel {
+Q_OBJECT
+
+ BinaryViewRef m_bv;
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> m_cache;
+ DSCContentsModelItem *m_root;
+
+ std::unordered_map<std::string, DSCContentsModelItem *> m_dscItems;
+
+ std::mutex m_updateMutex;
+
+ void refresh();
+
+public:
+ enum Column : int {
+ NameColumn = 0,
+ AddressColumn,
+ KindColumn,
+ };
+
+ DSCContentsModel(BinaryViewRef, QObject *parent = nullptr);
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+
+ QModelIndex parent(const QModelIndex &) const override;
+
+ QVariant headerData(int, Qt::Orientation, int role = Qt::DisplayRole) const override;
+
+ QVariant data(const QModelIndex &, int role = Qt::DisplayRole) const override;
+
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
+
+ Qt::ItemFlags flags(const QModelIndex &) const override;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ Qt::DropActions supportedDropActions() const override;
+
+};
+
+/// Filtering model to wrap a `ComponentModel`.
+class DSCFilterModel : public QSortFilterProxyModel {
+Q_OBJECT
+
+ DSCContentsModel *m_model;
+
+public:
+ DSCFilterModel(BinaryViewRef, QObject *parent = nullptr);
+
+ [[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
+};
+
+class DSCSidebarView : public QTreeView {
+ BinaryViewRef m_data;
+ ViewFrame *m_frame;
+ QWidget *m_parent;
+
+ void navigateToIndex(const QModelIndex &);
+
+ QMenu *createContextMenu();
+
+public:
+ DSCSidebarView(ViewFrame *, BinaryViewRef, QWidget *parent = nullptr);
+};
+
+class DSCSidebarWidget : public SidebarWidget, public FilterTarget {
+Q_OBJECT
+
+ friend DSCSidebarView;
+
+ BinaryViewRef m_data;
+ ViewFrame *m_frame;
+ QWidget *m_header;
+
+ DSCSidebarView *m_tree;
+
+ QSortFilterProxyModel *m_model;
+
+ FilterEdit *m_filterEdit;
+ FilteredView *m_filterView;
+
+public:
+ DSCSidebarWidget(ViewFrame *, BinaryViewRef);
+
+ QWidget *headerWidget() override;
+
+ void focus() override;
+
+ void setFilter(const std::string &) override;
+
+ void scrollToFirstItem() override;
+
+ void scrollToCurrentItem() override;
+
+ void selectFirstItem() override;
+
+ void activateFirstItem() override;
+};
+
+class DSCSidebarWidgetType : public SidebarWidgetType {
+public:
+ DSCSidebarWidgetType();
+
+ bool ValidForView(BinaryNinja::BinaryView* view)
+ {
+ if (!view)
+ return false;
+ return (view->GetTypeName() == VIEW_NAME);
+ }
+
+ SidebarWidget *createWidget(ViewFrame *, BinaryViewRef) override;
+};
+
+#endif //SHAREDCACHE_DSCSIDEBARWIDGET_H
diff --git a/view/sharedcache/workflow/CMakeLists.txt b/view/sharedcache/workflow/CMakeLists.txt
new file mode 100644
index 00000000..81e4498c
--- /dev/null
+++ b/view/sharedcache/workflow/CMakeLists.txt
@@ -0,0 +1,52 @@
+cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
+
+project(sharedcacheworkflow)
+file(GLOB SOURCES *.cpp *.h)
+add_library(sharedcacheworkflow OBJECT ${SOURCES})
+
+if (VIEW_NAME)
+ target_compile_definitions(sharedcacheworkflow PRIVATE VIEW_NAME="${VIEW_NAME}")
+else()
+ error("VIEW_NAME must be defined")
+endif()
+
+
+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)
+
+target_include_directories(sharedcacheworkflow
+ PUBLIC ${PROJECT_SOURCE_DIR} ${INCLUDES})
+
+set_target_properties(sharedcacheworkflow PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON
+ ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/out)
diff --git a/view/sharedcache/workflow/SharedCacheWorkflow.cpp b/view/sharedcache/workflow/SharedCacheWorkflow.cpp
new file mode 100644
index 00000000..4b6afe97
--- /dev/null
+++ b/view/sharedcache/workflow/SharedCacheWorkflow.cpp
@@ -0,0 +1,496 @@
+//
+// Created by kat on 8/6/24.
+//
+
+// TODO We could use an LLIL/MLIL workflow to rewrite off-image value-loads
+// (i.e. MLIL_VAR_LOAD.MLIL_DEREF.MLIL_CONST_PTR) to just read the value out of the cache and replace the load
+// in stub regions.
+//
+// This is a pretty rough workflow and has huge room for improvements all around.
+
+#include "SharedCacheWorkflow.h"
+#include "lowlevelilinstruction.h"
+#include "mediumlevelilinstruction.h"
+#include "../api/sharedcacheapi.h"
+#include "thread"
+
+
+std::unordered_map<uint64_t, std::mutex> imageLoadMutex;
+
+
+std::vector<std::string> splitSelector(const std::string& selector) {
+ std::vector<std::string> components;
+ std::istringstream stream(selector);
+ std::string component;
+
+ while (std::getline(stream, component, ':')) {
+ if (!component.empty()) {
+ components.push_back(component);
+ }
+ }
+
+ return components;
+}
+
+std::vector<std::string> generateArgumentNames(const std::vector<std::string>& components) {
+ std::vector<std::string> argumentNames;
+
+ for (const std::string& component : components) {
+ size_t startPos = component.find_last_of(" ");
+ std::string argumentName = (startPos == std::string::npos) ? component : component.substr(startPos + 1);
+ argumentNames.push_back(argumentName);
+ }
+
+ return argumentNames;
+}
+
+
+void SharedCacheWorkflow::ProcessOffImageCall(Ref<AnalysisContext> ctx, Ref<Function> func, Ref<MediumLevelILFunction> mssa, const MediumLevelILInstruction dest, ExprId exprIndex, bool applySymbolIfFoundToCurrentFunction)
+{
+ auto bv = func->GetView();
+ WorkerPriorityEnqueue([bv=bv, dest=dest, func=func, applySymbolIfFoundToCurrentFunction]()
+ {
+ SharedCacheAPI::SCRef<SharedCacheAPI::SharedCache> cache = new SharedCacheAPI::SharedCache(bv);
+ Ref<Settings> settings = bv->GetLoadSettings(VIEW_NAME);
+ bool autoLoadStubsAndDyldData = true;
+ if (settings && settings->Contains("loader.dsc.autoLoadStubsAndDyldData"))
+ {
+ autoLoadStubsAndDyldData = settings->Get<bool>("loader.dsc.autoLoadStubsAndDyldData", bv);
+ }
+ if (dest.operation != MLIL_CONST_PTR && dest.operation != MLIL_CONST)
+ return;
+ if (autoLoadStubsAndDyldData &&
+ (cache->GetNameForAddress(dest.GetConstant()).find("dyld_shared_cache_branch_islands") != std::string::npos
+ || cache->GetNameForAddress(dest.GetConstant()).find("::_stubs") != std::string::npos
+ )
+ )
+
+ {
+ if (cache->LoadSectionAtAddress(dest.GetConstant()))
+ {
+ func->Reanalyze();
+ }
+ }
+ else
+ {
+ if (applySymbolIfFoundToCurrentFunction)
+ cache->FindSymbolAtAddrAndApplyToAddr(dest.GetConstant(), func->GetStart(), false);
+ else
+ cache->FindSymbolAtAddrAndApplyToAddr(dest.GetConstant(), dest.GetConstant(), false);
+ }
+ });
+}
+
+
+void SharedCacheWorkflow::FixupStubs(Ref<AnalysisContext> ctx)
+{
+ try
+ {
+ const auto func = ctx->GetFunction();
+ const auto arch = func->GetArchitecture();
+
+ const auto bv = func->GetView();
+
+ auto funcStart = func->GetStart();
+ auto sectionExists = !bv->GetSectionsAt(funcStart).empty();
+ if (!sectionExists)
+ return;
+ auto section = bv->GetSectionsAt(funcStart)[0];
+
+ auto imageName = section->GetName();
+ // remove everything after ::
+ auto pos = imageName.find("::");
+ if (pos != std::string::npos)
+ imageName = imageName.substr(0, pos);
+
+ const auto llil = ctx->GetLowLevelILFunction();
+ if (!llil) {
+ return;
+ }
+ const auto ssa = llil->GetSSAForm();
+ if (!ssa) {
+ return;
+ }
+
+ const auto mlil = ctx->GetMediumLevelILFunction();
+ if (!mlil) {
+ return;
+ }
+ const auto mssa = mlil->GetSSAForm();
+ if (!mssa) {
+ return;
+ }
+
+ // FIXME optimize
+ Ref<Settings> settings = bv->GetLoadSettings(VIEW_NAME);
+ bool autoLoadObjC = true;
+ if (settings && settings->Contains("loader.dsc.autoLoadObjCStubRequirements"))
+ {
+ autoLoadObjC = settings->Get<bool>("loader.dsc.autoLoadObjCStubRequirements", bv);
+ }
+
+ // Processor that automatically loads the libObjC image when it encounters a stub (so we can do inlining).
+ if (autoLoadObjC && section->GetName().find("__objc_stubs") != std::string::npos)
+ {
+ auto firstInstruction = mlil->GetInstruction(0);
+ if (firstInstruction.operation == MLIL_TAILCALL)
+ {
+ auto dest = firstInstruction.GetDestExpr<MLIL_TAILCALL>();
+ if (dest.operation == MLIL_CONST_PTR)
+ {
+ // We're ready, everything is here
+ func->SetAutoInlinedDuringAnalysis(true);
+ return;
+ }
+ }
+ for (const auto& block : mssa->GetBasicBlocks())
+ {
+ for (size_t i = block->GetStart(), end = block->GetEnd(); i < end; ++i)
+ {
+ auto instr = mssa->GetInstruction(i);
+ // current_il_function.ssa_form.get_ssa_var_value(current_il_instruction.dest.var)
+ if (instr.operation == MLIL_JUMP)
+ {
+ if (instr.GetDestExpr<MLIL_JUMP>().operation == MLIL_VAR_SSA)
+ {
+ auto dest = instr.GetDestExpr<MLIL_JUMP>();
+ // RegisterValue value = mssa->GetSSAVarValue(instr.GetDestExpr().GetSourceSSAVariable())
+ // ExprId def = mssa->GetSSAVarDefinition(instr.GetDestExpr().GetSourceSSAVariable());
+ // MLILInstruction defInstr = mssa->GetInstruction(mssa->GetSSAVarDefinition(instr.GetDestExpr().GetSourceSSAVariable()));
+ // targetOffset = mssa->GetInstruction(mssa->GetSSAVarDefinition(instr.GetDestExpr().GetSourceSSAVariable())).GetSourceExpr().GetSourceExpr().GetConstant();
+ auto value = mssa->GetSSAVarValue(dest.GetSourceSSAVariable());
+ if (value.state == UndeterminedValue)
+ {
+ bool otherFunctionAlreadyRunning;
+ {
+ otherFunctionAlreadyRunning = !imageLoadMutex[bv->GetFile()->GetSessionId()].try_lock();
+ if (!otherFunctionAlreadyRunning)
+ imageLoadMutex[bv->GetFile()->GetSessionId()].unlock();
+ }
+ if (otherFunctionAlreadyRunning)
+ {
+ return;
+ }
+
+ std::unique_lock<std::mutex> lock(imageLoadMutex[bv->GetFile()->GetSessionId()]);
+ auto def = mssa->GetSSAVarDefinition(dest.GetSourceSSAVariable());
+ auto defInstr = mssa->GetInstruction(def);
+ auto targetOffset = defInstr.GetSourceExpr().GetSourceExpr().GetConstant();
+ auto sharedCache = SharedCacheAPI::SharedCache(bv);
+ if (!sharedCache.GetImageNameForAddress(targetOffset).empty())
+ {
+ sharedCache.LoadImageContainingAddress(targetOffset);
+ }
+ else
+ {
+ sharedCache.LoadSectionAtAddress(targetOffset);
+ }
+ for (const auto &sectFunc : bv->GetAnalysisFunctionList())
+ {
+ if (section->GetStart() <= sectFunc->GetStart() && sectFunc->GetStart() < section->GetEnd())
+ {
+ func->Reanalyze();
+ }
+ }
+ }
+ }
+
+ else if (instr.GetDestExpr<MLIL_JUMP>().operation == MLIL_CONST_PTR)
+ {
+ bool otherFunctionAlreadyRunning;
+ {
+ otherFunctionAlreadyRunning = !imageLoadMutex[bv->GetFile()->GetSessionId()].try_lock();
+ if (!otherFunctionAlreadyRunning)
+ imageLoadMutex[bv->GetFile()->GetSessionId()].unlock();
+ }
+ if (otherFunctionAlreadyRunning)
+ {
+ return;
+ }
+
+ std::unique_lock<std::mutex> lock(imageLoadMutex[bv->GetFile()->GetSessionId()]);
+ auto dest = instr.GetDestExpr<MLIL_JUMP>();
+ auto targetOffset = dest.GetConstant();
+ auto sharedCache = SharedCacheAPI::SharedCache(bv);
+ if (!sharedCache.GetImageNameForAddress(targetOffset).empty())
+ {
+ sharedCache.LoadImageContainingAddress(targetOffset);
+ }
+ else
+ {
+ sharedCache.LoadSectionAtAddress(targetOffset);
+ }
+ for (const auto &sectFunc : bv->GetAnalysisFunctionList())
+ {
+ if (section->GetStart() <= sectFunc->GetStart() && sectFunc->GetStart() < section->GetEnd())
+ {
+ func->Reanalyze();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return;
+ }
+
+ if (section->GetName().find("::_stubs") != std::string::npos // Branch Islands (iOS 16)
+ || section->GetName().find("dyld_shared_cache_branch_islands") != std::string::npos // Branch Islands (iOS 11-?)
+ || section->GetName().find("::__stubs") != std::string::npos // Stubs (non arm64e)
+ || section->GetName().find("::__auth_stubs") != std::string::npos // Stubs (arm64e)
+ )
+ {
+ auto firstInstruction = mlil->GetInstruction(0);
+ if (firstInstruction.operation == MLIL_TAILCALL)
+ {
+ auto dest = firstInstruction.GetDestExpr<MLIL_TAILCALL>();
+ if (dest.operation == MLIL_CONST_PTR)
+ {
+ if (auto symbol = bv->GetSymbolByAddress(dest.GetConstant()))
+ {
+ auto newSymbol = new Symbol(FunctionSymbol, "j_" + symbol->GetRawName(), func->GetStart());
+ bv->DefineUserSymbol(newSymbol);
+ }
+ }
+ }
+ else if (firstInstruction.operation == MLIL_JUMP)
+ {
+ auto dest = firstInstruction.GetDestExpr<MLIL_JUMP>();
+ if (dest.operation == MLIL_CONST_PTR)
+ {
+ if (!bv->IsValidOffset(dest.GetConstant()))
+ {
+ ProcessOffImageCall(ctx, func, mssa, dest, firstInstruction.GetSSAExprIndex(), true);
+ }
+ }
+
+ else if (dest.operation == MLIL_LOAD)
+ {
+ if (dest.GetSourceExpr().operation == MLIL_CONST_PTR)
+ {
+ dest = dest.GetSourceExpr();
+ if (!bv->IsValidOffset(dest.GetConstant()))
+ {
+ ProcessOffImageCall(ctx, func, mssa, dest, firstInstruction.GetSSAExprIndex());
+ }
+ }
+ }
+ }
+
+ else
+ {
+ for (const auto& block : mssa->GetBasicBlocks())
+ {
+ for (size_t i = block->GetStart(), end = block->GetEnd(); i < end; ++i)
+ {
+ auto instr = mssa->GetInstruction(i);
+ // current_il_function.ssa_form.get_ssa_var_value(current_il_instruction.dest.var)
+ if (instr.operation == MLIL_JUMP)
+ {
+ if (instr.GetDestExpr<MLIL_JUMP>().operation == MLIL_VAR_SSA)
+ {
+ auto dest = instr.GetDestExpr<MLIL_JUMP>();
+ // RegisterValue value = mssa->GetSSAVarValue(instr.GetDestExpr().GetSourceSSAVariable()) ExprId def = mssa->GetSSAVarDefinition(instr.GetDestExpr().GetSourceSSAVariable());
+ // MLILInstruction defInstr = mssa->GetInstruction(mssa->GetSSAVarDefinition(instr.GetDestExpr().GetSourceSSAVariable()));
+ // targetOffset = mssa->GetInstruction(mssa->GetSSAVarDefinition(instr.GetDestExpr().GetSourceSSAVariable())).GetSourceExpr().GetSourceExpr().GetConstant();
+ auto value = mssa->GetSSAVarValue(dest.GetSourceSSAVariable());
+ if (value.state == UndeterminedValue)
+ {
+ bool otherFunctionAlreadyRunning;
+ {
+ otherFunctionAlreadyRunning =
+ !imageLoadMutex[bv->GetFile()->GetSessionId()].try_lock();
+ if (!otherFunctionAlreadyRunning)
+ imageLoadMutex[bv->GetFile()->GetSessionId()].unlock();
+ }
+ if (otherFunctionAlreadyRunning)
+ {
+ return;
+ }
+
+ std::unique_lock<std::mutex> lock(
+ imageLoadMutex[bv->GetFile()->GetSessionId()]);
+ auto def = mssa->GetSSAVarDefinition(dest.GetSourceSSAVariable());
+ auto defInstr = mssa->GetInstruction(def);
+ auto targetOffset = defInstr.GetSourceExpr().GetSourceExpr().GetConstant();
+ auto sharedCache = SharedCacheAPI::SharedCache(bv);
+ if (!bv->IsValidOffset(targetOffset))
+ {
+ if (!sharedCache.GetImageNameForAddress(targetOffset).empty())
+ {
+ sharedCache.LoadImageContainingAddress(targetOffset);
+ }
+ else
+ {
+ sharedCache.LoadSectionAtAddress(targetOffset);
+ }
+ for (const auto& sectFunc : bv->GetAnalysisFunctionList())
+ {
+ if (section->GetStart() <= sectFunc->GetStart()
+ && sectFunc->GetStart() < section->GetEnd())
+ {
+ func->Reanalyze();
+ }
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ return;
+ }
+
+ for (const auto& block : mssa->GetBasicBlocks())
+ {
+ for (size_t i = block->GetStart(), end = block->GetEnd(); i < end; ++i)
+ {
+ auto instr = mssa->GetInstruction(i);
+ if (instr.operation == MLIL_CALL_SSA)
+ {
+ if (instr.GetDestExpr<MLIL_CALL_SSA>().operation == MLIL_CONST_PTR)
+ {
+ auto dest = instr.GetDestExpr<MLIL_CALL_SSA>();
+ if (!bv->IsValidOffset(dest.GetConstant()))
+ {
+ ProcessOffImageCall(ctx, func, mssa, dest, instr.GetSSAExprIndex());
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (...)
+ {}
+}
+
+
+static constexpr auto workflowInfo = R"({
+ "title": "Shared Cache Workflow",
+ "description": "Shared Cache Workflow",
+ "capabilities": []
+})";
+
+
+void fixObjCCallTypes(Ref<AnalysisContext> ctx)
+{
+ const auto func = ctx->GetFunction();
+ const auto arch = func->GetArchitecture();
+ const auto bv = func->GetView();
+
+ const auto llil = ctx->GetLowLevelILFunction();
+ if (!llil) {
+ return;
+ }
+ const auto ssa = llil->GetSSAForm();
+ if (!ssa) {
+ return;
+ }
+
+ const auto rewriteIfEligible = [bv, ssa](size_t insnIndex) {
+ auto insn = ssa->GetInstruction(insnIndex);
+
+ if (insn.operation == LLIL_CALL_SSA)
+ {
+ // Filter out calls that aren't to `objc_msgSend`.
+ auto callExpr = insn.GetDestExpr<LLIL_CALL_SSA>();
+ bool isMessageSend = false;
+ if (auto symbol = bv->GetSymbolByAddress(callExpr.GetValue().value))
+ isMessageSend = symbol->GetRawName() == "_objc_msgSend";
+ if (!isMessageSend)
+ return;
+
+ const auto llil = ssa->GetNonSSAForm();
+ const auto insn = ssa->GetInstruction(insnIndex);
+ const auto params = insn.GetParameterExprs<LLIL_CALL_SSA>();
+
+ // The second parameter passed to the objc_msgSend call is the address of
+ // either the selector reference or the method's name, which in both cases
+ // is dereferenced to retrieve a selector.
+ if (params.size() < 2)
+ return;
+ uint64_t rawSelector = 0;
+ if (params[1].operation == LLIL_REG_SSA)
+ {
+ const auto selectorRegister = params[1].GetSourceSSARegister<LLIL_REG_SSA>();
+ rawSelector = ssa->GetSSARegisterValue(selectorRegister).value;
+ }
+ else if (params[0].operation == LLIL_SEPARATE_PARAM_LIST_SSA)
+ {
+ if (params[0].GetParameterExprs<LLIL_SEPARATE_PARAM_LIST_SSA>().size() == 0)
+ {
+ return;
+ }
+ const auto selectorRegister = params[0].GetParameterExprs<LLIL_SEPARATE_PARAM_LIST_SSA>()[1].GetSourceSSARegister<LLIL_REG_SSA>();
+ rawSelector = ssa->GetSSARegisterValue(selectorRegister).value;
+ }
+ if (rawSelector == 0)
+ return;
+
+ // -- Do callsite override
+ auto reader = BinaryNinja::BinaryReader(bv);
+ reader.Seek(rawSelector);
+ auto selector = reader.ReadCString(500);
+ auto additionalArgumentCount = std::count(selector.begin(), selector.end(), ':');
+
+ auto retType = bv->GetTypeByName({ "id" });
+ if (!retType)
+ retType = BinaryNinja::Type::PointerType(ssa->GetArchitecture(), BinaryNinja::Type::VoidType());
+
+ std::vector<BinaryNinja::FunctionParameter> callTypeParams;
+ auto cc = bv->GetDefaultPlatform()->GetDefaultCallingConvention();
+
+ callTypeParams.push_back({"self", retType, true, BinaryNinja::Variable()});
+
+ auto selType = bv->GetTypeByName({ "SEL" });
+ if (!selType)
+ selType = BinaryNinja::Type::PointerType(ssa->GetArchitecture(), BinaryNinja::Type::IntegerType(1, true));
+ callTypeParams.push_back({"sel", selType, true, BinaryNinja::Variable()});
+
+ std::vector<std::string> selectorComponents = splitSelector(selector);
+ std::vector<std::string> argumentNames = generateArgumentNames(selectorComponents);
+
+ for (size_t i = 0; i < additionalArgumentCount; i++)
+ {
+ auto argType = BinaryNinja::Type::IntegerType(bv->GetAddressSize(), true);
+ if (argumentNames.size() > i && !argumentNames[i].empty())
+ callTypeParams.push_back({argumentNames[i], argType, true, BinaryNinja::Variable()});
+ else
+ callTypeParams.push_back({"arg" + std::to_string(i), argType, true, BinaryNinja::Variable()});
+ }
+
+ auto funcType = BinaryNinja::Type::FunctionType(retType, cc, callTypeParams);
+ ssa->GetFunction()->SetAutoCallTypeAdjustment(ssa->GetFunction()->GetArchitecture(), insn.address, {funcType, BN_DEFAULT_CONFIDENCE});
+ // --
+ }
+ };
+
+ for (const auto& block : ssa->GetBasicBlocks())
+ for (size_t i = block->GetStart(), end = block->GetEnd(); i < end; ++i)
+ rewriteIfEligible(i);
+}
+
+
+
+void SharedCacheWorkflow::Register()
+{
+ const auto wf = BinaryNinja::Workflow::Instance()->Clone("core.function.dsc");
+ wf->RegisterActivity(new BinaryNinja::Activity("core.analysis.dscstubs", &SharedCacheWorkflow::FixupStubs));
+ wf->RegisterActivity(new BinaryNinja::Activity("core.analysis.fixObjCCallTypes", &fixObjCCallTypes));
+ wf->Insert("core.function.analyzeTailCalls", "core.analysis.fixObjCCallTypes");
+ wf->Insert("core.function.analyzeTailCalls", "core.analysis.dscstubs");
+
+ BinaryNinja::Workflow::RegisterWorkflow(wf, workflowInfo);
+}
+
+extern "C"
+{
+ void RegisterSharedCacheWorkflow()
+ {
+ SharedCacheWorkflow::Register();
+ }
+}
diff --git a/view/sharedcache/workflow/SharedCacheWorkflow.h b/view/sharedcache/workflow/SharedCacheWorkflow.h
new file mode 100644
index 00000000..67ccb8fe
--- /dev/null
+++ b/view/sharedcache/workflow/SharedCacheWorkflow.h
@@ -0,0 +1,27 @@
+
+#include "binaryninjaapi.h"
+
+using namespace BinaryNinja;
+
+
+#ifndef SHAREDCACHE_SHAREDCACHEWORKFLOW_H
+#define SHAREDCACHE_SHAREDCACHEWORKFLOW_H
+
+
+class SharedCacheWorkflow
+{
+public:
+ static void ProcessOffImageCall(Ref<AnalysisContext> ctx, Ref<Function> func, Ref<MediumLevelILFunction> il, const MediumLevelILInstruction instr, ExprId exprIndex, bool applySymbolIfFoundToCurrentFunction = false);
+ static void FixupStubs(Ref<AnalysisContext> ctx);
+ static void Register();
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void RegisterSharedCacheWorkflow();
+#ifdef __cplusplus
+}
+#endif
+
+#endif //SHAREDCACHE_SHAREDCACHEWORKFLOW_H