class documentation

class Trace(object):

View In Hierarchy

Entry point object for information related to a trace, mostly transitions & contexts.

The trace is represented as a series of Transitions objects (which are CPU instruction most of the time) that take the virtual machine from one state (Context object "before") to the next (Context object "after").

Warnings

This object is not meant to be constructed directly. Use RevenServer.trace instead.

Examples

>>> # From a reven_server
>>> trace = reven_server.trace
>>> print("Trace is {} transitions long".format(trace.transition_count()))
>>> print("From {} to {} via {}.format(trace.context_before(0), trace.context_after(0), trace.transition(0)))
>>> print(trace.transition(trace.transition_count() - 1)
Method __init__ Undocumented
Property transition_count Property: Count of transitions in this trace.
Property first_transition Property: First transition in this trace.
Property last_transition Property: Last transition in this trace.
Property first_context Property: First context in this trace.
Property last_context Property: Last context in this trace.
Method transition Retrieve a transition from its unique id.
Method transitions Get a generator over the transitions in the trace.
Method context_before Retrieve the context before a transition from the latter's unique id.
Method context_after Retrieve the context after a transition from the latter's unique id.
Method contexts Get a generator over the contexts in the trace.
Method filter Get all context ranges that are associated to the given processes and given ring policy
Property search Property: Get a reven2.search.Search instance, which is the entry point to find interesting points in the trace.
Method memory_accesses Get a generator over the previous/next memory accesses to a range of addresses defined by (address, size).
Method strings Get a generator over the strings matching the pattern
Method __repr__ Undocumented
Instance Variable _data_source Undocumented
Instance Variable _ossi_data_source Undocumented
Property _rvn Undocumented
Method _context Undocumented
def __init__(self, _rvn, _ossi_data_source):

Undocumented

_data_source =

Undocumented

_ossi_data_source =

Undocumented

@property
_rvn =

Undocumented

@property
transition_count =

Property: Count of transitions in this trace.

Information

Returnsan integer.
@property
first_transition =

Property: First transition in this trace.

Information

Returnsa reven2.trace.Transition.
@property
last_transition =

Property: Last transition in this trace.

Information

Returnsa reven2.trace.Transition.
@property
first_context =

Property: First context in this trace.

Information

Returnsa reven2.trace.Context.
@property
last_context =

Property: Last context in this trace.

Information

Returnsa reven2.trace.Context.
def transition(self, transition_id):

Retrieve a transition from its unique id.

Information

ReturnsA Transition.
RaisesIndexErrorif the unique id does not belong to the trace.
def transitions(self, start=None, stop=None, step=1):

Get a generator over the transitions in the trace.

Examples

Iterates on transitions from the beginning to the end of the trace.

>>> for transition in trace.transitions():
>>>     print(transition)
#0 push -0x2f
#1 push rbp
#2 jmp 0xfffff800c99455a0 ($+0x185)
#3 push rsi
#4 sub rsp, 0x150
#5 lea rbp, [rsp + 0x80]
...

Iterates on transitions from the end to the beginning of the trace.

>>> for transition in trace.transitions(step=-1):
>>>     print(transition)
#2847570054 je 0x7fff599d16d8 ($+0x270)
#2847570053 cmp rdi, rax
#2847570052 mov dword ptr [rbp - 0x61], r14d
#2847570051 mov r13d, r14d
#2847570050 mov dword ptr [rbp - 0x65], ecx
#2847570049 mov ecx, 0x10
...

Iterates on the 5 first transitions in increasing order.

>>> for transition in trace.transitions(stop=5):
>>>     print(transition)
#0 push -0x2f
#1 push rbp
#2 jmp 0xfffff800c99455a0 ($+0x185)
#3 push rsi
#4 sub rsp, 0x150

Iterates on 10 transitions somewhere in the trace, in increasing order, and 2 by 2.

>>> start = trace.transition(1000)
>>> stop = start + 10
>>> for transition in trace.transitions(start, stop, 2):
>>>     print(transition)
#1000 mov r12, rax
#1002 jne 0xfffff800c98053fc ($+0x81)
#1004 mov rcx, rbx
#1006 mov qword ptr [rsp + 8], rbx
#1008 sub rsp, 0x20

Iterates on the 5 last transitions in decreasing order.

>>> for transition in trace.transitions(stop=trace.transition_count - 6, step=-1):
>>>     print(transition)
#2847570054 je 0x7fff599d16d8 ($+0x270)
#2847570053 cmp rdi, rax
#2847570052 mov dword ptr [rbp - 0x61], r14d
#2847570051 mov r13d, r14d
#2847570050 mov dword ptr [rbp - 0x65], ecx

Information

ParametersstartA Transition or an int(transition id) at which to start the generation. If start is None, then: * if step > 0, then start at the beginning of the trace. * if step < 0, then start at the end of the trace. start must be in trace (in [0, trace.transition_count - 1]).
stopA Transition or an int(transition id) at which to stop the generation. The stop is excluded from the generation. If stop is None, then: * if step > 0, then stop at the end of the trace. * if step < 0, then stop at the beginning of the trace. stop - 1 must be in trace (not in [0, trace.transition_count - 1]).
stepAn int which indicates the increment (or decrement) between generated Transitions. If 'step' > 0, then transitions are yielded in increasing order. If 'step' < 0, then transitions are yielded in decreasing order. Default is set to 1.
Returnsa generator of Transition.
RaisesTypeErrorif any argument is of the wrong type.
ValueErrorif one of the following cases: * step == 0. * step > 0 and start > stop. * step < 0 and start < stop.
IndexErrorif one of the following cases: * start is not in the trace (not in [0, trace.transition_count - 1]). * step > 0 and stop - 1 is not in the trace (not in [0, trace.transition_count - 1]). * step < 0 and stop + 1) is not in the trace (not in [0, trace.transition_count - 1]).
def context_before(self, transition_id):

Retrieve the context before a transition from the latter's unique id.

Information

ReturnsA Context.
RaisesIndexErrorif the unique id does not belong to the trace.
def context_after(self, transition_id):

Retrieve the context after a transition from the latter's unique id.

Information

ReturnsA Context.
RaisesIndexErrorif the unique id does not belong to the trace
def contexts(self, start=None, stop=None, step=1):

Get a generator over the contexts in the trace.

Examples

Iterates on contexts from the beginning to the end of the trace.

>>> for context in trace.contexts():
>>>     print(context)
Context before #0
Context before #1
Context before #2
Context before #3
Context before #4
Context before #5
Context before #6
...

Iterates on contexts from the end to the beginning of the trace.

>>> for context in trace.contexts(step=-1):
>>>     print(context)
Context after #2847570054
Context before #2847570054
Context before #2847570053
Context before #2847570052
Context before #2847570051
Context before #2847570050
...

Iterates on the 5 first contexts in increasing order.

>>> stop = trace.context_before(5)
>>> for context in trace.context(stop=stop):
>>>     print(context)
Context before #0
Context before #1
Context before #2
Context before #3
Context before #4

Iterates on 10 contexts somewhere in the trace, in increasing order, and 2 by 2.

>>> start = trace.context_before(1000)
>>> stop = start + 10
>>> for context in trace.context(start, stop, 2):
>>>     print(context)
Context before #1000
Context before #1002
Context before #1004
Context before #1006
Context before #1008

Iterates on the 5 last contexts in decreasing order.

>>> stop = trace.context_after(trace.transition_count - 5)
>>> for context in trace.contexts(stop=stop, step=-1):
>>>     print(context)
Context after #2847570054
Context before #2847570054
Context before #2847570053
Context before #2847570052
Context before #2847570051

Information

ParametersstartA Context at which to start the generation. If start is None, then: * start at the beginning of the trace if step > 0. * start at the end of the trace if step < 0.
stopA Context at which to stop the generation. The stop is excluded from the generation. If stop is None, then: * stop at the end of the trace if step > 0. * stop at the beginning of the trace if step < 0.
stepAn int which indicates the increment (or decrement) between generated Contexts. If 'step' > 0, then contexts are yielded in increasing order. If 'step' < 0, then contexts are yielded in decreasing order. Default is set to 1.
Returnsa generator of Context.
RaisesTypeErrorif any argument is of the wrong type.
ValueErrorif one of the following cases: * step == 0. * step > 0 and start > stop. * step < 0 and start < stop.
def filter(self, processes=None, ring_policy=_filter.RingPolicy.All, from_context=None, to_context=None, is_forward=True):

Get all context ranges that are associated to the given processes and given ring policy

A valid list of `ossi.Process` object must be given. To get one, use the `Ossi.executed_processes` method.

Warnings

Depends on the ossi range resource. If unavailable, this function will raise an error.

Examples

>>> # iterate on ranges in ring 0
>>> for range in reven_server.trace.filter(ring_policy=reven2.filter.RingPolicy.R0Only):
>>>     print(range)
[Context before #853196, Context before #857514]
[Context before #5113265, Context before #5118308]
[Context before #8202545, Context before #8208679]
[Context before #16222616, Context before #16229012]
>>> # iterate on any contexts associated to process `foo.exe` with pid 1234
>>> process = next(reven_server.ossi.executed_processes('foo.exe', 1234))
>>> for range in reven_server.trace.filter([process]):
>>>     for context in range:
>>>         print(context)
Context before #1000
Context before #1001
Context before #1002
Context before #1003
Context before #1004
Context before #1005
...

Information

Parametersprocessesthe list of process to filter. If None, all processes will be filtered. (type: _Optional[_Iterable[_ossi.Process]])
ring_policythe rings to filter. Muste be a filter.RingPolicy value. By default, all rings will be filtered. (type: _filter.RingPolicy)
from_contextthe first included context where the filter starts. Must be a trace.Context. If None, start at the first context of trace. (type: _Optional[Context])
to_contextthe first excluded context where the filter stops. Must be a trace.Context. If None, stop at the last context of trace (included). `from_context` must be strictly lower than `to_context`. (type: _Optional[Context])
is_forwardeither the results are returned in the forward or backward order. By default, the forward order is used. (type: bool)
ReturnsA generator of trace.ContextRange if `is_forward` equal `True`. Otherwise, a generator of trace.BackwardContextRange. (type: _Union[_Iterator[ContextRange], _Iterator[BackwardContextRange]])
RaisesValueErrorif `to_context` is lower or equal to `from_context`.
RuntimeErrorif the ossi ranges ressource is unavailable.
@property
search =

Property: Get a reven2.search.Search instance, which is the entry point to find interesting points in the trace.

Information

ReturnsA reven2.search.Search instance.
def memory_accesses(self, address=None, size=None, from_transition=None, to_transition=None, is_forward=True, operation=None, fetch_count=1000):

Get a generator over the previous/next memory accesses to a range of addresses defined by (address, size).

Performance

Returning a generator rather than a list allow to lazily compute the requested results. Calling next on the generator to get the next individual result should always be fast. Forcing eager computations of the results by converting the iterator to a list might be slow depending on the number of results.

Examples

>>> # Getting the first 10 accesses in the entire trace
>>> # (should always be fast):
>>> for (_, access) in zip(range(0, 10), trace.memory_accesses()):
>>>     print(access)
[#0 mov qword ptr [rsp+0x10], rdx]Write access at @phy:0x668f4ea8 (virtual address: lin:0x14fea8) of size 8
[#1 mov dword ptr [rsp+0x8], ecx]Write access at @phy:0x668f4ea0 (virtual address: lin:0x14fea0) of size 4
[#3 mov rax, qword ptr [rip+0x3eb1]]Read access at @phy:0x70b8c068 (virtual address: lin:0x140005068) of size 8
[#5 mov qword ptr [rsp+0x240], rax]Write access at @phy:0x668f4e80 (virtual address: lin:0x14fe80) of size 8
[#6 cmp dword ptr [rsp+0x260], 0x2]Read access at @phy:0x668f4ea0 (virtual address: lin:0x14fea0) of size 4
[#10 mov rcx, qword ptr [rsp+0x268]]Read access at @phy:0x668f4ea8 (virtual address: lin:0x14fea8) of size 8
[#11 mov rcx, qword ptr [rcx+rax*1]]Read access at @phy:0x5cf92028 (virtual address: lin:0x503028) of size 8
[#12 call qword ptr [rip+0x1ebe]]Read access at @phy:0x681210b8 (virtual address: lin:0x1400030b8) of size 8
[#12 call qword ptr [rip+0x1ebe]]Write access at @phy:0x668f4c38 (virtual address: lin:0x14fc38) of size 8
[#14 and qword ptr [rsp+0x28], 0x0]Read access at @phy:0x668f4c28 (virtual address: lin:0x14fc28) of size 8
>>> # Getting the accesses on the transition range [1000, 1010[
>>> # (should always be fast):
>>> for access in trace.memory_accesses(from_transition=trace.transition(1000),
                                        to_transition=trace.transition(1010)):
>>>     print(access)
[#1005 mov qword ptr [rsp+0x40], r14]Write access at @phy:0x6645a9b0
        (virtual address: lin:0xfffffe0ff31db9b0) of size 8
[#1007 mov qword ptr [rsp+0x98], r14]Write access at @phy:0x6645aa08
        (virtual address: lin:0xfffffe0ff31dba08) of size 8
[#1008 or dword ptr [rsp+0x90], 0x2]Read access at @phy:0x6645aa00
        (virtual address: lin:0xfffffe0ff31dba00) of size 4
[#1008 or dword ptr [rsp+0x90], 0x2]Write access at @phy:0x6645aa00
        (virtual address: lin:0xfffffe0ff31dba00) of size 4
>>> # Getting the first 10 accesses in the trace for range [0xffff88007fc03000, 0xffff88007fc04000)
>>> # (should always be fast):
>>> for (_, access) in zip(range(0, 10), trace.memory_accesses(0xffff88007fc03000, 4096,
>>> trace.transition(0))):
>>>     print(access)
[#39 call 0xffffffff81611fe0 ($+0x165133)]Write access at @phy:0x7fc03ec8 (virtual address:
        lin:0xffff88007fc03ec8) of size 8
[#41 call qword ptr [0xffffffff81c24448]]Write access at @phy:0x7fc03ec0 (virtual address:
        lin:0xffff88007fc03ec0) of size 8
[#42 push rdx]Write access at @phy:0x7fc03eb8 (virtual address: lin:0xffff88007fc03eb8) of size 8
[#48 pop rdx]Read access at @phy:0x7fc03eb8 (virtual address: lin:0xffff88007fc03eb8) of size 8
[#49 ret ]Read access at @phy:0x7fc03ec0 (virtual address: lin:0xffff88007fc03ec0) of size 8
[#51 push rdi]Write access at @phy:0x7fc03ec0 (virtual address: lin:0xffff88007fc03ec0) of size 8
[#52 popfq ]Read access at @phy:0x7fc03ec0 (virtual address: lin:0xffff88007fc03ec0) of size 8
[#54 ret ]Read access at @phy:0x7fc03ec8 (virtual address: lin:0xffff88007fc03ec8) of size 8
[#60 call 0xffffffff814abe30 ($-0x108f)]Write access at @phy:0x7fc03ec8 (virtual address:
        lin:0xffff88007fc03ec8) of size 8
[#62 push r14]Write access at @phy:0x7fc03ec0 (virtual address: lin:0xffff88007fc03ec0) of size 8
>>> # Getting the number of accesses in the trace for range [0xffff88007fc03000, 0xffff88007fc04000)
>>> # (can be slow if there are numerous accesses):
>>> len(list(trace.memory_accesses(0xffff88007fc03000, 4096, trace.transition(0))))
28413
>>> # Getting the last access in the trace to phy:0x36f05080 (will always be fast):
>>> last = trace.transition(trace.transition_count - 1)
>>> address = reven2.address.PhysicalAddress(0x36f05080)
>>> next(trace.memory_accesses(address, 1, last, is_forward=False))
MemoryAccess(transition=Transition(id=3515211), physical_address=PhysicalAddress(offset=0x36f05080), size=8,
        operation=MemoryAccessOperation.Write, virtual_address=None)

Information

ParametersaddressFirst address of the searched range of addresses. Can be a class from the reven2.address module, or an integer. If an integer x, it will be interpreted as ds:x. If a virtual address, then it must be mapped (possibly to non-contiguous physical pages) at the context just before/after (depending on the direction) the from_transition. If None, return accesses regardless of their address.
sizeSize of the searched range of addresses. If None and address not None, a default size of 1 will be used.
from_transitionTransition at which to start the search. The from_transition is included in the search results If None, start from the beginning/end of the trace in forward/backward direction, respectively. If specified, must be lower than/greater than to_transition, depending on the direction
to_transitionTransition at which to stop the search. The to_transition is excluded from the search results If None, stop to the end/beginning of the trace in forward/backward direction, respectively. If specified, must be greater than/lower than from_transition, depending on the direction
is_forwardIf True, return the accesses at the same transition and after from_transition. Otherwise, return the accesses at the same transition and before from_transition.
operationOnly return accesses whose operation equals the specified reven2.memhist.MemoryAccessOperation. If None, return accesses regardless of their operation.
fetch_countTechnical parameter indicating how many accesses should be fetched from the server per query. Modifying this parameter allows to fine-tune performance according to the current use-case:
  • If you intend to make a lot of queries that you expect to return only a few accesses, then you should use a small value (e.g., 100)
  • If you intend to make a few queries that you expect to return a lot of accesses, then you should use a large value (e.g., 10000)
  • The default value should provide sufficient performance in most cases.

NOTE: Modifying this parameter does not modify the results of the query

Returnsa generator of reven2.memhist.MemoryAccess.
RaisesRuntimeErrorif the memory history resource has not been generated.
RuntimeErrorif the passed address is virtual, and not mapped at the context before/after the from_transition (depending on the direction).
ValueErrorif size < 0.
ValueErrorif from_transition > to_transition in forward, or from_transition < to_transition in backward.
ValueErrorif fetch_count <= 0.
ValueErrorif size is not None but address is None.
def _context(self, context_id):

Undocumented

def strings(self, pattern='', fetch_count=200):

Get a generator over the strings matching the pattern

Performance

Returning a generator rather than a list allow to lazily compute the requested results. Calling next on the generator to get the next individual result should always be fast. Forcing eager computations of the results by converting the iterator to a list might be slow depending on the number of results.

Examples

>>> # Getting strings matching '*hello*' in the trace
>>> for string in trace.strings("hello")
>>>     print(string)
[#2545492692 - #2545492932] 'D:\>hello' of size 20 at address @lin:0xfffff90140081038
[#2583245618 - #2583245653] 'hello.exe' of size 18 at address @lin:0x15a2de19930
[#2583245992 - #2583246162] 'HELLO' of size 5 at address @lin:0xffffb0000f2331d2
[#2583245995 - #2583246018] 'Hello\0' of size 12 at address @lin:0xffffc000ea501fd4
>>> # Getting accesses of the string:
>>> string = next(strings)
>>> for memory_access in string.memory_accesses():
>>>     print(memory_access)
[#2594829 movdqu xmm0, xmmword ptr [rdx + rcx]]Write access at @phy:0x5274cfc0
        (virtual address: lin:0xffffc000e9dd2fc0) of size 16
[#2594830 movdqu xmm1, xmmword ptr [rdx + rcx + 0x10]]Read access at @phy:0x5274cfd0
        (virtual address: lin:0xffffc000e9dd2fd0) of size 16
[#2594836 movdqu xmm0, xmmword ptr [rdx + rcx]]Read access at @phy:0x5274cfe0
        (virtual address: lin:0xffffc000e9dd2fe0) of size 16
[#2594837 movdqu xmm1, xmmword ptr [rdx + rcx + 0x10]]Write access at @phy:0x5274cff0
        (virtual address: lin:0xffffc000e9dd2ff0) of size 8

Information

Parameterspatterna string pattern existing in the searched string. If "", match with all strings The search will match strings like *[pattern]* Note: A long pattern may impact on perfomance with large database
fetch_countTechnical parameter indicating how many strings should be fetched from the server per query. Modifying this parameter allows to fine-tune performance according to the current use-case:
  • If you intend to make a lot of queries that you expect to return only a few strings, then you should use a small value (e.g., 20)
  • If you intend to make a few queries that you expect to return a lot of strings, then you should use a large value (e.g., 1000)
  • The default value should provide sufficient performance in most cases.

NOTE: Modifying this parameter does not modify the results of the query.

Returnsa generator of reven2.string.String.
RaisesRuntimeErrorif the strings resource has not been generated.
ValueErrorif fetch_count <= 0.
def __repr__(self):

Undocumented

API Documentation for reven2, generated by pydoctor 21.2.2 at 2021-10-01 07:18:12.