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 Sandbox Developers. 
  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) == self.POWEROFF: 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, 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
300 - def __init__(self):
301 if not HAVE_LIBVIRT: 302 raise CuckooDependencyError("Unable to import libvirt") 303 304 super(LibVirtMachinery, self).__init__()
305
306 - def initialize(self, module):
307 """Initialize machine manager module. Override default to set proper 308 connection string. 309 @param module: machine manager module 310 """ 311 super(LibVirtMachinery, self).initialize(module)
312
313 - def _initialize_check(self):
314 """Runs all checks when a machine manager is initialized. 315 @raise CuckooMachineError: if libvirt version is not supported. 316 """ 317 # Version checks. 318 if not self._version_check(): 319 raise CuckooMachineError("Libvirt version is not supported, " 320 "please get an updated version") 321 322 # Preload VMs 323 self.vms = self._fetch_machines() 324 325 # Base checks. Also attempts to shutdown any machines which are 326 # currently still active. 327 super(LibVirtMachinery, self)._initialize_check()
328
329 - def start(self, label):
330 """Starts a virtual machine. 331 @param label: virtual machine name. 332 @raise CuckooMachineError: if unable to start virtual machine. 333 """ 334 log.debug("Starting machine %s", label) 335 336 if self._status(label) != self.POWEROFF: 337 msg = "Trying to start a virtual machine that has not " \ 338 "been turned off {0}".format(label) 339 raise CuckooMachineError(msg) 340 341 conn = self._connect() 342 343 vm_info = self.db.view_machine_by_label(label) 344 345 snapshot_list = self.vms[label].snapshotListNames(flags=0) 346 347 # If a snapshot is configured try to use it. 348 if vm_info.snapshot and vm_info.snapshot in snapshot_list: 349 # Revert to desired snapshot, if it exists. 350 log.debug("Using snapshot {0} for virtual machine " 351 "{1}".format(vm_info.snapshot, label)) 352 try: 353 vm = self.vms[label] 354 snapshot = vm.snapshotLookupByName(vm_info.snapshot, flags=0) 355 self.vms[label].revertToSnapshot(snapshot, flags=0) 356 except libvirt.libvirtError: 357 msg = "Unable to restore snapshot {0} on " \ 358 "virtual machine {1}".format(vm_info.snapshot, label) 359 raise CuckooMachineError(msg) 360 finally: 361 self._disconnect(conn) 362 elif self._get_snapshot(label): 363 snapshot = self._get_snapshot(label) 364 log.debug("Using snapshot {0} for virtual machine " 365 "{1}".format(snapshot.getName(), label)) 366 try: 367 self.vms[label].revertToSnapshot(snapshot, flags=0) 368 except libvirt.libvirtError: 369 raise CuckooMachineError("Unable to restore snapshot on " 370 "virtual machine {0}".format(label)) 371 finally: 372 self._disconnect(conn) 373 else: 374 self._disconnect(conn) 375 raise CuckooMachineError("No snapshot found for virtual machine " 376 "{0}".format(label)) 377 378 # Check state. 379 self._wait_status(label, self.RUNNING)
380
381 - def stop(self, label):
382 """Stops a virtual machine. Kill them all. 383 @param label: virtual machine name. 384 @raise CuckooMachineError: if unable to stop virtual machine. 385 """ 386 log.debug("Stopping machine %s", label) 387 388 if self._status(label) == self.POWEROFF: 389 raise CuckooMachineError("Trying to stop an already stopped " 390 "machine {0}".format(label)) 391 392 # Force virtual machine shutdown. 393 conn = self._connect() 394 try: 395 if not self.vms[label].isActive(): 396 log.debug("Trying to stop an already stopped machine %s. " 397 "Skip", label) 398 else: 399 self.vms[label].destroy() # Machete's way! 400 except libvirt.libvirtError as e: 401 raise CuckooMachineError("Error stopping virtual machine " 402 "{0}: {1}".format(label, e)) 403 finally: 404 self._disconnect(conn) 405 # Check state. 406 self._wait_status(label, self.POWEROFF)
407
408 - def shutdown(self):
409 """Override shutdown to free libvirt handlers - they print errors.""" 410 super(LibVirtMachinery, self).shutdown() 411 412 # Free handlers. 413 self.vms = None
414
415 - def dump_memory(self, label, path):
416 """Takes a memory dump. 417 @param path: path to where to store the memory dump. 418 """ 419 log.debug("Dumping memory for machine %s", label) 420 421 conn = self._connect() 422 try: 423 self.vms[label].coreDump(path, flags=libvirt.VIR_DUMP_MEMORY_ONLY) 424 except libvirt.libvirtError as e: 425 raise CuckooMachineError("Error dumping memory virtual machine " 426 "{0}: {1}".format(label, e)) 427 finally: 428 self._disconnect(conn)
429
430 - def _status(self, label):
431 """Gets current status of a vm. 432 @param label: virtual machine name. 433 @return: status string. 434 """ 435 log.debug("Getting status for %s", label) 436 437 # Stetes mapping of python-libvirt. 438 # virDomainState 439 # VIR_DOMAIN_NOSTATE = 0 440 # VIR_DOMAIN_RUNNING = 1 441 # VIR_DOMAIN_BLOCKED = 2 442 # VIR_DOMAIN_PAUSED = 3 443 # VIR_DOMAIN_SHUTDOWN = 4 444 # VIR_DOMAIN_SHUTOFF = 5 445 # VIR_DOMAIN_CRASHED = 6 446 # VIR_DOMAIN_PMSUSPENDED = 7 447 448 conn = self._connect() 449 try: 450 state = self.vms[label].state(flags=0) 451 except libvirt.libvirtError as e: 452 raise CuckooMachineError("Error getting status for virtual " 453 "machine {0}: {1}".format(label, e)) 454 finally: 455 self._disconnect(conn) 456 457 if state: 458 if state[0] == 1: 459 status = self.RUNNING 460 elif state[0] == 3: 461 status = self.PAUSED 462 elif state[0] == 4 or state[0] == 5: 463 status = self.POWEROFF 464 else: 465 status = self.ERROR 466 467 # Report back status. 468 if status: 469 self.set_status(label, status) 470 return status 471 else: 472 raise CuckooMachineError("Unable to get status for " 473 "{0}".format(label))
474
475 - def _connect(self):
476 """Connects to libvirt subsystem. 477 @raise CuckooMachineError: when unable to connect to libvirt. 478 """ 479 # Check if a connection string is available. 480 if not self.dsn: 481 raise CuckooMachineError("You must provide a proper " 482 "connection string") 483 484 try: 485 return libvirt.open(self.dsn) 486 except libvirt.libvirtError: 487 raise CuckooMachineError("Cannot connect to libvirt")
488
489 - def _disconnect(self, conn):
490 """Disconnects to libvirt subsystem. 491 @raise CuckooMachineError: if cannot disconnect from libvirt. 492 """ 493 try: 494 conn.close() 495 except libvirt.libvirtError: 496 raise CuckooMachineError("Cannot disconnect from libvirt")
497
498 - def _fetch_machines(self):
499 """Fetch machines handlers. 500 @return: dict with machine label as key and handle as value. 501 """ 502 vms = {} 503 for vm in self.machines(): 504 vms[vm.label] = self._lookup(vm.label) 505 return vms
506
507 - def _lookup(self, label):
508 """Search for a virtual machine. 509 @param conn: libvirt connection handle. 510 @param label: virtual machine name. 511 @raise CuckooMachineError: if virtual machine is not found. 512 """ 513 conn = self._connect() 514 try: 515 vm = conn.lookupByName(label) 516 except libvirt.libvirtError: 517 raise CuckooMachineError("Cannot find machine " 518 "{0}".format(label)) 519 finally: 520 self._disconnect(conn) 521 return vm
522
523 - def _list(self):
524 """List available virtual machines. 525 @raise CuckooMachineError: if unable to list virtual machines. 526 """ 527 conn = self._connect() 528 try: 529 names = conn.listDefinedDomains() 530 except libvirt.libvirtError: 531 raise CuckooMachineError("Cannot list domains") 532 finally: 533 self._disconnect(conn) 534 return names
535
536 - def _version_check(self):
537 """Check if libvirt release supports snapshots. 538 @return: True or false. 539 """ 540 if libvirt.getVersion() >= 8000: 541 return True 542 else: 543 return False
544
545 - def _get_snapshot(self, label):
546 """Get current snapshot for virtual machine 547 @param label: virtual machine name 548 @return None or current snapshot 549 @raise CuckooMachineError: if cannot find current snapshot or 550 when there are too many snapshots available 551 """ 552 # Checks for current snapshots. 553 conn = self._connect() 554 try: 555 vm = self.vms[label] 556 snap = vm.hasCurrentSnapshot(flags=0) 557 except libvirt.libvirtError: 558 self._disconnect(conn) 559 raise CuckooMachineError("Unable to get current snapshot for " 560 "virtual machine {0}".format(label)) 561 finally: 562 self._disconnect(conn) 563 564 if snap: 565 return vm.snapshotCurrent(flags=0) 566 567 # If no current snapshot, get the last one. 568 conn = self._connect() 569 try: 570 snaps = vm[label].snapshotListNames(flags=0) 571 572 def get_create(sn): 573 xml_desc = sn.getXMLDesc(flags=0) 574 return ET.fromstring(xml_desc).findtext("./creationTime")
575 576 return max(get_create(vm.snapshotLookupByName(name, flags=0)) 577 for name in snaps) 578 except libvirt.libvirtError: 579 return None 580 except ValueError: 581 return None 582 finally: 583 self._disconnect(conn)
584
585 -class Processing(object):
586 """Base abstract class for processing module.""" 587 order = 1 588 enabled = True 589
590 - def __init__(self):
591 self.analysis_path = "" 592 self.logs_path = "" 593 self.task = None 594 self.options = None
595
596 - def set_options(self, options):
597 """Set report options. 598 @param options: report options dict. 599 """ 600 self.options = options
601
602 - def set_task(self, task):
603 """Add task information. 604 @param task: task dictionary. 605 """ 606 self.task = task
607
608 - def set_path(self, analysis_path):
609 """Set paths. 610 @param analysis_path: analysis folder path. 611 """ 612 self.analysis_path = analysis_path 613 self.log_path = os.path.join(self.analysis_path, "analysis.log") 614 self.file_path = os.path.realpath(os.path.join(self.analysis_path, 615 "binary")) 616 self.dropped_path = os.path.join(self.analysis_path, "files") 617 self.logs_path = os.path.join(self.analysis_path, "logs") 618 self.shots_path = os.path.join(self.analysis_path, "shots") 619 self.pcap_path = os.path.join(self.analysis_path, "dump.pcap") 620 self.pmemory_path = os.path.join(self.analysis_path, "memory") 621 self.memory_path = os.path.join(self.analysis_path, "memory.dmp")
622
623 - def run(self):
624 """Start processing. 625 @raise NotImplementedError: this method is abstract. 626 """ 627 raise NotImplementedError
628
629 -class Signature(object):
630 """Base class for Cuckoo signatures.""" 631 632 name = "" 633 description = "" 634 severity = 1 635 categories = [] 636 families = [] 637 authors = [] 638 references = [] 639 alert = False 640 enabled = True 641 minimum = None 642 maximum = None 643 644 evented = False 645 filter_processnames = set() 646 filter_apinames = set() 647 filter_categories = set() 648
649 - def __init__(self, results=None):
650 self.data = [] 651 self.results = results 652 self._current_call_cache = None 653 self._current_call_dict = None
654
655 - def _check_value(self, pattern, subject, regex=False):
656 """Checks a pattern against a given subject. 657 @param pattern: string or expression to check for. 658 @param subject: target of the check. 659 @param regex: boolean representing if the pattern is a regular 660 expression or not and therefore should be compiled. 661 @return: boolean with the result of the check. 662 """ 663 if regex: 664 exp = re.compile(pattern, re.IGNORECASE) 665 if isinstance(subject, list): 666 for item in subject: 667 if exp.match(item): 668 return item 669 else: 670 if exp.match(subject): 671 return subject 672 else: 673 if isinstance(subject, list): 674 for item in subject: 675 if item == pattern: 676 return item 677 else: 678 if subject == pattern: 679 return subject 680 681 return None
682
683 - def check_file(self, pattern, regex=False):
684 """Checks for a file being opened. 685 @param pattern: string or expression to check for. 686 @param regex: boolean representing if the pattern is a regular 687 expression or not and therefore should be compiled. 688 @return: boolean with the result of the check. 689 """ 690 subject = self.results["behavior"]["summary"]["files"] 691 return self._check_value(pattern=pattern, 692 subject=subject, 693 regex=regex)
694
695 - def check_key(self, pattern, regex=False):
696 """Checks for a registry key being opened. 697 @param pattern: string or expression to check for. 698 @param regex: boolean representing if the pattern is a regular 699 expression or not and therefore should be compiled. 700 @return: boolean with the result of the check. 701 """ 702 subject = self.results["behavior"]["summary"]["keys"] 703 return self._check_value(pattern=pattern, 704 subject=subject, 705 regex=regex)
706
707 - def check_mutex(self, pattern, regex=False):
708 """Checks for a mutex being opened. 709 @param pattern: string or expression to check for. 710 @param regex: boolean representing if the pattern is a regular 711 expression or not and therefore should be compiled. 712 @return: boolean with the result of the check. 713 """ 714 subject = self.results["behavior"]["summary"]["mutexes"] 715 return self._check_value(pattern=pattern, 716 subject=subject, 717 regex=regex)
718
719 - def check_api(self, pattern, process=None, regex=False):
720 """Checks for an API being called. 721 @param pattern: string or expression to check for. 722 @param process: optional filter for a specific process name. 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 # Loop through processes. 728 for item in self.results["behavior"]["processes"]: 729 # Check if there's a process name filter. 730 if process: 731 if item["process_name"] != process: 732 continue 733 734 # Loop through API calls. 735 for call in item["calls"]: 736 # Check if the name matches. 737 if self._check_value(pattern=pattern, 738 subject=call["api"], 739 regex=regex): 740 return call["api"] 741 742 return None
743
744 - def check_argument_call(self, 745 call, 746 pattern, 747 name=None, 748 api=None, 749 category=None, 750 regex=False):
751 """Checks for a specific argument of an invoked API. 752 @param call: API call information. 753 @param pattern: string or expression to check for. 754 @param name: optional filter for the argument name. 755 @param api: optional filter for the API function name. 756 @param category: optional filter for a category name. 757 @param regex: boolean representing if the pattern is a regular 758 expression or not and therefore should be compiled. 759 @return: boolean with the result of the check. 760 """ 761 # Check if there's an API name filter. 762 if api: 763 if call["api"] != api: 764 return False 765 766 # Check if there's a category filter. 767 if category: 768 if call["category"] != category: 769 return False 770 771 # Loop through arguments. 772 for argument in call["arguments"]: 773 # Check if there's an argument name filter. 774 if name: 775 if argument["name"] != name: 776 return False 777 778 # Check if the argument value matches. 779 if self._check_value(pattern=pattern, 780 subject=argument["value"], 781 regex=regex): 782 return argument["value"] 783 784 return False
785
786 - def check_argument(self, 787 pattern, 788 name=None, 789 api=None, 790 category=None, 791 process=None, 792 regex=False):
793 """Checks for a specific argument of an invoked API. 794 @param pattern: string or expression to check for. 795 @param name: optional filter for the argument name. 796 @param api: optional filter for the API function name. 797 @param category: optional filter for a category name. 798 @param process: optional filter for a specific process name. 799 @param regex: boolean representing if the pattern is a regular 800 expression or not and therefore should be compiled. 801 @return: boolean with the result of the check. 802 """ 803 # Loop through processes. 804 for item in self.results["behavior"]["processes"]: 805 # Check if there's a process name filter. 806 if process: 807 if item["process_name"] != process: 808 continue 809 810 # Loop through API calls. 811 for call in item["calls"]: 812 r = self.check_argument_call(call, pattern, name, 813 api, category, regex) 814 if r: 815 return r 816 817 return None
818
819 - def check_ip(self, pattern, regex=False):
820 """Checks for an IP address being contacted. 821 @param pattern: string or expression to check for. 822 @param regex: boolean representing if the pattern is a regular 823 expression or not and therefore should be compiled. 824 @return: boolean with the result of the check. 825 """ 826 return self._check_value(pattern=pattern, 827 subject=self.results["network"]["hosts"], 828 regex=regex)
829
830 - def check_domain(self, pattern, regex=False):
831 """Checks for a domain being contacted. 832 @param pattern: string or expression to check for. 833 @param regex: boolean representing if the pattern is a regular 834 expression or not and therefore should be compiled. 835 @return: boolean with the result of the check. 836 """ 837 for item in self.results["network"]["domains"]: 838 if self._check_value(pattern=pattern, 839 subject=item["domain"], 840 regex=regex): 841 return item 842 843 return None
844
845 - def check_url(self, pattern, regex=False):
846 """Checks for a URL being contacted. 847 @param pattern: string or expression to check for. 848 @param regex: boolean representing if the pattern is a regular 849 expression or not and therefore should be compiled. 850 @return: boolean with the result of the check. 851 """ 852 for item in self.results["network"]["http"]: 853 if self._check_value(pattern=pattern, 854 subject=item["uri"], 855 regex=regex): 856 return item 857 858 return None
859
860 - def get_argument(self, call, name):
861 """Retrieves the value of a specific argument from an API call. 862 @param call: API call object. 863 @param name: name of the argument to retrieve. 864 @return: value of the requried argument. 865 """ 866 # Check if the call passed to it was cached already. 867 # If not, we can start caching it and store a copy converted to a dict. 868 if call is not self._current_call_cache: 869 self._current_call_cache = call 870 self._current_call_dict = dict() 871 872 for argument in call["arguments"]: 873 self._current_call_dict[argument["name"]] = argument["value"] 874 875 # Return the required argument. 876 if name in self._current_call_dict: 877 return self._current_call_dict[name] 878 879 return None
880
881 - def on_call(self, call, process):
882 """Notify signature about API call. Return value determines 883 if this signature is done or could still match. 884 @param call: logged API call. 885 @param process: process doing API call. 886 @raise NotImplementedError: this method is abstract. 887 """ 888 raise NotImplementedError
889
890 - def on_complete(self):
891 """Evented signature is notified when all API calls are done. 892 @return: Match state. 893 @raise NotImplementedError: this method is abstract. 894 """ 895 raise NotImplementedError
896
897 - def run(self):
898 """Start signature processing. 899 @param results: analysis results. 900 @raise NotImplementedError: this method is abstract. 901 """ 902 raise NotImplementedError
903
904 - def as_result(self):
905 """Properties as a dict (for results). 906 @return: result dictionary. 907 """ 908 return dict( 909 name=self.name, 910 description=self.description, 911 severity=self.severity, 912 references=self.references, 913 data=self.data, 914 alert=self.alert, 915 families=self.families 916 )
917
918 -class Report(object):
919 """Base abstract class for reporting module.""" 920 order = 1 921
922 - def __init__(self):
923 self.analysis_path = "" 924 self.reports_path = "" 925 self.task = None 926 self.options = None
927
928 - def set_path(self, analysis_path):
929 """Set analysis folder path. 930 @param analysis_path: analysis folder path. 931 """ 932 self.analysis_path = analysis_path 933 self.conf_path = os.path.join(self.analysis_path, "analysis.conf") 934 self.file_path = os.path.realpath(os.path.join(self.analysis_path, 935 "binary")) 936 self.reports_path = os.path.join(self.analysis_path, "reports") 937 self.shots_path = os.path.join(self.analysis_path, "shots") 938 self.pcap_path = os.path.join(self.analysis_path, "dump.pcap") 939 940 try: 941 create_folder(folder=self.reports_path) 942 except CuckooOperationalError as e: 943 CuckooReportError(e)
944
945 - def set_options(self, options):
946 """Set report options. 947 @param options: report options dict. 948 """ 949 self.options = options
950
951 - def set_task(self, task):
952 """Add task information. 953 @param task: task dictionary. 954 """ 955 self.task = task
956
957 - def run(self):
958 """Start report processing. 959 @raise NotImplementedError: this method is abstract. 960 """ 961 raise NotImplementedError
962