From 7f3f394c8bb50c987a5237bc74aa503486571058 Mon Sep 17 00:00:00 2001 From: kat Date: Wed, 19 Mar 2025 09:12:52 -0400 Subject: Add iOS/macOS MH_FILESET KernelCache View and loader. This loader is inspired by/based on our dyld_shared_cache loader, following the same design language. It targets primarily the latest kernels, but should support any with the MH_FILESET format. It allows you to decide which images you would like to map in (the kernel itself included), resulting in targeted analysis when you may only need to load a singular image. It also supports dropping in compressed KernelCaches, directly from the ipsw. This is an early solution and we have many more changes and improvements planned. We look forward to your feedback --- view/kernelcache/core/KCView.cpp | 921 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 921 insertions(+) create mode 100644 view/kernelcache/core/KCView.cpp (limited to 'view/kernelcache/core/KCView.cpp') diff --git a/view/kernelcache/core/KCView.cpp b/view/kernelcache/core/KCView.cpp new file mode 100644 index 00000000..f166eafd --- /dev/null +++ b/view/kernelcache/core/KCView.cpp @@ -0,0 +1,921 @@ +// +// Created by kat on 5/23/23. +// + +/* + * + * */ + +#include "KCView.h" +#include "view/macho/machoview.h" +#include "KernelCache.h" + +[[maybe_unused]] KCViewType* g_kcViewType; + + +#define COMPRESSION_DEBUG 1 + +using namespace BinaryNinja; + + +KCView::KCView(const std::string& typeName, BinaryView* data, bool parseOnly) : + BinaryView(typeName, data->GetFile(), data), m_parseOnly(parseOnly) +{ + CreateLogger("KernelCache"); +} + +KCView::~KCView() +{ +} + +enum KCPlatform { + KCPlatformMacOS = 1, + KCPlatformiOS = 2, + KCPlatformTVOS = 3, + KCPlatformWatchOS = 4, + KCPlatformBridgeOS = 5, // T1/T2 APL1023/T8012, this is your touchbar/touchid in intel macs. Similar to watchOS. + // KCPlatformMacCatalyst = 6, + KCPlatformiOSSimulator = 7, + KCPlatformTVOSSimulator = 8, + KCPlatformWatchOSSimulator = 9, + KCPlatformVisionOS = 11, // Apple Vision Pro + KCPlatformVisionOSSimulator = 12 // Apple Vision Pro Simulator +}; + +bool KCView::Init() +{ + BinaryReader reader(GetParentView()); + reader.Seek(0x4); + uint32_t cpuType = reader.Read32(); + reader.Seek(0x10); + uint64_t ncmds = reader.Read32(); + uint64_t sizeofcmds = reader.Read32(); + + // get __TEXT seg offset + uint64_t textSegOffset = 0; + uint64_t anyFilesetHeaderFileOffset = 0; + uint64_t offset = 0x20; + for (uint64_t i = 0; i < ncmds; i++) + { + reader.Seek(offset); + uint64_t cmd = reader.Read32(); + uint64_t cmdsize = reader.Read32(); + if (textSegOffset == 0 && cmd == LC_SEGMENT_64) + { + uint64_t segname = reader.Read64(); + reader.Read64(); + uint64_t vmaddr = reader.Read64(); + if (segname == 0x545845545f5f) // __TEXT + { + textSegOffset = vmaddr; + } + } + + if (anyFilesetHeaderFileOffset == 0 && cmd == LC_FILESET_ENTRY) + { + reader.SeekRelative(8); + anyFilesetHeaderFileOffset = reader.Read64(); + } + + offset += cmdsize; + if (textSegOffset && anyFilesetHeaderFileOffset) + break; + } + + Ref platform; + Ref architecture; + + if (anyFilesetHeaderFileOffset) + { + reader.Seek(anyFilesetHeaderFileOffset); + reader.SeekRelative(0x10); + + uint64_t ncmdsFH = reader.Read32(); + + reader.Seek(anyFilesetHeaderFileOffset + 0x20); + offset = anyFilesetHeaderFileOffset + 0x20; + bool foundBuildVersion = false; + for (uint64_t i = 0; i < ncmdsFH; i++) + { + reader.Seek(offset); + uint64_t cmd = reader.Read32(); + uint64_t cmdsize = reader.Read32(); + if (cmd == LC_BUILD_VERSION) + { + uint32_t platformID = reader.Read32(); + std::map> metadataMap = { + {"machoplatform", new Metadata((uint64_t) platformID)}, + }; + Ref metadata = new Metadata(metadataMap); + Ref plat = g_kcViewType->RecognizePlatform(cpuType, GetDefaultEndianness(), this, metadata); + SetDefaultArchitecture(plat->GetArchitecture()); + SetDefaultPlatform(plat); + foundBuildVersion = true; + } + offset += cmdsize; + } + if (!foundBuildVersion) + { + LogError("Failed to find LC_BUILD_VERSION in subheader"); + SetDefaultArchitecture(Architecture::GetByName("aarch64")); + SetDefaultPlatform(Platform::GetByName("macos-kernel-aarch64")); + } + } + else + { + LogError("MH_FILESET had no subheaders"); + return false; + } + + if (textSegOffset == 0) + { + LogError("Failed to find __TEXT segment"); + 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("dysymtab"); + 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); + + StructureBuilder unixThreadCommandBuilder; + unixThreadCommandBuilder.AddMember(Type::NamedType(this, QualifiedName("load_command_type_t")), "cmd"); + unixThreadCommandBuilder.AddMember(Type::IntegerType(4, false), "cmdsize"); + unixThreadCommandBuilder.AddMember(Type::IntegerType(4, false), "flavor"); + unixThreadCommandBuilder.AddMember(Type::IntegerType(4, false), "count"); + // The 'state' field is intentionally ignored. + Ref unixThreadCommandStruct = unixThreadCommandBuilder.Finalize(); + QualifiedName unixThreadCommandName = std::string("unix_thread_command"); + std::string unixThreadCommandTypeId = Type::GenerateAutoTypeId("macho", unixThreadCommandName); + Ref unixThreadCommandType = Type::StructureType(unixThreadCommandStruct); + DefineType(unixThreadCommandTypeId, unixThreadCommandName, unixThreadCommandType); + + std::vector regionsMappedIntoMemory; + if (auto metadata = KernelCacheCore::KernelCacheMetadata::LoadFromView(GetParentView())) + { + for (const auto& image : metadata->LoadedImages()) + { + auto header = KernelCacheCore::KernelCache::LoadHeaderForAddress(this, image.headerFileLocation, image.installName); + if (!header) + { + LogError("Failed to load header for image %s", image.installName.c_str()); + return false; + } + if (!KernelCacheCore::KernelCache::InitializeSegmentsForHeader(this, *header, image)) + { + LogError("Failed to initialize segments for image %s", image.installName.c_str()); + return false; + } + KernelCacheCore::KernelCache::InitializeHeader(this, *header); + } + } + + // Technically the header is executable, but there shouldn't reasonably be code in the header. + AddAutoSegment(textSegOffset, sizeofcmds + 0x20, 0, sizeofcmds + 0x20, SegmentReadable); + AddAutoSection("kernelcache_header", textSegOffset, sizeofcmds + 0x20, ReadOnlyDataSectionSemantics); + + if (m_parseOnly) + return true; + + DefineDataVariable(textSegOffset, Type::NamedType(this, QualifiedName("mach_header_64"))); + DefineAutoSymbol( + new Symbol(DataSymbol, "kernelcache_header", textSegOffset, LocalBinding)); + + try + { + reader.Seek(sizeof(mach_header_64)); + size_t sectionNum = 0; + for (size_t i = 0; i < ncmds; i++) + { + load_command load; + uint64_t curOffset = reader.GetOffset(); + load.cmd = reader.Read32(); + load.cmdsize = reader.Read32(); + uint64_t nextOffset = curOffset + load.cmdsize; + switch (load.cmd) + { + case LC_SEGMENT: + { + DefineDataVariable(reader.GetOffset() + textSegOffset, Type::NamedType(this, QualifiedName("segment_command"))); + reader.SeekRelative(5 * 8); + size_t numSections = reader.Read32(); + reader.SeekRelative(4); + for (size_t j = 0; j < numSections; j++) + { + DefineDataVariable( + reader.GetOffset() + textSegOffset, Type::NamedType(this, QualifiedName("section"))); + DefineAutoSymbol(new Symbol(DataSymbol, + "__macho_section::kernelcache[" + std::to_string(sectionNum++) + "]", + reader.GetOffset() + textSegOffset, LocalBinding)); + reader.SeekRelative((8 * 8) + 4); + } + break; + } + case LC_SEGMENT_64: + { + DefineDataVariable(curOffset + textSegOffset, Type::NamedType(this, QualifiedName("segment_command_64"))); + reader.SeekRelative(7 * 8); + size_t numSections = reader.Read32(); + reader.SeekRelative(4); + for (size_t j = 0; j < numSections; j++) + { + DefineDataVariable( + reader.GetOffset() + textSegOffset, Type::NamedType(this, QualifiedName("section_64"))); + DefineAutoSymbol(new Symbol(DataSymbol, + "__macho_section_64::kernelcache[" + std::to_string(sectionNum++) + "]", + reader.GetOffset() + textSegOffset, LocalBinding)); + reader.SeekRelative(10 * 8); + } + break; + } + case LC_SYMTAB: + DefineDataVariable(curOffset + textSegOffset, Type::NamedType(this, QualifiedName("symtab"))); + break; + case LC_DYSYMTAB: + DefineDataVariable(curOffset + textSegOffset, Type::NamedType(this, QualifiedName("dysymtab"))); + break; + case LC_UUID: + DefineDataVariable(curOffset + textSegOffset, Type::NamedType(this, QualifiedName("uuid"))); + break; + case LC_ID_DYLIB: + case LC_LOAD_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + DefineDataVariable(curOffset + textSegOffset, Type::NamedType(this, QualifiedName("dylib_command"))); + if (load.cmdsize - 24 <= 150) + DefineDataVariable( + curOffset + textSegOffset + 24, Type::ArrayType(Type::IntegerType(1, true), load.cmdsize - 24)); + break; + case LC_CODE_SIGNATURE: + case LC_SEGMENT_SPLIT_INFO: + case LC_FUNCTION_STARTS: + case LC_DATA_IN_CODE: + case LC_DYLIB_CODE_SIGN_DRS: + case LC_DYLD_EXPORTS_TRIE: + case LC_DYLD_CHAINED_FIXUPS: + DefineDataVariable(curOffset + textSegOffset, Type::NamedType(this, QualifiedName("linkedit_data"))); + break; + case LC_ENCRYPTION_INFO: + DefineDataVariable(curOffset + textSegOffset, Type::NamedType(this, QualifiedName("encryption_info"))); + break; + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + DefineDataVariable(curOffset + textSegOffset, Type::NamedType(this, QualifiedName("version_min"))); + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + DefineDataVariable(curOffset + textSegOffset, Type::NamedType(this, QualifiedName("dyld_info"))); + break; + case LC_FILESET_ENTRY: + DefineDataVariable(curOffset + textSegOffset, Type::NamedType(this, QualifiedName("fileset_entry_command"))); + break; + case LC_UNIXTHREAD: + { + DefineDataVariable( + curOffset + textSegOffset, Type::NamedType(this, QualifiedName("unix_thread_command"))); + reader.SeekRelative(4); + uint32_t count = reader.Read32(); + DefineDataVariable(reader.GetOffset() + textSegOffset, Type::ArrayType(Type::IntegerType(8, true), count)); + break; + } + default: + DefineDataVariable(curOffset + textSegOffset, Type::NamedType(this, QualifiedName("load_command"))); + break; + } + + DefineAutoSymbol(new Symbol(DataSymbol, + "__macho_load_command::kernelcache[" + std::to_string(i) + "]", curOffset + textSegOffset, + LocalBinding)); + reader.Seek(nextOffset); + } + } + catch (ReadException&) + { + LogError("Error when applying Mach-O header types at %" PRIx64, textSegOffset); + } + + return true; +} + + +KCViewType::KCViewType() : BinaryViewType(KC_VIEW_NAME, KC_VIEW_NAME) +{ +} + +BinaryNinja::Ref KCViewType::Create(BinaryNinja::BinaryView* data) +{ + uint32_t magic; + data->Read(&magic, data->GetStart(), 4); + if (magic != MH_CIGAM_64 && magic != MH_MAGIC_64) // FIXME 32 bit + { + uint32_t im4pMagic; + data->Read(&im4pMagic, data->GetStart() + 0x8, 4); + if (im4pMagic == 0x50344d49) // P4MI + { + auto img4 = Transform::GetByName("IMG4-Unencrypted"); + + DataBuffer img4Payload; + img4->Decode(data->ReadBuffer(data->GetStart(), data->GetLength()), img4Payload); + + DataBuffer machOPayload; + uint32_t magic = ((uint32_t*)img4Payload.GetData())[0]; + if (magic == FAT_MAGIC_64 || magic == MH_MAGIC_64 || magic == MH_MAGIC + || magic == MH_CIGAM_64 || magic == MH_CIGAM ) + { + machOPayload = img4Payload; + } + else if (strncmp((char*)img4Payload.GetData(), "bvx2", 4) == 0) + { + auto lzfse = Transform::GetByName("LZFSE"); + if (lzfse) + lzfse->Decode(img4Payload, machOPayload); + } + else + { +#ifdef COMPRESSION_DEBUG + LogError("Unknown compression type in IMG4 Payload, writing img4 payload to RAW view for debug purposes."); + LogError("KernelCache parsing will now fail to proceed."); + data->WriteBuffer(0, img4Payload); + return new KCView(KC_VIEW_NAME, data, false); +#else + LogError("Unknown compression type in IMG4 Payload, unable to proceed."); + LogError("You can manually extract the kernelcache using `kerneldec`,`ipsw`, or other tools.") + return nullptr; +#endif + } + + if (machOPayload.GetLength() == 0) + { +#ifdef COMPRESSION_DEBUG + LogError("Failed to perform extraction on IMG4 Payload, writing img4 payload to RAW view for debug purposes."); + LogError("KernelCache parsing will now fail to proceed."); + data->WriteBuffer(0, img4Payload); + return new KCView(KC_VIEW_NAME, data, false); +#else + return nullptr; +#endif + } + + uint32_t machoMagic = ((uint32_t*)machOPayload.GetData())[0]; + if (machoMagic == FAT_MAGIC_64) + { + DataBuffer output = machOPayload.GetSlice(0x1c, machOPayload.GetLength()-0x1c); + data->WriteBuffer(0, output); + } + else if (machoMagic == MH_MAGIC_64 || machoMagic == MH_MAGIC || machoMagic == MH_CIGAM_64 || machoMagic == MH_CIGAM) + { + data->WriteBuffer(0, machOPayload); + } + else + { +#ifdef COMPRESSION_DEBUG + LogError("Unknown Mach-O magic in IMG4 Payload, writing img4 payload to RAW view for debug purposes."); + LogError("KernelCache parsing will now fail to proceed."); + data->WriteBuffer(0, machOPayload); + return new KCView(KC_VIEW_NAME, data, false); +#else + return nullptr; +#endif + } + + return new KCView(KC_VIEW_NAME, data, false); + } + + return nullptr; + } + + return new KCView(KC_VIEW_NAME, data, false); +} + + +Ref KCViewType::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); + } + + // 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; +} + + +BinaryNinja::Ref KCViewType::Parse(BinaryNinja::BinaryView* data) +{ + uint32_t magic; + data->Read(&magic, data->GetStart(), 4); + if (magic != MH_CIGAM_64 && magic != MH_MAGIC_64) // FIXME 32 bit + { + uint32_t im4pMagic; + data->Read(&im4pMagic, data->GetStart() + 0x8, 4); + if (im4pMagic == 0x50344d49) // P4MI + { + auto img4 = Transform::GetByName("IMG4-Unencrypted"); + auto lzfse = Transform::GetByName("LZFSE"); + + DataBuffer img4Payload; + img4->Decode(data->ReadBuffer(data->GetStart(), data->GetLength()), img4Payload); + DataBuffer machOPayload; + lzfse->Decode(img4Payload, machOPayload); + + uint32_t magic = ((uint32_t*)machOPayload.GetData())[0]; + auto id = data->BeginUndoActions(); + if (magic == FAT_MAGIC_64) + { + DataBuffer output = machOPayload.GetSlice(0x1c, machOPayload.GetLength()-0x1c); + data->WriteBuffer(0, output); + } + else + { + data->WriteBuffer(0, machOPayload); + } + data->ForgetUndoActions(id); + return new KCView(KC_VIEW_NAME, data, true); + } + + return nullptr; + } + + return new KCView(KC_VIEW_NAME, data, true); +} + +bool KCViewType::IsTypeValidForData(BinaryNinja::BinaryView* data) +{ + if (!data) + return false; + + uint32_t magic; + data->Read(&magic, data->GetStart(), 4); + + if (magic != MH_CIGAM_64 && magic != MH_MAGIC_64) // FIXME 32 bit + { + uint32_t im4pMagic; + data->Read(&im4pMagic, data->GetStart() + 0x8, 4); + if (im4pMagic == 0x50344d49) // P4MI + { + auto img4 = Transform::GetByName("IMG4-Unencrypted"); + auto lzfse = Transform::GetByName("LZFSE"); + + DataBuffer img4Payload; + img4->Decode(data->ReadBuffer(data->GetStart(), data->GetLength()), img4Payload); + DataBuffer machOPayload; + lzfse->Decode(img4Payload, machOPayload); + + uint32_t magic = ((uint32_t*)machOPayload.GetData())[0]; + if (magic == FAT_MAGIC_64 || magic == MH_CIGAM_64 || magic == MH_MAGIC_64) + return true; + + return false; + } + + return false; + } + + uint32_t fileType; + data->Read(&fileType, data->GetStart() + 0xc, 4); + if (fileType != MH_FILESET) + { + return false; + } + + return true; +} + +extern "C" { + void InitKCViewType() + { + static KCViewType type; + BinaryViewType::Register(&type); + g_kcViewType = &type; + } +} + -- cgit v1.3.1