cemu-idapython/Scripts/SEHGraph.py

150 lines
4.4 KiB
Python
Raw Permalink Normal View History

"""
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()