-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathCFG.py
124 lines (97 loc) · 2.82 KB
/
CFG.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
from capstone import *
from capstone.x86 import *
from pwnlib.elf import ELF
from BasicBlock import BasicBlock
MAX_INST_LEN = 15
NUM_REGS = 234
md = Cs(CS_ARCH_X86, CS_MODE_64)
md.detail = True
def get_jmp_target(op):
target = op.operands[0]
if target.type == X86_OP_IMM:
return target.imm
def succ(op):
addrs = []
if op.id not in [X86_INS_RET, X86_INS_RETF, X86_INS_RETFQ, X86_INS_JMP]:
addrs.append(op.address + op.size)
if X86_GRP_JUMP in op.groups:
addrs.append(get_jmp_target(op))
return addrs
def construct_cfg(elf, start_addr):
cfg = {}
work_queue = [start_addr]
while len(work_queue) > 0:
addr = work_queue.pop()
if addr in cfg:
continue
code = elf.read(addr, MAX_INST_LEN)
op = next(md.disasm(code, addr))
op.succs = succ(op)
cfg[addr] = op
work_queue += op.succs
return cfg
def build_basic_blocks(opgraph):
ops = [opgraph[addr] for addr in sorted(opgraph.keys())]
splits = []
for op in ops:
if len(op.succs) >= 2 or op.id == X86_INS_JMP: #make this not suck
splits += op.succs
basic_blocks = {}
block = BasicBlock()
for op in ops:
if op.address in splits:
basic_blocks[block.start.address] = block
block = BasicBlock()
block.insn.append(op)
basic_blocks[block.start.address] = block
for start_addr, block in basic_blocks.items():
succs = block.insn[-1].succs
for s in succs:
if s not in basic_blocks:
continue
basic_blocks[s].in_blocks.append(block)
block.out_blocks.append(basic_blocks[s])
return basic_blocks
def op_str(op):
return '\t0x{:x}: {} {}'.format(op.address, op.mnemonic, op.op_str)
class CFG(object):
def __init__(self, elf, start_addr):
self.start_addr = start_addr
self.ops = construct_cfg(elf, start_addr)
self.end_addr = max(self.ops)
self.blocks = build_basic_blocks(self.ops)
def __getitem__(self, key):
if isinstance(key, (int, long)):
return self.ops[key]
@property
def start(self):
return self[self.start_addr]
@property
def end(self):
return self[self.end_addr]
@property
def start_block(self):
return self.blocks[self.start_addr]
def show(self):
visited = set()
work_list = [self.start_block]
while len(work_list) > 0:
block = work_list.pop()
visited.add(block)
print('block {:x}'.format(block.start.address))
for i in block.insn:
print(op_str(i))
for out_block in block.out_blocks:
if out_block not in visited:
work_list.append(out_block)
def __repr__(self):
return '{}(0x{:x})'.format(self.__class__.__name__, self.start_addr)
if __name__ == '__main__':
import sys
if len(sys.argv) != 2:
print('Usage: {} <file>'.format(sys.argv[0]))
sys.exit()
e = ELF(sys.argv[1])
main_addr = e.symbols['main']
cfg = CFG(e, main_addr)
cfg.show()