Package rekall :: Package plugins :: Package darwin :: Module checks
[frames] | no frames]

Source Code for Module rekall.plugins.darwin.checks

  1  # Rekall Memory Forensics 
  2  # 
  3  # Copyright 2013 Google Inc. All Rights Reserved. 
  4  # 
  5  # This program is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation; either version 2 of the License, or (at 
  8  # your option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, but 
 11  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 13  # General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with this program; if not, write to the Free Software 
 17  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 18  """Plugins for checking internal consistancy of pointers.""" 
 19   
 20  __author__ = "Michael Cohen <scudette@google.com>" 
 21   
 22   
 23  from rekall import obj 
 24  from rekall import scan 
 25  from rekall.plugins.darwin import common 
 26  from rekall_lib import utils 
27 28 29 -class DarwinFindSysent(common.AbstractDarwinParameterHook):
30 """Find sysent by scanning around nsysent. 31 32 The production kernel no longer ships with the 'sysent' symbol, 33 which is the address of the syscall switch table. However, because 34 sysent and nsysent are initialized around the same time it works out that 35 they are always near each other. 36 37 This is an old technique, documented around the internet, for example here: 38 https://reverse.put.as/2010/11/27/a-semi-automated-way-to-find-sysent/ 39 """ 40 41 name = "sysent_scan" 42 43 # Start looking 2560 pages below nsysent. 44 SYSENT_REL_OFFSET = -0x1000 * 0x1000 45 46 # Scan at most 2560 pages below and above. 47 LIMIT = 0x2000 * 0x1000 48
49 - def scan(self, start, limit):
50 scanner = scan.FastStructScanner( 51 session=self.session, 52 profile=self.session.profile, 53 type_name="sysent", 54 expected_values=[ 55 {"sy_arg_bytes": 0, "sy_narg": 0}, 56 {"sy_arg_bytes": 4, "sy_narg": 1}, 57 {"sy_arg_bytes": 0, "sy_narg": 0}, 58 {"sy_arg_bytes": 12, "sy_narg": 3}], 59 address_space=self.session.default_address_space) 60 61 for hit in scanner.scan(offset=start, maxlen=limit): 62 return hit
63
64 - def calculate(self):
65 nsysent_off = self.session.profile.get_constant( 66 "_nsysent", is_address=True) 67 68 if not nsysent_off: 69 return 70 71 return self.scan(start=nsysent_off + self.SYSENT_REL_OFFSET, 72 limit=self.LIMIT)
73
74 75 -class DarwinCheckSysCalls(common.AbstractDarwinCommand):
76 """Checks the syscall table.""" 77 78 __name = "check_syscalls" 79
80 - def CheckSyscallTables(self):
81 sysenter = self.profile.get_constant_object( 82 "_sysent", 83 target="Array", 84 target_args=dict( 85 count=self.profile.get_constant_object( 86 "_nsysent", "unsigned int"), 87 target="sysent" 88 ) 89 ) 90 91 # Resolve which kernel module or symbol the entry point is to. 92 resolver = self.session.address_resolver 93 for entry in sysenter: 94 call = entry.sy_call.deref() 95 yield entry, call, resolver.format_address(call)
96
97 - def render(self, renderer):
98 renderer.table_header( 99 [("Index", "index", "6"), 100 ("Address", "address", "[addrpad]"), 101 ("Target", "target", "[addrpad]"), 102 ("Symbol", "symbol", "")]) 103 104 for i, (entry, call, symbol) in enumerate(self.CheckSyscallTables()): 105 renderer.table_row(i, entry, call, symbol)
106
107 108 -class OIDInfo(object):
109 - def __init__(self, oidp, names=None, numbers=None):
110 self.oidp = oidp 111 self._names = names or [] 112 self._numbers = numbers or [] 113 self.handler = None
114
115 - def __iter__(self):
116 oidp = self.oidp 117 while oidp: 118 if oidp.oid_name: 119 yield OIDInfo(oidp, self._names, self._numbers) 120 121 oidp = oidp.oid_link.sle_next
122 123 @utils.safe_property
124 - def perms(self):
125 return (("R" if self.oidp.oid_perms.CTLFLAG_RD else "-") + 126 ("W" if self.oidp.oid_perms.CTLFLAG_WR else "-") + 127 ("L" if self.oidp.oid_perms.CTLFLAG_LOCKED else "-"))
128 129 @utils.safe_property
130 - def arg(self):
131 """Decode the arg according to its type.""" 132 if self.oidp.oid_kind_type == "CTLTYPE_NODE": 133 if self.oidp.oid_handler: 134 return "Node" 135 else: 136 names = self._names[:] 137 names.append(self.oidp.oid_name.deref()) 138 numbers = self._numbers[:] 139 numbers.append(self.oidp.oid_number) 140 141 oid = self.oidp.oid_arg1.dereference_as("sysctl_oid_list") 142 return OIDInfo(oid.slh_first, names, numbers) 143 144 elif self.oidp.oid_kind_type == "CTLTYPE_INT": 145 return self.oidp.oid_arg1.dereference_as("int") 146 147 elif self.oidp.oid_kind_type == "CTLTYPE_STRING": 148 return self.oidp.oid_arg1.dereference_as("String") 149 150 elif self.oidp.oid_kind_type == "CTLTYPE_QUAD": 151 return self.oidp.oid_arg1.dereference_as("long long int") 152 153 elif self.oidp.oid_kind_type == "CTLTYPE_OPAQUE": 154 return self.oidp.oid_arg1.dereference_as("Void") 155 156 return obj.NoneObject("Unknown arg type")
157 158 @utils.safe_property
159 - def name(self):
160 names = self._names[:] 161 names.append(self.oidp.oid_name.deref()) 162 return ".".join(["%s" % x for x in names])
163 164 @utils.safe_property
165 - def number(self):
166 numbers = self._numbers[:] 167 numbers.append(self.oidp.oid_number) 168 return ".".join(["%s" % x for x in numbers])
169
170 171 -class DarwinSysctl(common.AbstractDarwinCommand):
172 """Dumps the sysctl database. 173 174 On OSX the kernel is configured through the sysctl mechanism. This is 175 analogous to /proc or /sysfs on Linux. The configuration space is broken 176 into MIBs - or hierarchical namespace. 177 178 https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/sysctl.8.html 179 180 For example: 181 182 net.inet.ip.subnets_are_local 183 net.inet.ip.ttl 184 net.inet.ip.use_route_genid 185 186 This is implemented via a singly linked list of sysctl_oid structs. The 187 structs can be on the following types: 188 189 - CTLTYPE_INT means this MIB will handle an int. 190 - CTLTYPE_STRING means this MIB will handle a string. 191 - CTLTYPE_QUAD means this MIB will handle a long long int. 192 - CTLTYPE_NODE means this is a node which handles a sublevel of MIBs. It is 193 actually a pointer to a new sysctl_oid_list which handles the sublevel. 194 195 """ 196 197 __name = "sysctl" 198
199 - def CheckSysctl(self):
200 sysctrl_list = self.profile.get_constant_object( 201 "_sysctl__children", "sysctl_oid_list") 202 203 oidinfo = OIDInfo(sysctrl_list.slh_first) 204 for oid in self._process(oidinfo): 205 yield oid
206
207 - def _process(self, oidinfo):
208 # Output in sorted order since its eaiser to read. 209 for oid in sorted(oidinfo, key=lambda x: x.name): 210 if isinstance(oid.arg, OIDInfo): 211 for x in self._process(oid.arg): 212 yield x 213 else: 214 yield oid
215 216 table_header = [ 217 dict(name="name", width=45), 218 dict(name="mib", width=16), 219 dict(name="perms", width=6), 220 dict(name="handler", style="address"), 221 dict(name="symbol", width=40), 222 dict(name="value") 223 ] 224
225 - def column_types(self):
226 return dict(name="", 227 mib="101.101.104", 228 perms="RWL", 229 handler=self.session.profile.Pointer(), 230 symbol=utils.FormattedAddress( 231 self.session.address_resolver, 0), 232 value="")
233
234 - def collect(self):
235 for oid in self.CheckSysctl(): 236 # Format the value nicely. 237 value = oid.arg 238 if isinstance(value, obj.Pointer): 239 value = "@ 0x%X" % int(value) 240 241 elif not value == None: 242 try: 243 value = int(value) 244 value = "0x%X (%d)" % (value, value) 245 except (ValueError, AttributeError): 246 pass 247 248 yield (oid.name, 249 oid.number, 250 oid.perms, 251 oid.oidp.oid_handler, 252 utils.FormattedAddress( 253 self.session.address_resolver, 254 oid.oidp.oid_handler), 255 value)
256
257 258 -class CheckTrapTable(common.AbstractDarwinCommand):
259 """Checks the traps table for hooks.""" 260 261 __name = "check_trap_table" 262
263 - def __init__(self, **kwargs):
264 super(CheckTrapTable, self).__init__(**kwargs) 265 266 # The mach_trap_t struct is not exported in debug symbols, but can be 267 # found here xnu-2422.1.72/bsd/dev/dtrace/systrace.c: 268 # typedef struct { 269 # int mach_trap_arg_count; 270 # kern_return_t (*mach_trap_function)(void *); 271 # #if defined(__x86_64__) 272 # mach_munge_t *mach_trap_arg_munge32; 273 # #endif 274 # int mach_trap_u32_words; 275 # #if MACH_ASSERT 276 # const char *mach_trap_name; 277 # #endif /* MACH_ASSERT */ 278 # } mach_trap_t; 279 280 # We only really care about the mach_trap_function here. 281 if self.profile.metadata("arch") == "I386": 282 offset = 4 283 else: 284 offset = 8 285 286 self.profile.add_types({ 287 "mach_trap": [16, { 288 "mach_trap_function": [offset, ["Pointer", dict( 289 target="Function" 290 )]] 291 }], 292 })
293
294 - def CheckTrapTables(self):
295 # The trap table is simply an array of pointers to functions. 296 table = self.profile.get_constant_object( 297 "_mach_trap_table", 298 target="Array", 299 target_args=dict( 300 count=self.profile.get_constant_object( 301 "_mach_trap_count", "unsigned int"), 302 target="mach_trap", 303 ) 304 ) 305 306 resolver = self.session.address_resolver 307 for i, entry in enumerate(table): 308 call = entry.mach_trap_function.deref() 309 yield i, entry, call, resolver.format_address(call)
310
311 - def render(self, renderer):
312 renderer.table_header([ 313 ("Index", "index", "[addr]"), 314 ("Address", "address", "[addrpad]"), 315 ("Target", "target", "[addrpad]"), 316 ("Symbol", "symbol", "")]) 317 318 for i, entry, call, sym_name in self.CheckTrapTables(): 319 if call == None: 320 continue 321 322 renderer.table_row(i, entry, call, sym_name or "Unknown", 323 highlight=None if sym_name else "important")
324