Package modules :: Package machinery :: Module virtualbox
[hide private]
[frames] | no frames]

Source Code for Module modules.machinery.virtualbox

  1  # Copyright (C) 2010-2013 Claudio Guarnieri. 
  2  # Copyright (C) 2014-2016 Cuckoo Foundation. 
  3  # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org 
  4  # See the file 'docs/LICENSE' for copying permission. 
  5   
  6  import os 
  7  import re 
  8  import time 
  9  import logging 
 10  import subprocess 
 11  import os.path 
 12   
 13  from lib.cuckoo.common.abstracts import Machinery 
 14  from lib.cuckoo.common.exceptions import CuckooCriticalError 
 15  from lib.cuckoo.common.exceptions import CuckooMachineError 
 16   
 17  log = logging.getLogger(__name__) 
 18   
19 -class VirtualBox(Machinery):
20 """Virtualization layer for VirtualBox.""" 21 22 # VM states. 23 SAVED = "saved" 24 RUNNING = "running" 25 POWEROFF = "poweroff" 26 ABORTED = "aborted" 27 ERROR = "machete" 28
29 - def _initialize_check(self):
30 """Runs all checks when a machine manager is initialized. 31 @raise CuckooMachineError: if VBoxManage is not found. 32 """ 33 # VirtualBox specific checks. 34 if not self.options.virtualbox.path: 35 raise CuckooCriticalError("VirtualBox VBoxManage path missing, " 36 "please add it to the config file") 37 if not os.path.exists(self.options.virtualbox.path): 38 raise CuckooCriticalError("VirtualBox VBoxManage not found at " 39 "specified path \"%s\"" % 40 self.options.virtualbox.path) 41 42 # Base checks. 43 super(VirtualBox, self)._initialize_check()
44
45 - def start(self, label, task):
46 """Start a virtual machine. 47 @param label: virtual machine name. 48 @param task: task object. 49 @raise CuckooMachineError: if unable to start. 50 """ 51 log.debug("Starting vm %s" % label) 52 53 if self._status(label) == self.RUNNING: 54 raise CuckooMachineError("Trying to start an already " 55 "started vm %s" % label) 56 57 machine = self.db.view_machine_by_label(label) 58 virtualbox_args = [self.options.virtualbox.path, "snapshot", label] 59 if machine.snapshot: 60 log.debug("Using snapshot {0} for virtual machine " 61 "{1}".format(machine.snapshot, label)) 62 virtualbox_args.extend(["restore", machine.snapshot]) 63 else: 64 log.debug("Using current snapshot for virtual machine " 65 "{0}".format(label)) 66 virtualbox_args.extend(["restorecurrent"]) 67 68 try: 69 if subprocess.call(virtualbox_args, 70 stdout=subprocess.PIPE, 71 stderr=subprocess.PIPE, 72 close_fds=True): 73 raise CuckooMachineError("VBoxManage exited with error " 74 "restoring the machine's snapshot") 75 except OSError as e: 76 raise CuckooMachineError("VBoxManage failed restoring the " 77 "machine: %s" % e) 78 79 self._wait_status(label, self.SAVED) 80 81 try: 82 proc = subprocess.Popen([self.options.virtualbox.path, 83 "startvm", 84 label, 85 "--type", 86 self.options.virtualbox.mode], 87 stdout=subprocess.PIPE, 88 stderr=subprocess.PIPE, 89 close_fds=True) 90 output, err = proc.communicate() 91 if err: 92 raise OSError(err) 93 except OSError as e: 94 raise CuckooMachineError("VBoxManage failed starting the machine " 95 "in %s mode: %s" % 96 (self.options.virtualbox.mode.upper(), e)) 97 98 self._wait_status(label, self.RUNNING) 99 100 # Handle network dumping through the internal VirtualBox functionality. 101 if "nictrace" in machine.options: 102 self.dump_pcap(label, task)
103
104 - def dump_pcap(self, label, task):
105 """Dump the pcap for this analysis through the VirtualBox integrated 106 nictrace functionality. This is useful in scenarios where multiple 107 Virtual Machines are talking with each other in the same subnet (which 108 you normally don't see when tcpdump'ing on the gatway).""" 109 try: 110 args = [ 111 self.options.virtualbox.path, 112 "controlvm", label, 113 "nictracefile1", self.pcap_path(task.id), 114 ] 115 subprocess.check_call(args) 116 except subprocess.CalledProcessError as e: 117 log.critical("Unable to set NIC tracefile (pcap file): %s", e) 118 return 119 120 try: 121 args = [ 122 self.options.virtualbox.path, 123 "controlvm", label, 124 "nictrace1", "on", 125 ] 126 subprocess.check_call(args) 127 except subprocess.CalledProcessError as e: 128 log.critical("Unable to enable NIC tracing (pcap file): %s", e) 129 return
130
131 - def stop(self, label):
132 """Stops a virtual machine. 133 @param label: virtual machine name. 134 @raise CuckooMachineError: if unable to stop. 135 """ 136 log.debug("Stopping vm %s" % label) 137 138 if self._status(label) in [self.POWEROFF, self.ABORTED]: 139 raise CuckooMachineError("Trying to stop an already stopped " 140 "vm %s" % label) 141 142 try: 143 proc = subprocess.Popen([self.options.virtualbox.path, 144 "controlvm", label, "poweroff"], 145 stdout=subprocess.PIPE, 146 stderr=subprocess.PIPE, 147 close_fds=True) 148 # Sometimes VBoxManage stucks when stopping vm so we needed 149 # to add a timeout and kill it after that. 150 stop_me = 0 151 while proc.poll() is None: 152 if stop_me < int(self.options_globals.timeouts.vm_state): 153 time.sleep(1) 154 stop_me += 1 155 else: 156 log.debug("Stopping vm %s timeouted. Killing" % label) 157 proc.terminate() 158 159 if proc.returncode != 0 and \ 160 stop_me < int(self.options_globals.timeouts.vm_state): 161 log.debug("VBoxManage exited with error " 162 "powering off the machine") 163 except OSError as e: 164 raise CuckooMachineError("VBoxManage failed powering off the " 165 "machine: %s" % e) 166 self._wait_status(label, [self.POWEROFF, self.ABORTED, self.SAVED])
167
168 - def _list(self):
169 """Lists virtual machines installed. 170 @return: virtual machine names list. 171 """ 172 try: 173 proc = subprocess.Popen([self.options.virtualbox.path, 174 "list", "vms"], 175 stdout=subprocess.PIPE, 176 stderr=subprocess.PIPE, 177 close_fds=True) 178 output, _ = proc.communicate() 179 except OSError as e: 180 raise CuckooMachineError("VBoxManage error listing " 181 "installed machines: %s" % e) 182 183 machines = [] 184 for line in output.split("\n"): 185 try: 186 label = line.split('"')[1] 187 if label == "<inaccessible>": 188 log.warning("Found an inaccessible virtual machine, " 189 "please check its state.") 190 else: 191 machines.append(label) 192 except IndexError: 193 continue 194 195 return machines
196
197 - def _status(self, label):
198 """Gets current status of a vm. 199 @param label: virtual machine name. 200 @return: status string. 201 """ 202 log.debug("Getting status for %s" % label) 203 status = None 204 try: 205 proc = subprocess.Popen([self.options.virtualbox.path, 206 "showvminfo", 207 label, 208 "--machinereadable"], 209 stdout=subprocess.PIPE, 210 stderr=subprocess.PIPE, 211 close_fds=True) 212 output, err = proc.communicate() 213 214 if proc.returncode != 0: 215 # It's quite common for virtualbox crap utility to exit with: 216 # VBoxManage: error: Details: code E_ACCESSDENIED (0x80070005) 217 # So we just log to debug this. 218 log.debug("VBoxManage returns error checking status for " 219 "machine %s: %s", label, err) 220 status = self.ERROR 221 except OSError as e: 222 log.warning("VBoxManage failed to check status for machine %s: %s", 223 label, e) 224 status = self.ERROR 225 if not status: 226 for line in output.split("\n"): 227 state = re.match(r'VMState="(\w+)"', line, re.M | re.I) 228 if state: 229 status = state.group(1) 230 log.debug("Machine %s status %s" % (label, status)) 231 status = status.lower() 232 # Report back status. 233 if status: 234 self.set_status(label, status) 235 return status 236 else: 237 raise CuckooMachineError("Unable to get status for %s" % label)
238
239 - def dump_memory(self, label, path):
240 """Takes a memory dump. 241 @param path: path to where to store the memory dump. 242 """ 243 244 try: 245 proc = subprocess.Popen([self.options.virtualbox.path, "-v"], 246 stdout=subprocess.PIPE, 247 stderr=subprocess.PIPE, 248 close_fds=True) 249 output, err = proc.communicate() 250 251 if proc.returncode != 0: 252 # It's quite common for virtualbox crap utility to exit with: 253 # VBoxManage: error: Details: code E_ACCESSDENIED (0x80070005) 254 # So we just log to debug this. 255 log.debug("VBoxManage returns error checking status for " 256 "machine %s: %s", label, err) 257 except OSError as e: 258 raise CuckooMachineError("VBoxManage failed return it's version: %s" % (e)) 259 260 if output[:1] == str(5): 261 # VirtualBox version 5.x 262 dumpcmd = "dumpvmcore" 263 else: 264 # VirtualBox version 4.x 265 dumpcmd = "dumpguestcore" 266 267 try: 268 subprocess.call([self.options.virtualbox.path, "debugvm", 269 label, dumpcmd, "--filename", path], 270 stdout=subprocess.PIPE, 271 stderr=subprocess.PIPE, 272 close_fds=True) 273 log.info("Successfully generated memory dump for virtual machine " 274 "with label %s to path %s", label, path) 275 except OSError as e: 276 raise CuckooMachineError("VBoxManage failed to take a memory " 277 "dump of the machine with label %s: %s" % 278 (label, e))
279