modules.machinery.avd

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

# Source Code for Module modules.machinery.avd

      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  # Originally contributed by Check Point Software Technologies, Ltd.
      6
      7  import logging
      8  import os
      9  import subprocess
     10  import time
     11  import shutil
     12  import shlex
     13
     14  from lib.cuckoo.common.abstracts import Machinery
     15  from lib.cuckoo.common.exceptions import CuckooCriticalError
     16  from lib.cuckoo.core.resultserver import ResultServer
     17
     18  log = logging.getLogger(__name__)
     19
     20 -class Avd(Machinery):
     21      """Virtualization layer for Android Emulator."""
     22
     23 -    def _initialize_check(self):
     24          """Runs all checks when a machine manager is initialized.
     25          @raise CuckooMachineError: if the android emulator is not found.
     26          """
     27          self.emulator_processes = {}
     28
     29          if not self.options.avd.emulator_path:
     30              raise CuckooCriticalError("emulator path missing, "
     31                                        "please add it to the config file")
     32
     33          if not os.path.exists(self.options.avd.emulator_path):
     34              raise CuckooCriticalError("emulator not found at "
     35                                        "specified path \"%s\"" %
     36                                        self.options.avd.emulator_path)
     37
     38          if not self.options.avd.adb_path:
     39              raise CuckooCriticalError("adb path missing, "
     40                                        "please add it to the config file")
     41
     42          if not os.path.exists(self.options.avd.adb_path):
     43              raise CuckooCriticalError("adb not found at "
     44                                        "specified path \"%s\"" %
     45                                        self.options.avd.adb_path)
     46
     47          if not self.options.avd.avd_path:
     48              raise CuckooCriticalError("avd path missing, "
     49                                        "please add it to the config file")
     50
     51          if not os.path.exists(self.options.avd.avd_path):
     52              raise CuckooCriticalError("avd not found at "
     53                                        "specified path \"%s\"" %
     54                                        self.options.avd.avd_path)
     55
     56          if not self.options.avd.reference_machine:
     57              raise CuckooCriticalError("reference machine path missing, "
     58                                        "please add it to the config file")
     59
     60          machine_path = os.path.join(self.options.avd.avd_path,
     61                                      self.options.avd.reference_machine)
     62          if not os.path.exists("%s.avd" % machine_path) or \
     63                  not os.path.exists("%s.ini" % machine_path):
     64              raise CuckooCriticalError("reference machine not found at "
     65                                        "specified path \"%s\"" % machine_path)
     66
     67 -    def start(self, label, task):
     68          """Start a virtual machine.
     69          @param label: virtual machine name.
     70          @param task: task object.
     71          @raise CuckooMachineError: if unable to start.
     72          """
     73          log.debug("Starting vm %s" % label)
     74
     75          self.duplicate_reference_machine(label)
     76          self.start_emulator(label, task)
     77          self.port_forward(label)
     78          self.start_agent(label)
     79
     80 -    def stop(self, label):
     81          """Stops a virtual machine.
     82          @param label: virtual machine name.
     83          @raise CuckooMachineError: if unable to stop.
     84          """
     85          log.debug("Stopping vm %s" % label)
     86          self.stop_emulator(label)
     87
     88 -    def _list(self):
     89          """Lists virtual machines installed.
     90          @return: virtual machine names list.
     91          """
     92          return self.options.avd.machines
     93
     94 -    def _status(self, label):
     95          """Gets current status of a vm.
     96          @param label: virtual machine name.
     97          @return: status string.
     98          """
     99          log.debug("Getting status for %s" % label)
    100
    101 -    def duplicate_reference_machine(self, label):
    102          """Creates a new emulator based on a reference one."""
    103          reference_machine = self.options.avd.reference_machine
    104          log.debug("Duplicate Reference Machine '{0}'.".format(reference_machine))
    105
    106          # Clean/delete if new emulator already exists.
    107          self.delete_old_emulator(label)
    108
    109          avd_config_file = os.path.join(self.options.avd.avd_path, reference_machine+".ini")
    110          new_config_file = os.path.join(self.options.avd.avd_path, label+".ini")
    111          reference_avd_path = os.path.join(self.options.avd.avd_path, reference_machine+".avd/")
    112          new_avd_path = os.path.join(self.options.avd.avd_path, label+".avd/")
    113          hw_qemu_config_file = os.path.join(new_avd_path, "hardware-qemu.ini")
    114
    115          # First we copy the template.
    116          log.debug("Copy AVD reference config file '{0}' in '{1}'...".format(avd_config_file, new_config_file))
    117          shutil.copyfile(avd_config_file, new_config_file)
    118
    119          # Copy the internal files of the reference avd.
    120          log.debug("Duplicate the AVD internal content from '{0}' in '{1}'...".format(reference_avd_path, new_avd_path))
    121          cmd = "cp -R {0} {1}".format(reference_avd_path, new_avd_path)
    122          OSCommand.executeCommand(cmd)
    123
    124          # Than adapt the content of the copied files.
    125          self.replace_content_in_file(new_config_file, reference_machine, label)
    126          self.replace_content_in_file(hw_qemu_config_file, reference_machine, label)
    127
    128          # self.state = AVDEmulator.STATE_PREPARED
    129          # todo:will see
    130
    131 -    def delete_old_emulator(self, label):
    132          """Deletes any trace of an emulator that would have the same name as
    133          the one of the current emulator."""
    134          old_emulator_config_file = os.path.join(self.options.avd.avd_path,
    135                                                  "%s.ini" % label)
    136
    137          if os.path.exists(old_emulator_config_file):
    138              log.debug("Deleting old emulator config file '{0}'".format(old_emulator_config_file))
    139              os.remove(old_emulator_config_file)
    140
    141          old_emulator_path = os.path.join(self.options.avd.avd_path, label+".avd/")
    142          if os.path.isdir(old_emulator_path):
    143              log.debug("Deleting old emulator FS '{0}'".format(old_emulator_path))
    144              shutil.rmtree(old_emulator_path)
    145
    146 -    def replace_content_in_file(self, fileName, contentToReplace, replacementContent):
    147          """Replaces the specified motif by a specified value in the specified
    148          file.
    149          """
    150
    151          log.debug("Replacing '{0}' with '{1}' in '{2}'".format(contentToReplace, replacementContent, fileName))
    152          newLines = []
    153          with open(fileName, 'r') as fd:
    154              lines = fd.readlines()
    155              for line in lines:
    156                  newLines.append(line.replace(contentToReplace, replacementContent))
    157
    158          with open(fileName, 'w') as fd:
    159              fd.writelines(newLines)
    160
    161 -    def start_emulator(self, label, task):
    162          """Starts the emulator."""
    163          emulator_port = self.options.get(label)["emulator_port"]
    164
    165          cmd = [
    166              self.options.avd.emulator_path,
    167              "@%s" % label,
    168              "-no-snapshot-save",
    169              "-netspeed",
    170              "full",
    171              "-netdelay",
    172              "none",
    173              "-port",
    174              "%s" % emulator_port,
    175              "-tcpdump",
    176              self.pcap_path(task.id),
    177          ]
    178
    179          # In headless mode we remove the skin, audio, and window support.
    180          if self.options.avd.mode == "headless":
    181              cmd += ["-no-skin", "-no-audio", "-no-window"]
    182
    183          # If a proxy address has been provided for this analysis, then we have
    184          # to pass the proxy address along to the emulator command. The
    185          # mitmproxy instance is not located at the resultserver's IP address
    186          # though, so we manually replace the IP address by localhost.
    187          if "proxy" in task.options:
    188              _, port = task.options["proxy"].split(":")
    189              cmd += ["-http-proxy", "http://127.0.0.1:%s" % port]
    190
    191          self.emulator_processes[label] = OSCommand.executeAsyncCommand(cmd)
    192          time.sleep(10)
    193          # if not self.__checkADBRecognizeEmu(label):
    194          self.restart_adb_server()
    195          # Waits for device to be ready.
    196          self.wait_for_device_ready(label)
    197
    198 -    def stop_emulator(self, label):
    199          """Stop the emulator."""
    200          emulator_port = str(self.options.get(label)["emulator_port"])
    201          log.info("Stopping AVD listening on port {0}".format(emulator_port))
    202
    203          # Kill process.
    204          cmd = [
    205              self.options.avd.adb_path,
    206              "-s", "emulator-%s" % emulator_port,
    207              "emu", "kill",
    208          ]
    209          OSCommand.executeCommand(cmd)
    210
    211          time.sleep(1)
    212          if label in self.emulator_processes:
    213              try:
    214                  self.emulator_processes[label].kill()
    215              except Exception as e:
    216                  log.warning(e)
    217
    218              del self.emulator_processes[label]
    219
    220 -    def wait_for_device_ready(self, label):
    221          """Analyzes the emulator and returns when it's ready."""
    222
    223          emulator_port = str(self.options.get(label)["emulator_port"])
    224          adb = self.options.avd.adb_path
    225
    226          log.debug("Waiting for device emulator-"+emulator_port+" to be ready.")
    227          cmd = [
    228              adb,
    229              "-s", "emulator-%s" % emulator_port,
    230              "wait-for-device",
    231          ]
    232          OSCommand.executeCommand(cmd)
    233
    234          log.debug("Waiting for the emulator to be ready")
    235          log.debug(" - (dev.bootcomplete)")
    236          ready = False
    237          while not ready:
    238              cmd = [
    239                  adb,
    240                  "-s", "emulator-%s" % emulator_port,
    241                  "shell", "getprop", "dev.bootcomplete",
    242              ]
    243              result = OSCommand.executeCommand(cmd)
    244              if result is not None and result.strip() == "1":
    245                  ready = True
    246              else:
    247                  time.sleep(1)
    248
    249          log.debug("- (sys_bootcomplete)")
    250          ready = False
    251          while not ready:
    252              cmd = [
    253                  adb,
    254                  "-s", "emulator-%s" % emulator_port,
    255                  "shell", "getprop", "sys.boot_completed",
    256              ]
    257              result = OSCommand.executeCommand(cmd)
    258              if result is not None and result.strip() == "1":
    259                  ready = True
    260              else:
    261                  time.sleep(1)
    262
    263          log.debug(" - (init.svc.bootanim)")
    264          ready = False
    265          while not ready:
    266              cmd = [
    267                  adb,
    268                  "-s", "emulator-%s" % emulator_port,
    269                  "shell", "getprop", "init.svc.bootanim",
    270              ]
    271              result = OSCommand.executeCommand(cmd)
    272              if result is not None and result.strip() == "stopped":
    273                  ready = True
    274              else:
    275                  time.sleep(1)
    276
    277          time.sleep(5)
    278          log.debug("Emulator emulator-"+emulator_port+" is ready !")
    279
    280 -    def port_forward(self, label):
    281          cmd = [
    282              self.options.avd.adb_path,
    283              "-s", "emulator-%s" % self.options.get(label)["emulator_port"],
    284              "forward", "tcp:8000", "tcp:8000",
    285          ]
    286          OSCommand.executeAsyncCommand(cmd)
    287
    288 -    def start_agent(self, label):
    289          cmd = [
    290              self.options.avd.adb_path,
    291              "-s", "emulator-%s" % self.options.get(label)["emulator_port"],
    292              "shell", "/data/local/agent.sh",
    293          ]
    294          OSCommand.executeAsyncCommand(cmd)
    295          # Sleep 10 seconds to allow the agent to startup properly
    296          time.sleep(10)
    297
    298 -    def check_adb_recognize_emulator(self, label):
    299          """Checks that ADB recognizes the emulator. Returns True if device is
    300          recognized by ADB, False otherwise.
    301          """
    302          log.debug("Checking if ADB recognizes emulator...")
    303
    304          cmd = [self.options.avd.adb_path, "devices"]
    305          output = OSCommand.executeCommand(cmd)
    306
    307          emu = "emulator-%s" % self.options.get(label)["emulator_port"]
    308          if emu in output:
    309              log.debug("Emulator has been found!")
    310              return True
    311
    312          log.debug("Emulator has not been found.")
    313          return False
    314
    315 -    def restart_adb_server(self):
    316          """Restarts ADB server. This function is not used because we have to
    317          verify we don't have multiple devices.
    318          """
    319          log.debug("Restarting ADB server...")
    320
    321          cmd = [self.options.avd.adb_path, "kill-server"]
    322          OSCommand.executeCommand(cmd)
    323          log.debug("ADB server has been killed.")
    324
    325          cmd = [self.options.avd.adb_path, "start-server"]
    326          OSCommand.executeCommand(cmd)
    327          log.debug("ADB server has been restarted.")
    328
    329 -    def get_task_id(self, label):
    330          analysistasks = ResultServer().analysistasks
    331          for task_ip in analysistasks:
    332              if analysistasks[task_ip][1].label is label:
    333                  return analysistasks[task_ip][0].id
    334
    335          return None
    336
    337 -class OSCommand(object):
    338      """Tool class that provides common methods to execute commands on the OS."""
    339
    340      @staticmethod
    341 -    def executeAsyncCommand(commandAndArgs):
    342          return subprocess.Popen(commandAndArgs, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    343
    344      @staticmethod
    345 -    def executeCommand(commandAndArgs):
    346          if isinstance(commandAndArgs, str):
    347              commandAndArgs = shlex.split(commandAndArgs)
    348
    349          try:
    350              return subprocess.check_output(commandAndArgs, stderr=subprocess.STDOUT)
    351          except Exception:
    352              return None
    353