-
Notifications
You must be signed in to change notification settings - Fork 0
/
bintools.py
110 lines (94 loc) · 3.5 KB
/
bintools.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
import subprocess as sp
import re
from collections import defaultdict
__all__ = ['ArmBinary', 'X86Binary']
class Binary:
_objdump_opts = []
def __init__(self, binary, prefix=''):
self.binary = binary
self.OBJDUMP_BIN = prefix + 'objdump'
self.READELF_BIN = prefix + 'readelf'
self.CPPFILT_BIN = prefix + 'c++filt'
def get_callgraph(self):
"""Return adjacency list of all functions.
Leaves are included. Self-references are excluded.
Only direct calls are accounted for, indirect calls and storing
a pointer to a function are not accounted for.
"""
cmd = [self.OBJDUMP_BIN, '-d'] + self._objdump_opts + [self.binary]
f = sp.run(cmd, stdout=sp.PIPE, check=True).stdout
g = defaultdict(set)
rc = re.compile(r'<(.*?)>:')
rb = re.compile(r'<([^+>]+)')
current = None
for l in f.decode('ascii').split('\n'):
m = rc.search(l)
if m:
current = m.group(1)
g[current]
continue
fields = l.split('\t', 2)
if len(fields) < 2:
continue
ia = fields[-1].split()
instr = ia[0]
trg = ia[-1]
if self._is_branch(instr) and '<' in trg:
target = rb.search(trg).group(1)
if current != target:
g[current].add(target)
return g
def get_symbol_sizes(self):
f = sp.run([self.READELF_BIN, '-s', '-W', self.binary], stdout=sp.PIPE, check=True).stdout.decode('ascii').split('\n')
res = dict()
for l in f:
l = l.split()
if len(l) < 8:
continue
try:
size = int(l[2])
name = l[7]
res[name] = size
except ValueError:
continue
return res
def demangle(self, mangled):
"""For a list of mangled names, return a list of demangled names"""
input = '\n'.join(mangled).encode('ascii')
res = sp.run([self.CPPFILT_BIN], input=input, stdout=sp.PIPE, check=True)
out = res.stdout.decode('ascii').split('\n')
return out
def demangle_map(self, mangled):
"""Return dict mapping mangled names to their unmangled names."""
return dict(zip(mangled, self.demangle(mangled)))
class ArmBinary(Binary):
_is = 'blx,bx,bl,b'.split(',')
_cs = 'eq,ne,cs,hs,cc,lo,mi,pl,vs,vc,hi,ls,ge,lt,gt,le,'.split(',')
_ws = '.n,.w,'.split(',')
def __init__(self, binary, prefix='arm-none-eabi-'):
super().__init__(binary, prefix=prefix)
@staticmethod
def _stripprefix(s, prefs):
"""If some element of `prefs` is a prefix of `s`, strip it and return
the rest, otherwise return None.
If `s` is None, return None (allows for monadic use).
"""
if s is None:
return None
if s == '':
return s
for p in prefs:
if s.startswith(p):
return s[len(p):]
return None
def _is_branch(self, i):
"""Return true if `i` is a branch instruction."""
i2 = self._stripprefix(i, self._is)
i3 = self._stripprefix(i2, self._cs)
i4 = self._stripprefix(i3, self._ws)
return i4 == ''
class X86Binary(Binary):
_objdump_opts = ['-Mintel-mnemonics']
_is = 'jmp,je,jne,jg,jge,ja,jae,jl,jle,jb,jbe,jo,jno,jz,jnz,js,jns,call'.split(',')
def _is_branch(self, i):
return i in self._is