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

Source Code for Module modules.machinery.qemu

  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 time 
  8  import logging 
  9  import subprocess 
 10  import os.path 
 11   
 12  from lib.cuckoo.common.abstracts import Machinery 
 13  from lib.cuckoo.common.exceptions import CuckooCriticalError 
 14  from lib.cuckoo.common.exceptions import CuckooMachineError 
 15   
 16  log = logging.getLogger(__name__) 
 17   
 18  # this whole semi-hardcoded commandline thing is not the best 
 19  #  but in the config files we can't do arrays etc so we'd have to parse the 
 20  #  configured commandlines somehow and then fill in some more things 
 21  #  anyways, if someone has a cleaner suggestion for this, let me know 
 22  #  -> for now, just modify this to your needs 
 23  QEMU_ARGS = { 
 24      "default": { 
 25          "cmdline": ["qemu-system-x86_64", "-display", "none"], 
 26          "params": { 
 27              "memory": "512M", 
 28              "mac": "52:54:00:12:34:56", 
 29              "kernel": "{imagepath}/vmlinuz", 
 30          }, 
 31      }, 
 32      "mipsel": { 
 33          "cmdline": [ 
 34              "qemu-system-mipsel", "-display", "none", "-M", "malta", "-m", "{memory}", 
 35              "-kernel", "{kernel}", 
 36              "-hda", "{snapshot_path}", 
 37              "-append", "root=/dev/sda1 console=tty0", 
 38              "-netdev", "tap,id=net_{vmname},ifname=tap_{vmname}", 
 39              "-device", "e1000,netdev=net_{vmname},mac={mac}",  # virtio-net-pci doesn't work here 
 40          ], 
 41          "params": { 
 42              "kernel": "{imagepath}/vmlinux-3.2.0-4-4kc-malta-mipsel", 
 43          } 
 44      }, 
 45      "mips": { 
 46          "cmdline": [ 
 47              "qemu-system-mips", "-display", "none", "-M", "malta", "-m", "{memory}", 
 48              "-kernel", "{kernel}", 
 49              "-hda", "{snapshot_path}", 
 50              "-append", "root=/dev/sda1 console=tty0", 
 51              "-netdev", "tap,id=net_{vmname},ifname=tap_{vmname}", 
 52              "-device", "e1000,netdev=net_{vmname},mac={mac}",  # virtio-net-pci doesn't work here 
 53          ], 
 54          "params": { 
 55              "kernel": "{imagepath}/vmlinux-3.2.0-4-4kc-malta-mips", 
 56          } 
 57      }, 
 58      "armwrt": { 
 59          "cmdline": [ 
 60              "qemu-system-arm", "-display", "none", "-M", "realview-eb-mpcore", "-m", "{memory}", 
 61              "-kernel", "{kernel}", 
 62              "-drive", "if=sd,cache=unsafe,file={snapshot_path}", 
 63              "-append", "console=ttyAMA0 root=/dev/mmcblk0 rootwait", 
 64              "-net", "tap,ifname=tap_{vmname}", "-net", "nic,macaddr={mac}",  # this by default needs /etc/qemu-ifup to add the tap to the bridge, slightly awkward 
 65          ], 
 66          "params": { 
 67              "kernel": "{imagepath}/openwrt-realview-vmlinux.elf", 
 68          } 
 69      }, 
 70      "arm": { 
 71          "cmdline": [ 
 72              "qemu-system-arm", "-display", "none", "-M", "versatilepb", "-m", "{memory}", 
 73              "-kernel", "{kernel}", "-initrd", "{initrd}", 
 74              "-hda", "{snapshot_path}", 
 75              "-append", "root=/dev/sda1", 
 76              "-net", "tap,ifname=tap_{vmname}", "-net", "nic,macaddr={mac}",  # this by default needs /etc/qemu-ifup to add the tap to the bridge, slightly awkward 
 77          ], 
 78          "params": { 
 79              "memory": "256M",  # 512 didn't work for some reason 
 80              "kernel": "{imagepath}/vmlinuz-3.2.0-4-versatile-arm", 
 81              "initrd": "{imagepath}/initrd-3.2.0-4-versatile-arm", 
 82          } 
 83      }, 
 84      "x64": { 
 85          "cmdline": [ 
 86              "qemu-system-x86_64", "-display", "none", "-m", "{memory}", 
 87              "-hda", "{snapshot_path}", 
 88              "-net", "tap,ifname=tap_{vmname}", "-net", "nic,macaddr={mac}",  # this by default needs /etc/qemu-ifup to add the tap to the bridge, slightly awkward 
 89          ], 
 90          "params": { 
 91              "memory": "1024M", 
 92          } 
 93      }, 
 94      "x86": { 
 95          "cmdline": [ 
 96              "qemu-system-i386", "-display", "none", "-m", "{memory}", 
 97              "-hda", "{snapshot_path}", 
 98              "-net", "tap,ifname=tap_{vmname}", "-net", "nic,macaddr={mac}",  # this by default needs /etc/qemu-ifup to add the tap to the bridge, slightly awkward 
 99          ], 
100          "params": { 
101              "memory": "1024M", 
102          } 
103      }, 
104  } 
105   
106 -class QEMU(Machinery):
107 """Virtualization layer for QEMU (non-KVM).""" 108 109 # VM states. 110 RUNNING = "running" 111 STOPPED = "stopped" 112 ERROR = "machete" 113
114 - def __init__(self):
115 super(QEMU, self).__init__() 116 self.state = {}
117
118 - def _initialize_check(self):
119 """Runs all checks when a machine manager is initialized. 120 @raise CuckooMachineError: if QEMU binary is not found. 121 """ 122 # VirtualBox specific checks. 123 if not self.options.qemu.path: 124 raise CuckooCriticalError("QEMU binary path missing, " 125 "please add it to the config file") 126 if not os.path.exists(self.options.qemu.path): 127 raise CuckooCriticalError("QEMU binary not found at " 128 "specified path \"%s\"" % 129 self.options.qemu.path) 130 131 self.qemu_dir = os.path.dirname(self.options.qemu.path) 132 self.qemu_img = os.path.join(self.qemu_dir, "qemu-img")
133
134 - def start(self, label, task):
135 """Start a virtual machine. 136 @param label: virtual machine label. 137 @param task: task object. 138 @raise CuckooMachineError: if unable to start. 139 """ 140 log.debug("Starting vm %s" % label) 141 142 vm_info = self.db.view_machine_by_label(label) 143 vm_options = getattr(self.options, vm_info.name) 144 145 snapshot_path = os.path.join(os.path.dirname(vm_options.image), vm_info.name) + ".qcow2" 146 if os.path.exists(snapshot_path): 147 os.remove(snapshot_path) 148 149 # make sure we use a new harddisk layer by creating a new qcow2 with backing file 150 try: 151 proc = subprocess.Popen([self.qemu_img, "create", "-f", "qcow2", "-b", vm_options.image, snapshot_path], 152 stdout=subprocess.PIPE, stderr=subprocess.PIPE) 153 output, err = proc.communicate() 154 if err: 155 raise OSError(err) 156 except OSError as e: 157 raise CuckooMachineError("QEMU failed starting the machine: %s" % e) 158 159 vm_arch = getattr(vm_options, "arch", "default") 160 arch_config = dict(QEMU_ARGS[vm_arch]) 161 cmdline = arch_config["cmdline"] 162 params = dict(QEMU_ARGS["default"]["params"]) 163 params.update(QEMU_ARGS[vm_arch]["params"]) 164 165 params.update({ 166 "imagepath": os.path.dirname(vm_options.image), 167 "snapshot_path": snapshot_path, 168 "vmname": vm_info.name, 169 }) 170 171 # allow some overrides from the vm specific options 172 # also do another round of parameter formatting 173 for var in ["mac", "kernel", "initrd"]: 174 val = getattr(vm_options, var, params.get(var, None)) 175 if not val: 176 continue 177 params[var] = val.format(**params) 178 179 # magic arg building 180 final_cmdline = [i.format(**params) for i in cmdline] 181 182 log.debug("Executing QEMU %r", final_cmdline) 183 184 try: 185 proc = subprocess.Popen(final_cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 186 self.state[vm_info.name] = proc 187 except OSError as e: 188 raise CuckooMachineError("QEMU failed starting the machine: %s" % e)
189
190 - def stop(self, label):
191 """Stops a virtual machine. 192 @param label: virtual machine label. 193 @raise CuckooMachineError: if unable to stop. 194 """ 195 log.debug("Stopping vm %s" % label) 196 197 vm_info = self.db.view_machine_by_label(label) 198 199 if self._status(vm_info.name) == self.STOPPED: 200 raise CuckooMachineError("Trying to stop an already stopped vm %s" % label) 201 202 proc = self.state.get(vm_info.name, None) 203 proc.kill() 204 205 stop_me = 0 206 while proc.poll() is None: 207 if stop_me < int(self.options_globals.timeouts.vm_state): 208 time.sleep(1) 209 stop_me += 1 210 else: 211 log.debug("Stopping vm %s timeouted. Killing" % label) 212 proc.terminate() 213 time.sleep(1) 214 215 # if proc.returncode != 0 and stop_me < int(self.options_globals.timeouts.vm_state): 216 # log.debug("QEMU exited with error powering off the machine") 217 218 self.state[vm_info.name] = None
219
220 - def _status(self, name):
221 """Gets current status of a vm. 222 @param name: virtual machine name. 223 @return: status string. 224 """ 225 p = self.state.get(name, None) 226 if p is not None: 227 return self.RUNNING 228 return self.STOPPED
229