This repository was archived by the owner on Dec 24, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 298
Expand file tree
/
Copy pathSEHGraph.py
More file actions
149 lines (122 loc) · 4.65 KB
/
Copy pathSEHGraph.py
File metadata and controls
149 lines (122 loc) · 4.65 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
"""
A script that graphs all the exception handlers in a given process
It will be easy to see what thread uses what handler and what handlers are commonly used between threads
Copyright (c) 1990-2025 Hex-Rays
ALL RIGHTS RESERVED.
"""
from __future__ import print_function
import ida_kernwin
import ida_graph
import ida_idd
import ida_dbg
import ida_funcs
import idautils
# -----------------------------------------------------------------------
# Since Windbg debug module does not support get_thread_sreg_base()
# we will call the debugger engine "dg" command and parse its output
def WindbgGetRegBase(tid):
ok, s = ida_dbg.send_dbg_command("dg %x" % idautils.cpu.fs)
if not ok:
return 0
m = re.compile("[0-9a-f]{4} ([0-9a-f]{8})")
t = m.match(s.split('\n')[-2])
if not t:
return 0
return int(t.group(1), 16)
# -----------------------------------------------------------------------
def GetFsBase(tid):
ida_dbg.select_thread(tid)
base = ida_idd.dbg_get_thread_sreg_base(tid, idautils.cpu.fs)
if base != 0:
return base
return WindbgGetRegBase(tid)
# -----------------------------------------------------------------------
# Walks the SEH chain and returns a list of handlers
def GetExceptionChain(tid):
fs_base = GetFsBase(tid)
print("FS_BASE for %s: %s (cpu.fs=%s)" % (repr(tid), repr(fs_base), repr(idautils.cpu.fs)))
exc_rr = ida_bytes.get_wide_dword(fs_base)
result = []
while exc_rr != 0xffffffff:
prev = get_wide_dword(exc_rr)
handler = get_wide_dword(exc_rr + 4)
exc_rr = prev
result.append(handler)
return result
# -----------------------------------------------------------------------
class SEHGraph(ida_graph.GraphViewer):
def __init__(self, title, result):
ida_graph.GraphViewer.__init__(self, title)
self.result = result
self.names = {} # ea -> name
def OnRefresh(self):
self.Clear()
addr_id = {}
for (tid, chain) in self.result.items():
# Each node data will contain a tuple of the form: (Boolean->Is_thread, Int->Value, String->Label)
# For threads the is_thread will be true and the value will hold the thread id
# For exception handlers, is_thread=False and Value=Handler address
# Add the thread node
id_parent = self.AddNode( (True, tid, "Thread %X" % tid) )
# Add each handler
for handler in chain:
# Check if a function is created at the handler's address
f = ida_funcs.get_func(handler)
if not f:
# create function
ida_funcs.add_func(handler)
# Node label is function name or address
s = ida_funcs.get_func_name(handler)
if not s:
s = "%x" % handler
# cache name
self.names[handler] = s
# Get the node id given the handler address
# We use an addr -> id dictionary so that similar addresses get similar node id
if handler not in addr_id:
id = self.AddNode( (False, handler, s) )
addr_id[handler] = id # add this ID
else:
id = addr_id[handler]
# Link handlers to each other
self.AddEdge(id_parent, id)
id_parent = id
return True
def OnGetText(self, node_id):
is_thread, value, label = self[node_id]
if is_thread:
return (label, 0xff00f0)
return label
def OnDblClick(self, node_id):
is_thread, value, label = self[node_id]
if is_thread:
ida_dbg.select_thread(value)
self.Show()
s = "SEH chain for " + hex(value)
t = "-" * len(s)
print(t)
print(s)
print(t)
for handler in self.result[value]:
print("%x: %s" % (handler, self.names[handler]))
print(t)
else:
ida_kernwin.jumpto(value)
return True
# -----------------------------------------------------------------------
def main():
if not ida_idd.dbg_can_query():
print("The debugger must be active and suspended before using this script!")
return
# Save current thread id
tid = ida_dbg.get_current_thread()
# Iterate through all function instructions and take only call instructions
result = {}
for tid in idautils.Threads():
result[tid] = GetExceptionChain(tid)
# Restore previously selected thread
ida_dbg.select_thread(tid)
# Build the graph
g = SEHGraph("SEH graph", result)
g.Show()
main()