// // Created by kat on 5/19/23. // #include #include "KCView.h" #include "view/macho/machoview.h" #include "MetadataSerializable.hpp" #include "../api/kernelcachecore.h" #ifndef KERNELCACHE_KERNELCACHE_H #define KERNELCACHE_KERNELCACHE_H DECLARE_KERNELCACHE_API_OBJECT(BNKernelCache, KernelCache); namespace KernelCacheCore { enum KCViewState { KCViewStateUnloaded, KCViewStateLoaded, KCViewStateLoadedWithImages, }; const std::string KernelCacheMetadataTag = "KERNELCACHE-KernelCacheData"; struct MemoryRegion : public MetadataSerializable { enum class Type { Image, NonImage, }; std::string prettyName; uint64_t start; uint64_t size; uint64_t fileOffset; BNSegmentFlag flags; Type type; void Store(SerializationContext& context) const { MSS(prettyName); MSS(start); MSS(size); MSS(fileOffset); MSS_CAST(flags, uint64_t); MSS_CAST(type, uint8_t); } static MemoryRegion Load(DeserializationContext& context) { MemoryRegion region; region.MSL(prettyName); region.MSL(start); region.MSL(size); region.MSL(fileOffset); region.MSL_CAST(flags, uint64_t, BNSegmentFlag); region.MSL_CAST(type, uint8_t, Type); return region; } }; struct KernelCacheImage : public MetadataSerializable { std::string installName; uint64_t headerFileLocation; std::vector regions; void Store(SerializationContext& context) const; static KernelCacheImage Load(DeserializationContext& context); }; #if defined(__GNUC__) || defined(__clang__) #define PACKED_STRUCT __attribute__((packed)) #else #define PACKED_STRUCT #endif #if defined(_MSC_VER) #pragma pack(push, 1) #else #endif #if defined(_MSC_VER) #pragma pack(pop) #else #endif using namespace BinaryNinja; struct KernelCacheMachOHeader : public MetadataSerializable { uint64_t textBase = 0; uint64_t textBaseFileOffset = 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; std::vector moduleTermSections; 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 linkeditPresent = false; bool dysymPresent = false; bool dyldInfoPresent = false; bool exportTriePresent = false; bool chainedFixupsPresent = false; bool routinesPresent = false; bool functionStartsPresent = false; bool relocatable = false; void Store(SerializationContext& context) const { MSS(textBase); MSS(textBaseFileOffset); 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(moduleTermSections); 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(linkeditPresent); MSS(dysymPresent); MSS(dyldInfoPresent); MSS(exportTriePresent); MSS(chainedFixupsPresent); MSS(routinesPresent); MSS(functionStartsPresent); MSS(relocatable); } static KernelCacheMachOHeader Load(DeserializationContext& context) { KernelCacheMachOHeader header; header.MSL(textBase); header.MSL(textBaseFileOffset); 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(moduleTermSections); 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(linkeditPresent); 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 MetadataSerializable { IMPLEMENT_KERNELCACHE_API_OBJECT(BNKernelCache); std::atomic m_refs = 0; public: virtual void AddRef() { m_refs.fetch_add(1); } virtual void Release() { // undo actions will lock a file lock we hold and then wait for main thread // so we need to release the ref later. WorkerPriorityEnqueue([this]() { if (m_refs.fetch_sub(1) == 1) delete this; }); } virtual void AddAPIRef() { AddRef(); } virtual void ReleaseAPIRef() { Release(); } public: enum KernelCacheFormat { FilesetCacheFormat, PrelinkedCacheFormat, }; struct CacheInfo; struct ModifiedState; struct ViewSpecificState; void Store(SerializationContext& context) const; void Load(DeserializationContext& context); private: Ref m_logger; /* VIEW STATE BEGIN -- SERIALIZE ALL OF THIS AND STORE IT IN RAW VIEW */ // State that is initialized during `PerformInitialLoad` and does // not change thereafter. std::shared_ptr m_cacheInfo; // Protects member variables below. mutable std::mutex m_mutex; // State that has been modified since this instance was created // or last saved to the view-specific state. // To get an accurate view of the current state, both these modifications // and the view-specific state must be consulted. std::unique_ptr m_modifiedState; // Serialized once by PerformInitialLoad and available after m_viewState == Loaded bool m_metadataValid = false; /* API VIEW START */ BinaryNinja::Ref m_kcView; /* API VIEW END */ std::shared_ptr m_viewSpecificState; private: void PerformInitialLoad(std::lock_guard&); void DeserializeFromRawView(std::lock_guard&); public: static KernelCache* GetFromKCView(BinaryNinja::Ref kcView); static uint64_t FastGetImageCount(BinaryNinja::Ref kcView); bool SaveCacheInfoToKCView(std::lock_guard&); bool SaveModifiedStateToKCView(std::lock_guard&); std::optional GetImageStart(std::string installName); std::optional HeaderForVMAddress(uint64_t address); std::optional HeaderForFileAddress(uint64_t address); bool LoadImageWithInstallName(std::lock_guard& lock, std::string installName); bool LoadImageWithInstallName(std::string installName); bool LoadImageContainingAddress(std::lock_guard& lock, uint64_t address); bool LoadImageContainingAddress(uint64_t address); std::string NameForAddress(uint64_t address); std::string ImageNameForAddress(uint64_t address); std::vector GetAvailableImages(); std::vector GetLoadedImages(); bool IsImageLoaded(uint64_t address); std::vector>> LoadAllSymbolsAndWait(); const std::unordered_map& AllImageStarts() const; const std::unordered_map AllImageHeaders() const; std::string SerializedImageHeaderForVMAddress(uint64_t address); std::string SerializedImageHeaderForName(std::string name); KCViewState ViewState() const; explicit KernelCache(BinaryNinja::Ref rawView); virtual ~KernelCache(); static bool InitializeSegmentsForHeader(Ref view, const KernelCacheMachOHeader& header, const KernelCacheImage& targetImage); static std::optional LoadHeaderForAddress(Ref view, uint64_t address, std::string installName); static void InitializeHeader(Ref view, KernelCacheMachOHeader header); static void ReadExportNode(Ref view, std::vector>& symbolList, KernelCacheMachOHeader& header, DataBuffer& buffer, uint64_t textBase, const std::string& currentText, size_t cursor, uint32_t endGuard); static std::vector> ParseExportTrie(Ref view, KernelCacheMachOHeader header); static std::vector>> ParseSymbolTable(Ref view, KernelCacheMachOHeader header, bool defineSymbolsInView = true); }; class KernelCacheMetadata { public: static std::optional LoadFromView(BinaryView*); static bool ViewHasMetadata(BinaryView*); std::string InstallNameForImageBaseAddress(uint64_t baseAddress) const; std::vector LoadedImages(); ~KernelCacheMetadata(); KernelCacheMetadata(KernelCacheMetadata&&); KernelCacheMetadata& operator=(KernelCacheMetadata&&); private: KernelCacheMetadata(KernelCache::CacheInfo, KernelCache::ModifiedState); std::unique_ptr cacheInfo; std::unique_ptr state; friend struct KernelCache::ModifiedState; friend class KernelCache; static const std::string Tag; static const std::string CacheInfoTag; static const std::string ModifiedStateTagPrefix; static const std::string ModifiedStateCountTag; }; } void InitKernelcache(); #endif //KERNELCACHE_KERNELCACHE_H