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

Source Code for Module modules.processing.memory

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