-
Notifications
You must be signed in to change notification settings - Fork 1
/
exe_stats.py
136 lines (109 loc) · 5.39 KB
/
exe_stats.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import r2pipe
import sys
import operator
import collections
import argparse
import texttable
from itertools import zip_longest
import time
# pre analyze stuffs => statistics about the binary like most called functions, calls from functions,
# harder stuffs => hash, json database, comparison between function and functions in db, scoring, ...
def subdivise_fcts(fct_dic):
len_fct_dic = len(fct_dic)
tmp = fct_dic
basic_fcts = {k:v for k,v in tmp.items() if v['nmb_refs_from'] == 0}
result = {}
count = 1
result[0] = basic_fcts
warning_printed = False
result_fct_names = []
for k, v in basic_fcts.items():
result_fct_names.append(k)
del tmp[k]
while (len(tmp) != 0):
old_result_fct_names = list(result_fct_names)
result[count] = {}
for k,v in list(tmp.items()):
if all(key in old_result_fct_names for key in tmp[k]['refs_from'].keys()):
result[count][k] = v
del tmp[k]
result_fct_names.append(k)
continue
nmb_calls_categorized = 0
for key in tmp[k]['refs_from']:
if key in old_result_fct_names:
nmb_calls_categorized += 1
try:
if nmb_calls_categorized > 0.7 * tmp[k]['nmb_refs_from']:
result[count][k] = v
del tmp[k]
result_fct_names.append(k)
continue
except:
pass
if result[count] == {} and warning_printed == False:
print('[x] Possible infinite loop! result[count] is empty')
warning_printed = True
count = 99
result[count] = {}
for k,v in list(tmp.items()):
nmb_calls_categorized = 0
for key in tmp[k]['refs_from']:
result[count][k] = v
return result
count += 1
return result
def order_fct_dic(fct_dic, ordering_value):
fct_dic_sorted = collections.OrderedDict(sorted(fct_dic.items(), key=lambda x: x[1][ordering_value]))
return fct_dic_sorted
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Two different modes.\norder: show some stats from the executable passed as argument.\nsubdivise: Output the functions subdvised as follow: level 0 are the functions which don\'t call any other function; level 1 call only functions from level 0; level 2 call only functions from level 0 or level 1; ... This is not perfect (for example functions f6 which calls f7 and f7 calls f6 won\'t be classify, ...) and in order to mitigate some cases, the functions are "classified" in a level if 80\% of their call are already classified.')
parser.add_argument('-m', '--mode', help='', choices=['subdivise', 'order'], required=True)
parser.add_argument('path', type=str, help='Path to executable')
parser.add_argument('-o', '--ordering', type=str, help='Ordering value.', choices=['nmb_refs_to', 'nmb_refs_from', 'size'])
args = parser.parse_args()
path_file = args.path
if args.mode == 'order':
if args.ordering == None:
parser.print_help()
sys.exit(1)
r2p = r2pipe.open(path_file) # open without arguments only for #!pipe
r2p.cmd('aaa;aac') # analyze all symbols and calls
fct_dic = {}
for a in r2p.cmdj('aflj'): # return info of functions in jason
if a['size'] > 0: # if size of function is > 128 continue
refs_to = r2p.cmdj('axtj @'+ str(a['offset']))
tmp = r2p.cmd('s ' + str(a['offset']) + '; axfj @@i').split('\n')
tmp = [eval(ref_from) for ref_from in tmp if eval(ref_from) != []]
refs_from = {}
for ref_from in tmp:
if ref_from[0]['type'] == 'call':
try:
fct_name = r2p.cmdj('afij @ ' + str(ref_from[0]['to']))[0]['name']
refs_from[fct_name] = ref_from[0]
except:
pass
fct_dic[a['name']] = {'address': hex(a['offset']), 'refs_from': refs_from, 'refs_to': refs_to, 'nmb_refs_to': len(refs_to), 'size':a['size'], 'nmb_refs_from': len(refs_from)}
if args.mode == 'order':
ordering_value = args.ordering
fct_dic_sorted = order_fct_dic(fct_dic, ordering_value) # order_fct_dic return an array of list(fct_name, {json_dic_components})
#print(fct_dic_sorted)
tab = texttable.Texttable()
headings = ['Address','Function name','Number refs to fct','Number refs from fct','Size']
tab.header(headings)
for function, elements in fct_dic_sorted.items():
for row in list(zip_longest([elements['address']], [function], [elements['nmb_refs_to']], [elements['nmb_refs_from']], [elements['size']])):
tab.add_row(row)
s = tab.draw()
print (s)
if args.mode == 'subdivise':
fcts_ordered = subdivise_fcts(fct_dic)
tab_fcts = texttable.Texttable()
headings_fcts = ['Level', 'Function Name', 'Address']
tab_fcts.header(headings_fcts)
for level, fcts in fcts_ordered.items():
for key in fcts.keys():
for row in list(zip_longest([level], [key], [fcts[key]['address']])):
tab_fcts.add_row(row)
t = tab_fcts.draw()
print(t)