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
Method transition_count Property: Count of transitions 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 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
Method _rvn Undocumented
Method _context Undocumented
def __init__(self, _rvn, _ossi_data_source):
Undocumented
@property
def _rvn(self):
Undocumented
@property
def transition_count(self):

Property: Count of transitions in this trace.

Information

Returnsan integer.
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.
@property
def search(self):

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, size, 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 trace for range [0xffff88007fc03000, 0xffff88007fc04000)
>>> # (should always be fast):
>>> for (_, access) in itertools.izip(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=MemoryAccessesOperation.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.
sizeSize of the searched range of addresses.
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.
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\x00' 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 at 2019-11-13 18:38:15.