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