Skip to content

Commit 140f7c4

Browse files
committed
Initial MemoryMap implementation.
1 parent 50ce73a commit 140f7c4

File tree

2 files changed

+181
-1
lines changed

2 files changed

+181
-1
lines changed

binaryninjacore.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
// Current ABI version for linking to the core. This is incremented any time
3838
// there are changes to the API that affect linking, including new functions,
3939
// new types, or modifications to existing functions or types.
40-
#define BN_CURRENT_CORE_ABI_VERSION 62
40+
#define BN_CURRENT_CORE_ABI_VERSION 63
4141

4242
// Minimum ABI version that is supported for loading of plugins. Plugins that
4343
// are linked to an ABI version less than this will not be able to load and
@@ -3714,6 +3714,17 @@ extern "C"
37143714

37153715
BINARYNINJACOREAPI void BNUnregisterViewOfType(BNFileMetadata* file, const char* type, BNBinaryView* view);
37163716

3717+
// Memory Map
3718+
BINARYNINJACOREAPI char* BNGetMemoryMapDescription(BNBinaryView* view);
3719+
BINARYNINJACOREAPI bool BNAddBinaryMemoryRegion(BNBinaryView* view, const char* name, uint64_t start, BNBinaryView* data);
3720+
BINARYNINJACOREAPI bool BNAddDataMemoryRegion(BNBinaryView* view, const char* name, uint64_t start, BNDataBuffer* data);
3721+
BINARYNINJACOREAPI bool BNAddRemoteMemoryRegion(BNBinaryView* view, const char* name, uint64_t start, BNFileAccessor* accessor);
3722+
BINARYNINJACOREAPI bool BNRemoveMemoryRegion(BNBinaryView* view, const char* name);
3723+
BINARYNINJACOREAPI bool BNIsMemoryRegionEnabled(BNBinaryView* view, const char* name, uint64_t start);
3724+
BINARYNINJACOREAPI bool BNSetMemoryRegionEnabled(BNBinaryView* view, const char* name, uint64_t start, bool enable);
3725+
BINARYNINJACOREAPI bool BNSetMemoryRegionFill(BNBinaryView* view, const char* name, uint64_t start, uint8_t fill);
3726+
BINARYNINJACOREAPI void BNResetMemoryMap(BNBinaryView* view);
3727+
37173728
// Binary view access
37183729
BINARYNINJACOREAPI BNBinaryView* BNNewViewReference(BNBinaryView* view);
37193730
BINARYNINJACOREAPI void BNFreeBinaryView(BNBinaryView* view);

python/binaryview.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import ctypes
2727
import abc
2828
import json
29+
import pprint
2930
import inspect
3031
import os
3132
import uuid
@@ -2103,6 +2104,171 @@ def __iter__(self):
21032104
yield self._func_queue.popleft().function
21042105

21052106

2107+
class MemoryMap:
2108+
"""
2109+
The MemoryMap object is used to describe a system level MemoryMap for which a BinaryView is loaded into. A loaded
2110+
BinaryView has a view into the MemoryMap which is described by the Segments defined in that BinaryView. The MemoryMap
2111+
object allows for the addition of multiple, arbitrary overlapping regions of memory. Segmenting of the address space is
2112+
automatically handled when the MemoryMap is modified and in the case where a portion of the system address space has
2113+
multilple defined regions, the default ordering gives priority to the most recently added region.
2114+
2115+
:Example:
2116+
2117+
>>> base = 0x10000
2118+
>>> rom_base = 0xc0000000
2119+
>>> segments = Segment.serialize(image_base=base, start=base, length=0x1000, data_offset=0, data_length=0x1000, flags=SegmentFlag.SegmentReadable|SegmentFlag.SegmentExecutable)
2120+
>>> segments = Segment.serialize(image_base=base, start=rom_base, length=0x1000, flags=SegmentFlag.SegmentReadable, segments=segments)
2121+
>>> view = load(bytes.fromhex('5054ebfe'), options={'loader.imageBase': base, 'loader.platform': 'x86', 'loader.segments': segments})
2122+
>>> print(view.memory_map)
2123+
<region: 0x10000 - 0x10004>
2124+
size: 0x4
2125+
objects:
2126+
'origin<Mapped>' | Mapped<Absolute>
2127+
2128+
<region: 0xc0000000 - 0xc0001000>
2129+
size: 0x1000
2130+
objects:
2131+
'origin<Mapped>' | Unmapped | FILL<0x0>
2132+
2133+
<region: 0xc0001000 - 0xc0001014>
2134+
size: 0x14
2135+
objects:
2136+
'origin<Mapped>' | Unmapped | FILL<0x0>
2137+
>>> view.memory_map.add_memory_region("rom", rom_base, b'\x90' * 4096)
2138+
True
2139+
>>> print(view.memory_map)
2140+
<region: 0x10000 - 0x10004>
2141+
size: 0x4
2142+
objects:
2143+
'origin<Mapped>' | Mapped<Absolute>
2144+
2145+
<region: 0xc0000000 - 0xc0001000>
2146+
size: 0x1000
2147+
objects:
2148+
'rom' | Mapped
2149+
'origin<Mapped>' | Unmapped | FILL<0x0>
2150+
2151+
<region: 0xc0001000 - 0xc0001014>
2152+
size: 0x14
2153+
objects:
2154+
'origin<Mapped>' | Unmapped | FILL<0x0>
2155+
>>> view.read(rom_base, 16)
2156+
b'\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90'
2157+
>>> view.memory_map.add_memory_region("pad", rom_base, b'\xa5' * 8)
2158+
True
2159+
>>> view.read(rom_base, 16)
2160+
b'\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\x90\x90\x90\x90\x90\x90\x90\x90'
2161+
>>> print(view.memory_map)
2162+
<region: 0x10000 - 0x10004>
2163+
size: 0x4
2164+
objects:
2165+
'origin<Mapped>' | Mapped<Absolute>
2166+
2167+
<region: 0xc0000000 - 0xc0000008>
2168+
size: 0x8
2169+
objects:
2170+
'pad' | Mapped<Relative>
2171+
'rom' | Mapped<Relative>
2172+
'origin<Mapped>' | Unmapped | FILL<0x0>
2173+
2174+
<region: 0xc0000008 - 0xc0001000>
2175+
size: 0xff8
2176+
objects:
2177+
'rom' | Mapped<Relative>
2178+
'origin<Mapped>' | Unmapped | FILL<0x0>
2179+
2180+
<region: 0xc0001000 - 0xc0001014>
2181+
size: 0x14
2182+
objects:
2183+
'origin<Mapped>' | Unmapped | FILL<0x0>
2184+
"""
2185+
2186+
def __repr__(self):
2187+
return pprint.pformat(self.description())
2188+
2189+
def __str__(self):
2190+
description = self.description()
2191+
formatted_description = ""
2192+
for entry in description['MemoryMap']:
2193+
formatted_description += f"<region: {hex(entry['address'])} - {hex(entry['address'] + entry['length'])}>\n"
2194+
formatted_description += f"\tsize: {hex(entry['length'])}\n"
2195+
formatted_description += "\tobjects:\n"
2196+
for obj in entry['objects']:
2197+
if obj['target']:
2198+
mapped_state = f"Mapped<{'Absolute' if obj['absolute_address_mode'] else 'Relative'}>"
2199+
else:
2200+
mapped_state = "Unmapped"
2201+
formatted_description += f"\t\t'{obj['name']}' | {mapped_state}"
2202+
if not obj['target']:
2203+
formatted_description += f" | FILL<{hex(obj['fill'])}>"
2204+
if not obj['enabled']:
2205+
formatted_description += f" | <DISABLED>"
2206+
formatted_description += "\n"
2207+
formatted_description += "\n"
2208+
2209+
return formatted_description
2210+
2211+
def __init__(self, handle: 'BinaryView'):
2212+
self.handle = handle
2213+
2214+
def __len__(self):
2215+
mm_json = self.description()
2216+
if 'MemoryMap' in mm_json:
2217+
return len(mm_json['MemoryMap'])
2218+
else:
2219+
return 0
2220+
2221+
def description(self):
2222+
return json.loads(core.BNGetMemoryMapDescription(self.handle))
2223+
2224+
# // LoadBinary:
2225+
# // Loads a file in a loadable binary format.
2226+
# // Provides persistence, indicating that the loaded data will persist across sessions.
2227+
# // Presumably loads the file into memory according to the virtual and physical load addresses specified in the file itself.
2228+
# // LoadFile:
2229+
# // Loads a flat file or bytes directly at the specified address.
2230+
# // Provides persistence, suggesting that the loaded data will be saved and remain accessible across sessions.
2231+
# // Allows for direct loading of files or bytes into memory at a specified location.
2232+
# // LoadRemote:
2233+
# // Loads data from a remote source or interface.
2234+
# // Not persistent, implying that the loaded data is ephemeral and may not be saved across sessions.
2235+
# // Supports a target where bytes are provided/generated upon request, indicating a dynamic and potentially transient data source.
2236+
def add_memory_region(self, name: str, start: int, source: Union['os.PathLike', str, bytes, bytearray, 'BinaryView', 'databuffer.DataBuffer', 'fileaccessor.FileAccessor']) -> bool:
2237+
if isinstance(source, os.PathLike):
2238+
source = str(source)
2239+
if isinstance(source, bytes) or isinstance(source, bytearray):
2240+
source = databuffer.DataBuffer(source)
2241+
if isinstance(source, str):
2242+
with open(source, "rb") as f:
2243+
source = databuffer.DataBuffer(f.read())
2244+
2245+
if name is None:
2246+
name = ""
2247+
2248+
if isinstance(source, BinaryView):
2249+
return core.BNAddBinaryMemoryRegion(self.handle, name, start, source.handle)
2250+
elif isinstance(source, databuffer.DataBuffer):
2251+
return core.BNAddDataMemoryRegion(self.handle, name, start, source.handle)
2252+
elif isinstance(source, fileaccessor.FileAccessor):
2253+
return core.BNAddRemoteMemoryRegion(self.handle, name, start, source._cb)
2254+
else:
2255+
raise NotImplementedError
2256+
2257+
def remove_memory_region(self, name: str) -> bool:
2258+
return core.BNRemoveMemoryRegion(self.handle, name)
2259+
2260+
def is_memory_region_enabled(self, name: str, start: int) -> bool:
2261+
return core.BNIsMemoryRegionEnabled(self.handle, name, start)
2262+
2263+
def set_memory_region_enabled(self, name: str, start: int, enabled: bool = True) -> bool:
2264+
return core.BNSetMemoryRegionEnabled(self.handle, name, start, enabled)
2265+
2266+
def set_memory_region_fill(self, name: str, start: int, fill: int) -> bool:
2267+
return core.BNSetMemoryRegionFill(self.handle, name, start, fill)
2268+
2269+
def reset(self):
2270+
core.BNResetMemoryMap(self.handle)
2271+
21062272
class BinaryView:
21072273
"""
21082274
``class BinaryView`` implements a view on binary data, and presents a queryable interface of a binary file. One key
@@ -9464,6 +9630,9 @@ def get_external_locations(self) -> List[externallibrary.ExternalLocation]:
94649630
finally:
94659631
core.BNFreeExternalLocationList(handles, count.value)
94669632

9633+
@property
9634+
def memory_map(self):
9635+
return MemoryMap(handle=self.handle)
94679636

94689637
class BinaryReader:
94699638
"""

0 commit comments

Comments
 (0)