Package lib :: Package cuckoo :: Package core :: Module guest
[hide private]
[frames] | no frames]

Source Code for Module lib.cuckoo.core.guest

  1  # Copyright (C) 2010-2014 Cuckoo Foundation. 
  2  # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org 
  3  # See the file 'docs/LICENSE' for copying permission. 
  4   
  5  import os 
  6  import time 
  7  import socket 
  8  import logging 
  9  import xmlrpclib 
 10  from threading import Timer, Event 
 11  from StringIO import StringIO 
 12  from zipfile import ZipFile, ZIP_STORED 
 13   
 14  from lib.cuckoo.common.config import Config 
 15  from lib.cuckoo.common.constants import CUCKOO_GUEST_PORT, CUCKOO_GUEST_INIT 
 16  from lib.cuckoo.common.constants import CUCKOO_GUEST_COMPLETED 
 17  from lib.cuckoo.common.constants import CUCKOO_GUEST_FAILED 
 18  from lib.cuckoo.common.exceptions import CuckooGuestError 
 19  from lib.cuckoo.common.utils import TimeoutServer, sanitize_filename 
 20   
 21  log = logging.getLogger(__name__) 
 22   
23 -class GuestManager:
24 """Guest Mananager. 25 26 This class handles the communications with the agents running in the 27 machines. 28 """ 29
30 - def __init__(self, vm_id, ip, platform="windows"):
31 """@param ip: guest's IP address. 32 @param platform: guest's operating system type. 33 """ 34 self.id = vm_id 35 self.ip = ip 36 self.platform = platform 37 38 self.cfg = Config() 39 self.timeout = self.cfg.timeouts.critical 40 41 url = "http://{0}:{1}".format(ip, CUCKOO_GUEST_PORT) 42 self.server = TimeoutServer(url, allow_none=True, 43 timeout=self.timeout)
44
45 - def wait(self, status):
46 """Waiting for status. 47 @param status: status. 48 @return: always True. 49 """ 50 log.debug("%s: waiting for status 0x%.04x", self.id, status) 51 52 # Create an event that will invoke a function to stop the loop when 53 # the critical timeout is h it. 54 abort = Event() 55 abort.clear() 56 57 def die(): 58 abort.set()
59 60 # Initialize the timer. 61 timer = Timer(self.timeout, die) 62 timer.start() 63 self.server._set_timeout(self.timeout) 64 65 while True: 66 # Check if the timer was hit and the abort event was set. 67 if abort.is_set(): 68 raise CuckooGuestError("{0}: the guest initialization hit the " 69 "critical timeout, analysis " 70 "aborted".format(self.id)) 71 72 try: 73 # If the server returns the given status, break the loop 74 # and return. 75 if self.server.get_status() == status: 76 log.debug("%s: status ready", self.id) 77 break 78 except: 79 pass 80 81 log.debug("%s: not ready yet", self.id) 82 time.sleep(1) 83 84 self.server._set_timeout(None) 85 return True
86
87 - def upload_analyzer(self):
88 """Upload analyzer to guest. 89 @return: operation status. 90 """ 91 zip_data = StringIO() 92 zip_file = ZipFile(zip_data, "w", ZIP_STORED) 93 94 # Select the proper analyzer's folder according to the operating 95 # system associated with the current machine. 96 root = os.path.join("analyzer", self.platform) 97 root_len = len(os.path.abspath(root)) 98 99 if not os.path.exists(root): 100 log.error("No valid analyzer found at path: %s", root) 101 return False 102 103 # Walk through everything inside the analyzer's folder and write 104 # them to the zip archive. 105 for root, dirs, files in os.walk(root): 106 archive_root = os.path.abspath(root)[root_len:] 107 for name in files: 108 path = os.path.join(root, name) 109 archive_name = os.path.join(archive_root, name) 110 zip_file.write(path, archive_name) 111 112 zip_file.close() 113 data = xmlrpclib.Binary(zip_data.getvalue()) 114 zip_data.close() 115 116 log.debug("Uploading analyzer to guest (id=%s, ip=%s)", 117 self.id, self.ip) 118 119 # Send the zip containing the analyzer to the agent running inside 120 # the guest. 121 try: 122 self.server.add_analyzer(data) 123 except socket.timeout: 124 raise CuckooGuestError("{0}: guest communication timeout: unable " 125 "to upload agent, check networking or try " 126 "to increase timeout".format(self.id))
127
128 - def start_analysis(self, options):
129 """Start analysis. 130 @param options: options. 131 @return: operation status. 132 """ 133 log.info("Starting analysis on guest (id=%s, ip=%s)", self.id, self.ip) 134 135 # TODO: deal with unicode URLs. 136 if options["category"] == "file": 137 options["file_name"] = sanitize_filename(options["file_name"]) 138 139 try: 140 # Wait for the agent to respond. This is done to check the 141 # availability of the agent and verify that it's ready to receive 142 # data. 143 self.wait(CUCKOO_GUEST_INIT) 144 # Invoke the upload of the analyzer to the guest. 145 self.upload_analyzer() 146 # Give the analysis options to the guest, so it can generate the 147 # analysis.conf inside the guest. 148 try: 149 self.server.add_config(options) 150 except: 151 raise CuckooGuestError("{0}: unable to upload config to " 152 "analysis machine".format(self.id)) 153 154 # If the target of the analysis is a file, upload it to the guest. 155 if options["category"] == "file": 156 try: 157 file_data = open(options["target"], "rb").read() 158 except (IOError, OSError) as e: 159 raise CuckooGuestError("Unable to read {0}, error: " 160 "{1}".format(options["target"], e)) 161 162 data = xmlrpclib.Binary(file_data) 163 164 try: 165 self.server.add_malware(data, options["file_name"]) 166 except Exception as e: 167 raise CuckooGuestError("{0}: unable to upload malware to " 168 "analysis machine: {1}".format(self.id, e)) 169 170 # Launch the analyzer. 171 pid = self.server.execute() 172 log.debug("%s: analyzer started with PID %d", self.id, pid) 173 # If something goes wrong when establishing the connection, raise an 174 # exception and abort the analysis. 175 except (socket.timeout, socket.error): 176 raise CuckooGuestError("{0}: guest communication timeout, check " 177 "networking or try to increase " 178 "timeout".format(self.id))
179
180 - def wait_for_completion(self):
181 """Wait for analysis completion. 182 @return: operation status. 183 """ 184 log.debug("%s: waiting for completion", self.id) 185 186 # Same procedure as in self.wait(). Just look at the comments there. 187 abort = Event() 188 abort.clear() 189 190 def die(): 191 abort.set()
192 193 timer = Timer(self.timeout, die) 194 timer.start() 195 self.server._set_timeout(self.timeout) 196 197 while True: 198 time.sleep(1) 199 200 # If the analysis hits the critical timeout, just return straight 201 # straight away and try to recover the analysis results from the 202 # guest. 203 if abort.is_set(): 204 raise CuckooGuestError("The analysis hit the critical timeout," 205 " terminating") 206 207 try: 208 status = self.server.get_status() 209 except Exception as e: 210 log.debug("%s: error retrieving status: %s", self.id, e) 211 continue 212 213 # React according to the returned status. 214 if status == CUCKOO_GUEST_COMPLETED: 215 log.info("%s: analysis completed successfully", self.id) 216 break 217 elif status == CUCKOO_GUEST_FAILED: 218 error = self.server.get_error() 219 if not error: 220 error = "unknown error" 221 222 raise CuckooGuestError("Analysis failed: {0}".format(error)) 223 else: 224 log.debug("%s: analysis not completed yet (status=%s)", 225 self.id, status) 226 227 self.server._set_timeout(None) 228