2017-11-07 10:33:47 -06:00
|
|
|
require 'singleton'
|
|
|
|
|
#
|
|
|
|
|
# Core service class that provides storage of module metadata as well as operations on the metadata.
|
|
|
|
|
# Note that operations on this metadata are included as separate modules.
|
|
|
|
|
#
|
|
|
|
|
module Msf
|
|
|
|
|
module Modules
|
|
|
|
|
module Metadata
|
|
|
|
|
|
|
|
|
|
class Cache
|
|
|
|
|
include Singleton
|
|
|
|
|
include Msf::Modules::Metadata::Search
|
2017-11-15 16:38:01 -06:00
|
|
|
include Msf::Modules::Metadata::Store
|
2018-10-25 17:53:51 -05:00
|
|
|
include Msf::Modules::Metadata::Maps
|
2023-07-06 16:15:34 +01:00
|
|
|
include Msf::Modules::Metadata::Stats
|
2017-11-07 10:33:47 -06:00
|
|
|
|
|
|
|
|
#
|
2017-11-15 16:38:01 -06:00
|
|
|
# Refreshes cached module metadata as well as updating the store
|
2017-11-07 10:33:47 -06:00
|
|
|
#
|
2017-11-15 16:38:01 -06:00
|
|
|
def refresh_metadata_instance(module_instance)
|
2018-03-21 14:23:34 -05:00
|
|
|
@mutex.synchronize {
|
|
|
|
|
dlog "Refreshing #{module_instance.refname} of type: #{module_instance.type}"
|
|
|
|
|
refresh_metadata_instance_internal(module_instance)
|
|
|
|
|
update_store
|
|
|
|
|
}
|
2017-11-07 10:33:47 -06:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
#
|
2017-11-15 16:38:01 -06:00
|
|
|
# Returns the module data cache, but first ensures all the metadata is loaded
|
2017-11-07 10:33:47 -06:00
|
|
|
#
|
2017-11-15 16:38:01 -06:00
|
|
|
def get_metadata
|
2018-05-31 15:06:17 -05:00
|
|
|
@mutex.synchronize {
|
2018-03-21 14:23:34 -05:00
|
|
|
wait_for_load
|
|
|
|
|
@module_metadata_cache.values
|
2018-05-31 15:06:17 -05:00
|
|
|
}
|
2017-11-07 10:33:47 -06:00
|
|
|
end
|
|
|
|
|
|
2023-03-24 14:00:05 +00:00
|
|
|
def get_module_reference(type:, reference_name:)
|
|
|
|
|
@mutex.synchronize do
|
|
|
|
|
wait_for_load
|
|
|
|
|
@module_metadata_cache["#{type}_#{reference_name}"]
|
|
|
|
|
end
|
|
|
|
|
end
|
2017-11-07 10:33:47 -06:00
|
|
|
#
|
2017-11-15 16:38:01 -06:00
|
|
|
# Checks for modules loaded that are not a part of the cache and updates the underlying store
|
|
|
|
|
# if there are changes.
|
2017-11-07 10:33:47 -06:00
|
|
|
#
|
2017-11-15 16:38:01 -06:00
|
|
|
def refresh_metadata(module_sets)
|
2023-12-11 13:07:31 +00:00
|
|
|
has_changes = false
|
2018-03-21 14:23:34 -05:00
|
|
|
@mutex.synchronize {
|
|
|
|
|
unchanged_module_references = get_unchanged_module_references
|
|
|
|
|
module_sets.each do |mt|
|
|
|
|
|
unchanged_reference_name_set = unchanged_module_references[mt[0]]
|
|
|
|
|
|
|
|
|
|
mt[1].keys.sort.each do |mn|
|
|
|
|
|
next if unchanged_reference_name_set.include? mn
|
|
|
|
|
|
|
|
|
|
begin
|
2023-03-24 14:00:05 +00:00
|
|
|
module_instance = mt[1].create(mn, cache_type: Msf::ModuleManager::Cache::MEMORY)
|
2018-03-21 14:23:34 -05:00
|
|
|
rescue Exception => e
|
|
|
|
|
elog "Unable to create module: #{mn}. #{e.message}"
|
|
|
|
|
end
|
2018-02-28 08:41:14 -06:00
|
|
|
|
2018-03-21 14:23:34 -05:00
|
|
|
unless module_instance
|
|
|
|
|
wlog "Removing invalid module reference from cache: #{mn}"
|
|
|
|
|
existed = remove_from_cache(mn)
|
|
|
|
|
if existed
|
|
|
|
|
has_changes = true
|
|
|
|
|
end
|
|
|
|
|
next
|
2018-02-28 08:41:14 -06:00
|
|
|
end
|
|
|
|
|
|
2018-03-21 14:23:34 -05:00
|
|
|
begin
|
|
|
|
|
refresh_metadata_instance_internal(module_instance)
|
|
|
|
|
has_changes = true
|
|
|
|
|
rescue Exception => e
|
2020-06-11 13:09:25 +01:00
|
|
|
elog("Error updating module details for #{module_instance.fullname}", error: e)
|
2018-03-21 14:23:34 -05:00
|
|
|
end
|
2017-11-15 16:38:01 -06:00
|
|
|
end
|
|
|
|
|
end
|
2018-03-21 14:23:34 -05:00
|
|
|
}
|
2023-12-11 13:07:31 +00:00
|
|
|
if has_changes
|
|
|
|
|
update_store
|
|
|
|
|
clear_maps
|
|
|
|
|
update_stats
|
|
|
|
|
end
|
2017-11-07 10:33:47 -06:00
|
|
|
end
|
|
|
|
|
|
2024-01-15 14:56:46 +00:00
|
|
|
def module_metadata(type)
|
|
|
|
|
@mutex.synchronize do
|
|
|
|
|
wait_for_load
|
|
|
|
|
# TODO: Should probably figure out a way to cache this
|
|
|
|
|
@module_metadata_cache.filter_map { |_, metadata| [metadata.ref_name, metadata] if metadata.type == type }.to_h
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2018-03-21 14:23:34 -05:00
|
|
|
#######
|
|
|
|
|
private
|
|
|
|
|
#######
|
|
|
|
|
|
2017-11-07 10:33:47 -06:00
|
|
|
#
|
2017-11-15 16:38:01 -06:00
|
|
|
# Returns a hash(type->set) which references modules that have not changed.
|
2017-11-07 10:33:47 -06:00
|
|
|
#
|
2017-11-15 16:38:01 -06:00
|
|
|
def get_unchanged_module_references
|
|
|
|
|
skip_reference_name_set_by_module_type = Hash.new { |hash, module_type|
|
|
|
|
|
hash[module_type] = Set.new
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@module_metadata_cache.each_value do |module_metadata|
|
|
|
|
|
|
2018-02-28 11:11:26 -06:00
|
|
|
unless module_metadata.path && ::File.exist?(module_metadata.path)
|
2017-11-15 16:38:01 -06:00
|
|
|
next
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if ::File.mtime(module_metadata.path).to_i != module_metadata.mod_time.to_i
|
|
|
|
|
next
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
skip_reference_name_set = skip_reference_name_set_by_module_type[module_metadata.type]
|
|
|
|
|
skip_reference_name_set.add(module_metadata.ref_name)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return skip_reference_name_set_by_module_type
|
2017-11-07 10:33:47 -06:00
|
|
|
end
|
|
|
|
|
|
2018-02-28 08:41:14 -06:00
|
|
|
def remove_from_cache(module_name)
|
2018-02-28 11:21:34 -06:00
|
|
|
old_cache_size = @module_metadata_cache.size
|
|
|
|
|
@module_metadata_cache.delete_if {|_, module_metadata|
|
2018-02-28 11:00:40 -06:00
|
|
|
module_metadata.ref_name.eql? module_name
|
|
|
|
|
}
|
2018-02-28 08:41:14 -06:00
|
|
|
|
2018-02-28 11:21:34 -06:00
|
|
|
return old_cache_size != @module_metadata_cache.size
|
2018-02-28 08:41:14 -06:00
|
|
|
end
|
|
|
|
|
|
2017-11-07 10:33:47 -06:00
|
|
|
def wait_for_load
|
2017-11-15 16:38:01 -06:00
|
|
|
@load_thread.join unless @store_loaded
|
2017-11-07 10:33:47 -06:00
|
|
|
end
|
|
|
|
|
|
2017-11-15 16:38:01 -06:00
|
|
|
def refresh_metadata_instance_internal(module_instance)
|
|
|
|
|
metadata_obj = Obj.new(module_instance)
|
2017-11-07 10:33:47 -06:00
|
|
|
|
2018-02-28 11:00:40 -06:00
|
|
|
# Remove all instances of modules pointing to the same path. This prevents stale data hanging
|
2023-09-24 17:42:00 -04:00
|
|
|
# around when modules are incorrectly typed (eg: Auxiliary that should be Exploit)
|
2018-02-28 11:00:40 -06:00
|
|
|
@module_metadata_cache.delete_if {|_, module_metadata|
|
2018-05-21 14:18:43 -05:00
|
|
|
module_metadata.path.eql? metadata_obj.path && module_metadata.type != module_metadata.type
|
2018-02-28 11:00:40 -06:00
|
|
|
}
|
2018-02-28 08:41:14 -06:00
|
|
|
|
2018-02-28 11:00:40 -06:00
|
|
|
@module_metadata_cache[get_cache_key(module_instance)] = metadata_obj
|
2018-02-28 08:41:14 -06:00
|
|
|
end
|
|
|
|
|
|
2017-11-07 10:33:47 -06:00
|
|
|
def get_cache_key(module_instance)
|
|
|
|
|
key = ''
|
|
|
|
|
key << (module_instance.type.nil? ? '' : module_instance.type)
|
|
|
|
|
key << '_'
|
2019-08-26 13:22:14 -05:00
|
|
|
key << module_instance.class.refname
|
2017-11-07 10:33:47 -06:00
|
|
|
return key
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def initialize
|
2018-05-29 10:50:33 -05:00
|
|
|
super
|
2018-03-21 14:23:34 -05:00
|
|
|
@mutex = Mutex.new
|
2017-11-07 10:33:47 -06:00
|
|
|
@module_metadata_cache = {}
|
2017-11-15 16:38:01 -06:00
|
|
|
@store_loaded = false
|
2017-11-07 10:33:47 -06:00
|
|
|
@console = Rex::Ui::Text::Output::Stdio.new
|
2024-01-15 14:56:46 +00:00
|
|
|
@load_thread = Thread.new {
|
2017-11-15 16:38:01 -06:00
|
|
|
init_store
|
|
|
|
|
@store_loaded = true
|
|
|
|
|
}
|
2017-11-07 10:33:47 -06:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|