| Trees | Indices | Help |
|
|---|
|
|
1 # Rekall Memory Forensics
2 # Copyright (C) 2012 Michael Cohen
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 #
19
20 """This module implements the rekall session.
21
22 The session stores information about the a specific user interactive
23 session. Sessions can be saved and loaded between runs and provide a convenient
24 way for people to save their own results.
25 """
26
27 __author__ = "Michael Cohen <scudette@gmail.com>"
28
29 import logging
30 import os
31 import pdb
32 import sys
33 import time
34 import traceback
35 import weakref
36
37 from rekall import cache
38 from rekall import config
39 from rekall import constants
40 from rekall import io_manager
41 from rekall import kb
42 from rekall import obj
43 from rekall import plugin
44
45 from rekall.ui import renderer
46 from rekall_lib import registry
47 from rekall_lib import utils
48
49
50 config.DeclareOption(
51 "--repository_path", default=[], type="ArrayStringParser",
52 help="Path to search for profiles. This can take "
53 "any form supported by the IO Manager (e.g. zip files, "
54 "directories, URLs etc)")
55
56 config.DeclareOption("-f", "--filename",
57 help="The raw image to load.")
58
59 config.DeclareOption(
60 "--buffer_size", default=20 * 1024 * 1024,
61 type="IntParser",
62 help="The maximum size of buffers we are allowed to read. "
63 "This is used to control Rekall memory usage.")
64
65 config.DeclareOption(
66 "--output", default=None,
67 help="If specified we write output to this file.")
68
69 config.DeclareOption(
70 "--max_collector_cost", default=4, type="IntParser",
71 help="If specified, collectors with higher cost will not be used.")
72
73 config.DeclareOption(
74 "--home", default=None,
75 help="An alternative home directory path. If not set we use $HOME.")
76
77 config.DeclareOption(
78 "--logging_format",
79 default="%(asctime)s:%(levelname)s:%(name)s:%(message)s",
80 help="The format string to pass to the logging module.")
81
82 config.DeclareOption(
83 "--performance", default="normal", type="Choices",
84 choices=["normal", "fast", "thorough"],
85 help="Tune Rekall's choice of algorithms, depending on performance "
86 "priority.")
87
88 LIVE_MODES = ["API", "Memory"]
89
90 config.DeclareOption(
91 "--live", default=None, type="Choice", required=False,
92 choices=LIVE_MODES, help="Enable live memory analysis.")
97
100 """A runner for a specific plugin."""
101
103 super(PluginRunner, self).__init__(session.RunPlugin, plugin_name)
104 self.plugin_name = plugin_name
105 self.session = session
106
108 """Return metadata about this plugin."""
109 plugin_class = getattr( # pylint: disable=protected-access
110 self.session.plugins, self.plugin_name)._target
111 return config.CommandMetadata(plugin_class).Metadata()
112
115 """A container for plugins.
116
117 Dynamically figures out which plugins are active given the current session
118 state (profile, image file etc).
119 """
120
124
126 """Return the active plugin class that implements plugin name.
127
128 Plugins may not be active depending on the current configuration.
129 """
130 # Try to see if the requested plugin is active right now.
131 metadata = self.plugin_db.GetActivePlugin(name)
132 if metadata == None:
133 return metadata
134
135 return metadata.plugin_cls
136
139
141 """Gets a wrapped active plugin class.
142
143 A convenience function that returns a curry wrapping the plugin class
144 with the session parameter so users do not need to explicitly pass the
145 session.
146
147 This makes it easy to use in the interactive console:
148
149 pslist_plugin = plugins.pslist()
150 """
151 plugin_cls = self.GetPluginClass(name)
152 if plugin_cls == None:
153 return plugin_cls
154
155 return obj.Curry(plugin_cls, session=self.session)
156
162
165 """Like a PluginContainer but returns plugin runners."""
166
168 plugin_cls = self.GetPluginClass(name)
169 if plugin_cls == None:
170 return plugin_cls
171
172 return PluginRunner(self.session, name)
173
176 """The session's configuration is managed through this object.
177
178 The session can be configured using the SetParameter() method. However,
179 sometimes when a certain parameter is modified, some code needs to run in
180 response. For example, if the filename is modified, the profile must be
181 recalculated.
182
183 It is not sufficient to attach setter methods to every such parameter
184 though, because there is no guarantee which order these parameters are
185 configured. For example, suppose we want to set both the filename and the
186 profile:
187
188 session.SetParameter("filename", filename)
189 session.SetParameter("profile", "nt/...")
190
191 Since the profile is explicitly set we should not guess it, but if a simple
192 set hook is used, there is no way for the _set_filename() hook to determine
193 that the profile is explicitly given. So what will happen now is that the
194 filename will be changed, then a profile will be autodetected, then it will
195 be immediately overwritten with the user set profile.
196
197 To avoid this issue we use a context manager to essentially group
198 SetParameter() calls into an indivisible unit. The hooks are all run _after_
199 all the parameters are set:
200
201 with session:
202 session.SetParameter("filename", filename)
203 session.SetParameter("profile", "nt/...")
204
205 Now the _set_filename() hook can see that the profile is explicitly set so
206 it should not be auto-detected.
207
208 Upon entering the context manager, we create a new temporary place to store
209 configuration parameters. Then, when exiting the context manager we ensure
210 that those parameters with hooks are called. The hooks are passed the newly
211 set parameters. Each hook returns the value that will actually be set in the
212 session (so the hook may actually modify the value).
213 """
214 # The session which owns this configuration object.
215 session = None
216
217 # This holds a write lock on the configuration object.
218 _lock = False
219 _pending_parameters = None
220 _pending_hooks = None
221
222 _loaded_filename = None
223
225 super(Configuration, self).__init__(**kwargs)
226 self.session = session
227
228 # These will be processed on exit from the context manager.
229 self._pending_parameters = {}
230 self._pending_hooks = []
231
232 # Can not update the configuration object any more.
233 self._lock = 1
234
237
239 if live is not None and not self.live:
240 if isinstance(live, basestring):
241 live = [live]
242
243 # Default is to use Memory analysis.
244 if len(live) == 0:
245 mode = "Memory"
246 elif len(live) == 1:
247 mode = live[0]
248 else:
249 raise RuntimeError(
250 "--live parameter should specify only one mode.")
251
252 live_plugin = self.session.plugins.live(mode=mode)
253 live_plugin.live()
254
255 # When the session is destroyed, close the live plugin.
256 self.session.register_flush_hook(self, live_plugin.close)
257
258 return live
259
261 """Ensure the home directory is valid."""
262 if home:
263 home = os.path.abspath(home)
264 if not os.path.isdir(home):
265 raise ValueError("Home directory must be a directory.")
266 else:
267 home = config.GetHomeDir(self.session)
268
269 # We must update the environment so things like os.path.expandvars
270 # work.
271 os.environ["HOME"] = home
272 return home
273
275 """Callback for when a filename is set in the session.
276
277 When the user changes the filename parameter we must reboot the session:
278
279 - Reset the cache.
280 - Update the filename
281 - Reload the profile and possibly autodetect it.
282 """
283 if filename:
284 # This is used by the ipython prompt.
285 self.Set('base_filename', os.path.basename(filename))
286
287 # Reset any caches.
288 if self.session:
289 self.session.Reset()
290
291 # If a profile is not configured at this time, we need to auto-detect
292 # it.
293 if 'profile' not in parameters:
294 # Clear the existing profile which will trigger profile
295 # autodetection on the new image.
296 del self['profile']
297 self['filename'] = filename
298
299 return filename
300
302 """Update the tracked modules.
303
304 When someone updates the build local tracked parameter we need to remove
305 them from all the address resolver caches.
306 """
307 # Clear all profile caches in address resolver contexts.
308 for context in self.session.context_cache.values():
309 context.reset()
310
311 return set(tracked)
312
314 # Flush the profile cache if we change the profile path.
315 self.session.profile_cache = {}
316
317 return profile_path
318
320 """This is triggered when someone explicitly sets the profile.
321
322 We force load the profile and avoid autodetection.
323 """
324 profile_obj = self.session.LoadProfile(profile)
325 if profile_obj:
326 self.session.SetCache("profile_obj", profile_obj,
327 volatile=False)
328
329 return profile
330
332 if isinstance(level, basestring):
333 level = getattr(logging, level.upper(), logging.INFO)
334
335 if level == None:
336 return
337
338 self.session.logging.debug("Logging level set to %s", level)
339 self.session.logging.setLevel(int(level))
340 if isinstance(self.session, InteractiveSession):
341 # Also set the root logging level, to reflect it in the console.
342 logging.getLogger().setLevel(int(level))
343
344 # Create subloggers and suppress their logging level.
345 for log_domain in constants.LOG_DOMAINS:
346 logger = self.session.logging.getChild(log_domain)
347 logger.setLevel(logging.WARNING)
348
350 for domain in domains:
351 logger = self.session.logging.getChild(domain)
352 logger.setLevel(logging.DEBUG)
353
355 formatter = logging.Formatter(fmt=logging_format)
356
357 # Set the logging format on the console
358 root_logger = logging.getLogger()
359 if not root_logger.handlers:
360 logging.basicConfig(format=logging_format)
361 else:
362 for handler in root_logger.handlers:
363 handler.setFormatter(formatter)
364
365 # Now set the format of our custom handler(s).
366 for handler in self.session.logging.handlers:
367 handler.setFormatter(formatter)
368
373
377
379 if self.Get("session_id") == None:
380 return session_id
381
382 # We are allowed to set a session id which is not already set.
383 for session in self.session.session_list:
384 if session_id == session.session_id:
385 raise RuntimeError("Session_id clashes with existing session.")
386
387 return session_id
388
390 hook = getattr(self, "_set_%s" % attr, None)
391 if hook:
392 # If there is a set hook we must use the context manager.
393 if self._lock > 0:
394 raise ValueError(
395 "Can only update attribute %s using the context manager." %
396 attr)
397
398 if attr not in self._pending_hooks:
399 self._pending_hooks.append(attr)
400
401 self._pending_parameters[attr] = value
402 else:
403 super(Configuration, self).Set(attr, value)
404
410
415
417 self._lock += 1
418
419 # Run all the hooks _after_ all the parameters have been set.
420 if self._lock == 1:
421 while self._pending_hooks:
422 hooks = list(reversed(self._pending_hooks))
423 self._pending_hooks = []
424
425 # Allow the hooks to call Set() by temporarily entering the
426 # context manager.
427 with self:
428 # Hooks can call Set() which might add more hooks.
429 for attr in hooks:
430 hook = getattr(self, "_set_%s" % attr)
431 value = self._pending_parameters[attr]
432
433 res = hook(value, self._pending_parameters)
434 if res is None:
435 res = value
436
437 self._pending_parameters[attr] = res
438
439 self.update(**self._pending_parameters)
440 self._pending_parameters = {}
441
443 """Print the contents somewhat concisely."""
444 result = []
445 for k, v in self.iteritems():
446 if isinstance(v, obj.BaseObject):
447 v = repr(v)
448
449 value = "\n ".join(str(v).splitlines())
450 if len(value) > 100:
451 value = "%s ..." % value[:100]
452
453 result.append(" %s = %s" % (k, value))
454
455 return "{\n" + "\n".join(sorted(result)) + "\n}"
456
459 """An object to manage progress calls.
460
461 Since Rekall must be usable as a library it can not block for too
462 long. Rekall makes continuous reports of its progress to the
463 ProgressDispatcher, which then further dispatches them to other
464 callbacks. This allows users of the Rekall library to be aware of how
465 analysis is progressing. (e.g. to report it in a GUI).
466 """
467
469 self.callbacks = {}
470
472 self.callbacks[key] = callback
473
475 self.callbacks.pop(key, 0)
476
480
483 """A logging LogHandler that stores messages as long as a renderer hasn't
484 been assigned to it. Used to keep all messages that happen in Rekall before
485 a plugin has been initialized or run at all, to later send them to a
486 renderer.
487 """
488
490 self.logrecord_buffer = []
491 self.renderer = None
492 super(HoardingLogHandler, self).__init__(*args, **kwargs)
493
495 """Deliver a message if a renderer is defined or store it, otherwise."""
496 if not self.renderer:
497 self.logrecord_buffer.append(record)
498 else:
499 self.renderer.Log(record)
500
502 """Sets the renderer so messages can be delivered."""
503 self.renderer = renderer_obj
504 self.Flush()
505
513
516 """Base session.
517
518 This session contains the bare minimum to use rekall.
519 """
520
521 # We only serialize the following session variables since they make this
522 # session unique. When we unserialize we merge the other state variables
523 # from this current session.
524 #
525 # TODO: This is, for the moment, necessary to support the web UI. Come up
526 # with a better way to represent or generate this list.
527 SERIALIZABLE_STATE_PARAMETERS = [
528 ("ept", u"IntParser"),
529 ("profile", u"FileName"),
530 ("filename", u"FileName"),
531 ("pagefile", u"FileName"),
532 ("session_name", u"String"),
533 ("timezone", u"TimeZone"),
534 ]
535
536 __metaclass__ = registry.MetaclassRegistry
537
538 # The currently active address resolver.
539 _address_resolver = None
540
541 # Each session has a unique session id (within this process). The ID is only
542 # unique among the sessions currently active.
543 session_id = 0
544
545 # Privileged sessions are allowed to run dangerous plugins.
546 privileged = False
547
549 self.progress = ProgressDispatcher()
550
551 # Cache the profiles we get from LoadProfile() below.
552 self.profile_cache = {}
553
554 # A container for active plugins. This is done so that the interactive
555 # console can see which plugins are active by simply command completing
556 # on this object.
557 self.plugins = PluginContainer(self)
558
559 # When the session switches process context we store various things in
560 # this cache, so we can restore the context quickly. The cache is
561 # indexed by the current process_context which can be found from
562 # session.GetParameter("process_context").
563 self.context_cache = {}
564 self._repository_managers = []
565
566 # Store user configurable attributes here. These will be read/written to
567 # the configuration file.
568 self.state = Configuration(session=self)
569 self.cache = cache.Factory(self, "memory")
570 with self.state:
571 for k, v in kwargs.items():
572 self.state.Set(k, v)
573
574 # We use this logger if provided.
575 self.logger = kwargs.pop("logger", None)
576 self._logger = None
577
578 # Make this session id unique.
579 Session.session_id += 1
580
581 # At the start we haven't run any plugin.
582 self.last = None
583
584 # Locks for running hooks.
585 self._hook_locks = set()
586
587 # Hooks that will be called when we get flushed.
588 self._flush_hooks = []
589
590 self.renderers = []
591
592 @utils.safe_property
594 if self.logger is not None:
595 return self.logger
596
597 logger_name = u"rekall.%s" % self.session_id
598 if self._logger is None or self._logger.name != logger_name:
599 # Set up a logging object. All rekall logging must be done
600 # through the session's logger.
601 self._logger = logging.getLogger(logger_name)
602
603 # A special log handler that hoards all messages until there's a
604 # renderer that can transport them.
605 self._log_handler = HoardingLogHandler()
606
607 # Since the logger is a global it must not hold a permanent
608 # reference to the HoardingLogHandler, otherwise we may never be
609 # collected.
610 def Remove(_, l=self._log_handler):
611 l.handlers = []
612
613 self._logger.addHandler(weakref.proxy(
614 self._log_handler, Remove))
615
616 return self._logger
617
618 @utils.safe_property
622
623 @utils.safe_property
625 """The IO managers that are used to fetch profiles from the profile
626 repository.
627
628 """
629 if self._repository_managers:
630 return self._repository_managers
631
632 # The profile path is specified in search order.
633 repository_path = (self.GetParameter("repository_path") or
634 self.GetParameter("profile_path") or [])
635
636 for path in repository_path:
637 # TODO(scudette): remove this hack for 1.6 release. Github has
638 # changed their static URL access. If the user is using an old URL
639 # we warn and correct it.
640 if path in constants.OLD_DEPRECATED_URLS:
641 self.logging.warn(
642 "Rekall's profile repository is pointing to deprecated URL "
643 "(%s). Please update your ~/.rekallrc file.", path)
644 path = constants.PROFILE_REPOSITORIES[0]
645
646 try:
647 self._repository_managers.append(
648 (path, io_manager.Factory(path, session=self)))
649 except ValueError:
650 pass
651
652 if not self._repository_managers:
653 try:
654 self.logging.warn(
655 "No usable repositories were found. "
656 "Rekall Will attempt to use the local cache. "
657 "This is likely to fail if profiles are missing locally!")
658 self._repository_managers = [
659 (None, io_manager.DirectoryIOManager(
660 urn=cache.GetCacheDir(self), session=self))]
661 except IOError:
662 self._repository_managers = []
663
664 return self._repository_managers
665
670
672 self.state.__exit__(exc_type, exc_value, trace)
673
675 self.context_cache = {}
676 self.profile_cache = {}
677 self.kernel_address_space = None
678
679 # For volatile sessions we use a timed cache (which expires after a
680 # short time).
681 cache_type = self.GetParameter("cache", "memory")
682 if self.volatile:
683 cache_type = "timed"
684
685 if self.cache:
686 self.remove_flush_hook(self.cache)
687
688 self.cache = cache.Factory(self, cache_type)
689 if self.physical_address_space:
690 self.physical_address_space.ConfigureSession(self)
691
692 # Fix up the completer. This is sometimes required after the debugger
693 # steals readline focus. Typing session.Reset() fixes things again.
694 self.shell.init_completer()
695
696 @utils.safe_property
698 return self.GetParameter("default_address_space")
699
700 @utils.safe_property
702 """A convenience accessor for the address resolver implementation.
703
704 Note that the correct address resolver implementation depends on the
705 profile. For example, windows has its own address resolver, while Linux
706 and OSX have a different one.
707 """
708 # Get the current process context.
709 current_context = (self.GetParameter("process_context").obj_offset or
710 "Kernel")
711
712 # Get the resolver from the cache.
713 address_resolver = self.context_cache.get(current_context)
714 if address_resolver == None:
715 # Make a new address resolver.
716 address_resolver = self.plugins.address_resolver()
717 self.context_cache[current_context] = address_resolver
718
719 return address_resolver
720
722 """This will only get called if the attribute does not exist."""
723 return obj.NoneObject("Attribute not set")
724
726 """Returns if the session has the specified parameter set.
727
728 If False, a call to GetParameter() might trigger autodetection.
729 """
730 return (self.state.get(item) is not None or
731 self.cache.Get(item) is not None)
732
734 """Retrieves a stored parameter.
735
736 Parameters are managed by the Rekall session in two layers. The state
737 containers contains those parameters which are deliberately set by the
738 user.
739
740 Some parameters are calculated by plugins and are used in order to speed
741 up further calculations. These are cached in the state as well.
742
743 It is important to never override a user selection by the cached
744 results. Since the user must be allowed to override all parameters - for
745 example through the GUI or the command line. Therefore when resolving a
746 parameter, we first check in the state, and only if the parameter does
747 not exist, we check the cache.
748 """
749 result = self.state.get(item)
750 if result is not None:
751 return result
752
753 # None in the state dict means that the cache is empty. This is
754 # different from a NoneObject() returned (which represents a cacheable
755 # failure).
756 if cached:
757 result = self.cache.Get(item)
758 if result is not None:
759 return result
760
761 # We don't have or didn't look in the cache for the result. See if we
762 # can get if from a hook.
763 try:
764 result = self._RunParameterHook(item)
765 if result is not None:
766 return result
767 except RecursiveHookException:
768 pass
769
770 return default
771
775
777 """Sets a session parameter.
778
779 NOTE! This method should only be used for setting user provided data. It
780 must not be used to set cached data - use SetCache() instead. Parameters
781 set with this method are not cleared as part of session.Reset() and are
782 copied to cloned sessions.
783 """
784 self.state.Set(item, value)
785
787 """Launches the registered parameter hook for name."""
788 for cls in kb.ParameterHook.classes.values():
789 if cls.name == name and cls.is_active(self):
790 if name in self._hook_locks:
791 # This should never happen! If it does then this will block
792 # in a loop so we fail hard.
793 raise RecursiveHookException(
794 "Trying to invoke hook %s recursively!" % name)
795
796 try:
797 self._hook_locks.add(name)
798 hook = cls(session=self)
799 result = hook.calculate()
800
801 # Cache the output from the hook directly.
802 self.SetCache(name, result, volatile=hook.volatile)
803 finally:
804 self._hook_locks.remove(name)
805
806 return result
807
809 """Normalize args to use _ instead of -.
810
811 So we can pass them as valid python parameters.
812 """
813 result = {}
814 for k, v in kwargs.iteritems():
815 result[k.replace("-", "_")] = v
816 return result
817
819 """Launch a plugin and its render() method automatically.
820
821 We use the pager specified in session.GetParameter("pager").
822
823 Args:
824 plugin_obj: A string naming the plugin, or the plugin instance itself.
825 *pos_args: Args passed to the plugin if it is not an instance.
826 **kwargs: kwargs passed to the plugin if it is not an instance.
827 """
828 kwargs = self._CorrectKWArgs(kwargs)
829 output = kwargs.pop("output", self.GetParameter("output"))
830 ui_renderer = kwargs.pop("format", None)
831 result = None
832
833 if ui_renderer is None:
834 ui_renderer = self.GetRenderer(output=output)
835
836 self.renderers.append(ui_renderer)
837
838 # Set the renderer so we can transport log messages.
839 self._log_handler.SetRenderer(ui_renderer)
840
841 try:
842 plugin_name = self._GetPluginName(plugin_obj)
843 except Exception as e:
844 raise ValueError(
845 "Invalid plugin_obj parameter (%s)." % repr(plugin))
846
847 # On multiple calls to RunPlugin, we need to make sure the
848 # HoardingLogHandler doesn't send messages to the wrong renderer.
849 # We reset the renderer and make it hoard messages until we have the
850 # new one.
851 self.logging.debug(
852 u"Running plugin (%s) with args (%s) kwargs (%s)",
853 plugin_name, args, utils.SmartUnicode(kwargs)[:1000])
854
855 with ui_renderer.start(plugin_name=plugin_name, kwargs=kwargs):
856 try:
857 original_plugin_obj = plugin_obj
858 plugin_obj = self._GetPluginObj(plugin_obj, *args, **kwargs)
859 if not plugin_obj:
860 raise ValueError(
861 "Invalid plugin: %s" % original_plugin_obj)
862 result = plugin_obj.render(ui_renderer) or plugin_obj
863 self.last = plugin_obj
864 except (Exception, KeyboardInterrupt) as e:
865 self._HandleRunPluginException(ui_renderer, e)
866
867 finally:
868 self.renderers.pop(-1)
869
870 # At this point, the ui_renderer will have flushed all data.
871 # Further logging will be lost.
872 return result
873
875 """Handle exceptions thrown while trying to run a plugin."""
876 _ = ui_renderer, e
877 # This method is called from exception handlers.
878 raise # pylint: disable=misplaced-bare-raise
879
881 """Extract the name from the plugin object."""
882 if isinstance(plugin_obj, basestring):
883 return plugin_obj
884
885 elif utils.issubclass(plugin_obj, plugin.Command):
886 return plugin_obj.name
887
888 elif isinstance(plugin_obj, plugin.Command):
889 return plugin_obj.name
890
892 if isinstance(plugin_obj, basestring):
893 plugin_name = plugin_obj
894
895 elif utils.issubclass(plugin_obj, plugin.Command):
896 plugin_name = plugin_obj.name
897 plugin_cls = plugin_obj
898
899 elif isinstance(plugin_obj, plugin.Command):
900 return plugin_obj
901
902 else:
903 raise TypeError(
904 "First parameter should be a plugin name or instance.")
905
906 # When passed as a string this specifies a plugin name.
907 if isinstance(plugin_obj, basestring):
908 plugin_cls = getattr(self.plugins, plugin_obj, None)
909 if plugin_cls is None:
910 self.logging.error(
911 "Plugin %s is not active. Is it supported with "
912 "this profile?", plugin_name)
913 return
914
915 # Instantiate the plugin object.
916 kwargs["session"] = self
917 return plugin_cls(*args, **kwargs)
918
920 """Try to load a profile directly by its name.
921
922 Args:
923
924 name: A string which represents the canonical name for the profile. We
925 ask all repositories in the repository_path to resolve this name
926 into a profile.
927
928 Returns:
929 a Profile() instance or a NoneObject()
930
931 """
932 if not name:
933 return obj.NoneObject("No filename")
934
935 if isinstance(name, obj.Profile):
936 return name
937
938 # We only want to deal with unix paths.
939 name = name.replace("\\", "/")
940
941 try:
942 if use_cache:
943 cached_profile = self.profile_cache[name]
944 if cached_profile:
945 return cached_profile
946
947 else:
948 return obj.NoneObject(
949 "Unable to load profile %s from any repository." %
950 name)
951
952 except KeyError:
953 pass
954
955 result = None
956 try:
957 # If the name is a path we try to open it directly:
958 container = io_manager.DirectoryIOManager(os.path.dirname(name),
959 version=None,
960 session=self)
961 data = container.GetData(os.path.basename(name))
962 if data == None:
963 raise IOError("Not found.")
964
965 result = obj.Profile.LoadProfileFromData(data, self, name=name)
966 except IOError:
967 pass
968
969 # Traverse the profile path until one works.
970 if not result:
971 # Add the last supported repository as the last fallback path.
972 for path, manager in self.repository_managers:
973 try:
974 # The inventory allows us to fail fetching the profile
975 # quickly - without making the round trip.
976 if not manager.CheckInventory(name):
977 self.logging.debug(
978 "Skipped profile %s from %s (Not in inventory)",
979 name, path)
980 continue
981
982 now = time.time()
983 result = obj.Profile.LoadProfileFromData(
984 manager.GetData(name), session=self, name=name)
985 if result:
986 self.logging.info(
987 "Loaded profile %s from %s (in %s sec)",
988 name, manager, time.time() - now)
989 break
990
991 except (IOError, KeyError) as e:
992 result = obj.NoneObject(e)
993 self.logging.debug("Could not find profile %s in %s: %s",
994 name, path, e)
995
996 continue
997
998 # Cache it for later. Note that this also caches failures so we do not
999 # retry again.
1000 self.profile_cache[name] = result
1001 if result == None:
1002 return obj.NoneObject(
1003 "Unable to load profile %s from any repository." % name)
1004
1005 return result
1006
1009
1011 """Called by the library to report back on the progress."""
1012 self.progress.Broadcast(message, *args, **kwargs)
1013
1015 """Get a renderer for this session.
1016
1017 If a renderer is currently active we just reuse it, otherwise we
1018 instantiate the renderer specified in self.GetParameter("format").
1019 """
1020 # Reuse the current renderer.
1021 if self.renderers and output is None:
1022 return self.renderers[-1]
1023
1024 ui_renderer = self.GetParameter("format", "text")
1025 if isinstance(ui_renderer, basestring):
1026 ui_renderer_cls = renderer.BaseRenderer.ImplementationByName(
1027 ui_renderer)
1028 ui_renderer = ui_renderer_cls(session=self, output=output)
1029
1030 return ui_renderer
1031
1032 @utils.safe_property
1036
1037 @physical_address_space.setter
1039 # The physical_address_space is not part of the cache because
1040 # it needs to be set first before we know which cache
1041 # fingerprint to use (getting the fingerprint depends on the
1042 # physical_address_space).
1043 self.SetParameter("physical_address_space", value)
1044 self.Reset()
1045
1046 # Ask the physical_address_space to configure this session.
1047 if value:
1048 value.ConfigureSession(self)
1049
1050 @utils.safe_property
1052 # If a process context is specified, we use the profile from the process
1053 # context.
1054 process_context = self.GetParameter("process_context").obj_profile
1055 if process_context != None:
1056 return process_context
1057
1058 res = self.GetParameter("profile_obj")
1059 return res
1060
1061 @profile.setter
1063 # Clear the profile object. Next access to it will trigger profile
1064 # auto-detection.
1065 if value == None:
1066 self.SetCache('profile_obj', value, volatile=False)
1067
1068 elif isinstance(value, basestring):
1069 with self.state:
1070 self.state.Set('profile', value)
1071
1072 elif isinstance(value, obj.Profile):
1073 self.SetCache('profile_obj', value, volatile=False)
1074 self.SetCache("profile", value.name, volatile=False)
1075 else:
1076 raise AttributeError("Type %s not allowed for profile" % value)
1077
1079 new_state = self.state.copy()
1080 # Remove the cache from the copy so we start with a fresh cache.
1081 new_state.pop("cache", None)
1082
1083 # session_ids are automatically generated so we need to pop it.
1084 new_state.pop("session_id", None)
1085
1086 session_id = self._new_session_id()
1087 old_session_name = new_state.pop("session_name", None)
1088 new_session_name = kwargs.pop(
1089 "session_name", kwargs.get(
1090 "filename", "%s (%s)" % (old_session_name, session_id)))
1091 new_session = self.__class__(
1092 session_name=new_session_name, session_id=session_id, **new_state)
1093 new_session.Reset()
1094 new_session.locals = self.locals
1095
1096 # Now override all parameters as requested.
1097 with new_session:
1098 for k, v in kwargs.iteritems():
1099 new_session.SetParameter(k, v)
1100 return new_session
1101
1103 """This hook will run when the session is closed."""
1104 self._flush_hooks.append((owner, hook, args))
1105
1107 """Removes the flush hooks set by the owner.
1108
1109 Returns the hooks so they can be called if needed.
1110 """
1111 owners_hooks = []
1112 flush_hooks = []
1113 for x in self._flush_hooks:
1114 if x[0] is owner:
1115 owners_hooks.append(x)
1116 else:
1117 flush_hooks.append(x)
1118 self._flush_hooks = flush_hooks
1119
1120 return owners_hooks
1121
1123 """Destroy this session.
1124
1125 This should be called when the session is destroyed.
1126 """
1127 for _, hook, args in self._flush_hooks:
1128 hook(*args)
1129
1132 """A namespace which dynamically reflects the currently active plugins.
1133
1134 This forms the global namespace inside the ipython interpreter shell. There
1135 are some special variables prepopulated:
1136
1137 - plugins: A PluginRunnerContainer that users can use to see which plugins
1138 are active.
1139
1140 - session: A reference to the current session.
1141 """
1142
1144 if session is None:
1145 raise RuntimeError("Session must be given.")
1146
1147 self.help_profile = None
1148 self.session = session
1149
1150 super(DynamicNameSpace, self).__init__(
1151 session=session,
1152 plugins=PluginRunnerContainer(session),
1153 **kwargs)
1154
1156 res = set(super(DynamicNameSpace, self).__iter__())
1157 res.update(self["plugins"].__dir__())
1158
1159 return iter(res)
1160
1166
1168 return list(self)
1169
1171 try:
1172 return super(DynamicNameSpace, self).__getitem__(item)
1173 except KeyError:
1174 if getattr(self["session"].plugins, item):
1175 return self._prepare_runner(item)
1176
1177 raise KeyError(item)
1178
1184
1186 """Prepare a runner to run the given plugin."""
1187 # Create a runner for this plugin.
1188 return PluginRunner(self["session"], name)
1189
1192 """The session allows for storing of arbitrary values and configuration.
1193
1194 This session contains a lot of convenience features which are useful for
1195 interactive use.
1196 """
1197
1198 # A list of tuples (session_id, session) sorted by session id. This list is
1199 # shared by all session instances! TODO: Refactor into a session group.
1200 session_list = []
1201
1203 """Creates an interactive session.
1204
1205 Args:
1206 env: If passed we use this dict as the local environment.
1207
1208 use_config_file: If True we merge the system's config file into the
1209 session. This helps set the correct profile paths for example.
1210
1211 kwargs: Arbitrary parameters to store in the session.
1212
1213 Returns:
1214 an interactive session object.
1215 """
1216 # When this session was created.
1217 self._start_time = time.time()
1218
1219 # These keep track of the last run plugin.
1220 self._last_plugin = None
1221
1222 # Fill the session with helpful defaults.
1223 self.pager = obj.NoneObject("Set this to your favourite pager.")
1224
1225 # Set the session name
1226 self.session_name = kwargs.pop("session_name", u"Default session")
1227 super(InteractiveSession, self).__init__()
1228
1229 # Prepare the local namespace for the interactive session.
1230 self.locals = DynamicNameSpace(
1231 session=self,
1232
1233 # Prepopulate the namespace with our most important modules.
1234 v=self.v,
1235
1236 # Some useful modules which should be available always.
1237 sys=sys, os=os,
1238
1239 # A list of sessions.
1240 session_list=self.session_list,
1241
1242 # Pass additional environment.
1243 **(env or {})
1244 )
1245
1246 with self.state:
1247 self.state.Set("session_list", self.session_list)
1248 self.state.Set("session_name", self.session_name)
1249 self.state.Set("session_id", self._new_session_id())
1250
1251 # Configure the session from the config file and kwargs.
1252 if use_config_file:
1253 with self.state:
1254 config.MergeConfigOptions(self.state, self)
1255
1256 with self.state:
1257 for k, v in kwargs.items():
1258 self.state.Set(k, v)
1259
1260 @utils.safe_property
1263
1265 for session in self.session_list:
1266 if session.session_id == session_id:
1267 return session
1268
1269 return None
1270
1272 new_sid = 1
1273 for session in InteractiveSession.session_list:
1274 if new_sid <= session.session_id:
1275 new_sid = session.session_id + 1
1276
1277 return new_sid
1278
1280 """Handle all exceptions thrown by logging to the console."""
1281
1282 if isinstance(e, plugin.InvalidArgs):
1283 self.logging.fatal("Invalid Args: %s" % e)
1284
1285 elif isinstance(e, plugin.PluginError):
1286 self.logging.fatal(str(e))
1287
1288 elif isinstance(e, (KeyboardInterrupt, plugin.Abort)):
1289 logging.error("Aborted\r\n")
1290
1291 else:
1292 error_status = traceback.format_exc()
1293
1294 # Report the error to the renderer.
1295 self.logging.fatal(error_status)
1296
1297 # If anything goes wrong, we break into a debugger here.
1298 if self.GetParameter("debug"):
1299 pdb.post_mortem(sys.exc_info()[2])
1300
1301 # This method is called from the exception handler - this bare raise
1302 # will preserve backtraces.
1303 raise # pylint: disable=misplaced-bare-raise
1304
1309
1313
1315 return self.__unicode__()
1316
1318 result = u"""Rekall Memory Forensics session Started on %s.
1319
1320 Config:
1321 %s
1322
1323 Cache (%r):
1324 %s
1325 """ % (time.ctime(self._start_time), self.state, self.cache, self.cache)
1326 return result
1327
1329 items = self.__dict__.keys() + dir(self.__class__)
1330
1331 return [x for x in items if not x.startswith("_")]
1332
1334 """Creates a new session and adds it to the list.
1335
1336 Returns:
1337 the new session.
1338 """
1339 session_id = kwargs["session_id"] = self._new_session_id()
1340 if "session_name" not in kwargs:
1341 # Make a unique session name.
1342 kwargs["session_name"] = u"%s (%s)" % (
1343 kwargs.get("filename", session_id), session_id)
1344
1345 new_session = self.__class__()
1346 new_session.locals = self.locals
1347 # pylint: disable=protected-access
1348 new_session._repository_managers = self._repository_managers
1349 new_session.profile_cache = self.profile_cache
1350
1351 with new_session:
1352 for k, v in kwargs.iteritems():
1353 new_session.SetParameter(k, v)
1354
1355 self.session_list.append(new_session)
1356 self.session_list.sort(key=lambda x: x.session_id)
1357
1358 return new_session
1359
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Oct 9 03:29:54 2017 | http://epydoc.sourceforge.net |