-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathSinDiff.py
More file actions
152 lines (123 loc) · 5.41 KB
/
Copy pathSinDiff.py
File metadata and controls
152 lines (123 loc) · 5.41 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
# SinDiff.py – The ultimate binary diffing plugin by Sin
# works on IDA Pro 9.2 forever
# Author: Sinn
# GitHub: https://github.com/devilofen/SinDiff
import idaapi
import ida_kernwin
import ida_funcs
import ida_bytes
import ida_lines
import idautils
import sqlite3
import hashlib
import os
# --------------------------------------------------------------
def export_to_db(db_path):
if not idaapi.get_input_file_path():
ida_kernwin.warning("No file loaded! Open a binary first.")
return False
conn = sqlite3.connect(db_path)
cur = conn.cursor()
cur.execute("""CREATE TABLE IF NOT EXISTS funcs (
addr INTEGER PRIMARY KEY, name TEXT, asm_hash TEXT, pseudo TEXT)""")
ida_kernwin.show_wait_box("SinDiff → Exporting functions...")
count = 0
for func_ea in idautils.Functions():
count += 1
name = idaapi.get_func_name(func_ea) or f"sub_{func_ea:X}"
asm_lines = []
func = ida_funcs.get_func(func_ea)
for head in idautils.Heads(func.start_ea, func.end_ea):
if ida_bytes.is_code(ida_bytes.get_flags(head)):
raw = ida_lines.generate_disasm_line(head, 0)
if raw:
clean = ida_lines.tag_remove(raw).strip()
if clean:
asm_lines.append(clean)
asm_hash = hashlib.sha256("\n".join(asm_lines).encode()).hexdigest()
pseudo = ""
try:
cfunc = idaapi.decompile(func_ea)
if cfunc:
pseudo = str(cfunc)
except:
pass
cur.execute("INSERT OR REPLACE INTO funcs VALUES (?,?,?,?)",
(func_ea, name, asm_hash, pseudo))
if count % 100 == 0:
ida_kernwin.replace_wait_box(f"SinDiff → Exported {count} functions...")
conn.commit()
conn.close()
ida_kernwin.hide_wait_box()
ida_kernwin.info(f"SinDiff → Exported {count} functions → {os.path.basename(db_path)}")
return True
# --------------------------------------------------------------
def diff_and_show(vuln_db, patched_db):
try:
v = sqlite3.connect(vuln_db); vc = v.cursor()
p = sqlite3.connect(patched_db); pc = p.cursor()
changes = []
pc.execute("SELECT addr, name, asm_hash, pseudo FROM funcs")
for p_addr, p_name, p_asm, p_pseudo in pc.fetchall():
vc.execute("SELECT asm_hash, pseudo FROM funcs WHERE addr=?", (p_addr,))
row = vc.fetchone()
if not row:
changes.append((p_addr, p_name, "NEW function"))
continue
v_asm, v_pseudo = row
if v_asm == p_asm:
continue
note = "Modified"
if p_pseudo and v_pseudo:
pl = p_pseudo.lower(); vl = v_pseudo.lower()
if "memset" in pl and "memset" not in vl: note += " → memset added"
if pl.count("if (") > vl.count("if (") + 1: note += " → extra checks"
if any(x in vl for x in ["strcpy","sprintf","memcpy"]) and not any(x in pl for x in ["strcpy","sprintf","memcpy"]):
note += " → unsafe API removed"
changes.append((p_addr, p_name, note))
v.close(); p.close()
items = [[f"0x{addr:x}", name, reason] for addr, name, reason in changes]
class SinDiffChooser(ida_kernwin.Choose):
def __init__(self):
ida_kernwin.Choose.__init__(self, "SinDiff Results",
[["Address", 15 | ida_kernwin.Choose.CHCOL_HEX],
["Name", 30],
["Reason", 70]])
self.items = items
def OnGetSize(self): return len(self.items)
def OnGetLine(self, n): return self.items[n]
def OnSelectLine(self, n):
addr = int(self.items[n][0], 16)
ida_kernwin.jumpto(addr)
def OnClose(self): pass
if not changes:
ida_kernwin.info("SinDiff → No changes found – binaries are identical!")
else:
SinDiffChooser().Show()
except Exception as e:
ida_kernwin.warning(f"SinDiff error: {e}")
# --------------------------------------------------------------
class SinDiffPlugin(idaapi.plugin_t):
flags = idaapi.PLUGIN_UNL
comment = "SinDiff – Binary diffing tool by Sin"
help = "The fastest way to find patched functions"
wanted_name = "SinDiff"
wanted_hotkey = "Alt-Shift-S" # S for Sinn
def init(self): return idaapi.PLUGIN_OK
def term(self): pass
def run(self, arg):
choice = ida_kernwin.ask_str("export", 0, "SinDiff → Type 'export' or 'diff'")
if not choice: return
mode = choice.strip().lower()
if mode == "export":
db = ida_kernwin.ask_file(1, "*.db", "SinDiff → Save export as...")
if db: export_to_db(db)
elif mode == "diff":
vdb = ida_kernwin.ask_file(0, "*.db", "SinDiff → Select VULNERABLE export")
if not vdb: return
pdb = ida_kernwin.ask_file(0, "*.db", "SinDiff → Select PATCHED export")
if pdb: diff_and_show(vdb, pdb)
else:
ida_kernwin.warning("SinDiff → Type 'export' or 'diff'")
def PLUGIN_ENTRY():
return SinDiffPlugin()