modules.processing.memory

Package modules :: Package processing :: Module memory |
[hide private]
[frames] | no frames]

# Source Code for Module modules.processing.memory

       1  # Copyright (C) 2010-2013 Claudio Guarnieri.
       2  # Copyright (C) 2014-2016 Cuckoo Foundation.
       3  # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
       4  # See the file 'docs/LICENSE' for copying permission.
       5
       6  import os
       7  import logging
       8  import time
       9
      10  from lib.cuckoo.common.abstracts import Processing
      11  from lib.cuckoo.common.config import Config
      12  from lib.cuckoo.common.constants import CUCKOO_ROOT
      13
      14  log = logging.getLogger(__name__)
      15
      16  try:
      17      import volatility.conf as conf
      18      import volatility.registry as registry
      19      import volatility.commands as commands
      20      import volatility.utils as utils
      21      import volatility.plugins.malware.devicetree as devicetree
      22      import volatility.plugins.malware.apihooks as apihooks
      23      import volatility.plugins.getsids as sidm
      24      import volatility.plugins.privileges as privm
      25      import volatility.plugins.taskmods as taskmods
      26      import volatility.win32.tasks as tasks
      27      import volatility.obj as obj
      28      import volatility.exceptions as exc
      29      import volatility.plugins.filescan as filescan
      30      import volatility.protos as protos
      31
      32      HAVE_VOLATILITY = True
      33
      34      # Inherit Cuckoo debugging level for Volatility commands.
      35      rootlogger = logging.getLogger()
      36      logging.getLogger("volatility.obj").setLevel(rootlogger.level)
      37      logging.getLogger("volatility.utils").setLevel(rootlogger.level)
      38  except ImportError as e:
      39      if e.message == "No module named Crypto.Hash":
      40          log.error(
      41              "The PyCrypto package is missing (install with "
      42              "`pip install pycrypto`)"
      43          )
      44
      45      HAVE_VOLATILITY = False
      46
      47 -class VolatilityAPI(object):
      48      """ Volatility API interface."""
      49
      50 -    def __init__(self, memdump, osprofile=None):
      51          """@param memdump: the memdump file path
      52          @param osprofile: the profile (OS type)
      53          """
      54          registry.PluginImporter()
      55          self.memdump = memdump
      56          self.osprofile = osprofile
      57          self.config = None
      58          self.addr_space = None
      59          self.init_config()
      60
      61 -    def get_dtb(self):
      62          """Use psscan to get system dtb and apply it."""
      63          ps = filescan.PSScan(self.config)
      64
      65          for ep in ps.calculate():
      66              if str(ep.ImageFileName) == "System":
      67                  self.config.update("dtb", ep.Pcb.DirectoryTableBase)
      68                  return True
      69
      70          return False
      71
      72 -    def init_config(self):
      73          """Creates a volatility configuration."""
      74          if self.config is not None and self.addr_space is not None:
      75              return self.config
      76
      77          self.config = conf.ConfObject()
      78          self.config.optparser.set_conflict_handler("resolve")
      79          registry.register_global_options(self.config, commands.Command)
      80          base_conf = {
      81              "profile": "WinXPSP2x86",
      82              "use_old_as": None,
      83              "kdbg": None,
      84              "help": False,
      85              "kpcr": None,
      86              "tz": None,
      87              "pid": None,
      88              "output_file": None,
      89              "physical_offset": None,
      90              "conf_file": None,
      91              "dtb": None,
      92              "output": None,
      93              "info": None,
      94              "location": "file://" + self.memdump,
      95              "plugins": None,
      96              "debug": None,
      97              "cache_dtb": True,
      98              "filename": None,
      99              "cache_directory": None,
     100              "verbose": None,
     101              "write": False
     102          }
     103
     104          if self.osprofile:
     105              base_conf["profile"] = self.osprofile
     106
     107          for key, value in base_conf.items():
     108              self.config.update(key, value)
     109
     110          # Deal with Volatility support for KVM/qemu memory dump.
     111          # See: #464.
     112          try:
     113              self.addr_space = utils.load_as(self.config)
     114          except exc.AddrSpaceError:
     115              if self.get_dtb():
     116                  self.addr_space = utils.load_as(self.config)
     117              else:
     118                  raise
     119
     120          self.plugins = \
     121              registry.get_plugin_classes(commands.Command, lower=True)
     122          return self.config
     123
     124 -    def pslist(self):
     125          """Volatility pslist plugin.
     126          @see volatility/plugins/taskmods.py
     127          """
     128          results = []
     129
     130          command = taskmods.PSList(self.config)
     131          for process in command.calculate():
     132              new = {
     133                  "process_name": str(process.ImageFileName),
     134                  "process_id": int(process.UniqueProcessId),
     135                  "parent_id": int(process.InheritedFromUniqueProcessId),
     136                  "num_threads": str(process.ActiveThreads),
     137                  "num_handles": str(process.ObjectTable.HandleCount),
     138                  "session_id": str(process.SessionId),
     139                  "create_time": str(process.CreateTime or ""),
     140                  "exit_time": str(process.ExitTime or ""),
     141              }
     142
     143              results.append(new)
     144
     145          return dict(config={}, data=results)
     146
     147 -    def psxview(self):
     148          """Volatility psxview plugin.
     149          @see volatility/plugins/malware/psxview.py
     150          """
     151          results = []
     152
     153          command = self.plugins["psxview"](self.config)
     154          for offset, process, ps_sources in command.calculate():
     155              new = {
     156                  "process_name": str(process.ImageFileName),
     157                  "process_id": int(process.UniqueProcessId),
     158                  "pslist": str(offset in ps_sources["pslist"]),
     159                  "psscan": str(offset in ps_sources["psscan"]),
     160                  "thrdproc": str(offset in ps_sources["thrdproc"]),
     161                  "pspcid": str(offset in ps_sources["pspcid"]),
     162                  "csrss": str(offset in ps_sources["csrss"]),
     163                  "session": str(offset in ps_sources["session"]),
     164                  "deskthrd": str(offset in ps_sources["deskthrd"]),
     165              }
     166
     167              results.append(new)
     168
     169          return dict(config={}, data=results)
     170
     171 -    def callbacks(self):
     172          """Volatility callbacks plugin.
     173          @see volatility/plugins/malware/callbacks.py
     174          """
     175          results = []
     176
     177          command = self.plugins["callbacks"](self.config)
     178          for (sym, cb, detail), mods, mod_addrs in command.calculate():
     179              module = tasks.find_module(mods, mod_addrs, self.addr_space.address_mask(cb))
     180
     181              if module:
     182                  module_name = module.BaseDllName or module.FullDllName
     183              else:
     184                  module_name = "UNKNOWN"
     185
     186              new = {
     187                  "type": str(sym),
     188                  "callback": hex(int(cb)),
     189                  "module": str(module_name),
     190                  "details": str(detail or "-"),
     191              }
     192
     193              results.append(new)
     194
     195          return dict(config={}, data=results)
     196
     197 -    def idt(self):
     198          """Volatility idt plugin.
     199          @see volatility/plugins/malware/idt.py
     200          """
     201          results = []
     202
     203          command = self.plugins["idt"](self.config)
     204          for n, entry, addr, module in command.calculate():
     205              if module:
     206                  module_name = str(module.BaseDllName or "")
     207                  sect_name = command.get_section_name(module, addr)
     208              else:
     209                  module_name = "UNKNOWN"
     210                  sect_name = ''
     211
     212              # The parent is IDT. The grand-parent is _KPCR.
     213              cpu_number = entry.obj_parent.obj_parent.ProcessorBlock.Number
     214              new = {
     215                  "cpu_number": int(cpu_number),
     216                  "index": int(n),
     217                  "selector": hex(int(entry.Selector)),
     218                  "address": hex(int(addr)),
     219                  "module": module_name,
     220                  "section": sect_name,
     221              }
     222              results.append(new)
     223
     224          return dict(config={}, data=results)
     225
     226 -    def gdt(self):
     227          """Volatility gdt plugin.
     228          @see volatility/plugins/malware/idt.py
     229          """
     230          results = []
     231
     232          command = self.plugins["gdt"](self.config)
     233          # Comment: this code is pretty much ripped from render_text in volatility.
     234          for n, entry in command.calculate():
     235              selector = n * 8
     236
     237              # Is the entry present? This applies to all types of GDT entries
     238              if entry.Present:
     239                  present = "P"
     240              else:
     241                  present = "Np"
     242
     243              # The base, limit, and granularity is calculated differently
     244              # for 32bit call gates than they are for all other types.
     245              if entry.Type == "CallGate32":
     246                  base = entry.CallGate
     247                  limit = 0
     248                  granularity = "-"
     249              else:
     250                  base = entry.Base
     251                  limit = entry.Limit
     252                  if entry.Granularity:
     253                      granularity = "Pg"
     254                  else:
     255                      granularity = "By"
     256
     257              # The parent is GDT. The grand-parent is _KPCR.
     258              cpu_number = entry.obj_parent.obj_parent.ProcessorBlock.Number
     259
     260              new = {
     261                  "cpu_number": int(cpu_number),
     262                  "selector": hex(selector),
     263                  "base": hex(int(base)),
     264                  "limit": hex(int(limit)),
     265                  "type": str(entry.Type),
     266                  "dpl": str(entry.Dpl),
     267                  "granularity": granularity,
     268                  "present": present,
     269              }
     270              results.append(new)
     271
     272          return dict(config={}, data=results)
     273
     274 -    def ssdt(self):
     275          """Volatility ssdt plugin.
     276          @see volatility/plugins/ssdt.py
     277          """
     278          results = []
     279
     280          command = self.plugins["ssdt"](self.config)
     281
     282          # Comment: this code is pretty much ripped from render_text in volatility.
     283          addr_space = self.addr_space
     284          syscalls = addr_space.profile.syscalls
     285          bits32 = addr_space.profile.metadata.get("memory_model", "32bit") == "32bit"
     286
     287          for idx, table, n, vm, mods, mod_addrs in command.calculate():
     288              for i in range(n):
     289                  if bits32:
     290                      # These are absolute function addresses in kernel memory.
     291                      syscall_addr = obj.Object("address", table + (i * 4), vm).v()
     292                  else:
     293                      # These must be signed long for x64 because they are RVAs relative
     294                      # to the base of the table and can be negative.
     295                      offset = obj.Object("long", table + (i * 4), vm).v()
     296                      # The offset is the top 20 bits of the 32 bit number.
     297                      syscall_addr = table + (offset >> 4)
     298
     299                  try:
     300                      syscall_name = syscalls[idx][i]
     301                  except IndexError:
     302                      syscall_name = "UNKNOWN"
     303
     304                  syscall_mod = tasks.find_module(mods, mod_addrs, addr_space.address_mask(syscall_addr))
     305                  if syscall_mod:
     306                      syscall_modname = "{0}".format(syscall_mod.BaseDllName)
     307                  else:
     308                      syscall_modname = "UNKNOWN"
     309
     310                  new = {
     311                      "index": int(idx),
     312                      "table": "0x%x" % int(table),
     313                      "entry": "{0:#06x}".format(idx * 0x1000 + i),
     314                      "syscall_name": syscall_name,
     315                      "syscall_addr": "0x%x" % int(syscall_addr),
     316                      "syscall_modname": syscall_modname,
     317                  }
     318
     319                  if bits32 and syscall_mod is not None:
     320                      ret = apihooks.ApiHooks.check_inline(
     321                          va=syscall_addr, addr_space=vm,
     322                          mem_start=syscall_mod.DllBase,
     323                          mem_end=syscall_mod.DllBase + syscall_mod.SizeOfImage)
     324
     325                      # Could not analyze the memory.
     326                      if ret is not None:
     327                          hooked, data, dest_addr = ret
     328                          if hooked:
     329                              # We found a hook, try to resolve the hooker.
     330                              # No mask required because we currently only work
     331                              # on x86 anyway.
     332                              hook_mod = tasks.find_module(mods, mod_addrs,
     333                                                           dest_addr)
     334                              if hook_mod:
     335                                  hook_name = "{0}".format(hook_mod.BaseDllName)
     336                              else:
     337                                  hook_name = "UNKNOWN"
     338
     339                              # Report it now.
     340                              new.update({
     341                                  "hook_dest_addr": "{0:#x}".format(dest_addr),
     342                                  "hook_name": hook_name,
     343                              })
     344
     345                  results.append(new)
     346
     347          return dict(config={}, data=results)
     348
     349 -    def timers(self):
     350          """Volatility timers plugin.
     351          @see volatility/plugins/malware/timers.py
     352          """
     353          results = []
     354
     355          command = self.plugins["timers"](self.config)
     356          for timer, module in command.calculate():
     357              if timer.Header.SignalState.v():
     358                  signaled = "Yes"
     359              else:
     360                  signaled = "-"
     361
     362              if module:
     363                  module_name = str(module.BaseDllName or "")
     364              else:
     365                  module_name = "UNKNOWN"
     366
     367              due_time = "{0:#010x}:{1:#010x}".format(timer.DueTime.HighPart, timer.DueTime.LowPart)
     368
     369              new = {
     370                  "offset": hex(timer.obj_offset),
     371                  "due_time": due_time,
     372                  "period": int(timer.Period),
     373                  "signaled": signaled,
     374                  "routine": hex(int(timer.Dpc.DeferredRoutine)),
     375                  "module": module_name,
     376              }
     377              results.append(new)
     378
     379          return dict(config={}, data=results)
     380
     381 -    def messagehooks(self):
     382          """Volatility messagehooks plugin.
     383          @see volatility/plugins/malware/messagehooks.py
     384          """
     385          results = []
     386
     387          command = self.plugins["messagehooks"](self.config)
     388          for winsta, atom_tables in command.calculate():
     389              for desk in winsta.desktops():
     390                  for name, hook in desk.hooks():
     391                      module = command.translate_hmod(winsta, atom_tables, hook.ihmod)
     392                      new = {
     393                          "offset": hex(int(hook.obj_offset)),
     394                          "session": int(winsta.dwSessionId),
     395                          "desktop": "{0}\\{1}".format(winsta.Name, desk.Name),
     396                          "thread": "<any>",
     397                          "filter": str(name),
     398                          "flags": str(hook.flags),
     399                          "function": hex(int(hook.offPfn)),
     400                          "module": str(module),
     401                      }
     402                      results.append(new)
     403
     404                  for thrd in desk.threads():
     405                      info = "{0} ({1} {2})".format(
     406                          thrd.pEThread.Cid.UniqueThread,
     407                          thrd.ppi.Process.ImageFileName,
     408                          thrd.ppi.Process.UniqueProcessId)
     409
     410                      for name, hook in thrd.hooks():
     411                          module = command.translate_hmod(winsta, atom_tables, hook.ihmod)
     412
     413                          new = {
     414                              "offset": hex(int(hook.obj_offset)),
     415                              "session": int(winsta.dwSessionId),
     416                              "desktop": "{0}\\{1}".format(winsta.Name, desk.Name),
     417                              "thread": str(info),
     418                              "filter": str(name),
     419                              "flags": str(hook.flags),
     420                              "function": hex(int(hook.offPfn)),
     421                              "module": str(module),
     422                          }
     423                          results.append(new)
     424
     425          return dict(config={}, data=results)
     426
     427 -    def getsids(self):
     428          """Volatility getsids plugin.
     429          @see volatility/plugins/malware/getsids.py
     430          """
     431          results = []
     432
     433          command = self.plugins["getsids"](self.config)
     434          for task in command.calculate():
     435              token = task.get_token()
     436
     437              if not token:
     438                  continue
     439
     440              for sid_string in token.get_sids():
     441                  if sid_string in sidm.well_known_sids:
     442                      sid_name = " {0}".format(sidm.well_known_sids[sid_string])
     443                  else:
     444                      sid_name_re = sidm.find_sid_re(sid_string, sidm.well_known_sid_re)
     445                      if sid_name_re:
     446                          sid_name = " {0}".format(sid_name_re)
     447                      else:
     448                          sid_name = ""
     449
     450                  new = {
     451                      "filename": str(task.ImageFileName),
     452                      "process_id": int(task.UniqueProcessId),
     453                      "sid_string": str(sid_string),
     454                      "sid_name": str(sid_name),
     455                  }
     456                  results.append(new)
     457
     458          return dict(config={}, data=results)
     459
     460 -    def privs(self):
     461          """Volatility privs plugin.
     462          @see volatility/plugins/malware/privs.py
     463          """
     464          results = []
     465
     466          command = self.plugins["privs"](self.config)
     467
     468          for task in command.calculate():
     469              for value, present, enabled, default in task.get_token().privileges():
     470                  try:
     471                      name, desc = privm.PRIVILEGE_INFO[int(value)]
     472                  except KeyError:
     473                      continue
     474
     475                  attributes = []
     476                  if present:
     477                      attributes.append("Present")
     478                  if enabled:
     479                      attributes.append("Enabled")
     480                  if default:
     481                      attributes.append("Default")
     482
     483                  new = {
     484                      "process_id": int(task.UniqueProcessId),
     485                      "filename": str(task.ImageFileName),
     486                      "value": int(value),
     487                      "privilege": str(name),
     488                      "attributes": ",".join(attributes),
     489                      "description": str(desc),
     490                  }
     491                  results.append(new)
     492
     493          return dict(config={}, data=results)
     494
     495 -    def malfind(self, dump_dir=None):
     496          """Volatility malfind plugin.
     497          @param dump_dir: optional directory for dumps
     498          @see volatility/plugins/malware/malfind.py
     499          """
     500          results = []
     501
     502          command = self.plugins["malfind"](self.config)
     503          for task in command.calculate():
     504              for vad, address_space in task.get_vads(vad_filter=task._injection_filter):
     505                  if command._is_vad_empty(vad, address_space):
     506                      continue
     507
     508                  new = {
     509                      "process_name": str(task.ImageFileName),
     510                      "process_id": int(task.UniqueProcessId),
     511                      "vad_start": "{0:#x}".format(vad.Start),
     512                      "vad_tag": str(vad.Tag),
     513                  }
     514                  results.append(new)
     515
     516                  if dump_dir:
     517                      filename = os.path.join(dump_dir, "process.{0:#x}.{1:#x}.dmp".format(task.obj_offset, vad.Start))
     518                      command.dump_vad(filename, vad, address_space)
     519
     520          return dict(config={}, data=results)
     521
     522 -    def yarascan(self):
     523          """Volatility yarascan plugin.
     524          @see volatility/plugins/malware/yarascan.py
     525          """
     526          results = []
     527
     528          ypath = os.path.join(CUCKOO_ROOT, "data", "yara", "index_memory.yar")
     529          if not os.path.exists(ypath):
     530              return dict(config={}, data=[])
     531
     532          self.config.update("YARA_FILE", ypath)
     533
     534          command = self.plugins["yarascan"](self.config)
     535          for o, addr, hit, content in command.calculate():
     536              # Comment: this code is pretty much ripped from render_text in volatility.
     537              # Find out if the hit is from user or kernel mode
     538              if o is None:
     539                  owner = "Unknown Kernel Memory"
     540              elif o.obj_name == "_EPROCESS":
     541                  owner = "Process {0} Pid {1}".format(o.ImageFileName, o.UniqueProcessId)
     542              else:
     543                  owner = "{0}".format(o.BaseDllName)
     544
     545              hexdump = "".join(
     546                  "{0:#010x}  {1:<48}  {2}\n".format(addr + o, h, ''.join(c))
     547                  for o, h, c in utils.Hexdump(content[0:64]))
     548
     549              new = {
     550                  "rule": hit.rule,
     551                  "owner": owner,
     552                  "hexdump": hexdump,
     553              }
     554              results.append(new)
     555
     556          return dict(config={}, data=results)
     557
     558 -    def apihooks(self):
     559          """Volatility apihooks plugin.
     560          @see volatility/plugins/malware/apihooks.py
     561          """
     562          results = []
     563
     564          command = self.plugins["apihooks"](self.config)
     565          for process, module, hook in command.calculate():
     566              proc_name = str(process.ImageFileName) if process else ''
     567              if command.whitelist(hook.hook_mode | hook.hook_type,
     568                                   proc_name, hook.VictimModule,
     569                                   hook.HookModule, hook.Function):
     570                  continue
     571
     572              new = {
     573                  "hook_mode": str(hook.Mode),
     574                  "hook_type": str(hook.Type),
     575                  "victim_module": str(module.BaseDllName or ""),
     576                  "victim_function": str(hook.Detail),
     577                  "hook_address": "{0:#x}".format(hook.hook_address),
     578                  "hooking_module": str(hook.HookModule)
     579              }
     580
     581              if process:
     582                  new["process_id"] = int(process.UniqueProcessId)
     583                  new["process_name"] = str(process.ImageFileName)
     584
     585              results.append(new)
     586
     587          return dict(config={}, data=results)
     588
     589 -    def dlllist(self):
     590          """Volatility dlllist plugin.
     591          @see volatility/plugins/taskmods.py
     592          """
     593          results = []
     594
     595          command = self.plugins["dlllist"](self.config)
     596          for task in command.calculate():
     597              new = {
     598                  "process_id": int(task.UniqueProcessId),
     599                  "process_name": str(task.ImageFileName),
     600                  "commandline": str(task.Peb.ProcessParameters.CommandLine or ""),
     601                  "loaded_modules": []
     602              }
     603
     604              for module in task.get_load_modules():
     605                  new["loaded_modules"].append({
     606                      "dll_base": str(module.DllBase),
     607                      "dll_size": str(module.SizeOfImage),
     608                      "dll_full_name": str(module.FullDllName or ""),
     609                      "dll_load_count": int(module.LoadCount),
     610                  })
     611
     612              results.append(new)
     613
     614          return dict(config={}, data=results)
     615
     616 -    def handles(self):
     617          """Volatility handles plugin.
     618          @see volatility/plugins/handles.py
     619          """
     620          results = []
     621
     622          command = self.plugins["handles"](self.config)
     623          for pid, handle, object_type, name in command.calculate():
     624              new = {
     625                  "process_id": int(pid),
     626                  "handle_value": str(handle.HandleValue),
     627                  "handle_granted_access": str(handle.GrantedAccess),
     628                  "handle_type": str(object_type),
     629                  "handle_name": str(name)
     630              }
     631
     632              results.append(new)
     633
     634          return dict(config={}, data=results)
     635
     636 -    def ldrmodules(self):
     637          """Volatility ldrmodules plugin.
     638          @see volatility/plugins/malware/malfind.py
     639          """
     640          results = []
     641
     642          command = self.plugins["ldrmodules"](self.config)
     643          for task in command.calculate():
     644              # Build a dictionary for all three PEB lists where the
     645              # keys are base address and module objects are the values.
     646              inloadorder = dict((mod.DllBase.v(), mod) for mod in task.get_load_modules())
     647              ininitorder = dict((mod.DllBase.v(), mod) for mod in task.get_init_modules())
     648              inmemorder = dict((mod.DllBase.v(), mod) for mod in task.get_mem_modules())
     649
     650              # Build a similar dictionary for the mapped files.
     651              mapped_files = {}
     652              for vad, address_space in task.get_vads(vad_filter=task._mapped_file_filter):
     653                  # Note this is a lot faster than acquiring the full
     654                  # vad region and then checking the first two bytes.
     655                  if obj.Object("_IMAGE_DOS_HEADER", offset=vad.Start, vm=address_space).e_magic != 0x5A4D:
     656                      continue
     657
     658                  mapped_files[int(vad.Start)] = str(vad.FileObject.FileName or "")
     659
     660              # For each base address with a mapped file, print info on
     661              # the other PEB lists to spot discrepancies.
     662              for base in mapped_files.keys():
     663                  # Does the base address exist in the PEB DLL lists?
     664                  load_mod = inloadorder.get(base, None)
     665                  init_mod = ininitorder.get(base, None)
     666                  mem_mod = inmemorder.get(base, None)
     667
     668                  new = {
     669                      "process_id": int(task.UniqueProcessId),
     670                      "process_name": str(task.ImageFileName),
     671                      "dll_base": "{0:#x}".format(base),
     672                      "dll_in_load": load_mod is not None,
     673                      "dll_in_init": init_mod is not None,
     674                      "dll_in_mem": mem_mod is not None,
     675                      "dll_mapped_path": str(mapped_files[base]),
     676                      "load_full_dll_name": "",
     677                      "init_full_dll_name": "",
     678                      "mem_full_dll_name": ""
     679                  }
     680
     681                  if load_mod:
     682                      new["load_full_dll_name"] = str(load_mod.FullDllName)
     683
     684                  if init_mod:
     685                      new["init_full_dll_name"] = str(init_mod.FullDllName)
     686
     687                  if mem_mod:
     688                      new["mem_full_dll_name"] = str(mem_mod.FullDllName)
     689
     690                  results.append(new)
     691
     692          return dict(config={}, data=results)
     693
     694 -    def mutantscan(self):
     695          """Volatility mutantscan plugin.
     696          @see volatility/plugins/filescan.py
     697          """
     698          results = []
     699
     700          command = self.plugins["mutantscan"](self.config)
     701          for mutant in command.calculate():
     702              header = mutant.get_object_header()
     703              tid = 0
     704              pid = 0
     705              if mutant.OwnerThread > 0x80000000:
     706                  thread = mutant.OwnerThread.dereference_as("_ETHREAD")
     707                  tid = thread.Cid.UniqueThread
     708                  pid = thread.Cid.UniqueProcess
     709
     710              new = {
     711                  "mutant_offset": "{0:#x}".format(mutant.obj_offset),
     712                  "num_pointer": int(header.PointerCount),
     713                  "num_handles": int(header.HandleCount),
     714                  "mutant_signal_state": str(mutant.Header.SignalState),
     715                  "mutant_name": str(header.NameInfo.Name or ""),
     716                  "process_id": int(pid),
     717                  "thread_id": int(tid)
     718              }
     719
     720              results.append(new)
     721
     722          return dict(config={}, data=results)
     723
     724 -    def devicetree(self):
     725          """Volatility devicetree plugin.
     726          @see volatility/plugins/malware/devicetree.py
     727          """
     728          results = []
     729
     730          command = self.plugins["devicetree"](self.config)
     731          for driver_obj in command.calculate():
     732              new = {
     733                  "driver_offset": "0x{0:08x}".format(driver_obj.obj_offset),
     734                  "driver_name": str(driver_obj.DriverName or ""),
     735                  "devices": []
     736              }
     737
     738              for device in driver_obj.devices():
     739                  device_header = obj.Object(
     740                      "_OBJECT_HEADER",
     741                      offset=device.obj_offset - device.obj_vm.profile.get_obj_offset("_OBJECT_HEADER", "Body"),
     742                      vm=device.obj_vm,
     743                      native_vm=device.obj_native_vm
     744                  )
     745
     746                  device_name = str(device_header.NameInfo.Name or "")
     747
     748                  new_device = {
     749                      "device_offset": "0x{0:08x}".format(device.obj_offset),
     750                      "device_name": device_name,
     751                      "device_type": devicetree.DEVICE_CODES.get(device.DeviceType.v(), "UNKNOWN"),
     752                      "devices_attached": []
     753                  }
     754
     755                  new["devices"].append(new_device)
     756
     757                  level = 0
     758
     759                  for att_device in device.attached_devices():
     760                      device_header = obj.Object(
     761                          "_OBJECT_HEADER",
     762                          offset=att_device.obj_offset - att_device.obj_vm.profile.get_obj_offset("_OBJECT_HEADER", "Body"),
     763                          vm=att_device.obj_vm,
     764                          native_vm=att_device.obj_native_vm
     765                      )
     766
     767                      device_name = str(device_header.NameInfo.Name or "")
     768                      name = (device_name + " - " + str(att_device.DriverObject.DriverName or ""))
     769
     770                      new_device["devices_attached"].append({
     771                          "level": level,
     772                          "attached_device_offset": "0x{0:08x}".format(att_device.obj_offset),
     773                          "attached_device_name": name,
     774                          "attached_device_type": devicetree.DEVICE_CODES.get(att_device.DeviceType.v(), "UNKNOWN")
     775                      })
     776
     777                      level += 1
     778
     779              results.append(new)
     780
     781          return dict(config={}, data=results)
     782
     783 -    def svcscan(self):
     784          """Volatility svcscan plugin - scans for services.
     785          @see volatility/plugins/malware/svcscan.py
     786          """
     787          results = []
     788
     789          command = self.plugins["svcscan"](self.config)
     790          for rec in command.calculate():
     791              new = {
     792                  "service_offset": "{0:#x}".format(rec.obj_offset),
     793                  "service_order": int(rec.Order),
     794                  "process_id": int(rec.Pid),
     795                  "service_name": str(rec.ServiceName.dereference()),
     796                  "service_display_name": str(rec.DisplayName.dereference()),
     797                  "service_type": str(rec.Type),
     798                  "service_binary_path": str(rec.Binary),
     799                  "service_state": str(rec.State)
     800              }
     801
     802              results.append(new)
     803
     804          return dict(config={}, data=results)
     805
     806 -    def modscan(self):
     807          """Volatility modscan plugin.
     808          @see volatility/plugins/modscan.py
     809          """
     810          results = []
     811
     812          command = self.plugins["modscan"](self.config)
     813          for ldr_entry in command.calculate():
     814              new = {
     815                  "kernel_module_offset": "{0:#x}".format(ldr_entry.obj_offset),
     816                  "kernel_module_name": str(ldr_entry.BaseDllName or ""),
     817                  "kernel_module_file": str(ldr_entry.FullDllName or ""),
     818                  "kernel_module_base": "{0:#x}".format(ldr_entry.DllBase),
     819                  "kernel_module_size": int(ldr_entry.SizeOfImage),
     820              }
     821
     822              results.append(new)
     823
     824          return dict(config={}, data=results)
     825
     826 -    def imageinfo(self):
     827          """Volatility imageinfo plugin.
     828          @see volatility/plugins/imageinfo.py
     829          """
     830          results = []
     831
     832          command = self.plugins["imageinfo"](self.config)
     833          new = {}
     834          for key, value in command.calculate():
     835              new[key] = value
     836
     837          osp = new["Suggested Profile(s)"].split(",")[0]
     838          new["osprofile"] = osp
     839
     840          results.append(new)
     841
     842          return dict(config={}, data=results)
     843
     844 -    def sockscan(self):
     845          """Volatility sockscan plugin.
     846          @see volatility/plugins/sockscan.py
     847          """
     848          results = []
     849
     850          command = self.plugins["sockscan"](self.config)
     851          for sock in command.calculate():
     852              new = {
     853                  "offset": "{0:#010x}".format(sock.obj_offset),
     854                  "process_id": str(sock.Pid),
     855                  "address": str(sock.LocalIpAddress),
     856                  "port": str(sock.LocalPort),
     857                  "protocol": "{0} ({1})".format(sock.Protocol, protos.protos.get(sock.Protocol.v(), "-")),
     858                  "create_time": time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(int(sock.CreateTime))),
     859              }
     860              results.append(new)
     861
     862          return dict(config={}, data=results)
     863
     864 -    def netscan(self):
     865          """Volatility sockscan plugin.
     866          @see volatility/plugins/netscan.py
     867          """
     868          results = []
     869
     870          command = self.plugins["netscan"](self.config)
     871          for net_obj, proto, laddr, lport, raddr, rport, state in command.calculate():
     872              new = {
     873                  "offset": "{0:#010x}".format(net_obj.obj_offset),
     874                  "process_id": str(net_obj.Owner.UniqueProcessId),
     875                  "local_address": str(laddr),
     876                  "local_port": str(lport),
     877                  "remote_address": str(raddr),
     878                  "remote_port": str(rport),
     879                  "protocol": str(proto),
     880              }
     881              results.append(new)
     882
     883          return dict(config={}, data=results)
     884
     885
     886 -class VolatilityManager(object):
     887      """Handle several volatility results."""
     888      PLUGINS = [
     889          "pslist",
     890          "psxview",
     891          "callbacks",
     892          ["idt", "x86"],
     893          "ssdt",
     894          ["gdt", "x86"],
     895          "timers",
     896          "messagehooks",
     897          "getsids",
     898          "privs",
     899          "malfind",
     900          "apihooks",
     901          "dlllist",
     902          "handles",
     903          "ldrmodules",
     904          "mutantscan",
     905          "devicetree",
     906          "svcscan",
     907          "modscan",
     908          "yarascan",
     909          ["sockscan", "winxp"],
     910          ["netscan", "vista", "win7"],
     911      ]
     912
     913 -    def __init__(self, memfile, osprofile=None):
     914          self.mask_pid = []
     915          self.taint_pid = set()
     916          self.memfile = memfile
     917
     918          conf_path = os.path.join(CUCKOO_ROOT, "conf", "memory.conf")
     919          if not os.path.exists(conf_path):
     920              log.error("Configuration file {0} not found".format(conf_path))
     921              self.voptions = False
     922              return
     923
     924          self.voptions = Config("memory")
     925
     926          for pid in self.voptions.mask.pid_generic.split(","):
     927              pid = pid.strip()
     928              if pid:
     929                  self.mask_pid.append(int(pid))
     930
     931          self.no_filter = not self.voptions.mask.enabled
     932          if self.voptions.basic.guest_profile:
     933              self.osprofile = self.voptions.basic.guest_profile
     934          else:
     935              self.osprofile = osprofile or self.get_osprofile()
     936
     937 -    def get_osprofile(self):
     938          """Get the OS profile"""
     939          return VolatilityAPI(self.memfile).imageinfo()["data"][0]["osprofile"]
     940
     941 -    def run(self):
     942          results = {}
     943
     944          # Exit if options were not loaded.
     945          if not self.voptions:
     946              return
     947
     948          vol = VolatilityAPI(self.memfile, self.osprofile)
     949
     950          for plugin_name in self.PLUGINS:
     951              if isinstance(plugin_name, list):
     952                  plugin_name, profiles = plugin_name[0], plugin_name[1:]
     953              else:
     954                  profiles = []
     955
     956              # Some plugins can only run in certain profiles (i.e., only in
     957              # Windows XP/Vista/7, or only in x86 or x64).
     958              osp = self.osprofile.lower()
     959              for profile in profiles:
     960                  if osp.startswith(profile) or osp.endswith(profile):
     961                      break
     962              else:
     963                  if profiles:
     964                      continue
     965
     966              plugin = self.voptions.get(plugin_name)
     967              if not plugin or not plugin.enabled:
     968                  log.debug("Skipping '%s' volatility module", plugin_name)
     969                  continue
     970
     971              if plugin_name in vol.plugins:
     972                  log.debug("Executing volatility '%s' module.", plugin_name)
     973                  results[plugin_name] = getattr(vol, plugin_name)()
     974
     975          self.find_taint(results)
     976          self.cleanup()
     977
     978          return self.mask_filter(results)
     979
     980 -    def mask_filter(self, old):
     981          """Filter out masked stuff. Keep tainted stuff."""
     982          new = {}
     983
     984          for akey in old.keys():
     985              new[akey] = {"config": old[akey]["config"], "data": []}
     986              conf = getattr(self.voptions, akey, None)
     987              new[akey]["config"]["filter"] = conf.filter
     988              for item in old[akey]["data"]:
     989                  # TODO: need to improve this logic.
     990                  if not conf.filter:
     991                      new[akey]["data"].append(item)
     992                  elif "process_id" in item and \
     993                          item["process_id"] in self.mask_pid and \
     994                          item["process_id"] not in self.taint_pid:
     995                      pass
     996                  else:
     997                      new[akey]["data"].append(item)
     998          return new
     999
    1000 -    def find_taint(self, res):
    1001          """Find tainted items."""
    1002          if "malfind" in res:
    1003              for item in res["malfind"]["data"]:
    1004                  self.taint_pid.add(item["process_id"])
    1005
    1006 -    def cleanup(self):
    1007          """Delete the memory dump (if configured to do so)."""
    1008
    1009          if self.voptions.basic.delete_memdump:
    1010              try:
    1011                  os.remove(self.memfile)
    1012              except OSError:
    1013                  log.error("Unable to delete memory dump file at path \"%s\" ", self.memfile)
    1014
    1015 -class Memory(Processing):
    1016      """Volatility Analyzer."""
    1017
    1018 -    def run(self):
    1019          """Run analysis.
    1020          @return: volatility results dict.
    1021          """
    1022          self.key = "memory"
    1023
    1024          results = {}
    1025          if HAVE_VOLATILITY:
    1026              if self.memory_path and os.path.exists(self.memory_path):
    1027                  try:
    1028                      results = VolatilityManager(self.memory_path).run()
    1029                  except Exception:
    1030                      log.exception("Generic error executing volatility")
    1031              else:
    1032                  log.error("Memory dump not found: to run volatility you have to enable memory_dump")
    1033          else:
    1034              log.error("Cannot run volatility module: volatility library not available")
    1035
    1036          return results
    1037