Files
metasploit-gs/lib/msf/core/module_manager/cache.rb
T

192 lines
6.5 KiB
Ruby
Raw Normal View History

# -*- coding: binary -*-
2012-10-03 15:42:14 -05:00
#
# Gems
#
require 'active_support/concern'
2012-10-01 13:09:30 -05:00
# Concerns the module cache maintained by the {Msf::ModuleManager}.
module Msf::ModuleManager::Cache
extend ActiveSupport::Concern
2012-10-04 11:14:08 -05:00
# Returns whether the cache is empty
2012-10-01 13:09:30 -05:00
#
2012-10-04 11:14:08 -05:00
# @return [true] if the cache has no entries.
# @return [false] if the cache has any entries.
def cache_empty?
module_info_by_path.empty?
end
2013-08-30 16:28:33 -05:00
# @note path, reference_name, and type must be passed as options because when +class_or_module+ is a payload Module,
# those attributes will either not be set or not exist on the module.
#
# Updates the in-memory cache so that {#file_changed?} will report +false+ if
# the module is loaded again.
#
# @param class_or_module [Class<Msf::Module>, ::Module] either a module Class
# or a payload Module.
# @param options [Hash{Symbol => String}]
# @option options [String] :path the path to the file from which
# +class_or_module+ was loaded.
# @option options [String] :reference_name the reference name for
# +class_or_module+.
# @option options [String] :type the module type
# @return [void]
# @raise [KeyError] unless +:path+ is given.
# @raise [KeyError] unless +:reference_name+ is given.
# @raise [KeyError] unless +:type+ is given.
def cache_in_memory(class_or_module, options={})
options.assert_valid_keys(:path, :reference_name, :type)
path = options.fetch(:path)
begin
modification_time = File.mtime(path)
rescue Errno::ENOENT => error
log_lines = []
log_lines << "Could not find the modification of time of #{path}:"
log_lines << error.class.to_s
log_lines << error.to_s
log_lines << "Call stack:"
log_lines += error.backtrace
log_message = log_lines.join("\n")
elog(log_message)
else
parent_path = class_or_module.parent.parent_path
reference_name = options.fetch(:reference_name)
type = options.fetch(:type)
module_info_by_path[path] = {
:modification_time => modification_time,
:parent_path => parent_path,
:reference_name => reference_name,
:type => type
}
end
end
2013-05-22 12:28:42 -05:00
2012-10-04 11:14:08 -05:00
# Forces loading of the module with the given type and module reference name from the cache.
2012-10-01 13:09:30 -05:00
#
2012-10-04 11:14:08 -05:00
# @param [String] type the type of the module.
# @param [String] reference_name the module reference name.
# @return [false] if a module with the given type and reference name does not exist in the cache.
# @return (see Msf::Modules::Loader::Base#load_module)
def load_cached_module(type, reference_name)
loaded = false
2012-10-01 13:09:30 -05:00
2012-12-03 22:23:40 -06:00
module_info = self.module_info_by_path.values.find { |inner_info|
inner_info[:type] == type and inner_info[:reference_name] == reference_name
2012-10-04 11:14:08 -05:00
}
if module_info
parent_path = module_info[:parent_path]
2017-03-27 15:36:47 -05:00
# XXX borked
2012-10-04 11:14:08 -05:00
loaders.each do |loader|
if loader.loadable?(parent_path)
type = module_info[:type]
reference_name = module_info[:reference_name]
2012-10-01 13:09:30 -05:00
2012-10-04 11:14:08 -05:00
loaded = loader.load_module(parent_path, type, reference_name, :force => true)
2017-03-27 15:36:47 -05:00
break if loaded
2012-10-04 11:14:08 -05:00
end
2012-10-01 13:09:30 -05:00
end
end
2012-10-04 11:14:08 -05:00
loaded
2012-10-01 13:09:30 -05:00
end
2013-04-15 13:08:12 -05:00
# @overload refresh_cache_from_module_files
# Rebuilds database and in-memory cache for all modules.
2012-10-01 13:09:30 -05:00
#
2013-04-15 13:08:12 -05:00
# @return [void]
# @overload refresh_cache_from_module_files(module_class_or_instance)
# Rebuilds database and in-memory cache for given module_class_or_instance.
#
# @param (see Msf::DBManager#update_module_details)
# @return [void]
def refresh_cache_from_module_files(module_class_or_instance = nil)
2012-10-04 11:14:08 -05:00
if framework_migrated?
2013-04-15 13:08:12 -05:00
if module_class_or_instance
framework.db.update_module_details(module_class_or_instance)
2012-10-01 13:09:30 -05:00
else
framework.db.update_all_module_details
end
refresh_cache_from_database(self.module_paths)
2012-10-01 13:09:30 -05:00
end
end
2013-04-15 13:08:12 -05:00
# Refreshes the in-memory cache from the database cache.
2012-10-04 11:14:08 -05:00
#
# @return [void]
def refresh_cache_from_database(allowed_paths=[""])
self.module_info_by_path_from_database!(allowed_paths)
2012-10-04 11:14:08 -05:00
end
protected
# Returns whether the framework migrations have been run already.
#
# @return [true] if migrations have been run
# @return [false] otherwise
2012-10-01 13:09:30 -05:00
def framework_migrated?
framework.db && framework.db.migrated
2012-10-01 13:09:30 -05:00
end
2012-10-04 11:14:08 -05:00
# @!attribute [rw] module_info_by_path
# @return (see #module_info_by_path_from_database!)
attr_accessor :module_info_by_path
2013-04-26 13:14:38 -05:00
# Return a module info from Mdm::Module::Details in database.
2012-10-01 13:09:30 -05:00
#
2012-10-04 11:14:08 -05:00
# @note Also sets module_set(module_type)[module_reference_name] to Msf::SymbolicModule if it is not already set.
2012-10-01 13:09:30 -05:00
#
2013-04-26 13:14:38 -05:00
# @return [Hash{String => Hash{Symbol => Object}}] Maps path (Mdm::Module::Detail#file) to module information. Module
# information is a Hash derived from Mdm::Module::Detail. It includes :modification_time, :parent_path, :type,
2012-10-04 11:14:08 -05:00
# :reference_name.
def module_info_by_path_from_database!(allowed_paths=[""])
2012-10-04 11:14:08 -05:00
self.module_info_by_path = {}
if framework_migrated?
allowed_paths = allowed_paths.map{|x| x + "/"}
2013-08-30 16:28:33 -05:00
ActiveRecord::Base.connection_pool.with_connection do
# TODO record module parent_path in Mdm::Module::Detail so it does not need to be derived from file.
# Use find_each so Mdm::Module::Details are returned in batches, which will
# handle the growing number of modules better than all.each.
Mdm::Module::Detail.find_each do |module_detail|
path = module_detail.file
type = module_detail.mtype
reference_name = module_detail.refname
# Skip cached modules that are not in our allowed load paths
next if allowed_paths.select{|x| path.index(x) == 0}.empty?
2017-03-27 15:36:47 -05:00
# The load path is assumed to be the next level above the type directory
type_dir = File.join('', Mdm::Module::Detail::DIRECTORY_BY_TYPE[type], '')
parent_path = path.split(type_dir)[0..-2].join(type_dir) # TODO: rewrite
2013-08-30 16:28:33 -05:00
module_info_by_path[path] = {
:reference_name => reference_name,
:type => type,
:parent_path => parent_path,
:modification_time => module_detail.mtime
}
typed_module_set = module_set(type)
# Don't want to trigger as {Msf::ModuleSet#create} so check for
# key instead of using ||= which would call {Msf::ModuleSet#[]}
# which would potentially call {Msf::ModuleSet#create}.
unless typed_module_set.has_key? reference_name
typed_module_set[reference_name] = Msf::SymbolicModule
end
end
end
2012-10-04 11:14:08 -05:00
end
self.module_info_by_path
2012-10-01 13:09:30 -05:00
end
2012-12-03 22:23:40 -06:00
end