The percent script simulates the % key in VI command mode, more stack memory items. Basically this means that for a push, this makes you jump to the associated pop. For a call, this makes you jump to the corresponding ret, and so on.
It is built as an Axion script, so this also shows the use of a callback in Axion.
1 from axion_api
import axion
3 from PythonQt
import Qt
5 def register_command(callback, desc, name, shortcut):
6 action = Qt.QAction(desc, axion)
7 axion.shortcuts().register_action(action, name, Qt.QKeySequence(shortcut))
8 action.connect(
"triggered(bool)", callback)
10 def pick_memory_access(client, point):
11 "Return first non null logical address accessed by instruction at given execution point or None"
12 null_logical = reven.logical_address(0, 0)
15 accesses = client.memory_get_history_instruction(point)
16 for access
in accesses:
17 if access.logical != null_logical:
18 if not (result
and result.write
and not access.write):
22 def get_matching_instruction(client, point):
23 "Return execution point of matching instruction for given execution point or None if not found."
26 point_range = reven.execution_range(point.run_name, point.sequence_identifier, 1, point.instruction_index)
28 context = client.run_get_running_context_between(point_range, reven.vector_of_logical_address_range())
31 ss_before = context.before.numeric_registers[
'ss'].value
32 ss_after = context.after.numeric_registers[
'ss'].value
35 if ss_before != ss_after:
37 return client.run_search_next_register_use(point, forward=(ss_before > ss_after), read=
False, write=
True, register_name=
"ss")
40 access = pick_memory_access(client, point)
43 return client.run_search_next_memory_use(point, forward=
True, read=
True, write=
False, address=access.logical)
46 return client.run_search_next_memory_use(point, forward=
False, read=
False, write=
True, address=access.logical)
49 esp_before = context.before.numeric_registers[
'esp'].value
50 esp_after = context.after.numeric_registers[
'esp'].value
53 if esp_before != esp_after:
54 if esp_before > esp_after:
55 stack = reven.logical_address(ss_after, esp_after)
56 return client.run_search_next_memory_use(point, forward=
True, read=
True, write=
False, address=stack)
58 if esp_before < esp_after:
59 stack = reven.logical_address(ss_before, esp_before)
60 return client.run_search_next_memory_use(point, forward=
False, read=
False, write=
True, address=stack)
64 run, seq, instr = axion.selected_sequence()
67 host, port = axion.connection_info()
68 client = reven.reven_connection(host.encode(), port)
71 point = reven.execution_point(run.encode(), seq, instr)
74 result = get_matching_instruction(client, point)
76 if result ==
None or not result.valid():
77 axion.status_message(
"No matching instruction recorded for [%s@%d:%d]" % (run, seq, instr))
80 axion.select_sequence(result.run_name, result.sequence_identifier, result.instruction_index)
81 axion.status_message(
"Jumped to matching instruction at [%s@%d:%d] from [%s@%d:%d]" % (result.run_name, result.sequence_identifier, result.instruction_index, run, seq, instr))
83 register_command(axion_callback,
"percent",
"plugin.percent",
"%")