|
26 | 26 | import ctypes |
27 | 27 | import abc |
28 | 28 | import json |
| 29 | +import pprint |
29 | 30 | import inspect |
30 | 31 | import os |
31 | 32 | import uuid |
@@ -2103,6 +2104,171 @@ def __iter__(self): |
2103 | 2104 | yield self._func_queue.popleft().function |
2104 | 2105 |
|
2105 | 2106 |
|
| 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 | + |
2106 | 2272 | class BinaryView: |
2107 | 2273 | """ |
2108 | 2274 | ``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]: |
9464 | 9630 | finally: |
9465 | 9631 | core.BNFreeExternalLocationList(handles, count.value) |
9466 | 9632 |
|
| 9633 | + @property |
| 9634 | + def memory_map(self): |
| 9635 | + return MemoryMap(handle=self.handle) |
9467 | 9636 |
|
9468 | 9637 | class BinaryReader: |
9469 | 9638 | """ |
|
0 commit comments