diff options
| author | Mason Reed <mason@vector35.com> | 2025-03-10 11:05:40 -0400 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2025-04-02 05:36:54 -0400 |
| commit | 25cc02431b61097b2adfc2fbc493b648b0300c3b (patch) | |
| tree | a79d9c4f4f67234d3bf9bda413e8608f479a4cc8 /view/sharedcache/core/SharedCache.h | |
| parent | fa85bf28502286c4821427c5d0ed91a7ed46f8f6 (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.h | 829 |
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); +}; |
