1
2
3
4
5
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
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):
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
89 """Lists virtual machines installed.
90 @return: virtual machine names list.
91 """
92 return self.options.avd.machines
93
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
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
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
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
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
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
129
130
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
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
180 if self.options.avd.mode == "headless":
181 cmd += ["-no-skin", "-no-audio", "-no-window"]
182
183
184
185
186
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
194 self.restart_adb_server()
195
196 self.wait_for_device_ready(label)
197
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
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
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
287
297
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
328
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
338 """Tool class that provides common methods to execute commands on the OS."""
339
340 @staticmethod
342 return subprocess.Popen(commandAndArgs, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
343
344 @staticmethod
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