REVEN-Axion 2015v1.2.0
percent.py

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.

This will demonstrate the use of the following services:

It is built as an Axion script, so this also shows the use of a callback in Axion.

1 from axion_api import axion
2 import reven
3 from PythonQt import Qt
4 
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)
9 
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)
13  result = None
14 
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):
19  result = access
20  return result
21 
22 def get_matching_instruction(client, point):
23  "Return execution point of matching instruction for given execution point or None if not found."
24 
25  # create a range of size 1 to query context before and after selected instruction
26  point_range = reven.execution_range(point.run_name, point.sequence_identifier, 1, point.instruction_index)
27  # give an empty vector of logical address range to ignore memory for we don't nedd any memory here
28  context = client.run_get_running_context_between(point_range, reven.vector_of_logical_address_range())
29 
30  # ss values before and after selected instruction
31  ss_before = context.before.numeric_registers['ss'].value
32  ss_after = context.after.numeric_registers['ss'].value
33 
34  # ss basic heuristic to handle sysenter/sysexit
35  if ss_before != ss_after:
36  # ss is modified by instruction, search next or previous write for ss and return corresponding execution point
37  return client.run_search_next_register_use(point, forward=(ss_before > ss_after), read=False, write=True, register_name="ss")
38 
39  # memory heuristic
40  access = pick_memory_access(client, point)
41  if access != None:
42  if access.write:
43  return client.run_search_next_memory_use(point, forward=True, read=True, write=False, address=access.logical)
44 
45  if access.read:
46  return client.run_search_next_memory_use(point, forward=False, read=False, write=True, address=access.logical)
47 
48  # esp values before and after selected instruction
49  esp_before = context.before.numeric_registers['esp'].value
50  esp_after = context.after.numeric_registers['esp'].value
51 
52  # esp heuristic
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)
57 
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)
61 
62 def axion_callback():
63  # get current selected instruction from axion
64  run, seq, instr = axion.selected_sequence()
65 
66  # connect to Reven server project instance for latter service calls
67  host, port = axion.connection_info()
68  client = reven.reven_connection(host.encode(), port)
69 
70  # get reven execution point corresponding to selected instruction (for reven service calls take execution point)
71  point = reven.execution_point(run.encode(), seq, instr)
72 
73  # retrieve data from Reven and use custom heuristic to find an instruction matching the current one
74  result = get_matching_instruction(client, point)
75 
76  if result == None or not result.valid():
77  axion.status_message("No matching instruction recorded for [%s@%d:%d]" % (run, seq, instr))
78  else:
79  # select matching instruction in axion
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))
82 
83 register_command(axion_callback, "percent", "plugin.percent", "%")