1
2
3
4
5
6 import os
7 import re
8 import time
9 import logging
10 import subprocess
11 import os.path
12
13 from lib.cuckoo.common.abstracts import Machinery
14 from lib.cuckoo.common.exceptions import CuckooCriticalError
15 from lib.cuckoo.common.exceptions import CuckooMachineError
16
17 log = logging.getLogger(__name__)
18
20 """Virtualization layer for VirtualBox."""
21
22
23 SAVED = "saved"
24 RUNNING = "running"
25 POWEROFF = "poweroff"
26 ABORTED = "aborted"
27 ERROR = "machete"
28
44
45 - def start(self, label, task):
46 """Start a virtual machine.
47 @param label: virtual machine name.
48 @param task: task object.
49 @raise CuckooMachineError: if unable to start.
50 """
51 log.debug("Starting vm %s" % label)
52
53 if self._status(label) == self.RUNNING:
54 raise CuckooMachineError("Trying to start an already "
55 "started vm %s" % label)
56
57 machine = self.db.view_machine_by_label(label)
58 virtualbox_args = [self.options.virtualbox.path, "snapshot", label]
59 if machine.snapshot:
60 log.debug("Using snapshot {0} for virtual machine "
61 "{1}".format(machine.snapshot, label))
62 virtualbox_args.extend(["restore", machine.snapshot])
63 else:
64 log.debug("Using current snapshot for virtual machine "
65 "{0}".format(label))
66 virtualbox_args.extend(["restorecurrent"])
67
68 try:
69 if subprocess.call(virtualbox_args,
70 stdout=subprocess.PIPE,
71 stderr=subprocess.PIPE,
72 close_fds=True):
73 raise CuckooMachineError("VBoxManage exited with error "
74 "restoring the machine's snapshot")
75 except OSError as e:
76 raise CuckooMachineError("VBoxManage failed restoring the "
77 "machine: %s" % e)
78
79 self._wait_status(label, self.SAVED)
80
81 try:
82 proc = subprocess.Popen([self.options.virtualbox.path,
83 "startvm",
84 label,
85 "--type",
86 self.options.virtualbox.mode],
87 stdout=subprocess.PIPE,
88 stderr=subprocess.PIPE,
89 close_fds=True)
90 output, err = proc.communicate()
91 if err:
92 raise OSError(err)
93 except OSError as e:
94 raise CuckooMachineError("VBoxManage failed starting the machine "
95 "in %s mode: %s" %
96 (self.options.virtualbox.mode.upper(), e))
97
98 self._wait_status(label, self.RUNNING)
99
100
101 if "nictrace" in machine.options:
102 self.dump_pcap(label, task)
103
105 """Dump the pcap for this analysis through the VirtualBox integrated
106 nictrace functionality. This is useful in scenarios where multiple
107 Virtual Machines are talking with each other in the same subnet (which
108 you normally don't see when tcpdump'ing on the gatway)."""
109 try:
110 args = [
111 self.options.virtualbox.path,
112 "controlvm", label,
113 "nictracefile1", self.pcap_path(task.id),
114 ]
115 subprocess.check_call(args)
116 except subprocess.CalledProcessError as e:
117 log.critical("Unable to set NIC tracefile (pcap file): %s", e)
118 return
119
120 try:
121 args = [
122 self.options.virtualbox.path,
123 "controlvm", label,
124 "nictrace1", "on",
125 ]
126 subprocess.check_call(args)
127 except subprocess.CalledProcessError as e:
128 log.critical("Unable to enable NIC tracing (pcap file): %s", e)
129 return
130
131 - def stop(self, label):
132 """Stops a virtual machine.
133 @param label: virtual machine name.
134 @raise CuckooMachineError: if unable to stop.
135 """
136 log.debug("Stopping vm %s" % label)
137
138 if self._status(label) in [self.POWEROFF, self.ABORTED]:
139 raise CuckooMachineError("Trying to stop an already stopped "
140 "vm %s" % label)
141
142 try:
143 proc = subprocess.Popen([self.options.virtualbox.path,
144 "controlvm", label, "poweroff"],
145 stdout=subprocess.PIPE,
146 stderr=subprocess.PIPE,
147 close_fds=True)
148
149
150 stop_me = 0
151 while proc.poll() is None:
152 if stop_me < int(self.options_globals.timeouts.vm_state):
153 time.sleep(1)
154 stop_me += 1
155 else:
156 log.debug("Stopping vm %s timeouted. Killing" % label)
157 proc.terminate()
158
159 if proc.returncode != 0 and \
160 stop_me < int(self.options_globals.timeouts.vm_state):
161 log.debug("VBoxManage exited with error "
162 "powering off the machine")
163 except OSError as e:
164 raise CuckooMachineError("VBoxManage failed powering off the "
165 "machine: %s" % e)
166 self._wait_status(label, [self.POWEROFF, self.ABORTED, self.SAVED])
167
169 """Lists virtual machines installed.
170 @return: virtual machine names list.
171 """
172 try:
173 proc = subprocess.Popen([self.options.virtualbox.path,
174 "list", "vms"],
175 stdout=subprocess.PIPE,
176 stderr=subprocess.PIPE,
177 close_fds=True)
178 output, _ = proc.communicate()
179 except OSError as e:
180 raise CuckooMachineError("VBoxManage error listing "
181 "installed machines: %s" % e)
182
183 machines = []
184 for line in output.split("\n"):
185 try:
186 label = line.split('"')[1]
187 if label == "<inaccessible>":
188 log.warning("Found an inaccessible virtual machine, "
189 "please check its state.")
190 else:
191 machines.append(label)
192 except IndexError:
193 continue
194
195 return machines
196
198 """Gets current status of a vm.
199 @param label: virtual machine name.
200 @return: status string.
201 """
202 log.debug("Getting status for %s" % label)
203 status = None
204 try:
205 proc = subprocess.Popen([self.options.virtualbox.path,
206 "showvminfo",
207 label,
208 "--machinereadable"],
209 stdout=subprocess.PIPE,
210 stderr=subprocess.PIPE,
211 close_fds=True)
212 output, err = proc.communicate()
213
214 if proc.returncode != 0:
215
216
217
218 log.debug("VBoxManage returns error checking status for "
219 "machine %s: %s", label, err)
220 status = self.ERROR
221 except OSError as e:
222 log.warning("VBoxManage failed to check status for machine %s: %s",
223 label, e)
224 status = self.ERROR
225 if not status:
226 for line in output.split("\n"):
227 state = re.match(r'VMState="(\w+)"', line, re.M | re.I)
228 if state:
229 status = state.group(1)
230 log.debug("Machine %s status %s" % (label, status))
231 status = status.lower()
232
233 if status:
234 self.set_status(label, status)
235 return status
236 else:
237 raise CuckooMachineError("Unable to get status for %s" % label)
238
240 """Takes a memory dump.
241 @param path: path to where to store the memory dump.
242 """
243
244 try:
245 proc = subprocess.Popen([self.options.virtualbox.path, "-v"],
246 stdout=subprocess.PIPE,
247 stderr=subprocess.PIPE,
248 close_fds=True)
249 output, err = proc.communicate()
250
251 if proc.returncode != 0:
252
253
254
255 log.debug("VBoxManage returns error checking status for "
256 "machine %s: %s", label, err)
257 except OSError as e:
258 raise CuckooMachineError("VBoxManage failed return it's version: %s" % (e))
259
260 if output[:1] == str(5):
261
262 dumpcmd = "dumpvmcore"
263 else:
264
265 dumpcmd = "dumpguestcore"
266
267 try:
268 subprocess.call([self.options.virtualbox.path, "debugvm",
269 label, dumpcmd, "--filename", path],
270 stdout=subprocess.PIPE,
271 stderr=subprocess.PIPE,
272 close_fds=True)
273 log.info("Successfully generated memory dump for virtual machine "
274 "with label %s to path %s", label, path)
275 except OSError as e:
276 raise CuckooMachineError("VBoxManage failed to take a memory "
277 "dump of the machine with label %s: %s" %
278 (label, e))
279