This is an example of IDA script using the REVEN Python API. Must be run from IDA!
3 This script is a proof-of-concept for simple program profiling: each basic 4 block in a program will be colorized depending its execution frequency, one 5 can also observe basic block frequencies in the output window of IDA. 8 Use IDA to load the binary, and give the following arguments before 10 host = your REVEN server name, and 11 port = your REVEN's project port on this host. 14 For each basic block obtained from IDA's static analysis, the script counts 15 its occurrence frequency in the trace given by REVEN. The following limits 17 - the name of the binary must be obtained from REVEN (that is usually the 18 case if "dump process" is executed properly), and be case insentively 19 identical with the one analyzed in IDA (i.e. we do not rename the binary) 21 - the binary must be mapped at a unique virtual base address in the REVEN's 24 - very poor results for packed (or self-modifying) binaries (since the 25 limit of IDA's static analysis) 37 project = reven.Project(host, port)
39 runtime_base_address = get_base_address(project)
40 if runtime_base_address
is not None:
41 static_base_address = idaapi.get_imagebase()
43 print 'base addresses:' 44 print ' static: 0x{:x}'.format(idaapi.get_imagebase())
45 print ' runtime: 0x{:x}'.format(runtime_base_address)
47 offset = runtime_base_address - static_base_address
49 static_bbs = collect_basic_blocks()
50 binary_trace = get_binary_trace(project)
51 dynamic_bbs = calculate_dynamic_basic_blocks(static_bbs, binary_trace, offset)
53 bb_freq = calculate_dynamic_basic_block_frequency(dynamic_bbs, binary_trace, offset)
54 colorize_dynamic_basic_blocks(dynamic_bbs, bb_freq)
56 print 'colorizing basic blocks done.' 59 print 'cannot find the binary {:s} in the REVEN\'s trace or it is mapped at different base addresses.'.format(os.path.basename(idc.GetInputFilePath()))
61 def get_base_address(reven_project):
63 Look for the current IDA's binary name in the REVEN's binary mapping information. 64 If the binary is found, return the base address it is mapped at, else return None. 67 binary_name = os.path.basename(idc.GetInputFilePath()).lower()
69 loaded_binaries = reven_project.binaries()
70 for bin_path
in loaded_binaries:
71 if binary_name == os.path.basename(bin_path).lower():
72 bin_mappings = loaded_binaries[bin_path].mappings
76 for address_space
in bin_mappings.values():
77 if base_address
is None:
78 base_address = address_space.base_address
79 elif base_address != address_space.base_address:
87 def get_binary_trace(reven_project):
89 Return a python generator that filters the trace on the current IDA's binary only. 92 trace = reven_project.trace(
'Execution run')
97 binary_name = os.path.basename(idc.GetInputFilePath())
101 reven_points = trace.search_point([reven.BinaryCriterion(pattern=binary_name,
102 case_sensitive=
False)])
103 return list(reven_points)
106 def calculate_dynamic_basic_block_frequency(dynamic_bbs, reven_trace, relocation_offset):
107 bb_freq = { fst + relocation_offset: 0
for (fst, snd)
in dynamic_bbs }
109 for head_point
in reven_trace:
110 bb = head_point.basic_block
113 ins_addr = ins.address
114 if ins_addr
in bb_freq:
115 bb_freq[ins_addr] += 1
117 return { addr - relocation_offset: bb_freq[addr]
for addr
in bb_freq }
120 def calculate_frequency(ida_bbs, reven_trace, relocation_offset):
121 bb_freq = {bb.startEA + relocation_offset: 0
for bb
in ida_bbs}
126 for head_point
in reven_trace:
127 bb = head_point.basic_block
131 ins_addr = ins.address
132 if ins_addr
in bb_freq:
133 bb_freq[ins_addr] += 1
134 return {addr - relocation_offset: bb_freq[addr]
for addr
in bb_freq}
137 def collect_basic_blocks():
139 for fun_head
in idautils.Functions():
140 fun_flowchart = idaapi.FlowChart(idaapi.get_func(fun_head))
141 for bb
in fun_flowchart:
144 if (bb.startEA != bb.endEA):
150 def calculate_dynamic_basic_blocks(ida_bbs, reven_trace, relocation_offset):
151 dynamic_basic_blocks = { (bb.startEA + relocation_offset, bb.endEA + relocation_offset)
for bb
in ida_bbs }
153 for head_point
in reven_trace:
154 head_ins_addr = head_point.instruction.address
156 (fst, snd) = next((fst, snd)
for (fst, snd)
in dynamic_basic_blocks
if (head_ins_addr > fst)
and (snd > head_ins_addr))
158 dynamic_basic_blocks.remove((fst, snd))
160 dynamic_basic_blocks.add((fst, head_ins_addr))
162 dynamic_basic_blocks.add((head_ins_addr, snd))
164 except StopIteration:
169 dynamic_basic_blocks = { (fst - relocation_offset, snd - relocation_offset)
for (fst, snd)
in dynamic_basic_blocks }
171 return dynamic_basic_blocks
174 def color_gradient(bb_freq):
175 min_freq = bb_freq.itervalues().next()
177 for bb_addr
in bb_freq:
178 if min_freq > bb_freq[bb_addr]:
179 min_freq = bb_freq[bb_addr]
180 if max_freq < bb_freq[bb_addr]:
181 max_freq = bb_freq[bb_addr]
186 if max_freq == min_freq:
187 for bb_addr
in bb_freq:
188 bb_color[bb_addr] = max_color
190 color_step = float(max_color) / (max_freq - min_freq)
191 for bb_addr
in bb_freq:
192 bb_color[bb_addr] = max_color - int(round((bb_freq[bb_addr] - min_freq) * color_step))
197 def colorize_dynamic_basic_block(bb, color):
198 (addr, limit_addr) = bb
199 while addr < limit_addr:
200 idaapi.set_item_color(addr, color)
201 addr = idc.NextHead(addr)
204 def colorize_dynamic_basic_blocks(dynamic_bbs, bb_freq):
205 green_colors = color_gradient(bb_freq)
207 bb_dict = { fst: (fst, snd)
for (fst, snd)
in dynamic_bbs }
209 print '{:s}\t{:s}'.format(
'BAddr',
'Freq')
211 for bb_fst_addr
in bb_dict:
212 if bb_freq[bb_fst_addr] > 0:
214 color = 0xb0 * 0x10000 + green_colors[bb_fst_addr] * 0x100 + 0xff
215 colorize_dynamic_basic_block(bb_dict[bb_fst_addr], color)
217 print '0x{:x}\t{:d}'.format(bb_fst_addr, bb_freq[bb_fst_addr])
220 if __name__ ==
'__main__':
221 host_port_str = idc.AskStr(
'localhost:13370',
"REVEN's project address")
222 if host_port_str
is not None:
224 host, port_str = host_port_str.split(
':')
226 print(
"REVEN's project: {}:{}").format(host, port)
229 print(
"please give a correct REVEN\'s project address, e.g. localhost:13370")
230 except RuntimeError, e:
231 print(
'{}').format(e)
233 print(
'Unknown error')