summaryrefslogtreecommitdiff
path: root/view/kernelcache/api/python/_kernelcachecore.py
diff options
context:
space:
mode:
authorkat <kat@vector35.com>2025-03-19 09:12:52 -0400
committerkat <kat@vector35.com>2025-03-19 15:04:23 -0400
commit7f3f394c8bb50c987a5237bc74aa503486571058 (patch)
tree9f5ada34f6226562b79f0dce70bfebab124a1bdb /view/kernelcache/api/python/_kernelcachecore.py
parente6d4d38e2a5dc66d3c8004cc917bc08e39337482 (diff)
Add iOS/macOS MH_FILESET KernelCache View and loader.
This loader is inspired by/based on our dyld_shared_cache loader, following the same design language. It targets primarily the latest kernels, but should support any with the MH_FILESET format. It allows you to decide which images you would like to map in (the kernel itself included), resulting in targeted analysis when you may only need to load a singular image. It also supports dropping in compressed KernelCaches, directly from the ipsw. This is an early solution and we have many more changes and improvements planned. We look forward to your feedback
Diffstat (limited to 'view/kernelcache/api/python/_kernelcachecore.py')
-rw-r--r--view/kernelcache/api/python/_kernelcachecore.py515
1 files changed, 515 insertions, 0 deletions
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))