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/api/kernelcacheapi.h | 272 ++++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 view/kernelcache/api/kernelcacheapi.h (limited to 'view/kernelcache/api/kernelcacheapi.h') diff --git a/view/kernelcache/api/kernelcacheapi.h b/view/kernelcache/api/kernelcacheapi.h new file mode 100644 index 00000000..149f39bf --- /dev/null +++ b/view/kernelcache/api/kernelcacheapi.h @@ -0,0 +1,272 @@ +#pragma once + +#include +#include "../core/MetadataSerializable.hpp" +#include "../api/view/macho/machoview.h" +#include "kernelcachecore.h" + +using namespace BinaryNinja; + +namespace KernelCacheAPI { + template + class KCRefCountObject { + void AddRefInternal() { m_refs.fetch_add(1); } + + void ReleaseInternal() { + if (m_refs.fetch_sub(1) == 1) + delete this; + } + + public: + std::atomic m_refs; + T *m_object; + + KCRefCountObject() : m_refs(0), m_object(nullptr) {} + + virtual ~KCRefCountObject() {} + + T *GetObject() const { return m_object; } + + static T *GetObject(KCRefCountObject *obj) { + if (!obj) + return nullptr; + return obj->GetObject(); + } + + void AddRef() { AddRefInternal(); } + + void Release() { ReleaseInternal(); } + + void AddRefForRegistration() { AddRefInternal(); } + }; + + + template + class KCCoreRefCountObject { + void AddRefInternal() { m_refs.fetch_add(1); } + + void ReleaseInternal() { + if (m_refs.fetch_sub(1) == 1) { + if (!m_registeredRef) + delete this; + } + } + + public: + std::atomic m_refs; + bool m_registeredRef = false; + T *m_object; + + KCCoreRefCountObject() : m_refs(0), m_object(nullptr) {} + + virtual ~KCCoreRefCountObject() {} + + T *GetObject() const { return m_object; } + + static T *GetObject(KCCoreRefCountObject *obj) { + if (!obj) + return nullptr; + return obj->GetObject(); + } + + void AddRef() { + if (m_object && (m_refs != 0)) + AddObjectReference(m_object); + AddRefInternal(); + } + + void Release() { + if (m_object) + FreeObjectReference(m_object); + ReleaseInternal(); + } + + void AddRefForRegistration() { m_registeredRef = true; } + + void ReleaseForRegistration() { + m_object = nullptr; + m_registeredRef = false; + if (m_refs == 0) + delete this; + } + }; + + struct KCMemoryRegion { + uint64_t vmAddress; + uint64_t size; + std::string prettyName; + }; + + struct BackingCacheMapping { + uint64_t vmAddress; + uint64_t size; + uint64_t fileOffset; + }; + + struct BackingCache { + std::string path; + bool isPrimary; + std::vector mappings; + }; + + struct KCImageMemoryMapping { + std::string name; + uint64_t vmAddress; + uint64_t size; + bool loaded; + uint64_t rawViewOffset; + }; + + struct KCImage { + std::string name; + uint64_t headerFileAddress; + std::vector mappings; + }; + + struct KCSymbol { + uint64_t address; + std::string name; + std::string image; + }; + + using namespace BinaryNinja; + struct KernelCacheMachOHeader : public KernelCacheCore::MetadataSerializable { + uint64_t textBase = 0; + uint64_t loadCommandOffset = 0; + mach_header_64 ident; + std::string identifierPrefix; + std::string installName; + + std::vector> entryPoints; + std::vector m_entryPoints; //list of entrypoints + + symtab_command symtab; + dysymtab_command dysymtab; + dyld_info_command dyldInfo; + routines_command_64 routines64; + function_starts_command functionStarts; + std::vector moduleInitSections; + linkedit_data_command exportTrie; + linkedit_data_command chainedFixups {}; + + uint64_t relocationBase; + // Section and program headers, internally use 64-bit form as it is a superset of 32-bit + std::vector segments; //only three types of sections __TEXT, __DATA, __IMPORT + segment_command_64 linkeditSegment; + std::vector sections; + std::vector sectionNames; + + std::vector symbolStubSections; + std::vector symbolPointerSections; + + std::vector dylibs; + + build_version_command buildVersion; + std::vector buildToolVersions; + + bool dysymPresent = false; + bool dyldInfoPresent = false; + bool exportTriePresent = false; + bool chainedFixupsPresent = false; + bool routinesPresent = false; + bool functionStartsPresent = false; + bool relocatable = false; + + void Store(KernelCacheCore::SerializationContext& context) const { + MSS(textBase); + MSS(loadCommandOffset); + MSS_SUBCLASS(ident); + MSS(identifierPrefix); + MSS(installName); + MSS(entryPoints); + MSS(m_entryPoints); + MSS_SUBCLASS(symtab); + MSS_SUBCLASS(dysymtab); + MSS_SUBCLASS(dyldInfo); + MSS_SUBCLASS(routines64); + MSS_SUBCLASS(functionStarts); + MSS_SUBCLASS(moduleInitSections); + MSS_SUBCLASS(exportTrie); + MSS_SUBCLASS(chainedFixups); + MSS(relocationBase); + MSS_SUBCLASS(segments); + MSS_SUBCLASS(linkeditSegment); + MSS_SUBCLASS(sections); + MSS(sectionNames); + MSS_SUBCLASS(symbolStubSections); + MSS_SUBCLASS(symbolPointerSections); + MSS(dylibs); + MSS_SUBCLASS(buildVersion); + MSS_SUBCLASS(buildToolVersions); + MSS(dysymPresent); + MSS(dyldInfoPresent); + MSS(exportTriePresent); + MSS(chainedFixupsPresent); + MSS(routinesPresent); + MSS(functionStartsPresent); + MSS(relocatable); + } + + static KernelCacheMachOHeader Load(KernelCacheCore::DeserializationContext& context) { + KernelCacheMachOHeader header; + header.MSL(textBase); + header.MSL(loadCommandOffset); + header.MSL(ident); + header.MSL(identifierPrefix); + header.MSL(installName); + header.MSL(entryPoints); + header.MSL(m_entryPoints); + header.MSL(symtab); + header.MSL(dysymtab); + header.MSL(dyldInfo); + header.MSL(routines64); + header.MSL(functionStarts); + header.MSL(moduleInitSections); + header.MSL(exportTrie); + header.MSL(chainedFixups); + header.MSL(relocationBase); + header.MSL(segments); + header.MSL(linkeditSegment); + header.MSL(sections); + header.MSL(sectionNames); + header.MSL(symbolStubSections); + header.MSL(symbolPointerSections); + header.MSL(dylibs); + header.MSL(buildVersion); + header.MSL(buildToolVersions); + header.MSL(dysymPresent); + header.MSL(dyldInfoPresent); + header.MSL(exportTriePresent); + header.MSL(chainedFixupsPresent); + header.MSL(routinesPresent); + header.MSL(functionStartsPresent); + header.MSL(relocatable); + return header; + } + }; + + + class KernelCache : public KCCoreRefCountObject { + public: + KernelCache(Ref view); + + BNKCViewState GetState(); + static BNKCViewLoadProgress GetLoadProgress(Ref view); + static uint64_t FastGetImageCount(Ref view); + + bool LoadImageWithInstallName(std::string installName); + bool LoadImageContainingAddress(uint64_t addr); + std::vector GetAvailableImages(); + + std::vector LoadAllSymbolsAndWait(); + + std::string GetNameForAddress(uint64_t address); + std::string GetImageNameForAddress(uint64_t address); + + std::vector GetImages(); + std::vector GetLoadedImages(); + + std::optional GetMachOHeaderForImage(std::string name); + std::optional GetMachOHeaderForAddress(uint64_t address); + }; +} \ No newline at end of file -- cgit v1.3.1