Package lib :: Package cuckoo :: Package common :: Module abstracts
[hide private]
[frames] | no frames]

Source Code for Module lib.cuckoo.common.abstracts

  1  # Copyright (C) 2010-2014 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 re 
  7  import logging 
  8  import time 
  9   
 10  import xml.etree.ElementTree as ET 
 11   
 12  from lib.cuckoo.common.config import Config 
 13  from lib.cuckoo.common.constants import CUCKOO_ROOT 
 14  from lib.cuckoo.common.exceptions import CuckooCriticalError 
 15  from lib.cuckoo.common.exceptions import CuckooMachineError 
 16  from lib.cuckoo.common.exceptions import CuckooOperationalError 
 17  from lib.cuckoo.common.exceptions import CuckooReportError 
 18  from lib.cuckoo.common.exceptions import CuckooDependencyError 
 19  from lib.cuckoo.common.objects import Dictionary 
 20  from lib.cuckoo.common.utils import create_folder 
 21  from lib.cuckoo.core.database import Database 
 22   
 23  try: 
 24      import libvirt 
 25      HAVE_LIBVIRT = True 
 26  except ImportError: 
 27      HAVE_LIBVIRT = False 
 28   
 29  log = logging.getLogger(__name__) 
 30   
31 -class Auxiliary(object):
32 """Base abstract class for auxiliary modules.""" 33
34 - def __init__(self):
35 self.task = None 36 self.machine = None 37 self.options = None
38
39 - def set_task(self, task):
40 self.task = task
41
42 - def set_machine(self, machine):
43 self.machine = machine
44
45 - def set_options(self, options):
46 self.options = options
47
48 - def start(self):
49 raise NotImplementedError
50
51 - def stop(self):
52 raise NotImplementedError
53 54
55 -class Machinery(object):
56 """Base abstract class for machinery modules.""" 57
58 - def __init__(self):
59 self.module_name = "" 60 self.options = None 61 self.options_globals = Config(os.path.join(CUCKOO_ROOT, "conf", "cuckoo.conf")) 62 # Database pointer. 63 self.db = Database() 64 65 # Machine table is cleaned to be filled from configuration file 66 # at each start. 67 self.db.clean_machines()
68
69 - def set_options(self, options):
70 """Set machine manager options. 71 @param options: machine manager options dict. 72 """ 73 self.options = options
74
75 - def initialize(self, module_name):
76 """Read, load, and verify machines configuration. 77 @param module_name: module name. 78 """ 79 # Load. 80 self._initialize(module_name) 81 82 # Run initialization checks. 83 self._initialize_check()
84
85 - def _initialize(self, module_name):
86 """Read configuration. 87 @param module_name: module name. 88 """ 89 self.module_name = module_name 90 mmanager_opts = self.options.get(module_name) 91 92 for machine_id in mmanager_opts["machines"].strip().split(","): 93 try: 94 machine_opts = self.options.get(machine_id.strip()) 95 machine = Dictionary() 96 machine.id = machine_id.strip() 97 machine.label = machine_opts["label"] 98 machine.platform = machine_opts["platform"] 99 machine.tags = machine_opts.get("tags", None) 100 machine.ip = machine_opts["ip"] 101 102 # If configured, use specific network interface for this 103 # machine, else use the default value. 104 machine.interface = machine_opts.get("interface", None) 105 106 # If configured, use specific snapshot name, else leave it 107 # empty and use default behaviour. 108 machine.snapshot = machine_opts.get("snapshot", None) 109 110 # If configured, use specific resultserver IP and port, 111 # else use the default value. 112 opt_resultserver = self.options_globals.resultserver 113 ip = machine_opts.get("resultserver_ip", opt_resultserver.ip) 114 port = machine_opts.get("resultserver_port", opt_resultserver.port) 115 116 machine.resultserver_ip = ip 117 machine.resultserver_port = port 118 119 # Strip params. 120 for key in machine.keys(): 121 if machine[key]: 122 # Only strip strings 123 if isinstance(machine[key], (str, unicode)): 124 machine[key] = machine[key].strip() 125 126 self.db.add_machine(name=machine.id, 127 label=machine.label, 128 ip=machine.ip, 129 platform=machine.platform, 130 tags=machine.tags, 131 interface=machine.interface, 132 snapshot=machine.snapshot, 133 resultserver_ip=ip, 134 resultserver_port=port) 135 except (AttributeError, CuckooOperationalError) as e: 136 log.warning("Configuration details about machine %s " 137 "are missing: %s", machine_id, e) 138 continue
139
140 - def _initialize_check(self):
141 """Runs checks against virtualization software when a machine manager 142 is initialized. 143 @note: in machine manager modules you may override or superclass 144 his method. 145 @raise CuckooMachineError: if a misconfiguration or a unkown vm state 146 is found. 147 """ 148 try: 149 configured_vms = self._list() 150 except NotImplementedError: 151 return 152 153 for machine in self.machines(): 154 # If this machine is already in the "correct" state, then we 155 # go on to the next machine. 156 if machine.label in configured_vms and \ 157 self._status(machine.label) in [self.POWEROFF, self.ABORTED]: 158 continue 159 160 # This machine is currently not in its correct state, we're going 161 # to try to shut it down. If that works, then the machine is fine. 162 try: 163 self.stop(machine.label) 164 except CuckooMachineError as e: 165 msg = "Please update your configuration. Unable to shut " \ 166 "'{0}' down or find the machine in its proper state:" \ 167 " {1}".format(machine.label, e) 168 raise CuckooCriticalError(msg) 169 170 if not self.options_globals.timeouts.vm_state: 171 raise CuckooCriticalError("Virtual machine state change timeout " 172 "setting not found, please add it to " 173 "the config file")
174
175 - def machines(self):
176 """List virtual machines. 177 @return: virtual machines list 178 """ 179 return self.db.list_machines()
180
181 - def availables(self):
182 """How many machines are free. 183 @return: free machines count. 184 """ 185 return self.db.count_machines_available()
186
187 - def acquire(self, machine_id=None, platform=None, tags=None):
188 """Acquire a machine to start analysis. 189 @param machine_id: machine ID. 190 @param platform: machine platform. 191 @param tags: machine tags 192 @return: machine or None. 193 """ 194 if machine_id: 195 return self.db.lock_machine(name=machine_id) 196 elif platform: 197 return self.db.lock_machine(platform=platform, tags=tags) 198 else: 199 return self.db.lock_machine(tags=tags)
200
201 - def release(self, label=None):
202 """Release a machine. 203 @param label: machine name. 204 """ 205 self.db.unlock_machine(label)
206
207 - def running(self):
208 """Returns running virtual machines. 209 @return: running virtual machines list. 210 """ 211 return self.db.list_machines(locked=True)
212
213 - def shutdown(self):
214 """Shutdown the machine manager. Kills all alive machines. 215 @raise CuckooMachineError: if unable to stop machine. 216 """ 217 if len(self.running()) > 0: 218 log.info("Still %s guests alive. Shutting down...", 219 len(self.running())) 220 for machine in self.running(): 221 try: 222 self.stop(machine.label) 223 except CuckooMachineError as e: 224 log.warning("Unable to shutdown machine %s, please check " 225 "manually. Error: %s", machine.label, e)
226
227 - def set_status(self, label, status):
228 """Set status for a virtual machine. 229 @param label: virtual machine label 230 @param status: new virtual machine status 231 """ 232 self.db.set_machine_status(label, status)
233
234 - def start(self, label=None):
235 """Start a machine. 236 @param label: machine name. 237 @raise NotImplementedError: this method is abstract. 238 """ 239 raise NotImplementedError
240
241 - def stop(self, label=None):
242 """Stop a machine. 243 @param label: machine name. 244 @raise NotImplementedError: this method is abstract. 245 """ 246 raise NotImplementedError
247
248 - def _list(self):
249 """Lists virtual machines configured. 250 @raise NotImplementedError: this method is abstract. 251 """ 252 raise NotImplementedError
253
254 - def dump_memory(self, label, path):
255 """Takes a memory dump of a machine. 256 @param path: path to where to store the memory dump. 257 """ 258 raise NotImplementedError
259
260 - def _wait_status(self, label, state):
261 """Waits for a vm status. 262 @param label: virtual machine name. 263 @param state: virtual machine status, accepts multiple states as list. 264 @raise CuckooMachineError: if default waiting timeout expire. 265 """ 266 # This block was originally suggested by Loic Jaquemet. 267 waitme = 0 268 try: 269 current = self._status(label) 270 except NameError: 271 return 272 273 if isinstance(state, str): 274 state = [state] 275 while current not in state: 276 log.debug("Waiting %i cuckooseconds for machine %s to switch " 277 "to status %s", waitme, label, state) 278 if waitme > int(self.options_globals.timeouts.vm_state): 279 raise CuckooMachineError("Timeout hit while for machine {0} " 280 "to change status".format(label)) 281 time.sleep(1) 282 waitme += 1 283 current = self._status(label)
284 285
286 -class LibVirtMachinery(Machinery):
287 """Libvirt based machine manager. 288 289 If you want to write a custom module for a virtualization software 290 supported by libvirt you have just to inherit this machine manager and 291 change the connection string. 292 """ 293 294 # VM states. 295 RUNNING = "running" 296 PAUSED = "paused" 297 POWEROFF = "poweroff" 298 ERROR = "machete" 299 ABORTED = "abort" 300
301 - def __init__(self):
302 if not HAVE_LIBVIRT: 303 raise CuckooDependencyError("Unable to import libvirt") 304 305 super(LibVirtMachinery, self).__init__()
306
307 - def initialize(self, module):
308 """Initialize machine manager module. Override default to set proper 309 connection string. 310 @param module: machine manager module 311 """ 312 super(LibVirtMachinery, self).initialize(module)
313
314 - def _initialize_check(self):
315 """Runs all checks when a machine manager is initialized. 316 @raise CuckooMachineError: if libvirt version is not supported. 317 """ 318 # Version checks. 319 if not self._version_check(): 320 raise CuckooMachineError("Libvirt version is not supported, " 321 "please get an updated version") 322 323 # Preload VMs 324 self.vms = self._fetch_machines() 325 326 # Base checks. Also attempts to shutdown any machines which are 327 # currently still active. 328 super(LibVirtMachinery, self)._initialize_check()
329
330 - def start(self, label):
331 """Starts a virtual machine. 332 @param label: virtual machine name. 333 @raise CuckooMachineError: if unable to start virtual machine. 334 """ 335 log.debug("Starting machine %s", label) 336 337 if self._status(label) != self.POWEROFF: 338 msg = "Trying to start a virtual machine that has not " \ 339 "been turned off {0}".format(label) 340 raise CuckooMachineError(msg) 341 342 conn = self._connect() 343 344 vm_info = self.db.view_machine_by_label(label) 345 346 snapshot_list = self.vms[label].snapshotListNames(flags=0) 347 348 # If a snapshot is configured try to use it. 349 if vm_info.snapshot and vm_info.snapshot in snapshot_list: 350 # Revert to desired snapshot, if it exists. 351 log.debug("Using snapshot {0} for virtual machine " 352 "{1}".format(vm_info.snapshot, label)) 353 try: 354 vm = self.vms[label] 355 snapshot = vm.snapshotLookupByName(vm_info.snapshot, flags=0) 356 self.vms[label].revertToSnapshot(snapshot, flags=0) 357 except libvirt.libvirtError: 358 msg = "Unable to restore snapshot {0} on " \ 359 "virtual machine {1}".format(vm_info.snapshot, label) 360 raise CuckooMachineError(msg) 361 finally: 362 self._disconnect(conn) 363 elif self._get_snapshot(label): 364 snapshot = self._get_snapshot(label) 365 log.debug("Using snapshot {0} for virtual machine " 366 "{1}".format(snapshot.getName(), label)) 367 try: 368 self.vms[label].revertToSnapshot(snapshot, flags=0) 369 except libvirt.libvirtError: 370 raise CuckooMachineError("Unable to restore snapshot on " 371 "virtual machine {0}".format(label)) 372 finally: 373 self._disconnect(conn) 374 else: 375 self._disconnect(conn) 376 raise CuckooMachineError("No snapshot found for virtual machine " 377 "{0}".format(label)) 378 379 # Check state. 380 self._wait_status(label, self.RUNNING)
381
382 - def stop(self, label):
383 """Stops a virtual machine. Kill them all. 384 @param label: virtual machine name. 385 @raise CuckooMachineError: if unable to stop virtual machine. 386 """ 387 log.debug("Stopping machine %s", label) 388 389 if self._status(label) == self.POWEROFF: 390 raise CuckooMachineError("Trying to stop an already stopped " 391 "machine {0}".format(label)) 392 393 # Force virtual machine shutdown. 394 conn = self._connect() 395 try: 396 if not self.vms[label].isActive(): 397 log.debug("Trying to stop an already stopped machine %s. " 398 "Skip", label) 399 else: 400 self.vms[label].destroy() # Machete's way! 401 except libvirt.libvirtError as e: 402 raise CuckooMachineError("Error stopping virtual machine " 403 "{0}: {1}".format(label, e)) 404 finally: 405 self._disconnect(conn) 406 # Check state. 407 self._wait_status(label, self.POWEROFF)
408
409 - def shutdown(self):
410 """Override shutdown to free libvirt handlers - they print errors.""" 411 super(LibVirtMachinery, self).shutdown() 412 413 # Free handlers. 414 self.vms = None
415
416 - def dump_memory(self, label, path):
417 """Takes a memory dump. 418 @param path: path to where to store the memory dump. 419 """ 420 log.debug("Dumping memory for machine %s", label) 421 422 conn = self._connect() 423 try: 424 self.vms[label].coreDump(path, flags=libvirt.VIR_DUMP_MEMORY_ONLY) 425 except libvirt.libvirtError as e: 426 raise CuckooMachineError("Error dumping memory virtual machine " 427 "{0}: {1}".format(label, e)) 428 finally: 429 self._disconnect(conn)
430
431 - def _status(self, label):
432 """Gets current status of a vm. 433 @param label: virtual machine name. 434 @return: status string. 435 """ 436 log.debug("Getting status for %s", label) 437 438 # Stetes mapping of python-libvirt. 439 # virDomainState 440 # VIR_DOMAIN_NOSTATE = 0 441 # VIR_DOMAIN_RUNNING = 1 442 # VIR_DOMAIN_BLOCKED = 2 443 # VIR_DOMAIN_PAUSED = 3 444 # VIR_DOMAIN_SHUTDOWN = 4 445 # VIR_DOMAIN_SHUTOFF = 5 446 # VIR_DOMAIN_CRASHED = 6 447 # VIR_DOMAIN_PMSUSPENDED = 7 448 449 conn = self._connect() 450 try: 451 state = self.vms[label].state(flags=0) 452 except libvirt.libvirtError as e: 453 raise CuckooMachineError("Error getting status for virtual " 454 "machine {0}: {1}".format(label, e)) 455 finally: 456 self._disconnect(conn) 457 458 if state: 459 if state[0] == 1: 460 status = self.RUNNING 461 elif state[0] == 3: 462 status = self.PAUSED 463 elif state[0] == 4 or state[0] == 5: 464 status = self.POWEROFF 465 else: 466 status = self.ERROR 467 468 # Report back status. 469 if status: 470 self.set_status(label, status) 471 return status 472 else: 473 raise CuckooMachineError("Unable to get status for " 474 "{0}".format(label))
475
476 - def _connect(self):
477 """Connects to libvirt subsystem. 478 @raise CuckooMachineError: when unable to connect to libvirt. 479 """ 480 # Check if a connection string is available. 481 if not self.dsn: 482 raise CuckooMachineError("You must provide a proper " 483 "connection string") 484 485 try: 486 return libvirt.open(self.dsn) 487 except libvirt.libvirtError: 488 raise CuckooMachineError("Cannot connect to libvirt")
489
490 - def _disconnect(self, conn):
491 """Disconnects to libvirt subsystem. 492 @raise CuckooMachineError: if cannot disconnect from libvirt. 493 """ 494 try: 495 conn.close() 496 except libvirt.libvirtError: 497 raise CuckooMachineError("Cannot disconnect from libvirt")
498
499 - def _fetch_machines(self):
500 """Fetch machines handlers. 501 @return: dict with machine label as key and handle as value. 502 """ 503 vms = {} 504 for vm in self.machines(): 505 vms[vm.label] = self._lookup(vm.label) 506 return vms
507
508 - def _lookup(self, label):
509 """Search for a virtual machine. 510 @param conn: libvirt connection handle. 511 @param label: virtual machine name. 512 @raise CuckooMachineError: if virtual machine is not found. 513 """ 514 conn = self._connect() 515 try: 516 vm = conn.lookupByName(label) 517 except libvirt.libvirtError: 518 raise CuckooMachineError("Cannot find machine " 519 "{0}".format(label)) 520 finally: 521 self._disconnect(conn) 522 return vm
523
524 - def _list(self):
525 """List available virtual machines. 526 @raise CuckooMachineError: if unable to list virtual machines. 527 """ 528 conn = self._connect() 529 try: 530 names = conn.listDefinedDomains() 531 except libvirt.libvirtError: 532 raise CuckooMachineError("Cannot list domains") 533 finally: 534 self._disconnect(conn) 535 return names
536
537 - def _version_check(self):
538 """Check if libvirt release supports snapshots. 539 @return: True or false. 540 """ 541 if libvirt.getVersion() >= 8000: 542 return True 543 else: 544 return False
545
546 - def _get_snapshot(self, label):
547 """Get current snapshot for virtual machine 548 @param label: virtual machine name 549 @return None or current snapshot 550 @raise CuckooMachineError: if cannot find current snapshot or 551 when there are too many snapshots available 552 """ 553 # Checks for current snapshots. 554 conn = self._connect() 555 try: 556 vm = self.vms[label] 557 snap = vm.hasCurrentSnapshot(flags=0) 558 except libvirt.libvirtError: 559 self._disconnect(conn) 560 raise CuckooMachineError("Unable to get current snapshot for " 561 "virtual machine {0}".format(label)) 562 finally: 563 self._disconnect(conn) 564 565 if snap: 566 return vm.snapshotCurrent(flags=0) 567 568 # If no current snapshot, get the last one. 569 conn = self._connect() 570 try: 571 snaps = vm[label].snapshotListNames(flags=0) 572 573 def get_create(sn): 574 xml_desc = sn.getXMLDesc(flags=0) 575 return ET.fromstring(xml_desc).findtext("./creationTime")
576 577 return max(get_create(vm.snapshotLookupByName(name, flags=0)) 578 for name in snaps) 579 except libvirt.libvirtError: 580 return None 581 except ValueError: 582 return None 583 finally: 584 self._disconnect(conn)
585
586 -class Processing(object):
587 """Base abstract class for processing module.""" 588 order = 1 589 enabled = True 590
591 - def __init__(self):
592 self.analysis_path = "" 593 self.logs_path = "" 594 self.task = None 595 self.options = None
596
597 - def set_options(self, options):
598 """Set report options. 599 @param options: report options dict. 600 """ 601 self.options = options
602
603 - def set_task(self, task):
604 """Add task information. 605 @param task: task dictionary. 606 """ 607 self.task = task
608
609 - def set_path(self, analysis_path):
610 """Set paths. 611 @param analysis_path: analysis folder path. 612 """ 613 self.analysis_path = analysis_path 614 self.log_path = os.path.join(self.analysis_path, "analysis.log") 615 self.file_path = os.path.realpath(os.path.join(self.analysis_path, 616 "binary")) 617 self.dropped_path = os.path.join(self.analysis_path, "files") 618 self.logs_path = os.path.join(self.analysis_path, "logs") 619 self.shots_path = os.path.join(self.analysis_path, "shots") 620 self.pcap_path = os.path.join(self.analysis_path, "dump.pcap") 621 self.pmemory_path = os.path.join(self.analysis_path, "memory") 622 self.memory_path = os.path.join(self.analysis_path, "memory.dmp")
623
624 - def run(self):
625 """Start processing. 626 @raise NotImplementedError: this method is abstract. 627 """ 628 raise NotImplementedError
629
630 -class Signature(object):
631 """Base class for Cuckoo signatures.""" 632 633 name = "" 634 description = "" 635 severity = 1 636 categories = [] 637 families = [] 638 authors = [] 639 references = [] 640 alert = False 641 enabled = True 642 minimum = None 643 maximum = None 644 645 # Higher order will be processed later (only for non-evented signatures) 646 # this can be used for having meta-signatures that check on other lower- 647 # order signatures being matched 648 order = 0 649 650 evented = False 651 filter_processnames = set() 652 filter_apinames = set() 653 filter_categories = set() 654
655 - def __init__(self, results=None):
656 self.data = [] 657 self.results = results 658 self._current_call_cache = None 659 self._current_call_dict = None
660
661 - def _check_value(self, pattern, subject, regex=False):
662 """Checks a pattern against a given subject. 663 @param pattern: string or expression to check for. 664 @param subject: target of the check. 665 @param regex: boolean representing if the pattern is a regular 666 expression or not and therefore should be compiled. 667 @return: boolean with the result of the check. 668 """ 669 if regex: 670 exp = re.compile(pattern, re.IGNORECASE) 671 if isinstance(subject, list): 672 for item in subject: 673 if exp.match(item): 674 return item 675 else: 676 if exp.match(subject): 677 return subject 678 else: 679 if isinstance(subject, list): 680 for item in subject: 681 if item == pattern: 682 return item 683 else: 684 if subject == pattern: 685 return subject 686 687 return None
688
689 - def check_file(self, pattern, regex=False):
690 """Checks for a file being opened. 691 @param pattern: string or expression to check for. 692 @param regex: boolean representing if the pattern is a regular 693 expression or not and therefore should be compiled. 694 @return: boolean with the result of the check. 695 """ 696 subject = self.results["behavior"]["summary"]["files"] 697 return self._check_value(pattern=pattern, 698 subject=subject, 699 regex=regex)
700
701 - def check_key(self, pattern, regex=False):
702 """Checks for a registry key being opened. 703 @param pattern: string or expression to check for. 704 @param regex: boolean representing if the pattern is a regular 705 expression or not and therefore should be compiled. 706 @return: boolean with the result of the check. 707 """ 708 subject = self.results["behavior"]["summary"]["keys"] 709 return self._check_value(pattern=pattern, 710 subject=subject, 711 regex=regex)
712
713 - def check_mutex(self, pattern, regex=False):
714 """Checks for a mutex being opened. 715 @param pattern: string or expression to check for. 716 @param regex: boolean representing if the pattern is a regular 717 expression or not and therefore should be compiled. 718 @return: boolean with the result of the check. 719 """ 720 subject = self.results["behavior"]["summary"]["mutexes"] 721 return self._check_value(pattern=pattern, 722 subject=subject, 723 regex=regex)
724
725 - def check_api(self, pattern, process=None, regex=False):
726 """Checks for an API being called. 727 @param pattern: string or expression to check for. 728 @param process: optional filter for a specific process name. 729 @param regex: boolean representing if the pattern is a regular 730 expression or not and therefore should be compiled. 731 @return: boolean with the result of the check. 732 """ 733 # Loop through processes. 734 for item in self.results["behavior"]["processes"]: 735 # Check if there's a process name filter. 736 if process: 737 if item["process_name"] != process: 738 continue 739 740 # Loop through API calls. 741 for call in item["calls"]: 742 # Check if the name matches. 743 if self._check_value(pattern=pattern, 744 subject=call["api"], 745 regex=regex): 746 return call["api"] 747 748 return None
749
750 - def check_argument_call(self, 751 call, 752 pattern, 753 name=None, 754 api=None, 755 category=None, 756 regex=False):
757 """Checks for a specific argument of an invoked API. 758 @param call: API call information. 759 @param pattern: string or expression to check for. 760 @param name: optional filter for the argument name. 761 @param api: optional filter for the API function name. 762 @param category: optional filter for a category name. 763 @param regex: boolean representing if the pattern is a regular 764 expression or not and therefore should be compiled. 765 @return: boolean with the result of the check. 766 """ 767 # Check if there's an API name filter. 768 if api: 769 if call["api"] != api: 770 return False 771 772 # Check if there's a category filter. 773 if category: 774 if call["category"] != category: 775 return False 776 777 # Loop through arguments. 778 for argument in call["arguments"]: 779 # Check if there's an argument name filter. 780 if name: 781 if argument["name"] != name: 782 continue 783 784 # Check if the argument value matches. 785 if self._check_value(pattern=pattern, 786 subject=argument["value"], 787 regex=regex): 788 return argument["value"] 789 790 return False
791
792 - def check_argument(self, 793 pattern, 794 name=None, 795 api=None, 796 category=None, 797 process=None, 798 regex=False):
799 """Checks for a specific argument of an invoked API. 800 @param pattern: string or expression to check for. 801 @param name: optional filter for the argument name. 802 @param api: optional filter for the API function name. 803 @param category: optional filter for a category name. 804 @param process: optional filter for a specific process name. 805 @param regex: boolean representing if the pattern is a regular 806 expression or not and therefore should be compiled. 807 @return: boolean with the result of the check. 808 """ 809 # Loop through processes. 810 for item in self.results["behavior"]["processes"]: 811 # Check if there's a process name filter. 812 if process: 813 if item["process_name"] != process: 814 continue 815 816 # Loop through API calls. 817 for call in item["calls"]: 818 r = self.check_argument_call(call, pattern, name, 819 api, category, regex) 820 if r: 821 return r 822 823 return None
824
825 - def check_ip(self, pattern, regex=False):
826 """Checks for an IP address being contacted. 827 @param pattern: string or expression to check for. 828 @param regex: boolean representing if the pattern is a regular 829 expression or not and therefore should be compiled. 830 @return: boolean with the result of the check. 831 """ 832 return self._check_value(pattern=pattern, 833 subject=self.results["network"]["hosts"], 834 regex=regex)
835
836 - def check_domain(self, pattern, regex=False):
837 """Checks for a domain being contacted. 838 @param pattern: string or expression to check for. 839 @param regex: boolean representing if the pattern is a regular 840 expression or not and therefore should be compiled. 841 @return: boolean with the result of the check. 842 """ 843 for item in self.results["network"]["domains"]: 844 if self._check_value(pattern=pattern, 845 subject=item["domain"], 846 regex=regex): 847 return item 848 849 return None
850
851 - def check_url(self, pattern, regex=False):
852 """Checks for a URL being contacted. 853 @param pattern: string or expression to check for. 854 @param regex: boolean representing if the pattern is a regular 855 expression or not and therefore should be compiled. 856 @return: boolean with the result of the check. 857 """ 858 for item in self.results["network"]["http"]: 859 if self._check_value(pattern=pattern, 860 subject=item["uri"], 861 regex=regex): 862 return item 863 864 return None
865
866 - def get_argument(self, call, name):
867 """Retrieves the value of a specific argument from an API call. 868 @param call: API call object. 869 @param name: name of the argument to retrieve. 870 @return: value of the requried argument. 871 """ 872 # Check if the call passed to it was cached already. 873 # If not, we can start caching it and store a copy converted to a dict. 874 if call is not self._current_call_cache: 875 self._current_call_cache = call 876 self._current_call_dict = dict() 877 878 for argument in call["arguments"]: 879 self._current_call_dict[argument["name"]] = argument["value"] 880 881 # Return the required argument. 882 if name in self._current_call_dict: 883 return self._current_call_dict[name] 884 885 return None
886
887 - def on_call(self, call, process):
888 """Notify signature about API call. Return value determines 889 if this signature is done or could still match. 890 @param call: logged API call. 891 @param process: process doing API call. 892 @raise NotImplementedError: this method is abstract. 893 """ 894 raise NotImplementedError
895
896 - def on_complete(self):
897 """Evented signature is notified when all API calls are done. 898 @return: Match state. 899 @raise NotImplementedError: this method is abstract. 900 """ 901 raise NotImplementedError
902
903 - def run(self):
904 """Start signature processing. 905 @param results: analysis results. 906 @raise NotImplementedError: this method is abstract. 907 """ 908 raise NotImplementedError
909
910 - def as_result(self):
911 """Properties as a dict (for results). 912 @return: result dictionary. 913 """ 914 return dict( 915 name=self.name, 916 description=self.description, 917 severity=self.severity, 918 references=self.references, 919 data=self.data, 920 alert=self.alert, 921 families=self.families 922 )
923
924 -class Report(object):
925 """Base abstract class for reporting module.""" 926 order = 1 927
928 - def __init__(self):
929 self.analysis_path = "" 930 self.reports_path = "" 931 self.task = None 932 self.options = None
933
934 - def set_path(self, analysis_path):
935 """Set analysis folder path. 936 @param analysis_path: analysis folder path. 937 """ 938 self.analysis_path = analysis_path 939 self.conf_path = os.path.join(self.analysis_path, "analysis.conf") 940 self.file_path = os.path.realpath(os.path.join(self.analysis_path, 941 "binary")) 942 self.reports_path = os.path.join(self.analysis_path, "reports") 943 self.shots_path = os.path.join(self.analysis_path, "shots") 944 self.pcap_path = os.path.join(self.analysis_path, "dump.pcap") 945 946 try: 947 create_folder(folder=self.reports_path) 948 except CuckooOperationalError as e: 949 CuckooReportError(e)
950
951 - def set_options(self, options):
952 """Set report options. 953 @param options: report options dict. 954 """ 955 self.options = options
956
957 - def set_task(self, task):
958 """Add task information. 959 @param task: task dictionary. 960 """ 961 self.task = task
962
963 - def run(self):
964 """Start report processing. 965 @raise NotImplementedError: this method is abstract. 966 """ 967 raise NotImplementedError
968