summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Potchik <brian@vector35.com>2024-05-15 14:39:45 -0400
committerBrian Potchik <brian@vector35.com>2024-05-15 14:39:45 -0400
commit140f7c442883f815f5cfdddd673dca03cc87004f (patch)
treebc740d3cc5894d29112bf78c52701c566d18419f
parent50ce73aa576d63655919449fa111825b644c707f (diff)
Initial MemoryMap implementation.
-rw-r--r--binaryninjacore.h13
-rw-r--r--python/binaryview.py169
2 files changed, 181 insertions, 1 deletions
diff --git a/binaryninjacore.h b/binaryninjacore.h
index f6900c43..cdbd3a49 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -37,7 +37,7 @@
// Current ABI version for linking to the core. This is incremented any time
// there are changes to the API that affect linking, including new functions,
// new types, or modifications to existing functions or types.
-#define BN_CURRENT_CORE_ABI_VERSION 62
+#define BN_CURRENT_CORE_ABI_VERSION 63
// Minimum ABI version that is supported for loading of plugins. Plugins that
// are linked to an ABI version less than this will not be able to load and
@@ -3714,6 +3714,17 @@ extern "C"
BINARYNINJACOREAPI void BNUnregisterViewOfType(BNFileMetadata* file, const char* type, BNBinaryView* view);
+ // Memory Map
+ BINARYNINJACOREAPI char* BNGetMemoryMapDescription(BNBinaryView* view);
+ BINARYNINJACOREAPI bool BNAddBinaryMemoryRegion(BNBinaryView* view, const char* name, uint64_t start, BNBinaryView* data);
+ BINARYNINJACOREAPI bool BNAddDataMemoryRegion(BNBinaryView* view, const char* name, uint64_t start, BNDataBuffer* data);
+ BINARYNINJACOREAPI bool BNAddRemoteMemoryRegion(BNBinaryView* view, const char* name, uint64_t start, BNFileAccessor* accessor);
+ BINARYNINJACOREAPI bool BNRemoveMemoryRegion(BNBinaryView* view, const char* name);
+ BINARYNINJACOREAPI bool BNIsMemoryRegionEnabled(BNBinaryView* view, const char* name, uint64_t start);
+ BINARYNINJACOREAPI bool BNSetMemoryRegionEnabled(BNBinaryView* view, const char* name, uint64_t start, bool enable);
+ BINARYNINJACOREAPI bool BNSetMemoryRegionFill(BNBinaryView* view, const char* name, uint64_t start, uint8_t fill);
+ BINARYNINJACOREAPI void BNResetMemoryMap(BNBinaryView* view);
+
// Binary view access
BINARYNINJACOREAPI BNBinaryView* BNNewViewReference(BNBinaryView* view);
BINARYNINJACOREAPI void BNFreeBinaryView(BNBinaryView* view);
diff --git a/python/binaryview.py b/python/binaryview.py
index a6e0c872..e3c96857 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -26,6 +26,7 @@ import traceback
import ctypes
import abc
import json
+import pprint
import inspect
import os
import uuid
@@ -2103,6 +2104,171 @@ class AdvancedILFunctionList:
yield self._func_queue.popleft().function
+class MemoryMap:
+ """
+ The MemoryMap object is used to describe a system level MemoryMap for which a BinaryView is loaded into. A loaded
+ BinaryView has a view into the MemoryMap which is described by the Segments defined in that BinaryView. The MemoryMap
+ object allows for the addition of multiple, arbitrary overlapping regions of memory. Segmenting of the address space is
+ automatically handled when the MemoryMap is modified and in the case where a portion of the system address space has
+ multilple defined regions, the default ordering gives priority to the most recently added region.
+
+ :Example:
+
+ >>> base = 0x10000
+ >>> rom_base = 0xc0000000
+ >>> segments = Segment.serialize(image_base=base, start=base, length=0x1000, data_offset=0, data_length=0x1000, flags=SegmentFlag.SegmentReadable|SegmentFlag.SegmentExecutable)
+ >>> segments = Segment.serialize(image_base=base, start=rom_base, length=0x1000, flags=SegmentFlag.SegmentReadable, segments=segments)
+ >>> view = load(bytes.fromhex('5054ebfe'), options={'loader.imageBase': base, 'loader.platform': 'x86', 'loader.segments': segments})
+ >>> print(view.memory_map)
+ <region: 0x10000 - 0x10004>
+ size: 0x4
+ objects:
+ 'origin<Mapped>' | Mapped<Absolute>
+
+ <region: 0xc0000000 - 0xc0001000>
+ size: 0x1000
+ objects:
+ 'origin<Mapped>' | Unmapped | FILL<0x0>
+
+ <region: 0xc0001000 - 0xc0001014>
+ size: 0x14
+ objects:
+ 'origin<Mapped>' | Unmapped | FILL<0x0>
+ >>> view.memory_map.add_memory_region("rom", rom_base, b'\x90' * 4096)
+ True
+ >>> print(view.memory_map)
+ <region: 0x10000 - 0x10004>
+ size: 0x4
+ objects:
+ 'origin<Mapped>' | Mapped<Absolute>
+
+ <region: 0xc0000000 - 0xc0001000>
+ size: 0x1000
+ objects:
+ 'rom' | Mapped
+ 'origin<Mapped>' | Unmapped | FILL<0x0>
+
+ <region: 0xc0001000 - 0xc0001014>
+ size: 0x14
+ objects:
+ 'origin<Mapped>' | Unmapped | FILL<0x0>
+ >>> view.read(rom_base, 16)
+ b'\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90'
+ >>> view.memory_map.add_memory_region("pad", rom_base, b'\xa5' * 8)
+ True
+ >>> view.read(rom_base, 16)
+ b'\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\x90\x90\x90\x90\x90\x90\x90\x90'
+ >>> print(view.memory_map)
+ <region: 0x10000 - 0x10004>
+ size: 0x4
+ objects:
+ 'origin<Mapped>' | Mapped<Absolute>
+
+ <region: 0xc0000000 - 0xc0000008>
+ size: 0x8
+ objects:
+ 'pad' | Mapped<Relative>
+ 'rom' | Mapped<Relative>
+ 'origin<Mapped>' | Unmapped | FILL<0x0>
+
+ <region: 0xc0000008 - 0xc0001000>
+ size: 0xff8
+ objects:
+ 'rom' | Mapped<Relative>
+ 'origin<Mapped>' | Unmapped | FILL<0x0>
+
+ <region: 0xc0001000 - 0xc0001014>
+ size: 0x14
+ objects:
+ 'origin<Mapped>' | Unmapped | FILL<0x0>
+ """
+
+ def __repr__(self):
+ return pprint.pformat(self.description())
+
+ def __str__(self):
+ description = self.description()
+ formatted_description = ""
+ for entry in description['MemoryMap']:
+ formatted_description += f"<region: {hex(entry['address'])} - {hex(entry['address'] + entry['length'])}>\n"
+ formatted_description += f"\tsize: {hex(entry['length'])}\n"
+ formatted_description += "\tobjects:\n"
+ for obj in entry['objects']:
+ if obj['target']:
+ mapped_state = f"Mapped<{'Absolute' if obj['absolute_address_mode'] else 'Relative'}>"
+ else:
+ mapped_state = "Unmapped"
+ formatted_description += f"\t\t'{obj['name']}' | {mapped_state}"
+ if not obj['target']:
+ formatted_description += f" | FILL<{hex(obj['fill'])}>"
+ if not obj['enabled']:
+ formatted_description += f" | <DISABLED>"
+ formatted_description += "\n"
+ formatted_description += "\n"
+
+ return formatted_description
+
+ def __init__(self, handle: 'BinaryView'):
+ self.handle = handle
+
+ def __len__(self):
+ mm_json = self.description()
+ if 'MemoryMap' in mm_json:
+ return len(mm_json['MemoryMap'])
+ else:
+ return 0
+
+ def description(self):
+ return json.loads(core.BNGetMemoryMapDescription(self.handle))
+
+# // LoadBinary:
+# // Loads a file in a loadable binary format.
+# // Provides persistence, indicating that the loaded data will persist across sessions.
+# // Presumably loads the file into memory according to the virtual and physical load addresses specified in the file itself.
+# // LoadFile:
+# // Loads a flat file or bytes directly at the specified address.
+# // Provides persistence, suggesting that the loaded data will be saved and remain accessible across sessions.
+# // Allows for direct loading of files or bytes into memory at a specified location.
+# // LoadRemote:
+# // Loads data from a remote source or interface.
+# // Not persistent, implying that the loaded data is ephemeral and may not be saved across sessions.
+# // Supports a target where bytes are provided/generated upon request, indicating a dynamic and potentially transient data source.
+ def add_memory_region(self, name: str, start: int, source: Union['os.PathLike', str, bytes, bytearray, 'BinaryView', 'databuffer.DataBuffer', 'fileaccessor.FileAccessor']) -> bool:
+ if isinstance(source, os.PathLike):
+ source = str(source)
+ if isinstance(source, bytes) or isinstance(source, bytearray):
+ source = databuffer.DataBuffer(source)
+ if isinstance(source, str):
+ with open(source, "rb") as f:
+ source = databuffer.DataBuffer(f.read())
+
+ if name is None:
+ name = ""
+
+ if isinstance(source, BinaryView):
+ return core.BNAddBinaryMemoryRegion(self.handle, name, start, source.handle)
+ elif isinstance(source, databuffer.DataBuffer):
+ return core.BNAddDataMemoryRegion(self.handle, name, start, source.handle)
+ elif isinstance(source, fileaccessor.FileAccessor):
+ return core.BNAddRemoteMemoryRegion(self.handle, name, start, source._cb)
+ else:
+ raise NotImplementedError
+
+ def remove_memory_region(self, name: str) -> bool:
+ return core.BNRemoveMemoryRegion(self.handle, name)
+
+ def is_memory_region_enabled(self, name: str, start: int) -> bool:
+ return core.BNIsMemoryRegionEnabled(self.handle, name, start)
+
+ def set_memory_region_enabled(self, name: str, start: int, enabled: bool = True) -> bool:
+ return core.BNSetMemoryRegionEnabled(self.handle, name, start, enabled)
+
+ def set_memory_region_fill(self, name: str, start: int, fill: int) -> bool:
+ return core.BNSetMemoryRegionFill(self.handle, name, start, fill)
+
+ def reset(self):
+ core.BNResetMemoryMap(self.handle)
+
class BinaryView:
"""
``class BinaryView`` implements a view on binary data, and presents a queryable interface of a binary file. One key
@@ -9464,6 +9630,9 @@ class BinaryView:
finally:
core.BNFreeExternalLocationList(handles, count.value)
+ @property
+ def memory_map(self):
+ return MemoryMap(handle=self.handle)
class BinaryReader:
"""