From 25cc02431b61097b2adfc2fbc493b648b0300c3b Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Mon, 10 Mar 2025 11:05:40 -0400 Subject: [SharedCache] Refactor Shared Cache In absence of a better name, this commit refactors the shared cache code. --- view/sharedcache/core/SharedCache.h | 853 +++++++++--------------------------- 1 file changed, 213 insertions(+), 640 deletions(-) (limited to 'view/sharedcache/core/SharedCache.h') 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 +#include -#include -#include -#include -#include -#include -#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 +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, - }; + CacheSymbol(BNSymbolType type, uint64_t address, std::string name) : + type(type), address(address), name(std::move(name)) + {} - struct MemoryRegion : public MetadataSerializable - { - 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; - - - AddressRange AsAddressRange() const - { - return {start, start + size}; - } - - void Store(SerializationContext& context) const - { - MSS(prettyName); - MSS(start); - MSS(size); - MSS(imageStart); - MSS_CAST(flags, uint64_t); - MSS_CAST(type, uint8_t); - } - - 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); - - return region; - } - }; - - struct CacheImage : public MetadataSerializable { - std::string installName; - uint64_t headerLocation; - // Start addresses of the memory regions in this image. - std::vector regionStarts; - - void Store(SerializationContext& context) const; - static CacheImage Load(DeserializationContext& context); - }; - - #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 BackingCache : public MetadataSerializable { - std::string path; - BNBackingCacheType cacheType = BackingCacheTypeSecondary; - std::vector mappings; - - void Store(SerializationContext& context) const; - static BackingCache Load(DeserializationContext& context); - }; - - struct LoadedMapping - { - std::shared_ptr backingFile; - dyld_cache_mapping_info mappingInfo; - }; + ~CacheSymbol() = 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]; - }; - - struct dyld_cache_slide_info_entry { - uint8_t bits[4096/(8*4)]; // 128-byte bitmap - }; - - 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; - }; - #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; - }; + CacheSymbol(const CacheSymbol& other) = default; + CacheSymbol& operator=(const CacheSymbol& other) = default; - // 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 - }; + CacheSymbol(CacheSymbol&& other) noexcept = default; - // dyld_cache_slide_info4 is used in watchOS which we are not close to supporting right now. + CacheSymbol& operator=(CacheSymbol&& other) noexcept = default; - #define DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE 0xFFFF // page has no rebasing + // NOTE: you should really only call this when adding the symbol to the view. + BinaryNinja::Ref ToBNSymbol() const; +}; - 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 */]; - }; +enum class CacheRegionType +{ + Image, + StubIsland, + DyldData, + NonImage, +}; +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 imageStart; + BNSegmentFlag flags; - 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; - }; + CacheRegion() = default; + ~CacheRegion() = 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 - }; - - 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 - }; + CacheRegion(const CacheRegion& other) = default; - 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 - }; + CacheRegion& operator=(const CacheRegion& other) = default; - 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, 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 - }; - - 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]; - }; + CacheRegion(CacheRegion&& other) noexcept = 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; - }; - - #if defined(_MSC_VER) - #pragma pack(pop) - #else - - #endif - - struct SharedCacheMachOHeader : public MetadataSerializable - { - uint64_t textBase = 0; - uint64_t loadCommandOffset = 0; - mach_header_64 ident; - std::string identifierPrefix; - std::string installName; - - std::vector> entryPoints; - std::vector 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 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 segments; // only three types of sections __TEXT, __DATA, __IMPORT - segment_command_64 linkeditSegment; - std::vector sections; - std::vector sectionNames; - - std::vector symbolStubSections; - std::vector symbolPointerSections; - - std::vector dylibs; - - build_version_command buildVersion; - std::vector 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 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); - } - - 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; - } - }; - - 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; - }; + CacheRegion& operator=(CacheRegion&& other) noexcept = default; + AddressRange AsAddressRange() const { return {start, start + size}; } - class ScopedVMMapSession; - - static std::atomic sharedCacheReferences = 0; - - class SharedCache + BNSectionSemantics SectionSemanticsForRegion() const { - IMPLEMENT_SHAREDCACHE_API_OBJECT(BNSharedCache); - - std::atomic m_refs = 0; - - public: - virtual void AddRef() { m_refs.fetch_add(1); } + if ((flags & SegmentExecutable) && (flags & SegmentDenyWrite)) + return ReadOnlyCodeSectionSemantics; - 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; - }); - } + if (flags & SegmentExecutable) + return DefaultSectionSemantics; + + if (flags & SegmentDenyWrite) + return ReadOnlyDataSectionSemantics; + + return ReadWriteDataSectionSemantics; + } +}; - virtual void AddAPIRef() { AddRef(); } +// 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 regionStarts; + std::shared_ptr header; + + CacheImage() = default; + + ~CacheImage() = default; + + CacheImage(const CacheImage& other) = default; + + CacheImage& operator=(const CacheImage& other) = default; + + CacheImage(CacheImage&& other) noexcept = default; + + CacheImage& operator=(CacheImage&& other) noexcept = default; + + // Get the file name from the path. + std::string GetName() const { return BaseFileName(path); } + + // Get the names of the dependencies. + std::vector GetDependencies() const; +}; + +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, +}; + +// 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 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 m_images {}; - virtual void ReleaseAPIRef() { Release(); } +public: + CacheEntry(std::string filePath, std::string fileName, CacheEntryType type, dyld_cache_header header, + std::vector mappings, std::unordered_map images); - public: - enum SharedCacheFormat - { - RegularCacheFormat, - SplitCacheFormat, - LargeCacheFormat, - iOS16CacheFormat, - }; + CacheEntry() = default; - struct CacheInfo; - struct ModifiedState; + CacheEntry(const CacheEntry&) = default; - struct ViewSpecificState; + CacheEntry(CacheEntry&&) = default; + CacheEntry& operator=(CacheEntry&&) = default; - private: - Ref m_logger; - /* VIEW STATE BEGIN -- SERIALIZE ALL OF THIS AND STORE IT IN RAW VIEW */ + // Construct a cache entry from the file on disk. + // TODO: Seperate this out a bit more. + static std::optional FromFile(const std::string& filePath, const std::string& fileName, CacheEntryType type); - // State that is initialized during `PerformInitialLoad` and does - // not change thereafter. - std::shared_ptr m_cacheInfo; + // TODO: From Project file? - // Protects member variables below. - mutable std::mutex m_mutex; + WeakFileAccessor GetAccessor() const; - // 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 m_modifiedState; + // Get the headers virtual address. + // This is useful if you need to read relative to the start of the entry file. + std::optional GetHeaderAddress() const; - // Serialized once by PerformInitialLoad and available after m_viewState == Loaded - bool m_metadataValid = false; + // 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 GetMappedAddress(uint64_t fileOffset) const; - /* VIEWSTATE END -- NOTHING PAST THIS IS SERIALIZED */ + 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& GetMappings() const { return m_mappings; } + const std::unordered_map& GetImages() const { return m_images; } +}; - /* API VIEW START */ - BinaryNinja::Ref m_dscView; - /* API VIEW END */ +// The ID for a given CacheEntry, use this instead of passing a pointer around to avoid complexity :V +typedef uint32_t CacheEntryId; - std::shared_ptr m_viewSpecificState; +// TODO: Add a "ViewCache" that keeps track of what has been added to the view. - private: - void PerformInitialLoad(std::lock_guard&); - bool DeserializeFromRawView(std::lock_guard&); +// 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 m_vm; + std::unordered_map m_entries {}; + // This information is used in tandem with the cache images to load memory regions into the binary view. + AddressRangeMap m_regions {}; + // Describes the images of the cache. + std::unordered_map m_images {}; + // All the symbols for this cache. Both mapped and unmapped (not in the view). + std::unordered_map m_symbols {}; - public: - std::shared_ptr GetVMMap(); - std::shared_ptr GetVMMap(const CacheInfo& staticState); + bool ProcessEntryImage(const std::string& path, const dyld_cache_image_info& info); - static SharedCache* GetFromDSCView(BinaryNinja::Ref dscView); - static uint64_t FastGetBackingCacheCount(BinaryNinja::Ref dscView); - bool SaveCacheInfoToDSCView(std::lock_guard&); - bool SaveModifiedStateToDSCView(std::lock_guard&); + // Add a region known not to overlap with another, otherwise use AddRegion. + // returns whether the region was inserted. + bool AddNonOverlappingRegion(CacheRegion region); - static void ParseAndApplySlideInfoForFile(std::shared_ptr file, uint64_t baseAddress, Ref logger); - std::optional 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 GetAvailableImages(); +public: + explicit SharedCache(uint64_t addressSize); - std::vector GetMappedRegions() const; - bool IsMemoryMapped(uint64_t address); + uint64_t GetBaseAddress() const { return m_baseAddress; } + std::shared_ptr GetVirtualMemory() { return m_vm; } + const std::unordered_map& GetEntries() const { return m_entries; } + const AddressRangeMap& GetRegions() const { return m_regions; } + const std::unordered_map& GetImages() const { return m_images; } + const std::unordered_map& GetSymbols() const { return m_symbols; } - std::unordered_map>> LoadAllSymbolsAndWait(); + void AddImage(CacheImage image); - const std::unordered_map& AllImageStarts() const; - const std::unordered_map& AllImageHeaders() const; + // Add a region that may overlap with another. + void AddRegion(CacheRegion region); - std::string SerializedImageHeaderForAddress(uint64_t address); - std::string SerializedImageHeaderForName(std::string name); + void AddSymbol(CacheSymbol symbol); - void FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis); + void AddSymbols(std::vector symbols); - const std::vector& BackingCaches() const; + // 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); - DSCViewState ViewState() const; + void ProcessEntryImages(const CacheEntry& entry); - explicit SharedCache(BinaryNinja::Ref rawView); - virtual ~SharedCache(); + void ProcessEntryRegions(const CacheEntry& entry); - uint64_t GetObjCRelativeMethodBaseAddress(const VMReader& reader) const; + void ProcessEntrySlideInfo(const CacheEntry& entry); -private: - std::optional LoadHeaderForAddress( - std::shared_ptr vm, uint64_t address, std::string installName); - void InitializeHeader( - std::lock_guard&, Ref view, VM* vm, const SharedCacheMachOHeader& header, - std::vector regionsToLoad); - void ReadExportNode(std::vector>& 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> ParseExportTrie( - std::shared_ptr linkeditFile, const SharedCacheMachOHeader& header); - std::shared_ptr>> GetExportListForHeader(std::lock_guard&, const SharedCacheMachOHeader& header, - std::function()> provideLinkeditFile, bool* didModifyExportList = nullptr); - std::shared_ptr>> GetExistingExportListForBaseAddress(std::lock_guard&, uint64_t baseAddress) const; - void ProcessSymbols(std::shared_ptr file, const SharedCacheMachOHeader& header, - uint64_t stringsOffset, size_t stringsSize, uint64_t nlistEntriesOffset, uint32_t nlistCount, uint32_t nlistStartIndex = 0); - void ApplySymbol(Ref view, Ref typeLib, Ref symbol); + std::optional GetEntryContaining(uint64_t address) const; - void ProcessAllObjCSections(std::lock_guard&); - bool LoadImageWithInstallName(std::lock_guard&, std::string installName, bool skipObjC); + std::optional GetEntryWithImage(const CacheImage& image) const; - bool MemoryRegionIsLoaded(std::lock_guard&, const MemoryRegion& region) const; - void SetMemoryRegionIsLoaded(std::lock_guard&, const MemoryRegion& region); - bool MemoryRegionIsHeaderInitialized(std::lock_guard&, const MemoryRegion& region) const; - void SetMemoryRegionHeaderInitialized(std::lock_guard&, const MemoryRegion& region); + std::optional GetRegionAt(uint64_t address) const; - Ref TypeLibraryForImage(const std::string& installName); + std::optional GetRegionContaining(uint64_t address) const; - std::optional GetObjCOptimizationHeader(VMReader reader) const; - - std::shared_ptr MapFile(const std::string& path); - static std::shared_ptr MapFileWithoutApplyingSlide(const std::string& path); - }; - - class SharedCacheMetadata - { - public: - static std::optional LoadFromView(BinaryView*); - static bool ViewHasMetadata(BinaryView*); - static std::optional ViewMetadataVersion(BinaryView*); + std::optional GetImageAt(uint64_t address) const; - const std::unordered_map>>>& ExportInfos() const; - std::string InstallNameForImageBaseAddress(uint64_t baseAddress) const; + std::optional 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 GetImageWithName(const std::string& name) const; - private: - SharedCacheMetadata(SharedCache::CacheInfo, SharedCache::ModifiedState); + std::optional GetSymbolAt(uint64_t address) const; - std::unique_ptr cacheInfo; - std::unique_ptr state; + std::optional 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 m_view; + BinaryNinja::Ref 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 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); +}; -- cgit v1.3.1