class Trace:
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 | __repr__ |
Undocumented |
Method | context |
Retrieve the context after a transition from the latter's unique id. |
Method | context |
Retrieve the context before 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 |
Method | memory |
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 | transition |
Retrieve a transition from its unique id. |
Method | transitions |
Get a generator over the transitions in the trace. |
Property | first |
Property: First context in this trace. |
Property | first |
Property: First transition in this trace. |
Property | last |
Property: Last context in this trace. |
Property | last |
Property: Last transition in this trace. |
Property | search |
Property: Get a reven2.search.Search instance, which is the entry point to find interesting points in the trace. |
Property | transition |
Property: Count of transitions in this trace. |
Method | _context |
Undocumented |
Instance Variable | _data |
Undocumented |
Instance Variable | _ossi |
Undocumented |
Instance Variable | _stack |
Undocumented |
Property | _rvn |
Undocumented |
Undocumented
Parameters | |
_rvn:_reven_api.reven_connection | Undocumented |
_ossi_ossi._DataSource | Undocumented |
_stack_stack._DataSource | Undocumented |
Retrieve the context after a transition from the latter's unique id.
Information
Parameters | |
transitionint | Undocumented |
Returns | |
Context | Undocumented |
Raises | |
IndexError | if the unique id does not belong to the trace |
Retrieve the context before a transition from the latter's unique id.
Information
Parameters | |
transitionint | Undocumented |
Returns | |
Context | Undocumented |
Raises | |
IndexError | if the unique id does not belong to the trace. |
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
Parameters | |
start:_Optional[ | 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:_Optional[ | 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:int | 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 | |
_Iterator[ | Undocumented |
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. |
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
Parameters | |
processes:_Optional[ | the list of processes to filter. If None, all processes will be filtered. |
ring_filter.RingPolicy | the rings to filter. Must be a filter.RingPolicy value. By default, all rings will be filtered. |
from_Optional[ | the first included context where the filter starts. Must be a trace.Context . If None, start at the first context of trace. |
to_Optional[ | the first excluded context where the filter stops. Must be a trace.Context . If None, stop at the last context of the trace (included). `from_context` must be strictly lower than `to_context`. |
isbool | whether the results are returned in the forward or backward order. By default, the forward order is used. |
Returns | |
_Union[ | A generator of trace.ContextRange if `is_forward` equal `True`. Otherwise, a generator of trace.BackwardContextRange . |
Raises | |
ValueError | if `to_context` is lower or equal to `from_context`. |
RuntimeError | if the ossi ranges ressource is unavailable. |
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
Parameters | |
address:_Optional[ | 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. If None, return accesses regardless of their address. |
size:_Optional[ | Size of the searched range of addresses. If None and address not None, the address's default formatter size is used. |
from_Optional[ | 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_Optional[ | 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 |
isbool | 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:_Optional[ | Only return accesses whose operation equals the specified reven2.memhist.MemoryAccessOperation . If None, return accesses regardless of their operation. |
fetchint | 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 |
address_Optional[ | Searched range of addresses. A reven2.MemoryRange . If a virtual range, 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 and address is None, return accesses regardless of their address. If not None, then address and size must be None. |
Returns | |
_Iterator[ | 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. |
ValueError | if size is not None but address is None. |
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
Parameters | |
pattern:str | 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 |
fetchint | 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 | |
_Iterator[ | a generator of reven2.string.String . |
Raises | |
RuntimeError | if the strings resource has not been generated. |
ValueError | if fetch_count <= 0. |
Retrieve a transition from its unique id.
Information
Parameters | |
transitionint | Undocumented |
Returns | |
Transition | Undocumented |
Raises | |
IndexError | if the unique id does not belong to the trace. |
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
Parameters | |
start:_Optional[ | 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:_Optional[ | 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:int | 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 | |
_Iterator[ | Undocumented |
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]). |
Property: Get a reven2.search.Search
instance, which is the entry point to find interesting points in the trace.
Undocumented
Parameters | |
contextint | Undocumented |
Returns | |
Context | Undocumented |