summaryrefslogtreecommitdiff
path: root/view/sharedcache/core/SharedCache.h
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-03-10 11:05:40 -0400
committerMason Reed <mason@vector35.com>2025-04-02 05:36:54 -0400
commit25cc02431b61097b2adfc2fbc493b648b0300c3b (patch)
treea79d9c4f4f67234d3bf9bda413e8608f479a4cc8 /view/sharedcache/core/SharedCache.h
parentfa85bf28502286c4821427c5d0ed91a7ed46f8f6 (diff)
[SharedCache] Refactor Shared Cache
In absence of a better name, this commit refactors the shared cache code.
Diffstat (limited to 'view/sharedcache/core/SharedCache.h')
-rw-r--r--view/sharedcache/core/SharedCache.h829
1 files changed, 201 insertions, 628 deletions
diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h
index fadcdd3a..54ec9dfd 100644
--- a/view/sharedcache/core/SharedCache.h
+++ b/view/sharedcache/core/SharedCache.h
@@ -1,705 +1,278 @@
-//
-// Created by kat on 5/19/23.
-//
+#pragma once
-#ifndef SHAREDCACHE_SHAREDCACHE_H
-#define SHAREDCACHE_SHAREDCACHE_H
+#include <vector>
+#include <Dyld.h>
-#include <binaryninjaapi.h>
-#include <cstdint>
-#include <memory>
-#include <mutex>
-#include <unordered_map>
-#include "VM.h"
-#include "view/macho/machoview.h"
-#include "MetadataSerializable.hpp"
-#include "../api/sharedcachecore.h"
+#include "binaryninjaapi.h"
+#include "MachO.h"
+#include "VirtualMemory.h"
-#include <optional>
+class SharedCache;
-DECLARE_SHAREDCACHE_API_OBJECT(BNSharedCache, SharedCache);
+struct CacheSymbol
+{
+ BNSymbolType type;
+ uint64_t address;
+ std::string name;
-namespace SharedCacheCore {
+ CacheSymbol() = default;
- enum DSCViewState
- {
- DSCViewStateUnloaded,
- DSCViewStateLoaded,
- DSCViewStateLoadedWithImages,
- };
-
- struct MemoryRegion : public MetadataSerializable<MemoryRegion>
- {
- enum class Type
- {
- Image,
- StubIsland,
- DyldData,
- NonImage,
- };
-
- std::string prettyName;
- uint64_t start;
- uint64_t size;
- // Start address of the image this region belongs to.
- // 0 if the region does not belong to any image.
- uint64_t imageStart = 0;
- BNSegmentFlag flags;
- Type type;
+ CacheSymbol(BNSymbolType type, uint64_t address, std::string name) :
+ type(type), address(address), name(std::move(name))
+ {}
+ ~CacheSymbol() = default;
- AddressRange AsAddressRange() const
- {
- return {start, start + size};
- }
+ CacheSymbol(const CacheSymbol& other) = default;
- void Store(SerializationContext& context) const
- {
- MSS(prettyName);
- MSS(start);
- MSS(size);
- MSS(imageStart);
- MSS_CAST(flags, uint64_t);
- MSS_CAST(type, uint8_t);
- }
+ CacheSymbol& operator=(const CacheSymbol& other) = default;
- static MemoryRegion Load(DeserializationContext& context)
- {
- MemoryRegion region;
- region.MSL(prettyName);
- region.MSL(start);
- region.MSL(size);
- region.MSL_CAST(flags, uint64_t, BNSegmentFlag);
- region.MSL_CAST(type, uint8_t, Type);
- if (context.doc.HasMember("imageStart"))
- region.MSL(imageStart);
+ CacheSymbol(CacheSymbol&& other) noexcept = default;
- return region;
- }
- };
+ CacheSymbol& operator=(CacheSymbol&& other) noexcept = default;
- struct CacheImage : public MetadataSerializable<CacheImage> {
- std::string installName;
- uint64_t headerLocation;
- // Start addresses of the memory regions in this image.
- std::vector<uint64_t> regionStarts;
+ // NOTE: you should really only call this when adding the symbol to the view.
+ BinaryNinja::Ref<BinaryNinja::Symbol> ToBNSymbol() const;
+};
- void Store(SerializationContext& context) const;
- static CacheImage Load(DeserializationContext& context);
- };
+enum class CacheRegionType
+{
+ Image,
+ StubIsland,
+ DyldData,
+ NonImage,
+};
- #if defined(__GNUC__) || defined(__clang__)
- #define PACKED_STRUCT __attribute__((packed))
- #else
- #define PACKED_STRUCT
- #endif
+struct CacheRegion
+{
+ CacheRegionType type;
+ std::string name;
+ uint64_t start;
+ uint64_t size;
+ // Associate this region with this image, this makes it easier to identify what image owns this region.
+ std::optional<uint64_t> imageStart;
+ BNSegmentFlag flags;
- #if defined(_MSC_VER)
- #pragma pack(push, 1)
- #else
+ CacheRegion() = default;
- #endif
+ ~CacheRegion() = default;
- struct PACKED_STRUCT dyld_cache_mapping_info
- {
- uint64_t address;
- uint64_t size;
- uint64_t fileOffset;
- uint32_t maxProt;
- uint32_t initProt;
- };
-
- struct BackingCache : public MetadataSerializable<BackingCache> {
- std::string path;
- BNBackingCacheType cacheType = BackingCacheTypeSecondary;
- std::vector<dyld_cache_mapping_info> mappings;
+ CacheRegion(const CacheRegion& other) = default;
- void Store(SerializationContext& context) const;
- static BackingCache Load(DeserializationContext& context);
- };
+ CacheRegion& operator=(const CacheRegion& other) = default;
- struct LoadedMapping
- {
- std::shared_ptr<MMappedFileAccessor> backingFile;
- dyld_cache_mapping_info mappingInfo;
- };
+ CacheRegion(CacheRegion&& other) noexcept = default;
- struct dyld_cache_slide_info
- {
- uint32_t version;
- uint32_t toc_offset;
- uint32_t toc_count;
- uint32_t entries_offset;
- uint32_t entries_count;
- uint32_t entries_size;
- // uint16_t toc[toc_count];
- // entrybitmap entries[entries_count];
- };
+ CacheRegion& operator=(CacheRegion&& other) noexcept = default;
- struct dyld_cache_slide_info_entry {
- uint8_t bits[4096/(8*4)]; // 128-byte bitmap
- };
+ AddressRange AsAddressRange() const { return {start, start + size}; }
- struct PACKED_STRUCT dyld_cache_mapping_and_slide_info
+ BNSectionSemantics SectionSemanticsForRegion() const
{
- uint64_t address;
- uint64_t size;
- uint64_t fileOffset;
- uint64_t slideInfoFileOffset;
- uint64_t slideInfoFileSize;
- uint64_t flags;
- uint32_t maxProt;
- uint32_t initProt;
- };
+ if ((flags & SegmentExecutable) && (flags & SegmentDenyWrite))
+ return ReadOnlyCodeSectionSemantics;
- 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;
- };
- #define DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA 0x8000 // index is into extras array (not starts array)
- #define DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE 0x4000 // page has no rebasing
- #define DYLD_CACHE_SLIDE_PAGE_ATTR_END 0x8000 // last chain entry for page
-
- #define DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE 0xFFFF // page has no rebasing
-
- 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
- };
+ if (flags & SegmentExecutable)
+ return DefaultSectionSemantics;
- // 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
- };
+ if (flags & SegmentDenyWrite)
+ return ReadOnlyDataSectionSemantics;
- // dyld_cache_slide_info4 is used in watchOS which we are not close to supporting right now.
+ return ReadWriteDataSectionSemantics;
+ }
+};
- #define DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE 0xFFFF // page has no rebasing
+// Represents a single image and its associated memory regions.
+struct CacheImage
+{
+ uint64_t headerAddress;
+ std::string path;
+ // A list to the start of memory regions associated with the image.
+ // This lets us load all regions for a given image easily.
+ std::vector<uint64_t> regionStarts;
+ std::shared_ptr<SharedCacheMachOHeader> header;
- 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;
- uint32_t pad; // padding to ensure the value below is on an 8-byte boundary
- uint64_t value_add;
- // uint16_t page_starts[/* page_starts_count */];
- };
+ CacheImage() = default;
+ ~CacheImage() = default;
- struct PACKED_STRUCT dyld_cache_image_info
- {
- uint64_t address;
- uint64_t modTime;
- uint64_t inode;
- uint32_t pathFileOffset;
- uint32_t pad;
- };
+ CacheImage(const CacheImage& other) = default;
- 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;
- };
+ CacheImage& operator=(const CacheImage& other) = default;
+ CacheImage(CacheImage&& other) noexcept = default;
- 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
- };
+ CacheImage& operator=(CacheImage&& other) noexcept = default;
- 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
- };
+ // Get the file name from the path.
+ std::string GetName() const { return BaseFileName(path); }
- 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
- };
+ // Get the names of the dependencies.
+ std::vector<std::string> GetDependencies() const;
+};
- union dyld_cache_slide_pointer3
- {
- uint64_t raw;
- struct
- {
- uint64_t pointerValue : 51, offsetToNextPointer : 11, unused : 2;
- } plain;
+enum class CacheEntryType
+{
+ Primary,
+ Secondary,
+ // A special entry that holds symbols for other cache entries.
+ // TODO: We dont need this i think.
+ Symbols,
+ // If the type is marked as this then all mappings will be marked as such.
+ DyldData,
+ // A single stub mapping file.
+ Stub,
+};
- struct
- {
- uint64_t offsetFromSharedCacheBase : 32, diversityData : 16, hasAddressDiversity : 1, key : 2,
- offsetToNextPointer : 11, unused : 1,
- authenticated : 1; // = 1;
- } auth;
- };
+// Describes a single files cache information
+class CacheEntry
+{
+ CacheEntryType m_type;
+ std::string m_filePath;
+ std::string m_fileName;
+ dyld_cache_header m_header {};
+ // Mappings tell us _where_ to map the regions within the flat address space.
+ // Without this we wouldn't know where the entry is supposed to exist in the address space.
+ std::vector<dyld_cache_mapping_info> m_mappings {};
+ // TODO: We really should remove this methinks.
+ // TODO: Storing this here is basically useless? IDK
+ // Mapping of image path to image info, used within ProcessImagesAndRegions to add them to the cache.
+ std::unordered_map<std::string, dyld_cache_image_info> m_images {};
+public:
+ CacheEntry(std::string filePath, std::string fileName, CacheEntryType type, dyld_cache_header header,
+ std::vector<dyld_cache_mapping_info> mappings, std::unordered_map<std::string, dyld_cache_image_info> images);
- 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, 2 for multi-cache
- uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses
- uint32_t branchPoolsCount; // number of uint64_t entries
- uint64_t dyldInCacheMH; // (unslid) address of mach_header of dyld in cache
- uint64_t dyldInCacheEntry; // (unslid) address of entry point (_dyld_start) of dyld in cache
- 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; // VM offset from cache_header* 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
- uint32_t cacheSubType; // 0 for development, 1 for production, when cacheType is multi-cache(2)
- uint32_t padding2;
- uint64_t objcOptsOffset; // VM offset from cache_header* to ObjC optimizations header
- uint64_t objcOptsSize; // size of ObjC optimizations header
- uint64_t cacheAtlasOffset; // VM offset from cache_header* to embedded cache atlas for process introspection
- uint64_t cacheAtlasSize; // size of embedded cache atlas
- uint64_t dynamicDataOffset; // VM offset from cache_header* to the location of dyld_cache_dynamic_data_header
- uint64_t dynamicDataMaxSize; // maximum size of space reserved from dynamic data
- uint32_t tproMappingsOffset; // file offset to first dyld_cache_tpro_mapping_info
- uint32_t tproMappingsCount; // number of dyld_cache_tpro_mapping_info entries
- };
+ CacheEntry() = default;
- struct PACKED_STRUCT dyld_subcache_entry
- {
- char uuid[16];
- uint64_t address;
- };
+ CacheEntry(const CacheEntry&) = default;
- struct PACKED_STRUCT dyld_subcache_entry2
- {
- char uuid[16];
- uint64_t address;
- char fileExtension[32];
- };
+ CacheEntry(CacheEntry&&) = default;
- struct ObjCOptimizationHeader
- {
- uint32_t version;
- uint32_t flags;
- uint64_t headerInfoROCacheOffset;
- uint64_t headerInfoRWCacheOffset;
- uint64_t selectorHashTableCacheOffset;
- uint64_t classHashTableCacheOffset;
- uint64_t protocolHashTableCacheOffset;
- uint64_t relativeMethodSelectorBaseAddressOffset;
- };
+ CacheEntry& operator=(CacheEntry&&) = default;
- #if defined(_MSC_VER)
- #pragma pack(pop)
- #else
+ // Construct a cache entry from the file on disk.
+ // TODO: Seperate this out a bit more.
+ static std::optional<CacheEntry> FromFile(const std::string& filePath, const std::string& fileName, CacheEntryType type);
- #endif
-
- struct SharedCacheMachOHeader : public MetadataSerializable<SharedCacheMachOHeader>
- {
- uint64_t textBase = 0;
- uint64_t loadCommandOffset = 0;
- mach_header_64 ident;
- std::string identifierPrefix;
- std::string installName;
+ // TODO: From Project file?
- std::vector<std::pair<uint64_t, bool>> entryPoints;
- std::vector<uint64_t> m_entryPoints; // list of entrypoints
+ WeakFileAccessor GetAccessor() const;
- 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 {};
+ // Get the headers virtual address.
+ // This is useful if you need to read relative to the start of the entry file.
+ std::optional<uint64_t> GetHeaderAddress() const;
- 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;
+ // Get the mapped address for a given file offset.
+ // Ex. passing 0x0 will retrieve the mapped address for the start of the file (i.e. the header)
+ std::optional<uint64_t> GetMappedAddress(uint64_t fileOffset) const;
- std::vector<section_64> symbolStubSections;
- std::vector<section_64> symbolPointerSections;
+ CacheEntryType GetType() const { return m_type; }
+ // Ex. "/myuser/mypath/dyld_shared_cache_arm64e"
+ const std::string& GetFilePath() const { return m_filePath; }
+ // Ex. "dyld_shared_cache_arm64e"
+ const std::string GetFileName() const { return m_fileName; }
+ const dyld_cache_header& GetHeader() const { return m_header; }
+ const std::vector<dyld_cache_mapping_info>& GetMappings() const { return m_mappings; }
+ const std::unordered_map<std::string, dyld_cache_image_info>& GetImages() const { return m_images; }
+};
- std::vector<std::string> dylibs;
+// The ID for a given CacheEntry, use this instead of passing a pointer around to avoid complexity :V
+typedef uint32_t CacheEntryId;
- build_version_command buildVersion;
- std::vector<build_tool_version> buildToolVersions;
+// TODO: Add a "ViewCache" that keeps track of what has been added to the view.
- std::string exportTriePath;
+// The C in DSC.
+// This represents the entire cache, all regions and images are visible from here.
+// This is the dump for all the information, and what the workflow activities and the UI want.
+// Creating this is expensive, both in actual processing and just copying, so we only generate this
+// once every time the database is open.
+class SharedCache
+{
+ uint64_t m_addressSize = 8;
+ uint64_t m_baseAddress = 0;
+ // TODO: Figure out when to lock the mutex on this shit lmfao
+ // The shared cache can own the virtual memory, this is fine...
+ std::shared_ptr<VirtualMemory> m_vm;
+ std::unordered_map<CacheEntryId, CacheEntry> m_entries {};
+ // This information is used in tandem with the cache images to load memory regions into the binary view.
+ AddressRangeMap<CacheRegion> m_regions {};
+ // Describes the images of the cache.
+ std::unordered_map<uint64_t, CacheImage> m_images {};
+ // All the symbols for this cache. Both mapped and unmapped (not in the view).
+ std::unordered_map<uint64_t, CacheSymbol> m_symbols {};
- bool linkeditPresent = false;
- bool dysymPresent = false;
- bool dyldInfoPresent = false;
- bool exportTriePresent = false;
- bool chainedFixupsPresent = false;
- bool routinesPresent = false;
- bool functionStartsPresent = false;
- bool relocatable = false;
+ bool ProcessEntryImage(const std::string& path, const dyld_cache_image_info& info);
- void Store(SerializationContext& context) const {
- 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(linkeditPresent);
- MSS(dysymPresent);
- MSS(dyldInfoPresent);
- MSS(exportTriePresent);
- MSS(chainedFixupsPresent);
- MSS(routinesPresent);
- MSS(functionStartsPresent);
- MSS(relocatable);
- }
+ // Add a region known not to overlap with another, otherwise use AddRegion.
+ // returns whether the region was inserted.
+ bool AddNonOverlappingRegion(CacheRegion region);
- static SharedCacheMachOHeader Load(DeserializationContext& context) {
- SharedCacheMachOHeader header;
- header.MSL(textBase);
- header.MSL(loadCommandOffset);
- header.MSL(ident);
- header.MSL(identifierPrefix);
- header.MSL(installName);
- header.MSL(entryPoints);
- header.MSL(m_entryPoints);
- header.MSL(symtab);
- header.MSL(dysymtab);
- header.MSL(dyldInfo);
- header.MSL(routines64);
- header.MSL(functionStarts);
- header.MSL(moduleInitSections);
- header.MSL(exportTrie);
- header.MSL(chainedFixups);
- header.MSL(relocationBase);
- header.MSL(segments);
- header.MSL(linkeditSegment);
- header.MSL(sections);
- header.MSL(sectionNames);
- header.MSL(symbolStubSections);
- header.MSL(symbolPointerSections);
- header.MSL(dylibs);
- header.MSL(buildVersion);
- header.MSL(buildToolVersions);
- header.MSL(exportTriePath);
- header.MSL(linkeditPresent);
- header.MSL(dysymPresent);
- header.MSL(dyldInfoPresent);
- header.MSL(exportTriePresent);
- header.MSL(chainedFixupsPresent);
- header.MSL(routinesPresent);
- header.MSL(functionStartsPresent);
- header.MSL(relocatable);
- return header;
- }
- };
+public:
+ explicit SharedCache(uint64_t addressSize);
- struct MappingInfo
- {
- 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;
- };
+ uint64_t GetBaseAddress() const { return m_baseAddress; }
+ std::shared_ptr<VirtualMemory> GetVirtualMemory() { return m_vm; }
+ const std::unordered_map<CacheEntryId, CacheEntry>& GetEntries() const { return m_entries; }
+ const AddressRangeMap<CacheRegion>& GetRegions() const { return m_regions; }
+ const std::unordered_map<uint64_t, CacheImage>& GetImages() const { return m_images; }
+ const std::unordered_map<uint64_t, CacheSymbol>& GetSymbols() const { return m_symbols; }
+ void AddImage(CacheImage image);
- class ScopedVMMapSession;
+ // Add a region that may overlap with another.
+ void AddRegion(CacheRegion region);
- static std::atomic<uint64_t> sharedCacheReferences = 0;
+ void AddSymbol(CacheSymbol symbol);
- class SharedCache
- {
- IMPLEMENT_SHAREDCACHE_API_OBJECT(BNSharedCache);
+ void AddSymbols(std::vector<CacheSymbol> symbols);
- std::atomic<int> m_refs = 0;
+ // Adds the cache entry and populates the virtual memory using the mapping information.
+ // After being added the entry is read only, there is nothing that can modify it.
+ CacheEntryId AddEntry(CacheEntry entry);
- public:
- virtual void AddRef() { m_refs.fetch_add(1); }
+ void ProcessEntryImages(const CacheEntry& entry);
- 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;
- });
- }
+ void ProcessEntryRegions(const CacheEntry& entry);
- virtual void AddAPIRef() { AddRef(); }
+ void ProcessEntrySlideInfo(const CacheEntry& entry);
- virtual void ReleaseAPIRef() { Release(); }
+ std::optional<CacheEntry> GetEntryContaining(uint64_t address) const;
- public:
- enum SharedCacheFormat
- {
- RegularCacheFormat,
- SplitCacheFormat,
- LargeCacheFormat,
- iOS16CacheFormat,
- };
+ std::optional<CacheEntry> GetEntryWithImage(const CacheImage& image) const;
- struct CacheInfo;
- struct ModifiedState;
+ std::optional<CacheRegion> GetRegionAt(uint64_t address) const;
- struct ViewSpecificState;
+ std::optional<CacheRegion> GetRegionContaining(uint64_t address) const;
+ std::optional<CacheImage> GetImageAt(uint64_t address) const;
- private:
- Ref<Logger> m_logger;
- /* VIEW STATE BEGIN -- SERIALIZE ALL OF THIS AND STORE IT IN RAW VIEW */
-
- // State that is initialized during `PerformInitialLoad` and does
- // not change thereafter.
- std::shared_ptr<const CacheInfo> m_cacheInfo;
-
- // Protects member variables below.
- mutable std::mutex m_mutex;
-
- // State that has been modified since this instance was created
- // or last saved to the view-specific state.
- // To get an accurate view of the current state, both these modifications
- // and the view-specific state must be consulted.
- std::unique_ptr<ModifiedState> m_modifiedState;
-
- // Serialized once by PerformInitialLoad and available after m_viewState == Loaded
- bool m_metadataValid = false;
-
- /* VIEWSTATE END -- NOTHING PAST THIS IS SERIALIZED */
-
- /* API VIEW START */
- BinaryNinja::Ref<BinaryNinja::BinaryView> m_dscView;
- /* API VIEW END */
-
- std::shared_ptr<ViewSpecificState> m_viewSpecificState;
-
- private:
- void PerformInitialLoad(std::lock_guard<std::mutex>&);
- bool DeserializeFromRawView(std::lock_guard<std::mutex>&);
-
- public:
- std::shared_ptr<VM> GetVMMap();
- std::shared_ptr<VM> GetVMMap(const CacheInfo& staticState);
-
- static SharedCache* GetFromDSCView(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView);
- static uint64_t FastGetBackingCacheCount(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView);
- bool SaveCacheInfoToDSCView(std::lock_guard<std::mutex>&);
- bool SaveModifiedStateToDSCView(std::lock_guard<std::mutex>&);
-
- static void ParseAndApplySlideInfoForFile(std::shared_ptr<MMappedFileAccessor> file, uint64_t baseAddress, Ref<Logger> logger);
- std::optional<uint64_t> GetImageStart(std::string_view installName);
- const SharedCacheMachOHeader* HeaderForAddress(uint64_t);
- bool LoadImageWithInstallName(std::string installName, bool skipObjC);
- bool LoadSectionAtAddress(uint64_t address);
- bool LoadImageContainingAddress(uint64_t address, bool skipObjC);
- void ProcessObjCSectionsForImageWithInstallName(std::string installName);
- void ProcessAllObjCSections();
- std::string NameForAddress(uint64_t address);
- std::string ImageNameForAddress(uint64_t address);
- std::vector<std::string> GetAvailableImages();
-
- std::vector<const MemoryRegion*> GetMappedRegions() const;
- bool IsMemoryMapped(uint64_t address);
-
- std::unordered_map<std::string, std::vector<Ref<Symbol>>> LoadAllSymbolsAndWait();
-
- const std::unordered_map<std::string, uint64_t>& AllImageStarts() const;
- const std::unordered_map<uint64_t, SharedCacheMachOHeader>& AllImageHeaders() const;
-
- std::string SerializedImageHeaderForAddress(uint64_t address);
- std::string SerializedImageHeaderForName(std::string name);
-
- void FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis);
-
- const std::vector<BackingCache>& BackingCaches() const;
-
- DSCViewState ViewState() const;
-
- explicit SharedCache(BinaryNinja::Ref<BinaryNinja::BinaryView> rawView);
- virtual ~SharedCache();
-
- uint64_t GetObjCRelativeMethodBaseAddress(const VMReader& reader) const;
-
-private:
- std::optional<SharedCacheMachOHeader> LoadHeaderForAddress(
- std::shared_ptr<VM> vm, uint64_t address, std::string installName);
- void InitializeHeader(
- std::lock_guard<std::mutex>&, Ref<BinaryView> view, VM* vm, const SharedCacheMachOHeader& header,
- std::vector<const MemoryRegion*> regionsToLoad);
- void ReadExportNode(std::vector<Ref<Symbol>>& symbolList, const SharedCacheMachOHeader& header, const uint8_t* begin,
- const uint8_t *end, const uint8_t* current, uint64_t textBase, const std::string& currentText);
- std::vector<Ref<Symbol>> ParseExportTrie(
- std::shared_ptr<MMappedFileAccessor> linkeditFile, const SharedCacheMachOHeader& header);
- std::shared_ptr<std::unordered_map<uint64_t, Ref<Symbol>>> GetExportListForHeader(std::lock_guard<std::mutex>&, const SharedCacheMachOHeader& header,
- std::function<std::shared_ptr<MMappedFileAccessor>()> provideLinkeditFile, bool* didModifyExportList = nullptr);
- std::shared_ptr<std::unordered_map<uint64_t, Ref<Symbol>>> GetExistingExportListForBaseAddress(std::lock_guard<std::mutex>&, uint64_t baseAddress) const;
- void ProcessSymbols(std::shared_ptr<MMappedFileAccessor> file, const SharedCacheMachOHeader& header,
- uint64_t stringsOffset, size_t stringsSize, uint64_t nlistEntriesOffset, uint32_t nlistCount, uint32_t nlistStartIndex = 0);
- void ApplySymbol(Ref<BinaryView> view, Ref<TypeLibrary> typeLib, Ref<Symbol> symbol);
-
- void ProcessAllObjCSections(std::lock_guard<std::mutex>&);
- bool LoadImageWithInstallName(std::lock_guard<std::mutex>&, std::string installName, bool skipObjC);
-
- bool MemoryRegionIsLoaded(std::lock_guard<std::mutex>&, const MemoryRegion& region) const;
- void SetMemoryRegionIsLoaded(std::lock_guard<std::mutex>&, const MemoryRegion& region);
- bool MemoryRegionIsHeaderInitialized(std::lock_guard<std::mutex>&, const MemoryRegion& region) const;
- void SetMemoryRegionHeaderInitialized(std::lock_guard<std::mutex>&, const MemoryRegion& region);
-
- Ref<TypeLibrary> TypeLibraryForImage(const std::string& installName);
-
- std::optional<ObjCOptimizationHeader> GetObjCOptimizationHeader(VMReader reader) const;
-
- std::shared_ptr<MMappedFileAccessor> MapFile(const std::string& path);
- static std::shared_ptr<MMappedFileAccessor> MapFileWithoutApplyingSlide(const std::string& path);
- };
-
- class SharedCacheMetadata
- {
- public:
- static std::optional<SharedCacheMetadata> LoadFromView(BinaryView*);
- static bool ViewHasMetadata(BinaryView*);
- static std::optional<unsigned int> ViewMetadataVersion(BinaryView*);
-
- const std::unordered_map<uint64_t, std::shared_ptr<std::unordered_map<uint64_t, Ref<Symbol>>>>& ExportInfos() const;
- std::string InstallNameForImageBaseAddress(uint64_t baseAddress) const;
+ std::optional<CacheImage> GetImageContaining(uint64_t address) const;
- ~SharedCacheMetadata();
- SharedCacheMetadata(SharedCacheMetadata&&);
- SharedCacheMetadata& operator=(SharedCacheMetadata&&);
+ // TODO: Rename to GetImageWithPath and then make another one for the image name.
+ std::optional<CacheImage> GetImageWithName(const std::string& name) const;
- private:
- SharedCacheMetadata(SharedCache::CacheInfo, SharedCache::ModifiedState);
+ std::optional<CacheSymbol> GetSymbolAt(uint64_t address) const;
- std::unique_ptr<SharedCache::CacheInfo> cacheInfo;
- std::unique_ptr<SharedCache::ModifiedState> state;
+ std::optional<CacheSymbol> GetSymbolWithName(const std::string& name) const;
+};
- friend struct SharedCache::ModifiedState;
- friend class SharedCache;
+// This constructs a Cache, give it a file path, and it will add all relevant cache entries.
+class CacheProcessor
+{
+ BinaryNinja::Ref<BinaryNinja::BinaryView> m_view;
+ BinaryNinja::Ref<BinaryNinja::Logger> m_logger;
- static const std::string Tag;
- static const std::string CacheInfoTag;
- static const std::string ModifiedStateTagPrefix;
- static const std::string ModifiedStateCountTag;
- };
-}
+public:
+ explicit CacheProcessor(BinaryNinja::Ref<BinaryNinja::BinaryView> view);
-void InitDSCViewType();
+ // Construct a cache from the root file, this will parse the cache header and locate all
+ // applicable cache entries and add them as well.
+ bool ProcessCache(SharedCache& cache);
-#endif //SHAREDCACHE_SHAREDCACHE_H
+ // Process a cache on the file system, this is for when not using a project.
+ bool ProcessFileCache(SharedCache& cache);
+ // Process a cache using Binary Ninja's project system.
+ bool ProcessProjectCache(SharedCache& cache);
+};