-
Notifications
You must be signed in to change notification settings - Fork 7
/
CallStackWalk.py
175 lines (153 loc) · 5.17 KB
/
CallStackWalk.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
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
"""
A script that tries to determine the call stack
Run the application with the debugger, suspend the debugger, select a thread and finally run the script.
Copyright (c) 1990-2009 Hex-Rays
ALL RIGHTS RESERVED.
v1.0 - initial version
v1.0.1 - added stack segment bitness detection, thus works with 64bit processes too
"""
import idaapi
import idc
import idautils
# -----------------------------------------------------------------------
# class to take a copy of a segment_t
class Seg():
def __init__(self, s):
self.startEA = s.startEA
self.endEA = s.endEA
self.perm = s.perm
self.bitness = s.bitness
def __cmp__(self, other):
return cmp(self.startEA, other.startEA)
# -----------------------------------------------------------------------
# each item described as:
# [ delta, [ opcode(s) ] ]
#FF10 call d,[eax]
#FF5000 call d,[eax][0]
#FF9044332211 call d,[eax][011223344]
#FF1500000100 call d,[000010000]
#FF9300000000 call d,[ebx][0]
#FF10 call d,[eax]
CallPattern = \
[
[-2, [0xFF] ],
[-3, [0xFF] ],
[-5, [0xE8] ],
[-6, [0xFF] ],
]
# -----------------------------------------------------------------------
def IsPrevInsnCall(ea):
"""
Given a return address, this function tries to check if previous instruction
is a CALL instruction
"""
global CallPattern
if ea == idaapi.BADADDR or ea < 10:
return None
for delta, opcodes in CallPattern:
# assume caller's ea
caller = ea + delta
# get the bytes
bytes = [x for x in GetDataList(caller, len(opcodes), 1)]
# do we have a match? is it a call instruction?
if bytes == opcodes and idaapi.is_call_insn(caller):
return caller
return None
# -----------------------------------------------------------------------
def CallStackWalk(nn):
class Result:
"""
Class holding the result of one call stack item
Each call stack item instance has the following attributes:
caller = ea of caller
displ = display string
sp = stack pointer
"""
def __init__(self, caller, sp):
self.caller = caller
self.sp = sp
f = idaapi.get_func(caller)
self.displ = "%08x: " % caller
if f:
self.displ += idc.GetFunctionName(caller)
t = caller - f.startEA
if t > 0: self.displ += "+" + hex(t)
else:
self.displ += hex(caller)
self.displ += " [" + hex(sp) + "]"
def __str__(self):
return self.displ
# get stack pointer
sp = cpu.Esp
seg = idaapi.getseg(sp)
if not seg:
return (False, "Could not locate stack segment!")
stack_seg = Seg(seg)
word_size = 2 ** (seg.bitness + 1)
callers = []
sp = cpu.Esp - word_size
while sp < stack_seg.endEA:
sp += word_size
ptr = idautils.GetDataList(sp, 1, word_size).next()
seg = idaapi.getseg(ptr)
# only accept executable segments
if (not seg) or ((seg.perm & idaapi.SEGPERM_EXEC) == 0):
continue
# try to find caller
caller = IsPrevInsnCall(ptr)
# we have no recognized caller, skip!
if caller is None:
continue
# do we have a debug name that is near?
if nn:
ret = nn.find(caller)
if ret:
ea = ret[0]
# function exists?
f = idaapi.get_func(ea)
if not f:
# create function
idc.MakeFunction(ea, idaapi.BADADDR)
# get the flags
f = idc.GetFlags(caller)
# no code there?
if not isCode(f):
MakeCode(caller)
callers.append(Result(caller, sp))
#
return (True, callers)
# -----------------------------------------------------------------------
# Chooser class
class CallStackWalkChoose(Choose):
def __init__(self, list, title):
Choose.__init__(self, list, title)
self.width = 250
def enter(self, n):
o = self.list[n-1]
idc.Jump(o.caller)
# -----------------------------------------------------------------------
def main():
if not idaapi.is_debugger_on():
idc.Warning("Please run the process first!")
return
if idaapi.get_process_state() != -1:
idc.Warning("Please suspend the debugger first!")
return
# only avail from IdaPython r232
if hasattr(idaapi, "NearestName"):
# get all debug names
dn = idaapi.get_debug_names(idaapi.cvar.inf.minEA, idaapi.cvar.inf.maxEA)
# initiate a nearest name search (using debug names)
nn = idaapi.NearestName(dn)
else:
nn = None
ret, callstack = CallStackWalk(nn)
if ret:
title = "Call stack walker (thread %X)" % (GetCurrentThreadId())
idaapi.close_chooser(title)
c = CallStackWalkChoose(callstack, title)
c.choose()
else:
idc.Warning("Failed to walk the stack:" + callstack)
# -----------------------------------------------------------------------
main()