Package rekall :: Package plugins :: Package response :: Module common
[frames] | no frames]

Source Code for Module rekall.plugins.response.common

  1  # Rekall Memory Forensics 
  2  # 
  3  # Copyright 2016 Google Inc. All Rights Reserved. 
  4  # 
  5  # Authors: 
  6  # Michael Cohen <scudette@google.com> 
  7  # 
  8  # This program is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or (at 
 11  # your option) any later version. 
 12  # 
 13  # This program is distributed in the hope that it will be useful, but 
 14  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 16  # General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with this program; if not, write to the Free Software 
 20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 21  # 
 22   
 23  """This module adds support for incident response to Rekall.""" 
 24   
 25  __author__ = "Michael Cohen <scudette@google.com>" 
 26  import platform 
 27  import os 
 28  import stat 
 29   
 30  import arrow 
 31   
 32  from efilter.protocols import associative 
 33  from efilter.protocols import structured 
 34   
 35  from rekall import addrspace 
 36  from rekall import obj 
 37  from rekall import plugin 
 38   
 39  from rekall_lib import registry 
 40  from rekall_lib import utils 
 41   
 42  # Will be registered by OS specific implementations. 
 43  IRProcessAddressSpace = None 
 44  IRProfile = None 
45 46 47 -class APIDummyPhysicalAddressSpace(addrspace.BaseAddressSpace):
48 __image = True 49 volatile = True 50
51 - def __init__(self, base=None, session=None, **kwargs):
52 self.as_assert(base is None) 53 self.as_assert(session.GetParameter("live_mode") == "API") 54 55 with session: 56 session.SetParameter("profile", APIBaseProfile(session=session)) 57 58 super(APIDummyPhysicalAddressSpace, self).__init__( 59 session=session, **kwargs)
60
61 62 -class APIBaseProfile(obj.Profile):
63 """A class representing the profile for IR (live) analysis.""" 64 65 # This profile is used for API based analysis. 66 METADATA = dict(live=True, type="API", 67 os=platform.system())
68
69 70 -class FileSpec(utils.SlottedObject):
71 """Specification of a file path.""" 72 73 __slots__ = ("name", "filesystem", "path_sep") 74 75 default_path_sep = "\\" if platform.system() == "Windows" else "/" 76
77 - def __init__(self, filename, filesystem=u"API", path_sep=None):
78 super(FileSpec, self).__init__() 79 80 if isinstance(filename, FileSpec): 81 # Copy the other file spec. 82 self.name = filename.name 83 self.filesystem = filename.filesystem 84 self.path_sep = filename.path_sep 85 86 elif isinstance(filename, basestring): 87 self.name = utils.SmartUnicode(filename) 88 self.filesystem = filesystem 89 self.path_sep = path_sep or self.default_path_sep 90 91 else: 92 raise TypeError("Filename must be a string or file spec not %s." % type( 93 filename))
94 95 @property
96 - def dirname(self):
97 return FileSpec(filename=self.join(self.components()[:-1]), 98 path_sep=self.path_sep, 99 filesystem=self.filesystem)
100 101 @property
102 - def basename(self):
103 components = self.components() 104 if components: 105 return components[-1] 106 return ""
107
108 - def components(self):
109 return filter(None, self.name.split(self.path_sep))
110
111 - def os_path(self):
112 """Get a path suitable to be used with os APIs.""" 113 result = os.path.sep.join(self.components()) 114 # On unix systems we anchor at root but on windows the drive 115 # name should be first. 116 if os.path.sep == "/": 117 result = "/" + result 118 119 return result
120
121 - def __str__(self):
122 return self.name
123
124 - def join(self, components):
125 result = self.path_sep.join(components) 126 # Since all paths are absolute, Unix style paths always have a 127 # leading /. 128 if self.path_sep == "/": 129 return self.path_sep + result 130 131 # But Windows paths usually have the drive as the first 132 # component. 133 return result
134
135 - def add(self, component):
136 components = self.components() 137 components.extend(component.split(self.path_sep)) 138 139 return FileSpec(filename=self.join(components), path_sep=self.path_sep, 140 filesystem=self.filesystem)
141
142 143 -class User(utils.SlottedObject):
144 """A class to represent a user.""" 145 146 __slots__ = ("session", "uid", "username", "homedir", "shell") 147 148 @classmethod 149 @registry.memoize
150 - def from_uid(cls, uid):
151 result = cls() 152 153 # Only available on Linux. 154 try: 155 import pwd 156 157 record = pwd.getpwuid(uid) 158 result.uid = uid 159 result.username = record.pw_name 160 result.homedir = record.pw_dir 161 result.shell = record.pw_shell 162 except (KeyError, ImportError): 163 pass 164 165 return result
166
167 - def __int__(self):
168 return self.uid
169
170 - def __unicode__(self):
171 if self.username: 172 return u"%s (%s)" % (self.username, self.uid) 173 elif self.uid is not None: 174 return unicode(self.uid) 175 176 return ""
177
178 179 -class Group(utils.SlottedObject):
180 """A class to represent a user.""" 181 182 __slots__ = ("session", "gid", "group_name") 183 184 @classmethod 185 @registry.memoize
186 - def from_gid(cls, gid):
187 result = cls() 188 189 # Only available on Linux. 190 try: 191 import grp 192 193 record = grp.getgrgid(gid) 194 result.gid = gid 195 result.group_name = record.gr_name 196 except (KeyError, ImportError): 197 pass 198 199 return result
200
201 - def __int__(self):
202 return self.gid
203
204 - def __unicode__(self):
205 if self.group_name: 206 return u"%s (%s)" % (self.group_name, self.gid) 207 208 elif self.gid is not None: 209 return unicode(self.gid) 210 211 return ""
212
213 214 -class FileInformation(utils.SlottedObject):
215 """An object representing a file on disk. 216 217 This FileInformation uses the API to read data about the file. 218 """ 219 220 __slots__ = ("session", "st_mode", "st_ino", "st_size", 221 "st_dev", "st_nlink", "st_uid", "st_gid", "st_mtime", 222 "st_atime", "st_ctime", "filename") 223
224 - def __init__(self, session=None, filename=None, **kwargs):
225 super(FileInformation, self).__init__(**kwargs) 226 # Ensure the filename is a filespec. 227 self.filename = FileSpec(filename) 228 self.session = session
229 230 @classmethod
231 - def from_stat(cls, filespec, session=None):
232 filespec = FileSpec(filespec) 233 result = FileInformation(filename=filespec, session=session) 234 235 try: 236 path = filespec.os_path() 237 s = os.lstat(path) 238 except (IOError, OSError) as e: 239 return obj.NoneObject("Unable to stat %s", e) 240 241 result.st_mode = Permissions(s.st_mode) 242 result.st_ino = s.st_ino 243 result.st_size = s.st_size 244 result.st_dev = s.st_dev 245 result.st_nlink = s.st_nlink 246 result.st_uid = User.from_uid(s.st_uid) 247 result.st_gid = Group.from_gid(s.st_gid) 248 result.st_mtime = s.st_mtime 249 result.st_atime = s.st_atime 250 result.st_ctime = s.st_ctime 251 252 return result
253 254 @property
255 - def mtime(self):
256 return arrow.Arrow.fromtimestamp(self.st_mtime)
257 258 @property
259 - def atime(self):
260 return arrow.Arrow.fromtimestamp(self.st_atime)
261 262 @property
263 - def ctime(self):
264 return arrow.Arrow.fromtimestamp(self.st_ctime)
265
266 - def open(self):
267 try: 268 return open(self.filename.os_path(), "rb") 269 except (IOError, OSError) as e: 270 return obj.NoneObject("Unable to open file: %s", e)
271
272 - def list_names(self):
273 if not self.st_mode.is_dir(): 274 return [] 275 276 filename = self.filename.os_path() 277 try: 278 # Adding the separator forces listing as a directory. 279 return os.listdir(filename + os.path.sep) 280 except (OSError, IOError): 281 return []
282
283 - def list(self):
284 """If this is a directory return a list of children.""" 285 if not self.st_mode.is_dir(): 286 return 287 288 filename = self.filename.os_path() 289 try: 290 for name in os.listdir(filename): 291 yield self.filename.add(name) 292 except (OSError, IOError): 293 pass
294
295 296 -class Permissions(object):
297 298 """An object to represent permissions.""" 299 __metaclass__ = registry.UniqueObjectIdMetaclass 300 301 # Taken from Python3.3's stat.filemode. 302 _filemode_table = ( 303 ((stat.S_IFLNK, "l"), 304 (stat.S_IFREG, "-"), 305 (stat.S_IFBLK, "b"), 306 (stat.S_IFDIR, "d"), 307 (stat.S_IFCHR, "c"), 308 (stat.S_IFIFO, "p")), 309 310 ((stat.S_IRUSR, "r"),), 311 ((stat.S_IWUSR, "w"),), 312 ((stat.S_IXUSR|stat.S_ISUID, "s"), 313 (stat.S_ISUID, "S"), 314 (stat.S_IXUSR, "x")), 315 316 ((stat.S_IRGRP, "r"),), 317 ((stat.S_IWGRP, "w"),), 318 ((stat.S_IXGRP|stat.S_ISGID, "s"), 319 (stat.S_ISGID, "S"), 320 (stat.S_IXGRP, "x")), 321 322 ((stat.S_IROTH, "r"),), 323 ((stat.S_IWOTH, "w"),), 324 ((stat.S_IXOTH|stat.S_ISVTX, "t"), 325 (stat.S_ISVTX, "T"), 326 (stat.S_IXOTH, "x")) 327 ) 328
329 - def __init__(self, value):
330 self.value = int(value)
331
332 - def filemode(self, mode):
333 """Convert a file's mode to a string of the form '-rwxrwxrwx'.""" 334 perm = [] 335 for table in self._filemode_table: 336 for bit, char in table: 337 if mode & bit == bit: 338 perm.append(char) 339 break 340 else: 341 perm.append("-") 342 return "".join(perm)
343
344 - def __int__(self):
345 return self.value
346
347 - def __str__(self):
348 return self.filemode(self.value)
349
350 - def __unicode__(self):
351 return self.filemode(self.value)
352
353 - def is_dir(self):
354 return stat.S_ISDIR(self.value)
355
358
359 360 -class AbstractIRCommandPlugin(plugin.TypedProfileCommand, 361 plugin.ProfileCommand):
362 """A base class for all IR plugins. 363 364 IR Plugins are only active when the session is live. 365 """ 366 367 __abstract = True 368 369 PROFILE_REQUIRED = False 370 mode = "mode_live"
371
372 373 -class AbstractAPICommandPlugin(plugin.TypedProfileCommand, 374 plugin.ProfileCommand):
375 """A base class for all API access plugins. 376 377 These plugins are only active when we are in pure API mode (e.g. API 378 pslist). 379 """ 380 381 __abstract = True 382 383 PROFILE_REQUIRED = False 384 mode = "mode_live_api"
385 386 387 FILE_SPEC_DISPATCHER = dict(API=FileInformation)
388 389 390 -def FileFactory(filename, session=None):
391 """Return the correct FileInformation class from the filename. 392 393 Currently we only support OS API accessible files, but in the future we will 394 also support NTFS files. 395 """ 396 # Ensure this is a valid file spec. 397 filespec = FileSpec(filename) 398 399 handler = FILE_SPEC_DISPATCHER.get(filespec.filesystem) 400 if handler: 401 try: 402 return handler.from_stat(filespec, session=session) 403 except (IOError, OSError): 404 405 return obj.NoneObject("Cant open %s", filespec) 406 407 raise RuntimeError("Unsupported file spec type %s" % 408 filespec.filesystem)
409 410 411 # Efilter Glue code. 412 associative.IAssociative.implement( 413 for_type=FileInformation, 414 implementations={ 415 associative.select: getattr, 416 associative.reflect_runtime_key: structured.reflect_runtime_member, 417 associative.getkeys_runtime: structured.getmembers_runtime 418 } 419 ) 420 421 associative.IAssociative.implement( 422 for_type=FileSpec, 423 implementations={ 424 associative.select: getattr, 425 associative.reflect_runtime_key: lambda c: str, 426 associative.getkeys_runtime: lambda _: ["name"] 427 } 428 ) 429