modules.machinery.qemu

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