reven2.trace.Trace(object)
class documentationreven2.trace
(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").
This object is not meant to be constructed directly. Use RevenServer.trace
instead.
>>> # 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 |
Property: Count of transitions in this trace.
Returns | an integer. |
Retrieve a transition from its unique id.
Returns | A Transition . | |
Raises | IndexError | if the unique id does not belong to the trace. |
Get a generator over the transitions in the trace.
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
Parameters | start | A 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]). |
stop | A 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]). | |
step | An int which indicates the increment (or decrement) between
generated Transition s. 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. | |
Returns | a generator of Transition . | |
Raises | TypeError | if any argument is of the wrong type. |
ValueError | if one of the following cases: * step == 0 . * step >
0 and start > stop . * step < 0 and
start < stop . | |
IndexError | if 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]). |
Retrieve the context before a transition from the latter's unique id.
Returns | A Context . | |
Raises | IndexError | if the unique id does not belong to the trace. |
Retrieve the context after a transition from the latter's unique id.
Returns | A Context . | |
Raises | IndexError | if the unique id does not belong to the trace |
Get a generator over the contexts in the trace.
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
Parameters | start | A 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. |
stop | A 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. | |
step | An int which indicates the increment (or decrement) between
generated Context s. 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. | |
Returns | a generator of Context . | |
Raises | TypeError | if any argument is of the wrong type. |
ValueError | if one of the following cases: * step == 0 . * step >
0 and start > stop . * step < 0 and
start < stop . |
Property: Get a reven2.search.Search
instance, which is the entry point to find interesting points in the
trace.
Returns | A reven2.search.Search
instance. |
Get a generator over the previous/next memory accesses to a range of addresses defined by (address, size).
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.
>>> # 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)
Parameters | address | First 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. |
size | Size of the searched range of addresses. | |
from_transition | Transition 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_transition | Transition 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_forward | If True , return the accesses at the same transition and after
from_transition. Otherwise, return the accesses at the same transition and
before from_transition. | |
operation | Only return accesses whose operation equals the specified reven2.memhist.MemoryAccessOperation .
If None, return accesses regardless of their operation. | |
fetch_count | Technical 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:
NOTE: Modifying this parameter does not modify the results of the query | |
Returns | a generator of reven2.memhist.MemoryAccess . | |
Raises | RuntimeError | if the memory history resource has not been generated. |
RuntimeError | if the passed address is virtual, and not mapped at the context before/after the from_transition (depending on the direction). | |
ValueError | if size < 0. | |
ValueError | if from_transition > to_transition in forward, or from_transition < to_transition in backward. | |
ValueError | if fetch_count <= 0. |
Get a generator over the strings matching the pattern
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.
>>> # 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
Parameters | pattern | a 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_count | Technical 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:
NOTE: Modifying this parameter does not modify the results of the query. | |
Returns | a generator of reven2.string.String . | |
Raises | RuntimeError | if the strings resource has not been generated. |
ValueError | if fetch_count <= 0. |