summaryrefslogtreecommitdiff
path: root/view/sharedcache
diff options
context:
space:
mode:
Diffstat (limited to 'view/sharedcache')
-rw-r--r--view/sharedcache/core/DSCView.cpp18
-rw-r--r--view/sharedcache/core/ObjC.cpp116
-rw-r--r--view/sharedcache/core/ObjC.h9
-rw-r--r--view/sharedcache/core/SharedCache.cpp44
-rw-r--r--view/sharedcache/core/SharedCache.h163
-rw-r--r--view/sharedcache/workflow/SharedCacheWorkflow.cpp2
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