From 25cc02431b61097b2adfc2fbc493b648b0300c3b Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Mon, 10 Mar 2025 11:05:40 -0400 Subject: [SharedCache] Refactor Shared Cache In absence of a better name, this commit refactors the shared cache code. --- view/sharedcache/core/SharedCacheView.cpp | 854 ++++++++++++++++++++++++++++++ 1 file changed, 854 insertions(+) create mode 100644 view/sharedcache/core/SharedCacheView.cpp (limited to 'view/sharedcache/core/SharedCacheView.cpp') diff --git a/view/sharedcache/core/SharedCacheView.cpp b/view/sharedcache/core/SharedCacheView.cpp new file mode 100644 index 00000000..bf80c6eb --- /dev/null +++ b/view/sharedcache/core/SharedCacheView.cpp @@ -0,0 +1,854 @@ +#include "SharedCacheView.h" +#include +#include "SharedCacheController.h" +#include "FileAccessorCache.h" +#include "MappedFileAccessor.h" + +using namespace BinaryNinja; +using namespace BinaryNinja::DSC; + +SharedCacheViewType::SharedCacheViewType() : BinaryViewType(VIEW_NAME, VIEW_NAME) {} + +// We register all our one-shot stuff here, such as the object destructor. +void SharedCacheViewType::Register() +{ + auto fdLimit = AdjustFileDescriptorLimit(); + LogDebug("Shared Cache processing initialized with a max file descriptor limit of %lld", fdLimit); + + // Adjust the global accessor cache to the fdlimit. + FileAccessorCache::Global().SetCacheSize(fdLimit); + + // TODO: Register object destructor to clear accessor cache + 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()) + { + LogWarn("Failed to initialize view of type '%s'. 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); + } + + Ref programSettings = Settings::Instance(); + programSettings->Set("analysis.workflows.functionWorkflow", "core.function.sharedCache", viewRef); + + 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." + })"); + + // 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) +{ + CreateLogger("SharedCache"); + CreateLogger("SharedCache.ObjC"); +} + +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, + DSCPlatformVisionOS = 11, // Apple Vision Pro + DSCPlatformVisionOSSimulator = 12 // Apple Vision Pro Simulator +}; + +bool SharedCacheView::Init() +{ + std::string os; + std::string arch; + + auto logger = new Logger("SharedCacheView", GetFile()->GetSessionId()); + + uint32_t platform; + GetParentView()->Read(&platform, 0xd8, 4); + char magic[17]; + GetParentView()->Read(&magic, 0, 16); + magic[16] = 0; + + // TODO: Do we want to add any warnings about platform support here? + // TODO: Do we still consider macos experimental? + switch (platform) + { + case DSCPlatformMacOS: + case DSCPlatformTVOS: + case DSCPlatformTVOSSimulator: + os = "mac"; + break; + case DSCPlatformiOS: + case DSCPlatformiOSSimulator: + case DSCPlatformVisionOS: + case DSCPlatformVisionOSSimulator: + os = "ios"; + break; + // armv7 or slide info v1 (unsupported) + case DSCPlatformWatchOS: + case DSCPlatformWatchOSSimulator: + case DSCPlatformBridgeOS: + default: + logger->LogError("Unknown platform: %d", platform); + return false; + } + + 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 + { + logger->LogError("Unknown magic: %s", magic); + return false; + } + + SetDefaultPlatform(Platform::GetByName(os + "-" + arch)); + SetDefaultArchitecture(Architecture::GetByName(arch)); + + Ref settings = GetLoadSettings(GetTypeName()); + + if (!settings) + { + Ref programSettings = Settings::Instance(); + programSettings->Set("analysis.workflows.functionWorkflow", "core.function.sharedCache", 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) + { + logger->LogError("Failed to parse header type: %s", err.c_str()); + 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) + { + logger->LogError("Failed to read base pointer"); + return false; + } + uint64_t primaryBase = 0; + GetParentView()->Read(&primaryBase, basePointer, 8); + if (primaryBase == 0) + { + logger->LogError("Failed to read primary base at 0x%llx", 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); + + auto sharedCache = SharedCache(GetAddressSize()); + + { + // Add up all the cache entries using the cache processor. + // After this we should have all the mappings available as well. + CacheProcessor cacheProcessor = CacheProcessor(this); + auto startTime = std::chrono::high_resolution_clock::now(); + if (!cacheProcessor.ProcessCache(sharedCache)) + { + // Oh no, we failed to process the cache, this likely means the primary on-disk file was not able to be + // found. + // TODO: Prompt the user to select the primary cache file? We can still recover from here. + logger->LogError("Failed to process cache, likely missing cache files."); + // NOTE: An interaction handler headlessly could select yes to this, however for the purposes of this we will just let them continue by defualt. + if (IsUIEnabled() && ShowMessageBox("Continue opening", "This shared cache file was unable to be processed, would you like to open without shared cache information?", YesNoButtonSet, QuestionIcon) != YesButton) + return false; + } + auto endTime = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed = endTime - startTime; + logger->LogInfo("Processing %zu entries took %.3f seconds", sharedCache.GetEntries().size(), elapsed.count()); + } + + { + // 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; + logger->LogInfo("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; + auto images = sharedCache.GetImages(); + logger->LogInfo("Processing %zu images took %.3f seconds", images.size(), elapsed.count()); + // Warn if we found no images, and provide the likely explanation + if (images.empty()) + logger->LogWarn("Failed to process any images, likely missing cache files."); + } + + { + // TODO: Run this up on a separate thread maybe and have callback notifications? + // 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; + logger->LogInfo("Processing %zu regions took %.3f seconds", sharedCache.GetRegions().size(), elapsed.count()); + } + + auto cacheController = SharedCacheController::Initialize(*this, std::move(sharedCache)); + + // 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); + logger->LogDebug("Loading images using pattern: %s", autoLoadPattern.c_str()); + + std::regex autoLoadRegex(autoLoadPattern); + for (const auto& [_, image] : cacheController->GetCache().GetImages()) + if (std::regex_match(image.GetName(), autoLoadRegex)) + cacheController->ApplyImage(*this, image); + + return true; +} -- cgit v1.3.1