summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkat <kat@vector35.com>2025-07-06 15:05:01 -0400
committerkat <kat@vector35.com>2025-07-07 07:37:23 -0400
commit768f7c78465fb93936e5ca50a0ca712664fe54e7 (patch)
tree78c73e0022d6006882e365a257595a5b55a37432
parent8f3e251c42169fb4fe8db9403d900571434d88ff (diff)
KernelCache rewrite
-rw-r--r--python/scriptingprovider.py2
-rw-r--r--view/kernelcache/HeadlessPlugin.cpp9
-rw-r--r--view/kernelcache/api/kernelcache.cpp335
-rw-r--r--view/kernelcache/api/kernelcacheapi.h467
-rw-r--r--view/kernelcache/api/kernelcachecore.h149
-rw-r--r--view/kernelcache/api/python/kernelcache.py283
-rw-r--r--view/kernelcache/api/python/kernelcache_enums.py42
-rw-r--r--view/kernelcache/core/KCView.h48
-rw-r--r--view/kernelcache/core/KernelCache.cpp2520
-rw-r--r--view/kernelcache/core/KernelCache.h433
-rw-r--r--view/kernelcache/core/KernelCacheBuilder.cpp16
-rw-r--r--view/kernelcache/core/KernelCacheBuilder.h20
-rw-r--r--view/kernelcache/core/KernelCacheController.cpp198
-rw-r--r--view/kernelcache/core/KernelCacheController.h49
-rw-r--r--view/kernelcache/core/KernelCacheView.cpp (renamed from view/kernelcache/core/KCView.cpp)562
-rw-r--r--view/kernelcache/core/KernelCacheView.h63
-rw-r--r--view/kernelcache/core/MachO.cpp714
-rw-r--r--view/kernelcache/core/MachO.h79
-rw-r--r--view/kernelcache/core/MachOProcessor.cpp332
-rw-r--r--view/kernelcache/core/MachOProcessor.h22
-rw-r--r--view/kernelcache/core/MetadataSerializable.cpp552
-rw-r--r--view/kernelcache/core/MetadataSerializable.hpp254
-rw-r--r--view/kernelcache/core/Utility.cpp159
-rw-r--r--view/kernelcache/core/Utility.h106
-rw-r--r--view/kernelcache/core/ffi.cpp213
-rw-r--r--view/kernelcache/core/ffi_global.h41
-rw-r--r--view/kernelcache/core/refcountobject.h223
-rw-r--r--view/kernelcache/ui/KernelCacheUINotifications.cpp133
-rw-r--r--view/kernelcache/ui/kctriage.cpp258
-rw-r--r--view/kernelcache/ui/kctriage.h198
-rw-r--r--view/kernelcache/ui/symboltable.cpp216
-rw-r--r--view/kernelcache/ui/symboltable.h102
-rw-r--r--view/macho/machoview.h9
33 files changed, 4039 insertions, 4768 deletions
diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py
index e3ca09f6..3b7747bb 100644
--- a/python/scriptingprovider.py
+++ b/python/scriptingprovider.py
@@ -901,6 +901,8 @@ from binaryninja import *
settings = Settings()
if settings.contains('corePlugins.view.sharedCache') and settings.get_bool('corePlugins.view.sharedCache'):
from .sharedcache import SharedCacheController
+ if settings.contains('corePlugins.view.kernelCache') and settings.get_bool('corePlugins.view.kernelCache'):
+ from .kernelcache import KernelCacheController
if os.environ.get('BN_STANDALONE_DEBUGGER'):
# By the time this scriptingprovider.py file is imported, the user plugins are not loaded yet.
# So `from debugger import DebuggerController` would not work.
diff --git a/view/kernelcache/HeadlessPlugin.cpp b/view/kernelcache/HeadlessPlugin.cpp
index d14ccd6d..63f82d4d 100644
--- a/view/kernelcache/HeadlessPlugin.cpp
+++ b/view/kernelcache/HeadlessPlugin.cpp
@@ -1,11 +1,10 @@
#include <binaryninjaapi.h>
-#include "KCView.h"
-#include "KernelCache.h"
+#include "KernelCacheView.h"
+#include "transformers/KernelCacheTransforms.h"
#ifdef __cplusplus
extern "C" {
#endif
- // extern void RegisterSharedCacheWorkflow();
#ifdef __cplusplus
}
#endif
@@ -16,8 +15,8 @@ extern "C"
BINARYNINJAPLUGIN bool CorePluginInit()
{
- InitKernelcache();
- // RegisterSharedCacheWorkflow();
+ KernelCacheViewType::Register();
+ RegisterTransformers();
return true;
}
} \ No newline at end of file
diff --git a/view/kernelcache/api/kernelcache.cpp b/view/kernelcache/api/kernelcache.cpp
index 7a650660..30f40656 100644
--- a/view/kernelcache/api/kernelcache.cpp
+++ b/view/kernelcache/api/kernelcache.cpp
@@ -4,191 +4,192 @@
#include "kernelcacheapi.h"
-namespace KernelCacheAPI {
+using namespace BinaryNinja;
+using namespace KernelCacheAPI;
- KernelCache::KernelCache(Ref<BinaryView> view) {
- m_object = BNGetKernelCache(view->GetObject());
- }
+BNKernelCacheImage ImageToApi(CacheImage image)
+{
+ BNKernelCacheImage apiImage {};
+ apiImage.name = BNAllocString(image.name.c_str());
+ apiImage.headerFileAddress = image.headerFileAddress;
+ apiImage.headerVirtualAddress = image.headerVirtualAddress;
+ return apiImage;
+}
- BNKCViewLoadProgress KernelCache::GetLoadProgress(Ref<BinaryView> view)
- {
- return BNKCViewGetLoadProgress(view->GetFile()->GetSessionId());
- }
+CacheImage ImageFromApi(BNKernelCacheImage image)
+{
+ CacheImage apiImage {};
+ apiImage.name = image.name;
+ apiImage.headerVirtualAddress = image.headerVirtualAddress;
+ apiImage.headerFileAddress = image.headerFileAddress;
+ return apiImage;
+}
- uint64_t KernelCache::FastGetImageCount(Ref<BinaryView> view)
- {
- return BNKCViewFastGetImageCount(view->GetObject());
- }
+CacheSymbol SymbolFromApi(BNKernelCacheSymbol apiSymbol)
+{
+ CacheSymbol symbol;
+ symbol.name = apiSymbol.name;
+ symbol.address = apiSymbol.address;
+ symbol.type = apiSymbol.symbolType;
+ return symbol;
+}
- bool KernelCache::LoadImageWithInstallName(const std::string& installName)
- {
- return BNKCViewLoadImageWithInstallName(m_object, installName.c_str());
- }
+std::pair<std::string, Ref<Type>> CacheSymbol::DemangledName(BinaryView &view) const
+{
+ QualifiedName qname;
+ Ref<Type> outType = nullptr;
+ std::string shortName = name;
+ if (DemangleGeneric(view.GetDefaultArchitecture(), name, outType, qname, &view, true))
+ shortName = qname.GetString();
+ return {shortName, outType};
+}
- bool KernelCache::LoadImageContainingAddress(uint64_t addr)
- {
- return BNKCViewLoadImageContainingAddress(m_object, addr);
- }
+Ref<Symbol> CacheSymbol::GetBNSymbol(BinaryView &view) const
+{
+ auto [shortName, _] = DemangledName(view);
+ return new Symbol(type, shortName, shortName, name, address, nullptr);
+}
- std::vector<std::string> KernelCache::GetAvailableImages()
+std::string KernelCacheAPI::GetSymbolTypeAsString(const BNSymbolType &type)
+{
+ // NOTE: We currently only use the function and data symbol for cache symbols.
+ // update this if that changes.
+ switch (type)
{
- size_t count;
- char** value = BNKCViewGetInstallNames(m_object, &count);
- if (value == nullptr)
- {
- return {};
- }
-
- std::vector<std::string> result;
- for (size_t i = 0; i < count; i++)
- {
- result.push_back(value[i]);
- }
-
- BNFreeStringList(value, count);
- return result;
+ case FunctionSymbol:
+ return "Function";
+ case DataSymbol:
+ return "Data";
+ default:
+ return "Unknown";
}
+}
- bool KernelCache::IsImageLoaded(const uint64_t address) const
- {
- return BNKCViewIsImageLoaded(m_object, address);
- }
+KernelCacheController::KernelCacheController(BNKernelCacheController *controller)
+{
+ m_object = controller;
+}
- std::vector<KCImage> KernelCache::GetImages()
- {
- size_t count;
- BNKCImage* value = BNKCViewGetAllImages(m_object, &count);
- if (value == nullptr)
- {
- return {};
- }
+KCRef<KernelCacheController> KernelCacheController::GetController(BinaryView &view)
+{
+ BNKernelCacheController *controller = BNGetKernelCacheController(view.GetObject());
+ if (controller == nullptr)
+ return nullptr;
+ return new KernelCacheController(controller);
+}
- std::vector<KCImage> result;
- for (size_t i = 0; i < count; i++)
- {
- KCImage img;
- img.name = value[i].name;
- img.headerFileAddress = value[i].headerFileAddress;
- for (size_t j = 0; j < value[i].mappingCount; j++)
- {
- KCImageMemoryMapping mapping;
- mapping.name = value[i].mappings[j].name;
- mapping.vmAddress = value[i].mappings[j].vmAddress;
- mapping.rawViewOffset = value[i].mappings[j].rawViewOffset;
- mapping.size = value[i].mappings[j].size;
- mapping.loaded = value[i].mappings[j].loaded;
- img.mappings.push_back(mapping);
- }
- result.push_back(img);
- }
+bool KernelCacheController::ApplyImage(BinaryView &view, const CacheImage &image)
+{
+ auto apiImage = ImageToApi(image);
+ bool result = BNKernelCacheControllerApplyImage(m_object, view.GetObject(), &apiImage);
+ BNKernelCacheFreeImage(apiImage);
+ return result;
+}
- BNKCViewFreeAllImages(value, count);
- return result;
- }
+bool KernelCacheController::IsImageLoaded(const CacheImage &image) const
+{
+ auto apiImage = ImageToApi(image);
+ bool result = BNKernelCacheControllerIsImageLoaded(m_object, &apiImage);
+ BNKernelCacheFreeImage(apiImage);
+ return result;
+}
- std::vector<KCImage> KernelCache::GetLoadedImages()
- {
- size_t count;
- auto images = BNKCViewGetLoadedImages(m_object, &count);
- if (images == nullptr)
- {
- return {};
- }
- std::vector<KCImage> result;
- for (size_t i = 0; i < count; i++)
- {
- KCImage img;
- img.name = images[i].name;
- img.headerFileAddress = images[i].headerFileAddress;
- for (size_t j = 0; j < images[i].mappingCount; j++)
- {
- KCImageMemoryMapping mapping;
- mapping.name = images[i].mappings[j].name;
- mapping.vmAddress = images[i].mappings[j].vmAddress;
- mapping.rawViewOffset = images[i].mappings[j].rawViewOffset;
- mapping.size = images[i].mappings[j].size;
- mapping.loaded = images[i].mappings[j].loaded;
- img.mappings.push_back(mapping);
- }
- result.push_back(img);
- }
- BNKCViewFreeAllImages(images, count);
+std::optional<CacheImage> KernelCacheController::GetImageAt(uint64_t address) const
+{
+ BNKernelCacheImage apiImage;
+ if (!BNKernelCacheControllerGetImageAt(m_object, address, &apiImage))
+ return std::nullopt;
+ CacheImage image = ImageFromApi(apiImage);
+ BNKernelCacheFreeImage(apiImage);
+ return image;
+}
- return result;
- }
+std::optional<CacheImage> KernelCacheController::GetImageContaining(uint64_t address) const
+{
+ BNKernelCacheImage apiImage;
+ if (!BNKernelCacheControllerGetImageContaining(m_object, address, &apiImage))
+ return std::nullopt;
+ CacheImage image = ImageFromApi(apiImage);
+ BNKernelCacheFreeImage(apiImage);
+ return image;
+}
- std::vector<KCSymbol> KernelCache::LoadAllSymbolsAndWait()
- {
- size_t count;
- BNKCSymbolRep* value = BNKCViewLoadAllSymbolsAndWait(m_object, &count);
- if (value == nullptr)
- {
- return {};
- }
+std::optional<CacheImage> KernelCacheController::GetImageWithName(const std::string &name) const
+{
+ BNKernelCacheImage apiImage;
+ if (!BNKernelCacheControllerGetImageWithName(m_object, name.c_str(), &apiImage))
+ return std::nullopt;
+ CacheImage image = ImageFromApi(apiImage);
+ BNKernelCacheFreeImage(apiImage);
+ return image;
+}
- std::vector<KCSymbol> result;
- for (size_t i = 0; i < count; i++)
- {
- KCSymbol sym;
- sym.address = value[i].address;
- sym.name = value[i].name;
- sym.image = value[i].image;
- result.push_back(sym);
- }
+std::vector<std::string> KernelCacheController::GetImageDependencies(const CacheImage &image) const
+{
+ size_t count;
+ BNKernelCacheImage apiImage = ImageToApi(image);
+ char **dependencies = BNKernelCacheControllerGetImageDependencies(m_object, &apiImage, &count);
+ BNKernelCacheFreeImage(apiImage);
+ std::vector<std::string> result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.emplace_back(dependencies[i]);
+ BNFreeStringList(dependencies, count);
+ return result;
+}
- BNKCViewFreeSymbols(value, count);
- return result;
- }
+std::optional<CacheSymbol> KernelCacheController::GetSymbolAt(uint64_t address) const
+{
+ BNKernelCacheSymbol apiSymbol;
+ if (!BNKernelCacheControllerGetSymbolAt(m_object, address, &apiSymbol))
+ return std::nullopt;
+ CacheSymbol symbol = SymbolFromApi(apiSymbol);
+ BNKernelCacheFreeSymbol(apiSymbol);
+ return symbol;
+}
- std::string KernelCache::GetNameForAddress(uint64_t address)
- {
- char* name = BNKCViewGetNameForAddress(m_object, address);
- if (name == nullptr)
- return {};
- std::string result = name;
- BNFreeString(name);
- return result;
- }
+std::optional<CacheSymbol> KernelCacheController::GetSymbolWithName(const std::string &name) const
+{
+ BNKernelCacheSymbol apiSymbol;
+ if (!BNKernelCacheControllerGetSymbolWithName(m_object, name.c_str(), &apiSymbol))
+ return std::nullopt;
+ CacheSymbol symbol = SymbolFromApi(apiSymbol);
+ BNKernelCacheFreeSymbol(apiSymbol);
+ return symbol;
+}
- std::string KernelCache::GetImageNameForAddress(uint64_t address)
- {
- char* name = BNKCViewGetImageNameForAddress(m_object, address);
- if (name == nullptr)
- return {};
- std::string result = name;
- BNFreeString(name);
- return result;
- }
+std::vector<CacheImage> KernelCacheController::GetImages() const
+{
+ size_t count;
+ BNKernelCacheImage *images = BNKernelCacheControllerGetImages(m_object, &count);
+ std::vector<CacheImage> result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.emplace_back(ImageFromApi(images[i]));
+ BNKernelCacheFreeImageList(images, count);
+ return result;
+}
- std::optional<KernelCacheMachOHeader> KernelCache::GetMachOHeaderForImage(const std::string& name)
- {
- char* outputStr = BNKCViewGetImageHeaderForName(m_object, name.c_str());
- if (outputStr == nullptr)
- return {};
- std::string output = outputStr;
- BNFreeString(outputStr);
- if (output.empty())
- return {};
- KernelCacheMachOHeader header = KernelCacheMachOHeader::LoadFromString(output);
- return header;
- }
-
- std::optional<KernelCacheMachOHeader> KernelCache::GetMachOHeaderForAddress(uint64_t address)
- {
- char* outputStr = BNKCViewGetImageHeaderForAddress(m_object, address);
- if (outputStr == nullptr)
- return {};
- std::string output = outputStr;
- BNFreeString(outputStr);
- if (output.empty())
- return {};
- KernelCacheMachOHeader header = KernelCacheMachOHeader::LoadFromString(output);
- return header;
- }
-
- BNKCViewState KernelCache::GetState()
- {
- return BNKCViewGetState(m_object);
- }
+std::vector<CacheImage> KernelCacheController::GetLoadedImages() const
+{
+ size_t count;
+ BNKernelCacheImage *images = BNKernelCacheControllerGetLoadedImages(m_object, &count);
+ std::vector<CacheImage> result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.emplace_back(ImageFromApi(images[i]));
+ BNKernelCacheFreeImageList(images, count);
+ return result;
+}
-} // namespace KernelCacheAPI
+std::vector<CacheSymbol> KernelCacheController::GetSymbols() const
+{
+ size_t count;
+ BNKernelCacheSymbol *symbols = BNKernelCacheControllerGetSymbols(m_object, &count);
+ std::vector<CacheSymbol> result;
+ result.reserve(count);
+ for (size_t i = 0; i < count; i++)
+ result.emplace_back(SymbolFromApi(symbols[i]));
+ BNKernelCacheFreeSymbolList(symbols, count);
+ return result;
+}
diff --git a/view/kernelcache/api/kernelcacheapi.h b/view/kernelcache/api/kernelcacheapi.h
index 35c53d78..aceb30a5 100644
--- a/view/kernelcache/api/kernelcacheapi.h
+++ b/view/kernelcache/api/kernelcacheapi.h
@@ -1,274 +1,311 @@
#pragma once
#include <binaryninjaapi.h>
-#include "../core/MetadataSerializable.hpp"
-#include "../api/view/macho/machoview.h"
#include "kernelcachecore.h"
-using namespace BinaryNinja;
+template<class T>
+class KCRefCountObject {
+ void AddRefInternal() { m_refs.fetch_add(1); }
-namespace KernelCacheAPI {
- template<class T>
- class KCRefCountObject {
- void AddRefInternal() { m_refs.fetch_add(1); }
+ void ReleaseInternal() {
+ if (m_refs.fetch_sub(1) == 1)
+ delete this;
+ }
- void ReleaseInternal() {
- if (m_refs.fetch_sub(1) == 1)
- delete this;
- }
+public:
+ std::atomic<int> m_refs;
+ T *m_object;
- public:
- std::atomic<int> m_refs;
- T *m_object;
+ KCRefCountObject() : m_refs(0), m_object(nullptr) {}
+
+ virtual ~KCRefCountObject() = default;
+
+ T *GetObject() const { return m_object; }
+
+ static T *GetObject(KCRefCountObject *obj) {
+ if (!obj)
+ return nullptr;
+ return obj->GetObject();
+ }
- KCRefCountObject() : m_refs(0), m_object(nullptr) {}
+ void AddRef() { AddRefInternal(); }
- virtual ~KCRefCountObject() {}
+ void Release() { ReleaseInternal(); }
- T *GetObject() const { return m_object; }
+ void AddRefForRegistration() { AddRefInternal(); }
+};
- static T *GetObject(KCRefCountObject *obj) {
- if (!obj)
- return nullptr;
- return obj->GetObject();
+
+template<class T, T *(*AddObjectReference)(T *), void (*FreeObjectReference)(T *)>
+class KCCoreRefCountObject {
+ void AddRefInternal() { m_refs.fetch_add(1); }
+
+ void ReleaseInternal() {
+ if (m_refs.fetch_sub(1) == 1) {
+ if (!m_registeredRef)
+ delete this;
}
+ }
- void AddRef() { AddRefInternal(); }
+public:
+ std::atomic<int> m_refs;
+ bool m_registeredRef = false;
+ T *m_object;
- void Release() { ReleaseInternal(); }
+ KCCoreRefCountObject() : m_refs(0), m_object(nullptr) {}
- void AddRefForRegistration() { AddRefInternal(); }
- };
+ virtual ~KCCoreRefCountObject() = default;
+ T *GetObject() const { return m_object; }
- template<class T, T *(*AddObjectReference)(T *), void (*FreeObjectReference)(T *)>
- class KCCoreRefCountObject {
- void AddRefInternal() { m_refs.fetch_add(1); }
+ static T *GetObject(KCCoreRefCountObject *obj) {
+ if (!obj)
+ return nullptr;
+ return obj->GetObject();
+ }
- void ReleaseInternal() {
- if (m_refs.fetch_sub(1) == 1) {
- if (!m_registeredRef)
- delete this;
- }
- }
+ void AddRef() {
+ if (m_object && (m_refs != 0))
+ AddObjectReference(m_object);
+ AddRefInternal();
+ }
- public:
- std::atomic<int> m_refs;
- bool m_registeredRef = false;
- T *m_object;
+ void Release() {
+ if (m_object)
+ FreeObjectReference(m_object);
+ ReleaseInternal();
+ }
+
+ void AddRefForRegistration() { m_registeredRef = true; }
- KCCoreRefCountObject() : m_refs(0), m_object(nullptr) {}
+ void ReleaseForRegistration() {
+ m_object = nullptr;
+ m_registeredRef = false;
+ if (m_refs == 0)
+ delete this;
+ }
+};
- virtual ~KCCoreRefCountObject() {}
+template <class T>
+class KCRef
+{
+ T* m_obj;
+#ifdef BN_REF_COUNT_DEBUG
+ void* m_assignmentTrace = nullptr;
+#endif
- T *GetObject() const { return m_object; }
+public:
+ KCRef() : m_obj(NULL) {}
- static T *GetObject(KCCoreRefCountObject *obj) {
- if (!obj)
- return nullptr;
- return obj->GetObject();
+ KCRef(T* obj) : m_obj(obj)
+ {
+ if (m_obj)
+ {
+ m_obj->AddRef();
+#ifdef BN_REF_COUNT_DEBUG
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
}
+ }
- void AddRef() {
- if (m_object && (m_refs != 0))
- AddObjectReference(m_object);
- AddRefInternal();
+ KCRef(const KCRef<T>& obj) : m_obj(obj.m_obj)
+ {
+ if (m_obj)
+ {
+ m_obj->AddRef();
+#ifdef BN_REF_COUNT_DEBUG
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
}
+ }
- void Release() {
- if (m_object)
- FreeObjectReference(m_object);
- ReleaseInternal();
+ KCRef(KCRef<T>&& other) : m_obj(other.m_obj)
+ {
+ other.m_obj = 0;
+#ifdef BN_REF_COUNT_DEBUG
+ m_assignmentTrace = other.m_assignmentTrace;
+#endif
+ }
+
+ ~KCRef()
+ {
+ if (m_obj)
+ {
+ m_obj->Release();
+#ifdef BN_REF_COUNT_DEBUG
+ BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+#endif
}
+ }
- void AddRefForRegistration() { m_registeredRef = true; }
+ KCRef<T>& operator=(const BinaryNinja::Ref<T>& obj)
+ {
+#ifdef BN_REF_COUNT_DEBUG
+ if (m_obj)
+ BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+ if (obj.m_obj)
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
+ T* oldObj = m_obj;
+ m_obj = obj.m_obj;
+ if (m_obj)
+ m_obj->AddRef();
+ if (oldObj)
+ oldObj->Release();
+ return *this;
+ }
- void ReleaseForRegistration() {
- m_object = nullptr;
- m_registeredRef = false;
- if (m_refs == 0)
- delete this;
+ KCRef<T>& operator=(KCRef<T>&& other)
+ {
+ if (m_obj)
+ {
+#ifdef BN_REF_COUNT_DEBUG
+ BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+#endif
+ m_obj->Release();
}
- };
+ m_obj = other.m_obj;
+ other.m_obj = 0;
+#ifdef BN_REF_COUNT_DEBUG
+ m_assignmentTrace = other.m_assignmentTrace;
+#endif
+ return *this;
+ }
- struct KCMemoryRegion {
- uint64_t vmAddress;
- uint64_t size;
- std::string prettyName;
- };
+ KCRef<T>& operator=(T* obj)
+ {
+#ifdef BN_REF_COUNT_DEBUG
+ if (m_obj)
+ BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+ if (obj)
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
+ T* oldObj = m_obj;
+ m_obj = obj;
+ if (m_obj)
+ m_obj->AddRef();
+ if (oldObj)
+ oldObj->Release();
+ return *this;
+ }
- struct BackingCacheMapping {
- uint64_t vmAddress;
- uint64_t size;
- uint64_t fileOffset;
- };
+ operator T*() const
+ {
+ return m_obj;
+ }
- struct BackingCache {
- std::string path;
- bool isPrimary;
- std::vector<BackingCacheMapping> mappings;
- };
+ T* operator->() const
+ {
+ return m_obj;
+ }
- struct KCImageMemoryMapping {
- std::string name;
- uint64_t vmAddress;
- uint64_t size;
- bool loaded;
- uint64_t rawViewOffset;
- };
+ T& operator*() const
+ {
+ return *m_obj;
+ }
- struct KCImage {
- std::string name;
- uint64_t headerFileAddress;
- std::vector<KCImageMemoryMapping> mappings;
- };
+ bool operator!() const
+ {
+ return m_obj == NULL;
+ }
- struct KCSymbol {
- uint64_t address;
- std::string name;
- std::string image;
- };
+ bool operator==(const T* obj) const
+ {
+ return T::GetObject(m_obj) == T::GetObject(obj);
+ }
- using namespace BinaryNinja;
- struct KernelCacheMachOHeader : public KernelCacheCore::MetadataSerializable<KernelCacheMachOHeader> {
- uint64_t textBase = 0;
- uint64_t loadCommandOffset = 0;
- mach_header_64 ident;
- std::string identifierPrefix;
- std::string installName;
+ bool operator==(const KCRef<T>& obj) const
+ {
+ return T::GetObject(m_obj) == T::GetObject(obj.m_obj);
+ }
- std::vector<std::pair<uint64_t, bool>> entryPoints;
- std::vector<uint64_t> m_entryPoints; //list of entrypoints
+ bool operator!=(const T* obj) const
+ {
+ return T::GetObject(m_obj) != T::GetObject(obj);
+ }
- symtab_command symtab;
- dysymtab_command dysymtab;
- dyld_info_command dyldInfo;
- routines_command_64 routines64;
- function_starts_command functionStarts;
- std::vector<section_64> moduleInitSections;
- linkedit_data_command exportTrie;
- linkedit_data_command chainedFixups {};
+ bool operator!=(const KCRef<T>& obj) const
+ {
+ return T::GetObject(m_obj) != T::GetObject(obj.m_obj);
+ }
- uint64_t relocationBase;
- // Section and program headers, internally use 64-bit form as it is a superset of 32-bit
- std::vector<segment_command_64> segments; //only three types of sections __TEXT, __DATA, __IMPORT
- segment_command_64 linkeditSegment;
- std::vector<section_64> sections;
- std::vector<std::string> sectionNames;
+ bool operator<(const T* obj) const
+ {
+ return T::GetObject(m_obj) < T::GetObject(obj);
+ }
- std::vector<section_64> symbolStubSections;
- std::vector<section_64> symbolPointerSections;
+ bool operator<(const KCRef<T>& obj) const
+ {
+ return T::GetObject(m_obj) < T::GetObject(obj.m_obj);
+ }
- std::vector<std::string> dylibs;
+ T* GetPtr() const
+ {
+ return m_obj;
+ }
+};
- build_version_command buildVersion;
- std::vector<build_tool_version> 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);
- }
+namespace KernelCacheAPI {
+ struct CacheMappingInfo
+ {
+ uint64_t vmAddress;
+ uint64_t size;
+ uint64_t fileOffset;
+ };
- 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;
- }
+ struct CacheImage
+ {
+ uint64_t headerFileAddress;
+ uint64_t headerVirtualAddress;
+ std::string name;
};
+ struct CacheEntry
+ {
+ std::string path;
+ std::string name;
+ BNKernelCacheEntryType entryType;
+ std::vector<CacheMappingInfo> mappings;
+ };
- class KernelCache : public KCCoreRefCountObject<BNKernelCache, BNNewKernelCacheReference, BNFreeKernelCacheReference> {
- public:
- KernelCache(Ref<BinaryView> view);
+ struct CacheSymbol
+ {
+ BNSymbolType type;
+ uint64_t address;
+ std::string name;
+
+ std::pair<std::string, BinaryNinja::Ref<BinaryNinja::Type>> DemangledName(BinaryNinja::BinaryView &view) const;
+ BinaryNinja::Ref<BinaryNinja::Symbol> GetBNSymbol(BinaryNinja::BinaryView& view) const;
+ };
+
+ std::string GetSymbolTypeAsString(const BNSymbolType& type);
- BNKCViewState GetState();
- static BNKCViewLoadProgress GetLoadProgress(Ref<BinaryView> view);
- static uint64_t FastGetImageCount(Ref<BinaryView> view);
+ class KernelCacheController : public KCCoreRefCountObject<BNKernelCacheController, BNNewKernelCacheControllerReference, BNFreeKernelCacheControllerReference> {
+ public:
+ explicit KernelCacheController(BNKernelCacheController* controller);
+ static KCRef<KernelCacheController> GetController(BinaryNinja::BinaryView& view);
- bool LoadImageWithInstallName(const std::string& installName);
- bool LoadImageContainingAddress(uint64_t addr);
- std::vector<std::string> GetAvailableImages();
+ // Attempt to load the given image into the view.
+ //
+ // It is the callers responsibility to run linear sweep and update analysis, as you might want to add
+ // multiple images at a time.
+ bool ApplyImage(BinaryNinja::BinaryView& view, const CacheImage& image);
- bool IsImageLoaded(const uint64_t address) const;
+ bool IsImageLoaded(const CacheImage& image) const;
- std::vector<KCSymbol> LoadAllSymbolsAndWait();
+ std::optional<CacheImage> GetImageAt(uint64_t address) const;
+ std::optional<CacheImage> GetImageContaining(uint64_t address) const;
+ std::optional<CacheImage> GetImageWithName(const std::string& name) const;
- std::string GetNameForAddress(uint64_t address);
- std::string GetImageNameForAddress(uint64_t address);
+ std::vector<std::string> GetImageDependencies(const CacheImage& image) const;
- std::vector<KCImage> GetImages();
- std::vector<KCImage> GetLoadedImages();
+ std::optional<CacheSymbol> GetSymbolAt(uint64_t address) const;
+ std::optional<CacheSymbol> GetSymbolWithName(const std::string& name) const;
- std::optional<KernelCacheMachOHeader> GetMachOHeaderForImage(const std::string& name);
- std::optional<KernelCacheMachOHeader> GetMachOHeaderForAddress(uint64_t address);
+ std::vector<CacheImage> GetImages() const;
+ std::vector<CacheImage> GetLoadedImages() const;
+ std::vector<CacheSymbol> GetSymbols() const;
};
-} \ No newline at end of file
+}
diff --git a/view/kernelcache/api/kernelcachecore.h b/view/kernelcache/api/kernelcachecore.h
index 2f158131..02451a3e 100644
--- a/view/kernelcache/api/kernelcachecore.h
+++ b/view/kernelcache/api/kernelcachecore.h
@@ -28,6 +28,34 @@ extern "C"
#endif // _MSC_VER
#endif // __GNUC__C
+
+// binaryninjacore.h is not included so we must duplicate enum types here.
+#ifdef BN_TYPE_PARSER
+typedef enum BNSegmentFlag
+{
+ SegmentExecutable = 1,
+ SegmentWritable = 2,
+ SegmentReadable = 4,
+ SegmentContainsData = 8,
+ SegmentContainsCode = 0x10,
+ SegmentDenyWrite = 0x20,
+ SegmentDenyExecute = 0x40
+} BNSegmentFlag;
+
+typedef enum BNSymbolType
+{
+ FunctionSymbol = 0,
+ ImportAddressSymbol = 1,
+ ImportedFunctionSymbol = 2,
+ DataSymbol = 3,
+ ImportedDataSymbol = 4,
+ ExternalSymbol = 5,
+ LibraryFunctionSymbol = 6,
+ SymbolicFunctionSymbol = 7,
+ LocalLabelSymbol = 8,
+} BNSymbolType;
+#endif
+
#define CORE_ALLOCATED_STRUCT(T)
#define CORE_ALLOCATED_CLASS(T) \
@@ -35,101 +63,80 @@ extern "C"
CORE_ALLOCATED_STRUCT(T) \
private:
-#define DECLARE_KERNELCACHE_API_OBJECT_INTERNAL(handle, cls, ns) \
- namespace ns { class cls; } struct handle { ns::cls* object; }
-
-#define DECLARE_KERNELCACHE_API_OBJECT(handle, cls) DECLARE_KERNELCACHE_API_OBJECT_INTERNAL(handle, cls, KernelCacheCore)
-
-#define IMPLEMENT_KERNELCACHE_API_OBJECT(handle) \
- CORE_ALLOCATED_CLASS(handle) \
- private: \
- handle m_apiObject; \
- public: \
- typedef handle* APIHandle; \
- handle* GetAPIObject() { return &m_apiObject; } \
- private:
-#define INIT_KERNELCACHE_API_OBJECT() \
- m_apiObject.object = this;
+ typedef struct BNBinaryView BNBinaryView;
+ typedef struct BNKernelCacheController BNKernelCacheController;
- typedef enum BNKCViewState {
- Unloaded,
- Loaded,
- LoadedWithImages,
- } BNKCViewState;
+ typedef enum BNKernelCacheEntryType {
+ KernelCacheEntryTypePrimary,
+ KernelCacheEntryTypeSecondary,
+ KernelCacheEntryTypeSymbols,
+ KernelCacheEntryTypeDyldData,
+ KernelCacheEntryTypeStub,
+ } BNKernelCacheEntryType;
- typedef enum BNKCViewLoadProgress {
- LoadProgressNotStarted,
- LoadProgressLoadingCaches,
- LoadProgressLoadingImages,
- LoadProgressFinished,
- } BNKCViewLoadProgress;
+ typedef enum BNKernelCacheRegionType {
+ KernelCacheRegionTypeImage,
+ KernelCacheRegionTypeStubIsland,
+ KernelCacheRegionTypeDyldData,
+ KernelCacheRegionTypeNonImage,
+ } BNKernelCacheRegionType;
- typedef struct BNBinaryView BNBinaryView;
- typedef struct BNKernelCache BNKernelCache;
+ typedef struct BNKernelCacheImage {
+ char* name;
+ uint64_t headerVirtualAddress;
+ uint64_t headerFileAddress;
+ } BNKernelCacheImage;
- typedef struct BNKCImageMemoryMapping {
+ typedef struct BNKernelCacheRegion {
+ BNKernelCacheRegionType regionType;
char* name;
uint64_t vmAddress;
uint64_t size;
- bool loaded;
- uint64_t rawViewOffset;
- } BNKCImageMemoryMapping;
-
- typedef struct BNKCImage {
- char* name;
- uint64_t headerFileAddress;
- BNKCImageMemoryMapping* mappings;
- size_t mappingCount;
- } BNKCImage;
+ // NOTE: If not associated with an image this will be zero.
+ uint64_t imageStart;
+ BNSegmentFlag flags;
+ } BNKernelCacheRegion;
- typedef struct BNKCMappedMemoryRegion {
+ typedef struct BNKernelCacheMappingInfo {
uint64_t vmAddress;
uint64_t size;
- char* name;
- } BNKCMappedMemoryRegion;
+ uint64_t fileOffset;
+ } BNKernelCacheMappingInfo;
- typedef struct BNKCMemoryUsageInfo {
- uint64_t sharedCacheRefs;
- uint64_t mmapRefs;
- } BNKCMemoryUsageInfo;
-
- typedef struct BNKCSymbolRep {
+ typedef struct BNKernelCacheSymbol {
+ BNSymbolType symbolType;
uint64_t address;
char* name;
- char* image;
- } BNKCSymbolRep;
-
- KERNELCACHE_FFI_API BNKernelCache* BNGetKernelCache(BNBinaryView* data);
+ } BNKernelCacheSymbol;
- KERNELCACHE_FFI_API BNKernelCache* BNNewKernelCacheReference(BNKernelCache* cache);
- KERNELCACHE_FFI_API void BNFreeKernelCacheReference(BNKernelCache* cache);
+ KERNELCACHE_FFI_API BNKernelCacheController* BNGetKernelCacheController(BNBinaryView* data);
- KERNELCACHE_FFI_API char** BNKCViewGetInstallNames(BNKernelCache* cache, size_t* count);
+ KERNELCACHE_FFI_API BNKernelCacheController* BNNewKernelCacheControllerReference(BNKernelCacheController* controller);
+ KERNELCACHE_FFI_API void BNFreeKernelCacheControllerReference(BNKernelCacheController* controller);
- KERNELCACHE_FFI_API bool BNKCViewLoadImageWithInstallName(BNKernelCache* cache, const char* name);
- KERNELCACHE_FFI_API bool BNKCViewLoadImageContainingAddress(BNKernelCache* cache, uint64_t address);
+ KERNELCACHE_FFI_API bool BNKernelCacheControllerApplyImage(BNKernelCacheController* controller, BNBinaryView* view, BNKernelCacheImage* image);
- KERNELCACHE_FFI_API bool BNKCViewIsImageLoaded(BNKernelCache* cache, uint64_t address);
+ KERNELCACHE_FFI_API bool BNKernelCacheControllerIsImageLoaded(BNKernelCacheController* controller, BNKernelCacheImage* image);
- KERNELCACHE_FFI_API char* BNKCViewGetNameForAddress(BNKernelCache* cache, uint64_t address);
- KERNELCACHE_FFI_API char* BNKCViewGetImageNameForAddress(BNKernelCache* cache, uint64_t address);
+ KERNELCACHE_FFI_API bool BNKernelCacheControllerGetImageAt(BNKernelCacheController* controller, uint64_t address, BNKernelCacheImage* image);
+ KERNELCACHE_FFI_API bool BNKernelCacheControllerGetImageContaining(BNKernelCacheController* controller, uint64_t address, BNKernelCacheImage* image);
+ KERNELCACHE_FFI_API bool BNKernelCacheControllerGetImageWithName(BNKernelCacheController* controller, const char* name, BNKernelCacheImage* image);
- KERNELCACHE_FFI_API BNKCViewState BNKCViewGetState(BNKernelCache* cache);
- KERNELCACHE_FFI_API BNKCViewLoadProgress BNKCViewGetLoadProgress(uint64_t sessionID);
- KERNELCACHE_FFI_API uint64_t BNKCViewFastGetImageCount(BNBinaryView* view);
+ KERNELCACHE_FFI_API char** BNKernelCacheControllerGetImageDependencies(BNKernelCacheController* controller, BNKernelCacheImage* image, size_t* count);
- KERNELCACHE_FFI_API BNKCSymbolRep* BNKCViewLoadAllSymbolsAndWait(BNKernelCache* cache, size_t* count);
- KERNELCACHE_FFI_API void BNKCViewFreeSymbols(BNKCSymbolRep* symbols, size_t count);
+ KERNELCACHE_FFI_API BNKernelCacheImage* BNKernelCacheControllerGetImages(BNKernelCacheController* controller, size_t* count);
+ KERNELCACHE_FFI_API BNKernelCacheImage* BNKernelCacheControllerGetLoadedImages(BNKernelCacheController* controller, size_t* count);
- KERNELCACHE_FFI_API BNKCImage* BNKCViewGetAllImages(BNKernelCache* cache, size_t* count);
- KERNELCACHE_FFI_API void BNKCViewFreeAllImages(BNKCImage* images, size_t count);
+ KERNELCACHE_FFI_API void BNKernelCacheFreeImage(BNKernelCacheImage image);
+ KERNELCACHE_FFI_API void BNKernelCacheFreeImageList(BNKernelCacheImage* images, size_t count);
- KERNELCACHE_FFI_API BNKCImage* BNKCViewGetLoadedImages(BNKernelCache* cache, size_t* count);
- KERNELCACHE_FFI_API void BNKCViewFreeLoadedImages(BNKCImage* images, size_t count);
+ KERNELCACHE_FFI_API bool BNKernelCacheControllerGetSymbolAt(BNKernelCacheController* controller, uint64_t address, BNKernelCacheSymbol* symbol);
+ KERNELCACHE_FFI_API bool BNKernelCacheControllerGetSymbolWithName(BNKernelCacheController* controller, const char* name, BNKernelCacheSymbol* symbol);
- KERNELCACHE_FFI_API char* BNKCViewGetImageHeaderForAddress(BNKernelCache* cache, uint64_t address);
- KERNELCACHE_FFI_API char* BNKCViewGetImageHeaderForName(BNKernelCache* cache, const char* name);
+ KERNELCACHE_FFI_API BNKernelCacheSymbol* BNKernelCacheControllerGetSymbols(BNKernelCacheController* controller, size_t* count);
+ KERNELCACHE_FFI_API void BNKernelCacheFreeSymbol(BNKernelCacheSymbol symbol);
+ KERNELCACHE_FFI_API void BNKernelCacheFreeSymbolList(BNKernelCacheSymbol* symbols, size_t count);
#ifdef __cplusplus
}
#endif
diff --git a/view/kernelcache/api/python/kernelcache.py b/view/kernelcache/api/python/kernelcache.py
index 9425ef9c..25360213 100644
--- a/view/kernelcache/api/python/kernelcache.py
+++ b/view/kernelcache/api/python/kernelcache.py
@@ -1,229 +1,210 @@
-import os
import ctypes
import dataclasses
-import traceback
+from typing import Optional
import binaryninja
+from binaryninja import BinaryView
from binaryninja._binaryninjacore import BNFreeStringList, BNAllocString, BNFreeString
from . import _kernelcachecore as kccore
from .kernelcache_enums import *
-
@dataclasses.dataclass
-class KCImageMemoryMapping:
+class CacheImage:
name: str
- vmAddress: int
- rawViewOffset: int
- size: int
+ header_virtual_address: int
+ header_file_address: int
def __str__(self):
return repr(self)
def __repr__(self):
- return (f"<KCImageMemoryMapping '{self.name}' {self.vmAddress:x}+{self.size:x} "
- f"raw<{self.rawViewOffset:x}>>")
-
+ return f"<CacheImage '{self.name}': 0x{self.header_virtual_address:x} (@0x{self.header_file_address:x})>"
@dataclasses.dataclass
-class KCImage:
+class CacheSymbol:
+ symbol_type: kccore.SymbolTypeEnum
+ address: int
name: str
- headerFileAddress: int
- mappings: list[KCImageMemoryMapping]
def __str__(self):
return repr(self)
def __repr__(self):
- return f"<KCImage {self.name} @ {self.headerFileAddress:x}>"
+ return f"<CacheSymbol '{self.name}': 0x{self.address:x}>"
+def image_from_api(image: kccore.BNKernelCacheImage) -> CacheImage:
+ return CacheImage(
+ name=image.name,
+ header_file_address=image.headerFileAddress,
+ header_virtual_address=image.headerVirtualAddress
+ )
-@dataclasses.dataclass
-class KCSymbol:
- name: str
- image: str
- address: int
+def image_to_api(image: CacheImage) -> kccore.BNKernelCacheImage:
+ return kccore.BNKernelCacheImage(
+ _name=BNAllocString(image.name),
+ headerFileAddress=image.header_file_address,
+ headerVirtualAddress=image.header_virtual_address,
+ )
- def __str__(self):
- return repr(self)
+def symbol_from_api(symbol: kccore.BNKernelCacheSymbol) -> CacheSymbol:
+ return CacheSymbol(
+ symbol_type=symbol.symbolType,
+ address=symbol.address,
+ name=symbol.name
+ )
- def __repr__(self):
- return f"<KCSymbol {self.name} @ {self.address:x} ({self.image})>"
+def symbol_to_api(symbol: CacheSymbol) -> kccore.BNKernelCacheSymbol:
+ return kccore.BNKernelCacheSymbol(
+ symbolType=symbol.symbol_type,
+ address=symbol.address,
+ _name=BNAllocString(symbol.name)
+ )
+class KernelCacheController:
+ def __init__(self, view: BinaryView):
+ """
+ Retrieve the shared cache controller for a given view.
+ Call `is_valid` to check if the controller is valid.
+ """
+ self.handle = kccore.BNGetKernelCacheController(view.handle)
-@dataclasses.dataclass
-class KernelCacheMachOHeader:
- header: str
+ def __del__(self):
+ if self.handle is not None:
+ kccore.BNFreeKernelCacheControllerReference(self.handle)
- @classmethod
- def LoadFromString(cls, s: str):
- return cls(header=s)
+ def __str__(self):
+ return repr(self)
def __repr__(self):
- return f"<KernelCacheMachOHeader {self.header!r}>"
+ return f"<KernelCacheController: {len(self.images)} images>"
+ def is_valid(self) -> bool:
+ return self.handle is not None
-class KernelCache:
- def __init__(self, view):
- # Create a KernelCache object from a BinaryView.
- self.handle = kccore.BNGetKernelCache(view.handle)
+ def apply_image(self, view: BinaryView, image: CacheImage) -> bool:
+ api_image: kccore.BNKernelCacheImage = image_to_api(image)
+ result = kccore.BNKernelCacheControllerApplyImage(self.handle, view.handle, api_image)
+ kccore.BNKernelCacheFreeImage(api_image)
+ return result
- @staticmethod
- def get_load_progress(view) -> int:
- """
- Returns the current load progress.
- Note: In the C++ FFI this function takes the BinaryView's file session ID.
- """
- return KCViewLoadProgress(kccore.BNKCViewGetLoadProgress(view.file.session_id))
+ def is_image_loaded(self, image: CacheImage) -> bool:
+ api_image: kccore.BNKernelCacheImage = image_to_api(image)
+ result = kccore.BNKernelCacheControllerIsImageLoaded(self.handle, api_image)
+ kccore.BNKernelCacheFreeImage(api_image)
+ return result
- @staticmethod
- def fast_get_image_count(view) -> int:
- """
- Quickly returns the number of images in the kernel cache.
- """
- return kccore.BNKCViewFastGetImageCount(view.handle)
+ def get_image_at(self, address: int) -> Optional[CacheImage]:
+ api_image = kccore.BNKernelCacheImage()
+ if not kccore.BNKernelCacheControllerGetImageAt(self.handle, address, api_image):
+ return None
+ image = image_from_api(api_image)
+ kccore.BNKernelCacheFreeImage(api_image)
+ return image
- def load_image_with_install_name(self, install_name: str) -> bool:
- """
- Load a kernel cache image by its install name.
- """
- return kccore.BNKCViewLoadImageWithInstallName(self.handle, install_name)
+ def get_image_containing(self, address: int) -> Optional[CacheImage]:
+ api_image = kccore.BNKernelCacheImage()
+ if not kccore.BNKernelCacheControllerGetImageContaining(self.handle, address, api_image):
+ return None
+ image = image_from_api(api_image)
+ kccore.BNKernelCacheFreeImage(api_image)
+ return image
- def load_image_containing_address(self, addr: int) -> bool:
- """
- Load the image that contains the given address.
- """
- return kccore.BNKCViewLoadImageContainingAddress(self.handle, addr)
+ def get_image_with_name(self, name: str) -> Optional[CacheImage]:
+ api_image = kccore.BNKernelCacheImage()
+ if not kccore.BNKernelCacheControllerGetImageWithName(self.handle, name, api_image):
+ return None
+ image = image_from_api(api_image)
+ kccore.BNKernelCacheFreeImage(api_image)
+ return image
- @property
- def image_names(self) -> list[str]:
+ def get_image_dependencies(self, image: CacheImage) -> [str]:
"""
- Return a list of available kernel cache image install names.
+ Returns a list of image names that this image depends on.
"""
count = ctypes.c_ulonglong()
- value = kccore.BNKCViewGetInstallNames(self.handle, count)
+ api_image: kccore.BNKernelCacheImage = image_to_api(image)
+ value = kccore.BNKernelCacheControllerGetImageDependencies(self.handle, api_image, count)
+ kccore.BNKernelCacheFreeImage(api_image)
if value is None:
return []
result = []
for i in range(count.value):
- result.append(value[i].decode('utf-8'))
+ result.append(value[i].decode("utf-8"))
BNFreeStringList(value, count)
return result
+ def get_symbol_at(self, address: int) -> Optional[CacheSymbol]:
+ api_symbol = kccore.BNKernelCacheSymbol()
+ if not kccore.BNKernelCacheControllerGetSymbolAt(self.handle, address, api_symbol):
+ return None
+ symbol = symbol_from_api(api_symbol)
+ kccore.BNKernelCacheFreeSymbol(api_symbol)
+ return symbol
+
+ def get_symbol_with_name(self, name: str) -> Optional[CacheSymbol]:
+ api_symbol = kccore.BNKernelCacheSymbol()
+ if not kccore.BNKernelCacheControllerGetSymbolWithName(self.handle, name, api_symbol):
+ return None
+ symbol = symbol_from_api(api_symbol)
+ kccore.BNKernelCacheFreeSymbol(api_symbol)
+ return symbol
+
@property
- def images(self) -> list[KCImage]:
- """
- Return all kernel cache images.
- """
+ def images(self) -> [CacheImage]:
count = ctypes.c_ulonglong()
- value = kccore.BNKCViewGetAllImages(self.handle, count)
+ value = kccore.BNKernelCacheControllerGetImages(self.handle, count)
if value is None:
return []
result = []
for i in range(count.value):
- mappings = []
- for j in range(value[i].mappingCount):
- mapping = KCImageMemoryMapping(
- name=value[i].mappings[j].name,
- vmAddress=value[i].mappings[j].vmAddress,
- rawViewOffset=value[i].mappings[j].rawViewOffset,
- size=value[i].mappings[j].size,
- )
- mappings.append(mapping)
- result.append(KCImage(
- name=value[i].name,
- headerFileAddress=value[i].headerFileAddress,
- mappings=mappings
- ))
- kccore.BNKCViewFreeAllImages(value, count)
+ result.append(image_from_api(value[i]))
+ kccore.BNKernelCacheFreeImageList(value, count)
return result
@property
- def loaded_images(self) -> list[KCImage]:
+ def loaded_images(self) -> [CacheImage]:
"""
- Return the kernel cache images that are currently loaded.
+ Get a list of images that are currently loaded in the view.
"""
count = ctypes.c_ulonglong()
- images = kccore.BNKCViewGetLoadedImages(self.handle, count)
- if images is None:
+ value = kccore.BNKernelCacheControllerGetLoadedImages(self.handle, count)
+ if value is None:
return []
result = []
for i in range(count.value):
- mappings = []
- for j in range(images[i].mappingCount):
- mapping = KCImageMemoryMapping(
- name=images[i].mappings[j].name,
- vmAddress=images[i].mappings[j].vmAddress,
- rawViewOffset=images[i].mappings[j].rawViewOffset,
- size=images[i].mappings[j].size,
- )
- mappings.append(mapping)
- result.append(KCImage(
- name=images[i].name,
- headerFileAddress=images[i].headerFileAddress,
- mappings=mappings
- ))
- kccore.BNKCViewFreeAllImages(images, count)
+ result.append(image_from_api(value[i]))
+ kccore.BNKernelCacheFreeImageList(value, count)
return result
- def load_all_symbols_and_wait(self) -> list[KCSymbol]:
- """
- Load all symbols from the kernel cache and wait for completion.
- """
+ @property
+ def symbols(self) -> [CacheSymbol]:
count = ctypes.c_ulonglong()
- value = kccore.BNKCViewLoadAllSymbolsAndWait(self.handle, count)
+ value = kccore.BNKernelCacheControllerGetSymbols(self.handle, count)
if value is None:
return []
result = []
for i in range(count.value):
- sym = KCSymbol(
- name=value[i].name,
- image=value[i].image,
- address=value[i].address
- )
- result.append(sym)
- kccore.BNKCViewFreeSymbols(value, count)
+ result.append(symbol_from_api(value[i]))
+ kccore.BNKernelCacheFreeSymbolList(value, count)
return result
- def get_name_for_address(self, address: int) -> str:
- """
- Return the symbol name for a given address.
- """
- name = kccore.BNKCViewGetNameForAddress(self.handle, address)
- if name is None:
- return ""
- return name
- def get_image_name_for_address(self, address: int) -> str:
- """
- Return the image name for a given address.
- """
- name = kccore.BNKCViewGetImageNameForAddress(self.handle, address)
- if name is None:
- return ""
- return name
+def _get_kernel_cache(instance: binaryninja.PythonScriptingInstance):
+ if instance.interpreter.active_view is None:
+ return None
+ controller = KernelCacheController(instance.interpreter.active_view)
+ if not controller.is_valid():
+ return None
+ return controller
- def get_macho_header_for_image(self, name: str):
- """
- Return a KernelCacheMachOHeader for the image with the given install name.
- """
- outputStr = kccore.BNKCViewGetImageHeaderForName(self.handle, name)
- if outputStr is None:
- return None
- return KernelCacheMachOHeader.LoadFromString(outputStr)
- def get_macho_header_for_address(self, address: int):
- """
- Return a KernelCacheMachOHeader for the image containing the given address.
- """
- outputStr = kccore.BNKCViewGetImageHeaderForAddress(self.handle, address)
- if outputStr is None:
- return None
- return KernelCacheMachOHeader.LoadFromString(outputStr)
+binaryninja.PythonScriptingProvider.register_magic_variable(
+ "kc",
+ _get_kernel_cache
+)
- @property
- def state(self):
- """
- Return the current state of the kernel cache.
- """
- return KCViewState(kccore.BNKCViewGetState(self.handle))
+binaryninja.PythonScriptingProvider.register_magic_variable(
+ "kernel_cache",
+ _get_kernel_cache
+)
diff --git a/view/kernelcache/api/python/kernelcache_enums.py b/view/kernelcache/api/python/kernelcache_enums.py
index e610c527..83cbf027 100644
--- a/view/kernelcache/api/python/kernelcache_enums.py
+++ b/view/kernelcache/api/python/kernelcache_enums.py
@@ -1,14 +1,38 @@
import enum
-class KCViewLoadProgress(enum.IntEnum):
- LoadProgressNotStarted = 0
- LoadProgressLoadingCaches = 1
- LoadProgressLoadingImages = 2
- LoadProgressFinished = 3
+class KernelCacheEntryType(enum.IntEnum):
+ KernelCacheEntryTypePrimary = 0
+ KernelCacheEntryTypeSecondary = 1
+ KernelCacheEntryTypeSymbols = 2
+ KernelCacheEntryTypeDyldData = 3
+ KernelCacheEntryTypeStub = 4
-class KCViewState(enum.IntEnum):
- Unloaded = 0
- Loaded = 1
- LoadedWithImages = 2
+class KernelCacheRegionType(enum.IntEnum):
+ KernelCacheRegionTypeImage = 0
+ KernelCacheRegionTypeStubIsland = 1
+ KernelCacheRegionTypeDyldData = 2
+ KernelCacheRegionTypeNonImage = 3
+
+
+class SegmentFlag(enum.IntEnum):
+ SegmentExecutable = 1
+ SegmentWritable = 2
+ SegmentReadable = 4
+ SegmentContainsData = 8
+ SegmentContainsCode = 16
+ SegmentDenyWrite = 32
+ SegmentDenyExecute = 64
+
+
+class SymbolType(enum.IntEnum):
+ FunctionSymbol = 0
+ ImportAddressSymbol = 1
+ ImportedFunctionSymbol = 2
+ DataSymbol = 3
+ ImportedDataSymbol = 4
+ ExternalSymbol = 5
+ LibraryFunctionSymbol = 6
+ SymbolicFunctionSymbol = 7
+ LocalLabelSymbol = 8
diff --git a/view/kernelcache/core/KCView.h b/view/kernelcache/core/KCView.h
deleted file mode 100644
index 530e10de..00000000
--- a/view/kernelcache/core/KCView.h
+++ /dev/null
@@ -1,48 +0,0 @@
-//
-// Created by kat on 5/23/23.
-//
-
-#ifndef KERNELCACHE_KCVIEW_H
-#define KERNELCACHE_KCVIEW_H
-
-#include <binaryninjaapi.h>
-
-class KCView : public BinaryNinja::BinaryView {
- bool m_parseOnly;
-public:
-
- KCView(const std::string &typeName, BinaryView *data, bool parseOnly = false);
-
- ~KCView() override;
-
- bool Init() override;
-
- virtual bool PerformIsExecutable() const override { return true; }
-};
-
-
-class KCViewType : public BinaryNinja::BinaryViewType {
-
-public:
- KCViewType();
-
- BinaryNinja::Ref<BinaryNinja::BinaryView> Create(BinaryNinja::BinaryView *data) override;
-
- BinaryNinja::Ref<BinaryNinja::BinaryView> Parse(BinaryNinja::BinaryView *data) override;
-
- bool IsTypeValidForData(BinaryNinja::BinaryView *data) override;
-
- bool IsDeprecated() override { return false; }
-
- BinaryNinja::Ref<BinaryNinja::Settings> GetLoadSettingsForData(BinaryNinja::BinaryView *data) override;
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
- void InitKCViewType();
-#ifdef __cplusplus
-};
-#endif
-
-#endif //KERNELCACHE_KCVIEW_H
diff --git a/view/kernelcache/core/KernelCache.cpp b/view/kernelcache/core/KernelCache.cpp
index 6cb3fca6..36f3331b 100644
--- a/view/kernelcache/core/KernelCache.cpp
+++ b/view/kernelcache/core/KernelCache.cpp
@@ -1,1101 +1,127 @@
-//
-// Created by kat on 5/19/23.
-//
-
-#include "binaryninjaapi.h"
-
-/* ---
- * This is the primary image loader logic for Kernelcaches.
- * */
-
#include "KernelCache.h"
-#include <filesystem>
-#include <utility>
-#include <fcntl.h>
-#include <memory>
-#include <chrono>
-#include <thread>
+#include <regex>
+#include <filesystem>
using namespace BinaryNinja;
-using namespace KernelCacheCore;
-
-namespace KernelCacheCore {
-
-
-
-#ifdef _MSC_VER
-
-int count_trailing_zeros(uint64_t value) {
- unsigned long index; // 32-bit long on Windows
- if (_BitScanForward64(&index, value)) {
- return index;
- } else {
- return 64; // If the value is 0, return 64.
- }
-}
-#else
-int count_trailing_zeros(uint64_t value) {
- return value == 0 ? 64 : __builtin_ctzll(value);
-}
-#endif
-
-// State that does not change after `PerformInitialLoad`.
-struct KernelCache::CacheInfo :
- public MetadataSerializable<KernelCache::CacheInfo, std::optional<KernelCache::CacheInfo>>
-{
- // std::unordered_map<uint64_t, KernelCacheMachOHeader> headers;
- std::vector<KernelCacheImage> images;
- std::unordered_map<std::string, uint64_t> imageStarts;
-
- KernelCacheFormat cacheFormat = FilesetCacheFormat;
-
-#ifndef NDEBUG
- void Verify() const;
-#endif
-
- uint64_t BaseAddress() const;
-
- void Store(SerializationContext&) const;
- static std::optional<KernelCache::CacheInfo> Load(DeserializationContext&);
-};
-
-struct State : public MetadataSerializable<State>
-{
- std::unordered_map<uint64_t, KernelCacheImage> loadedImages;
- // Store only. Loading is done via `ModifiedState`.
- void Store(SerializationContext&, std::optional<KCViewState> viewState) const;
-};
-
-struct KernelCache::ModifiedState : public State, public MetadataSerializable<KernelCache::ModifiedState>
-{
- std::optional<KCViewState> viewState;
-
- using Base = MetadataSerializable<KernelCache::ModifiedState>;
- using Base::AsMetadata;
- using Base::LoadFromString;
-
- void Store(SerializationContext&) const;
- static KernelCache::ModifiedState Load(DeserializationContext&);
- static KernelCache::ModifiedState LoadAll(BinaryNinja::BinaryView*, const CacheInfo&);
-
- void Merge(KernelCache::ModifiedState&& other);
-};
-
-struct KernelCache::ViewSpecificState
-{
- std::mutex typeLibraryMutex;
- std::unordered_map<std::string, Ref<TypeLibrary>> typeLibraries;
-
- std::mutex viewOperationsThatInfluenceMetadataMutex;
-
- std::atomic<BNKCViewLoadProgress> progress;
-
- std::mutex cacheInfoMutex;
- std::shared_ptr<const KernelCache::CacheInfo> cacheInfo;
-
- std::mutex stateMutex;
- struct State state;
-
- std::atomic<KCViewState> viewState;
- uint64_t savedModifications = 0;
-};
-
-namespace {
-
- std::shared_ptr<KernelCache::ViewSpecificState> ViewSpecificStateForId(
- uint64_t viewIdentifier, bool insertIfNeeded = true)
- {
- static std::mutex viewSpecificStateMutex;
- static std::unordered_map<uint64_t, std::weak_ptr<KernelCache::ViewSpecificState>> viewSpecificState;
-
- std::lock_guard lock(viewSpecificStateMutex);
-
- if (auto it = viewSpecificState.find(viewIdentifier); it != viewSpecificState.end())
- {
- if (auto statePtr = it->second.lock())
- return statePtr;
- }
-
- if (!insertIfNeeded)
- return nullptr;
-
- auto statePtr = std::make_shared<KernelCache::ViewSpecificState>();
- viewSpecificState[viewIdentifier] = statePtr;
-
- // Prune entries for any views that are no longer in use.
- for (auto it = viewSpecificState.begin(); it != viewSpecificState.end();)
- {
- if (it->second.expired())
- it = viewSpecificState.erase(it);
- else
- ++it;
- }
-
- return statePtr;
- }
-
- std::shared_ptr<KernelCache::ViewSpecificState> ViewSpecificStateForView(Ref<BinaryNinja::BinaryView> view)
- {
- return ViewSpecificStateForId(view->GetFile()->GetSessionId());
- }
-
- std::string base_name(std::string const& path)
- {
- return path.substr(path.find_last_of("/\\") + 1);
- }
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function"
- static int64_t readSLEB128(DataBuffer& buffer, size_t length, size_t& offset)
- {
- uint8_t cur;
- int64_t value = 0;
- size_t shift = 0;
- while (offset < length)
- {
- cur = buffer[offset++];
- value |= (cur & 0x7f) << shift;
- shift += 7;
- if ((cur & 0x80) == 0)
- break;
- }
- value = (value << (64 - shift)) >> (64 - shift);
- return value;
- }
-#pragma clang diagnostic pop
-
-
- static uint64_t readLEB128(DataBuffer& p, size_t end, size_t& offset)
- {
- uint64_t result = 0;
- int bit = 0;
- do
- {
- if (offset >= end)
- return -1;
-
- uint64_t slice = p[offset] & 0x7f;
-
- if (bit > 63)
- return -1;
- else
- {
- result |= (slice << bit);
- bit += 7;
- }
- } while (p[offset++] & 0x80);
- return result;
- }
-
-
- uint64_t readValidULEB128(DataBuffer& buffer, size_t& cursor)
- {
- uint64_t value = readLEB128(buffer, buffer.GetLength(), cursor);
- if ((int64_t)value == -1)
- throw ReadException();
- return value;
- }
-
-} // namespace
-
-void KernelCache::PerformInitialLoad(std::lock_guard<std::mutex>& lock)
-{
- bool is64 = m_kcView->GetAddressSize() == 8;
-
- m_logger->LogInfo("Performing initial load of Kernel Cache");
-
- m_viewSpecificState->progress = LoadProgressLoadingCaches;
-
- CacheInfo initialState;
- initialState.cacheFormat = FilesetCacheFormat;
-
- m_viewSpecificState->progress = LoadProgressLoadingImages;
-
- // We have set up enough metadata to map VM now.
- // Iterate load comands:
- BinaryReader reader(m_kcView->GetParentView());
-
- uint32_t magic = reader.Read32();
- if (magic != MH_CIGAM_64 && magic != MH_MAGIC_64) // FIXME 32 bit
- {
- m_logger->LogError("Invalid magic number in KernelCache");
- return;
- }
- uint32_t cpuType = reader.Read32();
- reader.SeekRelative(4);
- uint32_t fileType = reader.Read32();
- if (fileType != MH_FILESET)
- {
- m_logger->LogError("Invalid file type in KernelCache");
- return;
- }
- uint32_t ncmds = reader.Read32();
- reader.SeekRelative(8);
- if ((cpuType & MachOABIMask) != MachOABI64)
- {
- m_logger->LogError("Invalid ABI in KernelCache. 32 bit not yet supported.");
- return;
- }
- if (is64)
- reader.SeekRelative(4);
-
- uint64_t off = reader.GetOffset();
-
- while (ncmds--)
- {
- reader.Seek(off);
- uint32_t cmd = reader.Read32();
- uint32_t cmdsize = reader.Read32();
- if (cmd == LC_FILESET_ENTRY)
- {
- // uint64_t vmAddr =reader.Read64();
- reader.SeekRelative(m_kcView->GetAddressSize());
- uint64_t fileOff = reader.Read64();
- uint32_t entryID = reader.Read32();
- reader.Seek(entryID + off);
- std::string installName = reader.ReadCString(0x1000);
- initialState.imageStarts[installName] = fileOff;
- }
- off += cmdsize;
- }
-
- for (const auto& start : initialState.imageStarts)
- {
- try {
- auto imageHeader = KernelCache::LoadHeaderForAddress(m_kcView, start.second, start.first);
- if (imageHeader)
- {
- KernelCacheImage image;
- image.installName = start.first;
- image.headerFileLocation = start.second;
- for (const auto& segment : imageHeader->segments)
- {
- char segName[17];
- memcpy(segName, segment.segname, 16);
- segName[16] = 0;
- MemoryRegion sectionRegion;
- sectionRegion.prettyName = imageHeader.value().identifierPrefix + "::" + std::string(segName);
- sectionRegion.start = segment.vmaddr;
- sectionRegion.size = segment.vmsize;
- sectionRegion.fileOffset = segment.fileoff;
- uint32_t flags = 0;
- if (segment.initprot & MACHO_VM_PROT_READ)
- flags |= SegmentReadable;
- if (segment.initprot & MACHO_VM_PROT_WRITE)
- flags |= SegmentWritable;
- if (segment.initprot & MACHO_VM_PROT_EXECUTE)
- flags |= SegmentExecutable;
- if (((segment.initprot & MACHO_VM_PROT_WRITE) == 0) &&
- ((segment.maxprot & MACHO_VM_PROT_WRITE) == 0))
- flags |= SegmentDenyWrite;
- if (((segment.initprot & MACHO_VM_PROT_EXECUTE) == 0) &&
- ((segment.maxprot & MACHO_VM_PROT_EXECUTE) == 0))
- flags |= SegmentDenyExecute;
-
- // if we're positive we have an entry point for some reason, force the segment
- // executable. this helps with kernel images.
- for (auto &entryPoint : imageHeader->m_entryPoints)
- if (segment.vmaddr <= entryPoint && (entryPoint < (segment.vmaddr + segment.filesize)))
- flags |= SegmentExecutable;
-
- sectionRegion.flags = (BNSegmentFlag)flags;
- image.regions.push_back(sectionRegion);
- }
- initialState.images.push_back(image);
- }
- else
- {
- m_logger->LogError("Failed to load Mach-O header for %s", start.first.c_str());
- }
- }
- catch (std::exception& ex)
- {
- m_logger->LogError("Failed to load Mach-O header for %s: %s", start.first.c_str(), ex.what());
- }
- }
-
- m_cacheInfo = std::make_shared<CacheInfo>(std::move(initialState));
- m_modifiedState->viewState = KCViewStateLoaded;
- SaveCacheInfoToKCView(lock);
- SaveModifiedStateToKCView(lock);
-
- m_logger->LogDebug("Finished initial load of KernelCache");
-
- m_viewSpecificState->progress = LoadProgressFinished;
-}
-
-void KernelCache::DeserializeFromRawView(std::lock_guard<std::mutex>& lock)
-{
- std::lock_guard cacheInfoLock(m_viewSpecificState->cacheInfoMutex);
- if (m_viewSpecificState->cacheInfo)
- {
- m_cacheInfo = m_viewSpecificState->cacheInfo;
- m_modifiedState = std::make_unique<ModifiedState>();
- m_metadataValid = true;
- return;
- }
-
- if (KernelCacheMetadata::ViewHasMetadata(m_kcView))
- {
- auto metadata = KernelCacheMetadata::LoadFromView(m_kcView);
- if (!metadata)
- {
- m_metadataValid = false;
- m_logger->LogError("Failed to deserialize Shared Cache metadata");
- return;
- }
-
- m_viewSpecificState->viewState = metadata->state->viewState.value_or(KCViewStateUnloaded);
- m_viewSpecificState->state = std::move(*metadata->state);
- m_viewSpecificState->cacheInfo = std::move(metadata->cacheInfo);
-
- m_cacheInfo = m_viewSpecificState->cacheInfo;
- m_modifiedState = std::make_unique<ModifiedState>();
- m_metadataValid = true;
- return;
- }
-
- m_cacheInfo = nullptr;
- m_modifiedState = std::make_unique<ModifiedState>();
- m_modifiedState->viewState = KCViewStateUnloaded;
- m_metadataValid = true;
-}
-
-std::string to_hex_string(uint64_t value)
+std::pair<std::string, Ref<Type>> CacheSymbol::DemangledName(BinaryView &view) const
{
- std::stringstream ss;
- ss << std::hex << value;
- return ss.str();
-}
-
-
-KernelCache::KernelCache(BinaryNinja::Ref<BinaryNinja::BinaryView> kcView) : m_kcView(kcView),
- m_viewSpecificState(ViewSpecificStateForView(kcView))
-{
- std::lock_guard lock(m_mutex);
- m_logger = LogRegistry::GetLogger("KernelCache", kcView->GetFile()->GetSessionId());
- if (kcView->GetTypeName() != KC_VIEW_NAME)
- {
- // Unreachable?
- m_logger->LogError("Attempted to create KernelCache object from non-KernelCache view");
- return;
- }
-
- INIT_KERNELCACHE_API_OBJECT()
- DeserializeFromRawView(lock);
- if (!m_metadataValid)
- return;
-
- if (m_modifiedState->viewState.value_or(m_viewSpecificState->viewState) != KCViewStateUnloaded)
- {
- m_viewSpecificState->progress = LoadProgressFinished;
- return;
- }
-
- std::unique_lock viewOperationsLock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex);
-
- try {
- PerformInitialLoad(lock);
- }
- catch (...)
- {
- m_logger->LogError("Failed to perform initial load of KernelCache");
- }
-}
-
-KernelCache::~KernelCache() {
+ QualifiedName qname;
+ Ref<Type> outType;
+ std::string shortName = name;
+ if (DemangleGeneric(view.GetDefaultArchitecture(), name, outType, qname, &view, true))
+ shortName = qname.GetString();
+ return { shortName, outType };
}
-KernelCache* KernelCache::GetFromKCView(BinaryNinja::Ref<BinaryNinja::BinaryView> kcView)
+std::pair<Ref<Symbol>, Ref<Type>> CacheSymbol::GetBNSymbolAndType(BinaryView& view) const
{
- if (kcView->GetTypeName() != KC_VIEW_NAME)
- return nullptr;
- try {
- return new KernelCache(kcView);
- }
- catch (...)
- {
- return nullptr;
- }
+ auto [shortName, demangledType] = DemangledName(view);
+ auto symbol = new Symbol(type, shortName, shortName, name, address, nullptr);
+ return {symbol, demangledType};
}
-
-uint64_t KernelCache::FastGetImageCount(BinaryNinja::Ref<BinaryNinja::BinaryView> kcView)
+std::vector<std::string> CacheImage::GetDependencies() const
{
- if (!kcView->GetParentView())
- return 0;
- auto reader = BinaryReader(kcView->GetParentView());
- uint32_t magic = reader.Read32();
- if (magic != MH_CIGAM_64 && magic != MH_MAGIC_64) // FIXME 32 bit
- {
- return 0;
- }
- reader.Seek(0xc);
- uint32_t fileType = reader.Read32();
- if (fileType != MH_FILESET)
- {
- return 0;
- }
- uint32_t ncmds = reader.Read32();
-
- uint64_t imageCount = 0;
-
- uint64_t off = 0x20; // FIXME 32 bit
- while (ncmds--)
- {
- reader.Seek(off);
- uint32_t cmd = reader.Read32();
- uint32_t cmdsize = reader.Read32();
- if (cmd == LC_FILESET_ENTRY)
- {
- imageCount++;
- }
- off += cmdsize;
- }
-
- return imageCount;
-}
-
-
-const std::unordered_map<std::string, uint64_t>& KernelCache::AllImageStarts() const
-{
- return m_cacheInfo->imageStarts;
-}
-
-
-const std::unordered_map<uint64_t, KernelCacheMachOHeader> KernelCache::AllImageHeaders() const
-{
- std::unordered_map<uint64_t, KernelCacheMachOHeader> headers;
- for (const auto& start : m_cacheInfo->imageStarts)
- {
- auto header = LoadHeaderForAddress(m_kcView, start.second, start.first);
- if (header)
- {
- headers[start.second] = *header;
- }
- }
- return headers;
-}
-
-
-KCViewState KernelCache::ViewState() const
-{
- return m_viewSpecificState->viewState;
-}
-
-std::optional<uint64_t> KernelCache::GetImageStart(std::string installName)
-{
- for (const auto& [name, start] : m_cacheInfo->imageStarts)
- {
- if (name == installName)
- {
- return start;
- }
- }
- return {};
-}
-
-std::optional<KernelCacheMachOHeader> KernelCache::HeaderForVMAddress(uint64_t address)
-{
- for (const auto& img : m_cacheInfo->images)
- {
- for (const auto& region : img.regions)
- {
- if (region.start <= address && region.start + region.size > address)
- {
- return LoadHeaderForAddress(m_kcView, img.headerFileLocation, img.installName);
- }
- }
- }
- return {};
-}
-
-std::optional<KernelCacheMachOHeader> KernelCache::HeaderForFileAddress(uint64_t address)
-{
- for (const auto& img : m_cacheInfo->images)
- {
- for (const auto& region : img.regions)
- {
- if (region.fileOffset <= address && region.fileOffset + region.size > address)
- {
- return LoadHeaderForAddress(m_kcView, img.headerFileLocation, img.installName);
- }
- }
- }
+ if (header)
+ return header->dylibs;
return {};
}
-std::string KernelCache::NameForAddress(uint64_t address)
+KernelCache::KernelCache(uint64_t addressSize)
{
- if (auto header = HeaderForVMAddress(address))
- {
- for (const auto& section : header->sections)
- {
- if (section.addr <= address && section.addr + section.size > address)
- {
- char sectionName[17];
- strncpy(sectionName, section.sectname, 16);
- sectionName[16] = '\0';
- return header->identifierPrefix + "::" + sectionName;
- }
- }
- }
- return "";
+ m_namedSymMutex = std::make_unique<std::shared_mutex>();
}
-std::string KernelCache::ImageNameForAddress(uint64_t address)
-{
- if (auto header = HeaderForVMAddress(address))
- {
- return header->identifierPrefix;
- }
- return "";
-}
-bool KernelCache::LoadImageContainingAddress(uint64_t address)
+void KernelCache::AddImage(CacheImage&& image)
{
- std::lock_guard<std::mutex> lock(m_mutex);
- return LoadImageContainingAddress(lock, address);
+ m_images.insert({image.headerVirtualAddress, std::move(image)});
}
-bool KernelCache::LoadImageContainingAddress(std::lock_guard<std::mutex>& lock, uint64_t address)
+void KernelCache::AddSymbol(CacheSymbol symbol)
{
- for (const auto& img : m_cacheInfo->images)
- {
- for (const auto& region : img.regions)
- {
- if (region.start <= address && region.start + region.size > address)
- {
- return LoadImageWithInstallName(lock, img.installName);
- }
- }
- }
- return false;
+ m_symbols.insert({symbol.address, std::move(symbol)});
}
-
-bool KernelCache::LoadImageWithInstallName(std::string installName)
+void KernelCache::AddSymbols(std::vector<CacheSymbol>&& symbols)
{
- std::lock_guard<std::mutex> lock(m_mutex);
- return LoadImageWithInstallName(lock, installName);
+ for (auto& symbol : symbols)
+ m_symbols.insert({symbol.address, std::move(symbol)});
}
-
-bool KernelCache::LoadImageWithInstallName(std::lock_guard<std::mutex>& lock, std::string installName)
+bool KernelCache::ProcessEntryImage(Ref<BinaryView> bv, const std::string& path, const fileset_entry_command& info)
{
- auto settings = m_kcView->GetLoadSettings(KC_VIEW_NAME);
-
- std::lock_guard viewSpecificStateLock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex);
-
- m_logger->LogInfo("Loading image %s", installName.c_str());
- const KernelCacheImage* targetImage = nullptr;
-
- // FIXME at some point the logic func should be one with targetImage passed, and an installName wrapper can be added.
- // In many use cases of this function we already have our target image.
- for (auto& cacheImage : m_cacheInfo->images)
- {
- if (cacheImage.installName == installName)
- {
- targetImage = &cacheImage;
- break;
- }
- }
- if (!targetImage)
- return false;
-
- const auto header = LoadHeaderForAddress(m_kcView, targetImage->headerFileLocation, targetImage->installName);
- if (!header)
+ auto imageHeader = KernelCacheMachOHeader::ParseHeaderForAddress(bv, info.vmaddr, info.fileoff, path);
+ if (!imageHeader.has_value())
return false;
- m_modifiedState->viewState = KCViewStateLoadedWithImages;
+ // Add the image to the cache.
+ CacheImage image;
+ image.headerFileAddress = info.fileoff;
+ image.headerVirtualAddress = info.vmaddr;
+ image.path = path;
- InitializeSegmentsForHeader(m_kcView, *header, *targetImage);
-
- // Optimize relocations we just added en masse.
- m_kcView->FinalizeNewSegments();
-
- // FIXME Load TypeLibrary here when we have those for the kernel.
+ // Add all image regions.
+ for (const auto& segment : imageHeader->segments)
+ {
+ char segName[17];
+ memcpy(segName, segment.segname, 16);
+ segName[16] = 0;
- m_modifiedState->loadedImages[targetImage->headerFileLocation] = *targetImage;
+ CacheRegion sectionRegion;
+ sectionRegion.name = imageHeader->identifierPrefix + "::" + std::string(segName);
+ sectionRegion.start = segment.vmaddr;
+ sectionRegion.size = segment.vmsize;
+ // Associate this region with this image, this makes it easier to identify what image owns this region.
+ sectionRegion.imageStart = image.headerFileAddress;
- SaveModifiedStateToKCView(lock);
+ uint32_t flags = SegmentFlagsFromMachOProtections(segment.initprot, segment.maxprot);
+ // if we're positive we have an entry point for some reason, force the segment
+ // executable. this helps with kernel images.
+ for (const auto& entryPoint : imageHeader->m_entryPoints)
+ if (segment.vmaddr <= entryPoint && (entryPoint < (segment.vmaddr + segment.filesize)))
+ flags |= SegmentExecutable;
+ sectionRegion.flags = static_cast<BNSegmentFlag>(flags);
- auto h = KernelCache::LoadHeaderForAddress(m_kcView, targetImage->headerFileLocation, installName);
- if (!h.has_value())
- {
- return false;
+ image.regions.push_back(std::move(sectionRegion));
}
- KernelCache::InitializeHeader(m_kcView, *h);
+ // Add the exported symbols to the available symbols.
+ std::vector<CacheSymbol> exportSymbols = imageHeader->ReadExportSymbolTrie(bv);
+ AddSymbols(std::move(exportSymbols));
+ TableInfo symbolInfo = { imageHeader->symtab.symoff, imageHeader->symtab.nsyms };
+ TableInfo stringInfo = { imageHeader->symtab.stroff, imageHeader->symtab.strsize };
+ std::vector<CacheSymbol> symbols = imageHeader->ReadSymbolTable(bv, symbolInfo, stringInfo);
+ AddSymbols(std::move(symbols));
- m_kcView->AddAnalysisOption("linearsweep");
- m_kcView->UpdateAnalysis();
+ // This is behind a shared pointer as the header itself is very large.
+ image.header = std::make_shared<KernelCacheMachOHeader>(std::move(*imageHeader));
+ AddImage(std::move(image));
return true;
}
-std::optional<KernelCacheMachOHeader> KernelCache::LoadHeaderForAddress(Ref<BinaryView> view, uint64_t address, std::string installName)
+void KernelCache::ProcessSymbols()
{
- KernelCacheMachOHeader header;
-
- header.installName = installName;
- header.identifierPrefix = base_name(installName);
-
- std::string errorMsg;
- // address is a Raw file offset
- BinaryReader reader(view->GetParentView());
- reader.Seek(address);
-
- header.ident.magic = reader.Read32();
-
- BNEndianness endianness;
- if (header.ident.magic == MH_MAGIC || header.ident.magic == MH_MAGIC_64)
- endianness = LittleEndian;
- else if (header.ident.magic == MH_CIGAM || header.ident.magic == MH_CIGAM_64)
- endianness = BigEndian;
- else
- {
- return {};
- }
-
- reader.SetEndianness(endianness);
- header.ident.cputype = reader.Read32();
- header.ident.cpusubtype = reader.Read32();
- header.ident.filetype = reader.Read32();
- header.ident.ncmds = reader.Read32();
- header.ident.sizeofcmds = reader.Read32();
- header.ident.flags = reader.Read32();
- if ((header.ident.cputype & MachOABIMask) == MachOABI64) // address size == 8
- {
- header.ident.reserved = reader.Read32();
- }
- header.loadCommandOffset = reader.GetOffset();
-
- // Parse segment commands
- try
- {
- for (size_t i = 0; i < header.ident.ncmds; i++)
- {
- // BNLogInfo("of 0x%llx", reader.GetOffset());
- load_command load;
- segment_command_64 segment64;
- section_64 sect;
- memset(&sect, 0, sizeof(sect));
- size_t curOffset = reader.GetOffset();
- load.cmd = reader.Read32();
- load.cmdsize = reader.Read32();
- size_t nextOffset = curOffset + load.cmdsize;
- if (load.cmdsize < sizeof(load_command))
- return {};
-
- switch (load.cmd)
- {
- case LC_MAIN:
- {
- uint64_t entryPoint = reader.Read64();
- header.entryPoints.push_back({entryPoint, true});
- (void)reader.Read64(); // Stack start
- break;
- }
- case LC_SEGMENT: // map the 32bit version to 64 bits
- segment64.cmd = LC_SEGMENT_64;
- reader.Read(&segment64.segname, 16);
- segment64.vmaddr = reader.Read32();
- segment64.vmsize = reader.Read32();
- segment64.fileoff = reader.Read32();
- segment64.filesize = reader.Read32();
- segment64.maxprot = reader.Read32();
- segment64.initprot = reader.Read32();
- segment64.nsects = reader.Read32();
- segment64.flags = reader.Read32();
- if (strncmp(segment64.segname, "__TEXT\0", 7) == 0)
- {
- header.relocationBase = segment64.vmaddr;
- header.textBase = segment64.vmaddr;
- header.textBaseFileOffset = segment64.fileoff;
- }
- for (size_t j = 0; j < segment64.nsects; j++)
- {
- reader.Read(&sect.sectname, 16);
- reader.Read(&sect.segname, 16);
- sect.addr = reader.Read32();
- sect.size = reader.Read32();
- sect.offset = reader.Read32();
- sect.align = reader.Read32();
- sect.reloff = reader.Read32();
- sect.nreloc = reader.Read32();
- sect.flags = reader.Read32();
- sect.reserved1 = reader.Read32();
- sect.reserved2 = reader.Read32();
- // if the segment isn't mapped into virtual memory don't add the corresponding sections.
- if (segment64.vmsize > 0)
- {
- header.sections.push_back(sect);
- }
- if (!strncmp(sect.sectname, "__mod_init_func", 15))
- header.moduleInitSections.push_back(sect);
- if (!strncmp(sect.sectname, "__mod_term_func", 15))
- header.moduleTermSections.push_back(sect);
- if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS))
- == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS))
- header.symbolStubSections.push_back(sect);
- if ((sect.flags & S_NON_LAZY_SYMBOL_POINTERS) == S_NON_LAZY_SYMBOL_POINTERS)
- header.symbolPointerSections.push_back(sect);
- if ((sect.flags & S_LAZY_SYMBOL_POINTERS) == S_LAZY_SYMBOL_POINTERS)
- header.symbolPointerSections.push_back(sect);
- }
- header.segments.push_back(segment64);
- break;
- case LC_SEGMENT_64:
- segment64.cmd = LC_SEGMENT_64;
- reader.Read(&segment64.segname, 16);
- segment64.vmaddr = reader.Read64();
- segment64.vmsize = reader.Read64();
- segment64.fileoff = reader.Read64();
- segment64.filesize = reader.Read64();
- segment64.maxprot = reader.Read32();
- segment64.initprot = reader.Read32();
- segment64.nsects = reader.Read32();
- segment64.flags = reader.Read32();
- if (strncmp(segment64.segname, "__LINKEDIT", 10) == 0)
- {
- header.linkeditSegment = segment64;
- header.linkeditPresent = true;
- }
- if (strncmp(segment64.segname, "__TEXT\0", 7) == 0)
- {
- header.relocationBase = segment64.vmaddr;
- header.textBase = segment64.vmaddr;
- header.textBaseFileOffset = segment64.fileoff;
- }
- for (size_t j = 0; j < segment64.nsects; j++)
- {
- reader.Read(&sect.sectname, 16);
- reader.Read(&sect.segname, 16);
- sect.addr = reader.Read64();
- sect.size = reader.Read64();
- sect.offset = reader.Read32();
- sect.align = reader.Read32();
- sect.reloff = reader.Read32();
- sect.nreloc = reader.Read32();
- sect.flags = reader.Read32();
- sect.reserved1 = reader.Read32();
- sect.reserved2 = reader.Read32();
- sect.reserved3 = reader.Read32();
- // if the segment isn't mapped into virtual memory don't add the corresponding sections.
- if (segment64.vmsize > 0)
- {
- header.sections.push_back(sect);
- }
-
- if (!strncmp(sect.sectname, "__mod_init_func", 15))
- header.moduleInitSections.push_back(sect);
- if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS))
- == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS))
- header.symbolStubSections.push_back(sect);
- if ((sect.flags & S_NON_LAZY_SYMBOL_POINTERS) == S_NON_LAZY_SYMBOL_POINTERS)
- header.symbolPointerSections.push_back(sect);
- if ((sect.flags & S_LAZY_SYMBOL_POINTERS) == S_LAZY_SYMBOL_POINTERS)
- header.symbolPointerSections.push_back(sect);
- }
- header.segments.push_back(segment64);
- break;
- case LC_ROUTINES: // map the 32bit version to 64bits
- header.routines64.cmd = LC_ROUTINES_64;
- header.routines64.init_address = reader.Read32();
- header.routines64.init_module = reader.Read32();
- header.routines64.reserved1 = reader.Read32();
- header.routines64.reserved2 = reader.Read32();
- header.routines64.reserved3 = reader.Read32();
- header.routines64.reserved4 = reader.Read32();
- header.routines64.reserved5 = reader.Read32();
- header.routines64.reserved6 = reader.Read32();
- header.routinesPresent = true;
- break;
- case LC_ROUTINES_64:
- header.routines64.cmd = LC_ROUTINES_64;
- header.routines64.init_address = reader.Read64();
- header.routines64.init_module = reader.Read64();
- header.routines64.reserved1 = reader.Read64();
- header.routines64.reserved2 = reader.Read64();
- header.routines64.reserved3 = reader.Read64();
- header.routines64.reserved4 = reader.Read64();
- header.routines64.reserved5 = reader.Read64();
- header.routines64.reserved6 = reader.Read64();
- header.routinesPresent = true;
- break;
- case LC_FUNCTION_STARTS:
- header.functionStarts.funcoff = reader.Read32();
- header.functionStarts.funcsize = reader.Read32();
- header.functionStartsPresent = true;
- break;
- case LC_SYMTAB:
- header.symtab.symoff = reader.Read32();
- header.symtab.nsyms = reader.Read32();
- header.symtab.stroff = reader.Read32();
- header.symtab.strsize = reader.Read32();
- break;
- case LC_DYSYMTAB:
- header.dysymtab.ilocalsym = reader.Read32();
- header.dysymtab.nlocalsym = reader.Read32();
- header.dysymtab.iextdefsym = reader.Read32();
- header.dysymtab.nextdefsym = reader.Read32();
- header.dysymtab.iundefsym = reader.Read32();
- header.dysymtab.nundefsym = reader.Read32();
- header.dysymtab.tocoff = reader.Read32();
- header.dysymtab.ntoc = reader.Read32();
- header.dysymtab.modtaboff = reader.Read32();
- header.dysymtab.nmodtab = reader.Read32();
- header.dysymtab.extrefsymoff = reader.Read32();
- header.dysymtab.nextrefsyms = reader.Read32();
- header.dysymtab.indirectsymoff = reader.Read32();
- header.dysymtab.nindirectsyms = reader.Read32();
- header.dysymtab.extreloff = reader.Read32();
- header.dysymtab.nextrel = reader.Read32();
- header.dysymtab.locreloff = reader.Read32();
- header.dysymtab.nlocrel = reader.Read32();
- header.dysymPresent = true;
- break;
- case LC_DYLD_CHAINED_FIXUPS:
- header.chainedFixups.dataoff = reader.Read32();
- header.chainedFixups.datasize = reader.Read32();
- header.chainedFixupsPresent = true;
- break;
- case LC_DYLD_INFO:
- case LC_DYLD_INFO_ONLY:
- header.dyldInfo.rebase_off = reader.Read32();
- header.dyldInfo.rebase_size = reader.Read32();
- header.dyldInfo.bind_off = reader.Read32();
- header.dyldInfo.bind_size = reader.Read32();
- header.dyldInfo.weak_bind_off = reader.Read32();
- header.dyldInfo.weak_bind_size = reader.Read32();
- header.dyldInfo.lazy_bind_off = reader.Read32();
- header.dyldInfo.lazy_bind_size = reader.Read32();
- header.dyldInfo.export_off = reader.Read32();
- header.dyldInfo.export_size = reader.Read32();
- header.exportTrie.dataoff = header.dyldInfo.export_off;
- header.exportTrie.datasize = header.dyldInfo.export_size;
- header.exportTriePresent = true;
- header.dyldInfoPresent = true;
- break;
- case LC_DYLD_EXPORTS_TRIE:
- header.exportTrie.dataoff = reader.Read32();
- header.exportTrie.datasize = reader.Read32();
- header.exportTriePresent = true;
- break;
- case LC_THREAD:
- case LC_UNIXTHREAD:
- /*while (reader.GetOffset() < nextOffset)
- {
-
- thread_command thread;
- thread.flavor = reader.Read32();
- thread.count = reader.Read32();
- switch (m_archId)
- {
- case MachOx64:
- m_logger->LogDebug("x86_64 Thread state\n");
- if (thread.flavor != X86_THREAD_STATE64)
- {
- reader.SeekRelative(thread.count * sizeof(uint32_t));
- break;
- }
- //This wont be big endian so we can just read the whole thing
- reader.Read(&thread.statex64, sizeof(thread.statex64));
- header.entryPoints.push_back({thread.statex64.rip, false});
- break;
- case MachOx86:
- m_logger->LogDebug("x86 Thread state\n");
- if (thread.flavor != X86_THREAD_STATE32)
- {
- reader.SeekRelative(thread.count * sizeof(uint32_t));
- break;
- }
- //This wont be big endian so we can just read the whole thing
- reader.Read(&thread.statex86, sizeof(thread.statex86));
- header.entryPoints.push_back({thread.statex86.eip, false});
- break;
- case MachOArm:
- m_logger->LogDebug("Arm Thread state\n");
- if (thread.flavor != _ARM_THREAD_STATE)
- {
- reader.SeekRelative(thread.count * sizeof(uint32_t));
- break;
- }
- //This wont be big endian so we can just read the whole thing
- reader.Read(&thread.statearmv7, sizeof(thread.statearmv7));
- header.entryPoints.push_back({thread.statearmv7.r15, false});
- break;
- case MachOAarch64:
- case MachOAarch6432:
- m_logger->LogDebug("Aarch64 Thread state\n");
- if (thread.flavor != _ARM_THREAD_STATE64)
- {
- reader.SeekRelative(thread.count * sizeof(uint32_t));
- break;
- }
- reader.Read(&thread.stateaarch64, sizeof(thread.stateaarch64));
- header.entryPoints.push_back({thread.stateaarch64.pc, false});
- break;
- case MachOPPC:
- m_logger->LogDebug("PPC Thread state\n");
- if (thread.flavor != PPC_THREAD_STATE)
- {
- reader.SeekRelative(thread.count * sizeof(uint32_t));
- break;
- }
- //Read individual entries for endian reasons
- header.entryPoints.push_back({reader.Read32(), false});
- (void)reader.Read32();
- (void)reader.Read32();
- //Read the rest of the structure
- (void)reader.Read(&thread.stateppc.r1, sizeof(thread.stateppc) - (3 * 4));
- break;
- case MachOPPC64:
- m_logger->LogDebug("PPC64 Thread state\n");
- if (thread.flavor != PPC_THREAD_STATE64)
- {
- reader.SeekRelative(thread.count * sizeof(uint32_t));
- break;
- }
- header.entryPoints.push_back({reader.Read64(), false});
- (void)reader.Read64();
- (void)reader.Read64(); // Stack start
- (void)reader.Read(&thread.stateppc64.r1, sizeof(thread.stateppc64) - (3 * 8));
- break;
- default:
- m_logger->LogError("Unknown archid: %x", m_archId);
- }
-
- }*/
- break;
- case LC_LOAD_DYLIB:
- {
- uint32_t offset = reader.Read32();
- if (offset < nextOffset)
- {
- reader.Seek(curOffset + offset);
- std::string libname = reader.ReadCString(reader.GetOffset());
- header.dylibs.push_back(libname);
- }
- }
- break;
- case LC_BUILD_VERSION:
- {
- // m_logger->LogDebug("LC_BUILD_VERSION:");
- header.buildVersion.platform = reader.Read32();
- header.buildVersion.minos = reader.Read32();
- header.buildVersion.sdk = reader.Read32();
- header.buildVersion.ntools = reader.Read32();
- // m_logger->LogDebug("Platform: %s", BuildPlatformToString(header.buildVersion.platform).c_str());
- // m_logger->LogDebug("MinOS: %s", BuildToolVersionToString(header.buildVersion.minos).c_str());
- // m_logger->LogDebug("SDK: %s", BuildToolVersionToString(header.buildVersion.sdk).c_str());
- for (uint32_t j = 0; (i < header.buildVersion.ntools) && (j < 10); j++)
- {
- uint32_t tool = reader.Read32();
- uint32_t version = reader.Read32();
- header.buildToolVersions.push_back({tool, version});
- // m_logger->LogDebug("Build Tool: %s: %s", BuildToolToString(tool).c_str(),
- // BuildToolVersionToString(version).c_str());
- }
- break;
- }
- case LC_FILESET_ENTRY:
- {
- throw ReadException();
- }
- default:
- // m_logger->LogDebug("Unhandled command: %s : %" PRIu32 "\n", CommandToString(load.cmd).c_str(),
- // load.cmdsize);
- break;
- }
- if (reader.GetOffset() != nextOffset)
- {
- // m_logger->LogDebug("Didn't parse load command: %s fully %" PRIx64 ":%" PRIxPTR,
- // CommandToString(load.cmd).c_str(), reader.GetOffset(), nextOffset);
- }
- reader.Seek(nextOffset);
- }
-
- for (auto& section : header.sections)
- {
- char sectionName[17];
- memcpy(sectionName, section.sectname, sizeof(section.sectname));
- sectionName[16] = 0;
- char segmentName[17];
- memcpy(segmentName, section.segname, sizeof(section.segname));
- segmentName[16] = 0;
- if (header.identifierPrefix.empty())
- header.sectionNames.push_back(sectionName);
- else
- header.sectionNames.push_back(header.identifierPrefix + "::" + segmentName + ":" + sectionName);
- }
- }
- catch (ReadException&)
- {
- return {};
- }
-
- return header;
+ std::unique_lock<std::shared_mutex> lock(*m_namedSymMutex);
+ // Populate the named symbols from the regular symbols map.
+ m_namedSymbols.reserve(m_symbols.size());
+ for (const auto& [address, symbol] : m_symbols)
+ m_namedSymbols.emplace(symbol.name, address);
}
-bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const KernelCacheMachOHeader& header, const KernelCacheImage& targetImage)
+void KernelCache::ProcessRelocations(Ref<BinaryView> view, linkedit_data_command chained_fixup_command)
{
- // FIXME this uselessly loads chained fixups if an image is already loaded.
- auto logger = LogRegistry::GetLogger("KernelCache", view->GetFile()->GetSessionId());
-
- bool hasRegionsToLoad = false;
-
- auto reader = BinaryReader(view->GetParentView());
-
- reader.Seek(0x10);
- uint32_t ncmds = reader.Read32();
- uint64_t off = 0x20; // FIXME 32 bit
-
- uint64_t kernelBaseAddress = 0;
-
- uint32_t chainedFixupDataOff = 0;
- uint32_t chainedFixupDataSize = 0;
-
- while (ncmds--)
- {
- reader.Seek(off);
- uint32_t cmd = reader.Read32();
- uint32_t cmdsize = reader.Read32();
- if (cmd == LC_SEGMENT_64)
- {
- segment_command_64 segment {};
- reader.Read(&segment.segname, 16);
- if (strncmp(segment.segname, "__TEXT\0", 7) == 0)
- {
- kernelBaseAddress = reader.Read64();
- }
- }
- if (cmd == LC_DYLD_CHAINED_FIXUPS)
- {
- chainedFixupDataOff = reader.Read32();
- chainedFixupDataSize = reader.Read32();
- }
- off += cmdsize;
- }
-
- BNRelocationInfo reloc;
- memset(&reloc, 0, sizeof(BNRelocationInfo));
- reloc.type = StandardRelocationType;
- reloc.size = 8;
- reloc.nativeType = BINARYNINJA_MANUAL_RELOCATION;
- logger->LogDebug("Processing Chained Fixups");
-
- std::vector<std::pair<uint64_t, uint64_t>> relocations;
- if (chainedFixupDataOff && chainedFixupDataSize)
+ m_relocations.clear();
+ if (chained_fixup_command.dataoff && chained_fixup_command.datasize)
{
BinaryReader parentReader(view->GetParentView());
try {
dyld_chained_fixups_header fixupsHeader {};
- uint64_t fixupHeaderAddress = chainedFixupDataOff;
+ uint64_t fixupHeaderAddress = chained_fixup_command.dataoff;
parentReader.Seek(fixupHeaderAddress);
fixupsHeader.fixups_version = parentReader.Read32();
fixupsHeader.starts_offset = parentReader.Read32();
@@ -1105,11 +131,11 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel
fixupsHeader.imports_format = parentReader.Read32();
fixupsHeader.symbols_format = parentReader.Read32();
- logger->LogDebug("Chained Fixups: Header @ %llx // Fixups version %lx", fixupHeaderAddress, fixupsHeader.fixups_version);
+ LogDebug("Chained Fixups: Header @ %llx // Fixups version %lx", fixupHeaderAddress, fixupsHeader.fixups_version);
if (fixupsHeader.fixups_version > 0)
{
- logger->LogError("Chained Fixup parsing failed. Unknown Fixups Version");
+ LogError("Chained Fixup parsing failed. Unknown Fixups Version");
throw ReadException();
}
@@ -1178,14 +204,14 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel
break;
default:
{
- logger->LogError("Chained Fixups: Unknown or unsupported pointer format %d, "
+ LogError("Chained Fixups: Unknown or unsupported pointer format %d, "
"unable to process chains for segment at @llx", starts.pointer_format, starts.segment_offset);
continue;
}
}
uint16_t fmt = starts.pointer_format;
- logger->LogDebug("Chained Fixups: Segment start @ %llx, fmt %d", starts.segment_offset, fmt);
+ LogDebug("Chained Fixups: Segment start @ %llx, fmt %d", starts.segment_offset, fmt);
uint64_t pageStartsTableStartAddress = parentReader.GetOffset();
std::vector<std::vector<uint16_t>> pageStartOffsets {};
@@ -1273,8 +299,8 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel
bind = false;
}
- logger->LogTrace("Chained Fixups: @ 0x%llx ( 0x%llx ) - %d 0x%llx", chainEntryAddress,
- kernelBaseAddress + (chainEntryAddress),
+ LogTrace("Chained Fixups: @ 0x%llx ( 0x%llx ) - %d 0x%llx", chainEntryAddress,
+ view->GetStart() + (chainEntryAddress),
bind, nextEntryStrideCount);
if (!bind)
@@ -1293,7 +319,7 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel
entryOffset = pointer.arm64e.rebase.target;
if ( starts.pointer_format != DYLD_CHAINED_PTR_ARM64E || pointer.arm64e.bind.auth)
- entryOffset += kernelBaseAddress;
+ entryOffset += view->GetStart();
break;
}
@@ -1301,12 +327,12 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel
entryOffset = pointer.generic64.rebase.target;
break;
case DYLD_CHAINED_PTR_64_OFFSET:
- entryOffset = pointer.generic64.rebase.target + kernelBaseAddress;
+ entryOffset = pointer.generic64.rebase.target + view->GetStart();
break;
// We expect only cases past this point will be applicable in this context.
case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
- entryOffset = pointer.kernel64.target + kernelBaseAddress;
+ entryOffset = pointer.kernel64.target + view->GetStart();
break;
case DYLD_CHAINED_PTR_32:
case DYLD_CHAINED_PTR_32_CACHE:
@@ -1319,7 +345,7 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel
// logger->LogInfo("Chained Fixups: Pointer at 0x%llx -> 0x%llx", view->GetStart() + chainEntryAddress, entryOffset);
- relocations.emplace_back(kernelBaseAddress + chainEntryAddress, entryOffset);
+ m_relocations.emplace_back(view->GetStart() + chainEntryAddress, entryOffset);
}
chainEntryAddress += (nextEntryStrideCount * strideSize);
@@ -1328,8 +354,8 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel
{
// Something is seriously wrong here. likely malformed binary, or our parsing failed elsewhere.
// This will log the pointer in mapped memory.
- logger->LogError("Chained Fixups: Pointer at 0x%llx left page",
- kernelBaseAddress + ((chainEntryAddress - (nextEntryStrideCount * strideSize))));
+ LogError("Chained Fixups: Pointer at 0x%llx left page",
+ view->GetStart() + ((chainEntryAddress - (nextEntryStrideCount * strideSize))));
fixupsDone = true;
}
@@ -1342,1379 +368,59 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel
}
catch (ReadException&)
{
- logger->LogError("Chained Fixup parsing failed");
+ LogError("Chained Fixup parsing failed");
}
}
- // Critical that we sort these as this optimization allows us to save a lot of time on image loading when we
- // need to apply relocations later. Now is the best time, since we aren't going to find more relocs now.
- std::sort(relocations.begin(), relocations.end(),
+
+ std::sort(m_relocations.begin(), m_relocations.end(),
[](const std::pair<uint64_t, uint64_t>& a, const std::pair<uint64_t, uint64_t>& b) {
return a.first < b.first;
});
-
- for (auto& region : targetImage.regions)
- {
- if (view->IsValidOffset(region.start))
- {
- logger->LogDebug("Skipping region %s as it is already loaded.", region.prettyName.c_str());
- continue;
- }
-
- hasRegionsToLoad = true;
-
- // Considerations at this point in processing:
- // * View is already finalized
- // * view (shown to user) has only one segment, at the kernel start address
- // * All of our relevant data is contained in the Raw view.
- view->AddAutoSegment(region.start, region.size, region.fileOffset, region.size, region.flags);
-
- auto begin = std::lower_bound(relocations.begin(), relocations.end(), region.start,
- [](const std::pair<uint64_t, uint64_t>& reloc, uint64_t addr) {
- return reloc.first < addr;
- });
-
- auto arch = view->GetDefaultArchitecture();
- // Process relocations until the VM address is beyond our region
- for (auto it = begin; it != relocations.end() && it->first < region.start + region.size; ++it) {
- reloc.address = it->first;
- view->DefineRelocation(arch, reloc, it->second, reloc.address);
- }
- }
-
- if (!hasRegionsToLoad)
- {
- logger->LogWarn("No regions to load for image %s", header.installName.c_str());
- return false;
- }
-
- return true;
}
-void KernelCache::InitializeHeader(Ref<BinaryView> view, KernelCacheMachOHeader header)
+std::optional<CacheImage> KernelCache::GetImageAt(const uint64_t address) const
{
- Ref<Settings> settings = view->GetLoadSettings(KC_VIEW_NAME);
- bool applyFunctionStarts = true; // FIXME
-
- view->AddAutoSection(header.identifierPrefix + "::__macho_header", header.textBase, header.ident.sizeofcmds + sizeof(mach_header_64), ReadOnlyDataSectionSemantics);
-
- for (size_t i = 0; i < header.sections.size(); i++)
- {
- if (!header.sections[i].size)
- continue;
-
- std::string type;
- BNSectionSemantics semantics = DefaultSectionSemantics;
- switch (header.sections[i].flags & 0xff)
- {
- case S_REGULAR:
- if (header.sections[i].flags & S_ATTR_PURE_INSTRUCTIONS)
- {
- type = "PURE_CODE";
- semantics = ReadOnlyCodeSectionSemantics;
- }
- else if (header.sections[i].flags & S_ATTR_SOME_INSTRUCTIONS)
- {
- type = "CODE";
- semantics = ReadOnlyCodeSectionSemantics;
- }
- else
- {
- type = "REGULAR";
- }
- break;
- case S_ZEROFILL:
- type = "ZEROFILL";
- semantics = ReadWriteDataSectionSemantics;
- break;
- case S_CSTRING_LITERALS:
- type = "CSTRING_LITERALS";
- semantics = ReadOnlyDataSectionSemantics;
- break;
- case S_4BYTE_LITERALS:
- type = "4BYTE_LITERALS";
- break;
- case S_8BYTE_LITERALS:
- type = "8BYTE_LITERALS";
- break;
- case S_LITERAL_POINTERS:
- type = "LITERAL_POINTERS";
- semantics = ReadOnlyDataSectionSemantics;
- break;
- case S_NON_LAZY_SYMBOL_POINTERS:
- type = "NON_LAZY_SYMBOL_POINTERS";
- semantics = ReadOnlyDataSectionSemantics;
- break;
- case S_LAZY_SYMBOL_POINTERS:
- type = "LAZY_SYMBOL_POINTERS";
- semantics = ReadOnlyDataSectionSemantics;
- break;
- case S_SYMBOL_STUBS:
- type = "SYMBOL_STUBS";
- semantics = ReadOnlyCodeSectionSemantics;
- break;
- case S_MOD_INIT_FUNC_POINTERS:
- type = "MOD_INIT_FUNC_POINTERS";
- semantics = ReadOnlyDataSectionSemantics;
- break;
- case S_MOD_TERM_FUNC_POINTERS:
- type = "MOD_TERM_FUNC_POINTERS";
- semantics = ReadOnlyDataSectionSemantics;
- break;
- case S_COALESCED:
- type = "COALESCED";
- break;
- case S_GB_ZEROFILL:
- type = "GB_ZEROFILL";
- semantics = ReadWriteDataSectionSemantics;
- break;
- case S_INTERPOSING:
- type = "INTERPOSING";
- break;
- case S_16BYTE_LITERALS:
- type = "16BYTE_LITERALS";
- break;
- case S_DTRACE_DOF:
- type = "DTRACE_DOF";
- break;
- case S_LAZY_DYLIB_SYMBOL_POINTERS:
- type = "LAZY_DYLIB_SYMBOL_POINTERS";
- semantics = ReadOnlyDataSectionSemantics;
- break;
- case S_THREAD_LOCAL_REGULAR:
- type = "THREAD_LOCAL_REGULAR";
- break;
- case S_THREAD_LOCAL_ZEROFILL:
- type = "THREAD_LOCAL_ZEROFILL";
- break;
- case S_THREAD_LOCAL_VARIABLES:
- type = "THREAD_LOCAL_VARIABLES";
- break;
- case S_THREAD_LOCAL_VARIABLE_POINTERS:
- type = "THREAD_LOCAL_VARIABLE_POINTERS";
- break;
- case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS:
- type = "THREAD_LOCAL_INIT_FUNCTION_POINTERS";
- break;
- default:
- type = "UNKNOWN";
- break;
- }
- if (i >= header.sectionNames.size())
- break;
- if (strncmp(header.sections[i].sectname, "__text", sizeof(header.sections[i].sectname)) == 0)
- semantics = ReadOnlyCodeSectionSemantics;
- if (strncmp(header.sections[i].sectname, "__const", sizeof(header.sections[i].sectname)) == 0)
- semantics = ReadOnlyDataSectionSemantics;
- if (strncmp(header.sections[i].sectname, "__data", sizeof(header.sections[i].sectname)) == 0)
- semantics = ReadWriteDataSectionSemantics;
- if (strncmp(header.sections[i].segname, "__DATA_CONST", sizeof(header.sections[i].segname)) == 0)
- semantics = ReadOnlyDataSectionSemantics;
-
- view->AddAutoSection(header.sectionNames[i], header.sections[i].addr, header.sections[i].size, semantics,
- type, header.sections[i].align);
- }
-
- auto typeLib = view->GetTypeLibrary(header.installName);
-
- BinaryReader virtualReader(view->GetParentView());
-
- view->DefineDataVariable(header.textBase, Type::NamedType(view, QualifiedName("mach_header_64")));
- view->DefineAutoSymbol(
- new Symbol(DataSymbol, "__macho_header::" + header.identifierPrefix, header.textBase, LocalBinding));
-
- try
- {
- virtualReader.Seek(header.textBaseFileOffset + sizeof(mach_header_64));
- size_t sectionNum = 0;
- uint64_t curVirtualOffset = header.textBase + sizeof(mach_header_64);
- uint64_t curFileOffset = header.textBaseFileOffset + sizeof(mach_header_64);
- for (size_t i = 0; i < header.ident.ncmds; i++)
- {
- load_command load;
- curFileOffset = virtualReader.GetOffset();
- load.cmd = virtualReader.Read32();
- load.cmdsize = virtualReader.Read32();
- uint64_t nextOffset = curFileOffset + load.cmdsize;
- switch (load.cmd)
- {
- case LC_SEGMENT:
- {
- view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("segment_command")));
- virtualReader.SeekRelative(5 * 8);
- size_t numSections = virtualReader.Read32();
- virtualReader.SeekRelative(4);
- for (size_t j = 0; j < numSections; j++)
- {
- view->DefineDataVariable(
- virtualReader.GetOffset(), Type::NamedType(view, QualifiedName("section")));
- view->DefineAutoSymbol(new Symbol(DataSymbol,
- "__macho_section::" + header.identifierPrefix + "_[" + std::to_string(sectionNum++) + "]",
- virtualReader.GetOffset(), LocalBinding));
- virtualReader.SeekRelative((8 * 8) + 4);
- }
- break;
- }
- case LC_SEGMENT_64:
- {
- view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("segment_command_64")));
- virtualReader.SeekRelative(7 * 8);
- size_t numSections = virtualReader.Read32();
- virtualReader.SeekRelative(4);
- for (size_t j = 0; j < numSections; j++)
- {
- view->DefineDataVariable(
- virtualReader.GetOffset(), Type::NamedType(view, QualifiedName("section_64")));
- view->DefineAutoSymbol(new Symbol(DataSymbol,
- "__macho_section_64::" + header.identifierPrefix + "_[" + std::to_string(sectionNum++) + "]",
- virtualReader.GetOffset(), LocalBinding));
- virtualReader.SeekRelative(10 * 8);
- }
- break;
- }
- case LC_SYMTAB:
- view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("symtab")));
- break;
- case LC_DYSYMTAB:
- view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("dysymtab")));
- break;
- case LC_UUID:
- view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, 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:
- view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("dylib_command")));
- if (load.cmdsize - 24 <= 150)
- view->DefineDataVariable(
- curVirtualOffset + 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:
- view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("linkedit_data")));
- break;
- case LC_ENCRYPTION_INFO:
- view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("encryption_info")));
- break;
- case LC_VERSION_MIN_MACOSX:
- case LC_VERSION_MIN_IPHONEOS:
- view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("version_min")));
- break;
- case LC_DYLD_INFO:
- case LC_DYLD_INFO_ONLY:
- view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("dyld_info")));
- break;
- default:
- view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("load_command")));
- break;
- }
-
- view->DefineAutoSymbol(new Symbol(DataSymbol,
- "__macho_load_command::" + header.identifierPrefix + "_[" + std::to_string(i) + "]", curVirtualOffset,
- LocalBinding));
- curVirtualOffset += (nextOffset - curFileOffset);
- virtualReader.Seek(nextOffset);
- }
- }
- catch (ReadException&)
- {
- LogError("Error when applying Mach-O header types at %" PRIx64, header.textBase);
- }
-
- if (applyFunctionStarts && header.functionStartsPresent && header.linkeditPresent)
- {
- size_t i = 0;
- uint64_t curfunc = header.textBase;
- uint64_t curOffset;
-
- auto funcStarts = view->GetParentView()->ReadBuffer(header.functionStarts.funcoff, header.functionStarts.funcsize);
-
- while (i < header.functionStarts.funcsize)
- {
- curOffset = readLEB128(funcStarts, header.functionStarts.funcsize, i);
- curfunc += curOffset;
- // LogError("0x%llx, 0x%llx", header.textBase, curOffset);
- if (curOffset == 0)
- continue;
- uint64_t target = curfunc;
- Ref<Platform> targetPlatform = view->GetDefaultPlatform();
- view->AddFunctionForAnalysis(targetPlatform, target);
- }
- }
-
- view->BeginBulkModifySymbols();
- ParseSymbolTable(view, header);
-
- BinaryReader reader(view);
-
- size_t modInitFuncCnt = 0;
- for (const auto& moduleInitSection : header.moduleInitSections)
- {
- // The mod_init section contains a list of function pointers called at initialization
- // if we don't have a defined entrypoint then use the first one in the list as the entrypoint
- size_t i = 0;
- reader.Seek(moduleInitSection.addr);
- for (; i < (moduleInitSection.size / view->GetAddressSize()); i++)
- {
- uint64_t target = (view->GetAddressSize() == 4) ? reader.Read32() : reader.Read64();
- if (!view->IsValidOffset(target))
- continue;
- Ref<Platform> targetPlatform = view->GetDefaultPlatform()->GetAssociatedPlatformByAddress(target);
- auto name = header.identifierPrefix + "_init_func_" + std::to_string(modInitFuncCnt++);
- view->AddEntryPointForAnalysis(targetPlatform, target);
- auto symbol = new Symbol(FunctionSymbol, name, target, GlobalBinding);
- view->DefineAutoSymbol(symbol);
- }
- }
-
- view->EndBulkModifySymbols();
-}
-
-
-std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> KernelCache::ParseSymbolTable(Ref<BinaryView> view, KernelCacheMachOHeader header, bool defineSymbolsInView)
-{
- if (header.symtab.symoff != 0 && header.linkeditPresent)
- {
- // Mach-O View symtab processing with
- // a ton of stuff cut out so it can work
- // auto symtab = reader->ReadBuffer(header.symtab.symoff, header.symtab.nsyms * sizeof(nlist_64));
- auto strtab = view->GetParentView()->ReadBuffer(header.symtab.stroff, header.symtab.strsize);
- nlist_64 sym;
- memset(&sym, 0, sizeof(sym));
- std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> symbolInfos;
- for (size_t i = 0; i < header.symtab.nsyms; i++)
- {
- view->GetParentView()->Read(&sym, header.symtab.symoff + i * sizeof(nlist_64), sizeof(nlist_64));
- if (sym.n_strx >= header.symtab.strsize || ((sym.n_type & N_TYPE) == N_INDR))
- continue;
-
- std::string symbol((char*)strtab.GetDataAt(sym.n_strx));
- // BNLogError("%s: 0x%llx", symbol.c_str(), sym.n_value);
- if (symbol == "<redacted>")
- continue;
-
- BNSymbolType type = DataSymbol;
- uint32_t flags;
- if ((sym.n_type & N_TYPE) == N_SECT && sym.n_sect > 0 && (size_t)(sym.n_sect - 1) < header.sections.size())
- {}
- else if ((sym.n_type & N_TYPE) == N_ABS)
- {}
- else if ((sym.n_type & 0x1))
- {
- type = ExternalSymbol;
- }
- else
- continue;
-
- for (auto s : header.sections)
- {
- if (s.addr < sym.n_value)
- {
- if (s.addr + s.size > sym.n_value)
- {
- flags = s.flags;
- }
- }
- }
-
- if (type != ExternalSymbol)
- {
- if ((flags & S_ATTR_PURE_INSTRUCTIONS) == S_ATTR_PURE_INSTRUCTIONS
- || (flags & S_ATTR_SOME_INSTRUCTIONS) == S_ATTR_SOME_INSTRUCTIONS)
- type = FunctionSymbol;
- else
- type = DataSymbol;
- }
- if ((sym.n_desc & N_ARM_THUMB_DEF) == N_ARM_THUMB_DEF)
- sym.n_value++;
-
- std::string rawName = symbol;
- std::string shortName = symbol;
- std::string fullName = symbol;
- Ref<Type> typeRef = nullptr;
-
- if (view->GetDefaultArchitecture())
- {
- QualifiedName demangledName;
- Ref<Type> demangledType;
- bool simplify = Settings::Instance()->Get<bool>("analysis.types.templateSimplifier", view);
- if (DemangleGeneric(view->GetDefaultArchitecture(), rawName, demangledType, demangledName, view, simplify))
- {
- shortName = demangledName.GetString();
- fullName = shortName;
- if (demangledType)
- fullName += demangledType->GetStringAfterName();
- if (!typeRef && !view->GetDefaultPlatform()->GetFunctionByName(rawName))
- typeRef = demangledType;
- }
- else
- {
- LogDebug("Failed to demangle name: '%s'\n", rawName.c_str());
- }
- }
-
- if (defineSymbolsInView)
- {
- auto symbolObj = new Symbol(type, shortName, fullName, rawName, sym.n_value, GlobalBinding);
-
- if (typeRef)
- view->DefineAutoSymbolAndVariableOrFunction(view->GetDefaultPlatform(), symbolObj, typeRef);
- else
- {
- view->DefineAutoSymbol(symbolObj);
- if (type == FunctionSymbol)
- {
- Ref<Platform> targetPlatform = view->GetDefaultPlatform();
- view->AddFunctionForAnalysis(targetPlatform, sym.n_value);
- }
- }
- }
- symbolInfos.push_back({sym.n_value, {type, fullName}});
- }
- return symbolInfos;
- }
- return {};
-}
-
-
-struct ExportNode
-{
- std::string text;
- uint64_t offset;
- uint64_t flags;
-};
-
-
-void KernelCache::ReadExportNode(Ref<BinaryView> view, std::vector<Ref<Symbol>>& symbolList, KernelCacheMachOHeader& header, DataBuffer& buffer, uint64_t textBase,
- const std::string& currentText, size_t cursor, uint32_t endGuard)
-{
-
- if (cursor > endGuard)
- throw ReadException();
-
- uint64_t terminalSize = readValidULEB128(buffer, cursor);
- uint64_t childOffset = cursor + terminalSize;
- if (terminalSize != 0) {
- uint64_t imageOffset = 0;
- uint64_t flags = readValidULEB128(buffer, cursor);
- if (!(flags & EXPORT_SYMBOL_FLAGS_REEXPORT))
- {
- imageOffset = readValidULEB128(buffer, cursor);
- // auto symbolType = view->GetAnalysisFunctionsForAddress(textBase + imageOffset).size() ? FunctionSymbol : DataSymbol;
- {
- if (!currentText.empty() && textBase + imageOffset)
- {
- uint32_t sectionFlags;
- BNSymbolType type;
- for (auto s : header.sections)
- {
- if (s.addr < textBase + imageOffset)
- {
- if (s.addr + s.size > textBase + imageOffset)
- {
- sectionFlags = s.flags;
- }
- }
- }
- if ((sectionFlags & S_ATTR_PURE_INSTRUCTIONS) == S_ATTR_PURE_INSTRUCTIONS
- || (sectionFlags & S_ATTR_SOME_INSTRUCTIONS) == S_ATTR_SOME_INSTRUCTIONS)
- type = FunctionSymbol;
- else
- type = DataSymbol;
-
-#if EXPORT_TRIE_DEBUG
- // BNLogInfo("export: %s -> 0x%llx", n.text.c_str(), image.baseAddress + n.offset);
-#endif
- auto sym = new Symbol(type, currentText, textBase + imageOffset);
- symbolList.push_back(sym);
- }
- }
- }
- }
- cursor = childOffset;
- uint8_t childCount = buffer[cursor];
- cursor++;
- if (cursor > endGuard)
- throw ReadException();
- for (uint8_t i = 0; i < childCount; ++i)
- {
- std::string childText;
- while (buffer[cursor] != 0 & cursor <= endGuard)
- childText.push_back(buffer[cursor++]);
- cursor++;
- if (cursor > endGuard)
- throw ReadException();
- auto next = readValidULEB128(buffer, cursor);
- if (next == 0)
- throw ReadException();
- ReadExportNode(view, symbolList, header, buffer, textBase, currentText + childText, next, endGuard);
- }
-}
-
-
-std::vector<Ref<Symbol>> KernelCache::ParseExportTrie(Ref<BinaryView> view, KernelCacheMachOHeader header)
-{
- if (!header.exportTriePresent || !header.exportTrie.dataoff || !header.exportTrie.datasize)
- return {};
-
- std::vector<Ref<Symbol>> symbols;
- try
- {
- std::vector<ExportNode> nodes;
-
- DataBuffer buffer = view->GetParentView()->ReadBuffer(header.exportTrie.dataoff, header.exportTrie.datasize);
- ReadExportNode(view, symbols, header, buffer, header.textBase, "", 0, header.exportTrie.datasize);
- }
- catch (std::exception& e)
- {
- BNLogError("Failed to load Export Trie");
- }
- return symbols;
-}
-
-std::vector<std::string> KernelCache::GetAvailableImages()
-{
- std::vector<std::string> installNames;
- for (const auto& img : m_cacheInfo->images)
- {
- installNames.push_back(img.installName);
- }
- return installNames;
-}
-
-
-std::vector<KernelCacheImage> KernelCache::GetLoadedImages()
-{
- std::lock_guard lock(m_viewSpecificState->stateMutex);
- std::vector<KernelCacheImage> images;
- for (const auto& [fileStart, image] : m_viewSpecificState->state.loadedImages)
- {
- images.push_back(image);
- }
- return images;
-}
-
-
-bool KernelCache::IsImageLoaded(uint64_t address)
-{
- std::lock_guard lock(m_viewSpecificState->stateMutex);
- return m_viewSpecificState->state.loadedImages.find(address)!= m_viewSpecificState->state.loadedImages.end();
-}
-
-
-std::vector<std::pair<uint64_t, std::pair<std::string, std::string>>> KernelCache::LoadAllSymbolsAndWait()
-{
- std::vector<std::pair<uint64_t, std::pair<std::string, std::string>>> symbols;
- for (const auto& img : m_cacheInfo->images)
- {
- auto header = HeaderForFileAddress(img.headerFileLocation);
- if (header)
- {
- auto newSymbolList = ParseSymbolTable(m_kcView, *header, false);
- for (const auto& symbol : newSymbolList)
- {
- if (symbol.first != 0)
- symbols.push_back({symbol.first, {header->installName, symbol.second.second}});
- }
- }
- }
- return symbols;
-}
-
-
-std::string KernelCache::SerializedImageHeaderForVMAddress(uint64_t address)
-{
- auto header = HeaderForVMAddress(address);
- if (header)
- {
- return header->AsString();
- }
- return "";
-}
-
-
-std::string KernelCache::SerializedImageHeaderForName(std::string name)
-{
- if (auto it = m_cacheInfo->imageStarts.find(name); it != m_cacheInfo->imageStarts.end())
- {
- if (auto header = HeaderForFileAddress(it->second))
- {
- return header->AsString();
- }
- }
- return "";
-}
-
-
-
-bool KernelCache::SaveCacheInfoToKCView(std::lock_guard<std::mutex>&)
-{
- if (!m_cacheInfo)
- return false;
-
- // The initial load should only populate `m_cacheInfo` and should not modify any state.
- assert(m_modifiedState->loadedImages.size() == 0);
-
- auto data = m_cacheInfo->AsMetadata();
- m_kcView->StoreMetadata(KernelCacheMetadata::Tag, data);
- m_kcView->GetParentView()->StoreMetadata(KernelCacheMetadata::Tag, data);
-
- {
- std::lock_guard lock(m_viewSpecificState->cacheInfoMutex);
- if (m_cacheInfo && !m_viewSpecificState->cacheInfo)
- m_viewSpecificState->cacheInfo = m_cacheInfo;
- else if (m_cacheInfo != m_viewSpecificState->cacheInfo)
- abort();
- }
-
- m_metadataValid = true;
- return true;
-}
-
-
-bool KernelCache::SaveModifiedStateToKCView(std::lock_guard<std::mutex>&)
-{
- if (!m_kcView)
- return false;
-
- {
- std::lock_guard lock(m_viewSpecificState->stateMutex);
-
- uint64_t modificationNumber = m_viewSpecificState->savedModifications++;
- if (modificationNumber == 0)
- {
- // The cached state in the view-specific state has not yet been saved.
- // For the initial load of a shared cache this will be empty, but if
- // the shared cache has been loaded from a database then this will
- // contain the full state that was saved.
- std::string metadataKey = KernelCacheMetadata::ModifiedStateTagPrefix + std::to_string(modificationNumber);
- auto data = m_viewSpecificState->state.AsMetadata(m_viewSpecificState->viewState);
-
- m_kcView->StoreMetadata(metadataKey, data);
- m_kcView->GetParentView()->StoreMetadata(metadataKey, data);
- modificationNumber = m_viewSpecificState->savedModifications++;
- }
-
- std::string metadataKey = KernelCacheMetadata::ModifiedStateTagPrefix + std::to_string(modificationNumber);
- auto data = m_modifiedState->AsMetadata();
-
- m_kcView->StoreMetadata(metadataKey, data);
- m_kcView->GetParentView()->StoreMetadata(metadataKey, data);
-
- Ref<Metadata> count = new Metadata(m_viewSpecificState->savedModifications);
- m_kcView->StoreMetadata(KernelCacheMetadata::ModifiedStateCountTag, count);
- m_kcView->GetParentView()->StoreMetadata(KernelCacheMetadata::ModifiedStateCountTag, count);
-
- m_viewSpecificState->state.loadedImages.merge(m_modifiedState->loadedImages);
- // `merge` will move a node to the target map if the corresponding key does not yet exist.
- // If we've redundantly loaded images, we may be left with symbols in the source maps.
- m_modifiedState->loadedImages.clear();
-
- // Clean up any metadata entries past the current modification number.
- // These can happen after being loaded from a database as all modifications are
- // merged into a single state object and the modification count is reset to zero.
- for (size_t i = modificationNumber + 1; i < std::numeric_limits<size_t>::max(); ++i)
- {
- std::string modifiedStateMetadataKey = KernelCacheMetadata::ModifiedStateTagPrefix + std::to_string(i);
- bool done = true;
- if (m_kcView->QueryMetadata(modifiedStateMetadataKey))
- {
- done = false;
- m_kcView->RemoveMetadata(modifiedStateMetadataKey);
- }
- if (m_kcView->GetParentView()->QueryMetadata(modifiedStateMetadataKey))
- {
- done = false;
- m_kcView->GetParentView()->RemoveMetadata(modifiedStateMetadataKey);
- }
- if (done)
- break;
- }
- }
-
- if (m_modifiedState->viewState)
- {
- m_viewSpecificState->viewState = m_modifiedState->viewState.value();
- m_modifiedState->viewState = std::nullopt;
- }
-
- m_metadataValid = true;
-
- return true;
-}
-
-
-const std::string KernelCacheMetadata::Tag = "KERNELCACHE-KernelCacheData";
-const std::string KernelCacheMetadata::CacheInfoTag = "KERNELCACHE-CacheInfo";
-const std::string KernelCacheMetadata::ModifiedStateTagPrefix = "KERNELCACHE-ModifiedState-";
-const std::string KernelCacheMetadata::ModifiedStateCountTag = "KERNELCACHE-ModifiedState-Count";
-
-KernelCacheMetadata::~KernelCacheMetadata() = default;
-KernelCacheMetadata::KernelCacheMetadata(KernelCacheMetadata&&) = default;
-KernelCacheMetadata& KernelCacheMetadata::operator=(KernelCacheMetadata&&) = default;
-
-KernelCacheMetadata::KernelCacheMetadata(KernelCache::CacheInfo cacheInfo, KernelCache::ModifiedState state) :
- cacheInfo(std::make_unique<KernelCache::CacheInfo>(std::move(cacheInfo))),
- state(std::make_unique<KernelCache::ModifiedState>(std::move(state)))
-{}
-
-
-// static
-bool KernelCacheMetadata::ViewHasMetadata(BinaryView* view)
-{
- return view->QueryMetadata(Tag);
-}
-
-// static
-std::optional<KernelCacheMetadata> KernelCacheMetadata::LoadFromView(BinaryView* view)
-{
- Ref<Metadata> viewMetadata = view->QueryMetadata(Tag);
- if (!viewMetadata)
+ const auto it = m_images.find(address);
+ if (it == m_images.end())
return std::nullopt;
-
- auto cacheInfo = KernelCache::CacheInfo::LoadFromString(viewMetadata->GetString());
- if (!cacheInfo)
- return std::nullopt;
-
- auto modifiedState = KernelCache::ModifiedState::LoadAll(view, *cacheInfo);
- return KernelCacheMetadata(std::move(*cacheInfo), std::move(modifiedState));
+ return it->second;
}
-std::string KernelCacheMetadata::InstallNameForImageBaseAddress(uint64_t baseAddress) const
+std::optional<CacheImage> KernelCache::GetImageContaining(const uint64_t address) const
{
- auto it = std::find_if(cacheInfo->imageStarts.begin(), cacheInfo->imageStarts.end(), [=](auto& pair) {
- return pair.second == baseAddress;
- });
-
- if (it == cacheInfo->imageStarts.end())
- return "";
-
- return it->first;
-}
-
-std::vector<KernelCacheImage> KernelCacheMetadata::LoadedImages()
-{
- std::vector<KernelCacheImage> images;
- for (const auto& image : state->loadedImages)
+ for (const auto& [startAddress, image] : m_images)
{
- images.push_back(image.second);
- }
- return images;
-}
-
-}
-
-extern "C"
-{
- BNKernelCache* BNGetKernelCache(BNBinaryView* data)
- {
- if (!data)
- return nullptr;
-
- Ref<BinaryView> view = new BinaryView(BNNewViewReference(data));
- if (auto cache = KernelCache::GetFromKCView(view))
+ for (const auto& region : image.regions)
{
- cache->AddAPIRef();
- return cache->GetAPIObject();
+ if (region.AsAddressRange().start <= address && address < region.AsAddressRange().end)
+ return image;
}
-
- return nullptr;
- }
-
- BNKernelCache* BNNewKernelCacheReference(BNKernelCache* cache)
- {
- if (!cache->object)
- return nullptr;
-
- cache->object->AddAPIRef();
- return cache;
- }
-
- void BNFreeKernelCacheReference(BNKernelCache* cache)
- {
- if (!cache->object)
- return;
-
- cache->object->ReleaseAPIRef();
- }
-
- bool BNKCViewLoadImageWithInstallName(BNKernelCache* cache, const char* name)
- {
- std::string imageName = std::string(name);
- if (cache->object)
- return cache->object->LoadImageWithInstallName(imageName);
-
- return false;
- }
-
- bool BNKCViewLoadImageContainingAddress(BNKernelCache* cache, uint64_t address)
- {
- if (cache->object)
- {
- return cache->object->LoadImageContainingAddress(address);
- }
-
- return false;
- }
-
- bool BNKCViewIsImageLoaded(BNKernelCache* cache, uint64_t address)
- {
- if (cache->object)
- return cache->object->IsImageLoaded(address);
-
- return false;
- }
-
- char** BNKCViewGetInstallNames(BNKernelCache* cache, size_t* count)
- {
- if (cache->object)
- {
- auto value = cache->object->GetAvailableImages();
- *count = value.size();
-
- std::vector<const char*> cstrings;
- for (size_t i = 0; i < value.size(); i++)
- {
- cstrings.push_back(value[i].c_str());
- }
- return BNAllocStringList(cstrings.data(), cstrings.size());
- }
- *count = 0;
- return nullptr;
- }
-
- BNKCSymbolRep* BNKCViewLoadAllSymbolsAndWait(BNKernelCache* cache, size_t* count)
- {
- if (cache->object)
- {
- auto value = cache->object->LoadAllSymbolsAndWait();
- *count = value.size();
-
- BNKCSymbolRep* symbols = (BNKCSymbolRep*)malloc(sizeof(BNKCSymbolRep) * value.size());
- for (size_t i = 0; i < value.size(); i++)
- {
- symbols[i].address = value[i].first;
- symbols[i].name = BNAllocString(value[i].second.second.c_str());
- symbols[i].image = BNAllocString(value[i].second.first.c_str());
- }
- return symbols;
- }
- *count = 0;
- return nullptr;
- }
-
- void BNKCViewFreeSymbols(BNKCSymbolRep* symbols, size_t count)
- {
- for (size_t i = 0; i < count; i++)
- {
- BNFreeString(symbols[i].name);
- BNFreeString(symbols[i].image);
- }
- delete symbols;
- }
-
- char* BNKCViewGetNameForAddress(BNKernelCache* cache, uint64_t address)
- {
- if (cache->object)
- {
- return BNAllocString(cache->object->NameForAddress(address).c_str());
- }
-
- return nullptr;
- }
-
- char* BNKCViewGetImageNameForAddress(BNKernelCache* cache, uint64_t address)
- {
- if (cache->object)
- {
- return BNAllocString(cache->object->ImageNameForAddress(address).c_str());
- }
-
- return nullptr;
- }
-
- uint64_t BNKCViewLoadedImageCount(BNKernelCache* cache)
- {
- // FIXME?
- return 0;
- }
-
- BNKCViewState BNKCViewGetState(BNKernelCache* cache)
- {
- if (cache->object)
- {
- return (BNKCViewState)cache->object->ViewState();
- }
-
- return BNKCViewState::Unloaded;
- }
-
- void BNKCViewFreeLoadedRegions(BNKCMappedMemoryRegion* images, size_t count)
- {
- for (size_t i = 0; i < count; i++)
- {
- BNFreeString(images[i].name);
- }
- delete images;
- }
-
- BNKCImage* BNKCViewGetAllImages(BNKernelCache* cache, size_t* count)
- {
- if (cache->object)
- {
- auto viewImageHeaders = cache->object->AllImageHeaders();
- *count = viewImageHeaders.size();
- BNKCImage* images = (BNKCImage*)malloc(sizeof(BNKCImage) * viewImageHeaders.size());
- size_t i = 0;
- for (const auto& [baseAddress, header] : viewImageHeaders)
- {
- images[i].name = BNAllocString(header.installName.c_str());
- images[i].headerFileAddress = baseAddress;
- images[i].mappingCount = header.sections.size();
- images[i].mappings = (BNKCImageMemoryMapping*)malloc(sizeof(BNKCImageMemoryMapping) * header.sections.size());
- for (size_t j = 0; j < header.sections.size(); j++)
- {
- images[i].mappings[j].rawViewOffset = header.sections[j].offset;
- images[i].mappings[j].vmAddress = header.sections[j].addr;
- images[i].mappings[j].size = header.sections[j].size;
- images[i].mappings[j].name = BNAllocString(header.sectionNames[j].c_str());
- }
- i++;
- }
- return images;
- }
- *count = 0;
- return nullptr;
- }
-
- void BNKCViewFreeAllImages(BNKCImage* images, size_t count)
- {
- for (size_t i = 0; i < count; i++)
- {
- for (size_t j = 0; j < images[i].mappingCount; j++)
- {
- BNFreeString(images[i].mappings[j].name);
- }
- delete[] images[i].mappings;
- BNFreeString(images[i].name);
- }
- delete[] images;
- }
-
- char* BNKCViewGetImageHeaderForAddress(BNKernelCache* cache, uint64_t address)
- {
- if (cache->object)
- {
- auto header = cache->object->SerializedImageHeaderForVMAddress(address);
- return BNAllocString(header.c_str());
- }
-
- return nullptr;
- }
-
- char* BNKCViewGetImageHeaderForName(BNKernelCache* cache, const char* name)
- {
- std::string imageName = std::string(name);
- if (cache->object)
- {
- auto header = cache->object->SerializedImageHeaderForName(imageName);
- return BNAllocString(header.c_str());
- }
-
- return nullptr;
- }
-
- BNKCViewLoadProgress BNKCViewGetLoadProgress(uint64_t sessionID)
- {
- if (auto viewSpecificState = ViewSpecificStateForId(sessionID, false))
- return viewSpecificState->progress;
-
- return LoadProgressNotStarted;
- }
-
- uint64_t BNKCViewFastGetImageCount(BNBinaryView* data)
- {
- Ref<BinaryView> view = new BinaryView(BNNewViewReference(data));
- if (view)
- return KernelCache::FastGetImageCount(view);
- return 0;
- }
-
- BNKCImage* BNKCViewGetLoadedImages(BNKernelCache* cache, size_t* count)
- {
- if (cache->object)
- {
- auto images = cache->object->GetLoadedImages();
- *count = images.size();
- BNKCImage* bnImages = new BNKCImage[images.size()];
- for (size_t i = 0; i < images.size(); i++)
- {
- bnImages[i].name = BNAllocString(images[i].installName.c_str());
- bnImages[i].headerFileAddress = images[i].headerFileLocation;
- bnImages[i].mappingCount = images[i].regions.size();
- bnImages[i].mappings = new BNKCImageMemoryMapping[images[i].regions.size()];
- for (size_t j = 0; j < images[i].regions.size(); j++)
- {
- bnImages[i].mappings[j].rawViewOffset = images[i].regions[j].fileOffset;
- bnImages[i].mappings[j].vmAddress = images[i].regions[j].start;
- bnImages[i].mappings[j].size = images[i].regions[j].size;
- bnImages[i].mappings[j].name = BNAllocString(images[i].regions[j].prettyName.c_str());
- }
- }
- return bnImages;
- }
- *count = 0;
- return nullptr;
- }
-
- void BNKCViewFreeLoadedImages(BNKCImage* images, size_t count)
- {
- for (size_t i = 0; i < count; i++)
- {
- for (size_t j = 0; j < images[i].mappingCount; j++)
- {
- BNFreeString(images[i].mappings[j].name);
- }
- delete[] images[i].mappings;
- BNFreeString(images[i].name);
- }
- delete[] images;
}
+ return std::nullopt;
}
-#ifdef __cplusplus
-extern "C" {
-#endif
- void RegisterTransformers();
-
-#ifdef __cplusplus
-}
-#endif
-
-void InitKernelcache()
-{
- InitKCViewType();
- RegisterTransformers();
-}
-
-namespace KernelCacheCore {
-
-void Deserialize(
- DeserializationContext& context, std::string_view name, std::optional<std::pair<uint64_t, uint64_t>>& value)
-{
- if (!context.doc.HasMember(name.data()))
- {
- value = std::nullopt;
- return;
- }
-
- auto array = context.doc[name.data()].GetArray();
- value = {array[0].GetUint64(), array[1].GetUint64()};
-}
-
-void Serialize(SerializationContext& context, const Ref<Symbol>& value)
+std::optional<CacheImage> KernelCache::GetImageWithName(const std::string& name) const
{
- context.writer.StartArray();
- Serialize(context, value->GetRawNameRef());
- Serialize(context, value->GetAddress());
- Serialize(context, value->GetType());
- context.writer.EndArray();
+ for (const auto& [address, image] : m_images)
+ if (image.path == name)
+ return image;
+ return std::nullopt;
}
-void Serialize(SerializationContext& context, const std::shared_ptr<std::unordered_map<uint64_t, Ref<Symbol>>>& value)
+std::optional<CacheSymbol> KernelCache::GetSymbolAt(uint64_t address) const
{
- context.writer.StartArray();
- for (const auto& [_, symbol] : *value)
- {
- Serialize(context, symbol);
- }
- context.writer.EndArray();
-}
-
-void Serialize(SerializationContext& context, const std::shared_ptr<std::vector<Ref<Symbol>>>& value)
-{
- Serialize(context, *value);
-}
-
-void Deserialize(DeserializationContext& context, std::string_view name,
- std::unordered_map<uint64_t, std::shared_ptr<std::unordered_map<uint64_t, Ref<Symbol>>>>& value)
-{
- auto array = context.doc[name.data()].GetArray();
- for (auto& pair : array)
- {
- auto symbols_array = pair[1].GetArray();
- std::unordered_map<uint64_t, Ref<Symbol>> symbols;
- for (auto& symbol_value : symbols_array)
- {
- auto symbol_array = symbol_value.GetArray();
- std::string symbolName = symbol_array[0].GetString();
- uint64_t address = symbol_array[1].GetUint64();
- BNSymbolType type = (BNSymbolType)symbol_array[2].GetUint();
- symbols.insert({address, new Symbol(type, symbolName, address)});
- }
- value[pair[0].GetUint64()] = std::make_shared<std::unordered_map<uint64_t, Ref<Symbol>>>(std::move(symbols));
- }
-}
-
-void Deserialize(DeserializationContext& context, std::string_view name,
- std::unordered_map<uint64_t, std::shared_ptr<std::vector<Ref<Symbol>>>>& value)
-{
- auto array = context.doc[name.data()].GetArray();
- for (auto& pair : array)
- {
- auto symbols_array = pair[1].GetArray();
- std::vector<Ref<Symbol>> symbols;
- symbols.reserve(symbols_array.Size());
- for (auto& symbol_value : symbols_array)
- {
- auto symbol_array = symbol_value.GetArray();
- std::string symbolName = symbol_array[0].GetString();
- uint64_t address = symbol_array[1].GetUint64();
- BNSymbolType type = (BNSymbolType)symbol_array[2].GetUint();
- symbols.push_back(new Symbol(type, symbolName, address));
- }
- value[pair[0].GetUint64()] = std::make_shared<std::vector<Ref<Symbol>>>(std::move(symbols));
- }
-}
-
-void Deserialize(DeserializationContext& context, std::string_view name, std::optional<KCViewState>& viewState)
-{
- auto& value = context.doc[name.data()];
- if (value.IsNull())
- viewState = std::nullopt;
- else
- viewState = (KCViewState)value.GetUint();
-}
-
-
-void Serialize(SerializationContext& context, const std::vector<MemoryRegion>& value)
-{
- context.writer.StartArray();
- for (const auto& region : value)
- {
- context.writer.StartArray();
- Serialize(context, region.prettyName);
- Serialize(context, region.start);
- Serialize(context, region.size);
- Serialize(context, region.fileOffset);
- Serialize(context, (uint64_t)region.flags);
- Serialize(context, (uint8_t)region.type);
- context.writer.EndArray();
- }
- context.writer.EndArray();
-}
-
-void Deserialize(DeserializationContext& context, std::string_view name, std::vector<MemoryRegion>& value)
-{
- auto array = context.doc[name.data()].GetArray();
- value.reserve(array.Size());
- for (auto& region : array)
- {
- auto region_array = region.GetArray();
- MemoryRegion newRegion;
- newRegion.prettyName = region_array[0].GetString();
- newRegion.start = region_array[1].GetUint64();
- newRegion.size = region_array[2].GetUint64();
- newRegion.fileOffset = region_array[3].GetUint64();
- newRegion.flags = (BNSegmentFlag)region_array[4].GetUint64();
- newRegion.type = (MemoryRegion::Type)region_array[5].GetUint();
- value.push_back(newRegion);
- }
-}
-
-void Serialize(SerializationContext& context, const std::vector<KernelCacheImage>& value)
-{
- context.writer.StartArray();
- for (const auto& image : value)
- {
- Serialize(context, image);
- }
- context.writer.EndArray();
-}
-
-
-void Deserialize(DeserializationContext& context, std::string_view name, std::vector<KernelCacheImage>& value)
-{
- auto array = context.doc[name.data()].GetArray();
- value.reserve(array.Size());
- for (auto& image : array)
- {
- KernelCacheImage img = KernelCacheImage::LoadFromValue(image);
- value.push_back(img);
- }
-}
-
-
-void Serialize(SerializationContext& context, const std::vector<std::pair<uint64_t, uint64_t>>& value)
-{
- context.writer.StartArray();
- for (const auto& pair : value)
- {
- context.writer.StartArray();
- Serialize(context, pair.first);
- Serialize(context, pair.second);
- context.writer.EndArray();
- }
- context.writer.EndArray();
-}
-
-void Deserialize(DeserializationContext& context, std::string_view name, std::vector<std::pair<uint64_t, uint64_t>>& value)
-{
- auto array = context.doc[name.data()].GetArray();
- value.reserve(array.Size());
- for (auto& pair : array)
- {
- auto pair_array = pair.GetArray();
- value.push_back({pair_array[0].GetUint64(), pair_array[1].GetUint64()});
- }
-}
-
-void Serialize(SerializationContext& context, const std::unordered_map<uint64_t, KernelCacheMachOHeader>& value)
-{
- context.writer.StartArray();
- for (const auto& pair : value)
- {
- context.writer.StartArray();
- Serialize(context, pair.first);
- Serialize(context, pair.second);
- context.writer.EndArray();
- }
- context.writer.EndArray();
-}
-
-void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<uint64_t, KernelCacheMachOHeader>& value)
-{
- auto array = context.doc[name.data()].GetArray();
- for (auto& pair : array)
- {
- auto pair_array = pair.GetArray();
- uint64_t key = pair_array[0].GetUint64();
- KernelCacheMachOHeader header = KernelCacheMachOHeader::LoadFromValue(pair_array[1]);
- value[key] = header;
- }
-}
-
-
-void Serialize(SerializationContext& context, const std::unordered_map<uint64_t, KernelCacheImage> value)
-{
- context.writer.StartArray();
- for (const auto& pair : value)
- {
- context.writer.StartArray();
- Serialize(context, pair.first);
- Serialize(context, pair.second);
- context.writer.EndArray();
- }
- context.writer.EndArray();
-}
-
-void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<uint64_t, KernelCacheImage>& value)
-{
- auto array = context.doc[name.data()].GetArray();
- for (auto& pair : array)
- {
- auto pair_array = pair.GetArray();
- uint64_t key = pair_array[0].GetUint64();
- KernelCacheImage image = KernelCacheImage::LoadFromValue(pair_array[1]);
- value[key] = image;
- }
-}
-
-
-void KernelCache::CacheInfo::Store(SerializationContext& context) const
-{
- Serialize(context, "metadataVersion", METADATA_VERSION);
-
- MSS(images);
- MSS(imageStarts);
- MSS_CAST(cacheFormat, uint8_t);
-}
-
-// static
-std::optional<KernelCache::CacheInfo> KernelCache::CacheInfo::Load(DeserializationContext& context)
-{
- if (!context.doc.HasMember("metadataVersion"))
- {
- LogError("Shared Cache metadata version missing");
- return std::nullopt;
- }
-
- if (context.doc["metadataVersion"].GetUint() != METADATA_VERSION)
- {
- LogError("Shared Cache metadata version mismatch");
+ const auto it = m_symbols.find(address);
+ if (it == m_symbols.end())
return std::nullopt;
- }
-
- CacheInfo cacheInfo;
- cacheInfo.MSL(images);
- cacheInfo.MSL(imageStarts);
- cacheInfo.MSL_CAST(cacheFormat, uint8_t, KernelCacheFormat);
- return cacheInfo;
-}
-void State::Store(SerializationContext& context, std::optional<KCViewState> viewState) const
-{
- MSS(loadedImages);
- MSS(viewState);
-}
-
-void KernelCache::ModifiedState::Store(SerializationContext& context) const
-{
- State::Store(context, viewState);
+ return it->second;
}
-KernelCache::ModifiedState KernelCache::ModifiedState::Load(DeserializationContext& context)
+std::optional<CacheSymbol> KernelCache::GetSymbolWithName(const std::string& name)
{
- KernelCache::ModifiedState state;
- state.MSL(loadedImages);
- state.MSL(viewState);
- return state;
-}
-
-KernelCache::ModifiedState KernelCache::ModifiedState::LoadAll(BinaryNinja::BinaryView *dscView, const CacheInfo& cacheInfo)
-{
- uint64_t stateCount = dscView->GetUIntMetadata(KernelCacheMetadata::ModifiedStateCountTag);
- KernelCache::ModifiedState state;
- for (uint64_t i = 0; i < stateCount; ++i)
- {
- std::string key = KernelCacheMetadata::ModifiedStateTagPrefix + std::to_string(i);
- std::string serialized = dscView->GetStringMetadata(key);
- auto thisState = KernelCache::ModifiedState::LoadFromString(serialized);
- state.Merge(std::move(thisState));
- }
- return state;
-}
-
-void KernelCache::ModifiedState::Merge(KernelCache::ModifiedState&& newer)
-{
- loadedImages.merge(newer.loadedImages);
-
- if (newer.viewState)
- viewState = newer.viewState;
-}
-
-void KernelCacheImage::Store(SerializationContext& context) const
-{
- MSS(installName);
- MSS(headerFileLocation);
- MSS(regions);
-}
-
-// static
-KernelCacheImage KernelCacheImage::Load(DeserializationContext& context)
-{
- KernelCacheImage cacheImage;
- cacheImage.MSL(installName);
- cacheImage.MSL(headerFileLocation);
- cacheImage.MSL(regions);
- return cacheImage;
+ std::shared_lock<std::shared_mutex> lock(*m_namedSymMutex);
+ const auto it = m_namedSymbols.find(name);
+ if (it == m_namedSymbols.end())
+ return std::nullopt;
+ return GetSymbolAt(it->second);
}
-
-
-} // namespace KernelCacheCore
diff --git a/view/kernelcache/core/KernelCache.h b/view/kernelcache/core/KernelCache.h
index e0e80cb6..38974225 100644
--- a/view/kernelcache/core/KernelCache.h
+++ b/view/kernelcache/core/KernelCache.h
@@ -1,361 +1,160 @@
-//
-// Created by kat on 5/19/23.
-//
+#pragma once
-#include <binaryninjaapi.h>
-#include "KCView.h"
-#include "view/macho/machoview.h"
-#include "MetadataSerializable.hpp"
-#include "../api/kernelcachecore.h"
+#include <vector>
-#ifndef KERNELCACHE_KERNELCACHE_H
-#define KERNELCACHE_KERNELCACHE_H
+#include "binaryninjaapi.h"
+#include "MachO.h"
-DECLARE_KERNELCACHE_API_OBJECT(BNKernelCache, KernelCache);
+#include <mutex>
+#include <shared_mutex>
+#include "Utility.h"
-namespace KernelCacheCore {
+struct CacheSymbol
+{
+ BNSymbolType type;
+ uint64_t address;
+ std::string name;
- enum KCViewState
- {
- KCViewStateUnloaded,
- KCViewStateLoaded,
- KCViewStateLoadedWithImages,
- };
-
- const std::string KernelCacheMetadataTag = "KERNELCACHE-KernelCacheData";
-
- struct MemoryRegion : public MetadataSerializable<MemoryRegion>
- {
- enum class Type
- {
- Image,
- NonImage,
- };
-
- std::string prettyName;
- uint64_t start;
- uint64_t size;
- uint64_t fileOffset;
- BNSegmentFlag flags;
- Type type;
+ CacheSymbol() = default;
+ CacheSymbol(BNSymbolType type, uint64_t address, std::string name) :
+ type(type), address(address), name(std::move(name))
+ {}
+ ~CacheSymbol() = default;
- void Store(SerializationContext& context) const
- {
- MSS(prettyName);
- MSS(start);
- MSS(size);
- MSS(fileOffset);
- MSS_CAST(flags, uint64_t);
- MSS_CAST(type, uint8_t);
- }
+ CacheSymbol(const CacheSymbol& other) = default;
+ CacheSymbol& operator=(const CacheSymbol& other) = default;
- 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;
- }
- };
+ CacheSymbol(CacheSymbol&& other) noexcept = default;
+ CacheSymbol& operator=(CacheSymbol&& other) noexcept = default;
- struct KernelCacheImage : public MetadataSerializable<KernelCacheImage> {
- std::string installName;
- uint64_t headerFileLocation;
- std::vector<MemoryRegion> regions;
+ std::pair<std::string, BinaryNinja::Ref<BinaryNinja::Type>> DemangledName(BinaryNinja::BinaryView& view) const;
- void Store(SerializationContext& context) const;
- static KernelCacheImage Load(DeserializationContext& context);
- };
+ // NOTE: you should really only call this when adding the symbol to the view.
+ std::pair<BinaryNinja::Ref<BinaryNinja::Symbol>, BinaryNinja::Ref<BinaryNinja::Type>> GetBNSymbolAndType(BinaryNinja::BinaryView& view) const;
+};
- #if defined(__GNUC__) || defined(__clang__)
- #define PACKED_STRUCT __attribute__((packed))
- #else
- #define PACKED_STRUCT
- #endif
+struct CacheRegion
+{
+ // type is always image
+ std::string name;
+ uint64_t start;
+ uint64_t size;
+ // Associate this region with this image, this makes it easier to identify what image owns this region.
+ std::optional<uint64_t> imageStart;
+ BNSegmentFlag flags;
- #if defined(_MSC_VER)
- #pragma pack(push, 1)
- #else
+ CacheRegion() = default;
+ ~CacheRegion() = default;
- #endif
+ CacheRegion(const CacheRegion& other) = default;
+ CacheRegion& operator=(const CacheRegion& other) = default;
- #if defined(_MSC_VER)
- #pragma pack(pop)
- #else
+ CacheRegion(CacheRegion&& other) noexcept = default;
+ CacheRegion& operator=(CacheRegion&& other) noexcept = default;
- #endif
+ AddressRange AsAddressRange() const { return {start, start + size}; }
- using namespace BinaryNinja;
- struct KernelCacheMachOHeader : public MetadataSerializable<KernelCacheMachOHeader>
+ BNSectionSemantics SectionSemanticsForRegion() const
{
- uint64_t textBase = 0;
- uint64_t textBaseFileOffset = 0;
- uint64_t loadCommandOffset = 0;
- mach_header_64 ident;
- std::string identifierPrefix;
- std::string installName;
-
- std::vector<std::pair<uint64_t, bool>> entryPoints;
- std::vector<uint64_t> 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<section_64> moduleInitSections;
- std::vector<section_64> 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<segment_command_64> segments; // only three types of sections __TEXT, __DATA, __IMPORT
- segment_command_64 linkeditSegment;
- std::vector<section_64> sections;
- std::vector<std::string> sectionNames;
+ if ((flags & SegmentExecutable) && (flags & SegmentDenyWrite))
+ return ReadOnlyCodeSectionSemantics;
- std::vector<section_64> symbolStubSections;
- std::vector<section_64> symbolPointerSections;
+ if (flags & SegmentExecutable)
+ return DefaultSectionSemantics;
- std::vector<std::string> dylibs;
-
- build_version_command buildVersion;
- std::vector<build_tool_version> 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<KernelCache>
- {
- IMPLEMENT_KERNELCACHE_API_OBJECT(BNKernelCache);
+ if (flags & SegmentDenyWrite)
+ return ReadOnlyDataSectionSemantics;
- std::atomic<int> m_refs = 0;
+ return ReadWriteDataSectionSemantics;
+ }
+};
- public:
- virtual void AddRef() { m_refs.fetch_add(1); }
+// Represents a single image and its associated memory regions.
+struct CacheImage
+{
+ uint64_t headerFileAddress;
+ uint64_t headerVirtualAddress;
+ std::string path;
+ // A list to the start of memory regions associated with the image.
+ // This lets us load all regions for a given image easily.
+ std::vector<CacheRegion> regions;
+ std::shared_ptr<KernelCacheMachOHeader> header;
- 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;
- });
- }
+ CacheImage() = default;
+ ~CacheImage() = default;
- virtual void AddAPIRef() { AddRef(); }
+ CacheImage(const CacheImage& other) = default;
+ CacheImage& operator=(const CacheImage& other) = default;
- virtual void ReleaseAPIRef() { Release(); }
+ CacheImage(CacheImage&& other) noexcept = default;
+ CacheImage& operator=(CacheImage&& other) noexcept = default;
- public:
- enum KernelCacheFormat
- {
- FilesetCacheFormat,
- PrelinkedCacheFormat,
- };
+ // Get the file name from the path.
+ std::string GetName() const { return BaseFileName(path); }
- struct CacheInfo;
- struct ModifiedState;
+ // Get the names of the dependencies.
+ std::vector<std::string> GetDependencies() const;
+};
- struct ViewSpecificState;
+// The C in KC.
+// This represents the entire cache, all regions and images are visible from here.
+// This is the dump for all the information, and what the workflow activities and the UI want.
+// Creating this is expensive, both in actual processing and just copying, so we only generate this
+// once every time the database is open.
+class KernelCache
+{
+ // Calculated within `AddEntry`, this indicates where the shared cache image is based at.
+ uint64_t m_baseAddress = 0;
- void Store(SerializationContext& context) const;
- void Load(DeserializationContext& context);
+ std::vector<std::pair<uint64_t, uint64_t>> m_relocations {};
- private:
- Ref<Logger> m_logger;
- /* VIEW STATE BEGIN -- SERIALIZE ALL OF THIS AND STORE IT IN RAW VIEW */
+ std::unordered_map<uint64_t, CacheImage> m_images {};
+ // All the external symbols for this cache. Both mapped and unmapped (not in the view).
+ std::unordered_map<uint64_t, CacheSymbol> m_symbols {};
+ // Quickly lookup a symbol by name, populated by `FinalizeSymbols`.
+ // `m_namedSymbols` is modified in a worker thread spawned by view init so we must not get a symbol until its populated.
+ std::unordered_map<std::string, uint64_t> m_namedSymbols {};
+ // Used to guard `m_namedSymbols` as it's accessed on multiple threads.
+ // NOTE: Wrapped in unique_ptr to keep KernelCache movable.
+ std::unique_ptr<std::shared_mutex> m_namedSymMutex;
- // State that is initialized during `PerformInitialLoad` and does
- // not change thereafter.
- std::shared_ptr<const CacheInfo> m_cacheInfo;
+public:
- // Protects member variables below.
- mutable std::mutex m_mutex;
+ bool ProcessEntryImage(BinaryNinja::Ref<BinaryNinja::BinaryView> bv, const std::string& path, const BinaryNinja::fileset_entry_command& info);
+ KernelCache() = default;
+ explicit KernelCache(uint64_t addressSize);
- // 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<ModifiedState> m_modifiedState;
+ KernelCache(const KernelCache &) = delete;
+ KernelCache &operator=(const KernelCache &) = delete;
- // Serialized once by PerformInitialLoad and available after m_viewState == Loaded
- bool m_metadataValid = false;
+ KernelCache(KernelCache &&) noexcept = default;
+ KernelCache &operator=(KernelCache &&) noexcept = default;
- /* API VIEW START */
- BinaryNinja::Ref<BinaryNinja::BinaryView> m_kcView;
- /* API VIEW END */
-
- std::shared_ptr<ViewSpecificState> m_viewSpecificState;
-
- private:
- void PerformInitialLoad(std::lock_guard<std::mutex>&);
- void DeserializeFromRawView(std::lock_guard<std::mutex>&);
-
- public:
- static KernelCache* GetFromKCView(BinaryNinja::Ref<BinaryNinja::BinaryView> kcView);
- static uint64_t FastGetImageCount(BinaryNinja::Ref<BinaryNinja::BinaryView> kcView);
- bool SaveCacheInfoToKCView(std::lock_guard<std::mutex>&);
- bool SaveModifiedStateToKCView(std::lock_guard<std::mutex>&);
- std::optional<uint64_t> GetImageStart(std::string installName);
- std::optional<KernelCacheMachOHeader> HeaderForVMAddress(uint64_t address);
- std::optional<KernelCacheMachOHeader> HeaderForFileAddress(uint64_t address);
- bool LoadImageWithInstallName(std::lock_guard<std::mutex>& lock, std::string installName);
- bool LoadImageWithInstallName(std::string installName);
- bool LoadImageContainingAddress(std::lock_guard<std::mutex>& lock, uint64_t address);
- bool LoadImageContainingAddress(uint64_t address);
- std::string NameForAddress(uint64_t address);
- std::string ImageNameForAddress(uint64_t address);
- std::vector<std::string> GetAvailableImages();
- std::vector<KernelCacheImage> GetLoadedImages();
- bool IsImageLoaded(uint64_t address);
-
- std::vector<std::pair<uint64_t, std::pair<std::string, std::string>>> LoadAllSymbolsAndWait();
-
- const std::unordered_map<std::string, uint64_t>& AllImageStarts() const;
- const std::unordered_map<uint64_t, KernelCacheMachOHeader> AllImageHeaders() const;
-
- std::string SerializedImageHeaderForVMAddress(uint64_t address);
- std::string SerializedImageHeaderForName(std::string name);
-
- KCViewState ViewState() const;
-
- explicit KernelCache(BinaryNinja::Ref<BinaryNinja::BinaryView> rawView);
- virtual ~KernelCache();
-
- static bool InitializeSegmentsForHeader(Ref<BinaryView> view, const KernelCacheMachOHeader& header, const KernelCacheImage& targetImage);
- static std::optional<KernelCacheMachOHeader> LoadHeaderForAddress(Ref<BinaryView> view, uint64_t address, std::string installName);
- static void InitializeHeader(Ref<BinaryView> view, KernelCacheMachOHeader header);
- static void ReadExportNode(Ref<BinaryView> view, std::vector<Ref<Symbol>>& symbolList, KernelCacheMachOHeader& header, DataBuffer& buffer,
- uint64_t textBase, const std::string& currentText, size_t cursor, uint32_t endGuard);
- static std::vector<Ref<Symbol>> ParseExportTrie(Ref<BinaryView> view, KernelCacheMachOHeader header);
- static std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> ParseSymbolTable(Ref<BinaryView> view, KernelCacheMachOHeader header, bool defineSymbolsInView = true);
- };
-
-
- class KernelCacheMetadata
- {
- public:
- static std::optional<KernelCacheMetadata> LoadFromView(BinaryView*);
- static bool ViewHasMetadata(BinaryView*);
+ uint64_t GetBaseAddress() const { return m_baseAddress; }
+ const std::unordered_map<uint64_t, CacheImage>& GetImages() const { return m_images; }
+ const std::unordered_map<uint64_t, CacheSymbol>& GetSymbols() const { return m_symbols; }
- std::string InstallNameForImageBaseAddress(uint64_t baseAddress) const;
+ void AddImage(CacheImage&& image);
- std::vector<KernelCacheImage> LoadedImages();
+ void AddSymbol(CacheSymbol symbol);
- ~KernelCacheMetadata();
- KernelCacheMetadata(KernelCacheMetadata&&);
- KernelCacheMetadata& operator=(KernelCacheMetadata&&);
+ void AddSymbols(std::vector<CacheSymbol>&& symbols);
- private:
- KernelCacheMetadata(KernelCache::CacheInfo, KernelCache::ModifiedState);
+ // Construct the named symbols lookup map for use with `GetSymbolWithName`.
+ void ProcessSymbols();
- std::unique_ptr<KernelCache::CacheInfo> cacheInfo;
- std::unique_ptr<KernelCache::ModifiedState> state;
+ void ProcessRelocations(BinaryNinja::Ref<BinaryNinja::BinaryView> view, BinaryNinja::linkedit_data_command chained_fixup_command);
- friend struct KernelCache::ModifiedState;
- friend class KernelCache;
+ const std::vector<std::pair<uint64_t, uint64_t>>& GetRelocations() const { return m_relocations; }
- static const std::string Tag;
- static const std::string CacheInfoTag;
- static const std::string ModifiedStateTagPrefix;
- static const std::string ModifiedStateCountTag;
- };
+ std::optional<CacheImage> GetImageAt(uint64_t address) const;
-}
+ std::optional<CacheImage> GetImageContaining(uint64_t address) const;
-void InitKernelcache();
+ // TODO: Rename to GetImageWithPath and then make another one for the image name.
+ std::optional<CacheImage> GetImageWithName(const std::string& name) const;
-#endif //KERNELCACHE_KERNELCACHE_H
+ std::optional<CacheSymbol> GetSymbolAt(uint64_t address) const;
+ std::optional<CacheSymbol> GetSymbolWithName(const std::string& name);
+};
diff --git a/view/kernelcache/core/KernelCacheBuilder.cpp b/view/kernelcache/core/KernelCacheBuilder.cpp
new file mode 100644
index 00000000..50c1355f
--- /dev/null
+++ b/view/kernelcache/core/KernelCacheBuilder.cpp
@@ -0,0 +1,16 @@
+#include <filesystem>
+#include "KernelCacheBuilder.h"
+
+using namespace BinaryNinja;
+
+KernelCacheBuilder::KernelCacheBuilder(Ref<BinaryView> view)
+{
+ m_view = std::move(view);
+ m_logger = new Logger("KernelCache.Builder", m_view->GetFile()->GetSessionId());
+ m_cache = KernelCache(m_view->GetAddressSize());
+}
+
+KernelCache KernelCacheBuilder::Finalize()
+{
+ return std::move(m_cache);
+}
diff --git a/view/kernelcache/core/KernelCacheBuilder.h b/view/kernelcache/core/KernelCacheBuilder.h
new file mode 100644
index 00000000..756d069b
--- /dev/null
+++ b/view/kernelcache/core/KernelCacheBuilder.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+#include "KernelCache.h"
+
+// This constructs a Cache
+class KernelCacheBuilder
+{
+ BinaryNinja::Ref<BinaryNinja::BinaryView> m_view;
+ BinaryNinja::Ref<BinaryNinja::Logger> m_logger;
+ // This cache is what is returned via `Finalize`.
+ KernelCache m_cache;
+
+public:
+ explicit KernelCacheBuilder(BinaryNinja::Ref<BinaryNinja::BinaryView> view);
+
+ KernelCache& GetCache() { return m_cache; };
+ // Returns a shared cache that is ready for processing, this should include all the required shared cache entries.
+ KernelCache Finalize();
+};
diff --git a/view/kernelcache/core/KernelCacheController.cpp b/view/kernelcache/core/KernelCacheController.cpp
new file mode 100644
index 00000000..ef2fbd04
--- /dev/null
+++ b/view/kernelcache/core/KernelCacheController.cpp
@@ -0,0 +1,198 @@
+#include "KernelCacheController.h"
+#include "MachOProcessor.h"
+
+using namespace BinaryNinja;
+using namespace BinaryNinja::KC;
+
+typedef uint64_t ViewId;
+
+std::shared_mutex GlobalControllersMutex;
+
+std::map<ViewId, KCRef<KernelCacheController>>& GlobalControllers()
+{
+ static std::map<ViewId, KCRef<KernelCacheController>> g_controllers = {};
+ return g_controllers;
+}
+
+
+ViewId GetViewIdFromFileMetadata(const FileMetadata& file)
+{
+ // Currently the view id is just the views session id.
+ // NOTE: If we want more than one shared cache controller per view we would need to make this more unique.
+ return file.GetSessionId();
+}
+
+void DeleteController(const FileMetadata& file)
+{
+ const auto id = GetViewIdFromFileMetadata(file);
+ std::unique_lock<std::shared_mutex> lock(GlobalControllersMutex);
+ auto& controllers = GlobalControllers();
+ if (auto it = controllers.find(id); it != controllers.end())
+ {
+ auto controller = it->second;
+ // Someone is still holding the controller, lets warn about this.
+ // 2 is expected here because we have one held in `controllers` and one held by `controller`.
+ if (controller->m_refs > 2)
+ LogWarn("Deleting KernelCacheController for view %llx, but there are still %d references", id,
+ controller->m_refs.load());
+
+ controllers.erase(it);
+ LogDebug("Deleted KernelCacheController for view %s", file.GetFilename().c_str());
+ }
+}
+
+void RegisterKernelCacheControllerDestructor()
+{
+ BNObjectDestructionCallbacks callbacks = {};
+ callbacks.destructFileMetadata = [](void* ctx, BNFileMetadata* obj) -> void {
+ const auto file = FileMetadata(obj);
+ DeleteController(file);
+ };
+ BNRegisterObjectDestructionCallbacks(&callbacks);
+}
+
+KernelCacheController::KernelCacheController(KernelCache&& cache, Ref<Logger> logger) : m_cache(std::move(cache))
+{
+ INIT_KC_API_OBJECT();
+ m_logger = std::move(logger);
+ m_loadedImages = {};
+ m_regionFilter = std::regex(".*LINKEDIT.*");
+}
+
+KCRef<KernelCacheController> KernelCacheController::Initialize(BinaryView& view, KernelCache&& cache)
+{
+ auto id = GetViewIdFromFileMetadata(*view.GetFile());
+ std::unique_lock<std::shared_mutex> lock(GlobalControllersMutex);
+ auto logger = new Logger("KernelCache.Controller", view.GetFile()->GetSessionId());
+ KCRef<KernelCacheController> controller = new KernelCacheController(std::move(cache), logger);
+
+ // Pull the settings from the view.
+ if (Ref<Settings> settings = view.GetLoadSettings(KC_VIEW_NAME))
+ {
+ if (settings->Contains("loader.kc.regionFilter"))
+ controller->m_regionFilter = std::regex(settings->Get<std::string>("loader.kc.regionFilter", &view));
+ }
+
+ // TODO: Support old shared cache metadata
+ // TODO: Not strictly necessary as the user has already loaded the information into the database, this would just
+ // TODO: prevent incidental extra work from being done when loading a region or image.
+ // const uint64_t oldStateCount = view.GetUIntMetadata(OLD_METADATA_KEY_COUNT);
+
+ // Check the view auto metadata for shared cache information.
+ // This effectively restores the state of the opened database to when it was last saved.
+ // NOTE: We store on the parent view because hilariously, the metadata is not present until after view init.
+ if (auto loadedImageMetadata = view.GetParentView()->QueryMetadata("KernelCacheLoadedImages"))
+ {
+ auto loadedImageList = loadedImageMetadata->GetArray();
+ for (const auto & imageAddrMeta : loadedImageList)
+ {
+ auto imageAddr = imageAddrMeta->GetUnsignedInteger();
+ controller->m_loadedImages.insert(imageAddr);
+ }
+ }
+
+ GlobalControllers().insert({id, controller});
+ return controller;
+}
+
+KCRef<KernelCacheController> KernelCacheController::FromView(const BinaryView& view)
+{
+ auto id = GetViewIdFromFileMetadata(*view.GetFile());
+ std::shared_lock<std::shared_mutex> lock(GlobalControllersMutex);
+ auto& dscViews = GlobalControllers();
+ auto dscView = dscViews.find(id);
+ if (dscView == dscViews.end())
+ return nullptr;
+ return dscView->second;
+}
+
+bool KernelCacheController::ApplyImage(BinaryView& view, const CacheImage& image)
+{
+ // Load all regions of an image and mark the image as loaded.
+ // NOTE: The regions lock m_loadMutex themselves, so we do not hold it up here.
+ bool loadedRegion = false;
+
+ BNRelocationInfo reloc;
+ memset(&reloc, 0, sizeof(BNRelocationInfo));
+ reloc.type = StandardRelocationType;
+ reloc.size = 8;
+ reloc.nativeType = BINARYNINJA_MANUAL_RELOCATION;
+
+ // We check for a valid offset as we apply auto segments and re-call this function on view init
+ if (!view.IsValidOffset(image.headerVirtualAddress))
+ {
+ loadedRegion = true;
+ for (const auto& segment : image.header->segments)
+ {
+ view.AddAutoSegment(segment.vmaddr, segment.vmsize, segment.fileoff, segment.filesize, segment.flags);
+
+ auto relocations = m_cache.GetRelocations();
+
+ auto begin = std::lower_bound(relocations.begin(), relocations.end(), segment.vmaddr,
+ [](const std::pair<uint64_t, uint64_t>& reloc, uint64_t addr) {
+ return reloc.first < addr;
+ });
+
+ auto arch = view.GetDefaultArchitecture();
+ // Process relocations until the VM address is beyond our region
+ for (auto it = begin; it != relocations.end() && it->first < segment.vmaddr + segment.vmsize; ++it) {
+ reloc.address = it->first;
+ view.DefineRelocation(arch, reloc, it->second, reloc.address);
+ }
+ }
+ }
+
+ view.FinalizeNewSegments();
+
+ // The ApplyRegionAtAddress no longer holds the lock, we can take it now.
+ std::unique_lock<std::shared_mutex> lock(m_loadMutex);
+
+
+ // If there was no loaded regions than we just want to forgo loading the image.
+ // We also skip if we already loaded the image itself. We do this after loading regions
+ // as we regions have their own check.
+
+ // On view init, we exit here as we just need to re-add the segments and relocations.
+ if (!loadedRegion || m_loadedImages.find(image.headerVirtualAddress) != m_loadedImages.end())
+ return false;
+
+ if (image.headerVirtualAddress)
+ {
+ // Header information is applied to the view here, such as sections.
+ auto machoProcessor = KernelCacheMachOProcessor(&view);
+
+ // Adding a user section will mark all functions for updates unless we disable this.
+ // Because images are known separate compilation units, we have a real reason to make sure we don't mark all previously
+ // analyzed functions as updated.
+ auto prevDisabledState = view.GetFunctionAnalysisUpdateDisabled();
+ view.SetFunctionAnalysisUpdateDisabled(true);
+ machoProcessor.ApplyHeader(GetCache(), *image.header);
+ view.SetFunctionAnalysisUpdateDisabled(prevDisabledState);
+ }
+
+ m_loadedImages.insert(image.headerVirtualAddress);
+
+ m_logger->LogInfoF("Loaded image: '{}'", image.path);
+
+ // TODO: This needs to be done in a "database save" callback.
+ // NOTE: We store on the parent view because hilariously, the view metadata is not available in view init.
+ std::vector<uint64_t> loadedImages;
+ loadedImages.reserve(m_loadedImages.size());
+ // Store the loaded images in the metadata.
+ for (const uint64_t& addr : m_loadedImages)
+ {
+ loadedImages.push_back(addr);
+ }
+ view.GetParentView()->StoreMetadata("KernelCacheLoadedImages", new Metadata(loadedImages));
+
+ // TODO: Partial failure state (i.e. 2 regions loaded, one failed)
+ return true;
+}
+
+bool KernelCacheController::IsImageLoaded(const CacheImage& image)
+{
+ std::shared_lock<std::shared_mutex> lock(m_loadMutex);
+ return std::any_of(m_loadedImages.begin(), m_loadedImages.end(), [&](const auto& loadedImage) {
+ return loadedImage == image.headerVirtualAddress;
+ });
+}
diff --git a/view/kernelcache/core/KernelCacheController.h b/view/kernelcache/core/KernelCacheController.h
new file mode 100644
index 00000000..71f5e5a9
--- /dev/null
+++ b/view/kernelcache/core/KernelCacheController.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <regex>
+
+#include <shared_mutex>
+#include "KernelCache.h"
+#include "refcountobject.h"
+#include "ffi_global.h"
+
+DECLARE_KC_API_OBJECT(BNKernelCacheController, KernelCacheController);
+
+void RegisterKernelCacheControllerDestructor();
+
+namespace BinaryNinja::KC {
+ // Represents the view state for a given `DSCache`
+ class KernelCacheController : public KCRefCountObject
+ {
+ IMPLEMENT_KC_API_OBJECT(BNKernelCacheController);
+ Ref<Logger> m_logger;
+ KernelCache m_cache;
+
+ // Locks on load attempts (region or image).
+ std::shared_mutex m_loadMutex;
+
+ // Store the open images.
+ std::unordered_set<uint64_t> m_loadedImages;
+
+ // Settings from the view.
+ std::regex m_regionFilter;
+
+ explicit KernelCacheController(KernelCache&& cache, Ref<Logger> logger);
+
+ public:
+ // Initialize the DSCacheView, this should be called from the view initialize function only!
+ static KCRef<KernelCacheController> Initialize(BinaryView& view, KernelCache&& cache);
+
+ // NOTE: This will not create one if it does not exist. To create one for the view call `Initialize`.
+ static KCRef<KernelCacheController> FromView(const BinaryView& view);
+
+ KernelCache& GetCache() { return m_cache; };
+ const std::unordered_set<uint64_t>& GetLoadedImages() { return m_loadedImages; };
+
+ // Loads the relevant image info into the view. This does not update analysis so if you
+ // call this make sure at some point you update analysis and likely with linear sweep.
+ bool ApplyImage(BinaryView& view, const CacheImage& image);
+
+ bool IsImageLoaded(const CacheImage& image);
+ };
+} // namespace BinaryNinja::KC
diff --git a/view/kernelcache/core/KCView.cpp b/view/kernelcache/core/KernelCacheView.cpp
index f166eafd..005a4af2 100644
--- a/view/kernelcache/core/KCView.cpp
+++ b/view/kernelcache/core/KernelCacheView.cpp
@@ -1,49 +1,252 @@
-//
-// Created by kat on 5/23/23.
-//
+#include "KernelCacheView.h"
+#include <regex>
+#include <filesystem>
+#include <MachOProcessor.h>
-/*
- *
- * */
+#include "KernelCacheController.h"
+#include "KernelCacheBuilder.h"
-#include "KCView.h"
-#include "view/macho/machoview.h"
-#include "KernelCache.h"
+using namespace BinaryNinja;
+using namespace BinaryNinja::KC;
-[[maybe_unused]] KCViewType* g_kcViewType;
+[[maybe_unused]] KernelCacheViewType* g_kcViewType;
+KernelCacheViewType::KernelCacheViewType() : BinaryViewType(KC_VIEW_NAME, KC_VIEW_NAME) {}
-#define COMPRESSION_DEBUG 1
+// We register all our one-shot stuff here, such as the object destructor.
+void KernelCacheViewType::Register()
+{
+ RegisterKernelCacheControllerDestructor();
-using namespace BinaryNinja;
+ static KernelCacheViewType kcViewType;
+ BinaryViewType::Register(&kcViewType);
+ g_kcViewType = &kcViewType;
+}
-KCView::KCView(const std::string& typeName, BinaryView* data, bool parseOnly) :
- BinaryView(typeName, data->GetFile(), data), m_parseOnly(parseOnly)
+Ref<BinaryView> KernelCacheViewType::Create(BinaryView* data)
{
- CreateLogger("KernelCache");
+ 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 KernelCacheView(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 KernelCacheView(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 KernelCacheView(KC_VIEW_NAME, data, false);
+#else
+ return nullptr;
+#endif
+ }
+
+ return new KernelCacheView(KC_VIEW_NAME, data, false);
+ }
+
+ return nullptr;
+ }
+
+ return new KernelCacheView(KC_VIEW_NAME, data, false);
}
-KCView::~KCView()
+Ref<Settings> KernelCacheViewType::GetLoadSettingsForData(BinaryView* data)
{
+ Ref<BinaryView> 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> settings = GetDefaultLoadSettingsForData(viewRef);
+
+ // specify default load settings that can be overridden
+ std::vector<std::string> overrides = {"loader.imageBase", "loader.platform"};
+ settings->UpdateProperty("loader.imageBase", "message", "Note: File indicates image is not relocatable.");
+
+ for (const auto& override : overrides)
+ {
+ if (settings->Contains(override))
+ settings->UpdateProperty(override, "readOnly", false);
+ }
+
+ settings->RegisterSetting("loader.kc.autoLoadPattern",
+ R"({
+ "title" : "Image Auto-Load Regex Pattern",
+ "type" : "string",
+ "default" : "",
+ "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.kc.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<Settings> loadSettings = viewRef->GetLoadSettings(GetName());
+ if (loadSettings && !loadSettings->IsEmpty())
+ settings->DeserializeSchema(loadSettings->SerializeSchema());
+
+ return settings;
+}
+
+Ref<BinaryView> KernelCacheViewType::Parse(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 KernelCacheView(KC_VIEW_NAME, data, true);
+ }
+
+ return nullptr;
+ }
+
+ return new KernelCacheView(KC_VIEW_NAME, data, true);
}
-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 KernelCacheViewType::IsTypeValidForData(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);
-bool KCView::Init()
+ 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;
+}
+
+KernelCacheView::KernelCacheView(const std::string& typeName, BinaryView* data, bool parseOnly) :
+ BinaryView(typeName, data->GetFile(), data), m_parseOnly(parseOnly)
{
+}
+
+bool KernelCacheView::Init()
+{
+ m_logger = new Logger("KernelCache.View", GetFile()->GetSessionId());
+
BinaryReader reader(GetParentView());
reader.Seek(0x4);
uint32_t cpuType = reader.Read32();
@@ -116,9 +319,16 @@ bool KCView::Init()
}
if (!foundBuildVersion)
{
- LogError("Failed to find LC_BUILD_VERSION in subheader");
- SetDefaultArchitecture(Architecture::GetByName("aarch64"));
- SetDefaultPlatform(Platform::GetByName("macos-kernel-aarch64"));
+ // FIXME: Is it in-spec to have LC_BUILD_VERSION only in the super header?
+ // This is sufficient as a fallback but source should be referenced here.
+ LogWarn("Failed to find LC_BUILD_VERSION in subheader. Assuming macOS platform.");
+ std::map<std::string, Ref<Metadata>> metadataMap = {
+ {"machoplatform", new Metadata((uint64_t) MACHO_PLATFORM_MACOS)},
+ };
+ Ref<Metadata> metadata = new Metadata(metadataMap);
+ Ref<Platform> plat = g_kcViewType->RecognizePlatform(cpuType, GetDefaultEndianness(), this, metadata);
+ SetDefaultArchitecture(plat->GetArchitecture());
+ SetDefaultPlatform(plat);
}
}
else
@@ -127,13 +337,6 @@ bool KCView::Init()
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);
@@ -557,37 +760,17 @@ bool KCView::Init()
Ref<Type> unixThreadCommandType = Type::StructureType(unixThreadCommandStruct);
DefineType(unixThreadCommandTypeId, unixThreadCommandName, unixThreadCommandType);
- std::vector<KernelCacheCore::MemoryRegion> 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));
@@ -702,220 +885,109 @@ bool KCView::Init()
LogError("Error when applying Mach-O header types at %" PRIx64, textSegOffset);
}
- return true;
+ return InitController();
}
-
-KCViewType::KCViewType() : BinaryViewType(KC_VIEW_NAME, KC_VIEW_NAME)
+bool KernelCacheView::InitController()
{
-}
+ // OK, we have the primary shared cache file, now let's add the entries.
+ auto kernelCacheBuilder = KernelCacheBuilder(this);
+ auto kernelCache = kernelCacheBuilder.Finalize();
+
+ // TODO: Here we should have all the regions and what not for the virtual memory populated, I think it might be a good idea
+ // TODO: To verify the regions are here and pointing at valid file accessor mappings, to make diagnosing issues quicker.
-BinaryNinja::Ref<BinaryNinja::BinaryView> 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
+ BinaryReader reader(GetParentView());
+ reader.Seek(0x4);
+ uint32_t cpuType = reader.Read32();
+ reader.Seek(0x10);
+ uint64_t ncmds = reader.Read32();
+ uint64_t sizeofcmds = reader.Read32();
+ uint64_t offset = 0x20;
+ for (uint64_t i = 0; i < ncmds; i++)
{
- auto img4 = Transform::GetByName("IMG4-Unencrypted");
-
- DataBuffer img4Payload;
- img4->Decode(data->ReadBuffer(data->GetStart(), data->GetLength()), img4Payload);
+ reader.Seek(offset);
+ uint64_t cmd = reader.Read32();
+ uint64_t cmdsize = reader.Read32();
- 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
+ if (cmd == LC_FILESET_ENTRY)
{
-#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
+ fileset_entry_command fileset_entry;
+ reader.Seek(offset);
+ reader.Read(&fileset_entry, sizeof(fileset_entry_command));
+ reader.Seek(offset + fileset_entry.nameEntryOffsetFromBaseOfCommand);
+ auto name = reader.ReadCString(1000);
+ kernelCache.ProcessEntryImage(this, name, fileset_entry);
}
- if (machOPayload.GetLength() == 0)
+ if (cmd == LC_DYLD_CHAINED_FIXUPS)
{
-#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
- }
+ linkedit_data_command chained_fixups;
+ reader.Seek(offset);
+ reader.Read(&chained_fixups, sizeof(linkedit_data_command));
- 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
+ kernelCache.ProcessRelocations(this, chained_fixups);
}
- return new KCView(KC_VIEW_NAME, data, false);
+ offset += cmdsize;
}
-
- return nullptr;
}
- return new KCView(KC_VIEW_NAME, data, false);
-}
-
+ auto cacheController = KernelCacheController::Initialize(*this, std::move(kernelCache));
-Ref<Settings> KCViewType::GetLoadSettingsForData(BinaryView* data)
-{
- Ref<BinaryView> viewRef = Parse(data);
- if (!viewRef || !viewRef->Init())
{
- LogWarn("Failed to initialize view of type '%s'. Generating default load settings.", GetName().c_str());
- viewRef = data;
+ auto logger = m_logger;
+ // Load up all the symbols into the named symbols lookup map.
+ // NOTE: We do this on a separate thread as image & region loading does not consult this.
+ WorkerPriorityEnqueue([logger, cacheController]() {
+ auto& kernelCache = cacheController->GetCache();
+ auto startTime = std::chrono::high_resolution_clock::now();
+ kernelCache.ProcessSymbols();
+ auto endTime = std::chrono::high_resolution_clock::now();
+ std::chrono::duration<double> elapsed = endTime - startTime;
+ logger->LogInfo("Processing %zu symbols took %.3f seconds (separate thread)", kernelCache.GetSymbols().size(), elapsed.count());
+ });
}
- Ref<Settings> settings = GetDefaultLoadSettingsForData(viewRef);
-
- // specify default load settings that can be overridden
- std::vector<std::string> overrides = {"loader.imageBase", "loader.platform"};
- settings->UpdateProperty("loader.imageBase", "message", "Note: File indicates image is not relocatable.");
+ Ref<Settings> settings = GetLoadSettings(GetTypeName());
+ // Users can adjust which images are loaded by default using the `loader.dsc.autoLoadPattern` setting.
+ std::string autoLoadPattern = ".*libsystem_c.dylib";
+ if (settings && settings->Contains("loader.kc.autoLoadPattern"))
+ autoLoadPattern = settings->Get<std::string>("loader.kc.autoLoadPattern", this);
+ m_logger->LogDebug("Loading images using pattern: %s", autoLoadPattern.c_str());
- for (const auto& override : overrides)
{
- if (settings->Contains(override))
- settings->UpdateProperty(override, "readOnly", false);
+ // TODO: Refusing to add undo action "Added section libsystem_c.dylib::__macho_header", there is literally
+ // TODO: no way around this warning as undo actions are implicit with user sections, for more detail see:
+ // TODO: - https://github.com/Vector35/binaryninja-api/issues/6742
+ // TODO: - https://github.com/Vector35/binaryninja-api/issues/6289
+ // Load all images that match the `autoLoadPattern`.
+ auto startTime = std::chrono::high_resolution_clock::now();
+ size_t loadedImages = 0;
+ std::regex autoLoadRegex(autoLoadPattern);
+ for (const auto& [_, image] : cacheController->GetCache().GetImages())
+ if (std::regex_match(image.GetName(), autoLoadRegex))
+ if (cacheController->ApplyImage(*this, image))
+ ++loadedImages;
+ auto endTime = std::chrono::high_resolution_clock::now();
+ std::chrono::duration<double> elapsed = endTime - startTime;
+ m_logger->LogInfo("Automatically loading %zu images took %.3f seconds", loadedImages, elapsed.count());
}
- // 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<Settings> loadSettings = viewRef->GetLoadSettings(GetName());
- if (loadSettings && !loadSettings->IsEmpty())
- settings->DeserializeSchema(loadSettings->SerializeSchema());
-
- return settings;
-}
-
-
-BinaryNinja::Ref<BinaryNinja::BinaryView> 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
+ if (auto loadedImageMetadata = GetParentView()->QueryMetadata("KernelCacheLoadedImages"))
{
- uint32_t im4pMagic;
- data->Read(&im4pMagic, data->GetStart() + 0x8, 4);
- if (im4pMagic == 0x50344d49) // P4MI
+ auto loadedImageList = loadedImageMetadata->GetArray();
+ for (const auto & imageAddrMeta : loadedImageList)
{
- 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)
+ auto imageAddr = imageAddrMeta->GetUnsignedInteger();
+ const auto& image = cacheController->GetCache().GetImageAt(imageAddr);
+ if (image)
{
- DataBuffer output = machOPayload.GetSlice(0x1c, machOPayload.GetLength()-0x1c);
- data->WriteBuffer(0, output);
+ cacheController->ApplyImage(*this, *image);
}
- 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;
- }
-}
-
diff --git a/view/kernelcache/core/KernelCacheView.h b/view/kernelcache/core/KernelCacheView.h
new file mode 100644
index 00000000..9a2baf6f
--- /dev/null
+++ b/view/kernelcache/core/KernelCacheView.h
@@ -0,0 +1,63 @@
+//
+// Created by kat on 5/23/23.
+//
+
+#ifndef KERNELCACHE_KERNELCACHEVIEW_H
+#define KERNELCACHE_KERNELCACHEVIEW_H
+
+#include <binaryninjaapi.h>
+
+static const char* VIEW_METADATA_KEY = "shared_cache_view";
+
+class KernelCacheView : public BinaryNinja::BinaryView
+{
+ bool m_parseOnly;
+ BinaryNinja::Ref<BinaryNinja::Logger> m_logger;
+
+public:
+ KernelCacheView(const std::string& typeName, BinaryView* data, bool parseOnly = false);
+
+ ~KernelCacheView() override = default;
+
+ bool Init() override;
+
+ // Initialized the shared cache controller for this view. This is what allows us to load images and regions.
+ bool InitController();
+
+ void SetPrimaryFileName(std::string primaryFileName);
+
+ // Logs the secondary file name to `m_secondaryFileNames`, see the note on the field about usage.
+ void LogSecondaryFileName(std::string associatedFileName);
+
+ // Get the path to the primary file.
+ std::optional<std::string> GetPrimaryFilePath();
+
+ // Get the metadata for saving the state of the shared cache.
+ BinaryNinja::Ref<BinaryNinja::Metadata> GetMetadata() const;
+
+ void LoadMetadata(const BinaryNinja::Metadata& metadata);
+
+ virtual bool PerformIsExecutable() const override { return true; }
+};
+
+
+class KernelCacheViewType : public BinaryNinja::BinaryViewType
+{
+public:
+ KernelCacheViewType();
+
+ static void Register();
+
+ BinaryNinja::Ref<BinaryNinja::BinaryView> Create(BinaryNinja::BinaryView* data) override;
+
+ BinaryNinja::Ref<BinaryNinja::BinaryView> Parse(BinaryNinja::BinaryView* data) override;
+
+ bool IsTypeValidForData(BinaryNinja::BinaryView* data) override;
+
+ bool IsDeprecated() override { return false; }
+
+ BinaryNinja::Ref<BinaryNinja::Settings> GetLoadSettingsForData(BinaryNinja::BinaryView* data) override;
+};
+
+
+#endif // KERNELCACHE_KERNELCACHEVIEW_H
diff --git a/view/kernelcache/core/MachO.cpp b/view/kernelcache/core/MachO.cpp
new file mode 100644
index 00000000..d3486b67
--- /dev/null
+++ b/view/kernelcache/core/MachO.cpp
@@ -0,0 +1,714 @@
+#include "MachO.h"
+#include "Utility.h"
+
+#include "KernelCache.h"
+
+using namespace BinaryNinja;
+
+std::vector<uint64_t> KernelCacheMachOHeader::ReadFunctionTable(Ref<BinaryView> bv) const
+{
+ // NOTE: The funcoff is relative to the file of the linkedit segment.
+ uint64_t funcStartsAddress = functionStarts.funcoff;
+ auto funcStarts = bv->GetParentView()->ReadBuffer(funcStartsAddress, functionStarts.funcsize);
+ uint64_t curfunc = textBase;
+ uint64_t curOffset = 0;
+
+ std::vector<uint64_t> functionTable = {};
+ auto current = static_cast<const uint8_t*>(funcStarts.GetData());
+ auto end = current + funcStarts.GetLength();
+ while (current != end)
+ {
+ curOffset = readLEB128(current, end);
+ curfunc += curOffset;
+ uint64_t target = curfunc;
+ functionTable.push_back(target);
+ }
+ return functionTable;
+}
+
+std::optional<KernelCacheMachOHeader> KernelCacheMachOHeader::ParseHeaderForAddress(
+ Ref<BinaryView> bv, uint64_t vmAddress, uint64_t fileAddress, const std::string& imagePath)
+{
+ KernelCacheMachOHeader header;
+
+ header.textBase = vmAddress;
+ header.installName = imagePath;
+ // The identifierPrefix is used for the display of the image name in the sections and segments.
+ header.identifierPrefix = BaseFileName(imagePath);
+
+ std::string errorMsg;
+ BinaryReader reader(bv->GetParentView());
+ reader.Seek(fileAddress);
+
+ header.ident.magic = reader.Read32();
+
+ BNEndianness endianness;
+ switch (header.ident.magic)
+ {
+ case MH_MAGIC:
+ case MH_MAGIC_64:
+ endianness = LittleEndian;
+ break;
+ case MH_CIGAM:
+ case MH_CIGAM_64:
+ endianness = BigEndian;
+ break;
+ default:
+ return {};
+ }
+
+ reader.SetEndianness(endianness);
+ header.ident.cputype = reader.Read32();
+ header.ident.cpusubtype = reader.Read32();
+ header.ident.filetype = reader.Read32();
+ header.ident.ncmds = reader.Read32();
+ header.ident.sizeofcmds = reader.Read32();
+ header.ident.flags = reader.Read32();
+ if ((header.ident.cputype & MachOABIMask) == MachOABI64) // address size == 8
+ {
+ header.ident.reserved = reader.Read32();
+ }
+ header.loadCommandOffset = reader.GetOffset();
+
+ bool first = true;
+ // Parse segment commands
+ try
+ {
+ for (size_t i = 0; i < header.ident.ncmds; i++)
+ {
+ // BNLogInfo("of 0x%llx", reader.GetOffset());
+ load_command load;
+ segment_command_64 segment64;
+ section_64 sect = {};
+ size_t curOffset = reader.GetOffset();
+ load.cmd = reader.Read32();
+ load.cmdsize = reader.Read32();
+ size_t nextOffset = curOffset + load.cmdsize;
+ if (load.cmdsize < sizeof(load_command))
+ return {};
+
+ switch (load.cmd)
+ {
+ case LC_MAIN:
+ {
+ uint64_t entryPoint = reader.Read64();
+ header.entryPoints.push_back({entryPoint, true});
+ (void)reader.Read64(); // Stack start
+ break;
+ }
+ case LC_SEGMENT: // map the 32bit version to 64 bits
+ segment64.cmd = LC_SEGMENT_64;
+ reader.Read(&segment64.segname, 16);
+ segment64.vmaddr = reader.Read32();
+ segment64.vmsize = reader.Read32();
+ segment64.fileoff = reader.Read32();
+ segment64.filesize = reader.Read32();
+ segment64.maxprot = reader.Read32();
+ segment64.initprot = reader.Read32();
+ segment64.nsects = reader.Read32();
+ segment64.flags = reader.Read32();
+ if (first)
+ {
+ if (!((header.ident.flags & MH_SPLIT_SEGS) || header.ident.cputype == MACHO_CPU_TYPE_X86_64)
+ || (segment64.flags & MACHO_VM_PROT_WRITE))
+ {
+ header.relocationBase = segment64.vmaddr;
+ first = false;
+ }
+ }
+ for (size_t j = 0; j < segment64.nsects; j++)
+ {
+ reader.Read(&sect.sectname, 16);
+ reader.Read(&sect.segname, 16);
+ sect.addr = reader.Read32();
+ sect.size = reader.Read32();
+ sect.offset = reader.Read32();
+ sect.align = reader.Read32();
+ sect.reloff = reader.Read32();
+ sect.nreloc = reader.Read32();
+ sect.flags = reader.Read32();
+ sect.reserved1 = reader.Read32();
+ sect.reserved2 = reader.Read32();
+ // if the segment isn't mapped into virtual memory don't add the corresponding sections.
+ if (segment64.vmsize > 0)
+ {
+ header.sections.push_back(sect);
+ }
+ if (!strncmp(sect.sectname, "__mod_init_func", 15))
+ header.moduleInitSections.push_back(sect);
+ if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS))
+ == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS))
+ header.symbolStubSections.push_back(sect);
+ if ((sect.flags & S_NON_LAZY_SYMBOL_POINTERS) == S_NON_LAZY_SYMBOL_POINTERS)
+ header.symbolPointerSections.push_back(sect);
+ if ((sect.flags & S_LAZY_SYMBOL_POINTERS) == S_LAZY_SYMBOL_POINTERS)
+ header.symbolPointerSections.push_back(sect);
+ }
+ header.segments.push_back(segment64);
+ break;
+ case LC_SEGMENT_64:
+ segment64.cmd = LC_SEGMENT_64;
+ reader.Read(&segment64.segname, 16);
+ segment64.vmaddr = reader.Read64();
+ segment64.vmsize = reader.Read64();
+ segment64.fileoff = reader.Read64();
+ segment64.filesize = reader.Read64();
+ segment64.maxprot = reader.Read32();
+ segment64.initprot = reader.Read32();
+ segment64.nsects = reader.Read32();
+ segment64.flags = reader.Read32();
+ if (strncmp(segment64.segname, "__LINKEDIT", 10) == 0)
+ {
+ header.linkeditSegment = segment64;
+ header.linkeditPresent = true;
+ }
+ if (first)
+ {
+ if (!((header.ident.flags & MH_SPLIT_SEGS) || header.ident.cputype == MACHO_CPU_TYPE_X86_64)
+ || (segment64.flags & MACHO_VM_PROT_WRITE))
+ {
+ header.relocationBase = segment64.vmaddr;
+ first = false;
+ }
+ }
+ for (size_t j = 0; j < segment64.nsects; j++)
+ {
+ reader.Read(&sect.sectname, 16);
+ reader.Read(&sect.segname, 16);
+ sect.addr = reader.Read64();
+ sect.size = reader.Read64();
+ sect.offset = reader.Read32();
+ sect.align = reader.Read32();
+ sect.reloff = reader.Read32();
+ sect.nreloc = reader.Read32();
+ sect.flags = reader.Read32();
+ sect.reserved1 = reader.Read32();
+ sect.reserved2 = reader.Read32();
+ sect.reserved3 = reader.Read32();
+ // if the segment isn't mapped into virtual memory don't add the corresponding sections.
+ if (segment64.vmsize > 0)
+ {
+ header.sections.push_back(sect);
+ }
+
+ if (!strncmp(sect.sectname, "__mod_init_func", 15))
+ header.moduleInitSections.push_back(sect);
+ if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS))
+ == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS))
+ header.symbolStubSections.push_back(sect);
+ if ((sect.flags & S_NON_LAZY_SYMBOL_POINTERS) == S_NON_LAZY_SYMBOL_POINTERS)
+ header.symbolPointerSections.push_back(sect);
+ if ((sect.flags & S_LAZY_SYMBOL_POINTERS) == S_LAZY_SYMBOL_POINTERS)
+ header.symbolPointerSections.push_back(sect);
+ }
+ header.segments.push_back(segment64);
+ break;
+ case LC_ROUTINES: // map the 32bit version to 64bits
+ header.routines64.cmd = LC_ROUTINES_64;
+ header.routines64.init_address = reader.Read32();
+ header.routines64.init_module = reader.Read32();
+ header.routines64.reserved1 = reader.Read32();
+ header.routines64.reserved2 = reader.Read32();
+ header.routines64.reserved3 = reader.Read32();
+ header.routines64.reserved4 = reader.Read32();
+ header.routines64.reserved5 = reader.Read32();
+ header.routines64.reserved6 = reader.Read32();
+ header.routinesPresent = true;
+ break;
+ case LC_ROUTINES_64:
+ header.routines64.cmd = LC_ROUTINES_64;
+ header.routines64.init_address = reader.Read64();
+ header.routines64.init_module = reader.Read64();
+ header.routines64.reserved1 = reader.Read64();
+ header.routines64.reserved2 = reader.Read64();
+ header.routines64.reserved3 = reader.Read64();
+ header.routines64.reserved4 = reader.Read64();
+ header.routines64.reserved5 = reader.Read64();
+ header.routines64.reserved6 = reader.Read64();
+ header.routinesPresent = true;
+ break;
+ case LC_FUNCTION_STARTS:
+ header.functionStarts.funcoff = reader.Read32();
+ header.functionStarts.funcsize = reader.Read32();
+ header.functionStartsPresent = true;
+ break;
+ case LC_SYMTAB:
+ header.symtab.symoff = reader.Read32();
+ header.symtab.nsyms = reader.Read32();
+ header.symtab.stroff = reader.Read32();
+ header.symtab.strsize = reader.Read32();
+ break;
+ case LC_DYSYMTAB:
+ header.dysymtab.ilocalsym = reader.Read32();
+ header.dysymtab.nlocalsym = reader.Read32();
+ header.dysymtab.iextdefsym = reader.Read32();
+ header.dysymtab.nextdefsym = reader.Read32();
+ header.dysymtab.iundefsym = reader.Read32();
+ header.dysymtab.nundefsym = reader.Read32();
+ header.dysymtab.tocoff = reader.Read32();
+ header.dysymtab.ntoc = reader.Read32();
+ header.dysymtab.modtaboff = reader.Read32();
+ header.dysymtab.nmodtab = reader.Read32();
+ header.dysymtab.extrefsymoff = reader.Read32();
+ header.dysymtab.nextrefsyms = reader.Read32();
+ header.dysymtab.indirectsymoff = reader.Read32();
+ header.dysymtab.nindirectsyms = reader.Read32();
+ header.dysymtab.extreloff = reader.Read32();
+ header.dysymtab.nextrel = reader.Read32();
+ header.dysymtab.locreloff = reader.Read32();
+ header.dysymtab.nlocrel = reader.Read32();
+ header.dysymPresent = true;
+ break;
+ case LC_DYLD_CHAINED_FIXUPS:
+ header.chainedFixups.dataoff = reader.Read32();
+ header.chainedFixups.datasize = reader.Read32();
+ header.chainedFixupsPresent = true;
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ header.dyldInfo.rebase_off = reader.Read32();
+ header.dyldInfo.rebase_size = reader.Read32();
+ header.dyldInfo.bind_off = reader.Read32();
+ header.dyldInfo.bind_size = reader.Read32();
+ header.dyldInfo.weak_bind_off = reader.Read32();
+ header.dyldInfo.weak_bind_size = reader.Read32();
+ header.dyldInfo.lazy_bind_off = reader.Read32();
+ header.dyldInfo.lazy_bind_size = reader.Read32();
+ header.dyldInfo.export_off = reader.Read32();
+ header.dyldInfo.export_size = reader.Read32();
+ header.exportTrie.dataoff = header.dyldInfo.export_off;
+ header.exportTrie.datasize = header.dyldInfo.export_size;
+ header.exportTriePresent = true;
+ header.dyldInfoPresent = true;
+ break;
+ case LC_DYLD_EXPORTS_TRIE:
+ header.exportTrie.dataoff = reader.Read32();
+ header.exportTrie.datasize = reader.Read32();
+ header.exportTriePresent = true;
+ break;
+ case LC_THREAD:
+ case LC_UNIXTHREAD:
+ /*while (reader.GetOffset() < nextOffset)
+ {
+
+ thread_command thread;
+ thread.flavor = reader.Read32();
+ thread.count = reader.Read32();
+ switch (m_archId)
+ {
+ case MachOx64:
+ m_logger->LogDebug("x86_64 Thread state\n");
+ if (thread.flavor != X86_THREAD_STATE64)
+ {
+ reader.SeekRelative(thread.count * sizeof(uint32_t));
+ break;
+ }
+ //This wont be big endian so we can just read the whole thing
+ reader.Read(&thread.statex64, sizeof(thread.statex64));
+ header.entryPoints.push_back({thread.statex64.rip, false});
+ break;
+ case MachOx86:
+ m_logger->LogDebug("x86 Thread state\n");
+ if (thread.flavor != X86_THREAD_STATE32)
+ {
+ reader.SeekRelative(thread.count * sizeof(uint32_t));
+ break;
+ }
+ //This wont be big endian so we can just read the whole thing
+ reader.Read(&thread.statex86, sizeof(thread.statex86));
+ header.entryPoints.push_back({thread.statex86.eip, false});
+ break;
+ case MachOArm:
+ m_logger->LogDebug("Arm Thread state\n");
+ if (thread.flavor != _ARM_THREAD_STATE)
+ {
+ reader.SeekRelative(thread.count * sizeof(uint32_t));
+ break;
+ }
+ //This wont be big endian so we can just read the whole thing
+ reader.Read(&thread.statearmv7, sizeof(thread.statearmv7));
+ header.entryPoints.push_back({thread.statearmv7.r15, false});
+ break;
+ case MachOAarch64:
+ case MachOAarch6432:
+ m_logger->LogDebug("Aarch64 Thread state\n");
+ if (thread.flavor != _ARM_THREAD_STATE64)
+ {
+ reader.SeekRelative(thread.count * sizeof(uint32_t));
+ break;
+ }
+ reader.Read(&thread.stateaarch64, sizeof(thread.stateaarch64));
+ header.entryPoints.push_back({thread.stateaarch64.pc, false});
+ break;
+ case MachOPPC:
+ m_logger->LogDebug("PPC Thread state\n");
+ if (thread.flavor != PPC_THREAD_STATE)
+ {
+ reader.SeekRelative(thread.count * sizeof(uint32_t));
+ break;
+ }
+ //Read individual entries for endian reasons
+ header.entryPoints.push_back({reader.Read32(), false});
+ (void)reader.Read32();
+ (void)reader.Read32();
+ //Read the rest of the structure
+ (void)reader.Read(&thread.stateppc.r1, sizeof(thread.stateppc) - (3 * 4));
+ break;
+ case MachOPPC64:
+ m_logger->LogDebug("PPC64 Thread state\n");
+ if (thread.flavor != PPC_THREAD_STATE64)
+ {
+ reader.SeekRelative(thread.count * sizeof(uint32_t));
+ break;
+ }
+ header.entryPoints.push_back({reader.Read64(), false});
+ (void)reader.Read64();
+ (void)reader.Read64(); // Stack start
+ (void)reader.Read(&thread.stateppc64.r1, sizeof(thread.stateppc64) - (3 * 8));
+ break;
+ default:
+ m_logger->LogError("Unknown archid: %x", m_archId);
+ }
+
+ }*/
+ break;
+ case LC_LOAD_DYLIB:
+ {
+ uint32_t offset = reader.Read32();
+ if (offset < nextOffset)
+ {
+ reader.Seek(curOffset + offset);
+ std::string libname = reader.ReadCString(reader.GetOffset());
+ header.dylibs.push_back(libname);
+ }
+ }
+ break;
+ case LC_BUILD_VERSION:
+ {
+ // m_logger->LogDebug("LC_BUILD_VERSION:");
+ header.buildVersion.platform = reader.Read32();
+ header.buildVersion.minos = reader.Read32();
+ header.buildVersion.sdk = reader.Read32();
+ header.buildVersion.ntools = reader.Read32();
+ // m_logger->LogDebug("Platform: %s", BuildPlatformToString(header.buildVersion.platform).c_str());
+ // m_logger->LogDebug("MinOS: %s", BuildToolVersionToString(header.buildVersion.minos).c_str());
+ // m_logger->LogDebug("SDK: %s", BuildToolVersionToString(header.buildVersion.sdk).c_str());
+ for (uint32_t j = 0; (i < header.buildVersion.ntools) && (j < 10); j++)
+ {
+ uint32_t tool = reader.Read32();
+ uint32_t version = reader.Read32();
+ header.buildToolVersions.push_back({tool, version});
+ // m_logger->LogDebug("Build Tool: %s: %s", BuildToolToString(tool).c_str(),
+ // BuildToolVersionToString(version).c_str());
+ }
+ break;
+ }
+ case LC_FILESET_ENTRY:
+ {
+ throw ReadException();
+ }
+ default:
+ // m_logger->LogDebug("Unhandled command: %s : %" PRIu32 "\n", CommandToString(load.cmd).c_str(),
+ // load.cmdsize);
+ break;
+ }
+ if (reader.GetOffset() != nextOffset)
+ {
+ // m_logger->LogDebug("Didn't parse load command: %s fully %" PRIx64 ":%" PRIxPTR,
+ // CommandToString(load.cmd).c_str(), reader.GetOffset(), nextOffset);
+ }
+ reader.Seek(nextOffset);
+ }
+
+ for (auto& section : header.sections)
+ {
+ char sectionName[17];
+ memcpy(sectionName, section.sectname, sizeof(section.sectname));
+ sectionName[16] = 0;
+
+ char segmentName[sizeof(section.segname)+1];
+ memcpy(segmentName, section.segname, sizeof(section.segname));
+ segmentName[sizeof(segmentName)-1] = 0;
+
+ // Section names used to be image name and section only but some images have duplicate section names
+ // so we now also use the segment name, this also is more close to what is seen with LLVM.
+ // Justification: https://github.com/Vector35/binaryninja-api/pull/6454#issuecomment-2777465476
+ if (header.identifierPrefix.empty())
+ header.sectionNames.push_back(fmt::format("{}.{}", segmentName, sectionName));
+ else
+ header.sectionNames.push_back(fmt::format("{}::{}.{}", header.identifierPrefix, segmentName, sectionName));
+ }
+ }
+ catch (ReadException&)
+ {
+ return {};
+ }
+
+ return header;
+}
+
+std::vector<CacheSymbol> KernelCacheMachOHeader::ReadSymbolTable(Ref<BinaryView> bv, const TableInfo &symbolInfo, const TableInfo &stringInfo) const
+{
+ try {
+ BinaryReader reader(bv->GetParentView());
+ std::vector<CacheSymbol> symbolList;
+ // TODO: This assumes that 95% (or more) are going to be added.
+ symbolList.reserve(symbolInfo.entries);
+ for (uint64_t entryIndex = 0; entryIndex < symbolInfo.entries; entryIndex++)
+ {
+ nlist_64 nlist = {};
+ if (bv->GetAddressSize() == 4)
+ {
+ // 32-bit KC
+ struct nlist nlist32 = {};
+ reader.Seek(symbolInfo.address + (entryIndex * sizeof(nlist32)));
+ reader.Read(&nlist, sizeof(nlist32));
+ nlist.n_strx = nlist32.n_strx;
+ nlist.n_type = nlist32.n_type;
+ nlist.n_sect = nlist32.n_sect;
+ nlist.n_desc = nlist32.n_desc;
+ nlist.n_value = nlist32.n_value;
+ }
+ else
+ {
+ // 64-bit KC
+ reader.Seek(symbolInfo.address + (entryIndex * sizeof(nlist)));
+ reader.Read(&nlist, sizeof(nlist));
+ }
+
+ auto symbolAddress = nlist.n_value;
+ if (((nlist.n_type & N_TYPE) == N_INDR) || symbolAddress == 0)
+ continue;
+
+ if (nlist.n_strx >= stringInfo.entries)
+ {
+ // TODO: where logger?
+ LogError(
+ "Symbol entry at index %llu has a string offset of %u which is outside the strings buffer of size %llu "
+ "for symbol table %x",
+ entryIndex, nlist.n_strx, stringInfo.address, stringInfo.entries);
+ continue;
+ }
+
+ reader.Seek(stringInfo.address + nlist.n_strx);
+ std::string symbolName = reader.ReadCString();
+ if (symbolName == "<redacted>")
+ continue;
+
+ std::optional<BNSymbolType> symbolType;
+ if ((nlist.n_type & N_TYPE) == N_SECT && nlist.n_sect > 0 && (size_t)(nlist.n_sect - 1) < sections.size())
+ symbolType = DataSymbol;
+ else if ((nlist.n_type & N_TYPE) == N_ABS)
+ symbolType = DataSymbol;
+ else if ((nlist.n_type & N_EXT))
+ symbolType = ExternalSymbol;
+
+ if (!symbolType.has_value())
+ {
+ // TODO: Where logger?
+ LogError("Symbol %s at address %llx has unknown symbol type", symbolName.c_str(), symbolAddress);
+ continue;
+ }
+
+ std::optional<uint32_t> flags;
+ for (auto s : sections)
+ {
+ if (s.addr <= symbolAddress && symbolAddress < s.addr + s.size)
+ {
+ // First section to contain the address we will use its flags.
+ flags = s.flags;
+ break;
+ }
+ }
+
+ if (symbolType != ExternalSymbol)
+ {
+ if (!flags.has_value())
+ {
+ // TODO: where logger?
+ LogError("Symbol %s at address %llx is not in any section", symbolName.c_str(), symbolAddress);
+ continue;
+ }
+
+ if ((flags.value() & S_ATTR_PURE_INSTRUCTIONS) == S_ATTR_PURE_INSTRUCTIONS
+ || (flags.value() & S_ATTR_SOME_INSTRUCTIONS) == S_ATTR_SOME_INSTRUCTIONS)
+ symbolType = FunctionSymbol;
+ else
+ symbolType = DataSymbol;
+ }
+ if ((nlist.n_desc & N_ARM_THUMB_DEF) == N_ARM_THUMB_DEF)
+ symbolAddress++;
+
+ CacheSymbol symbol;
+ symbol.address = symbolAddress;
+ symbol.name = std::move(symbolName);
+ symbol.type = symbolType.value();
+ symbolList.emplace_back(symbol);
+ }
+
+ return symbolList;
+ }
+ catch (ReadException& ex) {
+ LogError("Failed to read symbol table: %s", ex.what());
+ return {};
+ }
+
+}
+
+bool KernelCacheMachOHeader::AddExportTerminalSymbol(
+ std::vector<CacheSymbol>& symbols, const std::string& symbolName, const uint8_t *current, const uint8_t *end) const
+{
+ uint64_t symbolFlags = readValidULEB128(current, end);
+ if (symbolFlags & EXPORT_SYMBOL_FLAGS_REEXPORT)
+ return false;
+
+ uint64_t imageOffset = readValidULEB128(current, end);
+ uint64_t symbolAddress = textBase + imageOffset;
+ if (symbolName.empty() || symbolAddress == 0)
+ return false;
+
+ // Tries to get the symbol type based off the section containing it.
+ auto sectionSymbolType = [&]() -> BNSymbolType {
+ uint32_t sectionFlags = 0;
+ for (const auto& section : sections)
+ {
+ if (symbolAddress >= section.addr && symbolAddress < section.addr + section.size)
+ {
+ // Take the flags from the first containing section.
+ sectionFlags = section.flags;
+ break;
+ }
+ }
+
+ // TODO: Is this enough to determine a function symbol?
+ // TODO: Might be the cause of https://github.com/Vector35/binaryninja-api/issues/6526
+ // Check the sections flags to see if we actually have a function symbol instead.
+ if (sectionFlags & S_ATTR_PURE_INSTRUCTIONS || sectionFlags & S_ATTR_SOME_INSTRUCTIONS)
+ return FunctionSymbol;
+
+ // By default, just return data symbol.
+ return DataSymbol;
+ };
+
+ switch (symbolFlags & EXPORT_SYMBOL_FLAGS_KIND_MASK)
+ {
+ case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
+ case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
+ symbols.emplace_back(sectionSymbolType(), symbolAddress, symbolName);
+ break;
+ case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
+ symbols.emplace_back(DataSymbol, symbolAddress, symbolName);
+ break;
+ default:
+ LogWarn("Unhandled export symbol kind: %llx", symbolFlags & EXPORT_SYMBOL_FLAGS_KIND_MASK);
+ return false;
+ }
+
+ return true;
+}
+
+std::vector<CacheSymbol> KernelCacheMachOHeader::ReadExportSymbolTrie(Ref<BinaryView> bv) const
+{
+ // nothing to do if there’s no export‐trie
+ if (!exportTriePresent || exportTrie.datasize == 0 || exportTrie.dataoff == 0)
+ return {};
+ std::vector<CacheSymbol> symbols = {};
+ try {
+ DataBuffer exportTrieBuffer = bv->GetParentView()->ReadBuffer(exportTrie.dataoff, exportTrie.datasize);
+ const uint8_t* begin = static_cast<const uint8_t*>(exportTrieBuffer.GetData());
+ const uint8_t* end = begin + exportTrieBuffer.GetLength();
+ const uint8_t *cursor = begin;
+
+ struct Node
+ {
+ const uint8_t* cursor;
+ std::string text;
+ };
+ std::vector<Node> stack;
+ stack.reserve(64);
+ stack.push_back({ /* cursor */ begin, /* text */ "" });
+
+ while (!stack.empty())
+ {
+ Node node = std::move(stack.back());
+ stack.pop_back();
+
+ cursor = node.cursor;
+ const std::string currentText = std::move(node.text);
+
+ if (cursor > end)
+ {
+ LogError("Export Trie: Cursor left trie during initial bounds check");
+ throw ReadException();
+ }
+
+ uint64_t terminalSize = readValidULEB128(cursor, end);
+ const uint8_t* childCursor = cursor + terminalSize;
+
+ // If there's terminal data, define the symbol
+ if (terminalSize != 0)
+ {
+ AddExportTerminalSymbol(symbols, currentText, cursor, end);
+ }
+
+ cursor = childCursor;
+ if (cursor > end)
+ {
+ LogError("Export Trie: Cursor left trie while moving to child offset");
+ throw ReadException();
+ }
+
+ uint8_t childCount = *cursor;
+ cursor++;
+ if (cursor > end)
+ {
+ LogError("Export Trie: Cursor left trie while reading child count");
+ throw ReadException();
+ }
+
+ std::vector<Node> children;
+ children.reserve(childCount);
+ for (uint8_t i = 0; i < childCount; ++i)
+ {
+ if (cursor > end)
+ {
+ LogError("Export Trie: Cursor left trie while reading children");
+ throw ReadException();
+ }
+
+ std::string childText;
+ while (cursor <= end && *cursor != 0) {
+ childText.push_back(*cursor);
+ cursor++;
+ }
+ cursor++; // skip the `\0`
+ if (cursor > end)
+ {
+ LogError("Export Trie: Cursor left trie while reading child text");
+ throw ReadException();
+ }
+
+ uint64_t nextOffset = readValidULEB128(cursor, end);
+ if (nextOffset == 0)
+ {
+ LogError("Export Trie: Child offset is zero");
+ throw ReadException();
+ }
+
+ children.push_back({ begin + nextOffset, currentText + childText });
+ }
+
+ // Push in reverse so that the first child is processed next
+ for (auto it = children.rbegin(); it != children.rend(); ++it)
+ {
+ stack.push_back(*it);
+ }
+ }
+ }
+ catch (ReadException&)
+ {
+ LogError("Export trie is malformed. Could not load Exported symbol names.");
+ }
+
+ return symbols;
+}
diff --git a/view/kernelcache/core/MachO.h b/view/kernelcache/core/MachO.h
new file mode 100644
index 00000000..8c2a7709
--- /dev/null
+++ b/view/kernelcache/core/MachO.h
@@ -0,0 +1,79 @@
+#pragma once
+
+// TODO: Including this adds a bunch of binary ninja specific stuff :ugh:
+#include "view/macho/machoview.h"
+
+struct CacheSymbol;
+
+// Used when reading symbol/string table info.
+struct TableInfo
+{
+ // VM address where the reading will begin.
+ uint64_t address;
+ // Number of entries in the table.
+ uint32_t entries;
+};
+
+struct KernelCacheMachOHeader
+{
+ uint64_t textBase = 0;
+ uint64_t loadCommandOffset = 0;
+ BinaryNinja::mach_header_64 ident;
+ // NOTE: This should never be empty.
+ std::string identifierPrefix;
+ std::string installName;
+
+ std::vector<std::pair<uint64_t, bool>> entryPoints;
+ std::vector<uint64_t> m_entryPoints; // list of entrypoints
+
+ BinaryNinja::symtab_command symtab;
+ BinaryNinja::dysymtab_command dysymtab;
+ BinaryNinja::dyld_info_command dyldInfo;
+ BinaryNinja::routines_command_64 routines64;
+ BinaryNinja::function_starts_command functionStarts;
+ std::vector<BinaryNinja::section_64> moduleInitSections;
+ BinaryNinja::linkedit_data_command exportTrie;
+ BinaryNinja::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<BinaryNinja::segment_command_64> segments; // only three types of sections __TEXT, __DATA, __IMPORT
+ BinaryNinja::segment_command_64 linkeditSegment;
+ std::vector<BinaryNinja::section_64> sections;
+ std::vector<std::string> sectionNames;
+
+ std::vector<BinaryNinja::section_64> symbolStubSections;
+ std::vector<BinaryNinja::section_64> symbolPointerSections;
+
+ std::vector<std::string> dylibs;
+
+ BinaryNinja::build_version_command buildVersion;
+ std::vector<BinaryNinja::build_tool_version> buildToolVersions;
+
+ std::string exportTriePath;
+
+ bool linkeditPresent = false;
+ bool dysymPresent = false;
+ bool dyldInfoPresent = false;
+ bool exportTriePresent = false;
+ bool chainedFixupsPresent = false;
+ bool routinesPresent = false;
+ bool functionStartsPresent = false;
+ bool relocatable = false;
+
+ static std::optional<KernelCacheMachOHeader> ParseHeaderForAddress(
+ BinaryNinja::Ref<BinaryNinja::BinaryView> bv, uint64_t vmAddress, uint64_t fileAddress, const std::string& imagePath);
+
+ std::vector<CacheSymbol> ReadSymbolTable(BinaryNinja::Ref<BinaryNinja::BinaryView> bv, const TableInfo &symbolInfo, const TableInfo &stringInfo) const;
+
+ bool AddExportTerminalSymbol(
+ std::vector<CacheSymbol>& symbols, const std::string& symbolName, const uint8_t* current,
+ const uint8_t* end) const;
+
+ bool ProcessLinkEditTrie(std::vector<CacheSymbol>& symbols, const std::string& currentText, const uint8_t* begin,
+ const uint8_t* current, const uint8_t* end) const;
+
+ std::vector<CacheSymbol> ReadExportSymbolTrie(BinaryNinja::Ref<BinaryNinja::BinaryView> bv) const;
+
+ std::vector<uint64_t> ReadFunctionTable(BinaryNinja::Ref<BinaryNinja::BinaryView> bv) const;
+};
diff --git a/view/kernelcache/core/MachOProcessor.cpp b/view/kernelcache/core/MachOProcessor.cpp
new file mode 100644
index 00000000..e22ce853
--- /dev/null
+++ b/view/kernelcache/core/MachOProcessor.cpp
@@ -0,0 +1,332 @@
+#include "MachOProcessor.h"
+#include "KernelCache.h"
+
+
+using namespace BinaryNinja;
+
+KernelCacheMachOProcessor::KernelCacheMachOProcessor(Ref<BinaryView> view)
+{
+ m_view = view;
+ m_logger = new Logger("KernelCache.MachOProcessor", view->GetFile()->GetSessionId());
+
+ // Adjust processor settings.
+ if (Ref<Settings> settings = m_view->GetLoadSettings(KC_VIEW_NAME))
+ {
+ if (settings->Contains("loader.kc.processFunctionStarts"))
+ m_applyFunctions = settings->Get<bool>("loader.kc.processFunctionStarts", m_view);
+ }
+}
+
+void KernelCacheMachOProcessor::ApplyHeader(const KernelCache& cache, KernelCacheMachOHeader& header)
+{
+ auto typeLibraryFromName = [&](const std::string& name) -> Ref<TypeLibrary> {
+ // Check to see if we have already loaded the type library.
+ if (auto typeLib = m_view->GetTypeLibrary(name))
+ return typeLib;
+
+ auto typeLibs = m_view->GetDefaultPlatform()->GetTypeLibrariesByName(name);
+ if (!typeLibs.empty())
+ return typeLibs.front();
+ return nullptr;
+ };
+
+ // Add a section for the header itself.
+ std::string headerSection = fmt::format("{}::__macho_header", header.identifierPrefix);
+ uint64_t machHeaderSize = m_view->GetAddressSize() == 8 ? sizeof(mach_header_64) : sizeof(mach_header);
+ uint64_t headerSectionSize = machHeaderSize + header.ident.sizeofcmds;
+ m_view->AddUserSection(headerSection, header.textBase, headerSectionSize, ReadOnlyDataSectionSemantics);
+
+ ApplyHeaderSections(header);
+ ApplyHeaderDataVariables(header);
+
+ // Pull the available type library for the image we are loading, so we can apply known types.
+ auto typeLib = typeLibraryFromName(header.installName);
+
+ if (m_applyFunctions && header.functionStartsPresent)
+ {
+ auto targetPlatform = m_view->GetDefaultPlatform();
+ auto functions = header.ReadFunctionTable(m_view);
+ for (const auto& func : functions)
+ m_view->AddFunctionForAnalysis(targetPlatform, func, false);
+ }
+
+ m_view->BeginBulkModifySymbols();
+
+ // Apply symbols from symbol table.
+ if (header.symtab.symoff != 0)
+ {
+ // NOTE: This table is read relative to the link edit segment file base.
+ // NOTE: This does not handle the shared .symbols cache entry symbols, that is the responsibility of the caller.
+ TableInfo symbolInfo = { header.symtab.symoff, header.symtab.nsyms };
+ TableInfo stringInfo = { header.symtab.stroff, header.symtab.strsize };
+ const auto symbols = header.ReadSymbolTable(m_view, symbolInfo, stringInfo);
+ for (const auto& sym : symbols)
+ {
+ auto [symbol, symbolType] = sym.GetBNSymbolAndType(*m_view);
+ ApplySymbol(m_view, typeLib, symbol, symbolType);
+ }
+ }
+
+ // Apply symbols from export trie.
+ if (header.exportTriePresent)
+ {
+ // NOTE: This table is read relative to the link edit segment file base.
+ const auto exportSymbols = header.ReadExportSymbolTrie(m_view);
+ for (const auto& sym : exportSymbols)
+ {
+ auto [symbol, symbolType] = sym.GetBNSymbolAndType(*m_view);
+ ApplySymbol(m_view, typeLib, symbol, symbolType);
+ }
+ }
+ m_view->EndBulkModifySymbols();
+}
+
+uint64_t KernelCacheMachOProcessor::ApplyHeaderSections(KernelCacheMachOHeader& header)
+{
+ auto initSection = [&](const section_64& section, const std::string& sectionName) {
+ if (!section.size)
+ return false;
+
+ std::string type;
+ BNSectionSemantics semantics = DefaultSectionSemantics;
+ switch (section.flags & 0xff)
+ {
+ case S_REGULAR:
+ if (section.flags & S_ATTR_PURE_INSTRUCTIONS)
+ {
+ type = "PURE_CODE";
+ semantics = ReadOnlyCodeSectionSemantics;
+ }
+ else if (section.flags & S_ATTR_SOME_INSTRUCTIONS)
+ {
+ type = "CODE";
+ semantics = ReadOnlyCodeSectionSemantics;
+ }
+ else
+ {
+ type = "REGULAR";
+ }
+ break;
+ case S_ZEROFILL:
+ type = "ZEROFILL";
+ semantics = ReadWriteDataSectionSemantics;
+ break;
+ case S_CSTRING_LITERALS:
+ type = "CSTRING_LITERALS";
+ semantics = ReadOnlyDataSectionSemantics;
+ break;
+ case S_4BYTE_LITERALS:
+ type = "4BYTE_LITERALS";
+ break;
+ case S_8BYTE_LITERALS:
+ type = "8BYTE_LITERALS";
+ break;
+ case S_LITERAL_POINTERS:
+ type = "LITERAL_POINTERS";
+ semantics = ReadOnlyDataSectionSemantics;
+ break;
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ type = "NON_LAZY_SYMBOL_POINTERS";
+ semantics = ReadOnlyDataSectionSemantics;
+ break;
+ case S_LAZY_SYMBOL_POINTERS:
+ type = "LAZY_SYMBOL_POINTERS";
+ semantics = ReadOnlyDataSectionSemantics;
+ break;
+ case S_SYMBOL_STUBS:
+ type = "SYMBOL_STUBS";
+ semantics = ReadOnlyCodeSectionSemantics;
+ break;
+ case S_MOD_INIT_FUNC_POINTERS:
+ type = "MOD_INIT_FUNC_POINTERS";
+ semantics = ReadOnlyDataSectionSemantics;
+ break;
+ case S_MOD_TERM_FUNC_POINTERS:
+ type = "MOD_TERM_FUNC_POINTERS";
+ semantics = ReadOnlyDataSectionSemantics;
+ break;
+ case S_COALESCED:
+ type = "COALESCED";
+ break;
+ case S_GB_ZEROFILL:
+ type = "GB_ZEROFILL";
+ semantics = ReadWriteDataSectionSemantics;
+ break;
+ case S_INTERPOSING:
+ type = "INTERPOSING";
+ break;
+ case S_16BYTE_LITERALS:
+ type = "16BYTE_LITERALS";
+ break;
+ case S_DTRACE_DOF:
+ type = "DTRACE_DOF";
+ break;
+ case S_LAZY_DYLIB_SYMBOL_POINTERS:
+ type = "LAZY_DYLIB_SYMBOL_POINTERS";
+ semantics = ReadOnlyDataSectionSemantics;
+ break;
+ case S_THREAD_LOCAL_REGULAR:
+ type = "THREAD_LOCAL_REGULAR";
+ break;
+ case S_THREAD_LOCAL_ZEROFILL:
+ type = "THREAD_LOCAL_ZEROFILL";
+ break;
+ case S_THREAD_LOCAL_VARIABLES:
+ type = "THREAD_LOCAL_VARIABLES";
+ break;
+ case S_THREAD_LOCAL_VARIABLE_POINTERS:
+ type = "THREAD_LOCAL_VARIABLE_POINTERS";
+ break;
+ case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS:
+ type = "THREAD_LOCAL_INIT_FUNCTION_POINTERS";
+ break;
+ default:
+ type = "UNKNOWN";
+ break;
+ }
+
+ if (strncmp(section.sectname, "__text", sizeof(section.sectname)) == 0)
+ semantics = ReadOnlyCodeSectionSemantics;
+ if (strncmp(section.sectname, "__const", sizeof(section.sectname)) == 0)
+ semantics = ReadOnlyDataSectionSemantics;
+ if (strncmp(section.sectname, "__data", sizeof(section.sectname)) == 0)
+ semantics = ReadWriteDataSectionSemantics;
+ if (strncmp(section.sectname, "__auth_got", sizeof(section.sectname)) == 0)
+ semantics = ReadOnlyDataSectionSemantics;
+ if (strncmp(section.segname, "__DATA_CONST", sizeof(section.segname)) == 0)
+ semantics = ReadOnlyDataSectionSemantics;
+
+ // Typically a view would add auto sections but those won't persist when loading the BNDB.
+ // if we want to use an auto section here we would need to allow the core to apply auto sections from the database.
+ m_view->AddUserSection(sectionName, section.addr, section.size, semantics, type, section.align);
+
+ return true;
+ };
+
+ uint64_t addedSections = 0;
+ for (size_t i = 0; i < header.sections.size() && i < header.sectionNames.size(); i++)
+ {
+ if (initSection(header.sections[i], header.sectionNames[i]))
+ addedSections++;
+ }
+ return addedSections;
+}
+
+void KernelCacheMachOProcessor::ApplyHeaderDataVariables(KernelCacheMachOHeader& header)
+{
+ // TODO: By using a binary reader we assume the sections have all been mapped.
+ // TODO: Maybe we should just use the virtual memory reader...
+ // TODO: We can define symbols and data variables even if there is no backing region FWIW
+ BinaryReader reader(m_view);
+ // TODO: Do we support non 64 bit header?
+ reader.Seek(header.textBase + sizeof(mach_header_64));
+
+ m_view->DefineDataVariable(header.textBase, Type::NamedType(m_view, QualifiedName("mach_header_64")));
+ m_view->DefineAutoSymbol(
+ new Symbol(DataSymbol, "__macho_header::" + header.identifierPrefix, header.textBase, LocalBinding));
+
+ auto applyLoadCommand = [&](uint64_t cmdAddr, const load_command& load) {
+ switch (load.cmd)
+ {
+ case LC_SEGMENT:
+ {
+ m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("segment_command")));
+ reader.SeekRelative(5 * 8);
+ size_t numSections = reader.Read32();
+ reader.SeekRelative(4);
+ for (size_t j = 0; j < numSections; j++)
+ {
+ m_view->DefineDataVariable(reader.GetOffset(), Type::NamedType(m_view, QualifiedName("section")));
+ auto sectionSymName =
+ fmt::format("__macho_section::{}_[{}]", header.identifierPrefix, std::to_string(j));
+ auto sectionSym = new Symbol(DataSymbol, sectionSymName, reader.GetOffset(), LocalBinding);
+ m_view->DefineAutoSymbol(sectionSym);
+ reader.SeekRelative((8 * 8) + 4);
+ }
+ break;
+ }
+ case LC_SEGMENT_64:
+ {
+ m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("segment_command_64")));
+ reader.SeekRelative(7 * 8);
+ size_t numSections = reader.Read32();
+ reader.SeekRelative(4);
+ for (size_t j = 0; j < numSections; j++)
+ {
+ m_view->DefineDataVariable(reader.GetOffset(), Type::NamedType(m_view, QualifiedName("section_64")));
+ auto sectionSymName =
+ fmt::format("__macho_section_64::{}_[{}]", header.identifierPrefix, std::to_string(j));
+ auto sectionSym = new Symbol(DataSymbol, sectionSymName, reader.GetOffset(), LocalBinding);
+ m_view->DefineAutoSymbol(sectionSym);
+ reader.SeekRelative(10 * 8);
+ }
+ break;
+ }
+ case LC_SYMTAB:
+ m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("symtab")));
+ break;
+ case LC_DYSYMTAB:
+ m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("dysymtab")));
+ break;
+ case LC_UUID:
+ m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, 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:
+ m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("dylib_command")));
+ if (load.cmdsize - 24 <= 150)
+ m_view->DefineDataVariable(
+ cmdAddr + 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:
+ m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("linkedit_data")));
+ break;
+ case LC_ENCRYPTION_INFO:
+ m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("encryption_info")));
+ break;
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_IPHONEOS:
+ m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("version_min")));
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("dyld_info")));
+ break;
+ default:
+ m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("load_command")));
+ break;
+ }
+ };
+
+ try
+ {
+ for (size_t i = 0; i < header.ident.ncmds; i++)
+ {
+ load_command load {};
+ uint64_t curOffset = reader.GetOffset();
+ load.cmd = reader.Read32();
+ load.cmdsize = reader.Read32();
+
+ applyLoadCommand(curOffset, load);
+ m_view->DefineAutoSymbol(new Symbol(DataSymbol,
+ "__macho_load_command::" + header.identifierPrefix + "_[" + std::to_string(i) + "]", curOffset,
+ LocalBinding));
+
+ uint64_t nextOffset = curOffset + load.cmdsize;
+ reader.Seek(nextOffset);
+ }
+ }
+ catch (ReadException&)
+ {
+ m_logger->LogError("Error when applying Mach-O header types at %llx", header.textBase);
+ }
+}
diff --git a/view/kernelcache/core/MachOProcessor.h b/view/kernelcache/core/MachOProcessor.h
new file mode 100644
index 00000000..3e353a44
--- /dev/null
+++ b/view/kernelcache/core/MachOProcessor.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "MachO.h"
+#include "KernelCache.h"
+
+// Process `KernelCacheMachOHeader`.
+class KernelCacheMachOProcessor
+{
+ BinaryNinja::Ref<BinaryNinja::BinaryView> m_view;
+ BinaryNinja::Ref<BinaryNinja::Logger> m_logger;
+
+ bool m_applyFunctions = true;
+
+public:
+ explicit KernelCacheMachOProcessor(BinaryNinja::Ref<BinaryNinja::BinaryView> view);
+
+ // Initialize header information such as sections and symbols.
+ void ApplyHeader(const KernelCache& cache, KernelCacheMachOHeader& header);
+
+ uint64_t ApplyHeaderSections(KernelCacheMachOHeader& header);
+
+ void ApplyHeaderDataVariables(KernelCacheMachOHeader& header);
+};
diff --git a/view/kernelcache/core/MetadataSerializable.cpp b/view/kernelcache/core/MetadataSerializable.cpp
deleted file mode 100644
index dea6243e..00000000
--- a/view/kernelcache/core/MetadataSerializable.cpp
+++ /dev/null
@@ -1,552 +0,0 @@
-#include "MetadataSerializable.hpp"
-
-namespace KernelCacheCore {
-
- void Serialize(SerializationContext& context, std::string_view str) {
- context.writer.String(str.data(), str.length());
- }
-
- void Serialize(SerializationContext& context, const char* value) {
- Serialize(context, std::string_view(value));
- }
-
- void Serialize(SerializationContext& context, bool b)
- {
- context.writer.Bool(b);
- }
-
- void Serialize(SerializationContext& context, int8_t value) {
- context.writer.Int(value);
- }
-
- void Serialize(SerializationContext& context, uint8_t value) {
- context.writer.Uint(value);
- }
-
- void Serialize(SerializationContext& context, int16_t value) {
- context.writer.Int(value);
- }
-
- void Serialize(SerializationContext& context, uint16_t value) {
- context.writer.Uint(value);
- }
-
- void Serialize(SerializationContext& context, int32_t value) {
- context.writer.Int(value);
- }
-
- void Serialize(SerializationContext& context, uint32_t value) {
- context.writer.Uint(value);
- }
-
- void Serialize(SerializationContext& context, int64_t value) {
- context.writer.Int64(value);
- }
-
- void Serialize(SerializationContext& context, uint64_t value) {
- context.writer.Uint64(value);
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, bool& b) {
- b = context.doc[name.data()].GetBool();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, uint8_t& b)
- {
- b = static_cast<uint8_t>(context.doc[name.data()].GetUint64());
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, uint16_t& b)
- {
- b = static_cast<uint16_t>(context.doc[name.data()].GetUint64());
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, uint32_t& b)
- {
- b = static_cast<uint32_t>(context.doc[name.data()].GetUint64());
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, uint64_t& b)
- {
- b = context.doc[name.data()].GetUint64();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, int8_t& b)
- {
- b = context.doc[name.data()].GetInt64();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, int16_t& b)
- {
- b = context.doc[name.data()].GetInt64();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, int32_t& b)
- {
- b = context.doc[name.data()].GetInt();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, int64_t& b)
- {
- b = context.doc[name.data()].GetInt64();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::string& b)
- {
- b = context.doc[name.data()].GetString();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::map<uint64_t, std::string>& b)
- {
- for (auto& i : context.doc[name.data()].GetArray())
- b[i.GetArray()[0].GetUint64()] = i.GetArray()[1].GetString();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<uint64_t, std::string>& b)
- {
- for (auto& i : context.doc[name.data()].GetArray())
- b[i.GetArray()[0].GetUint64()] = i.GetArray()[1].GetString();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<uint64_t, uint64_t>& b)
- {
- for (auto& i : context.doc[name.data()].GetArray())
- b[i.GetArray()[0].GetUint64()] = i.GetArray()[1].GetUint64();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<std::string, std::unordered_map<uint64_t, uint64_t>>& b)
- {
- for (auto& i : context.doc[name.data()].GetArray())
- {
- std::string key = i.GetArray()[0].GetString();
- std::unordered_map<uint64_t, uint64_t> memArray;
- for (auto& member : i.GetArray()[1].GetArray())
- {
- memArray[member.GetArray()[0].GetUint64()] = member.GetArray()[1].GetUint64();
- }
- b[key] = memArray;
- }
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<std::string, std::string>& b)
- {
- for (auto& i : context.doc[name.data()].GetArray())
- b[i.GetArray()[0].GetString()] = i.GetArray()[1].GetString();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::vector<std::string>& b)
- {
- for (auto& i : context.doc[name.data()].GetArray())
- b.emplace_back(i.GetString());
- }
-
- // Note: This flattens the pair into [first, second.first, second.second] with no nested arrays.
- void Serialize(SerializationContext& context, const std::pair<uint64_t, std::pair<uint64_t, uint64_t>>& value)
- {
- context.writer.StartArray();
- Serialize(context, value.first);
- Serialize(context, value.second.first);
- Serialize(context, value.second.second);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::vector<std::pair<uint64_t, std::pair<uint64_t, uint64_t>>>& b)
- {
- for (auto& i : context.doc[name.data()].GetArray())
- {
- std::pair<uint64_t, std::pair<uint64_t, uint64_t>> j;
- j.first = i.GetArray()[0].GetUint64();
- j.second.first = i.GetArray()[1].GetUint64();
- j.second.second = i.GetArray()[2].GetUint64();
- b.push_back(j);
- }
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::vector<std::pair<uint64_t, bool>>& b)
- {
- for (auto& i : context.doc[name.data()].GetArray())
- {
- std::pair<uint64_t, bool> j;
- j.first = i.GetArray()[0].GetUint64();
- j.second = i.GetArray()[1].GetBool();
- b.push_back(j);
- }
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::vector<uint64_t>& b)
- {
- for (auto& i : context.doc[name.data()].GetArray())
- {
- b.push_back(i.GetUint64());
- }
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<std::string, uint64_t>& b)
- {
- for (auto& i : context.doc[name.data()].GetArray())
- {
- b[i.GetArray()[0].GetString()] = i.GetArray()[1].GetUint64();
- }
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::vector<std::pair<uint64_t, std::vector<std::pair<uint64_t, std::string>>>>& b)
- {
- for (auto& i : context.doc[name.data()].GetArray())
- {
- std::pair<uint64_t, std::vector<std::pair<uint64_t, std::string>>> j;
- j.first = i.GetArray()[0].GetUint64();
- for (auto& k : i.GetArray()[1].GetArray())
- {
- j.second.push_back({k.GetArray()[0].GetUint64(), k.GetArray()[1].GetString()});
- }
- b.push_back(j);
- }
- }
-
- void Serialize(SerializationContext& context, const mach_header_64& value) {
- context.writer.StartArray();
- Serialize(context, value.magic);
- // cputype and cpusubtype are signed but were serialized as unsigned in
- // v4.2 (metadata version 2). We continue serializing them as unsigned
- // so we don't need to bump the metadata version.
- Serialize(context, static_cast<uint32_t>(value.cputype));
- Serialize(context, static_cast<uint32_t>(value.cpusubtype));
- Serialize(context, value.filetype);
- Serialize(context, value.ncmds);
- Serialize(context, value.sizeofcmds);
- Serialize(context, value.flags);
- Serialize(context, value.reserved);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, mach_header_64& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- b.magic = bArr[0].GetUint();
- b.cputype = bArr[1].GetUint();
- b.cpusubtype = bArr[2].GetUint();
- b.filetype = bArr[3].GetUint();
- b.ncmds = bArr[4].GetUint();
- b.sizeofcmds = bArr[5].GetUint();
- b.flags = bArr[6].GetUint();
- b.reserved = bArr[7].GetUint();
- }
-
- void Serialize(SerializationContext& context, const symtab_command& value)
- {
- context.writer.StartArray();
- Serialize(context, value.cmd);
- Serialize(context, value.cmdsize);
- Serialize(context, value.symoff);
- Serialize(context, value.nsyms);
- Serialize(context, value.stroff);
- Serialize(context, value.strsize);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, symtab_command& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- b.cmd = bArr[0].GetUint();
- b.cmdsize = bArr[1].GetUint();
- b.symoff = bArr[2].GetUint();
- b.nsyms = bArr[3].GetUint();
- b.stroff = bArr[4].GetUint();
- b.strsize = bArr[5].GetUint();
- }
-
- void Serialize(SerializationContext& context, const dysymtab_command& value)
- {
- context.writer.StartArray();
- Serialize(context, value.cmd);
- Serialize(context, value.cmdsize);
- Serialize(context, value.ilocalsym);
- Serialize(context, value.nlocalsym);
- Serialize(context, value.iextdefsym);
- Serialize(context, value.nextdefsym);
- Serialize(context, value.iundefsym);
- Serialize(context, value.nundefsym);
- Serialize(context, value.tocoff);
- Serialize(context, value.ntoc);
- Serialize(context, value.modtaboff);
- Serialize(context, value.nmodtab);
- Serialize(context, value.extrefsymoff);
- Serialize(context, value.nextrefsyms);
- Serialize(context, value.indirectsymoff);
- Serialize(context, value.nindirectsyms);
- Serialize(context, value.extreloff);
- Serialize(context, value.nextrel);
- Serialize(context, value.locreloff);
- Serialize(context, value.nlocrel);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, dysymtab_command& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- b.cmd = bArr[0].GetUint();
- b.cmdsize = bArr[1].GetUint();
- b.ilocalsym = bArr[2].GetUint();
- b.nlocalsym = bArr[3].GetUint();
- b.iextdefsym = bArr[4].GetUint();
- b.nextdefsym = bArr[5].GetUint();
- b.iundefsym = bArr[6].GetUint();
- b.nundefsym = bArr[7].GetUint();
- b.tocoff = bArr[8].GetUint();
- b.ntoc = bArr[9].GetUint();
- b.modtaboff = bArr[10].GetUint();
- b.nmodtab = bArr[11].GetUint();
- b.extrefsymoff = bArr[12].GetUint();
- b.nextrefsyms = bArr[13].GetUint();
- b.indirectsymoff = bArr[14].GetUint();
- b.nindirectsyms = bArr[15].GetUint();
- b.extreloff = bArr[16].GetUint();
- b.nextrel = bArr[17].GetUint();
- b.locreloff = bArr[18].GetUint();
- b.nlocrel = bArr[19].GetUint();
- }
-
- void Serialize(SerializationContext& context, const dyld_info_command& value)
- {
- context.writer.StartArray();
- Serialize(context, value.cmd);
- Serialize(context, value.cmdsize);
- Serialize(context, value.rebase_off);
- Serialize(context, value.rebase_size);
- Serialize(context, value.bind_off);
- Serialize(context, value.bind_size);
- Serialize(context, value.weak_bind_off);
- Serialize(context, value.weak_bind_size);
- Serialize(context, value.lazy_bind_off);
- Serialize(context, value.lazy_bind_size);
- Serialize(context, value.export_off);
- Serialize(context, value.export_size);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, dyld_info_command& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- b.cmd = bArr[0].GetUint();
- b.cmdsize = bArr[1].GetUint();
- b.rebase_off = bArr[2].GetUint();
- b.rebase_size = bArr[3].GetUint();
- b.bind_off = bArr[4].GetUint();
- b.bind_size = bArr[5].GetUint();
- b.weak_bind_off = bArr[6].GetUint();
- b.weak_bind_size = bArr[7].GetUint();
- b.lazy_bind_off = bArr[8].GetUint();
- b.lazy_bind_size = bArr[9].GetUint();
- b.export_off = bArr[10].GetUint();
- b.export_size = bArr[11].GetUint();
- }
-
- void Serialize(SerializationContext& context, const routines_command_64& value)
- {
- context.writer.StartArray();
- Serialize(context, value.cmd);
- Serialize(context, value.cmdsize);
- Serialize(context, value.init_address);
- Serialize(context, value.init_module);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, routines_command_64& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- // Because we might open databases that had not previously serialized this, we must allow
- // an empty array, otherwise we will crash!
- if (bArr.Size() < 4)
- return;
- b.cmd = bArr[0].GetUint();
- b.cmdsize = bArr[1].GetUint();
- b.init_address = bArr[2].GetUint64();
- b.init_module = bArr[3].GetUint64();
- }
-
- void Serialize(SerializationContext& context, const function_starts_command& value)
- {
- context.writer.StartArray();
- Serialize(context, value.cmd);
- Serialize(context, value.cmdsize);
- Serialize(context, value.funcoff);
- Serialize(context, value.funcsize);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, function_starts_command& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- b.cmd = bArr[0].GetUint();
- b.cmdsize = bArr[1].GetUint();
- b.funcoff = bArr[2].GetUint();
- b.funcsize = bArr[3].GetUint();
- }
-
- void Serialize(SerializationContext& context, const section_64& value)
- {
- context.writer.StartArray();
-
- std::string_view sectname(value.sectname, 16);
- std::string_view segname(value.segname, 16);
-
- Serialize(context, sectname.substr(0, sectname.find('\0')));
- Serialize(context, segname.substr(0, segname.find('\0')));
- Serialize(context, value.addr);
- Serialize(context, value.size);
- Serialize(context, value.offset);
- Serialize(context, value.align);
- Serialize(context, value.reloff);
- Serialize(context, value.nreloc);
- Serialize(context, value.flags);
- Serialize(context, value.reserved1);
- Serialize(context, value.reserved2);
- Serialize(context, value.reserved3);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::vector<section_64>& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- for (auto& s : bArr)
- {
- section_64 sec;
- auto s2 = s.GetArray();
- std::string sectNameStr = s2[0].GetString();
- memset(sec.sectname, 0, 16);
- memcpy(sec.sectname, sectNameStr.c_str(), sectNameStr.size());
- std::string segNameStr = s2[1].GetString();
- memset(sec.segname, 0, 16);
- memcpy(sec.segname, segNameStr.c_str(), segNameStr.size());
- sec.addr = s2[2].GetUint64();
- sec.size = s2[3].GetUint64();
- sec.offset = s2[4].GetUint();
- sec.align = s2[5].GetUint();
- sec.reloff = s2[6].GetUint();
- sec.nreloc = s2[7].GetUint();
- sec.flags = s2[8].GetUint();
- sec.reserved1 = s2[9].GetUint();
- sec.reserved2 = s2[10].GetUint();
- sec.reserved3 = s2[11].GetUint();
- b.push_back(std::move(sec));
- }
- }
-
- void Serialize(SerializationContext& context, const linkedit_data_command& value)
- {
- context.writer.StartArray();
- Serialize(context, value.cmd);
- Serialize(context, value.cmdsize);
- Serialize(context, value.dataoff);
- Serialize(context, value.datasize);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, linkedit_data_command& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- b.cmd = bArr[0].GetUint();
- b.cmdsize = bArr[1].GetUint();
- b.dataoff = bArr[2].GetUint();
- b.datasize = bArr[3].GetUint();
- }
-
- void Serialize(SerializationContext& context, const segment_command_64& value)
- {
- context.writer.StartArray();
- std::string_view segname(value.segname, 16);
- Serialize(context, segname.substr(0, segname.find('\0')));
- Serialize(context, value.vmaddr);
- Serialize(context, value.vmsize);
- Serialize(context, value.fileoff);
- Serialize(context, value.filesize);
- Serialize(context, value.maxprot);
- Serialize(context, value.initprot);
- Serialize(context, value.nsects);
- Serialize(context, value.flags);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, segment_command_64& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- std::string segNameStr = bArr[0].GetString();
- memset(b.segname, 0, 16);
- memcpy(b.segname, segNameStr.c_str(), segNameStr.size());
- b.vmaddr = bArr[1].GetUint64();
- b.vmsize = bArr[2].GetUint64();
- b.fileoff = bArr[3].GetUint64();
- b.filesize = bArr[4].GetUint64();
- b.maxprot = bArr[5].GetUint();
- b.initprot = bArr[6].GetUint();
- b.nsects = bArr[7].GetUint();
- b.flags = bArr[8].GetUint();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::vector<segment_command_64>& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- for (auto& s : bArr)
- {
- segment_command_64 sec;
- auto s2 = s.GetArray();
- std::string segNameStr = s2[0].GetString();
- memset(sec.segname, 0, 16);
- memcpy(sec.segname, segNameStr.c_str(), segNameStr.size());
- sec.vmaddr = s2[1].GetUint64();
- sec.vmsize = s2[2].GetUint64();
- sec.fileoff = s2[3].GetUint64();
- sec.filesize = s2[4].GetUint64();
- sec.maxprot = s2[5].GetUint();
- sec.initprot = s2[6].GetUint();
- sec.nsects = s2[7].GetUint();
- sec.flags = s2[8].GetUint();
- b.push_back(std::move(sec));
- }
- }
-
- void Serialize(SerializationContext& context, const build_version_command& value)
- {
- context.writer.StartArray();
- Serialize(context, value.cmd);
- Serialize(context, value.cmdsize);
- Serialize(context, value.platform);
- Serialize(context, value.minos);
- Serialize(context, value.sdk);
- Serialize(context, value.ntools);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, build_version_command& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- b.cmd = bArr[0].GetUint();
- b.cmdsize = bArr[1].GetUint();
- b.platform = bArr[2].GetUint();
- b.minos = bArr[3].GetUint();
- b.sdk = bArr[4].GetUint();
- b.ntools = bArr[5].GetUint();
- }
-
- void Serialize(SerializationContext& context, const build_tool_version& value)
- {
- context.writer.StartArray();
- Serialize(context, value.tool);
- Serialize(context, value.version);
- context.writer.EndArray();
- }
-
- void Deserialize(DeserializationContext& context, std::string_view name, std::vector<build_tool_version>& b)
- {
- auto bArr = context.doc[name.data()].GetArray();
- for (auto& s : bArr)
- {
- build_tool_version sec;
- auto s2 = s.GetArray();
- sec.tool = s2[0].GetUint();
- sec.version = s2[1].GetUint();
- b.push_back(sec);
- }
- }
-
-} // namespace KernelCacheCore
diff --git a/view/kernelcache/core/MetadataSerializable.hpp b/view/kernelcache/core/MetadataSerializable.hpp
deleted file mode 100644
index 5397a56c..00000000
--- a/view/kernelcache/core/MetadataSerializable.hpp
+++ /dev/null
@@ -1,254 +0,0 @@
-//
-// Created by kat on 5/31/23.
-//
-
-/*
- * Welcome to, this file.
- *
- * This is a metadata serialization helper.
- *
- * Have you ever wished turning a complex datastructure into a Metadata object was as easy in C++ as it is in python?
- * Do you like macros and templates?
- *
- * Great news.
- *
- * Implement these on your `public MetadataSerializable<T>` subclass:
- * ```
- class MyClass : public MetadataSerializable<MyClass> {
- void Store(SerializationContext& context) const {
- MSS(m_someVariable);
- MSS(m_someOtherVariable);
- }
- void Load(DeserializationContext& context) {
- MSL(m_someVariable);
- MSL(m_someOtherVariable);
- }
- }
- ```
- * Then, you can turn your object into a Metadata object with `AsMetadata()`, and load it back with
- `LoadFromMetadata()`.
- *
- * Serialized fields will be automatically repopulated.
- *
- * Other ser/deser formats (rapidjson objects, strings) also exist. You can use these to achieve nesting, but probably
- avoid that.
- * */
-
-#ifndef KERNELCACHE_CORE_METADATASERIALIZABLE_HPP
-#define KERNELCACHE_CORE_METADATASERIALIZABLE_HPP
-
-#include <cassert>
-#include "binaryninjaapi.h"
-#include "rapidjson/document.h"
-#include "rapidjson/stringbuffer.h"
-#include "rapidjson/prettywriter.h"
-#include "../api/kernelcachecore.h"
-#include "view/macho/machoview.h"
-
-using namespace BinaryNinja;
-
-namespace KernelCacheCore {
-
-#define MSS(name) context.store(#name, name)
-#define MSS_CAST(name, type) context.store(#name, (type) name)
-#define MSS_SUBCLASS(name) Serialize(context, #name, name)
-#define MSL(name) name = context.load<decltype(name)>(#name)
-#define MSL_CAST(name, storedType, type) name = (type)context.load<storedType>(#name)
-
- struct DeserializationContext;
-
- struct SerializationContext {
- rapidjson::StringBuffer buffer;
- rapidjson::Writer<rapidjson::StringBuffer> writer;
-
- SerializationContext() : buffer(), writer(buffer) {
- }
-
- template <typename T>
- void store(std::string_view x, const T& y)
- {
- Serialize(*this, x, y);
- }
- };
-
- struct DeserializationContext {
- rapidjson::Document doc;
-
- template <typename T>
- T load(std::string_view x)
- {
- T value;
- Deserialize(*this, x, value);
- return value;
- }
- };
-
- template <typename Derived, typename LoadResult = Derived>
- class MetadataSerializable {
- public:
- template <typename... Args>
- std::string AsString(Args&&... args) const {
- SerializationContext context;
- Store(context, std::forward<Args>(args)...);
-
- return context.buffer.GetString();
- }
-
- static LoadResult LoadFromString(const std::string& s) {
- DeserializationContext context;
- [[maybe_unused]] rapidjson::ParseResult result = context.doc.Parse(s.c_str());
- assert(result);
- return Derived::Load(context);
- }
-
- static LoadResult LoadFromValue(rapidjson::Value& s) {
- DeserializationContext context;
- context.doc.CopyFrom(s, context.doc.GetAllocator());
- return Derived::Load(context);
- }
-
- template <typename... Args>
- Ref<Metadata> AsMetadata(Args&&... args) const {
- return new Metadata(AsString(std::forward<Args>(args)...));
- }
-
- template <typename... Args>
- void Store(SerializationContext& context, Args&&... args) const {
- context.writer.StartObject();
- AsDerived().Store(context, std::forward<Args>(args)...);
- context.writer.EndObject();
- }
-
- private:
- const Derived& AsDerived() const { return static_cast<const Derived&>(*this); }
- Derived& AsDerived() { return static_cast<Derived&>(*this); }
- };
-
- // The functions below are not part of the FFI API, but are exported so they can be shared with sharedcacheui.
-
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, std::string_view str);
-
- template <typename T>
- inline void Serialize(SerializationContext& context, const MetadataSerializable<T>& value)
- {
- value.Store(context);
- }
-
- template <typename T>
- inline void Serialize(SerializationContext& context, std::string_view name, const T& value)
- {
- Serialize(context, name);
- Serialize(context, value);
- }
-
- template <typename First, typename Second>
- void Serialize(SerializationContext& context, const std::pair<First, Second>& value)
- {
- context.writer.StartArray();
- Serialize(context, value.first);
- Serialize(context, value.second);
- context.writer.EndArray();
- }
-
- template <typename K, typename V, typename L>
- void Serialize(SerializationContext& context, const std::map<K, V, L>& value)
- {
- context.writer.StartArray();
- for (auto& pair : value)
- {
- Serialize(context, pair);
- }
- context.writer.EndArray();
- }
-
- template <typename K, typename V>
- void Serialize(SerializationContext& context, const std::unordered_map<K, V>& value)
- {
- context.writer.StartArray();
- for (auto& pair : value)
- {
- Serialize(context, pair);
- }
- context.writer.EndArray();
- }
-
- template <typename T>
- void Serialize(SerializationContext& context, const std::vector<T>& values)
- {
- context.writer.StartArray();
- for (const auto& value : values)
- {
- Serialize(context, value);
- }
- context.writer.EndArray();
- }
-
- template <typename T>
- void Serialize(SerializationContext& context, const std::optional<T>& value)
- {
- if (value.has_value())
- Serialize(context, *value);
- else
- context.writer.Null();
- }
-
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, const char*);
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, bool b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, bool& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, uint8_t b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, uint8_t& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, uint16_t b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, uint16_t& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, uint32_t b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, uint32_t& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, uint64_t b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, uint64_t& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, int8_t b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, int8_t& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, int16_t b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, int16_t& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, int32_t b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, int32_t& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, int64_t b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, int64_t& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, std::string_view b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext& context, const std::pair<uint64_t, std::pair<uint64_t, uint64_t>>& value);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::string& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::map<uint64_t, std::string>& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<uint64_t, std::string>& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<uint64_t, uint64_t>& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<std::string, std::unordered_map<uint64_t, uint64_t>>& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<std::string, std::string>& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::vector<std::string>& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::vector<std::pair<uint64_t, std::pair<uint64_t, uint64_t>>>& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::vector<std::pair<uint64_t, bool>>& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::vector<uint64_t>& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<std::string, uint64_t>& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext& context, std::string_view name, std::vector<std::pair<uint64_t, std::vector<std::pair<uint64_t, std::string>>>>& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext&, const mach_header_64& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, mach_header_64& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext&, const symtab_command& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, symtab_command& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext&, const dysymtab_command& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, dysymtab_command& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext&, const dyld_info_command& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, dyld_info_command& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext&, const routines_command_64& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, routines_command_64& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext&, const function_starts_command& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, function_starts_command& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext&, const section_64& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, std::vector<section_64>& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext&, const linkedit_data_command& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, linkedit_data_command& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext&, const segment_command_64& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, segment_command_64& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, std::vector<segment_command_64>& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext&, const build_version_command& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, build_version_command& b);
- KERNELCACHE_FFI_API void Serialize(SerializationContext&, const build_tool_version& b);
- KERNELCACHE_FFI_API void Deserialize(DeserializationContext&, std::string_view name, std::vector<build_tool_version>& b);
-
-} // namespace SharedCacheCore
-
-#endif // KERNELCACHE_METADATASERIALIZABLE_HPP
diff --git a/view/kernelcache/core/Utility.cpp b/view/kernelcache/core/Utility.cpp
new file mode 100644
index 00000000..6f8323bf
--- /dev/null
+++ b/view/kernelcache/core/Utility.cpp
@@ -0,0 +1,159 @@
+#include "Utility.h"
+#include "binaryninjaapi.h"
+#include "view/macho/machoview.h"
+
+using namespace BinaryNinja;
+
+BNSegmentFlag SegmentFlagsFromMachOProtections(int initProt, int maxProt)
+{
+ uint32_t flags = 0;
+ if (initProt & MACHO_VM_PROT_READ)
+ flags |= SegmentReadable;
+ if (initProt & MACHO_VM_PROT_WRITE)
+ flags |= SegmentWritable;
+ if (initProt & MACHO_VM_PROT_EXECUTE)
+ flags |= SegmentExecutable;
+ if (((initProt & MACHO_VM_PROT_WRITE) == 0) && ((maxProt & MACHO_VM_PROT_WRITE) == 0))
+ flags |= SegmentDenyWrite;
+ if (((initProt & MACHO_VM_PROT_EXECUTE) == 0) && ((maxProt & MACHO_VM_PROT_EXECUTE) == 0))
+ flags |= SegmentDenyExecute;
+ return static_cast<BNSegmentFlag>(flags);
+}
+
+int64_t readSLEB128(const uint8_t*& current, const uint8_t* end)
+{
+ uint8_t cur;
+ int64_t value = 0;
+ size_t shift = 0;
+ while (current != end)
+ {
+ cur = *current++;
+ value |= (cur & 0x7f) << shift;
+ shift += 7;
+ if ((cur & 0x80) == 0)
+ break;
+ }
+ value = (value << (64 - shift)) >> (64 - shift);
+ return value;
+}
+
+uint64_t readLEB128(const uint8_t*& current, const uint8_t* end)
+{
+ uint64_t result = 0;
+ int bit = 0;
+ do
+ {
+ if (current >= end)
+ return -1;
+
+ uint64_t slice = *current & 0x7f;
+
+ if (bit > 63)
+ return -1;
+ result |= (slice << bit);
+ bit += 7;
+ } while (*current++ & 0x80);
+ return result;
+}
+
+
+uint64_t readValidULEB128(const uint8_t*& current, const uint8_t* end)
+{
+ uint64_t value = readLEB128(current, end);
+ if ((int64_t)value == -1)
+ throw ReadException();
+ return value;
+}
+
+void ApplySymbol(Ref<BinaryView> view, Ref<TypeLibrary> typeLib, Ref<Symbol> symbol, Ref<Type> type)
+{
+ auto symbolAddress = symbol->GetAddress();
+ auto symbolName = symbol->GetFullName();
+
+ // Sometimes the symbol will be duplicated, so lets not do this work again.
+ if (view->GetSymbolByAddress(symbolAddress))
+ return;
+
+ // Define the symbol!
+ view->DefineAutoSymbol(symbol);
+
+ // Try and pull a type from a type library to apply at the symbol location.
+ // The type library type will take precedence over the passed in type.
+ Ref<Type> selectedType = type;
+ if (typeLib)
+ selectedType = view->ImportTypeLibraryObject(typeLib, {symbolName});
+
+ Ref<Function> func = nullptr;
+ if (symbol->GetType() == FunctionSymbol)
+ {
+ Ref<Platform> targetPlatform = view->GetDefaultPlatform();
+ // Make sure to check for already added function from the function table.
+ // Unless we have retrieved a type here we don't need to make a new function.
+ func = view->GetAnalysisFunction(targetPlatform, symbolAddress);
+ if (!func || selectedType != nullptr)
+ func = view->AddFunctionForAnalysis(targetPlatform, symbolAddress, false, selectedType);
+ // The above function might be overwritten so we also want to apply the type here.
+ if (func && selectedType != nullptr)
+ func->ApplyAutoDiscoveredType(selectedType);
+ }
+ else
+ {
+ // Other symbol types can just use this, they don't need to worry about linear sweep removing them.
+ view->DefineAutoSymbolAndVariableOrFunction(view->GetDefaultPlatform(), symbol, selectedType);
+ }
+
+ if (func)
+ {
+ // objective c type adjustment stuff.
+ if (symbolName == "_objc_msgSend")
+ {
+ func->SetHasVariableArguments(false);
+ }
+ else if (symbolName.find("_objc_retain_x") != std::string::npos
+ || symbolName.find("_objc_release_x") != std::string::npos)
+ {
+ auto x = symbolName.rfind('x');
+ auto num = symbolName.substr(x + 1);
+
+ std::vector<FunctionParameter> callTypeParams;
+ auto cc = view->GetDefaultArchitecture()->GetCallingConventionByName("apple-arm64-objc-fast-arc-" + num);
+
+ if (auto idType = view->GetTypeByName({"id"}))
+ {
+ callTypeParams.emplace_back("obj", idType, true, Variable());
+ auto funcType = Type::FunctionType(idType, cc, callTypeParams);
+ func->SetUserType(funcType);
+ }
+ else
+ {
+ LogWarn("Failed to find id type for %llx, objective-c processor not ran?", func->GetStart());
+ }
+ }
+ }
+}
+
+std::string BaseFileName(const std::string& path)
+{
+ auto lastSlashPos = path.find_last_of("/\\");
+ if (lastSlashPos != std::string::npos)
+ return path.substr(lastSlashPos + 1);
+ return path;
+}
+
+bool IsSameFolderForFile(Ref<ProjectFile> a, Ref<ProjectFile> b)
+{
+ if (!a && !b)
+ return true;
+ if (a && b)
+ return IsSameFolder(a->GetFolder(), b->GetFolder());
+ return false;
+}
+
+bool IsSameFolder(Ref<ProjectFolder> a, Ref<ProjectFolder> b)
+{
+ if (!a && !b)
+ return true;
+ if (a && b)
+ return a->GetId() == b->GetId();
+ return false;
+}
diff --git a/view/kernelcache/core/Utility.h b/view/kernelcache/core/Utility.h
new file mode 100644
index 00000000..5664fd9b
--- /dev/null
+++ b/view/kernelcache/core/Utility.h
@@ -0,0 +1,106 @@
+#pragma once
+
+#include <cstdint>
+#include <map>
+
+#include "binaryninjaapi.h"
+
+#ifdef _MSC_VER
+inline int CountTrailingZeros(uint64_t value)
+{
+ unsigned long index; // 32-bit long on Windows
+ if (_BitScanForward64(&index, value))
+ {
+ return index;
+ }
+ else
+ {
+ return 64; // If the value is 0, return 64.
+ }
+}
+#else
+inline int CountTrailingZeros(uint64_t value)
+{
+ return value == 0 ? 64 : __builtin_ctzll(value);
+}
+#endif
+
+BNSegmentFlag SegmentFlagsFromMachOProtections(int initProt, int maxProt);
+
+int64_t readSLEB128(const uint8_t*& current, const uint8_t* end);
+
+uint64_t readLEB128(const uint8_t*& current, const uint8_t* end);
+
+uint64_t readValidULEB128(const uint8_t*& current, const uint8_t* end);
+
+void ApplySymbol(BinaryNinja::Ref<BinaryNinja::BinaryView> view, BinaryNinja::Ref<BinaryNinja::TypeLibrary> typeLib,
+ BinaryNinja::Ref<BinaryNinja::Symbol> symbol, BinaryNinja::Ref<BinaryNinja::Type> type = nullptr);
+
+// Returns the "image name" for a given path.
+// /blah/foo/bar/libObjCThing.dylib -> libObjCThing.dylib
+std::string BaseFileName(const std::string& path);
+
+bool IsSameFolderForFile(BinaryNinja::Ref<BinaryNinja::ProjectFile> a, BinaryNinja::Ref<BinaryNinja::ProjectFile> b);
+bool IsSameFolder(BinaryNinja::Ref<BinaryNinja::ProjectFolder> a, BinaryNinja::Ref<BinaryNinja::ProjectFolder> b);
+
+// Represents a range of addresses [start, end).
+// Note that `end` is not included within the range.
+struct AddressRange
+{
+ uint64_t start;
+ uint64_t end;
+
+ AddressRange(uint64_t start, uint64_t end) : start(start), end(end) {}
+
+ AddressRange() : start(0), end(0) {}
+
+ bool Overlaps(const AddressRange& b) const { return start < b.end && b.start < end; }
+
+ bool operator<(const AddressRange& b) const { return start < b.start || (start == b.start && end < b.end); }
+
+ friend bool operator<(const AddressRange& range, uint64_t address) { return range.end <= address; }
+
+ friend bool operator<(uint64_t address, const AddressRange& range) { return address < range.start; }
+};
+
+// A map keyed by address ranges that can be looked up via any
+// address within a range thanks to C++14's transparent comparators.
+template <typename Value>
+using AddressRangeMap = std::map<AddressRange, Value, std::less<>>;
+
+// TODO: Document this!
+template <typename T>
+class WeakAllocPtr
+{
+protected:
+ std::weak_ptr<T> m_weakPtr; // Weak reference to the object
+ std::function<std::shared_ptr<T>()> m_allocator; // Function to recreate the object
+
+public:
+ explicit WeakAllocPtr(std::function<std::shared_ptr<T>()> allocator) : m_allocator(allocator) {}
+
+ WeakAllocPtr(std::weak_ptr<T> weakPtr, std::function<std::shared_ptr<T>()> allocator) : m_allocator(allocator)
+ {
+ if (weakPtr == nullptr)
+ {
+ m_weakPtr = {};
+ }
+ else
+ {
+ m_weakPtr = weakPtr;
+ }
+ }
+
+ std::shared_ptr<T> lock()
+ {
+ std::shared_ptr<T> sharedPtr = m_weakPtr.lock();
+ if (!sharedPtr)
+ {
+ sharedPtr = m_allocator();
+ m_weakPtr = sharedPtr;
+ }
+ return sharedPtr;
+ }
+
+ std::shared_ptr<T> lock_no_allocate() { return m_weakPtr.lock(); }
+};
diff --git a/view/kernelcache/core/ffi.cpp b/view/kernelcache/core/ffi.cpp
new file mode 100644
index 00000000..d7126894
--- /dev/null
+++ b/view/kernelcache/core/ffi.cpp
@@ -0,0 +1,213 @@
+#include "KernelCacheController.h"
+#include "../api/kernelcachecore.h"
+
+using namespace BinaryNinja;
+using namespace BinaryNinja::KC;
+
+BNKernelCacheImage ImageToApi(const CacheImage& image)
+{
+ BNKernelCacheImage apiImage;
+ apiImage.name = BNAllocString(image.path.c_str());
+ apiImage.headerVirtualAddress = image.headerVirtualAddress;
+ apiImage.headerFileAddress = image.headerFileAddress;
+ return apiImage;
+}
+
+CacheImage ImageFromApi(const BNKernelCacheImage& image)
+{
+ CacheImage apiImage;
+ apiImage.path = image.name;
+ apiImage.headerVirtualAddress = image.headerVirtualAddress;
+ apiImage.headerFileAddress = image.headerFileAddress;
+ apiImage.header = nullptr;
+ return apiImage;
+}
+
+BNKernelCacheSymbol SymbolToApi(const CacheSymbol& symbol)
+{
+ BNKernelCacheSymbol apiSymbol;
+ apiSymbol.name = BNAllocString(symbol.name.c_str());
+ apiSymbol.address = symbol.address;
+ apiSymbol.symbolType = symbol.type;
+ return apiSymbol;
+}
+
+CacheSymbol SymbolFromApi(const BNKernelCacheSymbol& apiSymbol)
+{
+ CacheSymbol symbol;
+ symbol.name = apiSymbol.name;
+ symbol.address = apiSymbol.address;
+ symbol.type = apiSymbol.symbolType;
+ return symbol;
+}
+
+extern "C"
+{
+ BNKernelCacheController* BNGetKernelCacheController(BNBinaryView* data)
+ {
+ Ref<BinaryView> view = new BinaryView(BNNewViewReference(data));
+ auto controller = KernelCacheController::FromView(*view);
+ if (!controller)
+ return nullptr;
+ return KC_API_OBJECT_REF(controller);
+ }
+
+ BNKernelCacheController* BNNewKernelCacheControllerReference(BNKernelCacheController* controller)
+ {
+ return KC_API_OBJECT_NEW_REF(controller);
+ }
+
+ void BNFreeKernelCacheControllerReference(BNKernelCacheController* controller)
+ {
+ KC_API_OBJECT_FREE(controller);
+ }
+
+ bool BNKernelCacheControllerApplyImage(
+ BNKernelCacheController* controller, BNBinaryView* data, BNKernelCacheImage* image)
+ {
+ Ref<BinaryView> view = new BinaryView(BNNewViewReference(data));
+ // LoadImage will use the header, lets do everyone a favor and use the existing image!
+ if (const auto realImage = controller->object->GetCache().GetImageAt(image->headerVirtualAddress))
+ return controller->object->ApplyImage(*view, *realImage);
+ // They gave us an unknown image, we will not have header information.
+ return controller->object->ApplyImage(*view, ImageFromApi(*image));
+ }
+ bool BNKernelCacheControllerIsImageLoaded(BNKernelCacheController* controller, BNKernelCacheImage* image)
+ {
+ return controller->object->IsImageLoaded(ImageFromApi(*image));
+ }
+
+ bool BNKernelCacheControllerGetImageAt(
+ BNKernelCacheController* controller, uint64_t address, BNKernelCacheImage* outImage)
+ {
+ const auto image = controller->object->GetCache().GetImageAt(address);
+ if (!image)
+ return false;
+ *outImage = ImageToApi(*image);
+ return true;
+ }
+
+ bool BNKernelCacheControllerGetImageContaining(
+ BNKernelCacheController* controller, uint64_t address, BNKernelCacheImage* outImage)
+ {
+ const auto image = controller->object->GetCache().GetImageContaining(address);
+ if (!image)
+ return false;
+ *outImage = ImageToApi(*image);
+ return true;
+ }
+
+ bool BNKernelCacheControllerGetImageWithName(
+ BNKernelCacheController* controller, const char* name, BNKernelCacheImage* outImage)
+ {
+ const auto image = controller->object->GetCache().GetImageWithName(name);
+ if (!image)
+ return false;
+ *outImage = ImageToApi(*image);
+ return true;
+ }
+
+ char** BNKernelCacheControllerGetImageDependencies(
+ BNKernelCacheController* controller, BNKernelCacheImage* image, size_t* count)
+ {
+ // GetDependencies will use the header, lets do everyone a favor and use the existing image!
+ const auto realImage = controller->object->GetCache().GetImageAt(image->headerFileAddress);
+ if (!realImage.has_value())
+ return nullptr;
+ const auto dependencies = realImage->GetDependencies();
+
+ std::vector<const char*> dependencyPtrs;
+ dependencyPtrs.reserve(dependencies.size());
+ for (const auto& dependency : dependencies)
+ dependencyPtrs.push_back(dependency.c_str());
+ *count = dependencyPtrs.size();
+ return BNAllocStringList(dependencyPtrs.data(), dependencyPtrs.size());
+ }
+
+ BNKernelCacheImage* BNKernelCacheControllerGetImages(BNKernelCacheController* controller, size_t* count)
+ {
+ const auto& images = controller->object->GetCache().GetImages();
+ *count = images.size();
+ BNKernelCacheImage* apiImages = new BNKernelCacheImage[*count];
+ size_t idx = 0;
+ for (const auto& [_, image] : images)
+ apiImages[idx++] = ImageToApi(image);
+ return apiImages;
+ }
+
+ BNKernelCacheImage* BNKernelCacheControllerGetLoadedImages(BNKernelCacheController* controller, size_t* count)
+ {
+ const auto& loadedImageStarts = controller->object->GetLoadedImages();
+
+ // TODO: This translation should likely exist in the core cache controller class?
+ std::vector<CacheImage> loadedImages;
+ for (auto start : loadedImageStarts)
+ {
+ auto image = controller->object->GetCache().GetImageAt(start);
+ if (image)
+ loadedImages.push_back(*image);
+ }
+
+ *count = loadedImages.size();
+ BNKernelCacheImage* apiImages = new BNKernelCacheImage[*count];
+ for (size_t i = 0; i < *count; i++)
+ apiImages[i] = ImageToApi(loadedImages[i]);
+ return apiImages;
+ }
+
+ void BNKernelCacheFreeImage(BNKernelCacheImage image)
+ {
+ BNFreeString(image.name);
+ }
+
+ void BNKernelCacheFreeImageList(BNKernelCacheImage* images, size_t count)
+ {
+ for (size_t i = 0; i < count; i++)
+ BNKernelCacheFreeImage(images[i]);
+ delete[] images;
+ }
+
+ bool BNKernelCacheControllerGetSymbolAt(
+ BNKernelCacheController* controller, uint64_t address, BNKernelCacheSymbol* outSymbol)
+ {
+ const auto symbol = controller->object->GetCache().GetSymbolAt(address);
+ if (!symbol)
+ return false;
+ *outSymbol = SymbolToApi(*symbol);
+ return true;
+ }
+
+ bool BNKernelCacheControllerGetSymbolWithName(
+ BNKernelCacheController* controller, const char* name, BNKernelCacheSymbol* outSymbol)
+ {
+ const auto symbol = controller->object->GetCache().GetSymbolWithName(name);
+ if (!symbol)
+ return false;
+ *outSymbol = SymbolToApi(*symbol);
+ return true;
+ }
+
+ BNKernelCacheSymbol* BNKernelCacheControllerGetSymbols(BNKernelCacheController* controller, size_t* count)
+ {
+ const auto& symbols = controller->object->GetCache().GetSymbols();
+ *count = symbols.size();
+ BNKernelCacheSymbol* apiSymbols = new BNKernelCacheSymbol[*count];
+ size_t idx = 0;
+ for (const auto& [_, symbol] : symbols)
+ apiSymbols[idx++] = SymbolToApi(symbol);
+ return apiSymbols;
+ }
+
+
+ void BNKernelCacheFreeSymbol(BNKernelCacheSymbol symbol)
+ {
+ BNFreeString(symbol.name);
+ }
+
+ void BNKernelCacheFreeSymbolList(BNKernelCacheSymbol* symbols, size_t count)
+ {
+ for (size_t i = 0; i < count; i++)
+ BNKernelCacheFreeSymbol(symbols[i]);
+ delete[] symbols;
+ }
+};
diff --git a/view/kernelcache/core/ffi_global.h b/view/kernelcache/core/ffi_global.h
new file mode 100644
index 00000000..0effdb81
--- /dev/null
+++ b/view/kernelcache/core/ffi_global.h
@@ -0,0 +1,41 @@
+/*
+Copyright 2020-2024 Vector 35 Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#pragma once
+
+// Define macros for defining objects exposed by the API
+#define DECLARE_KC_API_OBJECT(handle, cls) \
+ namespace BinaryNinja::KC { \
+ class cls; \
+ } \
+ struct handle \
+ { \
+ BinaryNinja::KC::cls* object; \
+ }
+#define IMPLEMENT_KC_API_OBJECT(handle) \
+\
+private: \
+ handle m_apiObject; \
+\
+public: \
+ typedef handle* APIHandle; \
+ handle* GetAPIObject() \
+ { \
+ return &m_apiObject; \
+ } \
+\
+private:
+#define INIT_KC_API_OBJECT() m_apiObject.object = this;
diff --git a/view/kernelcache/core/refcountobject.h b/view/kernelcache/core/refcountobject.h
new file mode 100644
index 00000000..06996da9
--- /dev/null
+++ b/view/kernelcache/core/refcountobject.h
@@ -0,0 +1,223 @@
+/*
+Copyright 2020-2024 Vector 35 Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#pragma once
+
+#ifdef WIN32
+ #include <windows.h>
+#endif
+#include <stddef.h>
+#include <vector>
+#include <atomic>
+
+namespace BinaryNinja::KC {
+ class KCRefCountObject
+ {
+ // CORE_ALLOCATED_CLASS(RefCountObject)
+
+ public:
+ std::atomic<int> m_refs;
+
+ KCRefCountObject() : m_refs(0) {}
+
+ virtual ~KCRefCountObject() {}
+
+ virtual void AddRef() { m_refs.fetch_add(1); }
+
+ virtual void Release()
+ {
+ if (m_refs.fetch_sub(1) == 1)
+ delete this;
+ }
+
+ virtual void AddAPIRef() { AddRef(); }
+
+ virtual void ReleaseAPIRef() { Release(); }
+ };
+
+
+ template <class T>
+ class KCRef
+ {
+ T* m_obj;
+#ifdef BN_REF_COUNT_DEBUG
+ void* m_assignmentTrace = nullptr;
+#endif
+
+ public:
+ KCRef() : m_obj(NULL) {}
+
+ KCRef(T* obj) : m_obj(obj)
+ {
+ if (m_obj)
+ {
+ m_obj->AddRef();
+#ifdef BN_REF_COUNT_DEBUG
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
+ }
+ }
+
+ KCRef(const KCRef& obj) : m_obj(obj.m_obj)
+ {
+ if (m_obj)
+ {
+ m_obj->AddRef();
+#ifdef BN_REF_COUNT_DEBUG
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
+ }
+ }
+
+ ~KCRef()
+ {
+ if (m_obj)
+ {
+ m_obj->Release();
+#ifdef BN_REF_COUNT_DEBUG
+ BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+#endif
+ }
+ }
+
+ // move constructor
+ KCRef(KCRef&& other) : m_obj(other.m_obj)
+ {
+ other.m_obj = 0;
+#ifdef BN_REF_COUNT_DEBUG
+ m_assignmentTrace = other.m_assignmentTrace;
+#endif
+ }
+
+ // move assignment (inefficient in this case)
+ // Ref<T>& operator=(Ref<T>&& other)
+ // {
+ // if (m_obj)
+ // {
+ // #ifdef BN_REF_COUNT_DEBUG
+ // BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+ // #endif
+ // m_obj->Release();
+ // }
+ // m_obj = other.m_obj;
+ // other.m_obj = 0;
+ // #ifdef BN_REF_COUNT_DEBUG
+ // m_assignmentTrace = other.m_assignmentTrace;
+ // #endif
+ // return *this;
+ // }
+
+ KCRef<T>& operator=(const KCRef<T>& obj)
+ {
+#ifdef BN_REF_COUNT_DEBUG
+ if (m_obj)
+ BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+ if (obj.m_obj)
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
+ T* oldObj = m_obj;
+ m_obj = obj.m_obj;
+ if (m_obj)
+ m_obj->AddRef();
+ if (oldObj)
+ oldObj->Release();
+ return *this;
+ }
+
+ KCRef<T>& operator=(T* obj)
+ {
+#ifdef BN_REF_COUNT_DEBUG
+ if (m_obj)
+ BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace);
+ if (obj)
+ m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name());
+#endif
+ T* oldObj = m_obj;
+ m_obj = obj;
+ if (m_obj)
+ m_obj->AddRef();
+ if (oldObj)
+ oldObj->Release();
+ return *this;
+ }
+
+ operator T*() const { return m_obj; }
+
+ T* operator->() const { return m_obj; }
+
+ T& operator*() const { return *m_obj; }
+
+ bool operator!() const { return m_obj == NULL; }
+
+ T* GetPtr() const { return m_obj; }
+
+ bool operator==(const KCRef<T>& obj) const { return m_obj == obj.m_obj; }
+
+ bool operator!=(const KCRef<T>& obj) const { return m_obj != obj.m_obj; }
+
+ bool operator<(const KCRef<T>& obj) const { return m_obj < obj.m_obj; }
+
+ template <typename H>
+ friend H AbslHashValue(H h, const KCRef<T>& value)
+ {
+ return AbslHashValue(std::move(h), value.m_obj);
+ }
+ };
+
+
+ // Macro-like functions to manage referenced objects for the external API
+ template <class T>
+ static typename T::APIHandle KC_API_OBJECT_REF(T* obj)
+ {
+ if (obj == nullptr)
+ return nullptr;
+ obj->AddAPIRef();
+ return obj->GetAPIObject();
+ }
+
+ template <class T>
+ static typename T::APIHandle KC_API_OBJECT_REF(const KCRef<T>& obj)
+ {
+ if (!obj)
+ return nullptr;
+ obj->AddAPIRef();
+ return obj->GetAPIObject();
+ }
+
+ // template <class T>
+ // static typename T::APIHandle KC_API_OBJECT_REF(const APIRef<T>& obj)
+ //{
+ // if (!obj)
+ // return nullptr;
+ // obj->AddAPIRef();
+ // return obj->GetAPIObject();
+ // }
+
+ template <class T>
+ static T* KC_API_OBJECT_NEW_REF(T* obj)
+ {
+ if (obj)
+ obj->object->AddAPIRef();
+ return obj;
+ }
+
+ template <class T>
+ static void KC_API_OBJECT_FREE(T* obj)
+ {
+ if (obj)
+ obj->object->ReleaseAPIRef();
+ }
+}; // namespace BinaryNinja::KC
diff --git a/view/kernelcache/ui/KernelCacheUINotifications.cpp b/view/kernelcache/ui/KernelCacheUINotifications.cpp
index 6bd8fdf0..9d7e17a9 100644
--- a/view/kernelcache/ui/KernelCacheUINotifications.cpp
+++ b/view/kernelcache/ui/KernelCacheUINotifications.cpp
@@ -3,13 +3,15 @@
//
#include "KernelCacheUINotifications.h"
-#include <QLayout>
#include <kernelcacheapi.h>
#include "ui/sidebar.h"
#include "ui/linearview.h"
#include "ui/viewframe.h"
#include "progresstask.h"
+using namespace BinaryNinja;
+using namespace KernelCacheAPI;
+
UINotifications* UINotifications::m_instance = nullptr;
void UINotifications::init()
@@ -18,75 +20,94 @@ void UINotifications::init()
UIContext::registerNotification(m_instance);
}
-
void UINotifications::OnViewChange(UIContext* context, ViewFrame* frame, const QString& type)
{
if (!frame)
return;
- // FIXME there is a bv func for this
- static std::function<bool(Ref<BinaryView>, uint64_t)> isAddrMapped = [](Ref<BinaryView> view, uint64_t addr) {
- if (view && view->GetTypeName() == KC_VIEW_NAME)
+ auto view = frame->getCurrentBinaryView();
+ if (!view || view->GetTypeName() != KC_VIEW_NAME)
+ return;
+
+ auto viewInt = frame->getCurrentViewInterface();
+ if (!viewInt)
+ return;
+
+ auto ah = viewInt->actionHandler();
+ // Check to see if we have already bound these actions.
+ if (ah->isBoundAction("Load Image by Name"))
+ return;
+
+ static auto loadImageAtAddr = [](BinaryView& view, uint64_t addr) {
+ auto controller = KernelCacheController::GetController(view);
+ if (!controller)
+ return;
+ if (auto foundImage = controller->GetImageContaining(addr))
{
- for (const auto& seg : view->GetSegments())
- {
- if (seg->GetStart() <= addr && seg->GetEnd() > addr)
- return true;
- }
+ // If we did not load the image, then we don't need to run analysis.
+ if (!controller->ApplyImage(view, *foundImage))
+ return;
+ view.AddAnalysisOption("linearsweep");
+ view.UpdateAnalysis();
}
- return false;
};
- auto view = frame->getCurrentBinaryView();
- if (view && view->GetTypeName() == KC_VIEW_NAME)
- {
- if (auto viewInt = frame->getCurrentViewInterface())
+ auto loadImageAddrAction = [](const UIActionContext& ctx) {
+ uint64_t addr = 0;
+ if (GetAddressInput(addr, "Address", "Address"))
{
- auto ah = viewInt->actionHandler();
- if (!ah->isBoundAction("KC Load IMGHERE"))
- {
- ah->bindAction("KC Load IMGHERE",
- UIAction(
- [](const UIActionContext& ctx) {
- Ref<BinaryView> view = ctx.binaryView;
- Ref<KernelCacheAPI::KernelCache> cache = new KernelCacheAPI::KernelCache(view);
- uint64_t addr = ctx.token.token.value;
- if (addr)
- {
- BackgroundThread::create(ctx.context->mainWindow())->thenBackground(
- [cache=cache, addr=addr]() {
- cache->LoadImageContainingAddress(addr);
- })->start();
- }
- },
- [](const UIActionContext& ctx) {
- Ref<KernelCacheAPI::KernelCache> cache = new KernelCacheAPI::KernelCache(ctx.binaryView);
- uint64_t addr = ctx.token.token.value;
- if (isAddrMapped(ctx.binaryView, addr))
- return false;
- return addr && cache->GetImageNameForAddress(addr) != ""; // bool
- }));
- ah->setActionDisplayName("KC Load IMGHERE", [](const UIActionContext& ctx) {
- Ref<KernelCacheAPI::KernelCache> cache = new KernelCacheAPI::KernelCache(ctx.binaryView);
- uint64_t addr = ctx.token.token.value;
- if (addr)
- return QString("Load ") + cache->GetImageNameForAddress(addr).c_str();
- return QString("Error");
- });
- if (auto linearView = qobject_cast<LinearView*>(viewInt->widget()))
- {
- linearView->contextMenu().addAction("KC Load IMGHERE", KC_VIEW_NAME);
- linearView->contextMenu().setGroupOrdering(KC_VIEW_NAME, 0);
- }
- }
+ BackgroundThread::create(ctx.context->mainWindow())
+ ->thenBackground([ctx, addr]() {
+ loadImageAtAddr(*ctx.binaryView, addr);
+ })->start();
}
+ };
+
+ auto loadImageTokenAction = [](const UIActionContext& ctx) {
+ BackgroundThread::create(ctx.context->mainWindow())
+ ->thenBackground([ctx](){ loadImageAtAddr(*ctx.binaryView, ctx.token.token.value); })
+ ->start();
+ };
+
+ auto isValidUnloadedImageAction = [](const UIActionContext& ctx) {
+ uint64_t addr = ctx.token.token.value;
+ // Check if the image is already loaded in the view.
+ if (!ctx.binaryView->GetSectionsAt(addr).empty())
+ return false;
+ auto controller = KernelCacheController::GetController(*ctx.binaryView);
+ if (!controller)
+ return false;
+ return controller->GetImageContaining(addr).has_value();
+ };
+
+ ah->bindAction("Load Image by Address", UIAction(loadImageAddrAction));
+
+ ah->bindAction("Load IMGHERE", UIAction(loadImageTokenAction, isValidUnloadedImageAction));
+
+ ah->setActionDisplayName("Load IMGHERE", [](const UIActionContext& ctx) {
+ auto controller = KernelCacheController::GetController(*ctx.binaryView);
+ if (!controller)
+ return QString("NO CONTROLLER");
+ uint64_t addr = ctx.token.token.value;
+ auto image = controller->GetImageContaining(addr);
+ if (!image)
+ return QString("NO IMAGE");
+ return QString("Load ") + image->name.c_str();
+ });
+
+ // Finally add the actions to the context menu.
+ if (auto linearView = qobject_cast<LinearView*>(viewInt->widget()))
+ {
+ constexpr auto groupOneName = KC_VIEW_NAME;
+ constexpr auto groupTwoName = KC_VIEW_NAME "2";
+ linearView->contextMenu().addAction("Load IMGHERE", groupOneName);
+ linearView->contextMenu().addAction("Load Image by Address", groupTwoName);
+ linearView->contextMenu().setGroupOrdering(groupOneName, 0);
+ linearView->contextMenu().setGroupOrdering(groupTwoName, 1);
}
}
+
void UINotifications::OnAfterOpenFile(UIContext* context, FileContext* file, ViewFrame* frame)
{
- if (frame->getCurrentBinaryView())
- {
- // Register BD notifications. We dont use them right now.
- }
UIContextNotification::OnAfterOpenFile(context, file, frame);
}
diff --git a/view/kernelcache/ui/kctriage.cpp b/view/kernelcache/ui/kctriage.cpp
index c28c323a..da2f2270 100644
--- a/view/kernelcache/ui/kctriage.cpp
+++ b/view/kernelcache/ui/kctriage.cpp
@@ -1,8 +1,9 @@
+#include <QHeaderView>
#include <QMessageBox>
-#include <QPainter>
-#include <cmath>
-#include "globalarea.h"
+#include <utility>
#include "kctriage.h"
+#include "globalarea.h"
+#include "symboltable.h"
#include "ui/fontsettings.h"
using namespace BinaryNinja;
@@ -36,7 +37,7 @@ void KCTriageViewType::Register()
}
-KCTriageView::KCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(parent), View(), m_data(data), m_cache(new KernelCache(data))
+KCTriageView::KCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(parent), m_data(std::move(data))
{
setBinaryDataNavigable(false);
setupView(this);
@@ -56,6 +57,11 @@ KCTriageView::KCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(parent
m_layout->addWidget(m_triageTabs);
setLayout(m_layout);
+ // In case we have already initialized the controller (user has opened this view type again)
+ // we will call refresh data. If this is the first triage view constructed (i.e. before view init) then this
+ // will do nothing.
+ RefreshData();
+
m_triageTabs->selectWidget(defaultWidget);
}
@@ -66,43 +72,88 @@ KCTriageView::~KCTriageView()
}
-void KCTriageView::loadImagesWithAddr(const std::vector<uint64_t>& addresses) {
- if (!m_cache)
+void KCTriageView::loadImagesWithAddr(const std::vector<uint64_t>& addresses, bool includeDependencies) {
+ auto controller = KernelCacheController::GetController(*m_data);
+ if (!controller)
return;
- std::map<uint64_t, std::string> images;
+ // TODO: NOTE ABOUT `IsImageLoaded` BEING COMMENTED OUT. PLEASE READ.
+ // TODO: Because commiting undo actions will use main thread to synchronize we must not be holding any locks
+ // TODO: This can really only ever be removed if:
+ // TODO: 1. we can set a user function type without creating an undo action, basically like the rest of shared cache
+ // TODo: use an auto function or some hack to get the user function but without the undo action.
+ // TODO: 2. we can use the undo buffer from any thread and not just the main thread
+ // TODO: I have exhausted all other options, this is a serious issue we should address soon.
+ typedef std::vector<CacheImage> ImageList;
+ ImageList images = {};
for (const uint64_t& addr : addresses)
{
- auto imageName = m_cache->GetImageNameForAddress(addr);
- if (!imageName.empty() && !m_cache->IsImageLoaded(addr))
+ std::optional<CacheImage> image = controller->GetImageContaining(addr);
+ if (image.has_value())
{
- images.insert({addr, imageName});
+ // Only try to load if we have not already.
+ // if (!controller->IsImageLoaded(*image))
+ images.emplace_back(*image);
+
+ // TODO: We currently only add direct dependencies, may want to make the depth configurable?
+ if (includeDependencies)
+ {
+ auto dependencies = controller->GetImageDependencies(*image);
+ for (const auto& depName : dependencies)
+ {
+ auto depImage = controller->GetImageWithName(depName);
+ if (depImage.has_value()/* && !controller->IsImageLoaded(*depImage) */)
+ {
+ images.emplace_back(*depImage);
+ }
+ }
+ }
}
}
// Don't create a worker action if we don't have any images.
if (images.empty())
return;
+ Ref<BackgroundTask> imageLoadTask = new BackgroundTask("Loading images...", true);
+
+ // Apply the images in a future then update the triage view and run analysis.
+ QPointer<QFutureWatcher<ImageList>> watcher = new QFutureWatcher<ImageList>(this);
+ connect(watcher, &QFutureWatcher<ImageList>::finished, this, [watcher, this]() {
+ if (watcher)
+ {
+ auto loadedImages = watcher->result();
+ if (loadedImages.empty())
+ return;
- WorkerPriorityEnqueue([this, images]() {
- size_t loadedImages = 0;
- const std::string initialLoad = fmt::format("Loading images... (0/{})", images.size());
- auto imageLoadTask = BackgroundTask(initialLoad, true);
+ // Update the triage to display the images as loaded.
+ for (const auto& image : loadedImages)
+ setImageLoaded(image.headerVirtualAddress);
- for (const auto& [addr, imageName] : images)
+ // Run analysis.
+ this->m_data->AddAnalysisOption("linearsweep");
+ this->m_data->UpdateAnalysis();
+ }
+ });
+ QFuture<ImageList> future = QtConcurrent::run([this, controller, images, imageLoadTask]() {
+ ImageList loadedImages = {};
+ for (const auto& image : images)
{
- if (imageLoadTask.IsCancelled())
+ if (imageLoadTask->IsCancelled() || QThread::currentThread()->isInterruptionRequested())
break;
- const std::string newLoad = fmt::format("Loading images... ({}/{})", loadedImages++, images.size());
- imageLoadTask.SetProgressText(newLoad);
- if (m_cache->LoadImageWithInstallName(imageName))
- setImageLoaded(addr);
+ std::string newLoad = fmt::format("Loading images... ({}/{})", loadedImages.size(), images.size());
+ imageLoadTask->SetProgressText(newLoad);
+ if (controller->ApplyImage(*this->m_data, image))
+ loadedImages.emplace_back(image);
+ }
+ imageLoadTask->Finish();
+ return loadedImages;
+ });
+ watcher->setFuture(future);
+ connect(this, &QObject::destroyed, this, [watcher, imageLoadTask]() {
+ if (watcher && watcher->isRunning()) {
+ watcher->cancel();
+ imageLoadTask->Cancel();
}
- imageLoadTask.Finish();
-
- // We have loaded images, lets make sure to update analysis!
- this->m_data->AddAnalysisOption("linearsweep");
- this->m_data->UpdateAnalysis();
});
}
@@ -131,7 +182,7 @@ QWidget* KCTriageView::initImageTable()
m_imageTable = new FilterableTableView(this);
m_imageModel = new QStandardItemModel(0, 3, m_imageTable);
- m_imageModel->setHorizontalHeaderLabels({"VM Address", "Loaded", "Name"});
+ m_imageModel->setHorizontalHeaderLabels({"Address", "Loaded", "Name"});
// Apply custom column styling
m_imageTable->setItemDelegateForColumn(0, new AddressColorDelegate(m_imageTable));
@@ -157,6 +208,7 @@ QWidget* KCTriageView::initImageTable()
QAction noSelectionAction("No Images Selected", m_imageTable);
QAction loadImagesAction("", m_imageTable);
+ QAction loadImagesWithDepsAction("", m_imageTable);
if (selectedCount == 0)
{
noSelectionAction.setEnabled(false);
@@ -168,57 +220,22 @@ QWidget* KCTriageView::initImageTable()
QString loadActionText = (selectedCount == 1) ? "Load Selected Image" : QString("Load %1 Selected Images").arg(selectedCount);
loadImagesAction.setText(loadActionText);
connect(&loadImagesAction, &QAction::triggered, [this, addresses]() {
- loadImagesWithAddr(addresses);
+ loadImagesWithAddr(addresses, false);
});
contextMenu.addAction(&loadImagesAction);
+
+ // Format action text for loading selected images with dependencies
+ QString loadWithDepsActionText = (selectedCount == 1) ? "Load Selected Image and Dependencies" : QString("Load %1 Selected Images and Dependencies").arg(selectedCount);
+ loadImagesWithDepsAction.setText(loadWithDepsActionText);
+ connect(&loadImagesWithDepsAction, &QAction::triggered, [this, addresses]() {
+ this->loadImagesWithAddr(addresses, true);
+ });
+ contextMenu.addAction(&loadImagesWithDepsAction);
}
contextMenu.exec(m_imageTable->viewport()->mapToGlobal(pos));
});
- BackgroundThread::create(m_imageTable)->thenBackground([this](const QVariant var) {
- QVariantList rows;
-
- auto images = m_cache->GetImages();
-
- auto newHeaders = std::make_shared<std::vector<KernelCacheMachOHeader>>();
- newHeaders->reserve(images.size());
-
- for (const auto& img : images)
- {
- if (auto header = m_cache->GetMachOHeaderForImage(img.name); header)
- {
- newHeaders->push_back(*header);
- rows.push_back(QList<QVariant>{
- QString("0x%1").arg(header->textBase, 0, 16),
- QString(""),
- QString::fromStdString(img.name)
- });
- }
- }
-
- std::unique_lock<std::mutex> lock(m_headersMutex);
- m_headers.swap(newHeaders);
-
- return QVariant(rows);
- })->thenMainThread([this](const QVariant var) {
- QVariantList rows = var.toList();
-
- if (m_imageModel->rowCount() > 0)
- m_imageModel->removeRows(0, m_imageModel->rowCount());
-
- for (const QVariant &rowVariant : rows) {
- QVariantList row = rowVariant.toList();
-
- QList<QStandardItem*> items;
- for (const QVariant &cellValue : row)
- items.append(new QStandardItem(cellValue.toString()));
-
- m_imageModel->appendRow(items);
- m_imageTable->resizeColumnsToContents();
- }
- })->start();
-
auto loadImageButton = new QPushButton();
connect(loadImageButton, &QPushButton::clicked, [this](bool) {
// Collect only visible selected rows
@@ -239,6 +256,13 @@ QWidget* KCTriageView::initImageTable()
});
loadImageButton->setText(" Load Selected ");
+ auto refreshDataButton = new QPushButton();
+ {
+ // TODO: Might want to introduce a cooldown for this button (if we even keep it)
+ connect(refreshDataButton, &QPushButton::clicked, [this](bool) { RefreshData(); });
+ refreshDataButton->setText("Refresh");
+ } // refreshDataButton
+
auto loadImageFilterEdit = new FilterEdit(m_imageTable);
connect(loadImageFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) {
m_imageTable->setFilter(filter.toStdString());
@@ -255,6 +279,7 @@ QWidget* KCTriageView::initImageTable()
auto loadImageFooterLayout = new QHBoxLayout;
loadImageFooterLayout->addWidget(loadImageButton);
+ loadImageFooterLayout->addWidget(refreshDataButton);
loadImageFooterLayout->setAlignment(Qt::AlignLeft);
loadImageLayout->addLayout(loadImageFooterLayout);
@@ -285,7 +310,10 @@ QWidget* KCTriageView::initImageTable()
void KCTriageView::initSymbolTable()
{
- m_symbolTable = new SymbolTableView(this, m_cache);
+ m_symbolTable = new SymbolTableView(this);
+
+ // Apply custom column styling
+ m_symbolTable->setItemDelegateForColumn(0, new AddressColorDelegate(m_symbolTable));
auto symbolFilterEdit = new FilterEdit(m_symbolTable);
connect(symbolFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) {
@@ -308,8 +336,14 @@ void KCTriageView::initSymbolTable()
currentImageLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
connect(m_symbolTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, [this, currentImageLabel](const QModelIndex &current, const QModelIndex &) {
auto symbol = m_symbolTable->getSymbolAtRow(current.row());
- auto imageName = m_cache->GetImageNameForAddress(symbol.address);
- currentImageLabel->setText("Image: " + QString::fromStdString(imageName));
+ auto controller = KernelCacheController::GetController(*this->m_data);
+ if (!controller)
+ return;
+ auto image = controller->GetImageContaining(symbol.address);
+ if (image)
+ currentImageLabel->setText("Image: " + QString::fromStdString(image->name));
+ else
+ currentImageLabel->setText("");
});
auto symbolFooterLayout = new QHBoxLayout;
@@ -325,27 +359,29 @@ void KCTriageView::initSymbolTable()
auto symbolWidget = new QWidget;
symbolWidget->setLayout(symbolLayout);
- std::function<void(uint64_t)> navigateToAddress = [=, this](uint64_t addr) {
- ExecuteOnMainThread([addr, this](){
- if (Settings::Instance()->Get<bool>("ui.view.graph.preferred"))
- m_data->Navigate("Graph:KCView", addr);
- else
- m_data->Navigate("Linear:KCView", addr);
- });
- };
-
connect(m_symbolTable, &SymbolTableView::activated, this, [=, this](const QModelIndex& index)
{
auto symbol = m_symbolTable->getSymbolAtRow(index.row());
- WorkerPriorityEnqueue([this, symbol, navigateToAddress]() {
- if (m_data->IsValidOffset(symbol.address))
- navigateToAddress(symbol.address);
- else
+ auto dialog = new QMessageBox(this);
+
+ auto controller = KernelCacheController::GetController(*this->m_data);
+ if (!controller)
+ return;
+
+ auto image = controller->GetImageContaining(symbol.address);
+ if (!image.has_value())
+ return;
+
+ dialog->setText("Load " + QString::fromStdString(image->name) + "?");
+ dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+
+ connect(dialog, &QMessageBox::buttonClicked, this, [=](QAbstractButton* button)
{
- m_cache->LoadImageWithInstallName(symbol.image);
- navigateToAddress(symbol.address);
- }
- });
+ if (button == dialog->button(QMessageBox::Yes))
+ loadImagesWithAddr({image->headerFileAddress});
+ });
+
+ dialog->exec();
});
m_triageTabs->addTab(symbolWidget, "Symbols");
@@ -375,3 +411,45 @@ uint64_t KCTriageView::getCurrentOffset()
{
return 0;
}
+
+
+SelectionInfoForXref KCTriageView::getSelectionForXref()
+{
+ // TODO: If we are in the symbols view we _can_ actually show a useful xref to the selected symbols.
+ SelectionInfoForXref selection = {};
+ selection.addrValid = false;
+ return selection;
+}
+
+
+void KCTriageView::OnAfterOpenFile(UIContext *context, FileContext *file, ViewFrame *frame)
+{
+ RefreshData();
+ UIContextNotification::OnAfterOpenFile(context, file, frame);
+}
+
+
+// Called when shared cache information has changed.
+void KCTriageView::RefreshData()
+{
+ // Controller should be available after view init.
+ auto controller = KernelCacheController::GetController(*m_data);
+ if (!controller)
+ return;
+
+ m_imageModel->setRowCount(0);
+ for (const auto& img : controller->GetImages())
+ {
+ m_imageModel->appendRow({
+ new QStandardItem(QString("0x%1").arg(img.headerVirtualAddress, 0, 16)),
+ new QStandardItem(""),
+ new QStandardItem(QString::fromStdString(img.name))
+ });
+ }
+
+ // Set images as loaded (updating the relevant image row)
+ for (const auto& loadedImg : controller->GetLoadedImages())
+ setImageLoaded(loadedImg.headerVirtualAddress);
+
+ m_symbolTable->populateSymbols(*m_data);
+}
diff --git a/view/kernelcache/ui/kctriage.h b/view/kernelcache/ui/kctriage.h
index 2bd106b6..d918cad6 100644
--- a/view/kernelcache/ui/kctriage.h
+++ b/view/kernelcache/ui/kctriage.h
@@ -6,18 +6,19 @@
#include <QStyledItemDelegate>
#include <QTableView>
#include <binaryninjaapi.h>
-#include <kernelcacheapi.h>
#include <progresstask.h>
+#include <kernelcacheapi.h>
#include "filter.h"
+#include "symboltable.h"
#include "ui/fontsettings.h"
#include "uicontext.h"
#include "uitypes.h"
#include "viewframe.h"
#ifndef BINARYNINJA_KCTRIAGE_H
-#define BINARYNINJA_KCTRIAGE_H
-
+ #define BINARYNINJA_KCTRIAGE_H
+using namespace BinaryNinja;
using namespace KernelCacheAPI;
@@ -40,30 +41,15 @@ public:
};
-class MonospaceFontDelegate : public QStyledItemDelegate {
-public:
- explicit MonospaceFontDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {}
-
- void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override {
- QStyleOptionViewItem opt = option;
- initStyleOption(&opt, index);
-
- opt.font = getMonospaceFont(qobject_cast<QWidget*>(parent()));
-
- QStyledItemDelegate::paint(painter, opt, index);
- }
-};
-
-
class LoadedDelegate : public QItemDelegate
{
-Q_OBJECT
+ Q_OBJECT
public:
explicit LoadedDelegate(QObject* parent = nullptr) : QItemDelegate(parent) {}
void paint(QPainter *painter, const QStyleOptionViewItem &option,
- const QModelIndex &index) const override
+ const QModelIndex &index) const override
{
if (!index.isValid())
return;
@@ -98,7 +84,7 @@ public:
}
QSize sizeHint(const QStyleOptionViewItem &option,
- const QModelIndex &index) const override
+ const QModelIndex &index) const override
{
Q_UNUSED(option);
Q_UNUSED(index);
@@ -113,7 +99,6 @@ public:
};
-
class FilterableTableView : public QTableView, public FilterTarget {
Q_OBJECT
@@ -190,165 +175,11 @@ signals:
};
-class SymbolTableProxyModel : public QSortFilterProxyModel
-{
-Q_OBJECT
-
-public:
- explicit SymbolTableProxyModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent), m_timer(new QTimer(this))
- {
- m_timer->setSingleShot(true);
- connect(m_timer, &QTimer::timeout, this, &SymbolTableProxyModel::delayedFilterChanged);
- }
-
- void setFilterString(const QString& filter)
- {
- QRegularExpression newRegEx(QRegularExpression::escape(filter), QRegularExpression::CaseInsensitiveOption);
- if (m_filter != newRegEx) {
- m_filter = std::move(newRegEx);
- m_timer->start(200);
- }
- }
-
-protected:
- bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override
- {
- if (m_filter.pattern().isEmpty())
- return true;
-
- for (int column = 0; column < sourceModel()->columnCount(source_parent); ++column)
- {
- QModelIndex index = sourceModel()->index(source_row, column, source_parent);
- QString data = sourceModel()->data(index).toString();
- if (m_filter.match(data).hasMatch())
- return true;
- }
- return false;
- }
-
-private slots:
- void delayedFilterChanged()
- {
- invalidateFilter();
- }
-
-private:
- QRegularExpression m_filter;
- QTimer* m_timer;
-};
-
-
-class SymbolTableView : public QTableView, public FilterTarget
-{
-Q_OBJECT
- friend class SymbolTableModel;
-
- std::vector<KernelCacheAPI::KCSymbol> m_symbols;
- QStandardItemModel* m_model;
- SymbolTableProxyModel* m_proxyModel;
-
-public:
- SymbolTableView(QWidget* parent, Ref<KernelCache>& cache)
- : QTableView(parent), m_model(new QStandardItemModel(this)), m_proxyModel(new SymbolTableProxyModel(this))
- {
- m_proxyModel->setSourceModel(m_model);
- setModel(m_proxyModel);
-
- // Set up the headers
- m_model->setColumnCount(3);
- m_model->setHorizontalHeaderLabels({"Address", "Name", "Image"});
- setFont(getMonospaceFont(parent));
- setItemDelegateForColumn(0, new AddressColorDelegate(this));
- setItemDelegateForColumn(1, new MonospaceFontDelegate(this));
- setItemDelegateForColumn(2, new MonospaceFontDelegate(this));
-
- // Configure view settings
- horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
- horizontalHeader()->setSectionResizeMode(1, QHeaderView::Interactive);
- horizontalHeader()->resizeSection(1, 400);
- horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
- setEditTriggers(QAbstractItemView::NoEditTriggers);
- setSelectionBehavior(QAbstractItemView::SelectRows);
- setSelectionMode(QAbstractItemView::SingleSelection);
- verticalHeader()->setVisible(false);
-
- setSortingEnabled(true);
-
- BackgroundThread::create(this)->thenBackground([this, cache](){
- m_symbols = cache->LoadAllSymbolsAndWait();
- })->thenMainThread([this](){
- updateSymbols();
- })->start();
- }
-
- ~SymbolTableView() override = default;
-
- void updateSymbols()
- {
- m_model->removeRows(0, m_model->rowCount());
- for (const auto& symbol : m_symbols)
- {
- QList<QStandardItem*> row;
- row << new QStandardItem(QString("0x%1").arg(symbol.address, 0, 16))
- << new QStandardItem(QString::fromStdString(symbol.name))
- << new QStandardItem(QString::fromStdString(symbol.image));
- m_model->appendRow(row);
- }
- }
-
- KernelCacheAPI::KCSymbol getSymbolAtRow(int row) const
- {
- QModelIndex proxyIndex = m_proxyModel->index(row, 0);
- QModelIndex sourceIndex = m_proxyModel->mapToSource(proxyIndex);
- return m_symbols[sourceIndex.row()];
- }
-
- void scrollToFirstItem() override
- {
- scrollToTop();
- }
-
- void scrollToCurrentItem() override
- {
- scrollTo(selectionModel()->currentIndex());
- }
-
- void selectFirstItem() override
- {
- if (m_proxyModel->rowCount() > 0) {
- QModelIndex idx = m_proxyModel->index(0, 0);
- if (idx.isValid()) {
- selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect);
- setCurrentIndex(idx);
- }
- }
- }
-
- void activateFirstItem() override
- {
- if (m_proxyModel->rowCount() > 0) {
- QModelIndex idx = m_proxyModel->index(0, 0);
- if (idx.isValid()) {
- setCurrentIndex(idx);
- emit activated(idx);
- }
- }
- }
-
- void setFilter(const std::string& text) override
- {
- m_proxyModel->setFilterString(QString::fromStdString(text));
- }
-};
-
-
class KCTriageView : public QWidget, public View, public UIContextNotification
{
BinaryViewRef m_data;
QVBoxLayout* m_layout;
- Ref<KernelCacheAPI::KernelCache> m_cache;
-
SplitTabWidget* m_triageTabs;
DockableTabCollection* m_triageCollection;
@@ -357,8 +188,10 @@ class KCTriageView : public QWidget, public View, public UIContextNotification
SymbolTableView* m_symbolTable;
- std::mutex m_headersMutex;
- std::shared_ptr<std::vector<KernelCacheAPI::KernelCacheMachOHeader>> m_headers;
+ FilterableTableView* m_mappingTable;
+ QStandardItemModel* m_mappingModel;
+
+ QStandardItemModel* m_regionModel;
public:
KCTriageView(QWidget* parent, BinaryViewRef data);
@@ -368,10 +201,14 @@ public:
QFont getFont() override;
bool navigate(uint64_t offset) override;
uint64_t getCurrentOffset() override;
+ SelectionInfoForXref getSelectionForXref() override;
+
+ void OnAfterOpenFile(UIContext* context, FileContext* file, ViewFrame* frame) override;
+ void RefreshData();
private:
- void loadImagesWithAddr(const std::vector<uint64_t>& addresses);
- void setImageLoaded(const uint64_t imageHeaderAddr);
+ void loadImagesWithAddr(const std::vector<uint64_t>& addresses, bool includeDependencies = false);
+ void setImageLoaded(uint64_t imageHeaderAddr);
QWidget* initImageTable();
void initSymbolTable();
};
@@ -386,5 +223,4 @@ public:
static void Register();
};
-
#endif // BINARYNINJA_KCTRIAGE_H
diff --git a/view/kernelcache/ui/symboltable.cpp b/view/kernelcache/ui/symboltable.cpp
new file mode 100644
index 00000000..5c86b3b4
--- /dev/null
+++ b/view/kernelcache/ui/symboltable.cpp
@@ -0,0 +1,216 @@
+#include <progresstask.h>
+#include "symboltable.h"
+
+#include <QHeaderView>
+
+#include "ui/fontsettings.h"
+
+#include "binaryninjaapi.h"
+
+using namespace BinaryNinja;
+using namespace KernelCacheAPI;
+
+
+SymbolTableModel::SymbolTableModel(SymbolTableView* parent)
+ : QAbstractTableModel(parent), m_parent(parent) {
+ // TODO: Need to implement updating this font if it is changed by the user
+ m_font = getMonospaceFont(parent);
+}
+
+
+int SymbolTableModel::rowCount(const QModelIndex& parent) const {
+ Q_UNUSED(parent);
+ return static_cast<int>(m_modelSymbols.size());
+}
+
+
+int SymbolTableModel::columnCount(const QModelIndex& parent) const {
+ Q_UNUSED(parent);
+ // We have 3 columns: Address, Type, Name
+ return 3;
+}
+
+
+QVariant SymbolTableModel::data(const QModelIndex& index, int role) const {
+ if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::FontRole)) {
+ return QVariant();
+ }
+
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ {
+ auto symbol = symbolAt(index.row());
+ auto symbolType = GetSymbolTypeAsString(symbol.type);
+
+ switch (index.column())
+ {
+ case 0: // Address column
+ return QString("0x%1").arg(symbol.address, 0, 16); // Display address as hexadecimal
+ case 1: // Type column
+ return QString::fromUtf8(symbolType.c_str(), symbolType.size());
+ case 2: // Name column
+ return QString::fromUtf8(symbol.name.c_str(), symbol.name.size());
+ default:
+ return QVariant();
+ }
+ }
+ case Qt::FontRole:
+ return m_font;
+ default:
+ return QVariant();
+ }
+}
+
+
+QVariant SymbolTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
+ if (role != Qt::DisplayRole || orientation != Qt::Horizontal) {
+ return QVariant();
+ }
+
+ switch (section) {
+ case 0:
+ return QString("Address");
+ case 1:
+ return QString("Type");
+ case 2:
+ return QString("Name");
+ default:
+ return QVariant();
+ }
+}
+
+
+void SymbolTableModel::sort(int column, Qt::SortOrder order)
+{
+ beginResetModel();
+
+ std::function<bool(const CacheSymbol&, const CacheSymbol&)> comparator;
+
+ switch (column)
+ {
+ case 0: // Address column
+ comparator = [](const CacheSymbol& a, const CacheSymbol& b) {
+ return a.address < b.address;
+ };
+ break;
+ case 1: // Type column
+ comparator = [](const CacheSymbol& a, const CacheSymbol& b) {
+ return GetSymbolTypeAsString(a.type) < GetSymbolTypeAsString(b.type);
+ };
+ break;
+ case 2: // Name column
+ comparator = [](const CacheSymbol& a, const CacheSymbol& b) {
+ return a.name < b.name;
+ };
+ break;
+ default:
+ endResetModel();
+ return;
+ }
+
+ if (order == Qt::DescendingOrder)
+ {
+ std::sort(m_modelSymbols.begin(), m_modelSymbols.end(),
+ [&comparator](const CacheSymbol& a, const CacheSymbol& b) {
+ return comparator(b, a);
+ });
+ }
+ else
+ {
+ std::sort(m_modelSymbols.begin(), m_modelSymbols.end(), comparator);
+ }
+
+ endResetModel();
+}
+
+
+void SymbolTableModel::updateSymbols(std::vector<CacheSymbol>&& symbols)
+{
+ m_preparedSymbols = symbols;
+ setFilter(m_filter);
+}
+
+
+const CacheSymbol& SymbolTableModel::symbolAt(int row) const
+{
+ return m_modelSymbols.at(row);
+}
+
+
+void SymbolTableModel::setFilter(std::string text)
+{
+ beginResetModel();
+
+ m_filter = text;
+ m_modelSymbols = {};
+
+ // Skip filtering if no filter applied.
+ if (!m_filter.empty())
+ {
+ m_modelSymbols.reserve(m_preparedSymbols.size());
+ for (const auto& symbol : m_preparedSymbols)
+ if (((std::string_view)symbol.name).find(m_filter) != std::string::npos)
+ m_modelSymbols.push_back(symbol);
+ m_modelSymbols.shrink_to_fit();
+ }
+ else
+ {
+ m_modelSymbols = m_preparedSymbols;
+ }
+
+ endResetModel();
+}
+
+
+SymbolTableView::SymbolTableView(QWidget* parent)
+ : QTableView(parent), m_model(new SymbolTableModel(this)) {
+
+ // Set up the filter model
+ setModel(m_model);
+
+ // Configure view settings
+ horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
+ horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
+ horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
+ setEditTriggers(QAbstractItemView::NoEditTriggers);
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ setSelectionMode(QAbstractItemView::SingleSelection);
+ verticalHeader()->setVisible(false);
+
+ setSortingEnabled(true);
+}
+
+SymbolTableView::~SymbolTableView() {
+ delete m_model;
+}
+
+void SymbolTableView::populateSymbols(BinaryView &view)
+{
+ if (auto controller = KernelCacheController::GetController(view)) {
+ typedef std::vector<CacheSymbol> SymbolList;
+ // Retrieve the symbols from the controller in a future than pass that to the model.
+ QPointer<QFutureWatcher<SymbolList>> watcher = new QFutureWatcher<SymbolList>(this);
+ connect(watcher, &QFutureWatcher<SymbolList>::finished, this, [watcher, this]() {
+ if (watcher)
+ {
+ auto symbols = watcher->result();
+ m_model->updateSymbols(std::move(symbols));
+ }
+ });
+ QFuture<SymbolList> future = QtConcurrent::run([controller]() {
+ return controller->GetSymbols();
+ });
+ watcher->setFuture(future);
+ connect(this, &QObject::destroyed, this, [watcher]() {
+ if (watcher && watcher->isRunning()) {
+ watcher->cancel();
+ watcher->waitForFinished();
+ }
+ });
+ }
+}
+
+void SymbolTableView::setFilter(const std::string& filter) {
+ m_model->setFilter(filter);
+}
diff --git a/view/kernelcache/ui/symboltable.h b/view/kernelcache/ui/symboltable.h
new file mode 100644
index 00000000..866390c7
--- /dev/null
+++ b/view/kernelcache/ui/symboltable.h
@@ -0,0 +1,102 @@
+#pragma once
+
+#include <kernelcacheapi.h>
+#include "viewframe.h"
+
+#include <QTableView>
+#include <QStandardItemModel>
+#include "filter.h"
+
+#ifndef BINARYNINJA_KCSYMBOLTABLE_H
+#define BINARYNINJA_KCSYMBOLTABLE_H
+
+class SymbolTableView;
+
+
+class SymbolTableModel : public QAbstractTableModel
+{
+Q_OBJECT
+ SymbolTableView* m_parent;
+ QFont m_font;
+ std::string m_filter;
+ std::vector<KernelCacheAPI::CacheSymbol> m_preparedSymbols{};
+ // These are the symbols we actually use
+ std::vector<KernelCacheAPI::CacheSymbol> m_modelSymbols{};
+
+public:
+ explicit SymbolTableModel(SymbolTableView* parent);
+
+ int rowCount(const QModelIndex& parent) const override;
+ int columnCount(const QModelIndex& parent) const override;
+ QVariant data(const QModelIndex& index, int role) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+ void sort(int column, Qt::SortOrder order) override;
+ void updateSymbols(std::vector<KernelCacheAPI::CacheSymbol>&& symbols);
+ void setFilter(std::string text);
+ const KernelCacheAPI::CacheSymbol& symbolAt(int row) const;
+
+};
+
+
+class SymbolTableView : public QTableView, public FilterTarget
+{
+Q_OBJECT
+ friend class SymbolTableModel;
+
+ SymbolTableModel* m_model;
+
+public:
+ explicit SymbolTableView(QWidget* parent);
+ ~SymbolTableView() override;
+
+ // Call this to populate the symbols from the given view.
+ void populateSymbols(BinaryNinja::BinaryView& view);
+
+ void scrollToFirstItem() override
+ {
+ if (model()->rowCount() > 0) {
+ QModelIndex top = indexAt(rect().topLeft());
+ if (top.isValid())
+ scrollTo(top);
+ }
+ }
+
+ void scrollToCurrentItem() override
+ {
+ QModelIndex currentIndex = selectionModel()->currentIndex();
+ if (currentIndex.isValid())
+ scrollTo(currentIndex);
+ }
+
+ void selectFirstItem() override
+ {
+ if (model()->rowCount() > 0) {
+ QModelIndex top = indexAt(rect().topLeft());
+ if (top.isValid()) {
+ selectionModel()->select(top, QItemSelectionModel::ClearAndSelect);
+ setCurrentIndex(top);
+ }
+ }
+ }
+
+ void activateFirstItem() override
+ {
+ if (model()->rowCount() > 0) {
+ QModelIndex topLeft = indexAt(rect().topLeft());
+ if (topLeft.isValid()) {
+ setCurrentIndex(topLeft);
+ emit activated(topLeft);
+ }
+ }
+ }
+
+ KernelCacheAPI::CacheSymbol getSymbolAtRow(int row) const
+ {
+ return m_model->symbolAt(row);
+ }
+
+ void setFilter(const std::string& filter) override;
+};
+
+
+#endif // BINARYNINJA_KCSYMBOLTABLE_H
diff --git a/view/macho/machoview.h b/view/macho/machoview.h
index 1a715991..dfbb24fe 100644
--- a/view/macho/machoview.h
+++ b/view/macho/machoview.h
@@ -1366,6 +1366,15 @@ namespace BinaryNinja
};
#endif
+ struct fileset_entry_command {
+ uint32_t cmd;
+ uint32_t cmdsize;
+ uint64_t vmaddr;
+ uint64_t fileoff;
+ uint32_t nameEntryOffsetFromBaseOfCommand;
+ uint32_t reserved;
+ };
+
struct MachOHeader {
bool isMainHeader = false;