来源:Github

因为原脚本有些api的使用比较老旧,所以就稍作修改,终于能用了

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
from barf.barf import BARF
import angr
import pyvex
import claripy
import struct
import sys
import copy

def get_retn_predispatcher(cfg):
global main_dispatcher
for block in cfg.basic_blocks:
if len(block.branches) == 0 and block.direct_branch == None:
retn = block.start_address
elif block.direct_branch == main_dispatcher:
pre_dispatcher = block.start_address
return retn, pre_dispatcher

def get_relevant_nop_blocks(cfg):
global pre_dispatcher, prologue, retn
relevant_blocks = []
nop_blocks = []
for block in cfg.basic_blocks:
if block.direct_branch == pre_dispatcher and len(block.instrs) != 1:
relevant_blocks.append(block.start_address)
elif block.start_address != prologue and block.start_address != retn:
nop_blocks.append(block)
return relevant_blocks, nop_blocks

def statement_inspect(state):
global modify_value
expressions = list(state.scratch.irsb.statements[state.inspect.statement].expressions)
if len(expressions) != 0 and isinstance(expressions[0], pyvex.expr.ITE):
state.scratch.temps[expressions[0].cond.tmp] = modify_value
state.inspect._breakpoints['statement'] = []

def symbolic_execution(start_addr, hook_addr=None, modify=None, inspect=False):
global b, relevants, modify_value
if hook_addr != None:
b.hook(hook_addr, retn_procedure, length=5)
if modify != None:
modify_value = modify
state = b.factory.blank_state(addr=start_addr, remove_options={angr.options.LAZY_SOLVES})
if inspect:
state.inspect.b('statement', when=angr.BP_BEFORE, action=statement_inspect)
p = b.factory.simgr(state)
succ=p.step().successors(state)
while succ.successors[0].addr not in relevants:
succ=succ.successors[0].step()
return succ.successors[0].addr

def retn_procedure(state):
global b
ip = state.se.eval(state.regs.ip)
b.unhook(ip)
return

def fill_nop(data, start, end):
global opcode
for i in range(start, end):
data[i] = opcode['nop']

def fill_jmp_offset(data, start, offset):
jmp_offset = struct.pack('<i', offset)
for i in range(4):
data[start + i] = jmp_offset[i]

def list2bytes(l):
data = copy.deepcopy(l)
for i in range(len(data)):
if isinstance(data[i], int):
continue
else:
data[i] = ord(data[i])
return bytes(data)

if __name__ == '__main__':
if len(sys.argv) != 3:
print('Usage: python deflat.py filename function_address(hex)')
exit(0)
opcode = {'a':'\x87', 'ae': '\x83', 'b':'\x82', 'be':'\x86', 'c':'\x82', 'e':'\x84', 'z':'\x84', 'g':'\x8F',
'ge':'\x8D', 'l':'\x8C', 'le':'\x8E', 'na':'\x86', 'nae':'\x82', 'nb':'\x83', 'nbe':'\x87', 'nc':'\x83',
'ne':'\x85', 'ng':'\x8E', 'nge':'\x8C', 'nl':'\x8D', 'nle':'\x8F', 'no':'\x81', 'np':'\x8B', 'ns':'\x89',
'nz':'\x85', 'o':'\x80', 'p':'\x8A', 'pe':'\x8A', 'po':'\x8B', 's':'\x88', 'nop':'\x90', 'jmp':'\xE9', 'j':'\x0F'}
filename = sys.argv[1]
start = int(sys.argv[2], 16)
barf = BARF(filename)
base_addr = barf.binary.entry_point >> 12 << 12
b = angr.Project(filename, load_options={'auto_load_libs': False, 'main_opts':{'custom_base_addr': 0}})
cfg = barf.recover_cfg(start=start)
blocks = cfg.basic_blocks
prologue = start
main_dispatcher = cfg.find_basic_block(prologue).direct_branch
retn, pre_dispatcher = get_retn_predispatcher(cfg)
relevant_blocks, nop_blocks = get_relevant_nop_blocks(cfg)
print('*******************relevant blocks************************')
print('prologue:%#x' % start)
print('main_dispatcher:%#x' % main_dispatcher)
print('pre_dispatcher:%#x' % pre_dispatcher)
print('retn:%#x' % retn)
print('relevant_blocks:', [hex(addr) for addr in relevant_blocks])

print('*******************symbolic execution*********************')
relevants = relevant_blocks
relevants.append(prologue)
relevants_without_retn = list(relevants)
relevants.append(retn)
flow = {}
for parent in relevants:
flow[parent] = []
modify_value = None
patch_instrs = {}
for relevant in relevants_without_retn:
print('-------------------dse %#x---------------------' % relevant)
block = cfg.find_basic_block(relevant)
has_branches = False
hook_addr = None
for ins in block.instrs:
if ins.mnemonic.startswith('cmov'):
patch_instrs[relevant] = ins
has_branches = True
elif ins.mnemonic.startswith('call'):
hook_addr = ins.address
if has_branches:
flow[relevant].append(symbolic_execution(relevant, hook_addr, claripy.BVV(1, 1), True))
flow[relevant].append(symbolic_execution(relevant, hook_addr, claripy.BVV(0, 1), True))
else:
flow[relevant].append(symbolic_execution(relevant, hook_addr))

print('************************flow******************************')
for (k, v) in flow.items():
print('%#x:' % k, [hex(child) for child in v])

print('************************patch*****************************')
flow.pop(retn)
origin = open(filename, 'rb')
origin_data = list(origin.read())
origin.close()
recovery = open(filename + '.recovered', 'wb')
for nop_block in nop_blocks:
fill_nop(origin_data, nop_block.start_address - base_addr, nop_block.end_address - base_addr + 1)
for (parent, childs) in flow.items():
if len(childs) == 1:
last_instr = cfg.find_basic_block(parent).instrs[-1]
file_offset = last_instr.address - base_addr
origin_data[file_offset] = opcode['jmp']
file_offset += 1
fill_nop(origin_data, file_offset, file_offset + last_instr.size - 1)
fill_jmp_offset(origin_data, file_offset, childs[0] - last_instr.address - 5)
else:
instr = patch_instrs[parent]
file_offset = instr.address - base_addr
fill_nop(origin_data, file_offset, cfg.find_basic_block(parent).end_address - base_addr + 1)
origin_data[file_offset] = opcode['j']
origin_data[file_offset + 1] = opcode[instr.mnemonic[4:]]
fill_jmp_offset(origin_data, file_offset + 2, childs[0] - instr.address - 6)
file_offset += 6
origin_data[file_offset] = opcode['jmp']
fill_jmp_offset(origin_data, file_offset + 1, childs[1] - (instr.address + 6) - 5)
origin_data_bytes = list2bytes(origin_data)
recovery.write(origin_data_bytes)
recovery.close()
print('Successful! The recovered file: %s' % (filename + '.recovered'))