diff options
| author | Mason Reed <mason@vector35.com> | 2025-03-10 11:05:40 -0400 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2025-04-02 05:36:54 -0400 |
| commit | 25cc02431b61097b2adfc2fbc493b648b0300c3b (patch) | |
| tree | a79d9c4f4f67234d3bf9bda413e8608f479a4cc8 /view/sharedcache/api | |
| parent | fa85bf28502286c4821427c5d0ed91a7ed46f8f6 (diff) | |
[SharedCache] Refactor Shared Cache
In absence of a better name, this commit refactors the shared cache code.
Diffstat (limited to 'view/sharedcache/api')
| -rw-r--r-- | view/sharedcache/api/python/sharedcache.py | 580 | ||||
| -rw-r--r-- | view/sharedcache/api/python/sharedcache_enums.py | 44 | ||||
| -rw-r--r-- | view/sharedcache/api/sharedcache.cpp | 485 | ||||
| -rw-r--r-- | view/sharedcache/api/sharedcacheapi.h | 490 | ||||
| -rw-r--r-- | view/sharedcache/api/sharedcachecore.h | 202 |
5 files changed, 927 insertions, 874 deletions
diff --git a/view/sharedcache/api/python/sharedcache.py b/view/sharedcache/api/python/sharedcache.py index dab5583e..83be388b 100644 --- a/view/sharedcache/api/python/sharedcache.py +++ b/view/sharedcache/api/python/sharedcache.py @@ -1,389 +1,293 @@ -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 _sharedcachecore as sccore from .sharedcache_enums import * - -@dataclasses.dataclass -class DSCMemoryMapping: - name: str - vmAddress: int - size: int - - def __str__(self): - return repr(self) - - def __repr__(self): - return f"<DSCMemoryMapping '{self.name}': {self.vmAddress:x}+{self.size:x}>" - - @dataclasses.dataclass -class LoadedRegion: - name: str - headerAddress: int - mappings: list[DSCMemoryMapping] - - def __str__(self): - return repr(self) - - def __repr__(self): - return f"<LoadedRegion {self.name} @ {self.headerAddress:x}>" - - -@dataclasses.dataclass -class DSCBackingCacheMapping: - vmAddress: int - size: int - fileOffset: int - - def __str__(self): - return repr(self) +class CacheRegion: + region_type: SharedCacheRegionType + name: str + start: int + size: int + image_start: int + # TODO: Might want to make this use the BN segment flag enum? + flags: sccore.SegmentFlagEnum - def __repr__(self): - return f"<DSCBackingCacheMapping {self.vmAddress:x}+{self.size:x} @ {self.fileOffset:x}" + def __str__(self): + return repr(self) + def __repr__(self): + return f"<CacheRegion '{self.name}': 0x{self.start:x} + {self.size:x}>" @dataclasses.dataclass -class DSCBackingCache: - path: str - cacheType: BackingCacheType - mappings: list[DSCBackingCacheMapping] - - def __str__(self): - return repr(self) - - def __repr__(self): - cache_type_str = 'Unknown' - if self.cacheType == BackingCacheType.BackingCacheTypePrimary: - cache_type_str = 'Primary' - elif self.cacheType == BackingCacheType.BackingCacheTypeSecondary: - cache_type_str = 'Secondary' - elif self.cacheType == BackingCacheType.BackingCacheTypeSymbols: - cache_type_str = 'Symbols' - return f"<DSCBackingCache {self.path} {cache_type_str} | {len(self.mappings)} mappings>" - - -@dataclasses.dataclass -class DSCImageMemoryMapping: - filePath: str - name: str - vmAddress: int - size: int - loaded: bool - rawViewOffset: int - - def __str__(self): - return repr(self) - - def __repr__(self): - return f"<DSCImageMemoryMapping '{self.name}' {os.path.basename(self.filePath)} raw<{self.rawViewOffset:x}>: {self.vmAddress:x}+{self.size:x}>" - - -@dataclasses.dataclass -class DSCImage: - name: str - headerAddress: int - mappings: list[DSCImageMemoryMapping] - - def __str__(self): - return repr(self) +class CacheImage: + name: str + header_address: int + region_starts: [int] - def __repr__(self): - return f"<DSCImage {self.name} @ {self.headerAddress:x}>" + def __str__(self): + return repr(self) + def __repr__(self): + return f"<CacheImage '{self.name}': 0x{self.header_address:x}>" @dataclasses.dataclass -class DSCSymbol: - name: str - image: str - address: int - - def __str__(self): - return repr(self) - - def __repr__(self): - return f"<DSCSymbol {self.name} @ {self.address:x} ({self.image}>" - - -class SharedCache: - """ - SharedCache is the primary class for interacting with the shared cache processor and DSCView metadata. - - You can create a SharedCache object from a BinaryView object by calling `SharedCache(bv)`, where `bv` is the BinaryView. - - By default `bv` in the console will return the instance of the BinaryView that is currently open, \ - so in the UI, you can use `dsc = SharedCache(bv)` to create a SharedCache object in the scripting console. - - Methods and attributes in this class have documentation which can be viewed by typing `SharedCache.method_or_attribute_name?` in the console. - """ - def __init__(self, view): - self.handle = sccore.BNGetSharedCache(view.handle) - - def load_image_with_install_name(self, install_name, skip_loading_objective_c = False) -> bool: - """ - Locate an image with the provided install name and load it into the shared cache view - - :param install_name: Install name of the image - :param skip_loading_objective_c: Whether to skip process Objective-C information for this image. Default false. - :return: - """ - return sccore.BNDSCViewLoadImageWithInstallName(self.handle, install_name, skip_loading_objective_c) - - def load_section_at_address(self, addr) -> bool: - """ - Load a singular section at the provided address into the shared cache view. - - This will partial-load the image, only mapping the requested segment containing this section. - - Image info will still be processed, but will only be applied to mapped regions. - - :param addr: Address within the section - :return: - """ - return sccore.BNDSCViewLoadSectionAtAddress(self.handle, addr) - - def load_image_containing_address(self, addr, skip_loading_objective_c = False) -> bool: - """ - Load the image containing the provided address into the shared cache view. - - :param addr: Address within the image to load - :param skip_loading_objective_c: Whether to skip processing Objective-C information for this image. Default false. - :return: - """ - return sccore.BNDSCViewLoadImageContainingAddress(self.handle, addr, skip_loading_objective_c) - - def process_objc_sections_for_image_with_install_name(self, install_name) -> bool: - """ - Process Objective-C information for the image with the provided install name. - - :param install_name: Install name of the image - :return: - """ - return sccore.BNDSCViewProcessObjCSectionsForImageWithInstallName(self.handle, install_name, False) - - def process_all_objc_sections(self) -> bool: - """ - Process Objective-C information for all images in the shared cache view. - - :return: - """ - return sccore.BNDSCViewProcessAllObjCSections(self.handle) - - @property - def caches(self) -> list[DSCBackingCache]: - """ - Get all backing caches in the shared cache. - :return: - """ - count = ctypes.c_ulonglong() - value = sccore.BNDSCViewGetBackingCaches(self.handle, count) - if value is None: - return [] - - result = [] - for i in range(count.value): - mappings = [] - for j in range(value[i].mappingCount): - mapping = DSCBackingCacheMapping( - value[i].mappings[j].vmAddress, - value[i].mappings[j].size, - value[i].mappings[j].fileOffset - ) - mappings.append(mapping) - result.append(DSCBackingCache( - value[i].path, - value[i].cacheType, - mappings - )) - - sccore.BNDSCViewFreeBackingCaches(value, count) - return result - - @property - def images(self) -> list[DSCImage]: - """ - Get all images in the shared cache - :return: - """ - count = ctypes.c_ulonglong() - value = sccore.BNDSCViewGetAllImages(self.handle, count) - if value is None: - return [] +class CacheSymbol: + symbol_type: sccore.SymbolTypeEnum + address: int + name: str - result = [] - for i in range(count.value): - mappings = [] - for j in range(value[i].mappingCount): - mapping = DSCImageMemoryMapping( - value[i].mappings[j].filePath, - value[i].mappings[j].name, - value[i].mappings[j].vmAddress, - value[i].mappings[j].size, - value[i].mappings[j].loaded, - value[i].mappings[j].rawViewOffset - ) - mappings.append(mapping) - result.append(DSCImage( - value[i].name, - value[i].headerAddress, - mappings - )) + def __str__(self): + return repr(self) - sccore.BNDSCViewFreeAllImages(value, count) - return result + def __repr__(self): + return f"<CacheSymbol '{self.name}': 0x{self.address:x}>" - @property - def loaded_regions(self) -> list[LoadedRegion]: - """ - Get all loaded regions in the shared cache +def region_from_api(region: sccore.BNSharedCacheRegion) -> CacheRegion: + return CacheRegion( + region_type=SharedCacheRegionType(region.regionType), + name=region.name, + start=region.vmAddress, + size=region.size, + image_start=region.imageStart, + flags=region.flags + ) - The internal logic for loading images treats a region as 'loaded' whenever - that region has been mapped into memory, and, if it's located within an image, header information has been applied to that region. +def region_to_api(region: CacheRegion) -> sccore.BNSharedCacheRegion: + return sccore.BNSharedCacheRegion( + regionType=region.region_type, + _name=BNAllocString(region.name), + vmAddress=region.start, + size=region.size, + imageStart=region.image_start, + flags=region.flags + ) - Individual segments within an image can be loaded independently of the image itself. +def image_from_api(image: sccore.BNSharedCacheImage) -> CacheImage: + region_starts = [] + for i in range(image.regionStartCount): + region_starts.append(image.regionStarts[i]) + return CacheImage( + name=image.name, + header_address=image.headerAddress, + region_starts=region_starts + ) - Only once all regions of an image are loaded will the header processor refuse to run on that region. - :return: - """ - count = ctypes.c_ulonglong() - value = sccore.BNDSCViewGetLoadedRegions(self.handle, count) - if value is None: - return [] +def image_to_api(image: CacheImage) -> sccore.BNSharedCacheImage: + region_start_array = (ctypes.c_ulonglong * len(image.region_starts))() + for i, region_start in enumerate(image.region_starts): + region_start_array[i] = region_start + core_region_starts = sccore.BNSharedCacheAllocRegionList(region_start_array, len(region_start_array)) + return sccore.BNSharedCacheImage( + _name=BNAllocString(image.name), + headerAddress=image.header_address, + regionStartCount=len(region_start_array), + regionStarts=core_region_starts + ) - result = [] - for i in range(count.value): - mapping = DSCMemoryMapping( - value[i].name, - value[i].vmAddress, - value[i].size, - ) - result.append(mapping) - sccore.BNDSCViewFreeLoadedRegions(value, count) - return result +def symbol_from_api(symbol: sccore.BNSharedCacheSymbol) -> CacheSymbol: + return CacheSymbol( + symbol_type=symbol.symbolType, + address=symbol.address, + name=symbol.name + ) - def load_all_symbols_and_wait(self) -> list[DSCSymbol]: - """ - Load all symbols in the shared cache. This will block on the current thread waiting for processing to finish. +def symbol_to_api(symbol: CacheSymbol) -> sccore.BNSharedCacheSymbol: + return sccore.BNSharedCacheSymbol( + symbolType=symbol.symbol_type, + address=symbol.address, + _name=BNAllocString(symbol.name) + ) - While all functions in this API are synchronous, this function can be particularly slow due to the large number - of symbols in the shared cache. "and_wait" is appended to the function name to indicate that this function - will block until processing is complete, and for performant applications, you should consider calling this - function in a separate thread and waiting on its return. An example of this is provided in the shared cache - triage view. +class SharedCacheController: + 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 = sccore.BNGetSharedCacheController(view.handle) - This may take several seconds if this is the first time this function is called. Subsequent calls will be faster. + def __del__(self): + if self.handle is not None: + sccore.BNFreeSharedCacheControllerReference(self.handle) - In UI-based API usage, it is likely that the triage view will have already performed this operation, and calls - to this function will be much faster. + def __str__(self): + return repr(self) - :return: A list of all symbols in the shared cache - """ - count = ctypes.c_ulonglong() - value = sccore.BNDSCViewLoadAllSymbolsAndWait(self.handle, count) - if value is None: - return [] - result = [] - for i in range(count.value): - sym = DSCSymbol( - value[i].name, - value[i].image, - value[i].address - ) - result.append(sym) + def __repr__(self): + return f"<SharedCacheController: {len(self.images)} images, {len(self.regions)} regions>" - sccore.BNDSCViewFreeSymbols(value, count) - return result + def is_valid(self) -> bool: + return self.handle is not None - @property - def image_names(self) -> list[str]: - """ - Get all image names in the shared cache - :return: - """ - count = ctypes.c_ulonglong() - value = sccore.BNDSCViewGetInstallNames(self.handle, count) - if value is None: - return [] + def apply_region(self, view: BinaryView, region: CacheRegion) -> bool: + api_region: sccore.BNSharedCacheRegion = region_to_api(region) + result = sccore.BNSharedCacheControllerApplyRegion(self.handle, view.handle, api_region) + sccore.BNSharedCacheFreeRegion(api_region) + return result - result = [] - for i in range(count.value): - result.append(value[i].decode('utf-8')) + def apply_image(self, view: BinaryView, image: CacheImage) -> bool: + api_image: sccore.BNSharedCacheImage = image_to_api(image) + result = sccore.BNSharedCacheControllerApplyImage(self.handle, view.handle, api_image) + sccore.BNSharedCacheFreeImage(api_image) + return result - BNFreeStringList(value, count) - return result + def is_region_loaded(self, region: CacheRegion) -> bool: + api_region: sccore.BNSharedCacheRegion = region_to_api(region) + result = sccore.BNSharedCacheControllerIsRegionLoaded(self.handle, api_region) + sccore.BNSharedCacheFreeRegion(api_region) + return result - @property - def state(self) -> DSCViewState: - """ - Get the current image state of the shared cache view. Useful for checking if images have been loaded yet or not. - :return: - """ - return DSCViewState(sccore.BNDSCViewGetState(self.handle)) + def is_image_loaded(self, image: CacheImage) -> bool: + api_image: sccore.BNSharedCacheImage = image_to_api(image) + result = sccore.BNSharedCacheControllerIsImageLoaded(self.handle, api_image) + sccore.BNSharedCacheFreeImage(api_image) + return result - def get_name_for_address(self, address) -> str: - """ - Get the "name" for the provided address. Specifically, the name of the memory region this address lies in. + def get_region_at(self, address: int) -> Optional[CacheRegion]: + api_region = sccore.BNSharedCacheRegion() + if not sccore.BNSharedCacheControllerGetRegionAt(self.handle, address, api_region): + return None + region = region_from_api(api_region) + sccore.BNSharedCacheFreeRegion(api_region) + return region - If this lies within an image segment, this will be in the format image_name + "::" + segment_name. + def get_region_containing(self, address: int) -> Optional[CacheRegion]: + api_region = sccore.BNSharedCacheRegion() + if not sccore.BNSharedCacheControllerGetRegionContaining(self.handle, address, api_region): + return None + region = region_from_api(api_region) + sccore.BNSharedCacheFreeRegion(api_region) + return region - It may also be the name of a branch pool or other non-image region. + def get_image_at(self, address: int) -> Optional[CacheImage]: + api_image = sccore.BNSharedCacheImage() + if not sccore.BNSharedCacheControllerGetImageAt(self.handle, address, api_image): + return None + image = image_from_api(api_image) + sccore.BNSharedCacheFreeImage(api_image) + return image - This is the API call utilized on the first dynamic entry in the right-click context menu. + def get_image_containing(self, address: int) -> Optional[CacheImage]: + api_image = sccore.BNSharedCacheImage() + if not sccore.BNSharedCacheControllerGetImageContaining(self.handle, address, api_image): + return None + image = image_from_api(api_image) + sccore.BNSharedCacheFreeImage(api_image) + return image - :param address: address to check - :return: - """ - name = sccore.BNDSCViewGetNameForAddress(self.handle, address) - if name is None: - return "" - result = name - return result + def get_image_with_name(self, name: str) -> Optional[CacheImage]: + api_image = sccore.BNSharedCacheImage() + if not sccore.BNSharedCacheControllerGetImageWithName(self.handle, name, api_image): + return None + image = image_from_api(api_image) + sccore.BNSharedCacheFreeImage(api_image) + return image - def get_image_name_for_address(self, address) -> str: - """ - Return the install name for the image containing the provided address. + def get_image_dependencies(self, image: CacheImage) -> [str]: + """ + Returns a list of image names that this image depends on. + """ + count = ctypes.c_ulonglong() + api_image: sccore.BNSharedCacheImage = image_to_api(image) + value = sccore.BNSharedCacheControllerGetImageDependencies(self.handle, api_image, count) + sccore.BNSharedCacheFreeImage(api_image) + if value is None: + return [] + result = [] + for i in range(count.value): + result.append(value[i].decode("utf-8")) + BNFreeStringList(value, count) + return result - If the address is not within an image, this will return an empty string. + @property + def regions(self) -> [CacheRegion]: + count = ctypes.c_ulonglong() + value = sccore.BNSharedCacheControllerGetRegions(self.handle, count) + if value is None: + return [] + result = [] + for i in range(count.value): + result.append(region_from_api(value[i])) + sccore.BNSharedCacheFreeRegionList(value, count) + return result - This is the API call used in the second dynamic entry in the right-click context menu. + @property + def loaded_regions(self) -> [CacheRegion]: + """ + Get a list of regions that are currently loaded in the view. + """ + count = ctypes.c_ulonglong() + value = sccore.BNSharedCacheControllerGetLoadedRegions(self.handle, count) + if value is None: + return [] + result = [] + for i in range(count.value): + result.append(region_from_api(value[i])) + sccore.BNSharedCacheFreeRegionList(value, count) + return result - :param address: address to check - :return: - """ - name = sccore.BNDSCViewGetImageNameForAddress(self.handle, address) - if name is None: - return "" - result = name - return result + @property + def images(self) -> [CacheImage]: + count = ctypes.c_ulonglong() + value = sccore.BNSharedCacheControllerGetImages(self.handle, count) + if value is None: + return [] + result = [] + for i in range(count.value): + result.append(image_from_api(value[i])) + sccore.BNSharedCacheFreeImageList(value, count) + return result - def find_symbol_at_addr_and_apply_to_addr(self, symbol_address, target_address, trigger_reanalysis) -> None: - """ - This is primarily a function utilized for automated backwards symbol propagation for stubs in the workflow, however - it is passed through here as well in the event you need to use it to do something similar, or want to create - your own version of the workflow. + @property + def loaded_images(self) -> [CacheImage]: + """ + Get a list of images that are currently loaded in the view. + """ + count = ctypes.c_ulonglong() + value = sccore.BNSharedCacheControllerGetLoadedImages(self.handle, count) + if value is None: + return [] + result = [] + for i in range(count.value): + result.append(image_from_api(value[i])) + sccore.BNSharedCacheFreeImageList(value, count) + return result - This is currently a blocking function. + @property + def symbols(self) -> [CacheSymbol]: + count = ctypes.c_ulonglong() + value = sccore.BNSharedCacheControllerGetSymbols(self.handle, count) + if value is None: + return [] + result = [] + for i in range(count.value): + result.append(symbol_from_api(value[i])) + sccore.BNSharedCacheFreeSymbolList(value, count) + return result - This will check the cache for a symbol located at symbol_address, and apply it to target_address, appending a - `j_` to the front of the symbol name copy at `target_address`. - It will additionally backwards-propagate type information that was specifically applied via TypeLibrary to the stub. +def _get_shared_cache(instance: binaryninja.PythonScriptingInstance): + if instance.interpreter.active_view is None: + return None + controller = SharedCacheController(instance.interpreter.active_view) + if not controller.is_valid(): + return None + return controller - This includes calling conventions and can be seen in stubs pointing to objc_release_x[register] functions. - This check will not run if: - - The symbol address and target address are the same - - The target address has already been given a name by this function +binaryninja.PythonScriptingProvider.register_magic_variable( + "dsc", + _get_shared_cache +) - :param symbol_address: Symbol address to check - :param target_address: Target address to apply symbol name and type to - :param trigger_reanalysis: Whether to reanalyze the function at target_address if the function already existed. - :return: None - """ - sccore.BNDSCFindSymbolAtAddressAndApplyToAddress(self.handle, symbol_address, target_address, trigger_reanalysis) +binaryninja.PythonScriptingProvider.register_magic_variable( + "shared_cache", + _get_shared_cache +) diff --git a/view/sharedcache/api/python/sharedcache_enums.py b/view/sharedcache/api/python/sharedcache_enums.py index ea86b5c6..9a98eba4 100644 --- a/view/sharedcache/api/python/sharedcache_enums.py +++ b/view/sharedcache/api/python/sharedcache_enums.py @@ -1,20 +1,38 @@ import enum -class BackingCacheType(enum.IntEnum): - BackingCacheTypePrimary = 0 - BackingCacheTypeSecondary = 1 - BackingCacheTypeSymbols = 2 +class SegmentFlag(enum.IntEnum): + SegmentExecutable = 1 + SegmentWritable = 2 + SegmentReadable = 4 + SegmentContainsData = 8 + SegmentContainsCode = 16 + SegmentDenyWrite = 32 + SegmentDenyExecute = 64 -class DSCViewLoadProgress(enum.IntEnum): - LoadProgressNotStarted = 0 - LoadProgressLoadingCaches = 1 - LoadProgressLoadingImages = 2 - LoadProgressFinished = 3 +class SharedCacheEntryType(enum.IntEnum): + SharedCacheEntryTypePrimary = 0 + SharedCacheEntryTypeSecondary = 1 + SharedCacheEntryTypeSymbols = 2 + SharedCacheEntryTypeDyldData = 3 + SharedCacheEntryTypeStub = 4 -class DSCViewState(enum.IntEnum): - Unloaded = 0 - Loaded = 1 - LoadedWithImages = 2 +class SharedCacheRegionType(enum.IntEnum): + SharedCacheRegionTypeImage = 0 + SharedCacheRegionTypeStubIsland = 1 + SharedCacheRegionTypeDyldData = 2 + SharedCacheRegionTypeNonImage = 3 + + +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/sharedcache/api/sharedcache.cpp b/view/sharedcache/api/sharedcache.cpp index d9838057..6d552f0d 100644 --- a/view/sharedcache/api/sharedcache.cpp +++ b/view/sharedcache/api/sharedcache.cpp @@ -4,231 +4,324 @@ #include "sharedcacheapi.h" -namespace SharedCacheAPI { +using namespace BinaryNinja; +using namespace SharedCacheAPI; - SharedCache::SharedCache(Ref<BinaryView> view) { - m_object = BNGetSharedCache(view->GetObject()); - } +BNSharedCacheImage ImageToApi(CacheImage image) +{ + BNSharedCacheImage apiImage; + apiImage.name = BNAllocString(image.name.c_str()); + apiImage.headerAddress = image.headerAddress; + apiImage.regionStartCount = image.regionStarts.size(); + // TODO: If we alloc then core cannot delete + uint64_t *regionStarts = new uint64_t[image.regionStarts.size()]; + for (size_t i = 0; i < image.regionStarts.size(); i++) + regionStarts[i] = image.regionStarts[i]; + apiImage.regionStarts = regionStarts; + return apiImage; +} - BNDSCViewLoadProgress SharedCache::GetLoadProgress(Ref<BinaryView> view) - { - return BNDSCViewGetLoadProgress(view->GetFile()->GetSessionId()); - } +CacheImage ImageFromApi(BNSharedCacheImage image) +{ + CacheImage apiImage; + apiImage.name = image.name; + apiImage.headerAddress = image.headerAddress; + apiImage.regionStarts.reserve(image.regionStartCount); + for (size_t i = 0; i < image.regionStartCount; i++) + apiImage.regionStarts.push_back(image.regionStarts[i]); + return apiImage; +} - uint64_t SharedCache::FastGetBackingCacheCount(Ref<BinaryView> view) - { - return BNDSCViewFastGetBackingCacheCount(view->GetObject()); - } +BNSharedCacheRegion RegionToApi(const CacheRegion ®ion) +{ + BNSharedCacheRegion apiRegion; + apiRegion.vmAddress = region.start; + apiRegion.name = BNAllocString(region.name.c_str()); + apiRegion.size = region.size; + apiRegion.flags = region.flags; + apiRegion.regionType = region.type; + // If not associated with image this will be zeroed. + apiRegion.imageStart = region.imageStart.value_or(0); + return apiRegion; +} - bool SharedCache::LoadImageWithInstallName(std::string installName, bool skipObjC) - { - char* str = BNAllocString(installName.c_str()); - return BNDSCViewLoadImageWithInstallName(m_object, str, skipObjC); - } +CacheRegion RegionFromApi(BNSharedCacheRegion apiRegion) +{ + CacheRegion region; + region.start = apiRegion.vmAddress; + region.name = apiRegion.name; + region.size = apiRegion.size; + region.flags = apiRegion.flags; + region.type = apiRegion.regionType; + return region; +} - bool SharedCache::LoadSectionAtAddress(uint64_t addr) - { - return BNDSCViewLoadSectionAtAddress(m_object, addr); - } +BNSharedCacheMappingInfo MappingToApi(const CacheMappingInfo &mapping) +{ + BNSharedCacheMappingInfo apiMapping; + apiMapping.vmAddress = mapping.vmAddress; + apiMapping.size = mapping.size; + apiMapping.fileOffset = mapping.fileOffset; + return apiMapping; +} - bool SharedCache::LoadImageContainingAddress(uint64_t addr, bool skipObjC) - { - return BNDSCViewLoadImageContainingAddress(m_object, addr, skipObjC); - } +CacheMappingInfo MappingFromApi(BNSharedCacheMappingInfo apiMapping) +{ + CacheMappingInfo mapping; + mapping.vmAddress = apiMapping.vmAddress; + mapping.size = apiMapping.size; + mapping.fileOffset = apiMapping.fileOffset; + return mapping; +} - std::vector<std::string> SharedCache::GetAvailableImages() - { - size_t count; - char** value = BNDSCViewGetInstallNames(m_object, &count); - if (value == nullptr) - { - return {}; - } +BNSharedCacheEntry EntryToApi(const CacheEntry &entry) +{ + BNSharedCacheEntry apiEntry; + apiEntry.path = BNAllocString(entry.path.c_str()); + apiEntry.entryType = entry.entryType; + const auto &mappings = entry.mappings; + apiEntry.mappingCount = mappings.size(); + // TODO: If we alloc then the core cannot delete. + apiEntry.mappings = new BNSharedCacheMappingInfo[mappings.size()]; + for (size_t i = 0; i < mappings.size(); i++) + apiEntry.mappings[i] = MappingToApi(mappings[i]); + return apiEntry; +} - std::vector<std::string> result; - for (size_t i = 0; i < count; i++) - { - result.push_back(value[i]); - } +CacheEntry EntryFromApi(BNSharedCacheEntry apiEntry) +{ + CacheEntry entry; + entry.path = apiEntry.path; + entry.entryType = apiEntry.entryType; + entry.mappings.reserve(apiEntry.mappingCount); + for (size_t i = 0; i < apiEntry.mappingCount; i++) + entry.mappings.push_back(MappingFromApi(apiEntry.mappings[i])); + return entry; +} - BNFreeStringList(value, count); - return result; - } +CacheSymbol SymbolFromApi(BNSharedCacheSymbol apiSymbol) +{ + CacheSymbol symbol; + symbol.name = apiSymbol.name; + symbol.address = apiSymbol.address; + symbol.type = apiSymbol.symbolType; + return symbol; +} - void SharedCache::ProcessObjCSectionsForImageWithInstallName(std::string installName) +std::string SharedCacheAPI::GetRegionTypeAsString(const BNSharedCacheRegionType &type) +{ + switch (type) { - char* str = BNAllocString(installName.c_str()); - BNDSCViewProcessObjCSectionsForImageWithInstallName(m_object, str, true); + case SharedCacheRegionTypeImage: + return "Image"; + case SharedCacheRegionTypeStubIsland: + return "StubIsland"; + case SharedCacheRegionTypeDyldData: + return "DyldData"; + case SharedCacheRegionTypeNonImage: + return "NonImage"; + default: + return "Unknown"; } +} - void SharedCache::ProcessAllObjCSections() - { - BNDSCViewProcessAllObjCSections(m_object); - } +SharedCacheController::SharedCacheController(BNSharedCacheController *controller) +{ + m_object = controller; +} - std::vector<DSCMemoryRegion> SharedCache::GetLoadedMemoryRegions() - { - size_t count; - BNDSCMappedMemoryRegion* value = BNDSCViewGetLoadedRegions(m_object, &count); - if (value == nullptr) - { - return {}; - } +DSCRef<SharedCacheController> SharedCacheController::GetController(BinaryView &view) +{ + BNSharedCacheController *controller = BNGetSharedCacheController(view.GetObject()); + if (controller == nullptr) + return nullptr; + return new SharedCacheController(controller); +} - std::vector<DSCMemoryRegion> result; - for (size_t i = 0; i < count; i++) - { - DSCMemoryRegion region; - region.vmAddress = value[i].vmAddress; - region.size = value[i].size; - region.prettyName = value[i].name; - result.push_back(region); - } +bool SharedCacheController::ApplyRegion(BinaryView &view, const CacheRegion ®ion) +{ + auto apiRegion = RegionToApi(region); + bool result = BNSharedCacheControllerApplyRegion(m_object, view.GetObject(), &apiRegion); + BNSharedCacheFreeRegion(apiRegion); + return result; +} - BNDSCViewFreeLoadedRegions(value, count); - return result; - } - std::vector<BackingCache> SharedCache::GetBackingCaches() - { - size_t count; - BNDSCBackingCache* value = BNDSCViewGetBackingCaches(m_object, &count); - if (value == nullptr) - { - return {}; - } +bool SharedCacheController::ApplyImage(BinaryView &view, const CacheImage &image) +{ + auto apiImage = ImageToApi(image); + bool result = BNSharedCacheControllerApplyImage(m_object, view.GetObject(), &apiImage); + BNSharedCacheFreeImage(apiImage); + return result; +} - std::vector<BackingCache> result; - for (size_t i = 0; i < count; i++) - { - BackingCache cache; - cache.path = value[i].path; - cache.cacheType = value[i].cacheType; - for (size_t j = 0; j < value[i].mappingCount; j++) - { - BackingCacheMapping mapping; - mapping.vmAddress = value[i].mappings[j].vmAddress; - mapping.size = value[i].mappings[j].size; - mapping.fileOffset = value[i].mappings[j].fileOffset; - cache.mappings.push_back(mapping); - } - result.push_back(cache); - } +bool SharedCacheController::IsRegionLoaded(const CacheRegion ®ion) const +{ + auto apiRegion = RegionToApi(region); + bool result = BNSharedCacheControllerIsRegionLoaded(m_object, &apiRegion); + BNSharedCacheFreeRegion(apiRegion); + return result; +} - BNDSCViewFreeBackingCaches(value, count); - return result; - } +bool SharedCacheController::IsImageLoaded(const CacheImage &image) const +{ + auto apiImage = ImageToApi(image); + bool result = BNSharedCacheControllerIsImageLoaded(m_object, &apiImage); + BNSharedCacheFreeImage(apiImage); + return result; +} - std::vector<DSCImage> SharedCache::GetImages() - { - size_t count; - BNDSCImage* value = BNDSCViewGetAllImages(m_object, &count); - if (value == nullptr) - { - return {}; - } +std::optional<CacheRegion> SharedCacheController::GetRegionAt(uint64_t address) const +{ + BNSharedCacheRegion apiRegion; + if (!BNSharedCacheControllerGetRegionAt(m_object, address, &apiRegion)) + return std::nullopt; + CacheRegion region = RegionFromApi(apiRegion); + BNSharedCacheFreeRegion(apiRegion); + return region; +} - std::vector<DSCImage> result; - for (size_t i = 0; i < count; i++) - { - DSCImage img; - img.name = value[i].name; - img.headerAddress = value[i].headerAddress; - for (size_t j = 0; j < value[i].mappingCount; j++) - { - DSCImageMemoryMapping mapping; - mapping.filePath = value[i].mappings[j].filePath; - 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); - } +std::optional<CacheRegion> SharedCacheController::GetRegionContaining(uint64_t address) const +{ + BNSharedCacheRegion apiRegion; + if (!BNSharedCacheControllerGetRegionContaining(m_object, address, &apiRegion)) + return std::nullopt; + CacheRegion region = RegionFromApi(apiRegion); + BNSharedCacheFreeRegion(apiRegion); + return region; +} - BNDSCViewFreeAllImages(value, count); - return result; - } +std::optional<CacheImage> SharedCacheController::GetImageAt(uint64_t address) const +{ + BNSharedCacheImage apiImage; + if (!BNSharedCacheControllerGetImageAt(m_object, address, &apiImage)) + return std::nullopt; + CacheImage image = ImageFromApi(apiImage); + BNSharedCacheFreeImage(apiImage); + return image; +} - std::vector<DSCSymbol> SharedCache::LoadAllSymbolsAndWait() - { - size_t count; - BNDSCSymbolRep* value = BNDSCViewLoadAllSymbolsAndWait(m_object, &count); - if (value == nullptr) - { - return {}; - } +std::optional<CacheImage> SharedCacheController::GetImageContaining(uint64_t address) const +{ + BNSharedCacheImage apiImage; + if (!BNSharedCacheControllerGetImageContaining(m_object, address, &apiImage)) + return std::nullopt; + CacheImage image = ImageFromApi(apiImage); + BNSharedCacheFreeImage(apiImage); + return image; +} - std::vector<DSCSymbol> result; - result.reserve(count); - for (size_t i = 0; i < count; i++) - { - DSCSymbol sym; - sym.address = value[i].address; - sym.name = StringRef(BNDuplicateStringRef(value[i].name)); - sym.image = value[i].image; - result.push_back(sym); - } +std::optional<CacheImage> SharedCacheController::GetImageWithName(const std::string &name) const +{ + BNSharedCacheImage apiImage; + if (!BNSharedCacheControllerGetImageWithName(m_object, name.c_str(), &apiImage)) + return std::nullopt; + CacheImage image = ImageFromApi(apiImage); + BNSharedCacheFreeImage(apiImage); + return image; +} - BNDSCViewFreeSymbols(value, count); - return result; - } +std::vector<std::string> SharedCacheController::GetImageDependencies(const CacheImage &image) const +{ + size_t count; + BNSharedCacheImage apiImage = ImageToApi(image); + char **dependencies = BNSharedCacheControllerGetImageDependencies(m_object, &apiImage, &count); + BNSharedCacheFreeImage(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; +} - std::string SharedCache::GetNameForAddress(uint64_t address) - { - char* name = BNDSCViewGetNameForAddress(m_object, address); - if (name == nullptr) - return {}; - std::string result = name; - BNFreeString(name); - return result; - } +std::optional<CacheSymbol> SharedCacheController::GetSymbolAt(uint64_t address) const +{ + BNSharedCacheSymbol apiSymbol; + if (!BNSharedCacheControllerGetSymbolAt(m_object, address, &apiSymbol)) + return std::nullopt; + CacheSymbol symbol = SymbolFromApi(apiSymbol); + BNSharedCacheFreeSymbol(apiSymbol); + return symbol; +} - std::string SharedCache::GetImageNameForAddress(uint64_t address) - { - char* name = BNDSCViewGetImageNameForAddress(m_object, address); - if (name == nullptr) - return {}; - std::string result = name; - BNFreeString(name); - return result; - } +std::optional<CacheSymbol> SharedCacheController::GetSymbolWithName(const std::string &name) const +{ + BNSharedCacheSymbol apiSymbol; + if (!BNSharedCacheControllerGetSymbolWithName(m_object, name.c_str(), &apiSymbol)) + return std::nullopt; + CacheSymbol symbol = SymbolFromApi(apiSymbol); + BNSharedCacheFreeSymbol(apiSymbol); + return symbol; +} - std::optional<SharedCacheMachOHeader> SharedCache::GetMachOHeaderForImage(std::string name) - { - char* str = BNAllocString(name.c_str()); - char* outputStr = BNDSCViewGetImageHeaderForName(m_object, str); - if (outputStr == nullptr) - return {}; - std::string output = outputStr; - BNFreeString(outputStr); - if (output.empty()) - return {}; +std::vector<CacheEntry> SharedCacheController::GetEntries() const +{ + size_t count; + BNSharedCacheEntry *entries = BNSharedCacheControllerGetEntries(m_object, &count); + std::vector<CacheEntry> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.emplace_back(EntryFromApi(entries[i])); + BNSharedCacheFreeEntryList(entries, count); + return result; +} - return SharedCacheMachOHeader::LoadFromString(output); - } +std::vector<CacheRegion> SharedCacheController::GetLoadedRegions() const +{ + size_t count; + BNSharedCacheRegion *regions = BNSharedCacheControllerGetLoadedRegions(m_object, &count); + std::vector<CacheRegion> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.emplace_back(RegionFromApi(regions[i])); + BNSharedCacheFreeRegionList(regions, count); + return result; +} - std::optional<SharedCacheMachOHeader> SharedCache::GetMachOHeaderForAddress(uint64_t address) - { - char* outputStr = BNDSCViewGetImageHeaderForAddress(m_object, address); - if (outputStr == nullptr) - return {}; - std::string output = outputStr; - BNFreeString(outputStr); - if (output.empty()) - return {}; - return SharedCacheMachOHeader::LoadFromString(output); - } +std::vector<CacheRegion> SharedCacheController::GetRegions() const +{ + size_t count; + BNSharedCacheRegion *regions = BNSharedCacheControllerGetRegions(m_object, &count); + std::vector<CacheRegion> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.emplace_back(RegionFromApi(regions[i])); + BNSharedCacheFreeRegionList(regions, count); + return result; +} - BNDSCViewState SharedCache::GetState() - { - return BNDSCViewGetState(m_object); - } +std::vector<CacheImage> SharedCacheController::GetImages() const +{ + size_t count; + BNSharedCacheImage *images = BNSharedCacheControllerGetImages(m_object, &count); + std::vector<CacheImage> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.emplace_back(ImageFromApi(images[i])); + BNSharedCacheFreeImageList(images, count); + return result; +} - void SharedCache::FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis) const - { - BNDSCFindSymbolAtAddressAndApplyToAddress(m_object, symbolLocation, targetLocation, triggerReanalysis); - } +std::vector<CacheImage> SharedCacheController::GetLoadedImages() const +{ + size_t count; + BNSharedCacheImage *images = BNSharedCacheControllerGetLoadedImages(m_object, &count); + std::vector<CacheImage> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.emplace_back(ImageFromApi(images[i])); + BNSharedCacheFreeImageList(images, count); + return result; +} -} // namespace SharedCacheAPI +std::vector<CacheSymbol> SharedCacheController::GetSymbols() const +{ + size_t count; + BNSharedCacheSymbol *symbols = BNSharedCacheControllerGetSymbols(m_object, &count); + std::vector<CacheSymbol> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.emplace_back(SymbolFromApi(symbols[i])); + BNSharedCacheFreeSymbolList(symbols, count); + return result; +} diff --git a/view/sharedcache/api/sharedcacheapi.h b/view/sharedcache/api/sharedcacheapi.h index 5c50e283..e12582f2 100644 --- a/view/sharedcache/api/sharedcacheapi.h +++ b/view/sharedcache/api/sharedcacheapi.h @@ -1,285 +1,327 @@ #pragma once #include <binaryninjaapi.h> -#include "../core/MetadataSerializable.hpp" -#include "view/macho/machoview.h" #include "sharedcachecore.h" -namespace SharedCacheAPI { - template<class T> - class SCRefCountObject { - void AddRefInternal() { m_refs.fetch_add(1); } +template<class T> +class DSCRefCountObject { + 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; - SCRefCountObject() : m_refs(0), m_object(nullptr) {} + DSCRefCountObject() : m_refs(0), m_object(nullptr) {} - virtual ~SCRefCountObject() {} + virtual ~DSCRefCountObject() = default; - T *GetObject() const { return m_object; } + T *GetObject() const { return m_object; } - static T *GetObject(SCRefCountObject *obj) { - if (!obj) - return nullptr; - return obj->GetObject(); - } + static T *GetObject(DSCRefCountObject *obj) { + if (!obj) + return nullptr; + return obj->GetObject(); + } - void AddRef() { AddRefInternal(); } + void AddRef() { AddRefInternal(); } - void Release() { ReleaseInternal(); } + void Release() { ReleaseInternal(); } - void AddRefForRegistration() { AddRefInternal(); } - }; + void AddRefForRegistration() { AddRefInternal(); } +}; - template<class T, T *(*AddObjectReference)(T *), void (*FreeObjectReference)(T *)> - class SCCoreRefCountObject { - void AddRefInternal() { m_refs.fetch_add(1); } +template<class T, T *(*AddObjectReference)(T *), void (*FreeObjectReference)(T *)> +class DSCCoreRefCountObject { + void AddRefInternal() { m_refs.fetch_add(1); } - void ReleaseInternal() { - if (m_refs.fetch_sub(1) == 1) { - if (!m_registeredRef) - delete this; - } + 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; +public: + std::atomic<int> m_refs; + bool m_registeredRef = false; + T *m_object; + + DSCCoreRefCountObject() : m_refs(0), m_object(nullptr) {} + + virtual ~DSCCoreRefCountObject() = default; + + T *GetObject() const { return m_object; } + + static T *GetObject(DSCCoreRefCountObject *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(); + } - SCCoreRefCountObject() : m_refs(0), m_object(nullptr) {} + void AddRefForRegistration() { m_registeredRef = true; } - virtual ~SCCoreRefCountObject() {} + void ReleaseForRegistration() { + m_object = nullptr; + m_registeredRef = false; + if (m_refs == 0) + delete this; + } +}; - T *GetObject() const { return m_object; } +template <class T> +class DSCRef +{ + T* m_obj; +#ifdef BN_REF_COUNT_DEBUG + void* m_assignmentTrace = nullptr; +#endif - static T *GetObject(SCCoreRefCountObject *obj) { - if (!obj) - return nullptr; - return obj->GetObject(); +public: + DSCRef() : m_obj(NULL) {} + + DSCRef(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(); + DSCRef(const DSCRef<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 } + } + + DSCRef(DSCRef<T>&& other) : m_obj(other.m_obj) + { + other.m_obj = 0; +#ifdef BN_REF_COUNT_DEBUG + m_assignmentTrace = other.m_assignmentTrace; +#endif + } - void Release() { - if (m_object) - FreeObjectReference(m_object); - ReleaseInternal(); + ~DSCRef() + { + if (m_obj) + { + m_obj->Release(); +#ifdef BN_REF_COUNT_DEBUG + BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace); +#endif } + } - void AddRefForRegistration() { m_registeredRef = true; } + DSCRef<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; + DSCRef<T>& operator=(DSCRef<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 DSCMemoryRegion { - uint64_t vmAddress; - uint64_t size; - std::string prettyName; - }; + DSCRef<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; - BNBackingCacheType cacheType; - std::vector<BackingCacheMapping> mappings; - }; + T* operator->() const + { + return m_obj; + } - struct DSCImageMemoryMapping { - std::string filePath; - std::string name; - uint64_t vmAddress; - uint64_t size; - bool loaded; - uint64_t rawViewOffset; - }; + T& operator*() const + { + return *m_obj; + } - struct DSCImage { - std::string name; - uint64_t headerAddress; - std::vector<DSCImageMemoryMapping> mappings; - }; + bool operator!() const + { + return m_obj == NULL; + } - struct DSCSymbol { - uint64_t address; - BinaryNinja::StringRef name; - std::string image; - }; + bool operator==(const T* obj) const + { + return T::GetObject(m_obj) == T::GetObject(obj); + } - struct SharedCacheMachOHeader : public SharedCacheCore::MetadataSerializable<SharedCacheMachOHeader> { - uint64_t textBase = 0; - uint64_t loadCommandOffset = 0; - mach_header_64 ident; - std::string identifierPrefix; - std::string installName; + bool operator==(const DSCRef<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 DSCRef<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 DSCRef<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; - 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; +// TODO: replace namespace? +namespace SharedCacheAPI { + struct CacheRegion + { + BNSharedCacheRegionType type; + std::string name; + uint64_t start; + uint64_t size; + std::optional<uint64_t> imageStart; + BNSegmentFlag flags; + }; - void Store(SharedCacheCore::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(exportTriePath); - MSS(linkeditPresent); - MSS(dysymPresent); - MSS(dyldInfoPresent); - MSS(exportTriePresent); - MSS(chainedFixupsPresent); - MSS(routinesPresent); - MSS(functionStartsPresent); - MSS(relocatable); - } + std::string GetRegionTypeAsString(const BNSharedCacheRegionType& type); - static SharedCacheMachOHeader Load(SharedCacheCore::DeserializationContext& context) { - SharedCacheMachOHeader 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(exportTriePath); - 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; - } + struct CacheMappingInfo + { + uint64_t vmAddress; + uint64_t size; + uint64_t fileOffset; + }; + + struct CacheImage + { + uint64_t headerAddress; + std::string name; + std::vector<uint64_t> regionStarts; }; + struct CacheEntry + { + std::string path; + BNSharedCacheEntryType entryType; + std::vector<CacheMappingInfo> mappings; + }; + + struct CacheSymbol + { + BNSymbolType type; + uint64_t address; + std::string name; + }; - class SharedCache : public SCCoreRefCountObject<BNSharedCache, BNNewSharedCacheReference, BNFreeSharedCacheReference> { + class SharedCacheController : public DSCCoreRefCountObject<BNSharedCacheController, BNNewSharedCacheControllerReference, BNFreeSharedCacheControllerReference> { public: - SharedCache(Ref<BinaryView> view); + explicit SharedCacheController(BNSharedCacheController* controller); + static DSCRef<SharedCacheController> GetController(BinaryNinja::BinaryView& view); - BNDSCViewState GetState(); - static BNDSCViewLoadProgress GetLoadProgress(Ref<BinaryView> view); - static uint64_t FastGetBackingCacheCount(Ref<BinaryView> view); + bool ApplyRegion(BinaryNinja::BinaryView& view, const CacheRegion& region); - bool LoadImageWithInstallName(std::string installName, bool skipObjC = false); - bool LoadSectionAtAddress(uint64_t addr); - bool LoadImageContainingAddress(uint64_t addr, bool skipObjC = false); - std::vector<std::string> GetAvailableImages(); - - void ProcessObjCSectionsForImageWithInstallName(std::string installName); - void ProcessAllObjCSections(); + // 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); - std::vector<DSCSymbol> LoadAllSymbolsAndWait(); + bool IsRegionLoaded(const CacheRegion& region) const; + bool IsImageLoaded(const CacheImage& image) const; - std::string GetNameForAddress(uint64_t address); - std::string GetImageNameForAddress(uint64_t address); + std::optional<CacheRegion> GetRegionAt(uint64_t address) const; + std::optional<CacheRegion> GetRegionContaining(uint64_t address) const; - std::vector<BackingCache> GetBackingCaches(); - std::vector<DSCImage> GetImages(); + 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::optional<SharedCacheMachOHeader> GetMachOHeaderForImage(std::string name); - std::optional<SharedCacheMachOHeader> GetMachOHeaderForAddress(uint64_t address); + std::vector<std::string> GetImageDependencies(const CacheImage& image) const; - std::vector<DSCMemoryRegion> GetLoadedMemoryRegions(); + std::optional<CacheSymbol> GetSymbolAt(uint64_t address) const; + std::optional<CacheSymbol> GetSymbolWithName(const std::string& name) const; - void FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis = true) const; + std::vector<CacheEntry> GetEntries() const; + std::vector<CacheRegion> GetRegions() const; + std::vector<CacheRegion> GetLoadedRegions() const; + 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/sharedcache/api/sharedcachecore.h b/view/sharedcache/api/sharedcachecore.h index 54899919..ab2acb60 100644 --- a/view/sharedcache/api/sharedcachecore.h +++ b/view/sharedcache/api/sharedcachecore.h @@ -1,11 +1,5 @@ #pragma once - -#ifdef __cplusplus -extern "C" -{ -#endif - #ifdef __GNUC__ #ifdef SHAREDCACHE_LIBRARY #define SHAREDCACHE_FFI_API __attribute__((visibility("default"))) @@ -28,137 +22,139 @@ extern "C" #endif // _MSC_VER #endif // __GNUC__C -#define CORE_ALLOCATED_STRUCT(T) - -#define CORE_ALLOCATED_CLASS(T) \ - public: \ - CORE_ALLOCATED_STRUCT(T) \ - private: - -#define DECLARE_SHAREDCACHE_API_OBJECT_INTERNAL(handle, cls, ns) \ - namespace ns { class cls; } struct handle { ns::cls* object; } - -#define DECLARE_SHAREDCACHE_API_OBJECT(handle, cls) DECLARE_SHAREDCACHE_API_OBJECT_INTERNAL(handle, cls, SharedCacheCore) - -#define IMPLEMENT_SHAREDCACHE_API_OBJECT(handle) \ - CORE_ALLOCATED_CLASS(handle) \ - private: \ - handle m_apiObject; \ - public: \ - typedef handle* APIHandle; \ - handle* GetAPIObject() { return &m_apiObject; } \ - private: -#define INIT_SHAREDCACHE_API_OBJECT() \ - m_apiObject.object = this; - - typedef enum BNDSCViewState { - Unloaded, - Loaded, - LoadedWithImages, - } BNDSCViewState; +#ifdef __cplusplus +extern "C" +{ +#endif - typedef enum BNDSCViewLoadProgress { - LoadProgressNotStarted, - LoadProgressLoadingCaches, - LoadProgressLoadingImages, - LoadProgressFinished, - } BNDSCViewLoadProgress; + // 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 BNBackingCacheType { - BackingCacheTypePrimary, - BackingCacheTypeSecondary, - BackingCacheTypeSymbols, - } BNBackingCacheType; + typedef enum BNSymbolType + { + FunctionSymbol = 0, + ImportAddressSymbol = 1, + ImportedFunctionSymbol = 2, + DataSymbol = 3, + ImportedDataSymbol = 4, + ExternalSymbol = 5, + LibraryFunctionSymbol = 6, + SymbolicFunctionSymbol = 7, + LocalLabelSymbol = 8, + } BNSymbolType; +#endif typedef struct BNBinaryView BNBinaryView; - typedef struct BNSharedCache BNSharedCache; - typedef struct BNStringRef BNStringRef; + typedef struct BNSharedCacheController BNSharedCacheController; - typedef struct BNDSCImageMemoryMapping { - char* filePath; - char* name; - uint64_t vmAddress; - uint64_t size; - bool loaded; - uint64_t rawViewOffset; - } BNDSCImageMemoryMapping; + typedef enum BNSharedCacheEntryType { + SharedCacheEntryTypePrimary, + SharedCacheEntryTypeSecondary, + SharedCacheEntryTypeSymbols, + SharedCacheEntryTypeDyldData, + SharedCacheEntryTypeStub, + } BNSharedCacheEntryType; - typedef struct BNDSCImage { + typedef enum BNSharedCacheRegionType { + SharedCacheRegionTypeImage, + SharedCacheRegionTypeStubIsland, + SharedCacheRegionTypeDyldData, + SharedCacheRegionTypeNonImage, + } BNSharedCacheRegionType; + + typedef struct BNSharedCacheImage { char* name; uint64_t headerAddress; - BNDSCImageMemoryMapping* mappings; - size_t mappingCount; - } BNDSCImage; + size_t regionStartCount; + uint64_t* regionStarts; + } BNSharedCacheImage; - typedef struct BNDSCMappedMemoryRegion { + typedef struct BNSharedCacheRegion { + BNSharedCacheRegionType regionType; + char* name; uint64_t vmAddress; uint64_t size; - char* name; - } BNDSCMappedMemoryRegion; + // NOTE: If not associated with an image this will be zero. + uint64_t imageStart; + BNSegmentFlag flags; + } BNSharedCacheRegion; - typedef struct BNDSCBackingCacheMapping { + typedef struct BNSharedCacheMappingInfo { uint64_t vmAddress; uint64_t size; uint64_t fileOffset; - } BNDSCBackingCacheMapping; + } BNSharedCacheMappingInfo; - typedef struct BNDSCBackingCache { + typedef struct BNSharedCacheEntry { char* path; - BNBackingCacheType cacheType; - BNDSCBackingCacheMapping* mappings; + BNSharedCacheEntryType entryType; size_t mappingCount; - } BNDSCBackingCache; - - typedef struct BNDSCMemoryUsageInfo { - uint64_t sharedCacheRefs; - uint64_t mmapRefs; - } BNDSCMemoryUsageInfo; + BNSharedCacheMappingInfo* mappings; + } BNSharedCacheEntry; - typedef struct BNDSCSymbolRep { + typedef struct BNSharedCacheSymbol { + BNSymbolType symbolType; uint64_t address; - BNStringRef* name; - char* image; - } BNDSCSymbolRep; + char* name; + } BNSharedCacheSymbol; - SHAREDCACHE_FFI_API BNSharedCache* BNGetSharedCache(BNBinaryView* data); + SHAREDCACHE_FFI_API BNSharedCacheController* BNGetSharedCacheController(BNBinaryView* data); - SHAREDCACHE_FFI_API BNSharedCache* BNNewSharedCacheReference(BNSharedCache* cache); - SHAREDCACHE_FFI_API void BNFreeSharedCacheReference(BNSharedCache* cache); + SHAREDCACHE_FFI_API BNSharedCacheController* BNNewSharedCacheControllerReference(BNSharedCacheController* controller); + SHAREDCACHE_FFI_API void BNFreeSharedCacheControllerReference(BNSharedCacheController* controller); - SHAREDCACHE_FFI_API char** BNDSCViewGetInstallNames(BNSharedCache* cache, size_t* count); + SHAREDCACHE_FFI_API bool BNSharedCacheControllerApplyImage(BNSharedCacheController* controller, BNBinaryView* view, BNSharedCacheImage* image); + SHAREDCACHE_FFI_API bool BNSharedCacheControllerApplyRegion(BNSharedCacheController* controller, BNBinaryView* view, BNSharedCacheRegion* region); - SHAREDCACHE_FFI_API bool BNDSCViewLoadImageWithInstallName(BNSharedCache* cache, char* name, bool skipObjC); - SHAREDCACHE_FFI_API bool BNDSCViewLoadSectionAtAddress(BNSharedCache* cache, uint64_t name); - SHAREDCACHE_FFI_API bool BNDSCViewLoadImageContainingAddress(BNSharedCache* cache, uint64_t address, bool skipObjC); + SHAREDCACHE_FFI_API bool BNSharedCacheControllerIsImageLoaded(BNSharedCacheController* controller, BNSharedCacheImage* image); + SHAREDCACHE_FFI_API bool BNSharedCacheControllerIsRegionLoaded(BNSharedCacheController* controller, BNSharedCacheRegion* region); - SHAREDCACHE_FFI_API void BNDSCViewProcessObjCSectionsForImageWithInstallName(BNSharedCache* cache, char* name, bool deallocName); - SHAREDCACHE_FFI_API void BNDSCViewProcessAllObjCSections(BNSharedCache* cache); + SHAREDCACHE_FFI_API bool BNSharedCacheControllerGetRegionAt(BNSharedCacheController* controller, uint64_t address, BNSharedCacheRegion* outRegion); + SHAREDCACHE_FFI_API bool BNSharedCacheControllerGetRegionContaining(BNSharedCacheController* controller, uint64_t address, BNSharedCacheRegion* region); + + SHAREDCACHE_FFI_API BNSharedCacheRegion* BNSharedCacheControllerGetRegions(BNSharedCacheController* controller, size_t* count); + SHAREDCACHE_FFI_API BNSharedCacheRegion* BNSharedCacheControllerGetLoadedRegions(BNSharedCacheController* controller, size_t* count); + + SHAREDCACHE_FFI_API uint64_t* BNSharedCacheAllocRegionList(uint64_t* list, size_t count); + + SHAREDCACHE_FFI_API void BNSharedCacheFreeRegion(BNSharedCacheRegion region); + SHAREDCACHE_FFI_API void BNSharedCacheFreeRegionList(BNSharedCacheRegion* regions, size_t count); + + SHAREDCACHE_FFI_API bool BNSharedCacheControllerGetImageAt(BNSharedCacheController* controller, uint64_t address, BNSharedCacheImage* image); + SHAREDCACHE_FFI_API bool BNSharedCacheControllerGetImageContaining(BNSharedCacheController* controller, uint64_t address, BNSharedCacheImage* image); + SHAREDCACHE_FFI_API bool BNSharedCacheControllerGetImageWithName(BNSharedCacheController* controller, const char* name, BNSharedCacheImage* image); - SHAREDCACHE_FFI_API char* BNDSCViewGetNameForAddress(BNSharedCache* cache, uint64_t address); - SHAREDCACHE_FFI_API char* BNDSCViewGetImageNameForAddress(BNSharedCache* cache, uint64_t address); + SHAREDCACHE_FFI_API char** BNSharedCacheControllerGetImageDependencies(BNSharedCacheController* controller, BNSharedCacheImage* image, size_t* count); - SHAREDCACHE_FFI_API BNDSCViewState BNDSCViewGetState(BNSharedCache* cache); - SHAREDCACHE_FFI_API BNDSCViewLoadProgress BNDSCViewGetLoadProgress(uint64_t sessionID); - SHAREDCACHE_FFI_API uint64_t BNDSCViewFastGetBackingCacheCount(BNBinaryView* view); + SHAREDCACHE_FFI_API BNSharedCacheImage* BNSharedCacheControllerGetImages(BNSharedCacheController* controller, size_t* count); + SHAREDCACHE_FFI_API BNSharedCacheImage* BNSharedCacheControllerGetLoadedImages(BNSharedCacheController* controller, size_t* count); - SHAREDCACHE_FFI_API BNDSCSymbolRep* BNDSCViewLoadAllSymbolsAndWait(BNSharedCache* cache, size_t* count); - SHAREDCACHE_FFI_API void BNDSCViewFreeSymbols(BNDSCSymbolRep* symbols, size_t count); + SHAREDCACHE_FFI_API void BNSharedCacheFreeImage(BNSharedCacheImage image); + SHAREDCACHE_FFI_API void BNSharedCacheFreeImageList(BNSharedCacheImage* images, size_t count); - SHAREDCACHE_FFI_API BNDSCMappedMemoryRegion* BNDSCViewGetLoadedRegions(BNSharedCache* cache, size_t* count); - SHAREDCACHE_FFI_API void BNDSCViewFreeLoadedRegions(BNDSCMappedMemoryRegion* images, size_t count); + SHAREDCACHE_FFI_API bool BNSharedCacheControllerGetSymbolAt(BNSharedCacheController* controller, uint64_t address, BNSharedCacheSymbol* symbol); + SHAREDCACHE_FFI_API bool BNSharedCacheControllerGetSymbolWithName(BNSharedCacheController* controller, const char* name, BNSharedCacheSymbol* symbol); - SHAREDCACHE_FFI_API BNDSCImage* BNDSCViewGetAllImages(BNSharedCache* cache, size_t* count); - SHAREDCACHE_FFI_API void BNDSCViewFreeAllImages(BNDSCImage* images, size_t count); + SHAREDCACHE_FFI_API BNSharedCacheSymbol* BNSharedCacheControllerGetSymbols(BNSharedCacheController* controller, size_t* count); - SHAREDCACHE_FFI_API BNDSCBackingCache* BNDSCViewGetBackingCaches(BNSharedCache* cache, size_t* count); - SHAREDCACHE_FFI_API void BNDSCViewFreeBackingCaches(BNDSCBackingCache* caches, size_t count); + SHAREDCACHE_FFI_API void BNSharedCacheFreeSymbol(BNSharedCacheSymbol symbol); + SHAREDCACHE_FFI_API void BNSharedCacheFreeSymbolList(BNSharedCacheSymbol* symbols, size_t count); - SHAREDCACHE_FFI_API void BNDSCFindSymbolAtAddressAndApplyToAddress(BNSharedCache* cache, uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis); + SHAREDCACHE_FFI_API BNSharedCacheEntry* BNSharedCacheControllerGetEntries(BNSharedCacheController* controller, size_t* count); - SHAREDCACHE_FFI_API char* BNDSCViewGetImageHeaderForAddress(BNSharedCache* cache, uint64_t address); - SHAREDCACHE_FFI_API char* BNDSCViewGetImageHeaderForName(BNSharedCache* cache, char* name); + SHAREDCACHE_FFI_API void BNSharedCacheFreeEntry(BNSharedCacheEntry entry); + SHAREDCACHE_FFI_API void BNSharedCacheFreeEntryList(BNSharedCacheEntry* entries, size_t count); - [[maybe_unused]] SHAREDCACHE_FFI_API BNDSCMemoryUsageInfo BNDSCViewGetMemoryUsageInfo(); #ifdef __cplusplus } |
