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

Source Code for Module lib.cuckoo.core.startup

  1  # Copyright (C) 2010-2015 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 shutil 
  7  import sys 
  8  import copy 
  9  import json 
 10  import urllib 
 11  import urllib2 
 12  import logging 
 13  import logging.handlers 
 14   
 15  import modules.auxiliary 
 16  import modules.processing 
 17  import modules.signatures 
 18  import modules.reporting 
 19   
 20  from lib.cuckoo.common.colors import red, green, yellow, cyan 
 21  from lib.cuckoo.common.config import Config 
 22  from lib.cuckoo.common.constants import CUCKOO_ROOT, CUCKOO_VERSION 
 23  from lib.cuckoo.common.exceptions import CuckooStartupError 
 24  from lib.cuckoo.common.exceptions import CuckooOperationalError 
 25  from lib.cuckoo.common.utils import create_folders 
 26  from lib.cuckoo.core.database import Database, TASK_RUNNING, TASK_FAILED_ANALYSIS 
 27  from lib.cuckoo.core.plugins import import_plugin, import_package, list_plugins 
 28   
 29  log = logging.getLogger() 
 30   
31 -def check_python_version():
32 """Checks if Python version is supported by Cuckoo. 33 @raise CuckooStartupError: if version is not supported. 34 """ 35 if sys.version_info[:2] != (2, 7): 36 raise CuckooStartupError("You are running an incompatible version " 37 "of Python, please use 2.7")
38 39
40 -def check_working_directory():
41 """Checks if working directories are ready. 42 @raise CuckooStartupError: if directories are not properly configured. 43 """ 44 if not os.path.exists(CUCKOO_ROOT): 45 raise CuckooStartupError("You specified a non-existing root " 46 "directory: {0}".format(CUCKOO_ROOT)) 47 48 cwd = os.path.join(os.getcwd(), "cuckoo.py") 49 if not os.path.exists(cwd): 50 raise CuckooStartupError("You are not running Cuckoo from it's " 51 "root directory")
52 53
54 -def check_configs():
55 """Checks if config files exist. 56 @raise CuckooStartupError: if config files do not exist. 57 """ 58 configs = [os.path.join(CUCKOO_ROOT, "conf", "cuckoo.conf"), 59 os.path.join(CUCKOO_ROOT, "conf", "reporting.conf"), 60 os.path.join(CUCKOO_ROOT, "conf", "auxiliary.conf")] 61 62 for config in configs: 63 if not os.path.exists(config): 64 raise CuckooStartupError("Config file does not exist at " 65 "path: {0}".format(config)) 66 67 return True
68
69 -def create_structure():
70 """Creates Cuckoo directories.""" 71 folders = [ 72 "log", 73 "storage", 74 os.path.join("storage", "analyses"), 75 os.path.join("storage", "binaries") 76 ] 77 78 try: 79 create_folders(root=CUCKOO_ROOT, folders=folders) 80 except CuckooOperationalError as e: 81 raise CuckooStartupError(e)
82
83 -def check_version():
84 """Checks version of Cuckoo.""" 85 cfg = Config() 86 87 if not cfg.cuckoo.version_check: 88 return 89 90 print(" Checking for updates...") 91 92 url = "http://api.cuckoosandbox.org/checkversion.php" 93 data = urllib.urlencode({"version": CUCKOO_VERSION}) 94 95 try: 96 request = urllib2.Request(url, data) 97 response = urllib2.urlopen(request) 98 except (urllib2.URLError, urllib2.HTTPError): 99 print(red(" Failed! ") + "Unable to establish connection.\n") 100 return 101 102 try: 103 response_data = json.loads(response.read()) 104 except ValueError: 105 print(red(" Failed! ") + "Invalid response.\n") 106 return 107 108 if not response_data["error"]: 109 if response_data["response"] == "NEW_VERSION": 110 msg = "Cuckoo Sandbox version {0} is available " \ 111 "now.\n".format(response_data["current"]) 112 print(red(" Outdated! ") + msg) 113 else: 114 print(green(" Good! ") + "You have the latest version " 115 "available.\n")
116 117
118 -class DatabaseHandler(logging.Handler):
119 """Logging to database handler. 120 Used to log errors related to tasks in database. 121 """ 122
123 - def emit(self, record):
124 if hasattr(record, "task_id"): 125 db = Database() 126 db.add_error(record.msg, int(record.task_id))
127
128 -class ConsoleHandler(logging.StreamHandler):
129 """Logging to console handler.""" 130
131 - def emit(self, record):
132 colored = copy.copy(record) 133 134 if record.levelname == "WARNING": 135 colored.msg = yellow(record.msg) 136 elif record.levelname == "ERROR" or record.levelname == "CRITICAL": 137 colored.msg = red(record.msg) 138 else: 139 if "analysis procedure completed" in record.msg: 140 colored.msg = cyan(record.msg) 141 else: 142 colored.msg = record.msg 143 144 logging.StreamHandler.emit(self, colored)
145
146 -def init_logging():
147 """Initializes logging.""" 148 formatter = logging.Formatter("%(asctime)s [%(name)s] %(levelname)s: %(message)s") 149 150 fh = logging.handlers.WatchedFileHandler(os.path.join(CUCKOO_ROOT, "log", "cuckoo.log")) 151 fh.setFormatter(formatter) 152 log.addHandler(fh) 153 154 ch = ConsoleHandler() 155 ch.setFormatter(formatter) 156 log.addHandler(ch) 157 158 dh = DatabaseHandler() 159 dh.setLevel(logging.ERROR) 160 log.addHandler(dh) 161 162 log.setLevel(logging.INFO)
163
164 -def init_console_logging():
165 """Initializes logging only to console.""" 166 formatter = logging.Formatter("%(asctime)s [%(name)s] %(levelname)s: %(message)s") 167 168 ch = ConsoleHandler() 169 ch.setFormatter(formatter) 170 log.addHandler(ch) 171 172 log.setLevel(logging.INFO)
173
174 -def init_tasks():
175 """Check tasks and reschedule uncompleted ones.""" 176 db = Database() 177 cfg = Config() 178 179 log.debug("Checking for locked tasks...") 180 tasks = db.list_tasks(status=TASK_RUNNING) 181 182 for task in tasks: 183 if cfg.cuckoo.reschedule: 184 db.reschedule(task.id) 185 log.info("Rescheduled task with ID {0} and " 186 "target {1}".format(task.id, task.target)) 187 else: 188 db.set_status(task.id, TASK_FAILED_ANALYSIS) 189 log.info("Updated running task ID {0} status to failed_analysis".format(task.id))
190
191 -def init_modules():
192 """Initializes plugins.""" 193 log.debug("Importing modules...") 194 195 # Import all auxiliary modules. 196 import_package(modules.auxiliary) 197 # Import all processing modules. 198 import_package(modules.processing) 199 # Import all signatures. 200 import_package(modules.signatures) 201 # Import all reporting modules. 202 import_package(modules.reporting) 203 204 # Import machine manager. 205 import_plugin("modules.machinery." + Config().cuckoo.machinery) 206 207 for category, entries in list_plugins().items(): 208 log.debug("Imported \"%s\" modules:", category) 209 210 for entry in entries: 211 if entry == entries[-1]: 212 log.debug("\t `-- %s", entry.__name__) 213 else: 214 log.debug("\t |-- %s", entry.__name__)
215
216 -def init_yara():
217 """Generates index for yara signatures.""" 218 219 def find_signatures(root): 220 signatures = [] 221 for entry in os.listdir(root): 222 if entry.endswith(".yara") or entry.endswith(".yar"): 223 signatures.append(os.path.join(root, entry)) 224 225 return signatures
226 227 log.debug("Initializing Yara...") 228 229 # Generate root directory for yara rules. 230 yara_root = os.path.join(CUCKOO_ROOT, "data", "yara") 231 232 # We divide yara rules in three categories. 233 categories = ["binaries", "urls", "memory"] 234 generated = [] 235 # Loop through all categories. 236 for category in categories: 237 # Check if there is a directory for the given category. 238 category_root = os.path.join(yara_root, category) 239 if not os.path.exists(category_root): 240 continue 241 242 # Check if the directory contains any rules. 243 signatures = [] 244 for entry in os.listdir(category_root): 245 if entry.endswith(".yara") or entry.endswith(".yar"): 246 signatures.append(os.path.join(category_root, entry)) 247 248 if not signatures: 249 continue 250 251 # Generate path for the category's index file. 252 index_name = "index_{0}.yar".format(category) 253 index_path = os.path.join(yara_root, index_name) 254 255 # Create index file and populate it. 256 with open(index_path, "w") as index_handle: 257 for signature in signatures: 258 index_handle.write("include \"{0}\"\n".format(signature)) 259 260 generated.append(index_name) 261 262 for entry in generated: 263 if entry == generated[-1]: 264 log.debug("\t `-- %s", entry) 265 else: 266 log.debug("\t |-- %s", entry) 267 268
269 -def cuckoo_clean():
270 """Clean up cuckoo setup. 271 It deletes logs, all stored data from file system and configured databases (SQL 272 and MongoDB. 273 """ 274 # Init logging. 275 # This need to init a console logger handler, because the standard 276 # logger (init_logging()) logs to a file which will be deleted. 277 create_structure() 278 init_console_logging() 279 280 # Initialize the database connection. 281 db = Database() 282 283 # Drop all tables. 284 db.drop() 285 286 # Check if MongoDB reporting is enabled and drop that if it is. 287 cfg = Config("reporting") 288 if cfg.mongodb and cfg.mongodb.enabled: 289 from pymongo import MongoClient 290 host = cfg.mongodb.get("host", "127.0.0.1") 291 port = cfg.mongodb.get("port", 27017) 292 mdb = cfg.mongodb.get("db", "cuckoo") 293 try: 294 conn = MongoClient(host, port) 295 conn.drop_database(mdb) 296 conn.disconnect() 297 except: 298 log.warning("Unable to drop MongoDB database: %s", mdb) 299 300 # Paths to clean. 301 paths = [ 302 os.path.join(CUCKOO_ROOT, "db"), 303 os.path.join(CUCKOO_ROOT, "log"), 304 os.path.join(CUCKOO_ROOT, "storage"), 305 ] 306 307 # Delete various directories. 308 for path in paths: 309 if os.path.isdir(path): 310 try: 311 shutil.rmtree(path) 312 except (IOError, OSError) as e: 313 log.warning("Error removing directory %s: %s", path, e) 314 315 # Delete all compiled Python objects ("*.pyc"). 316 for dirpath, dirnames, filenames in os.walk(CUCKOO_ROOT): 317 for fname in filenames: 318 if not fname.endswith(".pyc"): 319 continue 320 321 path = os.path.join(CUCKOO_ROOT, dirpath, fname) 322 323 try: 324 os.unlink(path) 325 except (IOError, OSError) as e: 326 log.warning("Error removing file %s: %s", path, e)
327