reven2.trace.Context(object)
class documentationreven2.trace
(View In Hierarchy)
Entry point object for data related to a context.
A context represents the full state of the VM at a point in time. You can access contexts before or after a transition:
>>> ... -> Context Before #N -> Transition #N -> Context After #N ->
A context can usually be referenced as both a context before and a context after:
>>> ... -> Transition #N-1 -> Context After #N-1 / Before #N -> Transition #N -> ...
Except at the trace's begin and end:
>>> Context Before #0 -> Transition #0 -> ... -> Transition #M -> Context After #M
This object is not meant to be constructed directly. Use Trace.context_before
,
Trace.context_after
,
Transition.context_before
or Transition.context_after
instead.
>>> # From a transition >>> reven_server.trace.transition(id).context_before() >>> reven_server.trace.transition(id).context_after()
>>> # From a trace directly >>> reven_server.trace.context_before(transition_id) >>> reven_server.trace.context_after(transition_id)
>>> # From a context >>> ctx = reven_server.trace.context_before(transition_id) >>> next_ctx = ctx + 1 >>> prev_ctx = ctx - 1 >>> other_ctx = ctx + 10000
Method | __init__ | Undocumented |
Method | transition_before | Get Transition that led to this context |
Method | transition_after | Get Transition that will execute right after this |
Method | ossi | Property: OSSI information related to the context |
Method | stack | Property: The active stack linked to the context. |
Method | is64b | Undocumented |
Method | physical_memory_regions | Get all the physical memory regions that are accessible. |
Method | read | read(register [, raw=False]) -> read bytes from a register read(source, size[, raw=False]) -> read bytes from a register or a memory read(source, type) -> read bytes from a register or a memory |
Method | deref | Recursively dereferences a pointer and read the value read as a pointing type. |
Method | find_register_change | Find the next/previous context in which the content of the register is modified. |
Method | __str__ | Undocumented |
Method | __repr__ | Undocumented |
Method | __eq__ | Undocumented |
Method | __ne__ | Undocumented |
Method | __lt__ | Undocumented |
Method | __le__ | Undocumented |
Method | __gt__ | Undocumented |
Method | __ge__ | Undocumented |
Method | __add__ | Undocumented |
Method | __sub__ | Undocumented |
Method | _trace | Undocumented |
Method | _read_size | Undocumented |
Method | _read_as | Undocumented |
Method | _read_raw | Undocumented |
Method | _read_st_register | Undocumented |
Method | _read_register | Undocumented |
Method | _rvn | Undocumented |
Get Transition that led to this context
>>> Transition Before -> This context
Returns | A Transition . | |
Raises | IndexError | if called on the context before the first transition |
Get Transition that will execute right after this
>>> This context -> Transition After
Returns | A Transition . | |
Raises | IndexError | if called on the context after the last transition |
Property: OSSI information related to the context
>>> # the current symbol >>> print(context.ossi.location()) 'ntoskrnl!KiIsrLinkage+0x10'
Returns | A reven2.ossi.OssiContext . |
Property: The active stack linked to the context.
>>> # the active stack >>> print(context.stack.backtrace) [0] #2753249448 - ntoskrnl!KeZeroPages [1] #2753249199 - ntoskrnl!MiZeroPhysicalPage [2] #2753248836 - ntoskrnl!MiResolveDemandZeroFault [3] #2753248607 - ntoskrnl!MmAccessFault [4] #2753248552 - ntoskrnl!KiPageFault [5] ??? - ntoskrnl!KiSystemCall64+0x15
Returns | A reven2.stack.Stack . |
Get all the physical memory regions that are accessible.
These regions are assumed to not change during the entire trace.
Any physical read inside these regions is guaranteed to succeed.
>>> for (start, size) in ctx.physical_memory_regions(): >>> print("{} - {}".format(start, start + size)) phy:0x0 - phy:0x80000000 phy:0xfd000000 - phy:0xfe000000
Returns | A generator of tuples composed of the start physical address of a region and its size. |
read(register [, raw=False]) -> read bytes from a register read(source, size[, raw=False]) -> read bytes from a register or a memory read(source, type) -> read bytes from a register or a memory
If 'size' in [1, 2, 4, 8], the bytes are interpreted as an
unsigned int
. If 'raw' is True
, a raw buffer is
returned even if 'size' is in [1, 2, 4, 8]. If 'type' is provided (a reven2.types.Type
),
the bytes are interpreted as the given 'type'. In all other cases, a raw
buffer is returned.
If 'source' is a virtual address, then [source; length] must be mapped in the current context, with `length == size` if 'size' was provided, or `length == type.size_bytes(self)` if `type` was provided
If 'source' is a physical address, then [source; length] must be in the
available physical memory (see Context.physical_memory_regions
),
with `length == size` if 'size' was provided, or `length ==
type.size_bytes(self)` if `type` was provided
Read rax without size:
>>> hex(context.read(arch.x64.rax)) '0xffffc000e9997ac0'
>>> Get a raw buffer >>> context.read(arch.x64.rax, raw=True) bytearray(b'\xc0z\x99\xe9\x00\xc0\xff\xff')
Read rax with size:
>>> # size not in [1, 2, 4, 8] >>> context.read(arch.x64.rax, 6) bytearray(b'\xc0z\x99\xe9\x00\xc0')
>>> # size in [1, 2, 4, 8] >>> hex(context.read(arch.x64.rax, 4)) '0xe9997ac0'
>>> # Get a raw buffer >>> hex(context.read(arch.x64.rax, 4, raw=True)) bytearray(b'\xc0z\x99\xe9')
Read rax as a U64 integer:
>>> hex(context.read(arch.x64.rax, types.U64)) '0xffffc000e9997ac0'
Read rax as two U8 integers:
>>> context.read(arch.x64.rax, types.Array(types.U16, 2)) ListContainer([0xe999, 0x7ac0])
Read a buffer in memory at address ds:0x1234:
>>> address = address.LogicalAddress(0x1234)
>>> # size not in [1, 2, 4, 8] >>> context.read(address, 6) bytearray(b'\xc0z\x99\xe9\x00\xc0')
>>> # size in [1, 2, 4, 8] >>> hex(context.read(address, 8)) '0xffffc000e9997ac0'
>>> # Get a raw buffer >>> context.read(address, 8, raw=True) bytearray(b'\xc0z\x99\xe9\x00\xc0\xff\xff')
Read a string in memory at address ds:0x1234:
>>> address = address.LogicalAddress(0x1234) >>> WString1000 = types.CString(encoding=types.Encoding.Utf16, max_character_count=1000) >>> context.read(address, WString1000) 'Hello World'
Parameters | source | From where the bytes are read. Can be an address (virtual or physical) or a
Register . |
type | A reven2.types.Type . See
the reven2.types package
documentation for more information on types. | |
size | The byte size to read. If no 'size' and 'source' is a
reven2.arch.Register , then the Register.size_bytes
is used. | |
raw | Force the return type to be a raw buffer of 'size' bytes. | |
Raises | TypeError | if 'source' is not supported. |
TypeError | if 'type' is not a Type . | |
ValueError | if 'source' is an address and 'size' or 'type' are not
provided. | |
ValueError | if 'source' is a Register and 'size' is bigger than the
register byte size. | |
RuntimeError | if 'source' is an unavailable register. | |
RuntimeError | if 'source' is a virtual address and is not mapped in the current context. | |
RuntimeError | if 'source' is a physical address and is outside the physical memory. |
Recursively dereferences a pointer and read the value read as a pointing type.
This method allows to directly returns the pointed-to value when passed a pointer type and a source from which read the pointer address.
A (simplified) desugaring of this method can be thought of like the following:
>>> addr = self.read(source, ptr_ty) >>> return self.read(addr, ptr_ty.elem_type)
Note, however that the actual implementation will recursively remove all levels of indirection, e.g. if the type is Pointer(Pointer(U64)), then the implementation will do the following:
>>> addr1 = self.read(source, Pointer(Pointer(U64))) >>> addr2 = self.read(addr1, Pointer(U64)) >>> return self.read(addr2, U64)
Reading the stack pointer:
>>> hex(context.deref(reven2.arch.x64.rsp, types.Pointer(types.USize))) '0xfffff801653ff072L'
Looks like an address, let's try to dereference twice:
>>> hex(context.deref(reven2.arch.x64.rsp, types.Pointer(types.Pointer(types.USize)))) '0x7c894828247c894c'
Doesn't look like an address anymore, show raw:
>>> [hex(x) for x in context.deref(reven2.arch.x64.rsp, types.Pointer(types.Pointer(types.RawBuffer(8))))] ['0x4c', '0x89', '0x7c', '0x24', '0x28', '0x48', '0x89', '0x7c']
Parameters | source | The source where to read the address of the pointer. Can be an address (virtual or physical) or a register. |
ptr_ty | A reven2.types.Pointer . | |
Returns | The data interpreted as the requested type. If the pointer points to an
unmapped memory, None is returned. | |
Raises | TypeError | if 'source' is not supported. |
TypeError | if 'ptr_ty' is not a Pointer . | |
ValueError | if 'source' is a Register and there is not enough bytes to
read the pointer address in it. | |
RuntimeError | if 'source' is an unavailable register. |
Find the next/previous context in which the content of the register is modified.
Parameters | register | reven2.arch.Register |
is_forward | If True return next context. Otherwise return previous
context. | |
fetch_count | Technical parameter indicating how many contexts the server should handle
per query. Modifying this parameter allows to fine-tune performance:
NOTE: Modifying this parameter does not modify the results of the query | |
Returns | A reven2.trace.Context
or None if no context was found. |