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