summaryrefslogtreecommitdiff
path: root/view/kernelcache/api
diff options
context:
space:
mode:
Diffstat (limited to 'view/kernelcache/api')
-rw-r--r--view/kernelcache/api/CMakeLists.txt79
-rw-r--r--view/kernelcache/api/kernelcache.cpp191
-rw-r--r--view/kernelcache/api/kernelcacheapi.h272
-rw-r--r--view/kernelcache/api/kernelcachecore.h133
-rw-r--r--view/kernelcache/api/python/CMakeLists.txt50
-rw-r--r--view/kernelcache/api/python/__init__.py8
-rw-r--r--view/kernelcache/api/python/_kernelcachecore.py515
-rw-r--r--view/kernelcache/api/python/_kernelcachecore_template.py43
-rw-r--r--view/kernelcache/api/python/generator.cpp617
-rw-r--r--view/kernelcache/api/python/kernelcache.py238
-rw-r--r--view/kernelcache/api/python/kernelcache_enums.py14
11 files changed, 2160 insertions, 0 deletions
diff --git a/view/kernelcache/api/CMakeLists.txt b/view/kernelcache/api/CMakeLists.txt
new file mode 100644
index 00000000..ae7e454a
--- /dev/null
+++ b/view/kernelcache/api/CMakeLists.txt
@@ -0,0 +1,79 @@
+cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
+
+project(kernelcacheapi)
+file(GLOB BN_MACHO_API_SOURCES *.cpp *.h)
+add_library(kernelcacheapi OBJECT ${BN_MACHO_API_SOURCES})
+
+
+set(COMPILE_DEFS "")
+
+if (HARD_FAIL_MODE)
+ set(COMPILE_DEFS "${COMPILE_DEFS} ABORT_FAILURES;")
+endif()
+
+if (BN_REF_COUNT_DEBUG)
+ set(COMPILE_DEFS "${COMPILE_DEFS} BN_REF_COUNT_DEBUG;")
+endif()
+
+if (SLIDEINFO_DEBUG_TAGS)
+ set(COMPILE_DEFS "${COMPILE_DEFS} SLIDEINFO_DEBUG_TAGS;")
+endif()
+
+if (METADATA_VERSION)
+ set(COMPILE_DEFS "${COMPILE_DEFS} METADATA_VERSION=${METADATA_VERSION};")
+else()
+ message(FATAL_ERROR "No metadata version provided. Fatal.")
+endif()
+
+if (KC_VIEW_NAME)
+ set(COMPILE_DEFS "${COMPILE_DEFS} KC_VIEW_NAME=\"${KC_VIEW_NAME}\";")
+else()
+ message(FATAL_ERROR "No view name provided. Fatal.")
+endif()
+
+target_compile_definitions(kernelcacheapi PRIVATE ${COMPILE_DEFS})
+
+
+function(get_recursive_include_dirs target result)
+ # Initialize an empty list to store include directories
+ set(include_dirs "")
+
+ # Get the include directories of the current target
+ get_target_property(current_target_includes ${target} INTERFACE_INCLUDE_DIRECTORIES)
+ if(current_target_includes)
+ list(APPEND include_dirs ${current_target_includes})
+ endif()
+
+ # Get the libraries that this target links to
+ get_target_property(linked_libraries ${target} INTERFACE_LINK_LIBRARIES)
+ if(linked_libraries)
+ foreach(linked_library IN LISTS linked_libraries)
+ # Skip plain library names (non-target libraries)
+ if(TARGET ${linked_library})
+ # Recursively get include directories from linked libraries
+ get_recursive_include_dirs(${linked_library} linked_library_includes)
+ list(APPEND include_dirs ${linked_library_includes})
+ endif()
+ endforeach()
+ endif()
+
+ # Set the result to the collected include directories
+ set(${result} ${include_dirs} PARENT_SCOPE)
+endfunction()
+
+get_recursive_include_dirs(binaryninjaapi INCLUDES)
+
+target_include_directories(kernelcacheapi
+ PUBLIC ${PROJECT_SOURCE_DIR} ${INCLUDES})
+
+set_target_properties(kernelcacheapi PROPERTIES
+ CXX_STANDARD 17
+ CXX_VISIBILITY_PRESET hidden
+ CXX_STANDARD_REQUIRED ON
+ VISIBILITY_INLINES_HIDDEN ON
+ POSITION_INDEPENDENT_CODE ON
+ ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/out)
+
+if (NOT DEMO)
+ add_subdirectory(python)
+endif() \ No newline at end of file
diff --git a/view/kernelcache/api/kernelcache.cpp b/view/kernelcache/api/kernelcache.cpp
new file mode 100644
index 00000000..6cca9033
--- /dev/null
+++ b/view/kernelcache/api/kernelcache.cpp
@@ -0,0 +1,191 @@
+//
+// Created by kat on 5/21/23.
+//
+
+#include "kernelcacheapi.h"
+
+namespace KernelCacheAPI {
+
+ KernelCache::KernelCache(Ref<BinaryView> view) {
+ m_object = BNGetKernelCache(view->GetObject());
+ }
+
+ BNKCViewLoadProgress KernelCache::GetLoadProgress(Ref<BinaryView> view)
+ {
+ return BNKCViewGetLoadProgress(view->GetFile()->GetSessionId());
+ }
+
+ uint64_t KernelCache::FastGetImageCount(Ref<BinaryView> view)
+ {
+ return BNKCViewFastGetImageCount(view->GetObject());
+ }
+
+ bool KernelCache::LoadImageWithInstallName(std::string installName)
+ {
+ char* str = BNAllocString(installName.c_str());
+ return BNKCViewLoadImageWithInstallName(m_object, str);
+ }
+
+ bool KernelCache::LoadImageContainingAddress(uint64_t addr)
+ {
+ return BNKCViewLoadImageContainingAddress(m_object, addr);
+ }
+
+ std::vector<std::string> KernelCache::GetAvailableImages()
+ {
+ 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;
+ }
+
+ std::vector<KCImage> KernelCache::GetImages()
+ {
+ size_t count;
+ BNKCImage* value = BNKCViewGetAllImages(m_object, &count);
+ if (value == nullptr)
+ {
+ return {};
+ }
+
+ 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);
+ }
+
+ BNKCViewFreeAllImages(value, count);
+ 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);
+
+ return result;
+ }
+
+ std::vector<KCSymbol> KernelCache::LoadAllSymbolsAndWait()
+ {
+ size_t count;
+ BNKCSymbolRep* value = BNKCViewLoadAllSymbolsAndWait(m_object, &count);
+ if (value == nullptr)
+ {
+ return {};
+ }
+
+ 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);
+ }
+
+ BNKCViewFreeSymbols(value, count);
+ return result;
+ }
+
+ 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::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::optional<KernelCacheMachOHeader> KernelCache::GetMachOHeaderForImage(std::string name)
+ {
+ char* str = BNAllocString(name.c_str());
+ char* outputStr = BNKCViewGetImageHeaderForName(m_object, 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);
+ }
+
+} // namespace KernelCacheAPI
diff --git a/view/kernelcache/api/kernelcacheapi.h b/view/kernelcache/api/kernelcacheapi.h
new file mode 100644
index 00000000..149f39bf
--- /dev/null
+++ b/view/kernelcache/api/kernelcacheapi.h
@@ -0,0 +1,272 @@
+#pragma once
+
+#include <binaryninjaapi.h>
+#include "../core/MetadataSerializable.hpp"
+#include "../api/view/macho/machoview.h"
+#include "kernelcachecore.h"
+
+using namespace BinaryNinja;
+
+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;
+ }
+
+ public:
+ std::atomic<int> m_refs;
+ T *m_object;
+
+ KCRefCountObject() : m_refs(0), m_object(nullptr) {}
+
+ virtual ~KCRefCountObject() {}
+
+ T *GetObject() const { return m_object; }
+
+ static T *GetObject(KCRefCountObject *obj) {
+ if (!obj)
+ return nullptr;
+ return obj->GetObject();
+ }
+
+ void AddRef() { AddRefInternal(); }
+
+ void Release() { ReleaseInternal(); }
+
+ void AddRefForRegistration() { AddRefInternal(); }
+ };
+
+
+ template<class 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;
+ }
+ }
+
+ public:
+ std::atomic<int> m_refs;
+ bool m_registeredRef = false;
+ T *m_object;
+
+ KCCoreRefCountObject() : m_refs(0), m_object(nullptr) {}
+
+ virtual ~KCCoreRefCountObject() {}
+
+ T *GetObject() const { return m_object; }
+
+ static T *GetObject(KCCoreRefCountObject *obj) {
+ if (!obj)
+ return nullptr;
+ return obj->GetObject();
+ }
+
+ void AddRef() {
+ if (m_object && (m_refs != 0))
+ AddObjectReference(m_object);
+ AddRefInternal();
+ }
+
+ void Release() {
+ if (m_object)
+ FreeObjectReference(m_object);
+ ReleaseInternal();
+ }
+
+ void AddRefForRegistration() { m_registeredRef = true; }
+
+ void ReleaseForRegistration() {
+ m_object = nullptr;
+ m_registeredRef = false;
+ if (m_refs == 0)
+ delete this;
+ }
+ };
+
+ struct KCMemoryRegion {
+ uint64_t vmAddress;
+ uint64_t size;
+ std::string prettyName;
+ };
+
+ struct BackingCacheMapping {
+ uint64_t vmAddress;
+ uint64_t size;
+ uint64_t fileOffset;
+ };
+
+ struct BackingCache {
+ std::string path;
+ bool isPrimary;
+ std::vector<BackingCacheMapping> mappings;
+ };
+
+ struct KCImageMemoryMapping {
+ std::string name;
+ uint64_t vmAddress;
+ uint64_t size;
+ bool loaded;
+ uint64_t rawViewOffset;
+ };
+
+ struct KCImage {
+ std::string name;
+ uint64_t headerFileAddress;
+ std::vector<KCImageMemoryMapping> mappings;
+ };
+
+ struct KCSymbol {
+ uint64_t address;
+ std::string name;
+ std::string image;
+ };
+
+ 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;
+
+ 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;
+ 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;
+
+ std::vector<section_64> symbolStubSections;
+ std::vector<section_64> symbolPointerSections;
+
+ std::vector<std::string> dylibs;
+
+ 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);
+ }
+
+ static KernelCacheMachOHeader Load(KernelCacheCore::DeserializationContext& context) {
+ KernelCacheMachOHeader header;
+ header.MSL(textBase);
+ header.MSL(loadCommandOffset);
+ header.MSL(ident);
+ header.MSL(identifierPrefix);
+ header.MSL(installName);
+ header.MSL(entryPoints);
+ header.MSL(m_entryPoints);
+ header.MSL(symtab);
+ header.MSL(dysymtab);
+ header.MSL(dyldInfo);
+ header.MSL(routines64);
+ header.MSL(functionStarts);
+ header.MSL(moduleInitSections);
+ header.MSL(exportTrie);
+ header.MSL(chainedFixups);
+ header.MSL(relocationBase);
+ header.MSL(segments);
+ header.MSL(linkeditSegment);
+ header.MSL(sections);
+ header.MSL(sectionNames);
+ header.MSL(symbolStubSections);
+ header.MSL(symbolPointerSections);
+ header.MSL(dylibs);
+ header.MSL(buildVersion);
+ header.MSL(buildToolVersions);
+ header.MSL(dysymPresent);
+ header.MSL(dyldInfoPresent);
+ header.MSL(exportTriePresent);
+ header.MSL(chainedFixupsPresent);
+ header.MSL(routinesPresent);
+ header.MSL(functionStartsPresent);
+ header.MSL(relocatable);
+ return header;
+ }
+ };
+
+
+ class KernelCache : public KCCoreRefCountObject<BNKernelCache, BNNewKernelCacheReference, BNFreeKernelCacheReference> {
+ public:
+ KernelCache(Ref<BinaryView> view);
+
+ BNKCViewState GetState();
+ static BNKCViewLoadProgress GetLoadProgress(Ref<BinaryView> view);
+ static uint64_t FastGetImageCount(Ref<BinaryView> view);
+
+ bool LoadImageWithInstallName(std::string installName);
+ bool LoadImageContainingAddress(uint64_t addr);
+ std::vector<std::string> GetAvailableImages();
+
+ std::vector<KCSymbol> LoadAllSymbolsAndWait();
+
+ std::string GetNameForAddress(uint64_t address);
+ std::string GetImageNameForAddress(uint64_t address);
+
+ std::vector<KCImage> GetImages();
+ std::vector<KCImage> GetLoadedImages();
+
+ std::optional<KernelCacheMachOHeader> GetMachOHeaderForImage(std::string name);
+ std::optional<KernelCacheMachOHeader> GetMachOHeaderForAddress(uint64_t address);
+ };
+} \ No newline at end of file
diff --git a/view/kernelcache/api/kernelcachecore.h b/view/kernelcache/api/kernelcachecore.h
new file mode 100644
index 00000000..d425bf15
--- /dev/null
+++ b/view/kernelcache/api/kernelcachecore.h
@@ -0,0 +1,133 @@
+#pragma once
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef __GNUC__
+ #ifdef KERNELCACHE_LIBRARY
+ #define KERNELCACHE_FFI_API __attribute__((visibility("default")))
+ #else // KERNELCACHE_LIBRARY
+ #define KERNELCACHE_FFI_API
+ #endif // KERNELCACHE_LIBRARY
+#else // __GNUC__
+ #ifdef _MSC_VER
+ #ifndef DEMO_VERSION
+ #ifdef KERNELCACHE_LIBRARY
+ #define KERNELCACHE_FFI_API __declspec(dllexport)
+ #else // KERNELCACHE_LIBRARY
+ #define KERNELCACHE_FFI_API __declspec(dllimport)
+ #endif // KERNELCACHE_LIBRARY
+ #else
+ #define KERNELCACHE_FFI_API
+ #endif
+ #else // _MSC_VER
+ #define KERNELCACHE_FFI_API
+ #endif // _MSC_VER
+#endif // __GNUC__C
+
+#define CORE_ALLOCATED_STRUCT(T)
+
+#define CORE_ALLOCATED_CLASS(T) \
+ public: \
+ 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 enum BNKCViewState {
+ Unloaded,
+ Loaded,
+ LoadedWithImages,
+ } BNKCViewState;
+
+ typedef enum BNKCViewLoadProgress {
+ LoadProgressNotStarted,
+ LoadProgressLoadingCaches,
+ LoadProgressLoadingImages,
+ LoadProgressFinished,
+ } BNKCViewLoadProgress;
+
+ typedef struct BNBinaryView BNBinaryView;
+ typedef struct BNKernelCache BNKernelCache;
+
+ typedef struct BNKCImageMemoryMapping {
+ 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;
+
+ typedef struct BNKCMappedMemoryRegion {
+ uint64_t vmAddress;
+ uint64_t size;
+ char* name;
+ } BNKCMappedMemoryRegion;
+
+ typedef struct BNKCMemoryUsageInfo {
+ uint64_t sharedCacheRefs;
+ uint64_t mmapRefs;
+ } BNKCMemoryUsageInfo;
+
+ typedef struct BNKCSymbolRep {
+ uint64_t address;
+ char* name;
+ char* image;
+ } BNKCSymbolRep;
+
+ KERNELCACHE_FFI_API BNKernelCache* BNGetKernelCache(BNBinaryView* data);
+
+ KERNELCACHE_FFI_API BNKernelCache* BNNewKernelCacheReference(BNKernelCache* cache);
+ KERNELCACHE_FFI_API void BNFreeKernelCacheReference(BNKernelCache* cache);
+
+ KERNELCACHE_FFI_API char** BNKCViewGetInstallNames(BNKernelCache* cache, size_t* count);
+
+ KERNELCACHE_FFI_API bool BNKCViewLoadImageWithInstallName(BNKernelCache* cache, char* name);
+ KERNELCACHE_FFI_API bool BNKCViewLoadImageContainingAddress(BNKernelCache* cache, uint64_t address);
+
+ KERNELCACHE_FFI_API char* BNKCViewGetNameForAddress(BNKernelCache* cache, uint64_t address);
+ KERNELCACHE_FFI_API char* BNKCViewGetImageNameForAddress(BNKernelCache* cache, uint64_t address);
+
+ 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 BNKCSymbolRep* BNKCViewLoadAllSymbolsAndWait(BNKernelCache* cache, size_t* count);
+ KERNELCACHE_FFI_API void BNKCViewFreeSymbols(BNKCSymbolRep* symbols, 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 BNKCImage* BNKCViewGetLoadedImages(BNKernelCache* cache, size_t* count);
+ KERNELCACHE_FFI_API void BNKCViewFreeLoadedImages(BNKCImage* images, size_t count);
+
+ KERNELCACHE_FFI_API char* BNKCViewGetImageHeaderForAddress(BNKernelCache* cache, uint64_t address);
+ KERNELCACHE_FFI_API char* BNKCViewGetImageHeaderForName(BNKernelCache* cache, char* name);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/view/kernelcache/api/python/CMakeLists.txt b/view/kernelcache/api/python/CMakeLists.txt
new file mode 100644
index 00000000..60fceeeb
--- /dev/null
+++ b/view/kernelcache/api/python/CMakeLists.txt
@@ -0,0 +1,50 @@
+cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+
+project(kernelcache-python-api)
+
+file(GLOB PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/*.py)
+list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_kernelcachecore.py)
+list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/enums.py)
+
+add_executable(kernelcache_generator
+ ${PROJECT_SOURCE_DIR}/generator.cpp)
+target_link_libraries(kernelcache_generator binaryninjaapi)
+target_include_directories(kernelcache_generator PUBLIC {PROJECT_SOURCE_DIR}/../../api)
+
+set_target_properties(kernelcache_generator PROPERTIES
+ CXX_STANDARD 17
+ CXX_STANDARD_REQUIRED ON
+ BUILD_WITH_INSTALL_RPATH OFF
+ RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+
+if(BN_INTERNAL_BUILD)
+ set(PYTHON_OUTPUT_DIRECTORY ${BN_RESOURCE_DIR}/python/binaryninja/kernelcache/)
+else()
+ set(PYTHON_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/plugins/kernelcache/)
+endif()
+
+if(WIN32)
+ if (BN_INTERNAL_BUILD)
+ add_custom_command(TARGET kernelcache_generator PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${BN_CORE_OUTPUT_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/)
+ else()
+ add_custom_command(TARGET kernelcache_generator PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${BN_INSTALL_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/)
+ endif()
+endif()
+
+add_custom_target(kernelcache_generator_copy ALL
+ BYPRODUCTS ${PROJECT_SOURCE_DIR}/_kernelcachecore.py ${PROJECT_SOURCE_DIR}/enums.py
+ DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../kernelcachecore.h $<TARGET_FILE:kernelcache_generator>
+ COMMAND ${CMAKE_COMMAND} -E echo "Copying Shared Cache Python Sources"
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_OUTPUT_DIRECTORY}
+ COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:kernelcache_generator>
+ ${PROJECT_SOURCE_DIR}/../kernelcachecore.h
+ ${PROJECT_SOURCE_DIR}/_kernelcachecore.py
+ ${PROJECT_SOURCE_DIR}/_kernelcachecore_template.py
+ ${PROJECT_SOURCE_DIR}/kernelcache_enums.py
+
+ COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_SOURCES} ${PYTHON_OUTPUT_DIRECTORY}
+ COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_kernelcachecore.py ${PYTHON_OUTPUT_DIRECTORY}
+ COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/kernelcache_enums.py ${PYTHON_OUTPUT_DIRECTORY})
+
diff --git a/view/kernelcache/api/python/__init__.py b/view/kernelcache/api/python/__init__.py
new file mode 100644
index 00000000..c6f05346
--- /dev/null
+++ b/view/kernelcache/api/python/__init__.py
@@ -0,0 +1,8 @@
+import os
+
+from binaryninja._binaryninjacore import BNGetUserPluginDirectory
+user_plugin_dir = os.path.realpath(BNGetUserPluginDirectory())
+current_path = os.path.realpath(__file__)
+
+from .kernelcache import *
+
diff --git a/view/kernelcache/api/python/_kernelcachecore.py b/view/kernelcache/api/python/_kernelcachecore.py
new file mode 100644
index 00000000..6bef0d06
--- /dev/null
+++ b/view/kernelcache/api/python/_kernelcachecore.py
@@ -0,0 +1,515 @@
+import binaryninja
+import ctypes, os
+
+from typing import Optional
+from . import kernelcache_enums
+# Load core module
+import platform
+core = None
+core_platform = platform.system()
+
+# By the time the debugger is loaded, binaryninja has not fully initialized.
+# So we cannot call binaryninja.bundled_plugin_path()
+from binaryninja._binaryninjacore import BNGetBundledPluginDirectory, BNFreeString
+if core_platform == "Darwin":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libkernelcache.dylib"))
+
+elif core_platform == "Linux":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libkernelcache.so"))
+
+elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0):
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "kernelcache.dll"))
+else:
+ raise Exception("OS not supported")
+
+def cstr(var) -> Optional[ctypes.c_char_p]:
+ if var is None:
+ return None
+ if isinstance(var, bytes):
+ return var
+ return var.encode("utf-8")
+
+def pyNativeStr(arg):
+ if isinstance(arg, str):
+ return arg
+ else:
+ return arg.decode('utf8')
+
+def free_string(value:ctypes.c_char_p) -> None:
+ BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte)))
+
+# Type definitions
+from binaryninja._binaryninjacore import BNBinaryView, BNBinaryViewHandle
+class BNKCImage(ctypes.Structure):
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+BNKCImageHandle = ctypes.POINTER(BNKCImage)
+class BNKCImageMemoryMapping(ctypes.Structure):
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+BNKCImageMemoryMappingHandle = ctypes.POINTER(BNKCImageMemoryMapping)
+class BNKCMappedMemoryRegion(ctypes.Structure):
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+BNKCMappedMemoryRegionHandle = ctypes.POINTER(BNKCMappedMemoryRegion)
+class BNKCMemoryUsageInfo(ctypes.Structure):
+ pass
+BNKCMemoryUsageInfoHandle = ctypes.POINTER(BNKCMemoryUsageInfo)
+class BNKCSymbolRep(ctypes.Structure):
+ @property
+ def name(self):
+ return pyNativeStr(self._name)
+ @name.setter
+ def name(self, value):
+ self._name = cstr(value)
+ @property
+ def image(self):
+ return pyNativeStr(self._image)
+ @image.setter
+ def image(self, value):
+ self._image = cstr(value)
+BNKCSymbolRepHandle = ctypes.POINTER(BNKCSymbolRep)
+KCViewLoadProgressEnum = ctypes.c_int
+KCViewStateEnum = ctypes.c_int
+class BNKernelCache(ctypes.Structure):
+ pass
+BNKernelCacheHandle = ctypes.POINTER(BNKernelCache)
+
+# Structure definitions
+BNKCImage._fields_ = [
+ ("_name", ctypes.c_char_p),
+ ("headerFileAddress", ctypes.c_ulonglong),
+ ("mappings", ctypes.POINTER(BNKCImageMemoryMapping)),
+ ("mappingCount", ctypes.c_ulonglong),
+ ]
+BNKCImageMemoryMapping._fields_ = [
+ ("_name", ctypes.c_char_p),
+ ("vmAddress", ctypes.c_ulonglong),
+ ("size", ctypes.c_ulonglong),
+ ("loaded", ctypes.c_bool),
+ ("rawViewOffset", ctypes.c_ulonglong),
+ ]
+BNKCMappedMemoryRegion._fields_ = [
+ ("vmAddress", ctypes.c_ulonglong),
+ ("size", ctypes.c_ulonglong),
+ ("_name", ctypes.c_char_p),
+ ]
+BNKCMemoryUsageInfo._fields_ = [
+ ("sharedCacheRefs", ctypes.c_ulonglong),
+ ("mmapRefs", ctypes.c_ulonglong),
+ ]
+BNKCSymbolRep._fields_ = [
+ ("address", ctypes.c_ulonglong),
+ ("_name", ctypes.c_char_p),
+ ("_image", ctypes.c_char_p),
+ ]
+
+# Function definitions
+# -------------------------------------------------------
+# _BNFreeKernelCacheReference
+
+_BNFreeKernelCacheReference = core.BNFreeKernelCacheReference
+_BNFreeKernelCacheReference.restype = None
+_BNFreeKernelCacheReference.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNFreeKernelCacheReference(
+ cache: ctypes.POINTER(BNKernelCache)
+ ) -> None:
+ return _BNFreeKernelCacheReference(cache)
+
+
+# -------------------------------------------------------
+# _BNGetKernelCache
+
+_BNGetKernelCache = core.BNGetKernelCache
+_BNGetKernelCache.restype = ctypes.POINTER(BNKernelCache)
+_BNGetKernelCache.argtypes = [
+ ctypes.POINTER(BNBinaryView),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNGetKernelCache(
+ data: ctypes.POINTER(BNBinaryView)
+ ) -> Optional[ctypes.POINTER(BNKernelCache)]:
+ result = _BNGetKernelCache(data)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNKCViewFastGetImageCount
+
+_BNKCViewFastGetImageCount = core.BNKCViewFastGetImageCount
+_BNKCViewFastGetImageCount.restype = ctypes.c_ulonglong
+_BNKCViewFastGetImageCount.argtypes = [
+ ctypes.POINTER(BNBinaryView),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewFastGetImageCount(
+ view: ctypes.POINTER(BNBinaryView)
+ ) -> int:
+ return _BNKCViewFastGetImageCount(view)
+
+
+# -------------------------------------------------------
+# _BNKCViewFreeAllImages
+
+_BNKCViewFreeAllImages = core.BNKCViewFreeAllImages
+_BNKCViewFreeAllImages.restype = None
+_BNKCViewFreeAllImages.argtypes = [
+ ctypes.POINTER(BNKCImage),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewFreeAllImages(
+ images: ctypes.POINTER(BNKCImage),
+ count: int
+ ) -> None:
+ return _BNKCViewFreeAllImages(images, count)
+
+
+# -------------------------------------------------------
+# _BNKCViewFreeLoadedImages
+
+_BNKCViewFreeLoadedImages = core.BNKCViewFreeLoadedImages
+_BNKCViewFreeLoadedImages.restype = None
+_BNKCViewFreeLoadedImages.argtypes = [
+ ctypes.POINTER(BNKCImage),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewFreeLoadedImages(
+ images: ctypes.POINTER(BNKCImage),
+ count: int
+ ) -> None:
+ return _BNKCViewFreeLoadedImages(images, count)
+
+
+# -------------------------------------------------------
+# _BNKCViewFreeSymbols
+
+_BNKCViewFreeSymbols = core.BNKCViewFreeSymbols
+_BNKCViewFreeSymbols.restype = None
+_BNKCViewFreeSymbols.argtypes = [
+ ctypes.POINTER(BNKCSymbolRep),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewFreeSymbols(
+ symbols: ctypes.POINTER(BNKCSymbolRep),
+ count: int
+ ) -> None:
+ return _BNKCViewFreeSymbols(symbols, count)
+
+
+# -------------------------------------------------------
+# _BNKCViewGetAllImages
+
+_BNKCViewGetAllImages = core.BNKCViewGetAllImages
+_BNKCViewGetAllImages.restype = ctypes.POINTER(BNKCImage)
+_BNKCViewGetAllImages.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewGetAllImages(
+ cache: ctypes.POINTER(BNKernelCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(BNKCImage)]:
+ result = _BNKCViewGetAllImages(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNKCViewGetImageHeaderForAddress
+
+_BNKCViewGetImageHeaderForAddress = core.BNKCViewGetImageHeaderForAddress
+_BNKCViewGetImageHeaderForAddress.restype = ctypes.POINTER(ctypes.c_byte)
+_BNKCViewGetImageHeaderForAddress.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewGetImageHeaderForAddress(
+ cache: ctypes.POINTER(BNKernelCache),
+ address: int
+ ) -> Optional[Optional[str]]:
+ result = _BNKCViewGetImageHeaderForAddress(cache, address)
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNKCViewGetImageHeaderForName
+
+_BNKCViewGetImageHeaderForName = core.BNKCViewGetImageHeaderForName
+_BNKCViewGetImageHeaderForName.restype = ctypes.POINTER(ctypes.c_byte)
+_BNKCViewGetImageHeaderForName.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ctypes.c_char_p,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewGetImageHeaderForName(
+ cache: ctypes.POINTER(BNKernelCache),
+ name: Optional[str]
+ ) -> Optional[Optional[str]]:
+ result = _BNKCViewGetImageHeaderForName(cache, cstr(name))
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNKCViewGetImageNameForAddress
+
+_BNKCViewGetImageNameForAddress = core.BNKCViewGetImageNameForAddress
+_BNKCViewGetImageNameForAddress.restype = ctypes.POINTER(ctypes.c_byte)
+_BNKCViewGetImageNameForAddress.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewGetImageNameForAddress(
+ cache: ctypes.POINTER(BNKernelCache),
+ address: int
+ ) -> Optional[Optional[str]]:
+ result = _BNKCViewGetImageNameForAddress(cache, address)
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNKCViewGetInstallNames
+
+_BNKCViewGetInstallNames = core.BNKCViewGetInstallNames
+_BNKCViewGetInstallNames.restype = ctypes.POINTER(ctypes.c_char_p)
+_BNKCViewGetInstallNames.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewGetInstallNames(
+ cache: ctypes.POINTER(BNKernelCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(ctypes.c_char_p)]:
+ result = _BNKCViewGetInstallNames(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNKCViewGetLoadProgress
+
+_BNKCViewGetLoadProgress = core.BNKCViewGetLoadProgress
+_BNKCViewGetLoadProgress.restype = KCViewLoadProgressEnum
+_BNKCViewGetLoadProgress.argtypes = [
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewGetLoadProgress(
+ sessionID: int
+ ) -> KCViewLoadProgressEnum:
+ return _BNKCViewGetLoadProgress(sessionID)
+
+
+# -------------------------------------------------------
+# _BNKCViewGetLoadedImages
+
+_BNKCViewGetLoadedImages = core.BNKCViewGetLoadedImages
+_BNKCViewGetLoadedImages.restype = ctypes.POINTER(BNKCImage)
+_BNKCViewGetLoadedImages.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewGetLoadedImages(
+ cache: ctypes.POINTER(BNKernelCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(BNKCImage)]:
+ result = _BNKCViewGetLoadedImages(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNKCViewGetNameForAddress
+
+_BNKCViewGetNameForAddress = core.BNKCViewGetNameForAddress
+_BNKCViewGetNameForAddress.restype = ctypes.POINTER(ctypes.c_byte)
+_BNKCViewGetNameForAddress.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewGetNameForAddress(
+ cache: ctypes.POINTER(BNKernelCache),
+ address: int
+ ) -> Optional[Optional[str]]:
+ result = _BNKCViewGetNameForAddress(cache, address)
+ if not result:
+ return None
+ string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))
+ BNFreeString(result)
+ return string
+
+
+# -------------------------------------------------------
+# _BNKCViewGetState
+
+_BNKCViewGetState = core.BNKCViewGetState
+_BNKCViewGetState.restype = KCViewStateEnum
+_BNKCViewGetState.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewGetState(
+ cache: ctypes.POINTER(BNKernelCache)
+ ) -> KCViewStateEnum:
+ return _BNKCViewGetState(cache)
+
+
+# -------------------------------------------------------
+# _BNKCViewLoadAllSymbolsAndWait
+
+_BNKCViewLoadAllSymbolsAndWait = core.BNKCViewLoadAllSymbolsAndWait
+_BNKCViewLoadAllSymbolsAndWait.restype = ctypes.POINTER(BNKCSymbolRep)
+_BNKCViewLoadAllSymbolsAndWait.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ctypes.POINTER(ctypes.c_ulonglong),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewLoadAllSymbolsAndWait(
+ cache: ctypes.POINTER(BNKernelCache),
+ count: ctypes.POINTER(ctypes.c_ulonglong)
+ ) -> Optional[ctypes.POINTER(BNKCSymbolRep)]:
+ result = _BNKCViewLoadAllSymbolsAndWait(cache, count)
+ if not result:
+ return None
+ return result
+
+
+# -------------------------------------------------------
+# _BNKCViewLoadImageContainingAddress
+
+_BNKCViewLoadImageContainingAddress = core.BNKCViewLoadImageContainingAddress
+_BNKCViewLoadImageContainingAddress.restype = ctypes.c_bool
+_BNKCViewLoadImageContainingAddress.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ctypes.c_ulonglong,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewLoadImageContainingAddress(
+ cache: ctypes.POINTER(BNKernelCache),
+ address: int
+ ) -> bool:
+ return _BNKCViewLoadImageContainingAddress(cache, address)
+
+
+# -------------------------------------------------------
+# _BNKCViewLoadImageWithInstallName
+
+_BNKCViewLoadImageWithInstallName = core.BNKCViewLoadImageWithInstallName
+_BNKCViewLoadImageWithInstallName.restype = ctypes.c_bool
+_BNKCViewLoadImageWithInstallName.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ctypes.c_char_p,
+ ]
+
+
+# noinspection PyPep8Naming
+def BNKCViewLoadImageWithInstallName(
+ cache: ctypes.POINTER(BNKernelCache),
+ name: Optional[str]
+ ) -> bool:
+ return _BNKCViewLoadImageWithInstallName(cache, cstr(name))
+
+
+# -------------------------------------------------------
+# _BNNewKernelCacheReference
+
+_BNNewKernelCacheReference = core.BNNewKernelCacheReference
+_BNNewKernelCacheReference.restype = ctypes.POINTER(BNKernelCache)
+_BNNewKernelCacheReference.argtypes = [
+ ctypes.POINTER(BNKernelCache),
+ ]
+
+
+# noinspection PyPep8Naming
+def BNNewKernelCacheReference(
+ cache: ctypes.POINTER(BNKernelCache)
+ ) -> Optional[ctypes.POINTER(BNKernelCache)]:
+ result = _BNNewKernelCacheReference(cache)
+ if not result:
+ return None
+ return result
+
+
+
+# Helper functions
+def handle_of_type(value, handle_type):
+ if isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):
+ return ctypes.cast(value, ctypes.POINTER(handle_type))
+ raise ValueError('expected pointer to %s' % str(handle_type))
diff --git a/view/kernelcache/api/python/_kernelcachecore_template.py b/view/kernelcache/api/python/_kernelcachecore_template.py
new file mode 100644
index 00000000..c1f05e72
--- /dev/null
+++ b/view/kernelcache/api/python/_kernelcachecore_template.py
@@ -0,0 +1,43 @@
+import binaryninja
+import ctypes, os
+
+from typing import Optional
+from . import kernelcache_enums
+# Load core module
+import platform
+core = None
+core_platform = platform.system()
+
+# By the time the debugger is loaded, binaryninja has not fully initialized.
+# So we cannot call binaryninja.bundled_plugin_path()
+from binaryninja._binaryninjacore import BNGetBundledPluginDirectory, BNFreeString
+if core_platform == "Darwin":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libkernelcache.dylib"))
+
+elif core_platform == "Linux":
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "libkernelcache.so"))
+
+elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0):
+ _base_path = BNGetBundledPluginDirectory()
+ core = ctypes.CDLL(os.path.join(_base_path, "kernelcache.dll"))
+else:
+ raise Exception("OS not supported")
+
+def cstr(var) -> Optional[ctypes.c_char_p]:
+ if var is None:
+ return None
+ if isinstance(var, bytes):
+ return var
+ return var.encode("utf-8")
+
+def pyNativeStr(arg):
+ if isinstance(arg, str):
+ return arg
+ else:
+ return arg.decode('utf8')
+
+def free_string(value:ctypes.c_char_p) -> None:
+ BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte)))
+
diff --git a/view/kernelcache/api/python/generator.cpp b/view/kernelcache/api/python/generator.cpp
new file mode 100644
index 00000000..91ff88c0
--- /dev/null
+++ b/view/kernelcache/api/python/generator.cpp
@@ -0,0 +1,617 @@
+/*
+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.
+*/
+
+
+
+#include <stdio.h>
+#include <inttypes.h>
+#include "binaryninjaapi.h"
+
+using namespace BinaryNinja;
+using namespace std;
+
+
+map<string, string> g_pythonKeywordReplacements = {
+ {"False", "False_"},
+ {"True", "True_"},
+ {"None", "None_"},
+ {"and", "and_"},
+ {"as", "as_"},
+ {"assert", "assert_"},
+ {"async", "async_"},
+ {"await", "await_"},
+ {"break", "break_"},
+ {"class", "class_"},
+ {"continue", "continue_"},
+ {"def", "def_"},
+ {"del", "del_"},
+ {"elif", "elif_"},
+ {"else", "else_"},
+ {"except", "except_"},
+ {"finally", "finally_"},
+ {"for", "for_"},
+ {"from", "from_"},
+ {"global", "global_"},
+ {"if", "if_"},
+ {"import", "import_"},
+ {"in", "in_"},
+ {"is", "is_"},
+ {"lambda", "lambda_"},
+ {"nonlocal", "nonlocal_"},
+ {"not", "not_"},
+ {"or", "or_"},
+ {"pass", "pass_"},
+ {"raise", "raise_"},
+ {"return", "return_"},
+ {"try", "try_"},
+ {"while", "while_"},
+ {"with", "with_"},
+ {"yield", "yield_"},
+};
+
+
+void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallback = false)
+{
+ switch (type->GetClass())
+ {
+ case BoolTypeClass:
+ fprintf(out, "ctypes.c_bool");
+ break;
+ case IntegerTypeClass:
+ switch (type->GetWidth())
+ {
+ case 1:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_byte");
+ else
+ fprintf(out, "ctypes.c_ubyte");
+ break;
+ case 2:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_short");
+ else
+ fprintf(out, "ctypes.c_ushort");
+ break;
+ case 4:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_int");
+ else
+ fprintf(out, "ctypes.c_uint");
+ break;
+ default:
+ if (type->IsSigned())
+ fprintf(out, "ctypes.c_longlong");
+ else
+ fprintf(out, "ctypes.c_ulonglong");
+ break;
+ }
+ break;
+ case FloatTypeClass:
+ if (type->GetWidth() == 4)
+ fprintf(out, "ctypes.c_float");
+ else
+ fprintf(out, "ctypes.c_double");
+ break;
+ case NamedTypeReferenceClass:
+ if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass)
+ {
+ string name = type->GetNamedTypeReference()->GetName().GetString();
+ if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger")
+ name = name.substr(3);
+ else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger")
+ name = name.substr(2);
+ else if (name.size() > 15 && name.substr(0, 7) == "BNDebug")
+ name = name.substr(2);
+ else if (name.size() > 2 && name.substr(0, 2) == "BN")
+ name = name.substr(2);
+ fprintf(out, "%sEnum", name.c_str());
+ }
+ else
+ {
+ fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str());
+ }
+ break;
+ case PointerTypeClass:
+ if (isCallback || (type->GetChildType()->GetClass() == VoidTypeClass))
+ {
+ fprintf(out, "ctypes.c_void_p");
+ break;
+ }
+ else if ((type->GetChildType()->GetClass() == IntegerTypeClass) &&
+ (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned()))
+ {
+ if (isReturnType)
+ fprintf(out, "ctypes.POINTER(ctypes.c_byte)");
+ else
+ fprintf(out, "ctypes.c_char_p");
+ break;
+ }
+ else if (type->GetChildType()->GetClass() == FunctionTypeClass)
+ {
+ fprintf(out, "ctypes.CFUNCTYPE(");
+ OutputType(out, type->GetChildType()->GetChildType(), true, true);
+ for (auto& i : type->GetChildType()->GetParameters())
+ {
+ fprintf(out, ", ");
+ OutputType(out, i.type);
+ }
+ fprintf(out, ")");
+ break;
+ }
+ fprintf(out, "ctypes.POINTER(");
+ OutputType(out, type->GetChildType());
+ fprintf(out, ")");
+ break;
+ case ArrayTypeClass:
+ OutputType(out, type->GetChildType());
+ fprintf(out, " * %" PRId64, type->GetElementCount());
+ break;
+ default:
+ fprintf(out, "None");
+ break;
+ }
+}
+
+
+void OutputSwizzledType(FILE* out, Type* type)
+{
+ switch (type->GetClass())
+ {
+ case BoolTypeClass:
+ fprintf(out, "bool");
+ break;
+ case IntegerTypeClass:
+ fprintf(out, "int");
+ break;
+ case FloatTypeClass:
+ fprintf(out, "float");
+ break;
+ case NamedTypeReferenceClass:
+ if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass)
+ {
+ string name = type->GetNamedTypeReference()->GetName().GetString();
+ if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger")
+ name = name.substr(3);
+ else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger")
+ name = name.substr(2);
+ else if (name.size() > 15 && name.substr(0, 7) == "BNDebug")
+ name = name.substr(2);
+ else if (name.size() > 2 && name.substr(0, 2) == "BN")
+ name = name.substr(2);
+ fprintf(out, "%sEnum", name.c_str());
+ }
+ else
+ {
+ fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str());
+ }
+ break;
+ case PointerTypeClass:
+ if (type->GetChildType()->GetClass() == VoidTypeClass)
+ {
+ fprintf(out, "Optional[ctypes.c_void_p]");
+ break;
+ }
+ else if ((type->GetChildType()->GetClass() == IntegerTypeClass) &&
+ (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned()))
+ {
+ fprintf(out, "Optional[str]");
+ break;
+ }
+ else if (type->GetChildType()->GetClass() == FunctionTypeClass)
+ {
+ fprintf(out, "ctypes.CFUNCTYPE(");
+ OutputType(out, type->GetChildType()->GetChildType(), true, true);
+ for (auto& i : type->GetChildType()->GetParameters())
+ {
+ fprintf(out, ", ");
+ OutputType(out, i.type);
+ }
+ fprintf(out, ")");
+ break;
+ }
+ fprintf(out, "ctypes.POINTER(");
+ OutputType(out, type->GetChildType());
+ fprintf(out, ")");
+ break;
+ case ArrayTypeClass:
+ OutputType(out, type->GetChildType());
+ fprintf(out, " * %" PRId64, type->GetElementCount());
+ break;
+ default:
+ fprintf(out, "None");
+ break;
+ }
+}
+
+
+int main(int argc, char* argv[])
+{
+ if (argc < 5)
+ {
+ fprintf(stderr, "Usage: generator <header> <output> <output_template> <output_enum>\n");
+ return 1;
+ }
+
+ // Parse API header to get type and function information
+ map<QualifiedName, Ref<Type>> types, vars, funcs;
+ string errors;
+ auto arch = new CoreArchitecture(BNGetNativeTypeParserArchitecture());
+
+ // Enable ephemeral settings
+ Settings::Instance()->LoadSettingsFile("");
+ Settings::Instance()->Set("analysis.types.parserName", "ClangTypeParser");
+ bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors);
+
+ if (!ok)
+ {
+ fprintf(stderr, "Errors: %s\n", errors.c_str());
+ return 1;
+ }
+
+ FILE* out = fopen(argv[2], "w");
+ FILE* out_template = fopen(argv[3], "r");
+ FILE* enums = fopen(argv[4], "w");
+
+ fprintf(enums, "import enum\n");
+
+ // Copy the content of the template to the output file
+ int c;
+ while((c = fgetc(out_template)) != EOF)
+ fputc(c, out);
+
+ // Create type objects
+ fprintf(out, "# Type definitions\n");
+ for (auto& i : types)
+ {
+ string name;
+ if (i.first.size() != 1)
+ continue;
+ name = i.first[0];
+ if (name == "BNBinaryView")
+ {
+ fprintf(out, "from binaryninja._binaryninjacore import BNBinaryView, BNBinaryViewHandle\n");
+ continue;
+ }
+ if (i.second->GetClass() == StructureTypeClass)
+ {
+ fprintf(out, "class %s(ctypes.Structure):\n", name.c_str());
+
+ // python uses str's, C uses byte-arrays
+ bool stringField = false;
+ for (auto& arg : i.second->GetStructure()->GetMembers())
+ {
+ if ((arg.type->GetClass() == PointerTypeClass) &&
+ (arg.type->GetChildType()->GetWidth() == 1) &&
+ (arg.type->GetChildType()->IsSigned()))
+ {
+ fprintf(out, "\t@property\n\tdef %s(self):\n\t\treturn pyNativeStr(self._%s)\n", arg.name.c_str(), arg.name.c_str());
+ fprintf(out, "\t@%s.setter\n\tdef %s(self, value):\n\t\tself._%s = cstr(value)\n", arg.name.c_str(), arg.name.c_str(), arg.name.c_str());
+ stringField = true;
+ }
+ }
+
+ if (!stringField)
+ fprintf(out, "\tpass\n");
+
+ fprintf(out, "%sHandle = ctypes.POINTER(%s)\n", name.c_str(), name.c_str());
+ }
+ else if (i.second->GetClass() == EnumerationTypeClass)
+ {
+ bool isBNAPIEnum = false;
+ if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger")
+ name = name.substr(3);
+ else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger")
+ name = name.substr(2);
+ else if (name.size() > 15 && name.substr(0, 7) == "BNDebug")
+ name = name.substr(2);
+ else if (name.size() > 2 && name.substr(0, 2) == "BN")
+ {
+ name = name.substr(2);
+ isBNAPIEnum = false;
+ }
+ else
+ continue;
+
+ if (isBNAPIEnum)
+ {
+ fprintf(out, "from binaryninja._binaryninjacore import %sEnum\n", name.c_str());
+ continue;
+ }
+
+ fprintf(out, "%sEnum = ctypes.c_int\n", name.c_str());
+
+ fprintf(enums, "\n\nclass %s(enum.IntEnum):\n", name.c_str());
+ for (auto& j : i.second->GetEnumeration()->GetMembers())
+ {
+ fprintf(enums, "\t%s = %" PRId64 "\n", j.name.c_str(), j.value);
+ }
+ }
+ else if ((i.second->GetClass() == BoolTypeClass) || (i.second->GetClass() == IntegerTypeClass) ||
+ (i.second->GetClass() == FloatTypeClass) || (i.second->GetClass() == ArrayTypeClass))
+ {
+ fprintf(out, "%s = ", name.c_str());
+ OutputType(out, i.second);
+ fprintf(out, "\n");
+ }
+ }
+
+
+ fprintf(out, "\n# Structure definitions\n");
+ set<QualifiedName> structsToProcess;
+ set<QualifiedName> finishedStructs;
+ for (auto& i : types)
+ structsToProcess.insert(i.first);
+ while (structsToProcess.size() != 0)
+ {
+ set<QualifiedName> currentStructList = structsToProcess;
+ structsToProcess.clear();
+ bool processedSome = false;
+ for (auto& i : currentStructList)
+ {
+ string name;
+ if (i.size() != 1)
+ continue;
+ Ref<Type> type = types[i];
+ name = i[0];
+ if ((type->GetClass() == StructureTypeClass) && (type->GetStructure()->GetMembers().size() != 0))
+ {
+ bool requiresDependency = false;
+ for (auto& j : type->GetStructure()->GetMembers())
+ {
+ if ((j.type->GetClass() == NamedTypeReferenceClass) &&
+ (types[j.type->GetNamedTypeReference()->GetName()]->GetClass() == StructureTypeClass) &&
+ (finishedStructs.count(j.type->GetNamedTypeReference()->GetName()) == 0))
+ {
+ // This structure needs another structure that isn't fully defined yet, need to wait
+ // for the dependencies to be defined
+ structsToProcess.insert(i);
+ requiresDependency = true;
+ break;
+ }
+ }
+
+ if (requiresDependency)
+ continue;
+ fprintf(out, "%s._fields_ = [\n", name.c_str());
+ for (auto& j : type->GetStructure()->GetMembers())
+ {
+ // To help the python->C wrappers
+ if ((j.type->GetClass() == PointerTypeClass) &&
+ (j.type->GetChildType()->GetWidth() == 1) &&
+ (j.type->GetChildType()->IsSigned()))
+ {
+ fprintf(out, "\t\t(\"_%s\", ", j.name.c_str());
+ }
+ else
+ fprintf(out, "\t\t(\"%s\", ", j.name.c_str());
+ OutputType(out, j.type);
+ fprintf(out, "),\n");
+ }
+ fprintf(out, "\t]\n");
+ finishedStructs.insert(i);
+ processedSome = true;
+ }
+ else if (type->GetClass() == NamedTypeReferenceClass)
+ {
+ if (type->GetNamedTypeReference()->GetTypeReferenceClass() == StructNamedTypeClass)
+ {
+ fprintf(out, "%s = %s\n", name.c_str(), type->GetNamedTypeReference()->GetName().GetString().c_str());
+ fprintf(out, "%sHandle = %sHandle\n", name.c_str(), type->GetNamedTypeReference()->GetName().GetString().c_str());
+ }
+ else if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass)
+ {
+ fprintf(out, "%s = ctypes.c_int\n", name.c_str());
+ }
+ finishedStructs.insert(i);
+ processedSome = true;
+ }
+ }
+
+ if (!processedSome && structsToProcess.size() != 0)
+ {
+ fprintf(stderr, "Detected dependency cycle in structures\n");
+ for (auto& i : structsToProcess)
+ fprintf(stderr, "%s\n", i.GetString().c_str());
+ return 1;
+ }
+ }
+
+ fprintf(out, "\n# Function definitions\n");
+ for (auto& i : funcs)
+ {
+ string name;
+ if (i.first.size() != 1)
+ continue;
+ name = i.first[0];
+
+ // Check for a string result, these will be automatically wrapped to free the string
+ // memory and return a Python string
+ bool stringResult = (i.second->GetChildType()->GetClass() == PointerTypeClass) &&
+ (i.second->GetChildType()->GetChildType()->GetWidth() == 1) &&
+ (i.second->GetChildType()->GetChildType()->IsSigned());
+ // Pointer returns will be automatically wrapped to return None on null pointer
+ bool pointerResult = (i.second->GetChildType()->GetClass() == PointerTypeClass);
+
+ // From python -> C python3 requires str -> str.encode('charmap')
+ bool swizzleArgs = true;
+ if (name == "BNFreeString")
+ swizzleArgs = false;
+
+ bool callbackConvention = false;
+ if (name == "BNAllocString")
+ {
+ // Don't perform automatic wrapping of string allocation, and return a void
+ // pointer so that callback functions (which is the only valid use of BNDebuggerAllocString)
+ // can properly return the result
+ stringResult = false;
+ callbackConvention = true;
+ swizzleArgs = false;
+ }
+
+ string funcName = string("_") + name;
+
+ fprintf(out, "# -------------------------------------------------------\n");
+ fprintf(out, "# %s\n\n", funcName.c_str());
+ fprintf(out, "%s = core.%s\n", funcName.c_str(), name.c_str());
+ fprintf(out, "%s.restype = ", funcName.c_str());
+ OutputType(out, i.second->GetChildType(), true, callbackConvention);
+ fprintf(out, "\n");
+ if (!i.second->HasVariableArguments())
+ {
+ fprintf(out, "%s.argtypes = [\n", funcName.c_str());
+ for (auto& j : i.second->GetParameters())
+ {
+ fprintf(out, "\t\t");
+ if (name == "BNFreeString")
+ {
+ // BNDebuggerFreeString expects a pointer to a string allocated by the core, so do not use
+ // a c_char_p here, as that would be allocated by the Python runtime. This can
+ // be enforced by outputting like a return value.
+ OutputType(out, j.type, true);
+ }
+ else
+ {
+ OutputType(out, j.type);
+ }
+ fprintf(out, ",\n");
+ }
+ fprintf(out, "\t]");
+ }
+ else
+ {
+ // As of writing this, only BNLog's have variable instruction lengths, but in an attempt not to break in the future:
+ if (funcName.compare(0, 6, "_BNLog") == 0)
+ {
+ if (funcName != "_BNLog")
+ {
+ fprintf(out, "def %s(*args):\n", name.c_str());
+ fprintf(out, "\treturn %s(*[cstr(arg) for arg in args])\n\n", funcName.c_str());
+ continue;
+ }
+ else
+ {
+ fprintf(out, "def %s(level, *args):\n", name.c_str());
+ fprintf(out, "\treturn %s(level, *[cstr(arg) for arg in args])\n\n", funcName.c_str());
+ continue;
+ }
+ }
+ }
+ fprintf(out, "\n\n\n# noinspection PyPep8Naming\n");
+ fprintf(out, "def %s(", name.c_str());
+ if (!i.second->HasVariableArguments())
+ {
+ size_t argN = 0;
+ for (auto& arg: i.second->GetParameters())
+ {
+ string argName = arg.name;
+ if (g_pythonKeywordReplacements.find(argName) != g_pythonKeywordReplacements.end())
+ argName = g_pythonKeywordReplacements[argName];
+
+ if (argName.empty())
+ argName = "arg" + to_string(argN);
+
+ if (argN > 0)
+ fprintf(out, ", ");
+ fprintf(out, "\n\t\t");
+ fprintf(out, "%s: ", argName.c_str());
+ if (swizzleArgs)
+ OutputSwizzledType(out, arg.type);
+ else
+ OutputType(out, arg.type);
+ argN ++;
+ }
+ }
+ fprintf(out, "\n\t\t) -> ");
+ if (swizzleArgs)
+ {
+ if (stringResult || pointerResult)
+ fprintf(out, "Optional[");
+ OutputSwizzledType(out, i.second->GetChildType());
+ if (stringResult || pointerResult)
+ fprintf(out, "]");
+ }
+ else
+ {
+ OutputType(out, i.second->GetChildType());
+ }
+ fprintf(out, ":\n");
+
+ string stringArgFuncCall = funcName + "(";
+ size_t argN = 0;
+ for (auto& arg : i.second->GetParameters())
+ {
+ string argName = arg.name;
+ if (g_pythonKeywordReplacements.find(argName) != g_pythonKeywordReplacements.end())
+ argName = g_pythonKeywordReplacements[argName];
+
+ if (argName.empty())
+ argName = "arg" + to_string(argN);
+
+ if (swizzleArgs && (arg.type->GetClass() == PointerTypeClass) &&
+ (arg.type->GetChildType()->GetClass() == IntegerTypeClass) &&
+ (arg.type->GetChildType()->GetWidth() == 1) &&
+ (arg.type->GetChildType()->IsSigned()))
+ {
+ stringArgFuncCall += string("cstr(") + argName + "), ";
+ }
+ else
+ {
+ stringArgFuncCall += argName + ", ";
+ }
+ argN++;
+ }
+ if (argN > 0)
+ stringArgFuncCall = stringArgFuncCall.substr(0, stringArgFuncCall.size()-2);
+ stringArgFuncCall += ")";
+
+ if (stringResult)
+ {
+ // Emit wrapper to get Python string and free native memory
+ fprintf(out, "\tresult = ");
+ fprintf(out, "%s\n", stringArgFuncCall.c_str());
+ fprintf(out, "\tif not result:\n");
+ fprintf(out, "\t\treturn None\n");
+ fprintf(out, "\tstring = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))\n");
+ fprintf(out, "\tBNFreeString(result)\n");
+ fprintf(out, "\treturn string\n");
+ }
+ else if (pointerResult)
+ {
+ // Emit wrapper to return None on null pointer
+ fprintf(out, "\tresult = ");
+ fprintf(out, "%s\n", stringArgFuncCall.c_str());
+ fprintf(out, "\tif not result:\n");
+ fprintf(out, "\t\treturn None\n");
+ fprintf(out, "\treturn result\n");
+ }
+ else
+ {
+ fprintf(out, "\treturn ");
+ fprintf(out, "%s\n", stringArgFuncCall.c_str());
+ }
+ fprintf(out, "\n\n");
+ }
+
+ fprintf(out, "\n# Helper functions\n");
+ fprintf(out, "def handle_of_type(value, handle_type):\n");
+ fprintf(out, "\tif isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):\n");
+ fprintf(out, "\t\treturn ctypes.cast(value, ctypes.POINTER(handle_type))\n");
+ fprintf(out, "\traise ValueError('expected pointer to %%s' %% str(handle_type))\n");
+
+ fclose(out);
+ fclose(enums);
+ return 0;
+}
diff --git a/view/kernelcache/api/python/kernelcache.py b/view/kernelcache/api/python/kernelcache.py
new file mode 100644
index 00000000..2148f2a8
--- /dev/null
+++ b/view/kernelcache/api/python/kernelcache.py
@@ -0,0 +1,238 @@
+import os
+import ctypes
+import dataclasses
+import traceback
+
+import binaryninja
+from binaryninja._binaryninjacore import BNFreeStringList, BNAllocString, BNFreeString
+
+from . import _kernelcachecore as kccore
+from .kernelcache_enums import *
+
+
+@dataclasses.dataclass
+class KCImageMemoryMapping:
+ name: str
+ vmAddress: int
+ rawViewOffset: int
+ size: 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}>>")
+
+
+@dataclasses.dataclass
+class KCImage:
+ name: str
+ headerFileAddress: int
+ mappings: list[KCImageMemoryMapping]
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<KCImage {self.name} @ {self.headerFileAddress:x}>"
+
+
+@dataclasses.dataclass
+class KCSymbol:
+ name: str
+ image: str
+ address: int
+
+ def __str__(self):
+ return repr(self)
+
+ def __repr__(self):
+ return f"<KCSymbol {self.name} @ {self.address:x} ({self.image})>"
+
+
+@dataclasses.dataclass
+class KernelCacheMachOHeader:
+ header: str
+
+ @classmethod
+ def LoadFromString(cls, s: str):
+ return cls(header=s)
+
+ def __repr__(self):
+ return f"<KernelCacheMachOHeader {self.header!r}>"
+
+
+class KernelCache:
+ def __init__(self, view):
+ # Create a KernelCache object from a BinaryView.
+ self.handle = kccore.BNGetKernelCache(view.handle)
+
+ @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))
+
+ @staticmethod
+ def fast_get_image_count(view) -> int:
+ """
+ Quickly returns the number of images in the kernel cache.
+ """
+ return kccore.BNKCViewFastGetImageCount(view.handle)
+
+ 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 load_image_containing_address(self, addr: int) -> bool:
+ """
+ Load the image that contains the given address.
+ """
+ return kccore.BNKCViewLoadImageContainingAddress(self.handle, addr)
+
+ @property
+ def image_names(self) -> list[str]:
+ """
+ Return a list of available kernel cache image install names.
+ """
+ count = ctypes.c_ulonglong()
+ value = kccore.BNKCViewGetInstallNames(self.handle, count)
+ if value is None:
+ return []
+ result = []
+ for i in range(count.value):
+ result.append(value[i].decode('utf-8'))
+ BNFreeStringList(value, count)
+ return result
+
+ @property
+ def images(self) -> list[KCImage]:
+ """
+ Return all kernel cache images.
+ """
+ count = ctypes.c_ulonglong()
+ value = kccore.BNKCViewGetAllImages(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)
+ return result
+
+ @property
+ def loaded_images(self) -> list[KCImage]:
+ """
+ Return the kernel cache images that are currently loaded.
+ """
+ count = ctypes.c_ulonglong()
+ images = kccore.BNKCViewGetLoadedImages(self.handle, count)
+ if images 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)
+ return result
+
+ def load_all_symbols_and_wait(self) -> list[KCSymbol]:
+ """
+ Load all symbols from the kernel cache and wait for completion.
+ """
+ count = ctypes.c_ulonglong()
+ value = kccore.BNKCViewLoadAllSymbolsAndWait(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)
+ 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_macho_header_for_image(self, name: str):
+ """
+ Return a KernelCacheMachOHeader for the image with the given install name.
+ """
+ s = BNAllocString(name)
+ outputStr = kccore.BNKCViewGetImageHeaderForName(self.handle, s)
+ if outputStr is None:
+ return None
+ output = outputStr
+ BNFreeString(outputStr)
+ if output == "":
+ return None
+ return KernelCacheMachOHeader.LoadFromString(output)
+
+ 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
+ output = outputStr
+ BNFreeString(outputStr)
+ if output == "":
+ return None
+ return KernelCacheMachOHeader.LoadFromString(output)
+
+ @property
+ def state(self):
+ """
+ Return the current state of the kernel cache.
+ """
+ return KCViewState(kccore.BNKCViewGetState(self.handle))
diff --git a/view/kernelcache/api/python/kernelcache_enums.py b/view/kernelcache/api/python/kernelcache_enums.py
new file mode 100644
index 00000000..e610c527
--- /dev/null
+++ b/view/kernelcache/api/python/kernelcache_enums.py
@@ -0,0 +1,14 @@
+import enum
+
+
+class KCViewLoadProgress(enum.IntEnum):
+ LoadProgressNotStarted = 0
+ LoadProgressLoadingCaches = 1
+ LoadProgressLoadingImages = 2
+ LoadProgressFinished = 3
+
+
+class KCViewState(enum.IntEnum):
+ Unloaded = 0
+ Loaded = 1
+ LoadedWithImages = 2