#include "SharedCacheView.h" #include #include #include "SharedCacheController.h" #include "SharedCacheBuilder.h" using namespace BinaryNinja; using namespace BinaryNinja::DSC; static const char* VIEW_METADATA_KEY = "shared_cache_view"; SharedCacheViewType::SharedCacheViewType() : BinaryViewType(VIEW_NAME, VIEW_NAME) {} // We register all our one-shot stuff here, such as the object destructor. void SharedCacheViewType::Register() { RegisterSharedCacheControllerDestructor(); static SharedCacheViewType type; BinaryViewType::Register(&type); } Ref SharedCacheViewType::Create(BinaryView* data) { return new SharedCacheView(VIEW_NAME, data, false); } Ref SharedCacheViewType::GetLoadSettingsForData(BinaryView* data) { Ref viewRef = Parse(data); if (!viewRef || !viewRef->Init()) { LogWarnF("Failed to initialize view of type '{}'. Generating default load settings.", GetName().c_str()); viewRef = data; } Ref settings = GetDefaultLoadSettingsForData(viewRef); // specify default load settings that can be overridden std::vector overrides = {"loader.imageBase", "loader.platform"}; settings->UpdateProperty("loader.imageBase", "message", "Note: File indicates image is not relocatable."); for (const auto& override : overrides) { if (settings->Contains(override)) settings->UpdateProperty(override, "readOnly", false); } settings->RegisterSetting("loader.dsc.processCFStrings", R"({ "title" : "Process CFString Metadata", "type" : "boolean", "default" : true, "description" : "Processes CoreFoundation strings, applying string values from encoded metadata" })"); settings->RegisterSetting("loader.dsc.autoLoadPattern", R"({ "title" : "Image Auto-Load Regex Pattern", "type" : "string", "default" : ".*libsystem_c.dylib", "description" : "A regex pattern to auto load matching images at the end of view init, defaults to the system_c image only." })"); settings->RegisterSetting("loader.dsc.processObjC", R"({ "title" : "Process Objective-C Metadata", "type" : "boolean", "default" : true, "description" : "Processes Objective-C metadata, applying class and method names from encoded metadata" })"); settings->RegisterSetting("loader.dsc.regionFilter", R"({ "title" : "Region Regex Filter", "type" : "string", "default" : ".*LINKEDIT.*", "description" : "Regex filter for region names to skip loading, by default this filters out the link edit region which is not necessary to be applied to the view." })"); settings->RegisterSetting("loader.dsc.autoLoadObjCStubRequirements", R"({ "title" : "Auto-Load Objective-C Stub Requirements", "type" : "boolean", "default" : true, "description" : "Automatically loads segments required for inlining Objective-C stubs. Recommended you keep this on." })"); settings->RegisterSetting("loader.dsc.autoLoadStubsAndDyldData", R"({ "title" : "Auto-Load Stub Islands", "type" : "boolean", "default" : true, "description" : "Automatically loads stub and dylddata regions that contain just branches and pointers. These are required for resolving stub names, and performance impact is minimal. Recommended you keep this on." })"); settings->RegisterSetting("loader.dsc.processFunctionStarts", R"({ "title" : "Process Mach-O Function Starts Tables", "type" : "boolean", "default" : true, "description" : "Add function starts sourced from the Function Starts tables to the core for analysis." })"); // Place the synthetic sections well after the shared cache to ensure they do // not collide with any images that are later loaded from the shared cache. // We do not have easy access to the size of the shared cache's mapping here // so we use a large fixed offset. // TODO: This will have to be updated if we add support for 32-bit shared caches. const uint64_t syntheticSectionsOffset = 12ull * 1024 * 1024 * 1024; settings->UpdateProperty("loader.syntheticSectionBase", "default", viewRef->GetStart() + syntheticSectionsOffset); // Merge existing load settings if they exist. This allows for the selection of a specific object file from a Mach-O // Universal file. The 'Universal' BinaryViewType generates a schema with 'loader.universal.architectures'. This // schema contains an appropriate 'Mach-O' load schema for selecting a specific object file. The embedded schema // contains 'loader.macho.universalImageOffset'. Ref loadSettings = viewRef->GetLoadSettings(GetName()); if (loadSettings && !loadSettings->IsEmpty()) settings->DeserializeSchema(loadSettings->SerializeSchema()); return settings; } Ref SharedCacheViewType::Parse(BinaryView* data) { return new SharedCacheView(VIEW_NAME, data, true); } bool SharedCacheViewType::IsTypeValidForData(BinaryView* data) { if (!data) return false; DataBuffer sig = data->ReadBuffer(data->GetStart(), 4); if (sig.GetLength() != 4) return false; const char* magic = (char*)sig.GetData(); if (strncmp(magic, "dyld", 4) == 0) return true; return false; } SharedCacheView::SharedCacheView(const std::string& typeName, BinaryView* data, bool parseOnly) : BinaryView(typeName, data->GetFile(), data), m_parseOnly(parseOnly) { // By default, we will assume the primary file name from the original file path. // This is subject to be overridden via `LoadMetadata`. m_primaryFileName = BaseFileName(GetFile()->GetOriginalFilename()); // Load up the metadata from the parent view (because our metadata is hilariously not available during view init) if (const auto metadata = GetParentView()->QueryMetadata(VIEW_METADATA_KEY)) LoadMetadata(*metadata); } enum DSCPlatform { DSCPlatformMacOS = 1, DSCPlatformiOS = 2, DSCPlatformTVOS = 3, DSCPlatformWatchOS = 4, DSCPlatformBridgeOS = 5, // T1/T2 APL1023/T8012, this is your touchbar/touchid in intel macs. Similar to watchOS. // DSCPlatformMacCatalyst = 6, DSCPlatformiOSSimulator = 7, DSCPlatformTVOSSimulator = 8, DSCPlatformWatchOSSimulator = 9, DSCPlatformDriverKit = 10, DSCPlatformVisionOS = 11, // Apple Vision Pro DSCPlatformVisionOSSimulator = 12 // Apple Vision Pro Simulator }; bool SharedCacheView::Init() { std::string os; std::string arch; m_logger = new Logger("SharedCache.View", GetFile()->GetSessionId()); char magic[17]; GetParentView()->Read(&magic, 0, 16); magic[16] = 0; if (std::string(magic) == "dyld_v1 arm64" || std::string(magic) == "dyld_v1 arm64e" || std::string(magic) == "dyld_v1arm64_32") { arch = "aarch64"; } else if (std::string(magic) == "dyld_v1 x86_64" || std::string(magic) == "dyld_v1 x86_64h") { arch = "x86_64"; } else { m_logger->LogErrorF("Unknown magic: {:?}", magic); return false; } // Use the value of mappingOffset as an upper bound for the size of the // header to avoid misinterpreting bytes outside of the header. uint32_t mappingOffset; GetParentView()->Read(&mappingOffset, 0x10, 4); uint32_t platform; if (mappingOffset >= 0xd8 + 4) { GetParentView()->Read(&platform, 0xd8, 4); } else { m_logger->LogWarn("Old header without platform field: Defaulting to iOS"); platform = DSCPlatformiOS; } switch (platform) { case DSCPlatformMacOS: case DSCPlatformTVOS: case DSCPlatformTVOSSimulator: os = "mac"; break; case DSCPlatformiOS: case DSCPlatformiOSSimulator: case DSCPlatformVisionOS: case DSCPlatformVisionOSSimulator: os = "ios"; break; case DSCPlatformDriverKit: // TODO: The same platform value is used for both macOS and iOS DriverKit shared caches. // Until we have a way to differentiate them, default to macOS. os = "mac"; break; // armv7 or slide info v1 (unsupported) case DSCPlatformWatchOS: case DSCPlatformWatchOSSimulator: case DSCPlatformBridgeOS: m_logger->LogErrorF("Unsupported platform: {}", platform); return false; default: m_logger->LogWarnF("Unknown platform: {}", platform); return false; } SetDefaultPlatform(Platform::GetByName(os + "-" + arch)); SetDefaultArchitecture(Architecture::GetByName(arch)); Ref settings = GetLoadSettings(GetTypeName()); if (!settings) { Ref programSettings = Settings::Instance(); auto previousWorkflow = programSettings->Get("analysis.workflows.functionWorkflow", this); // Earlier versions of the shared cache plug-in used a named workflow rather than // modifying `core.function.metaAnalysis`. Update reference to those older workflow // names to `core.function.metaAnalysis`. if (previousWorkflow == "core.function.dsc" || previousWorkflow == "core.function.sharedCache") programSettings->Set("analysis.workflows.functionWorkflow", "core.function.metaAnalysis", this); } if (m_parseOnly) return true; QualifiedNameAndType headerType; std::string err; ParseTypeString( "\n" "\tstruct dyld_cache_header\n" "\t{\n" "\t\tchar magic[16];\t\t\t\t\t // e.g. \"dyld_v0 i386\"\n" "\t\tuint32_t mappingOffset;\t\t\t // file offset to first dyld_cache_mapping_info\n" "\t\tuint32_t mappingCount;\t\t\t // number of dyld_cache_mapping_info entries\n" "\t\tuint32_t imagesOffsetOld;\t\t // UNUSED: moved to imagesOffset to prevent older dsc_extarctors from " "crashing\n" "\t\tuint32_t imagesCountOld;\t\t // UNUSED: moved to imagesCount to prevent older dsc_extarctors from " "crashing\n" "\t\tuint64_t dyldBaseAddress;\t\t // base address of dyld when cache was built\n" "\t\tuint64_t codeSignatureOffset;\t // file offset of code signature blob\n" "\t\tuint64_t codeSignatureSize;\t\t // size of code signature blob (zero means to end of file)\n" "\t\tuint64_t slideInfoOffsetUnused;\t // unused. Used to be file offset of kernel slid info\n" "\t\tuint64_t slideInfoSizeUnused;\t // unused. Used to be size of kernel slid info\n" "\t\tuint64_t localSymbolsOffset;\t // file offset of where local symbols are stored\n" "\t\tuint64_t localSymbolsSize;\t\t // size of local symbols information\n" "\t\tuint8_t uuid[16];\t\t\t\t // unique value for each shared cache file\n" "\t\tuint64_t cacheType;\t\t\t\t // 0 for development, 1 for production // Kat: , 2 for iOS 16?\n" "\t\tuint32_t branchPoolsOffset;\t\t // file offset to table of uint64_t pool addresses\n" "\t\tuint32_t branchPoolsCount;\t\t // number of uint64_t entries\n" "\t\tuint64_t accelerateInfoAddr;\t // (unslid) address of optimization info\n" "\t\tuint64_t accelerateInfoSize;\t // size of optimization info\n" "\t\tuint64_t imagesTextOffset;\t\t // file offset to first dyld_cache_image_text_info\n" "\t\tuint64_t imagesTextCount;\t\t // number of dyld_cache_image_text_info entries\n" "\t\tuint64_t patchInfoAddr;\t\t\t // (unslid) address of dyld_cache_patch_info\n" "\t\tuint64_t patchInfoSize;\t // Size of all of the patch information pointed to via the " "dyld_cache_patch_info\n" "\t\tuint64_t otherImageGroupAddrUnused;\t // unused\n" "\t\tuint64_t otherImageGroupSizeUnused;\t // unused\n" "\t\tuint64_t progClosuresAddr;\t\t\t // (unslid) address of list of program launch closures\n" "\t\tuint64_t progClosuresSize;\t\t\t // size of list of program launch closures\n" "\t\tuint64_t progClosuresTrieAddr;\t\t // (unslid) address of trie of indexes into program launch closures\n" "\t\tuint64_t progClosuresTrieSize;\t\t // size of trie of indexes into program launch closures\n" "\t\tuint32_t platform;\t\t\t\t\t // platform number (macOS=1, etc)\n" "\t\tuint32_t formatVersion : 8,\t\t\t // dyld3::closure::kFormatVersion\n" "\t\t\tdylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to " "see if cache is valid\n" "\t\t\tsimulator : 1,\t\t\t // for simulator of specified platform\n" "\t\t\tlocallyBuiltCache : 1,\t // 0 for B&I built cache, 1 for locally built cache\n" "\t\t\tbuiltFromChainedFixups : 1,\t // some dylib in cache was built using chained fixups, so patch tables " "must be used for overrides\n" "\t\t\tpadding : 20;\t\t\t\t // TBD\n" "\t\tuint64_t sharedRegionStart;\t\t // base load address of cache if not slid\n" "\t\tuint64_t sharedRegionSize;\t\t // overall size required to map the cache and all subCaches, if any\n" "\t\tuint64_t maxSlide;\t\t\t\t // runtime slide of cache can be between zero and this value\n" "\t\tuint64_t dylibsImageArrayAddr;\t // (unslid) address of ImageArray for dylibs in this cache\n" "\t\tuint64_t dylibsImageArraySize;\t // size of ImageArray for dylibs in this cache\n" "\t\tuint64_t dylibsTrieAddr;\t\t // (unslid) address of trie of indexes of all cached dylibs\n" "\t\tuint64_t dylibsTrieSize;\t\t // size of trie of cached dylib paths\n" "\t\tuint64_t otherImageArrayAddr;\t // (unslid) address of ImageArray for dylibs and bundles with dlopen " "closures\n" "\t\tuint64_t otherImageArraySize;\t // size of ImageArray for dylibs and bundles with dlopen closures\n" "\t\tuint64_t otherTrieAddr;\t // (unslid) address of trie of indexes of all dylibs and bundles with dlopen " "closures\n" "\t\tuint64_t otherTrieSize;\t // size of trie of dylibs and bundles with dlopen closures\n" "\t\tuint32_t mappingWithSlideOffset;\t\t // file offset to first dyld_cache_mapping_and_slide_info\n" "\t\tuint32_t mappingWithSlideCount;\t\t\t // number of dyld_cache_mapping_and_slide_info entries\n" "\t\tuint64_t dylibsPBLStateArrayAddrUnused;\t // unused\n" "\t\tuint64_t dylibsPBLSetAddr;\t\t\t\t // (unslid) address of PrebuiltLoaderSet of all cached dylibs\n" "\t\tuint64_t programsPBLSetPoolAddr;\t\t // (unslid) address of pool of PrebuiltLoaderSet for each program\n" "\t\tuint64_t programsPBLSetPoolSize;\t\t // size of pool of PrebuiltLoaderSet for each program\n" "\t\tuint64_t programTrieAddr;\t\t\t\t // (unslid) address of trie mapping program path to PrebuiltLoaderSet\n" "\t\tuint32_t programTrieSize;\n" "\t\tuint32_t osVersion;\t\t\t\t// OS Version of dylibs in this cache for the main platform\n" "\t\tuint32_t altPlatform;\t\t\t// e.g. iOSMac on macOS\n" "\t\tuint32_t altOsVersion;\t\t\t// e.g. 14.0 for iOSMac\n" "\t\tuint64_t swiftOptsOffset;\t\t// file offset to Swift optimizations header\n" "\t\tuint64_t swiftOptsSize;\t\t\t// size of Swift optimizations header\n" "\t\tuint32_t subCacheArrayOffset;\t// file offset to first dyld_subcache_entry\n" "\t\tuint32_t subCacheArrayCount;\t// number of subCache entries\n" "\t\tuint8_t symbolFileUUID[16];\t\t// unique value for the shared cache file containing unmapped local " "symbols\n" "\t\tuint64_t rosettaReadOnlyAddr;\t// (unslid) address of the start of where Rosetta can add " "read-only/executable data\n" "\t\tuint64_t rosettaReadOnlySize;\t// maximum size of the Rosetta read-only/executable region\n" "\t\tuint64_t rosettaReadWriteAddr;\t// (unslid) address of the start of where Rosetta can add read-write " "data\n" "\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); if (!err.empty() || !headerType.type) { m_logger->LogErrorF("Failed to parse header type: {}", err); return false; } // Add Mach-O file header type info EnumerationBuilder cpuTypeBuilder; cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ANY", MACHO_CPU_TYPE_ANY); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_VAX", MACHO_CPU_TYPE_VAX); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC680x0", MACHO_CPU_TYPE_MC680x0); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_X86", MACHO_CPU_TYPE_X86); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_X86_64", MACHO_CPU_TYPE_X86_64); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MIPS", MACHO_CPU_TYPE_MIPS); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC98000", MACHO_CPU_TYPE_MC98000); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_HPPA", MACHO_CPU_TYPE_HPPA); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM", MACHO_CPU_TYPE_ARM); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM64", MACHO_CPU_TYPE_ARM64); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ARM64_32", MACHO_CPU_TYPE_ARM64_32); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_MC88000", MACHO_CPU_TYPE_MC88000); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_SPARC", MACHO_CPU_TYPE_SPARC); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_I860", MACHO_CPU_TYPE_I860); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_ALPHA", MACHO_CPU_TYPE_ALPHA); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_POWERPC", MACHO_CPU_TYPE_POWERPC); cpuTypeBuilder.AddMemberWithValue("CPU_TYPE_POWERPC64", MACHO_CPU_TYPE_POWERPC64); Ref cpuTypeEnum = cpuTypeBuilder.Finalize(); Ref cpuTypeEnumType = Type::EnumerationType(nullptr, cpuTypeEnum, 4, false); std::string cpuTypeEnumName = "cpu_type_t"; std::string cpuTypeEnumId = Type::GenerateAutoTypeId("macho", cpuTypeEnumName); DefineType(cpuTypeEnumId, cpuTypeEnumName, cpuTypeEnumType); EnumerationBuilder fileTypeBuilder; fileTypeBuilder.AddMemberWithValue("MH_OBJECT", MH_OBJECT); fileTypeBuilder.AddMemberWithValue("MH_EXECUTE", MH_EXECUTE); fileTypeBuilder.AddMemberWithValue("MH_FVMLIB", MH_FVMLIB); fileTypeBuilder.AddMemberWithValue("MH_CORE", MH_CORE); fileTypeBuilder.AddMemberWithValue("MH_PRELOAD", MH_PRELOAD); fileTypeBuilder.AddMemberWithValue("MH_DYLIB", MH_DYLIB); fileTypeBuilder.AddMemberWithValue("MH_DYLINKER", MH_DYLINKER); fileTypeBuilder.AddMemberWithValue("MH_BUNDLE", MH_BUNDLE); fileTypeBuilder.AddMemberWithValue("MH_DYLIB_STUB", MH_DYLIB_STUB); fileTypeBuilder.AddMemberWithValue("MH_DSYM", MH_DSYM); fileTypeBuilder.AddMemberWithValue("MH_KEXT_BUNDLE", MH_KEXT_BUNDLE); fileTypeBuilder.AddMemberWithValue("MH_FILESET", MH_FILESET); Ref fileTypeEnum = fileTypeBuilder.Finalize(); Ref fileTypeEnumType = Type::EnumerationType(nullptr, fileTypeEnum, 4, false); std::string fileTypeEnumName = "file_type_t"; std::string fileTypeEnumId = Type::GenerateAutoTypeId("macho", fileTypeEnumName); DefineType(fileTypeEnumId, fileTypeEnumName, fileTypeEnumType); EnumerationBuilder flagsTypeBuilder; flagsTypeBuilder.AddMemberWithValue("MH_NOUNDEFS", MH_NOUNDEFS); flagsTypeBuilder.AddMemberWithValue("MH_INCRLINK", MH_INCRLINK); flagsTypeBuilder.AddMemberWithValue("MH_DYLDLINK", MH_DYLDLINK); flagsTypeBuilder.AddMemberWithValue("MH_BINDATLOAD", MH_BINDATLOAD); flagsTypeBuilder.AddMemberWithValue("MH_PREBOUND", MH_PREBOUND); flagsTypeBuilder.AddMemberWithValue("MH_SPLIT_SEGS", MH_SPLIT_SEGS); flagsTypeBuilder.AddMemberWithValue("MH_LAZY_INIT", MH_LAZY_INIT); flagsTypeBuilder.AddMemberWithValue("MH_TWOLEVEL", MH_TWOLEVEL); flagsTypeBuilder.AddMemberWithValue("MH_FORCE_FLAT", MH_FORCE_FLAT); flagsTypeBuilder.AddMemberWithValue("MH_NOMULTIDEFS", MH_NOMULTIDEFS); flagsTypeBuilder.AddMemberWithValue("MH_NOFIXPREBINDING", MH_NOFIXPREBINDING); flagsTypeBuilder.AddMemberWithValue("MH_PREBINDABLE", MH_PREBINDABLE); flagsTypeBuilder.AddMemberWithValue("MH_ALLMODSBOUND", MH_ALLMODSBOUND); flagsTypeBuilder.AddMemberWithValue("MH_SUBSECTIONS_VIA_SYMBOLS", MH_SUBSECTIONS_VIA_SYMBOLS); flagsTypeBuilder.AddMemberWithValue("MH_CANONICAL", MH_CANONICAL); flagsTypeBuilder.AddMemberWithValue("MH_WEAK_DEFINES", MH_WEAK_DEFINES); flagsTypeBuilder.AddMemberWithValue("MH_BINDS_TO_WEAK", MH_BINDS_TO_WEAK); flagsTypeBuilder.AddMemberWithValue("MH_ALLOW_STACK_EXECUTION", MH_ALLOW_STACK_EXECUTION); flagsTypeBuilder.AddMemberWithValue("MH_ROOT_SAFE", MH_ROOT_SAFE); flagsTypeBuilder.AddMemberWithValue("MH_SETUID_SAFE", MH_SETUID_SAFE); flagsTypeBuilder.AddMemberWithValue("MH_NO_REEXPORTED_DYLIBS", MH_NO_REEXPORTED_DYLIBS); flagsTypeBuilder.AddMemberWithValue("MH_PIE", MH_PIE); flagsTypeBuilder.AddMemberWithValue("MH_DEAD_STRIPPABLE_DYLIB", MH_DEAD_STRIPPABLE_DYLIB); flagsTypeBuilder.AddMemberWithValue("MH_HAS_TLV_DESCRIPTORS", MH_HAS_TLV_DESCRIPTORS); flagsTypeBuilder.AddMemberWithValue("MH_NO_HEAP_EXECUTION", MH_NO_HEAP_EXECUTION); flagsTypeBuilder.AddMemberWithValue("MH_APP_EXTENSION_SAFE", _MH_APP_EXTENSION_SAFE); flagsTypeBuilder.AddMemberWithValue("MH_NLIST_OUTOFSYNC_WITH_DYLDINFO", _MH_NLIST_OUTOFSYNC_WITH_DYLDINFO); flagsTypeBuilder.AddMemberWithValue("MH_SIM_SUPPORT", _MH_SIM_SUPPORT); flagsTypeBuilder.AddMemberWithValue("MH_DYLIB_IN_CACHE", _MH_DYLIB_IN_CACHE); Ref flagsTypeEnum = flagsTypeBuilder.Finalize(); Ref flagsTypeEnumType = Type::EnumerationType(nullptr, flagsTypeEnum, 4, false); std::string flagsTypeEnumName = "flags_type_t"; std::string flagsTypeEnumId = Type::GenerateAutoTypeId("macho", flagsTypeEnumName); DefineType(flagsTypeEnumId, flagsTypeEnumName, flagsTypeEnumType); StructureBuilder machoHeaderBuilder; machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "magic"); machoHeaderBuilder.AddMember(Type::NamedType(this, QualifiedName("cpu_type_t")), "cputype"); machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "cpusubtype"); machoHeaderBuilder.AddMember(Type::NamedType(this, QualifiedName("file_type_t")), "filetype"); machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "ncmds"); machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "sizeofcmds"); machoHeaderBuilder.AddMember(Type::NamedType(this, QualifiedName("flags_type_t")), "flags"); if (GetAddressSize() == 8) machoHeaderBuilder.AddMember(Type::IntegerType(4, false), "reserved"); Ref machoHeaderStruct = machoHeaderBuilder.Finalize(); QualifiedName headerName = (GetAddressSize() == 8) ? std::string("mach_header_64") : std::string("mach_header"); std::string headerTypeId = Type::GenerateAutoTypeId("macho", headerName); Ref machoHeaderType = Type::StructureType(machoHeaderStruct); DefineType(headerTypeId, headerName, machoHeaderType); EnumerationBuilder cmdTypeBuilder; cmdTypeBuilder.AddMemberWithValue("LC_REQ_DYLD", LC_REQ_DYLD); cmdTypeBuilder.AddMemberWithValue("LC_SEGMENT", LC_SEGMENT); cmdTypeBuilder.AddMemberWithValue("LC_SYMTAB", LC_SYMTAB); cmdTypeBuilder.AddMemberWithValue("LC_SYMSEG", LC_SYMSEG); cmdTypeBuilder.AddMemberWithValue("LC_THREAD", LC_THREAD); cmdTypeBuilder.AddMemberWithValue("LC_UNIXTHREAD", LC_UNIXTHREAD); cmdTypeBuilder.AddMemberWithValue("LC_LOADFVMLIB", LC_LOADFVMLIB); cmdTypeBuilder.AddMemberWithValue("LC_IDFVMLIB", LC_IDFVMLIB); cmdTypeBuilder.AddMemberWithValue("LC_IDENT", LC_IDENT); cmdTypeBuilder.AddMemberWithValue("LC_FVMFILE", LC_FVMFILE); cmdTypeBuilder.AddMemberWithValue("LC_PREPAGE", LC_PREPAGE); cmdTypeBuilder.AddMemberWithValue("LC_DYSYMTAB", LC_DYSYMTAB); cmdTypeBuilder.AddMemberWithValue("LC_LOAD_DYLIB", LC_LOAD_DYLIB); cmdTypeBuilder.AddMemberWithValue("LC_ID_DYLIB", LC_ID_DYLIB); cmdTypeBuilder.AddMemberWithValue("LC_LOAD_DYLINKER", LC_LOAD_DYLINKER); cmdTypeBuilder.AddMemberWithValue("LC_ID_DYLINKER", LC_ID_DYLINKER); cmdTypeBuilder.AddMemberWithValue("LC_PREBOUND_DYLIB", LC_PREBOUND_DYLIB); cmdTypeBuilder.AddMemberWithValue("LC_ROUTINES", LC_ROUTINES); cmdTypeBuilder.AddMemberWithValue("LC_SUB_FRAMEWORK", LC_SUB_FRAMEWORK); cmdTypeBuilder.AddMemberWithValue("LC_SUB_UMBRELLA", LC_SUB_UMBRELLA); cmdTypeBuilder.AddMemberWithValue("LC_SUB_CLIENT", LC_SUB_CLIENT); cmdTypeBuilder.AddMemberWithValue("LC_SUB_LIBRARY", LC_SUB_LIBRARY); cmdTypeBuilder.AddMemberWithValue("LC_TWOLEVEL_HINTS", LC_TWOLEVEL_HINTS); cmdTypeBuilder.AddMemberWithValue("LC_PREBIND_CKSUM", LC_PREBIND_CKSUM); cmdTypeBuilder.AddMemberWithValue("LC_LOAD_WEAK_DYLIB", LC_LOAD_WEAK_DYLIB); // (0x18 | LC_REQ_DYLD) cmdTypeBuilder.AddMemberWithValue("LC_SEGMENT_64", LC_SEGMENT_64); cmdTypeBuilder.AddMemberWithValue("LC_ROUTINES_64", LC_ROUTINES_64); cmdTypeBuilder.AddMemberWithValue("LC_UUID", LC_UUID); cmdTypeBuilder.AddMemberWithValue("LC_RPATH", LC_RPATH); // (0x1c | LC_REQ_DYLD) cmdTypeBuilder.AddMemberWithValue("LC_CODE_SIGNATURE", LC_CODE_SIGNATURE); cmdTypeBuilder.AddMemberWithValue("LC_SEGMENT_SPLIT_INFO", LC_SEGMENT_SPLIT_INFO); cmdTypeBuilder.AddMemberWithValue("LC_REEXPORT_DYLIB", LC_REEXPORT_DYLIB); // (0x1f | LC_REQ_DYLD) cmdTypeBuilder.AddMemberWithValue("LC_LAZY_LOAD_DYLIB", LC_LAZY_LOAD_DYLIB); cmdTypeBuilder.AddMemberWithValue("LC_ENCRYPTION_INFO", LC_ENCRYPTION_INFO); cmdTypeBuilder.AddMemberWithValue("LC_DYLD_INFO", LC_DYLD_INFO); cmdTypeBuilder.AddMemberWithValue("LC_DYLD_INFO_ONLY", LC_DYLD_INFO_ONLY); // (0x22 | LC_REQ_DYLD) cmdTypeBuilder.AddMemberWithValue("LC_LOAD_UPWARD_DYLIB", LC_LOAD_UPWARD_DYLIB); // (0x23 | LC_REQ_DYLD) cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_MACOSX", LC_VERSION_MIN_MACOSX); cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_IPHONEOS", LC_VERSION_MIN_IPHONEOS); cmdTypeBuilder.AddMemberWithValue("LC_FUNCTION_STARTS", LC_FUNCTION_STARTS); cmdTypeBuilder.AddMemberWithValue("LC_DYLD_ENVIRONMENT", LC_DYLD_ENVIRONMENT); cmdTypeBuilder.AddMemberWithValue("LC_MAIN", LC_MAIN); // (0x28 | LC_REQ_DYLD) cmdTypeBuilder.AddMemberWithValue("LC_DATA_IN_CODE", LC_DATA_IN_CODE); cmdTypeBuilder.AddMemberWithValue("LC_SOURCE_VERSION", LC_SOURCE_VERSION); cmdTypeBuilder.AddMemberWithValue("LC_DYLIB_CODE_SIGN_DRS", LC_DYLIB_CODE_SIGN_DRS); cmdTypeBuilder.AddMemberWithValue("LC_ENCRYPTION_INFO_64", _LC_ENCRYPTION_INFO_64); cmdTypeBuilder.AddMemberWithValue("LC_LINKER_OPTION", _LC_LINKER_OPTION); cmdTypeBuilder.AddMemberWithValue("LC_LINKER_OPTIMIZATION_HINT", _LC_LINKER_OPTIMIZATION_HINT); cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_TVOS", _LC_VERSION_MIN_TVOS); cmdTypeBuilder.AddMemberWithValue("LC_VERSION_MIN_WATCHOS", LC_VERSION_MIN_WATCHOS); cmdTypeBuilder.AddMemberWithValue("LC_NOTE", LC_NOTE); cmdTypeBuilder.AddMemberWithValue("LC_BUILD_VERSION", LC_BUILD_VERSION); cmdTypeBuilder.AddMemberWithValue("LC_DYLD_EXPORTS_TRIE", LC_DYLD_EXPORTS_TRIE); cmdTypeBuilder.AddMemberWithValue("LC_DYLD_CHAINED_FIXUPS", LC_DYLD_CHAINED_FIXUPS); cmdTypeBuilder.AddMemberWithValue("LC_FILESET_ENTRY", LC_FILESET_ENTRY); Ref cmdTypeEnum = cmdTypeBuilder.Finalize(); Ref cmdTypeEnumType = Type::EnumerationType(nullptr, cmdTypeEnum, 4, false); std::string cmdTypeEnumName = "load_command_type_t"; std::string cmdTypeEnumId = Type::GenerateAutoTypeId("macho", cmdTypeEnumName); DefineType(cmdTypeEnumId, cmdTypeEnumName, cmdTypeEnumType); StructureBuilder loadCommandBuilder; loadCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); loadCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); Ref loadCommandStruct = loadCommandBuilder.Finalize(); QualifiedName loadCommandName = std::string("load_command"); std::string loadCommandTypeId = Type::GenerateAutoTypeId("macho", loadCommandName); Ref loadCommandType = Type::StructureType(loadCommandStruct); DefineType(loadCommandTypeId, loadCommandName, loadCommandType); EnumerationBuilder protTypeBuilder; protTypeBuilder.AddMemberWithValue("VM_PROT_NONE", MACHO_VM_PROT_NONE); protTypeBuilder.AddMemberWithValue("VM_PROT_READ", MACHO_VM_PROT_READ); protTypeBuilder.AddMemberWithValue("VM_PROT_WRITE", MACHO_VM_PROT_WRITE); protTypeBuilder.AddMemberWithValue("VM_PROT_EXECUTE", MACHO_VM_PROT_EXECUTE); // protTypeBuilder.AddMemberWithValue("VM_PROT_DEFAULT", MACHO_VM_PROT_DEFAULT); // protTypeBuilder.AddMemberWithValue("VM_PROT_ALL", MACHO_VM_PROT_ALL); protTypeBuilder.AddMemberWithValue("VM_PROT_NO_CHANGE", MACHO_VM_PROT_NO_CHANGE); protTypeBuilder.AddMemberWithValue("VM_PROT_COPY_OR_WANTS_COPY", MACHO_VM_PROT_COPY); // protTypeBuilder.AddMemberWithValue("VM_PROT_WANTS_COPY", MACHO_VM_PROT_WANTS_COPY); Ref protTypeEnum = protTypeBuilder.Finalize(); Ref protTypeEnumType = Type::EnumerationType(nullptr, protTypeEnum, 4, false); std::string protTypeEnumName = "vm_prot_t"; std::string protTypeEnumId = Type::GenerateAutoTypeId("macho", protTypeEnumName); DefineType(protTypeEnumId, protTypeEnumName, protTypeEnumType); EnumerationBuilder segFlagsTypeBuilder; segFlagsTypeBuilder.AddMemberWithValue("SG_HIGHVM", SG_HIGHVM); segFlagsTypeBuilder.AddMemberWithValue("SG_FVMLIB", SG_FVMLIB); segFlagsTypeBuilder.AddMemberWithValue("SG_NORELOC", SG_NORELOC); segFlagsTypeBuilder.AddMemberWithValue("SG_PROTECTED_VERSION_1", SG_PROTECTED_VERSION_1); Ref segFlagsTypeEnum = segFlagsTypeBuilder.Finalize(); Ref segFlagsTypeEnumType = Type::EnumerationType(nullptr, segFlagsTypeEnum, 4, false); std::string segFlagsTypeEnumName = "sg_flags_t"; std::string segFlagsTypeEnumId = Type::GenerateAutoTypeId("macho", segFlagsTypeEnumName); DefineType(segFlagsTypeEnumId, segFlagsTypeEnumName, segFlagsTypeEnumType); StructureBuilder loadSegmentCommandBuilder; loadSegmentCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); loadSegmentCommandBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "vmaddr"); loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "vmsize"); loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "fileoff"); loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "filesize"); loadSegmentCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("vm_prot_t")), "maxprot"); loadSegmentCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("vm_prot_t")), "initprot"); loadSegmentCommandBuilder.AddMember(Type::IntegerType(4, false), "nsects"); loadSegmentCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("sg_flags_t")), "flags"); Ref loadSegmentCommandStruct = loadSegmentCommandBuilder.Finalize(); QualifiedName loadSegmentCommandName = std::string("segment_command"); std::string loadSegmentCommandTypeId = Type::GenerateAutoTypeId("macho", loadSegmentCommandName); Ref loadSegmentCommandType = Type::StructureType(loadSegmentCommandStruct); DefineType(loadSegmentCommandTypeId, loadSegmentCommandName, loadSegmentCommandType); StructureBuilder loadSegmentCommand64Builder; loadSegmentCommand64Builder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); loadSegmentCommand64Builder.AddMember(Type::IntegerType(4, false), "cmdsize"); loadSegmentCommand64Builder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "vmaddr"); loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "vmsize"); loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "fileoff"); loadSegmentCommand64Builder.AddMember(Type::IntegerType(8, false), "filesize"); loadSegmentCommand64Builder.AddMember(Type::NamedType(this, QualifiedName("vm_prot_t")), "maxprot"); loadSegmentCommand64Builder.AddMember(Type::NamedType(this, QualifiedName("vm_prot_t")), "initprot"); loadSegmentCommand64Builder.AddMember(Type::IntegerType(4, false), "nsects"); loadSegmentCommand64Builder.AddMember(Type::NamedType(this, QualifiedName("sg_flags_t")), "flags"); Ref loadSegmentCommand64Struct = loadSegmentCommand64Builder.Finalize(); QualifiedName loadSegment64CommandName = std::string("segment_command_64"); std::string loadSegment64CommandTypeId = Type::GenerateAutoTypeId("macho", loadSegment64CommandName); Ref loadSegment64CommandType = Type::StructureType(loadSegmentCommand64Struct); DefineType(loadSegment64CommandTypeId, loadSegment64CommandName, loadSegment64CommandType); StructureBuilder sectionBuilder; sectionBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "sectname"); sectionBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); sectionBuilder.AddMember(Type::IntegerType(4, false), "addr"); sectionBuilder.AddMember(Type::IntegerType(4, false), "size"); sectionBuilder.AddMember(Type::IntegerType(4, false), "offset"); sectionBuilder.AddMember(Type::IntegerType(4, false), "align"); sectionBuilder.AddMember(Type::IntegerType(4, false), "reloff"); sectionBuilder.AddMember(Type::IntegerType(4, false), "nreloc"); sectionBuilder.AddMember(Type::IntegerType(4, false), "flags"); sectionBuilder.AddMember(Type::IntegerType(4, false), "reserved1"); sectionBuilder.AddMember(Type::IntegerType(4, false), "reserved2"); Ref sectionStruct = sectionBuilder.Finalize(); QualifiedName sectionName = std::string("section"); std::string sectionTypeId = Type::GenerateAutoTypeId("macho", sectionName); Ref sectionType = Type::StructureType(sectionStruct); DefineType(sectionTypeId, sectionName, sectionType); StructureBuilder section64Builder; section64Builder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "sectname"); section64Builder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 16), "segname"); section64Builder.AddMember(Type::IntegerType(8, false), "addr"); section64Builder.AddMember(Type::IntegerType(8, false), "size"); section64Builder.AddMember(Type::IntegerType(4, false), "offset"); section64Builder.AddMember(Type::IntegerType(4, false), "align"); section64Builder.AddMember(Type::IntegerType(4, false), "reloff"); section64Builder.AddMember(Type::IntegerType(4, false), "nreloc"); section64Builder.AddMember(Type::IntegerType(4, false), "flags"); section64Builder.AddMember(Type::IntegerType(4, false), "reserved1"); section64Builder.AddMember(Type::IntegerType(4, false), "reserved2"); section64Builder.AddMember(Type::IntegerType(4, false), "reserved3"); Ref section64Struct = section64Builder.Finalize(); QualifiedName section64Name = std::string("section_64"); std::string section64TypeId = Type::GenerateAutoTypeId("macho", section64Name); Ref section64Type = Type::StructureType(section64Struct); DefineType(section64TypeId, section64Name, section64Type); StructureBuilder symtabBuilder; symtabBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); symtabBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); symtabBuilder.AddMember(Type::IntegerType(4, false), "symoff"); symtabBuilder.AddMember(Type::IntegerType(4, false), "nsyms"); symtabBuilder.AddMember(Type::IntegerType(4, false), "stroff"); symtabBuilder.AddMember(Type::IntegerType(4, false), "strsize"); Ref symtabStruct = symtabBuilder.Finalize(); QualifiedName symtabName = std::string("symtab"); std::string symtabTypeId = Type::GenerateAutoTypeId("macho", symtabName); Ref symtabType = Type::StructureType(symtabStruct); DefineType(symtabTypeId, symtabName, symtabType); StructureBuilder dynsymtabBuilder; dynsymtabBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "ilocalsym"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nlocalsym"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "iextdefsym"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nextdefsym"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "iundefsym"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nundefsym"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "tocoff"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "ntoc"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "modtaboff"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nmodtab"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "extrefsymoff"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nextrefsyms"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "indirectsymoff"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nindirectsyms"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "extreloff"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nextrel"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "locreloff"); dynsymtabBuilder.AddMember(Type::IntegerType(4, false), "nlocrel"); Ref dynsymtabStruct = dynsymtabBuilder.Finalize(); QualifiedName dynsymtabName = std::string("dynsymtab"); std::string dynsymtabTypeId = Type::GenerateAutoTypeId("macho", dynsymtabName); Ref dynsymtabType = Type::StructureType(dynsymtabStruct); DefineType(dynsymtabTypeId, dynsymtabName, dynsymtabType); StructureBuilder uuidBuilder; uuidBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); uuidBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); uuidBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, false), 16), "uuid"); Ref uuidStruct = uuidBuilder.Finalize(); QualifiedName uuidName = std::string("uuid"); std::string uuidTypeId = Type::GenerateAutoTypeId("macho", uuidName); Ref uuidType = Type::StructureType(uuidStruct); DefineType(uuidTypeId, uuidName, uuidType); StructureBuilder linkeditDataBuilder; linkeditDataBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); linkeditDataBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); linkeditDataBuilder.AddMember(Type::IntegerType(4, false), "dataoff"); linkeditDataBuilder.AddMember(Type::IntegerType(4, false), "datasize"); Ref linkeditDataStruct = linkeditDataBuilder.Finalize(); QualifiedName linkeditDataName = std::string("linkedit_data"); std::string linkeditDataTypeId = Type::GenerateAutoTypeId("macho", linkeditDataName); Ref linkeditDataType = Type::StructureType(linkeditDataStruct); DefineType(linkeditDataTypeId, linkeditDataName, linkeditDataType); StructureBuilder encryptionInfoBuilder; encryptionInfoBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cryptoff"); encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cryptsize"); encryptionInfoBuilder.AddMember(Type::IntegerType(4, false), "cryptid"); Ref encryptionInfoStruct = encryptionInfoBuilder.Finalize(); QualifiedName encryptionInfoName = std::string("encryption_info"); std::string encryptionInfoTypeId = Type::GenerateAutoTypeId("macho", encryptionInfoName); Ref encryptionInfoType = Type::StructureType(encryptionInfoStruct); DefineType(encryptionInfoTypeId, encryptionInfoName, encryptionInfoType); StructureBuilder versionMinBuilder; versionMinBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); versionMinBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); versionMinBuilder.AddMember(Type::IntegerType(4, false), "version"); versionMinBuilder.AddMember(Type::IntegerType(4, false), "sdk"); Ref versionMinStruct = versionMinBuilder.Finalize(); QualifiedName versionMinName = std::string("version_min"); std::string versionMinTypeId = Type::GenerateAutoTypeId("macho", versionMinName); Ref versionMinType = Type::StructureType(versionMinStruct); DefineType(versionMinTypeId, versionMinName, versionMinType); StructureBuilder dyldInfoBuilder; dyldInfoBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "rebase_off"); dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "rebase_size"); dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "bind_off"); dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "bind_size"); dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "weak_bind_off"); dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "weak_bind_size"); dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "lazy_bind_off"); dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "lazy_bind_size"); dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "export_off"); dyldInfoBuilder.AddMember(Type::IntegerType(4, false), "export_size"); Ref dyldInfoStruct = dyldInfoBuilder.Finalize(); QualifiedName dyldInfoName = std::string("dyld_info"); std::string dyldInfoTypeId = Type::GenerateAutoTypeId("macho", dyldInfoName); Ref dyldInfoType = Type::StructureType(dyldInfoStruct); DefineType(dyldInfoTypeId, dyldInfoName, dyldInfoType); StructureBuilder dylibBuilder; dylibBuilder.AddMember(Type::IntegerType(4, false), "name"); dylibBuilder.AddMember(Type::IntegerType(4, false), "timestamp"); dylibBuilder.AddMember(Type::IntegerType(4, false), "current_version"); dylibBuilder.AddMember(Type::IntegerType(4, false), "compatibility_version"); Ref dylibStruct = dylibBuilder.Finalize(); QualifiedName dylibName = std::string("dylib"); std::string dylibTypeId = Type::GenerateAutoTypeId("macho", dylibName); Ref dylibType = Type::StructureType(dylibStruct); DefineType(dylibTypeId, dylibName, dylibType); StructureBuilder dylibCommandBuilder; dylibCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); dylibCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); dylibCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("dylib")), "dylib"); Ref dylibCommandStruct = dylibCommandBuilder.Finalize(); QualifiedName dylibCommandName = std::string("dylib_command"); std::string dylibCommandTypeId = Type::GenerateAutoTypeId("macho", dylibCommandName); Ref dylibCommandType = Type::StructureType(dylibCommandStruct); DefineType(dylibCommandTypeId, dylibCommandName, dylibCommandType); StructureBuilder filesetEntryCommandBuilder; filesetEntryCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); filesetEntryCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); filesetEntryCommandBuilder.AddMember(Type::IntegerType(8, false), "vmaddr"); filesetEntryCommandBuilder.AddMember(Type::IntegerType(8, false), "fileoff"); filesetEntryCommandBuilder.AddMember(Type::IntegerType(4, false), "entry_id"); filesetEntryCommandBuilder.AddMember(Type::IntegerType(4, false), "reserved"); Ref filesetEntryCommandStruct = filesetEntryCommandBuilder.Finalize(); QualifiedName filesetEntryCommandName = std::string("fileset_entry_command"); std::string filesetEntryCommandTypeId = Type::GenerateAutoTypeId("macho", filesetEntryCommandName); Ref filesetEntryCommandType = Type::StructureType(filesetEntryCommandStruct); DefineType(filesetEntryCommandTypeId, filesetEntryCommandName, filesetEntryCommandType); // uint32_t at 0x10 is offset to mapping table. // first mapping struct in that table is base of primary // first uint64_t in that struct is the base address of the primary // double gpv here because DSCRaw explicitly stops at the start of this mapping table uint64_t basePointer = 0; GetParentView()->Read(&basePointer, 16, 4); if (basePointer == 0) { m_logger->LogError("Failed to read base pointer"); return false; } uint64_t primaryBase = 0; GetParentView()->Read(&primaryBase, basePointer, 8); if (primaryBase == 0) { m_logger->LogErrorF("Failed to read primary base at {:#x}", basePointer); return false; } 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); if (GetFile()->GetFilename().empty()) { // We have initialized the view with no backing file. // We are going to forgo initialization of shared cache controller, as there is no way to populate entries. // This can occur when downloading shared cache files from a remote project. In the collaboration core it will // call view init to push metadata about the remote IIRC. LogInfo("No backing file, skipping shared cache controller initialization..."); return true; } return InitController(); } bool SharedCacheView::InitController() { auto primaryFilePath = GetPrimaryFilePath(); if (!primaryFilePath.has_value()) { m_logger->LogError("Failed to get primary file path!"); return false; } std::string primaryFileDir = std::filesystem::path(*primaryFilePath).parent_path().string(); // Get the primary project file from the current files project. // This is required to allow selecting a primary file in a different directory. Otherwise, we search the current database directory. Ref primaryProjectFile = nullptr; auto currentProjectFile = GetFile()->GetProjectFile(); if (currentProjectFile) primaryProjectFile = currentProjectFile->GetProject()->GetFileByPathOnDisk(*primaryFilePath); if (!IsSameFolderForFile(primaryProjectFile, currentProjectFile)) { // TODO: Remove this restriction using stored cache UUID's and a fast project file search. m_logger->LogWarn("Because the primary file is in a different project folder you will need to select it on every open, consider moving the database file into the same folder."); } // OK, we have the primary shared cache file, now let's add the entries. auto sharedCacheBuilder = SharedCacheBuilder(this); sharedCacheBuilder.SetPrimaryFileName(m_primaryFileName); // Add the primary file. If we fail log alert that the primary cache file is invalid. // We process the primary cache entry first as it might be consulted in the processing of later entries. if (!sharedCacheBuilder.AddFile(*primaryFilePath, m_primaryFileName, CacheEntryType::Primary)) { m_logger->LogAlertF("Failed to add primary cache file: '{}'", m_primaryFileName); return false; } { // After this we should have all the mappings available as well. auto startTime = std::chrono::high_resolution_clock::now(); if (primaryProjectFile) sharedCacheBuilder.AddProjectFolder(primaryProjectFile->GetFolder()); else sharedCacheBuilder.AddDirectory(primaryFileDir); auto totalEntries = sharedCacheBuilder.GetCache().GetEntries().size(); auto endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = endTime - startTime; m_logger->LogInfoF("Processing {} entries took {:.3f} seconds", totalEntries, elapsed.count()); // Verify that we are not missing any entries that were stored in the metadata. // If we are that means we should alert the user that a previously associated cache entry is missing. std::set missingCacheEntries = m_secondaryFileNames; std::optional expectedFileCount; for (const auto& entry : sharedCacheBuilder.GetCache().GetEntries()) { missingCacheEntries.erase(entry.GetFileName()); LogSecondaryFileName(entry.GetFileName()); // Set the number of sub-caches so we can verify it later. if (entry.GetType() == CacheEntryType::Primary) { const auto& entryHeader = entry.GetHeader(); if (expectedFileCount) m_logger->LogWarnF("Multiple primary cache files found for '{}'.", m_primaryFileName); // TODO: Some caches seem to have fewer sub caches than what we report having. expectedFileCount = 1 + entryHeader.subCacheArrayCount; } } for (const auto& missingFileName: missingCacheEntries) m_logger->LogErrorF("Secondary cache file '{}' is missing!", missingFileName); // Verify that we have the required amount of sub-caches, if not alert the user. if (!expectedFileCount) m_logger->LogWarnF("Did not find a primary cache file for '{}'! You are likely opening a secondary cache file instead of a primary one.", m_primaryFileName); else if (totalEntries < expectedFileCount) m_logger->LogWarnF("Insufficient cache files in dyld header ({}/{}), loading as partial shared cache...", totalEntries, *expectedFileCount); } auto sharedCache = sharedCacheBuilder.Finalize(); { // Write all the slide info pointers to the virtual memory. // This should be done before any other work begins, as the backing data will be altered by this process. auto startTime = std::chrono::high_resolution_clock::now(); for (const auto& entry : sharedCache.GetEntries()) sharedCache.ProcessEntrySlideInfo(entry); auto endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = endTime - startTime; m_logger->LogInfoF("Processing slide info took {:.3f} seconds", elapsed.count()); } { // Load up all images into the cache before adding extra regions. // Currently, it is expected that a primary entry contain all relevant images, so we only check that one. auto startTime = std::chrono::high_resolution_clock::now(); for (const auto& entry : sharedCache.GetEntries()) if (entry.GetType() == CacheEntryType::Primary) sharedCache.ProcessEntryImages(entry); auto endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = endTime - startTime; const auto& images = sharedCache.GetImages(); m_logger->LogInfoF("Processing {} images took {:.3f} seconds", images.size(), elapsed.count()); // Warn if we found no images, and provide the likely explanation if (images.empty()) m_logger->LogWarn("Failed to process any images, likely missing cache files."); } { // Load up all the regions into the cache. auto startTime = std::chrono::high_resolution_clock::now(); for (const auto& entry : sharedCache.GetEntries()) sharedCache.ProcessEntryRegions(entry); auto endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = endTime - startTime; m_logger->LogInfoF("Processing {} regions took {:.3f} seconds", sharedCache.GetRegions().size(), elapsed.count()); } // TODO: Here we should have all the regions and what not for the virtual memory populated, I think it might be a good idea // TODO: To verify the regions are here and pointing at valid file accessor mappings, to make diagnosing issues quicker. auto cacheController = SharedCacheController::Initialize(*this, std::move(sharedCache)); { auto logger = m_logger; // Load up all the symbols into the named symbols lookup map. // NOTE: We do this on a separate thread as image & region loading does not consult this. WorkerPriorityEnqueue([logger, cacheController]() { auto& sharedCache = cacheController->GetCache(); auto startTime = std::chrono::high_resolution_clock::now(); sharedCache.ProcessSymbols(); auto endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = endTime - startTime; logger->LogInfoF("Processing {} symbols took {:.3f} seconds (separate thread)", sharedCache.GetSymbols().size(), elapsed.count()); }); } Ref settings = GetLoadSettings(GetTypeName()); // Users can adjust which images are loaded by default using the `loader.dsc.autoLoadPattern` setting. std::string autoLoadPattern = ".*libsystem_c.dylib"; if (settings && settings->Contains("loader.dsc.autoLoadPattern")) autoLoadPattern = settings->Get("loader.dsc.autoLoadPattern", this); m_logger->LogDebugF("Loading images using pattern: {:?}", autoLoadPattern); { // TODO: Refusing to add undo action "Added section libsystem_c.dylib::__macho_header", there is literally // TODO: no way around this warning as undo actions are implicit with user sections, for more detail see: // TODO: - https://github.com/Vector35/binaryninja-api/issues/6742 // TODO: - https://github.com/Vector35/binaryninja-api/issues/6289 // Load all images that match the `autoLoadPattern`. auto startTime = std::chrono::high_resolution_clock::now(); size_t loadedImages = 0; std::regex autoLoadRegex(autoLoadPattern); for (const auto& [_, image] : cacheController->GetCache().GetImages()) if (std::regex_match(image.GetName(), autoLoadRegex)) if (cacheController->ApplyImage(*this, image)) ++loadedImages; auto endTime = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = endTime - startTime; m_logger->LogInfoF("Automatically loading {} images took {:.3f} seconds", loadedImages, elapsed.count()); } return true; } void SharedCacheView::OnAfterSnapshotDataApplied() { if (auto controller = SharedCacheController::FromView(*this)) controller->ProcessObjCForLoadedImages(*this); } void SharedCacheView::SetPrimaryFileName(std::string primaryFileName) { m_primaryFileName = std::move(primaryFileName); GetParentView()->StoreMetadata(VIEW_METADATA_KEY, GetMetadata()); } void SharedCacheView::LogSecondaryFileName(std::string secondaryFileName) { m_secondaryFileNames.insert(std::move(secondaryFileName)); GetParentView()->StoreMetadata(VIEW_METADATA_KEY, GetMetadata()); } std::optional SharedCacheView::GetPrimaryFilePath() { auto viewFile = GetFile(); // 1. Try and get the primary file path using `GetOriginalFilename`. auto primaryFilePath = viewFile->GetOriginalFilename(); // 2. If the original file name is not a usable file path then prompt the user to select one. if (primaryFilePath.empty() || !std::filesystem::exists(primaryFilePath)) { if (!GetOpenFileNameInput(primaryFilePath, "Please select the primary shared cache file")) return std::nullopt; SetPrimaryFileName(BaseFileName(primaryFilePath)); // Update so next load we don't need to prompt the user. viewFile->SetOriginalFilename(primaryFilePath); } // 3. If we are not in a project, we can go ahead and return the file path, it does not need to be resolved from project. auto primaryProjectFile = viewFile->GetProjectFile(); if (!primaryProjectFile) return primaryFilePath; auto project = primaryProjectFile->GetProject(); auto primaryProjectFileName = primaryProjectFile->GetName(); auto primaryProjectFilePath = primaryProjectFile->GetPathOnDisk(); // 4. If we are not a BNDB project file than we can return the path on disk as we are the primary file. if (primaryProjectFileName.find(".bndb") == std::string::npos) { // Set the primary file name to the project file name so on subsequent loads we can pick it up. SetPrimaryFileName(primaryProjectFileName); return primaryProjectFilePath; } // 5. If we are a BNDB project file the path must be resolved from the file name. auto primaryProjectFileFolder = primaryProjectFile->GetFolder(); for (const auto& pj : project->GetFiles()) { // Skip files not in the same folder. if (!IsSameFolder(pj->GetFolder(), primaryProjectFileFolder)) continue; // We are looking for the file with file name we stored in metadata. if (pj->GetName() != m_primaryFileName) continue; return pj->GetPathOnDisk(); } // 6. If we fail to resolve the project file given the `m_primaryFileName` than we fall back to asking the user. std::string newPrimaryFilePath; if (!GetOpenFileNameInput(newPrimaryFilePath, "Please select the primary shared cache file")) return std::nullopt; // TODO: We likely want to verify that the project file exists in the same directory as the BNDB. // TODO: We currently require the database to exist in the same directory as the files. // Update the primary file name for later loads, otherwise we would keep prompting to select a file. primaryProjectFile = project->GetFileByPathOnDisk(newPrimaryFilePath); SetPrimaryFileName(primaryProjectFile->GetName()); return newPrimaryFilePath; } Ref SharedCacheView::GetMetadata() const { std::map> viewMeta; std::vector secondaryFileNames; secondaryFileNames.reserve(m_secondaryFileNames.size()); for (const auto& secondaryFileName : m_secondaryFileNames) secondaryFileNames.push_back(secondaryFileName); // TODO: Refactor this to just "cache files" which is a new struct of: // TODO: cache file name // TODO: cache file UUID // TODO: cache file entry type? viewMeta["secondaryFileNames"] = new Metadata(secondaryFileNames); viewMeta["primaryFileName"] = new Metadata(m_primaryFileName); return new Metadata(viewMeta); } void SharedCacheView::LoadMetadata(const Metadata &metadata) { auto viewMeta = metadata.GetKeyValueStore(); if (viewMeta.find("secondaryFileNames") != viewMeta.end()) { const auto secondaryFileNames = viewMeta["secondaryFileNames"]->GetStringList(); for (const auto& secondaryFileName : secondaryFileNames) m_secondaryFileNames.insert(secondaryFileName); } if (viewMeta.find("primaryFileName") != viewMeta.end()) m_primaryFileName = viewMeta["primaryFileName"]->GetString(); }