diff options
Diffstat (limited to 'view/kernelcache/api/python/kernelcache.py')
| -rw-r--r-- | view/kernelcache/api/python/kernelcache.py | 238 |
1 files changed, 238 insertions, 0 deletions
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)) |
