This repository was archived by the owner on Dec 24, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 298
Expand file tree
/
Copy pathcompare_traces.py
More file actions
executable file
·169 lines (135 loc) · 4.91 KB
/
Copy pathcompare_traces.py
File metadata and controls
executable file
·169 lines (135 loc) · 4.91 KB
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/python3
import glob
import os
import sys
import ast
import re
class args_t:
def __init__(self, static):
self.static = static
try:
import idaapi
# running as f.i.: idat -B -t -Scompare_traces.py -Loutput.log
args = args_t(False)
except:
# running stand-alone - static analysis
args = args_t(True)
#-----------------------------------------------------------------------
# used by default, if no option -s
class runtime_analysis_t:
def load(self):
self._import_all_idapython()
self.funcs = set()
for module in self._idapython_modules():
self.level = [module.__name__]
self._examine(module)
# a variation of tests/t2/phases/tests/trace_idapython_calls_idapythonrc.py,
# idapython_wrapper_t._wrap()
def _examine(self, obj):
for name in dir(obj):
# ignore "internals" for now
if name.startswith("_"):
continue
if name not in obj.__dict__:
continue
child = obj.__dict__[name] # different from getattr(...)
if not hasattr(child, "__class__"): # f.i. SWIG's cvar, "C global variable"
continue
typename = child.__class__.__name__
if typename == "type": # class
self.level.append(name)
self._examine(child)
self.level.pop()
elif typename in ("function", "staticmethod"):
self.funcs.add(".".join(self.level + [name]))
def _import_all_idapython(self):
for name in os.listdir(os.path.dirname(idaapi.__file__)):
name, ext = os.path.splitext(name)
if name.startswith("ida_") and ext == ".py":
__import__(name)
def _idapython_modules(self):
for name in sorted(sys.modules):
if name.startswith("ida_"):
yield sys.modules[name]
#-----------------------------------------------------------------------
# used only on option -s
class static_analysis_t(ast.NodeVisitor):
def __init__(self):
super().__init__()
def load(self, basedir):
self.funcs = set()
for pathname in glob.glob(os.path.join(basedir, "ida_*.py")):
self._examine(pathname)
def _examine(self, pathname):
module, _ = os.path.splitext(os.path.basename(pathname))
with open(pathname, "r") as f:
tree = ast.parse(f.read())
self.level = [module]
self.generic_visit(tree)
def visit_ClassDef(self, node):
self.level.append(node.name)
super().generic_visit(node)
self.level.pop()
def visit_FunctionDef(self, node):
if node.name.startswith("_") and not node.name.startswith("__"):
return
self.funcs.add(".".join(self.level + [node.name]))
# skip entering the node's body
#-----------------------------------------------------------------------
class call_traces_t(object):
def load(self, path):
with open(path, "r") as f:
content = ast.literal_eval(f.read())
self.funcs = set()
prefix = "ida_"
for key in content:
if key == "#empty":
for fun in content[key]:
self.funcs.add(prefix + fun)
else:
self.funcs.add(prefix + key)
#-----------------------------------------------------------------------
def compare(analysis_funcs, runtime_funcs):
check_diff = runtime_funcs - analysis_funcs
if check_diff:
print("? Strange, functions tested but not in the API:")
for func in sorted(check_diff):
print(" ", func)
diff = analysis_funcs - runtime_funcs
print("Coverage: {:.1f}%".format(
100 * (1 - len(diff) / len(analysis_funcs))
))
if diff:
print("Untested functions:")
for func in sorted(diff):
print(" ", func)
#-----------------------------------------------------------------------
def get_dirs():
# TODO
py_ver = 3
this_dir = os.path.dirname(__file__)
api_dir = os.path.abspath(os.path.join(
this_dir, "..", "..", "..",
"bin", "x64_linux_gcc", "python", str(py_ver)
))
return this_dir, api_dir
#-----------------------------------------------------------------------
def main():
this_dir, api_dir = get_dirs()
if args.static:
print("Static analysis...")
analysis = static_analysis_t()
analysis.load(api_dir)
else:
print("Runtime analysis...")
analysis = runtime_analysis_t()
analysis.load()
print("Reading traces...")
call_traces = call_traces_t()
call_traces.load(os.path.join(this_dir, "collected_traces.txt"))
print("Comparing...")
compare(analysis.funcs, call_traces.funcs)
if "idaapi" in sys.modules:
# running inside idat - get out
idaapi.qexit(0)
main()