diff options
Diffstat (limited to 'view/sharedcache')
| -rw-r--r-- | view/sharedcache/core/DSCView.cpp | 18 | ||||
| -rw-r--r-- | view/sharedcache/core/ObjC.cpp | 116 | ||||
| -rw-r--r-- | view/sharedcache/core/ObjC.h | 9 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCache.cpp | 44 | ||||
| -rw-r--r-- | view/sharedcache/core/SharedCache.h | 163 | ||||
| -rw-r--r-- | view/sharedcache/workflow/SharedCacheWorkflow.cpp | 2 |
6 files changed, 227 insertions, 125 deletions
diff --git a/view/sharedcache/core/DSCView.cpp b/view/sharedcache/core/DSCView.cpp index 31af3bd7..1ef6198b 100644 --- a/view/sharedcache/core/DSCView.cpp +++ b/view/sharedcache/core/DSCView.cpp @@ -200,6 +200,15 @@ bool DSCView::Init() "\t\tuint64_t rosettaReadWriteSize;\t// maximum size of the Rosetta read-write region\n" "\t\tuint32_t imagesOffset;\t\t\t// file offset to first dyld_cache_image_info\n" "\t\tuint32_t imagesCount;\t\t\t// number of dyld_cache_image_info entries\n" + "\t\tuint32_t cacheSubType; // 0 for development, 1 for production, when cacheType is multi-cache(2)\n" + "\t\tuint64_t objcOptsOffset; // VM offset from cache_header* to ObjC optimizations header\n" + "\t\tuint64_t objcOptsSize; // size of ObjC optimizations header\n" + "\t\tuint64_t cacheAtlasOffset; // VM offset from cache_header* to embedded cache atlas for process introspection\n" + "\t\tuint64_t cacheAtlasSize; // size of embedded cache atlas\n" + "\t\tuint64_t dynamicDataOffset; // VM offset from cache_header* to the location of dyld_cache_dynamic_data_header\n" + "\t\tuint64_t dynamicDataMaxSize; // maximum size of space reserved from dynamic data\n" + "\t\tuint32_t tproMappingsOffset; // file offset to first dyld_cache_tpro_mapping_info\n" + "\t\tuint32_t tproMappingsCount; // number of dyld_cache_tpro_mapping_info entries\n" "\t};", headerType, err); Ref<Settings> settings = GetLoadSettings(GetTypeName()); @@ -732,8 +741,13 @@ bool DSCView::Init() return false; } - AddAutoSegment(primaryBase, 0x200, 0, 0x200, SegmentReadable); - AddAutoSection("__dsc_header", primaryBase, 0x200, ReadOnlyCodeSectionSemantics); + uint64_t headerSize = std::min(basePointer, headerType.type->GetWidth()); + // Truncate the `dyld_cache_header` structure to the structure present in the cache file. + auto newStructure = StructureBuilder(headerType.type->GetStructure()).SetWidth(headerSize).Finalize(); + headerType.type = TypeBuilder::StructureType(newStructure).Finalize(); + + AddAutoSegment(primaryBase, headerSize, 0, headerSize, SegmentReadable); + AddAutoSection("__dsc_header", primaryBase, headerSize, ReadOnlyDataSectionSemantics); DefineType("dyld_cache_header", headerType.name, headerType.type); DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), new Symbol(DataSymbol, "primary_cache_header", primaryBase), headerType.type); diff --git a/view/sharedcache/core/ObjC.cpp b/view/sharedcache/core/ObjC.cpp index 95fbb2a2..6e998e2a 100644 --- a/view/sharedcache/core/ObjC.cpp +++ b/view/sharedcache/core/ObjC.cpp @@ -775,7 +775,7 @@ void DSCObjCProcessor::LoadProtocols(VMReader* reader, Ref<Section> listSection) } } -void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start) +void DSCObjCProcessor::ReadListOfMethodLists(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start) { reader->Seek(start); method_list_t head; @@ -783,14 +783,49 @@ void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::str head.count = reader->Read32(); if (head.count > 0x1000) { + m_logger->LogError("List of method lists at 0x%llx has an invalid count of 0x%x", start, head.count); + return; + } + + for (size_t i = 0; i < head.count; ++i) { + relative_list_list_entry_t list_entry; + reader->Read(&list_entry, sizeof(list_entry)); + + ReadMethodList(reader, cls, name, reader->GetOffset() - sizeof(list_entry) + list_entry.listOffset); + // Reset the cursor to immediately past the list entry. + reader->Seek(start + sizeof(method_list_t) + ((i + 1) * sizeof(relative_list_list_entry_t))); + } +} + +void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start) +{ + // Lower two bits indicate the type of method list. + switch (start & 0b11) { + case 0: + break; + case 1: + return ReadListOfMethodLists(reader, cls, name, start - 1); + default: + m_logger->LogDebug("ReadMethodList: Unknown method list type at 0x%llx: %d", start, start & 0x3); + return; + } + + reader->Seek(start); + method_list_t head; + head.entsizeAndFlags = reader->Read32(); + head.count = reader->Read32(); + + if (head.count > 0x1000) + { m_logger->LogError("Method list at 0x%llx has an invalid count of 0x%x", start, head.count); return; } + uint64_t pointerSize = m_data->GetAddressSize(); bool relativeOffsets = (head.entsizeAndFlags & 0xFFFF0000) & 0x80000000; bool directSelectors = (head.entsizeAndFlags & 0xFFFF0000) & 0x40000000; auto methodSize = relativeOffsets ? 12 : pointerSize * 3; - DefineObjCSymbol(DataSymbol, m_typeNames.methodList, "method_list_" + name, start, true); + DefineObjCSymbol(DataSymbol, m_typeNames.methodList, "method_list_" + std::string(name), start, true); for (unsigned i = 0; i < head.count; i++) { @@ -806,18 +841,14 @@ void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::str // -- if (relativeOffsets) { - if (m_customRelativeMethodSelectorBase.has_value()) - { - meth.name = m_customRelativeMethodSelectorBase.value() + reader->ReadS32(); - meth.types = reader->GetOffset() + reader->ReadS32(); - meth.imp = reader->GetOffset() + reader->ReadS32(); - } - else - { - meth.name = reader->GetOffset() + reader->ReadS32(); - meth.types = reader->GetOffset() + reader->ReadS32(); - meth.imp = reader->GetOffset() + reader->ReadS32(); + auto selectorBaseOffset = reader->GetOffset(); + if (directSelectors && m_customRelativeMethodSelectorBase.has_value()) { + selectorBaseOffset = m_customRelativeMethodSelectorBase.value(); } + + meth.name = selectorBaseOffset + reader->Read32(); + meth.types = reader->GetOffset() + reader->ReadS32(); + meth.imp = reader->GetOffset() + reader->ReadS32(); } else { @@ -881,14 +912,14 @@ void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::str } } -void DSCObjCProcessor::ReadIvarList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start) +void DSCObjCProcessor::ReadIvarList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start) { reader->Seek(start); ivar_list_t head; head.entsizeAndFlags = reader->Read32(); head.count = reader->Read32(); auto addressSize = m_data->GetAddressSize(); - DefineObjCSymbol(DataSymbol, m_typeNames.ivarList, "ivar_list_" + name, start, true); + DefineObjCSymbol(DataSymbol, m_typeNames.ivarList, "ivar_list_" + std::string(name), start, true); if (head.count > 0x1000) { m_logger->LogError("Ivar list at 0x%llx has an invalid count of 0x%llx", start, head.count); @@ -1010,6 +1041,10 @@ void DSCObjCProcessor::GenerateClassTypes() bool DSCObjCProcessor::ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod) { + if (!method.imp || !m_data->IsValidOffset(method.imp)) { + return false; + } + std::stringstream r(method.name); std::string token; @@ -1221,6 +1256,19 @@ void DSCObjCProcessor::ProcessObjCData(std::shared_ptr<VM> vm, std::string baseN m_typeNames.nsuInteger = defineTypedef(m_data, {"NSUInteger"}, Type::IntegerType(addrSize, false)); m_typeNames.cgFloat = defineTypedef(m_data, {"CGFloat"}, Type::FloatType(addrSize)); + Ref<Type> relativeSelectorPtr; + auto reader = VMReader(vm); + if (auto objCRelativeMethodsBaseAddr = m_cache->GetObjCRelativeMethodBaseAddress(reader)) { + m_logger->LogDebug("RelativeMethodSelector Base: 0x%llx", objCRelativeMethodsBaseAddr); + m_customRelativeMethodSelectorBase = objCRelativeMethodsBaseAddr; + + auto type = TypeBuilder::PointerType(4, Type::PointerType(addrSize, Type::IntegerType(1, false))) + .SetPointerBase(RelativeToConstantPointerBaseType, objCRelativeMethodsBaseAddr) + .Finalize(); + auto relativeSelectorPtrName = defineTypedef(m_data, {"relative_SEL"}, type); + relativeSelectorPtr = Type::NamedType(m_data, relativeSelectorPtrName); + } + // https://github.com/apple-oss-distributions/objc4/blob/196363c165b175ed925ef6b9b99f558717923c47/runtime/objc-abi.h EnumerationBuilder imageInfoFlagBuilder; imageInfoFlagBuilder.AddMemberWithValue("IsReplacement", 1 << 0); @@ -1256,7 +1304,7 @@ void DSCObjCProcessor::ProcessObjCData(std::shared_ptr<VM> vm, std::string baseN m_typeNames.imageInfo = imageInfoType.first; StructureBuilder methodEntry; - methodEntry.AddMember(rptr_t, "name"); + methodEntry.AddMember(relativeSelectorPtr ? relativeSelectorPtr : rptr_t, "name"); methodEntry.AddMember(rptr_t, "types"); methodEntry.AddMember(rptr_t, "imp"); auto type = finalizeStructureBuilder(m_data, methodEntry, "objc_method_entry_t"); @@ -1360,42 +1408,6 @@ void DSCObjCProcessor::ProcessObjCData(std::shared_ptr<VM> vm, std::string baseN protocolBuilder.AddMember(Type::IntegerType(4, false), "flags"); m_typeNames.protocol = finalizeStructureBuilder(m_data, protocolBuilder, "objc_protocol_t").first; - auto reader = VMReader(vm); - - if (auto addr = m_cache->GetImageStart("/usr/lib/libobjc.A.dylib")) - { - auto header = m_cache->HeaderForAddress(addr.value()); - uint64_t scoffs_addr = 0; - size_t scoffs_size = 0; - - for (const auto& section : header->sections) - { - char name[17]; - memcpy(name, section.sectname, 16); - name[16] = 0; - if (std::string(name) == "__objc_scoffs") - { - scoffs_addr = section.addr; - scoffs_size = section.size; - break; - } - } - - if (scoffs_size && scoffs_addr) - { - if (scoffs_size == 0x20) - { - m_customRelativeMethodSelectorBase = reader.ReadULong(scoffs_addr); - } - else - { - m_customRelativeMethodSelectorBase = reader.ReadULong(scoffs_addr + 8); - } - m_logger->LogDebug("RelativeMethodSelector Base: 0x%llx", m_customRelativeMethodSelectorBase.value()); - } - } - - m_data->BeginBulkModifySymbols(); if (auto classList = m_data->GetSectionByName(baseName + "::__objc_classlist")) LoadClasses(&reader, classList); diff --git a/view/sharedcache/core/ObjC.h b/view/sharedcache/core/ObjC.h index 016ff022..0f4526c0 100644 --- a/view/sharedcache/core/ObjC.h +++ b/view/sharedcache/core/ObjC.h @@ -58,6 +58,10 @@ namespace DSCObjC { typedef struct { uint64_t count; } protocol_list_t; + struct relative_list_list_entry_t { + uint64_t imageIndex: 16; + int64_t listOffset: 48; + }; typedef struct { view_ptr_t isa; view_ptr_t mangledName; @@ -214,8 +218,9 @@ namespace DSCObjC { std::vector<QualifiedNameOrType> ParseEncodedType(const std::string& type); void DefineObjCSymbol(BNSymbolType symbolType, QualifiedName typeName, const std::string& name, uint64_t addr, bool deferred); void DefineObjCSymbol(BNSymbolType symbolType, Ref<Type> type, const std::string& name, uint64_t addr, bool deferred); - void ReadIvarList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start); - void ReadMethodList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start); + void ReadIvarList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start); + void ReadMethodList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start); + void ReadListOfMethodLists(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start); void LoadClasses(VMReader* reader, Ref<Section> listSection); void LoadCategories(VMReader* reader, Ref<Section> listSection); void LoadProtocols(VMReader* reader, Ref<Section> listSection); diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp index 0f61da63..b9150c31 100644 --- a/view/sharedcache/core/SharedCache.cpp +++ b/view/sharedcache/core/SharedCache.cpp @@ -75,6 +75,8 @@ struct SharedCache::State std::vector<MemoryRegion> dyldDataRegions; std::vector<MemoryRegion> nonImageRegions; + std::optional<std::pair<size_t, size_t>> objcOptimizationDataRange; + std::string baseFilePath; SharedCacheFormat cacheFormat; DSCViewState viewState = DSCViewStateUnloaded; @@ -311,6 +313,10 @@ void SharedCache::PerformInitialLoad() MutableState().cacheFormat = iOS16CacheFormat; } + if (primaryCacheHeader.objcOptsOffset && primaryCacheHeader.objcOptsSize) { + MutableState().objcOptimizationDataRange = {primaryCacheHeader.objcOptsOffset, primaryCacheHeader.objcOptsSize}; + } + switch (State().cacheFormat) { case RegularCacheFormat: @@ -3673,4 +3679,42 @@ const std::unordered_map<uint64_t, SharedCacheMachOHeader>& SharedCache::AllImag { return State().headers; } +size_t SharedCache::GetBaseAddress() const { + if (State().backingCaches.empty()) { + return 0; + } + + const BackingCache& primaryCache = State().backingCaches[0]; + if (!primaryCache.isPrimary) { + abort(); + return 0; + } + + if (primaryCache.mappings.empty()) { + return 0; + } + + return primaryCache.mappings[0].address; +} + +// Intentionally takes a copy to avoid modifying the cursor position in the original reader. +std::optional<ObjCOptimizationHeader> SharedCache::GetObjCOptimizationHeader(VMReader reader) const { + if (!State().objcOptimizationDataRange) { + return {}; + } + + ObjCOptimizationHeader header{}; + // Ignoring `objcOptsSize` in favor of `sizeof(ObjCOptimizationHeader)` matches dyld's behavior. + reader.Read(&header, GetBaseAddress() + State().objcOptimizationDataRange->first, sizeof(ObjCOptimizationHeader)); + + return header; +} + +size_t SharedCache::GetObjCRelativeMethodBaseAddress(const VMReader& reader) const { + if (auto header = GetObjCOptimizationHeader(reader); header.has_value()) { + return GetBaseAddress() + header->relativeMethodSelectorBaseAddressOffset; + } + return 0; +} + } // namespace SharedCacheCore diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h index a0d4e596..b5a0491f 100644 --- a/view/sharedcache/core/SharedCache.h +++ b/view/sharedcache/core/SharedCache.h @@ -262,74 +262,84 @@ namespace SharedCacheCore { struct PACKED_STRUCT dyld_cache_header { - char magic[16]; // e.g. "dyld_v0 i386" - uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info - uint32_t mappingCount; // number of dyld_cache_mapping_info entries - uint32_t imagesOffsetOld; // UNUSED: moved to imagesOffset to prevent older dsc_extarctors from crashing - uint32_t imagesCountOld; // UNUSED: moved to imagesCount to prevent older dsc_extarctors from crashing - uint64_t dyldBaseAddress; // base address of dyld when cache was built - uint64_t codeSignatureOffset; // file offset of code signature blob - uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file) - uint64_t slideInfoOffsetUnused; // unused. Used to be file offset of kernel slid info - uint64_t slideInfoSizeUnused; // unused. Used to be size of kernel slid info - uint64_t localSymbolsOffset; // file offset of where local symbols are stored - uint64_t localSymbolsSize; // size of local symbols information - uint8_t uuid[16]; // unique value for each shared cache file - uint64_t cacheType; // 0 for development, 1 for production // Kat: , 2 for iOS 16? - uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses - uint32_t branchPoolsCount; // number of uint64_t entries - uint64_t accelerateInfoAddr; // (unslid) address of optimization info - uint64_t accelerateInfoSize; // size of optimization info - uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info - uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries - uint64_t patchInfoAddr; // (unslid) address of dyld_cache_patch_info - uint64_t patchInfoSize; // Size of all of the patch information pointed to via the dyld_cache_patch_info - uint64_t otherImageGroupAddrUnused; // unused - uint64_t otherImageGroupSizeUnused; // unused - uint64_t progClosuresAddr; // (unslid) address of list of program launch closures - uint64_t progClosuresSize; // size of list of program launch closures - uint64_t progClosuresTrieAddr; // (unslid) address of trie of indexes into program launch closures - uint64_t progClosuresTrieSize; // size of trie of indexes into program launch closures - uint32_t platform; // platform number (macOS=1, etc) - uint32_t formatVersion : 8, // dyld3::closure::kFormatVersion - dylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid - simulator : 1, // for simulator of specified platform - locallyBuiltCache : 1, // 0 for B&I built cache, 1 for locally built cache - builtFromChainedFixups : 1, // some dylib in cache was built using chained fixups, so patch tables must be used for overrides - padding : 20; // TBD - uint64_t sharedRegionStart; // base load address of cache if not slid - uint64_t sharedRegionSize; // overall size required to map the cache and all subCaches, if any - uint64_t maxSlide; // runtime slide of cache can be between zero and this value - uint64_t dylibsImageArrayAddr; // (unslid) address of ImageArray for dylibs in this cache - uint64_t dylibsImageArraySize; // size of ImageArray for dylibs in this cache - uint64_t dylibsTrieAddr; // (unslid) address of trie of indexes of all cached dylibs - uint64_t dylibsTrieSize; // size of trie of cached dylib paths - uint64_t otherImageArrayAddr; // (unslid) address of ImageArray for dylibs and bundles with dlopen closures - uint64_t otherImageArraySize; // size of ImageArray for dylibs and bundles with dlopen closures - uint64_t otherTrieAddr; // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures - uint64_t otherTrieSize; // size of trie of dylibs and bundles with dlopen closures - uint32_t mappingWithSlideOffset; // file offset to first dyld_cache_mapping_and_slide_info - uint32_t mappingWithSlideCount; // number of dyld_cache_mapping_and_slide_info entries - uint64_t dylibsPBLStateArrayAddrUnused; // unused - uint64_t dylibsPBLSetAddr; // (unslid) address of PrebuiltLoaderSet of all cached dylibs - uint64_t programsPBLSetPoolAddr; // (unslid) address of pool of PrebuiltLoaderSet for each program - uint64_t programsPBLSetPoolSize; // size of pool of PrebuiltLoaderSet for each program - uint64_t programTrieAddr; // (unslid) address of trie mapping program path to PrebuiltLoaderSet - uint32_t programTrieSize; - uint32_t osVersion; // OS Version of dylibs in this cache for the main platform - uint32_t altPlatform; // e.g. iOSMac on macOS - uint32_t altOsVersion; // e.g. 14.0 for iOSMac - uint64_t swiftOptsOffset; // file offset to Swift optimizations header - uint64_t swiftOptsSize; // size of Swift optimizations header - uint32_t subCacheArrayOffset; // file offset to first dyld_subcache_entry - uint32_t subCacheArrayCount; // number of subCache entries - uint8_t symbolFileUUID[16]; // unique value for the shared cache file containing unmapped local symbols - uint64_t rosettaReadOnlyAddr; // (unslid) address of the start of where Rosetta can add read-only/executable data - uint64_t rosettaReadOnlySize; // maximum size of the Rosetta read-only/executable region - uint64_t rosettaReadWriteAddr; // (unslid) address of the start of where Rosetta can add read-write data - uint64_t rosettaReadWriteSize; // maximum size of the Rosetta read-write region - uint32_t imagesOffset; // file offset to first dyld_cache_image_info - uint32_t imagesCount; // number of dyld_cache_image_info entries + 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 @@ -345,6 +355,18 @@ namespace SharedCacheCore { char fileExtension[32]; }; + 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 @@ -592,6 +614,8 @@ namespace SharedCacheCore { explicit SharedCache(BinaryNinja::Ref<BinaryNinja::BinaryView> rawView); virtual ~SharedCache(); + size_t GetObjCRelativeMethodBaseAddress(const VMReader& reader) const; + private: std::optional<SharedCacheMachOHeader> LoadHeaderForAddress( std::shared_ptr<VM> vm, uint64_t address, std::string installName); @@ -604,6 +628,9 @@ private: Ref<TypeLibrary> TypeLibraryForImage(const std::string& installName); + size_t GetBaseAddress() const; + std::optional<ObjCOptimizationHeader> GetObjCOptimizationHeader(VMReader reader) const; + const State& State() const { return *m_state; } struct State& MutableState() { AssertMutable(); return *m_state; } diff --git a/view/sharedcache/workflow/SharedCacheWorkflow.cpp b/view/sharedcache/workflow/SharedCacheWorkflow.cpp index 61832e93..fa34e2f5 100644 --- a/view/sharedcache/workflow/SharedCacheWorkflow.cpp +++ b/view/sharedcache/workflow/SharedCacheWorkflow.cpp @@ -468,7 +468,7 @@ void fixObjCCallTypes(Ref<AnalysisContext> ctx) const auto selectorRegister = params[0].GetParameterExprs<LLIL_SEPARATE_PARAM_LIST_SSA>()[1].GetSourceSSARegister<LLIL_REG_SSA>(); rawSelector = ssa->GetSSARegisterValue(selectorRegister).value; } - if (rawSelector == 0) + if (!rawSelector || !bv->IsValidOffset(rawSelector)) return; // -- Do callsite override |
