-
Notifications
You must be signed in to change notification settings - Fork 52
/
findSyscallTable.py
executable file
·212 lines (168 loc) · 5.2 KB
/
findSyscallTable.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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# This script is part of the IDA IOS Toolkit
# (C) Copyright 2011 Stefan Esser
# This script searches the iOS syscall table within the iOS kernelcache
# without relying on symbols because Apple loves to either remove them
# or to move them around
from idaapi import *
from idc import *
import idautils
import re
def parsesyscallsmaster(filename):
'''
parsesyscallsmaster: filename
Parses the syscalls.master file to retrieve names and arguments
for syscalls.
'''
syscallinfo = {}
syscalldef_re = re.compile('(?P<num>[0-9]+)\s+([a-zA-Z0-9_]+)\s+([a-zA-Z0-9_]+)\s+\{\s+([a-zA-Z0-9_]+)\s+([a-zA-Z0-9_]+)\s*\(([^)]+)\)')
try:
with open(filename) as f:
for line in f:
m = syscalldef_re.match(line)
if m:
num = int(m.group(1))
name = m.group(5)
params = m.group(6)
if not syscallinfo.has_key(num):
syscallinfo[num] = { "name" : name, "params" : params }
else:
# Overwrite if not nosys
if name != "nosys":
syscallinfo[num] = { "name" : name, "params" : params }
except IOError, err:
pass
return syscallinfo
def registersysentstruct():
'''
registersysentstruct:
Registers the 'sysent' struct type in IDA.
'''
strsysent = """
struct sysent {
unsigned short sy_narg;
unsigned char sy_resv;
unsigned char sy_flags;
void (*sy_call)();
void (*sy_arg_munge32)();
void (*sy_arg_munge64)();
unsigned int sy_return_types;
unsigned short sy_arg_bytes;
};
"""
r = idc.SetLocalType(-1, strsysent, 0)
r = Til2Idb(-1, "sysent")
def findsyscalltable(syscallinfo):
'''
findsyscalltable: syscallinfo
Searches for the syscall table inside the iOS kernel
binary. Search is performed inside the __data segment
and syscall table is recognized by a combination of
a pattern match for the first syscall handler and the
assumption that the syscall table is immediately
followed by the nsysent variable that contains the
number of syscall handlers.
'''
# retrieve information about 'sysent' struct
sid = get_struc_id("sysent")
ssize = get_struc_size(sid)
if ssize == 0:
print "Could not load information about 'sysent' struct"
return
# text segment
textsegment = get_segm_by_name("__text")
if not textsegment:
print "Could not find __text segment"
return
# syscall table is assumed to be found in section ''
syscalltablesegment = get_segm_by_name("__data")
if not syscalltablesegment:
print "Could not find segment __data"
return
curEA = syscalltablesegment.startEA
endEA = syscalltablesegment.endEA
while curEA < endEA:
# signature of first syscall entry
#
# 0x00000000
# handler
# 0x00000000
# 0x00000000
# 0x00000001
# 0x00000000
curEA += 4
if idc.Dword(curEA) != 0:
continue
handler = idc.Dword(curEA+4)
if handler < textsegment.startEA:
continue
if handler > textsegment.endEA:
continue
if idc.Dword(curEA+8) != 0:
continue
if idc.Dword(curEA+12) != 0:
continue
if idc.Dword(curEA+16) != 1:
continue
if idc.Dword(curEA+20) != 0:
continue
# Passed signature of syscall handler 0
# now check if we can find the end
# Assume a syscall table longer than 32 entries
cnt = 31
innerEA = curEA + 31 * ssize
while innerEA < endEA:
innerEA += ssize
cnt += 1
# Assume less than 700 syscalls for now
if cnt > 700:
break
nsys = idc.Dword(innerEA)
if nsys == cnt:
# Found end of syscall table
# mark nsysent variable
idc.MakeDword(innerEA)
idaapi.set_name(innerEA, "_nsysent", 0)
# mark syscall table as an array of struct 'sysent'
idaapi.do_unknown_range(curEA, ssize * nsys, DOUNK_DELNAMES)
idaapi.set_name(curEA, "_sysent", 0)
idaapi.doStruct(curEA, ssize, sid)
idc.MakeArray(curEA, nsys)
idc.SetArrayFormat(curEA, AP_INDEX | AP_IDXDEC, 1, -1)
# loop through all syscall handlers and set functype
i = 0
ht = {}
while i < nsys:
handler = idc.Dword(curEA + ssize * i + 4)
# We have to support ARM THUMB code
addr = handler & 0xFFFFFFFE
if syscallinfo.has_key(i):
si = syscallinfo[i]
# check for name collision
if not ht.has_key(handler):
ht[handler] = si["name"]
# set name of syscall
idaapi.set_name(addr, "_" + si["name"])
if si["params"] == "void":
typestring = "void"
else:
typestring = "struct %s_args" % (si["name"])
params = si["params"].replace(",", ";")
print typestring + "{" + params + ";};"
r = idc.SetLocalType(-1, typestring + "{" + params + ";};", 0)
Til2Idb(-1, "%s_args" % (si["name"]))
functype = "int xxx(proc_t p, %s *uap, int *retval);" % (typestring)
r = idc.SetType(addr, functype)
i = i + 1
# notify user
print "Found syscall table _sysent at %08x" % (curEA)
print "Number of entries in syscall table _nsysent = %u" % (nsys)
print "Syscall number count _nsysent is at %08x" % (innerEA)
idc.Jump(curEA)
return
if __name__ == '__main__':
registersysentstruct()
fname = idc.AskFile(0, "syscalls.master", "Find /bsd/kern/syscalls.master from XNU source code")
syscallinfo = parsesyscallsmaster(fname)
findsyscalltable(syscallinfo)
print "Done."
# TODO: fix problems with unknown types