Source code for revenge.memory.memory


import logging
logger = logging.getLogger(__name__)

from prettytable import PrettyTable
import binascii
import operator
import struct
from termcolor import cprint, colored

from .. import common, types, symbols

[docs]class Memory(object): """Class to simplify getting and writing things to memory. Examples: .. code-block:: python3 # Read a signed 8-bit int from address memory[0x12345].int8 # Returns MemoryBytes object for memory memory[0x12345:0x12666] # Write int directly to memory memory[0x12345] = types.Int8(12) # Write string directly to memory memory[0x12345] = types.StringUTF8("hello!") """ def __init__(self, engine): self._engine = engine # Keep track of where we've inserted breakpoints # key == address of breakpoint, value == memory location to un-breakpoint it self._active_breakpoints = {} # Keep track of what we've allocated. # key == address of allocation, value = script where we allocated it. # NOTE: It's important to keep the script alive until we're done with the alloc or javascript might gc it. self._allocated_memory = {} # key == address of replaced function, value = tuple: what it's being replaced with, script so we can unload later self._active_replacements = {} # key == address of onEnter function, value = tuple: what it's being hooked with, script so we can unload later self._active_on_enter = {} """ def __new__(klass, process, engine=None): # If we're in a proper subclass, don't monkey with the engine if engine is False: return super(Memory, klass).__new__(klass) if engine is None: engine = process._engine mod = importlib.import_module('...engines.{engine}.memory'.format(engine=engine), package=__name__) return super(Memory, klass).__new__(mod.Memory) """
[docs] @common.implement_in_engine() def alloc(self, size): """Allocate size bytes of memory and get a MemoryBytes object back to use it. Args: size (int): How many bytes to allocate. Returns: revenge.memory.MemoryBytes: Object for the new memory location. """ pass
[docs] @common.validate_argument_types(struct=types.Struct) def alloc_struct(self, struct): """Short-hand to alloc appropriate space for the struct and write it in. Args: struct (revenge.types.Struct): The struct to write into memory. Returns: revenge.types.Struct: The original struct, but now bound to the new memory location. """ struct._process = self._process # Allocate the amount of size needed mem = self.alloc(struct.sizeof) # Write the struct in memory mem.struct = struct # Tell the struct object where the memory is struct.memory = mem # Return the struct return struct
[docs] def alloc_string(self, s, encoding='latin-1'): """Short-hand to run alloc of appropriate size, then write in the string. Args: s (bytes, str): String to allocate encoding (str, optional): How to encode the string if passed in as type str. """ # TODO: Smart guess encoding, linux is usually utf-8, Windows has function call to determine utf-8 vs 16. Mac...? if type(s) in [types.StringUTF8, types.StringUTF16]: if s.type == 'utf8': encoding = 'utf-8' elif s.type == 'utf16': encoding = 'utf-16' else: logger.error('How did i get here??') return s = str(s) if type(s) is str: s = s.encode(encoding) if encoding == 'utf-16': s = s[2:] # Remove BOM s += b'\x00' # Extra null at end of utf-16 if type(s) is not bytes: logger.error("Invalid string type of {}".format(type(s))) return None # Null terminate s += b'\x00' mem = self.alloc(len(s)) mem.bytes = s return mem
[docs] def find(self, *args, **kwargs): """Search for thing in memory. Must be one of the defined types.""" return self._MemoryFind(self._engine, *args, **kwargs)
[docs] def describe_address(self, address, color=False): """Takes in address and attempts to return a better description of what's there. Args: address (int): What address to describe color (bool, optional): Should the description be colored? (default: False) Returns: str: description of the address """ if isinstance(address, symbols.Symbol): address = address.address if isinstance(address, types.Telescope): address = int(address) assert isinstance(address, int), "Unexpected address type of {}".format(type(address)) desc = "" module = self._process.modules[address] if module is not None: if color: desc += colored(module.name,"magenta") or "" else: desc += module.name or "" try: # If we can find a closest function, use that. func_name, func_addr = next((name, addr.address) for name,addr in sorted(module.symbols.items(), key=operator.itemgetter(1),reverse=True) if address >= addr) func_name = str(func_name) offset = address - func_addr # This is probably not really the function if func_name.startswith('plt.') and offset >= 0x10: raise StopIteration if color: desc += ":" + colored(func_name, "magenta", attrs=["bold"]) else: desc += ":" + func_name except StopIteration: # We did not find a closest function, just offset from module base offset = address - module.base if offset != 0: if color: desc += "+" + colored(hex(offset), "cyan") else: desc += "+" + hex(offset) else: if color: desc += colored(hex(address), "cyan") else: desc += hex(address) return desc
def _type_to_search_string(self, thing): """Converts the given object into something relevant that can be fed into a memory search query.""" if not isinstance(thing, types.all_types): logger.error("Please use valid type.") return None endian_str = "<" if self._process.endianness == 'little' else '>' if isinstance(thing, types.StringUTF8): # Normal string return binascii.hexlify(thing.encode('utf-8')).decode() elif isinstance(thing, types.StringUTF16): # Wide Char String (Windows/UTF16) return binascii.hexlify(thing.encode('utf-16')[2:]).decode() elif isinstance(thing, types.UInt8): return binascii.hexlify(struct.pack(endian_str + "B", thing)).decode() elif isinstance(thing, types.Int8): return binascii.hexlify(struct.pack(endian_str + "b", thing)).decode() elif isinstance(thing, types.UInt16): return binascii.hexlify(struct.pack(endian_str + "H", thing)).decode() elif isinstance(thing, types.Int16): return binascii.hexlify(struct.pack(endian_str + "h", thing)).decode() elif isinstance(thing, types.UInt32): return binascii.hexlify(struct.pack(endian_str + "I", thing)).decode() elif isinstance(thing, types.Int32): return binascii.hexlify(struct.pack(endian_str + "i", thing)).decode() elif isinstance(thing, types.UInt64): return binascii.hexlify(struct.pack(endian_str + "Q", thing)).decode() elif isinstance(thing, types.Int64): return binascii.hexlify(struct.pack(endian_str + "q", thing)).decode() else: logger.error("Unexpected type to convert of {}".format(type(thing))) return None def __getitem__(self, item): if type(item) == str: # Assume it's something we need to resolve item = self._process.modules.lookup_symbol(item) if isinstance(item, symbols.Symbol): item = item.address if isinstance(item, types.Telescope): item = int(item) if isinstance(item, int): return self._MemoryBytes(self._engine, item) elif type(item) == slice: if item.start is None or item.stop is None or item.step is not None: logger.error("Memory slices must have start and stop and not contain a step option.") return return self._MemoryBytes(self._engine, item.start, item.stop) logger.error("Unhandled memory type of {}".format(type(item))) def __setitem__(self, index, value): if not isinstance(value, types.all_types): logger.error("When implicitly memory writing, you MUST use an instantiation of revenge.types.*") return # Grab the mem mem = self._engine.memory[index] # TODO: Implement char? if isinstance(value, types.Struct): mem.struct = value elif isinstance(value, types.Int8): mem.int8 = value elif isinstance(value, types.UInt8): mem.uint8 = value elif isinstance(value, types.Int16): mem.int16 = value elif isinstance(value, types.UInt16): mem.uint16 = value elif isinstance(value, types.Int32): mem.int32 = value elif isinstance(value, types.UInt32): mem.uint32 = value elif isinstance(value, types.Int64): mem.int64 = value elif isinstance(value, types.UInt64): mem.uint64 = value elif isinstance(value, types.Double): mem.double = value elif isinstance(value, types.Float): mem.float = value elif isinstance(value, types.Pointer): mem.pointer = value elif isinstance(value, types.StringUTF8): mem.string_utf8 = value elif isinstance(value, types.StringUTF16): mem.string_utf16 = value else: logger.error("Unhandled memory write type of {}".format(type(value))) @property def maps(self): """Return a list of memory ranges that are currently allocated.""" return self._MemoryMap(self._engine) def __str__(self): table = PrettyTable(['range', 'prot', 'file']) table.header = False table.align = 'l' table.border = False for range in self.maps: table.add_row([ hex(range.base)[2:] + '-' + hex(range.base+range.size)[2:], range.protection, range.file or '', ]) return str(table) @property def _process(self): return self._engine._process
from ..exceptions import * #Memory.find.__doc__ = MemoryFind.__init__.__doc__