-
Notifications
You must be signed in to change notification settings - Fork 5
/
genpatch.py
120 lines (94 loc) · 3.68 KB
/
genpatch.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
#!/usr/bin/env python3
# coding: UTF-8
# IDA libraries
import idaapi
import ida_kernwin
import ida_name
import idc
# Python modules
import os
import sys
class PatchManager(object):
def __init__(self):
self.patched_bytes = []
self.prev_addr = None
def generate(self):
idaapi.visit_patched_bytes(0, idaapi.BADADDR, self.get_patch_byte)
if len(self.patched_bytes) == 0:
msg = 'Cannot generate patch because there is no patch applied.'
print(f'genpatch: {msg}')
ida_kernwin.warning(msg)
return False
template_path = ''
for path in sys.path:
if 'plugins' in path:
template_path = os.path.join(path, 'patch_script_template.txt')
patch_path = idc.get_input_file_path() + '_patch.py'
template_data = None
with open(template_path, 'r') as f:
template_data = f.readlines()
lines = 13
with open(patch_path, 'w') as f:
for data in self.patched_bytes:
template_data.insert(lines, f"# address: 0x{data['begin_addr']:x}\n")
lines += 1
template_data.insert(lines, f"# function name: {data['func_name']}\n")
lines += 1
template_data.insert(lines, f"# comment: {data['comment']}\n")
lines += 1
template_data.insert(lines, f"matches = re.findall('{data['original']}', target_data)\n")
lines += 1
template_data.insert(lines, "if len(matches) == 1:\n")
lines += 1
template_data.insert(lines, f" target_data = target_data.replace('{data['original']}', '{data['patched']}')\n")
lines += 1
template_data.insert(lines, "else:\n")
lines += 1
template_data.insert(lines, ' print("Patch pattern isn\'t unique")\n')
lines += 1
template_data.insert(lines, " sys.exit()\n")
lines += 1
f.writelines(template_data)
msg = f'Successfully generated patch to {patch_path} from Patched Bytes'
print(f'genpatch: {msg}')
ida_kernwin.info(msg)
return True
# callback in 3rd argument of idaapi.visit_patched_bytes
def get_patch_byte(self, ea, fpos, org_val, patched_val):
org_byte = f"{org_val:02x}"
patched_byte = f"{patched_val:02x}"
if self.prev_addr is None or ea != (self.prev_addr + 1):
func_name = idc.get_segm_name(ea)
if idc.get_func_name(ea) or idc.get_name(ea, ida_name.GN_VISIBLE):
func_name += ': '
func_name += idc.get_func_name(ea)
func_name += idc.get_name(ea, ida_name.GN_VISIBLE)
comment = idc.get_cmt(ea, 0) or idc.get_cmt(ea, 1) or ''
comment = comment.replace('\n', ' ')
self.patched_bytes.append({
'func_name': func_name,
'begin_addr': ea,
'original': org_byte,
'patched': patched_byte,
'comment': comment
})
else:
self.patched_bytes[-1]['original'] += org_byte
self.patched_bytes[-1]['patched'] += patched_byte
self.prev_addr = ea
return 0
class genpatch_t(idaapi.plugin_t):
flags = idaapi.PLUGIN_KEEP
comment = 'generate patch from patched bytes'
help = ''
wanted_name = "genpatch"
wanted_hotkey = ""
def init(self):
return idaapi.PLUGIN_KEEP
def run(self, arg):
p = PatchManager()
p.generate()
def term(self):
return None
def PLUGIN_ENTRY():
return genpatch_t()