mirror of
https://github.com/cemu-project/idapython.git
synced 2024-11-25 02:26:55 +01:00
150 lines
4.6 KiB
Python
150 lines
4.6 KiB
Python
|
"""
|
||
|
|
||
|
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-2009 Hex-Rays
|
||
|
ALL RIGHTS RESERVED.
|
||
|
|
||
|
|
||
|
v1.0 - initial version
|
||
|
|
||
|
"""
|
||
|
|
||
|
import idaapi
|
||
|
import idautils
|
||
|
import idc
|
||
|
|
||
|
from idaapi import GraphViewer
|
||
|
|
||
|
# -----------------------------------------------------------------------
|
||
|
# 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):
|
||
|
s = idc.Eval('WinDbgCommand("dg %x")' % cpu.fs)
|
||
|
if "IDC_FAILURE" in s:
|
||
|
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):
|
||
|
idc.SelectThread(tid)
|
||
|
base = idaapi.dbg_get_thread_sreg_base(tid, 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)
|
||
|
exc_rr = Dword(fs_base)
|
||
|
result = []
|
||
|
while exc_rr != 0xffffffff:
|
||
|
prev = Dword(exc_rr)
|
||
|
handler = Dword(exc_rr + 4)
|
||
|
exc_rr = prev
|
||
|
result.append(handler)
|
||
|
return result
|
||
|
|
||
|
# -----------------------------------------------------------------------
|
||
|
class SEHGraph(GraphViewer):
|
||
|
def __init__(self, title, result):
|
||
|
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 = idaapi.get_func(handler)
|
||
|
if not f:
|
||
|
# create function
|
||
|
idc.MakeFunction(handler, idaapi.BADADDR)
|
||
|
|
||
|
# Node label is function name or address
|
||
|
s = GetFunctionName(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 not addr_id.has_key(handler):
|
||
|
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:
|
||
|
idc.SelectThread(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:
|
||
|
idc.Jump(value)
|
||
|
return True
|
||
|
|
||
|
|
||
|
# -----------------------------------------------------------------------
|
||
|
def main():
|
||
|
if not idaapi.dbg_can_query():
|
||
|
print "The debugger must be active and suspended before using this script!"
|
||
|
return
|
||
|
|
||
|
# Save current thread id
|
||
|
tid = GetCurrentThreadId()
|
||
|
|
||
|
# Iterate through all function instructions and take only call instructions
|
||
|
result = {}
|
||
|
for tid in idautils.Threads():
|
||
|
result[tid] = GetExceptionChain(tid)
|
||
|
|
||
|
# Restore previously selected thread
|
||
|
idc.SelectThread(tid)
|
||
|
|
||
|
# Build the graph
|
||
|
g = SEHGraph("SEH graph", result)
|
||
|
g.Show()
|
||
|
|
||
|
main()
|