1
2
3
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.getsids as sidm
19 import volatility.plugins.privileges as privm
20 import volatility.plugins.taskmods as taskmods
21 import volatility.win32.tasks as tasks
22 import volatility.obj as obj
23 HAVE_VOLATILITY = True
24 except ImportError:
25 HAVE_VOLATILITY = False
26
27 log = logging.getLogger(__name__)
28
30 """ Volatility API interface."""
31
32 - def __init__(self, memdump, osprofile=None):
33 """@param memdump: the memdump file path
34 @param osprofile: the profile (OS type)
35 """
36 registry.PluginImporter()
37 self.memdump = memdump
38 self.osprofile = osprofile
39 self.config = None
40 self.__config()
41
43 """Creates a volatility configuration."""
44 self.config = conf.ConfObject()
45 self.config.optparser.set_conflict_handler("resolve")
46 registry.register_global_options(self.config, commands.Command)
47 base_conf = {
48 "profile": "WinXPSP2x86",
49 "use_old_as": None,
50 "kdbg": None,
51 "help": False,
52 "kpcr": None,
53 "tz": None,
54 "pid": None,
55 "output_file": None,
56 "physical_offset": None,
57 "conf_file": None,
58 "dtb": None,
59 "output": None,
60 "info": None,
61 "location": "file://" + self.memdump,
62 "plugins": None,
63 "debug": None,
64 "cache_dtb": True,
65 "filename": None,
66 "cache_directory": None,
67 "verbose": None,
68 "write": False
69 }
70
71 if self.osprofile:
72 base_conf["profile"] = self.osprofile
73
74 for key, value in base_conf.items():
75 self.config.update(key, value)
76
77 self.addr_space = utils.load_as(self.config)
78 self.plugins = registry.get_plugin_classes(commands.Command,
79 lower=True)
80
81 return self.config
82
84 """Volatility pslist plugin.
85 @see volatility/plugins/taskmods.py
86 """
87 log.debug("Executing Volatility pslist plugin on "
88 "{0}".format(self.memdump))
89
90 self.__config()
91 results = []
92
93 command = taskmods.PSList(self.config)
94 for process in command.calculate():
95 new = {
96 "process_name": str(process.ImageFileName),
97 "process_id": int(process.UniqueProcessId),
98 "parent_id": int(process.InheritedFromUniqueProcessId),
99 "num_threads": str(process.ActiveThreads),
100 "num_handles": str(process.ObjectTable.HandleCount),
101 "session_id": str(process.SessionId),
102 "create_time": str(process.CreateTime or ""),
103 "exit_time": str(process.ExitTime or ""),
104 }
105
106 results.append(new)
107
108 return dict(config={}, data=results)
109
111 """Volatility psxview plugin.
112 @see volatility/plugins/malware/psxview.py
113 """
114 log.debug("Executing Volatility psxview plugin on "
115 "{0}".format(self.memdump))
116
117 self.__config()
118 results = []
119
120 command = self.plugins["psxview"](self.config)
121 for offset, process, ps_sources in command.calculate():
122 new = {
123 "process_name": str(process.ImageFileName),
124 "process_id": int(process.UniqueProcessId),
125 "pslist": str(ps_sources['pslist'].has_key(offset)),
126 "psscan": str(ps_sources['psscan'].has_key(offset)),
127 "thrdproc": str(ps_sources['thrdproc'].has_key(offset)),
128 "pspcid": str(ps_sources['pspcid'].has_key(offset)),
129 "csrss": str(ps_sources['csrss'].has_key(offset)),
130 "session": str(ps_sources['session'].has_key(offset)),
131 "deskthrd": str(ps_sources['deskthrd'].has_key(offset))
132 }
133
134 results.append(new)
135
136 return dict(config={}, data=results)
137
139 """Volatility callbacks plugin.
140 @see volatility/plugins/malware/callbacks.py
141 """
142 log.debug("Executing Volatility callbacks plugin on "
143 "{0}".format(self.memdump))
144
145 self.__config()
146 results = []
147
148 command = self.plugins["callbacks"](self.config)
149 for (sym, cb, detail), mods, mod_addrs in command.calculate():
150 module = tasks.find_module(mods, mod_addrs, command.kern_space.address_mask(cb))
151
152 if module:
153 module_name = module.BaseDllName or module.FullDllName
154 else:
155 module_name = "UNKNOWN"
156
157 new = {
158 "type": str(sym),
159 "callback": hex(int(cb)),
160 "module": str(module_name),
161 "details": str(detail or "-"),
162 }
163
164 results.append(new)
165
166 return dict(config={}, data=results)
167
169 """Volatility idt plugin.
170 @see volatility/plugins/malware/idt.py
171 """
172 log.debug("Executing Volatility idt plugin on "
173 "{0}".format(self.memdump))
174
175 self.__config()
176 results = []
177
178 command = self.plugins["idt"](self.config)
179 for n, entry, addr, module in command.calculate():
180 if module:
181 module_name = str(module.BaseDllName or '')
182 sect_name = command.get_section_name(module, addr)
183 else:
184 module_name = "UNKNOWN"
185 sect_name = ''
186
187
188 cpu_number = entry.obj_parent.obj_parent.ProcessorBlock.Number
189 new = {
190 "cpu_number": int(cpu_number),
191 "index": int(n),
192 "selector": hex(int(entry.Selector)),
193 "address": hex(int(addr)),
194 "module": module_name,
195 "section": sect_name,
196 }
197 results.append(new)
198
199 return dict(config={}, data=results)
200
202 """Volatility timers plugin.
203 @see volatility/plugins/malware/timers.py
204 """
205 log.debug("Executing Volatility timers plugin on "
206 "{0}".format(self.memdump))
207
208 self.__config()
209 results = []
210
211 command = self.plugins["timers"](self.config)
212 for timer, module in command.calculate():
213 if timer.Header.SignalState.v():
214 signaled = "Yes"
215 else:
216 signaled = "-"
217
218 if module:
219 module_name = str(module.BaseDllName or '')
220 else:
221 module_name = "UNKNOWN"
222
223 due_time = "{0:#010x}:{1:#010x}".format(timer.DueTime.HighPart, timer.DueTime.LowPart)
224
225 new = {
226 "offset": hex(timer.obj_offset),
227 "due_time": due_time,
228 "period": int(timer.Period),
229 "signaled": signaled,
230 "routine": hex(int(timer.Dpc.DeferredRoutine)),
231 "module": module_name,
232 }
233 results.append(new)
234
235 return dict(config={}, data=results)
236
238 """Volatility messagehooks plugin.
239 @see volatility/plugins/malware/messagehooks.py
240 """
241 log.debug("Executing Volatility messagehooks plugin on "
242 "{0}".format(self.memdump))
243
244 self.__config()
245 results = []
246
247 command = self.plugins["messagehooks"](self.config)
248 for winsta, atom_tables in command.calculate():
249 for desk in winsta.desktops():
250 for name, hook in desk.hooks():
251 module = command.translate_hmod(winsta, atom_tables, hook.ihmod)
252 new = {
253 "offset": hex(int(hook.obj_offset)),
254 "session": int(winsta.dwSessionId),
255 "desktop": "{0}\\{1}".format(winsta.Name, desk.Name),
256 "thread": "<any>",
257 "filter": str(name),
258 "flags": str(hook.flags),
259 "function": hex(int(hook.offPfn)),
260 "module": str(module),
261 }
262 results.append(new)
263
264 for thrd in desk.threads():
265 info = "{0} ({1} {2})".format(
266 thrd.pEThread.Cid.UniqueThread,
267 thrd.ppi.Process.ImageFileName,
268 thrd.ppi.Process.UniqueProcessId)
269
270 for name, hook in thrd.hooks():
271 module = command.translate_hmod(winsta, atom_tables, hook.ihmod)
272
273 new = {
274 "offset": hex(int(hook.obj_offset)),
275 "session": int(winsta.dwSessionId),
276 "desktop": "{0}\\{1}".format(winsta.Name, desk.Name),
277 "thread": str(info),
278 "filter": str(name),
279 "flags": str(hook.flags),
280 "function": hex(int(hook.offPfn)),
281 "module": str(module),
282 }
283 results.append(new)
284
285 return dict(config={}, data=results)
286
288 """Volatility getsids plugin.
289 @see volatility/plugins/malware/getsids.py
290 """
291
292 log.debug("Executing Volatility getsids plugin on "
293 "{0}".format(self.memdump))
294
295 self.__config()
296 results = []
297
298 command = self.plugins["getsids"](self.config)
299 for task in command.calculate():
300 token = task.get_token()
301
302 if not token:
303 continue
304
305 for sid_string in token.get_sids():
306 if sid_string in sidm.well_known_sids:
307 sid_name = " {0}".format(sidm.well_known_sids[sid_string])
308 else:
309 sid_name_re = sidm.find_sid_re(sid_string, sidm.well_known_sid_re)
310 if sid_name_re:
311 sid_name = " {0}".format(sid_name_re)
312 else:
313 sid_name = ""
314
315 new = {
316 "filename": str(task.ImageFileName),
317 "process_id": int(task.UniqueProcessId),
318 "sid_string": str(sid_string),
319 "sid_name": str(sid_name),
320 }
321 results.append(new)
322
323 return dict(config={}, data=results)
324
326 """Volatility privs plugin.
327 @see volatility/plugins/malware/privs.py
328 """
329
330 log.debug("Executing Volatility privs plugin on "
331 "{0}".format(self.memdump))
332
333 self.__config()
334 results = []
335
336 command = self.plugins["privs"](self.config)
337
338 for task in command.calculate():
339 for value, present, enabled, default in task.get_token().privileges():
340 try:
341 name, desc = privm.PRIVILEGE_INFO[int(value)]
342 except KeyError:
343 continue
344
345 attributes = []
346 if present:
347 attributes.append("Present")
348 if enabled:
349 attributes.append("Enabled")
350 if default:
351 attributes.append("Default")
352
353 new = {
354 "process_id": int(task.UniqueProcessId),
355 "filename": str(task.ImageFileName),
356 "value": int(value),
357 "privilege": str(name),
358 "attributes": ",".join(attributes),
359 "description": str(desc),
360 }
361 results.append(new)
362
363 return dict(config={}, data=results)
364
366 """Volatility malfind plugin.
367 @param dump_dir: optional directory for dumps
368 @see volatility/plugins/malware/malfind.py
369 """
370 log.debug("Executing Volatility malfind plugin on "
371 "{0}".format(self.memdump))
372
373 self.__config()
374 results = []
375
376 command = self.plugins["malfind"](self.config)
377 for task in command.calculate():
378 for vad, address_space in task.get_vads(vad_filter=task._injection_filter):
379 if command._is_vad_empty(vad, address_space):
380 continue
381
382 new = {
383 "process_name": str(task.ImageFileName),
384 "process_id": int(task.UniqueProcessId),
385 "vad_start": "{0:#x}".format(vad.Start),
386 "vad_tag": str(vad.Tag),
387 }
388 results.append(new)
389
390 if dump_dir:
391 filename = os.path.join(dump_dir, "process.{0:#x}.{1:#x}.dmp".format(task.obj_offset, vad.Start))
392 command.dump_vad(filename, vad, address_space)
393
394 return dict(config={}, data=results)
395
397 """Volatility apihooks plugin.
398 @see volatility/plugins/malware/apihooks.py
399 """
400 log.debug("Executing Volatility apihooks plugin on {0}".format(self.memdump))
401
402 self.__config()
403 results = []
404
405 command = self.plugins["apihooks"](self.config)
406 for process, module, hook in command.calculate():
407 new = {
408 "hook_mode": str(hook.Mode),
409 "hook_type": str(hook.Type),
410 "victim_module": str(module.BaseDllName or ""),
411 "victim_function": str(hook.Detail),
412 "hook_address": "{0:#x}".format(hook.hook_address),
413 "hooking_module": str(hook.HookModule)
414 }
415
416 if process:
417 new["process_id"] = int(process.UniqueProcessId)
418 new["process_name"] = str(process.ImageFileName)
419
420 results.append(new)
421
422 return dict(config={}, data=results)
423
425 """Volatility dlllist plugin.
426 @see volatility/plugins/taskmods.py
427 """
428 log.debug("Executing Volatility dlllist plugin on {0}".format(self.memdump))
429
430 self.__config()
431 results = []
432
433 command = self.plugins["dlllist"](self.config)
434 for task in command.calculate():
435 new = {
436 "process_id": int(task.UniqueProcessId),
437 "process_name": str(task.ImageFileName),
438 "commandline": str(task.Peb.ProcessParameters.CommandLine or ""),
439 "loaded_modules": []
440 }
441
442 for module in task.get_load_modules():
443 new["loaded_modules"].append({
444 "dll_base": str(module.DllBase),
445 "dll_size": str(module.SizeOfImage),
446 "dll_full_name": str(module.FullDllName or ""),
447 "dll_load_count": int(module.LoadCount),
448 })
449
450 results.append(new)
451
452 return dict(config={}, data=results)
453
455 """Volatility handles plugin.
456 @see volatility/plugins/handles.py
457 """
458 log.debug("Executing Volatility handles plugin on {0}".format(self.memdump))
459
460 self.__config()
461 results = []
462
463 command = self.plugins["handles"](self.config)
464 for pid, handle, object_type, name in command.calculate():
465 new = {
466 "process_id": int(pid),
467 "handle_value": str(handle.HandleValue),
468 "handle_granted_access": str(handle.GrantedAccess),
469 "handle_type": str(object_type),
470 "handle_name": str(name)
471 }
472
473 results.append(new)
474
475 return dict(config={}, data=results)
476
478 """Volatility ldrmodules plugin.
479 @see volatility/plugins/malware/malfind.py
480 """
481 log.debug("Executing Volatility ldrmodules plugin on {0}".format(self.memdump))
482
483 self.__config()
484 results = []
485
486 command = self.plugins["ldrmodules"](self.config)
487 for task in command.calculate():
488
489
490 inloadorder = dict((mod.DllBase.v(), mod) for mod in task.get_load_modules())
491 ininitorder = dict((mod.DllBase.v(), mod) for mod in task.get_init_modules())
492 inmemorder = dict((mod.DllBase.v(), mod) for mod in task.get_mem_modules())
493
494
495 mapped_files = {}
496 for vad, address_space in task.get_vads(vad_filter=task._mapped_file_filter):
497
498
499 if obj.Object("_IMAGE_DOS_HEADER", offset=vad.Start, vm=address_space).e_magic != 0x5A4D:
500 continue
501
502 mapped_files[int(vad.Start)] = str(vad.FileObject.FileName or "")
503
504
505
506 for base in mapped_files.keys():
507
508 load_mod = inloadorder.get(base, None)
509 init_mod = ininitorder.get(base, None)
510 mem_mod = inmemorder.get(base, None)
511
512 new = {
513 "process_id": int(task.UniqueProcessId),
514 "process_name": str(task.ImageFileName),
515 "dll_base": "{0:#x}".format(base),
516 "dll_in_load": not load_mod is None,
517 "dll_in_init": not init_mod is None,
518 "dll_in_mem": not mem_mod is None,
519 "dll_mapped_path": str(mapped_files[base]),
520 "load_full_dll_name": "",
521 "init_full_dll_name": "",
522 "mem_full_dll_name": ""
523 }
524
525 if load_mod:
526 new["load_full_dll_name"] = str(load_mod.FullDllName)
527
528 if init_mod:
529 new["init_full_dll_name"] = str(init_mod.FullDllName)
530
531 if mem_mod:
532 new["mem_full_dll_name"] = str(mem_mod.FullDllName)
533
534 results.append(new)
535
536 return dict(config={}, data=results)
537
539 """Volatility mutantscan plugin.
540 @see volatility/plugins/filescan.py
541 """
542 log.debug("Executing Volatility mutantscan module on {0}".format(self.memdump))
543
544 self.__config()
545 results = []
546
547 command = self.plugins["mutantscan"](self.config)
548 for object_obj, mutant in command.calculate():
549 tid = 0
550 pid = 0
551 if mutant.OwnerThread > 0x80000000:
552 thread = mutant.OwnerThread.dereference_as("_ETHREAD")
553 tid = thread.Cid.UniqueThread
554 pid = thread.Cid.UniqueProcess
555
556 new = {
557 "mutant_offset": "{0:#x}".format(mutant.obj_offset),
558 "num_pointer": int(object_obj.PointerCount),
559 "num_handles": int(object_obj.HandleCount),
560 "mutant_signal_state": str(mutant.Header.SignalState),
561 "mutant_name": str(object_obj.NameInfo.Name or ""),
562 "process_id": int(pid),
563 "thread_id": int(tid)
564 }
565
566 results.append(new)
567
568 return dict(config={}, data=results)
569
571 """Volatility devicetree plugin.
572 @see volatility/plugins/malware/devicetree.py
573 """
574 log.debug("Executing Volatility devicetree module on {0}".format(self.memdump))
575
576 self.__config()
577 results = []
578
579 command = self.plugins["devicetree"](self.config)
580 for _object_obj, driver_obj, _ in command.calculate():
581 new = {
582 "driver_offset": "0x{0:08x}".format(driver_obj.obj_offset),
583 "driver_name": str(driver_obj.DriverName or ""),
584 "devices": []
585 }
586
587 for device in driver_obj.devices():
588 device_header = obj.Object(
589 "_OBJECT_HEADER",
590 offset=device.obj_offset - device.obj_vm.profile.get_obj_offset("_OBJECT_HEADER", "Body"),
591 vm=device.obj_vm,
592 native_vm=device.obj_native_vm
593 )
594
595 device_name = str(device_header.NameInfo.Name or "")
596
597 new_device = {
598 "device_offset": "0x{0:08x}".format(device.obj_offset),
599 "device_name": device_name,
600 "device_type": devicetree.DEVICE_CODES.get(device.DeviceType.v(), "UNKNOWN"),
601 "devices_attached": []
602 }
603
604 new["devices"].append(new_device)
605
606 level = 0
607
608 for att_device in device.attached_devices():
609 device_header = obj.Object(
610 "_OBJECT_HEADER",
611 offset=att_device.obj_offset - att_device.obj_vm.profile.get_obj_offset("_OBJECT_HEADER", "Body"),
612 vm=att_device.obj_vm,
613 native_vm=att_device.obj_native_vm
614 )
615
616 device_name = str(device_header.NameInfo.Name or "")
617 name = (device_name + " - " + str(att_device.DriverObject.DriverName or ""))
618
619 new_device["devices_attached"].append({
620 "level": level,
621 "attached_device_offset": "0x{0:08x}".format(att_device.obj_offset),
622 "attached_device_name": name,
623 "attached_device_type": devicetree.DEVICE_CODES.get(att_device.DeviceType.v(), "UNKNOWN")
624 })
625
626 level += 1
627
628 results.append(new)
629
630 return dict(config={}, data=results)
631
633 """Volatility svcscan plugin - scans for services.
634 @see volatility/plugins/malware/svcscan.py
635 """
636 log.debug("Executing Volatility svcscan plugin on {0}".format(self.memdump))
637
638 self.__config()
639 results = []
640
641 command = self.plugins["svcscan"](self.config)
642 for rec in command.calculate():
643 new = {
644 "service_offset": "{0:#x}".format(rec.obj_offset),
645 "service_order": int(rec.Order),
646 "process_id": int(rec.Pid),
647 "service_name": str(rec.ServiceName.dereference()),
648 "service_display_name": str(rec.DisplayName.dereference()),
649 "service_type": str(rec.Type),
650 "service_binary_path": str(rec.Binary),
651 "service_state": str(rec.State)
652 }
653
654 results.append(new)
655
656 return dict(config={}, data=results)
657
659 """Volatility modscan plugin.
660 @see volatility/plugins/modscan.py
661 """
662 log.debug("Executing Volatility modscan plugin on {0}".format(self.memdump))
663
664 self.__config()
665 results = []
666
667 command = self.plugins["modscan"](self.config)
668 for ldr_entry in command.calculate():
669 new = {
670 "kernel_module_offset": "{0:#x}".format(ldr_entry.obj_offset),
671 "kernel_module_name": str(ldr_entry.BaseDllName or ""),
672 "kernel_module_file": str(ldr_entry.FullDllName or ""),
673 "kernel_module_base": "{0:#x}".format(ldr_entry.DllBase),
674 "kernel_module_size": int(ldr_entry.SizeOfImage),
675 }
676
677 results.append(new)
678
679 return dict(config={}, data=results)
680
682 """Volatility imageinfo plugin.
683 @see volatility/plugins/imageinfo.py
684 """
685 log.debug("Executing Volatility imageinfo plugin on {0}".format(self.memdump))
686
687 self.__config()
688 results = []
689
690 command = self.plugins["imageinfo"](self.config)
691 new = {}
692 for key, value in command.calculate():
693 new[key] = value
694
695 osp = new["Suggested Profile(s)"].split(",")[0]
696 new["osprofile"] = osp
697
698 results.append(new)
699
700 return dict(config={}, data=results)
701
703 """Handle several volatility results."""
704
705 - def __init__(self, memfile, osprofile=None):
706 self.mask_pid = []
707 self.taint_pid = set()
708 self.memfile = memfile
709
710 conf_path = os.path.join(CUCKOO_ROOT, "conf", "memory.conf")
711 if not os.path.exists(conf_path):
712 log.error("Configuration file volatility.conf not found".format(conf_path))
713 self.voptions = False
714 return
715
716 self.voptions = Config(conf_path)
717
718 for pid in self.voptions.mask.pid_generic.split(","):
719 pid = pid.strip()
720 if pid:
721 self.mask_pid.append(int(pid))
722
723 self.no_filter = not self.voptions.mask.enabled
724 if self.voptions.basic.guest_profile:
725 self.osprofile = self.voptions.basic.guest_profile
726 else:
727 self.osprofile = osprofile or self.get_osprofile()
728
732
782
784 """Filter out masked stuff. Keep tainted stuff."""
785 new = {}
786
787 for akey in old.keys():
788 new[akey] = {"config": old[akey]["config"], "data": []}
789 conf = getattr(self.voptions, akey, None)
790 new[akey]["config"]["filter"] = conf.filter
791 for item in old[akey]["data"]:
792
793 if not conf.filter:
794 new[akey]["data"].append(item)
795 elif ("process_id" in item and item["process_id"] in self.mask_pid and not item["process_id"] in self.taint_pid):
796 pass
797 else:
798 new[akey]["data"].append(item)
799 return new
800
802 """Find tainted items."""
803 if "malfind" in res:
804 for item in res["malfind"]["data"]:
805 self.taint_pid.add(item["process_id"])
806
808 """Delete the memory dump (if configured to do so)."""
809
810 if self.voptions.basic.delete_memdump:
811 try:
812 os.remove(self.memfile)
813 except OSError:
814 log.error("Unable to delete memory dump file at path \"%s\" ", self.memfile)
815
817 """Volatility Analyzer."""
818
820 """Run analysis.
821 @return: volatility results dict.
822 """
823 self.key = "memory"
824
825 results = {}
826 if HAVE_VOLATILITY:
827 if self.memory_path and os.path.exists(self.memory_path):
828 try:
829 vol = VolatilityManager(self.memory_path)
830 results = vol.run()
831 except Exception:
832 log.exception("Generic error executing volatility")
833 else:
834 log.error("Memory dump not found: to run volatility you have to enable memory_dump")
835 else:
836 log.error("Cannot run volatility module: volatility library not available")
837
838 return results
839