Really fix payload recalculation
Instead of deleting all non-symbolics before the re-adding phase of PayloadSet#recalculate, store a list of old module names, populate a list of new ones during the re-adding phase, and finally remove any non-symbolic module that was in the old list but wasn't in the new list. Also includes a minor refactoring to make ModuleManager its own thing instead of being an awkard subclass of ModuleSet. Now PayloadSet doesn't need to know about the existence of framework.modules, which makes the separation a little more natural. [FixRM #7037]bug/bundler_fix
parent
6bd4306214
commit
f4476cb1b7
|
@ -375,7 +375,6 @@ class DBManager
|
|||
refresh.each {|md| md.destroy }
|
||||
refresh = nil
|
||||
|
||||
stime = Time.now.to_f
|
||||
[
|
||||
[ 'exploit', framework.exploits ],
|
||||
[ 'auxiliary', framework.auxiliary ],
|
||||
|
|
|
@ -12,11 +12,15 @@ require 'msf/core'
|
|||
require 'msf/core/module_set'
|
||||
|
||||
module Msf
|
||||
# Upper management decided to throw in some middle management # because the modules were getting out of hand. This
|
||||
# bad boy takes care of the work of managing the interaction with modules in terms of loading and instantiation.
|
||||
# Upper management decided to throw in some middle management
|
||||
# because the modules were getting out of hand. This bad boy takes
|
||||
# care of the work of managing the interaction with modules in terms
|
||||
# of loading and instantiation.
|
||||
#
|
||||
# @todo add unload support
|
||||
class ModuleManager < ModuleSet
|
||||
class ModuleManager #< ModuleSet
|
||||
include Msf::Framework::Offspring
|
||||
|
||||
require 'msf/core/payload_set'
|
||||
|
||||
# require here so that Msf::ModuleManager is already defined
|
||||
|
@ -39,25 +43,14 @@ module Msf
|
|||
# Maps module type directory to its module type.
|
||||
TYPE_BY_DIRECTORY = Msf::Modules::Loader::Base::DIRECTORY_BY_TYPE.invert
|
||||
|
||||
# Overrides the module set method for adding a module so that some extra steps can be taken to subscribe the module
|
||||
# and notify the event dispatcher.
|
||||
#
|
||||
# @param (see Msf::ModuleSet#add_module)
|
||||
# @return (see Msf::ModuleSet#add_module)
|
||||
def add_module(mod, name, file_paths)
|
||||
# Call {Msf::ModuleSet#add_module} with same arguments
|
||||
dup = super
|
||||
def [](key)
|
||||
names = key.split("/")
|
||||
type = names.shift
|
||||
|
||||
# Automatically subscribe a wrapper around this module to the necessary
|
||||
# event providers based on whatever events it wishes to receive. We
|
||||
# only do this if we are the module manager instance, as individual
|
||||
# module sets need not subscribe.
|
||||
auto_subscribe_module(dup)
|
||||
module_set = module_set_by_type[type]
|
||||
|
||||
# Notify the framework that a module was loaded
|
||||
framework.events.on_module_load(name, dup)
|
||||
|
||||
dup
|
||||
module_reference_name = names.join("/")
|
||||
module_set.create(module_reference_name)
|
||||
end
|
||||
|
||||
# Creates a module instance using the supplied reference name.
|
||||
|
@ -91,7 +84,7 @@ module Msf
|
|||
end
|
||||
|
||||
|
||||
# @param [Msf::Framework] framework The framework for which this instance is managing the modules.
|
||||
# @param framework [Msf::Framework] The framework for which this instance is managing the modules.
|
||||
# @param [Array<String>] types List of module types to load. Defaults to all module types in {Msf::MODULE_TYPES}.
|
||||
def initialize(framework, types=Msf::MODULE_TYPES)
|
||||
#
|
||||
|
@ -113,18 +106,18 @@ module Msf
|
|||
types.each { |type|
|
||||
init_module_set(type)
|
||||
}
|
||||
|
||||
super(nil)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This method automatically subscribes a module to whatever event providers it wishes to monitor. This can be used
|
||||
# to allow modules to automatically # execute or perform other tasks when certain events occur. For instance, when
|
||||
# a new host is detected, other aux modules may wish to run such that they can collect more information about the
|
||||
# host that was detected.
|
||||
# This method automatically subscribes a module to whatever event
|
||||
# providers it wishes to monitor. This can be used to allow modules
|
||||
# to automatically execute or perform other tasks when certain
|
||||
# events occur. For instance, when a new host is detected, other
|
||||
# aux modules may wish to run such that they can collect more
|
||||
# information about the host that was detected.
|
||||
#
|
||||
# @param [Class] mod a Msf::Module subclass
|
||||
# @param mod [Class] A subclass of Msf::Module
|
||||
# @return [void]
|
||||
def auto_subscribe_module(mod)
|
||||
# If auto-subscribe has been disabled
|
||||
|
|
|
@ -24,8 +24,8 @@ module Msf::ModuleManager::Cache
|
|||
def load_cached_module(type, reference_name)
|
||||
loaded = false
|
||||
|
||||
module_info = self.module_info_by_path.values.find { |module_info|
|
||||
module_info[:type] == type and module_info[:reference_name] == reference_name
|
||||
module_info = self.module_info_by_path.values.find { |inner_info|
|
||||
inner_info[:type] == type and inner_info[:reference_name] == reference_name
|
||||
}
|
||||
|
||||
if module_info
|
||||
|
@ -116,8 +116,9 @@ module Msf::ModuleManager::Cache
|
|||
|
||||
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}.
|
||||
# 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
|
||||
|
|
|
@ -57,21 +57,16 @@ module Msf::ModuleManager::Loading
|
|||
# categorized accordingly.
|
||||
#
|
||||
def on_module_load(mod, type, name, modinfo)
|
||||
# Payload modules require custom loading as the individual files
|
||||
# may not directly contain a logical payload that a user would
|
||||
# reference, such as would be the case with a payload stager or
|
||||
# stage. As such, when payload modules are loaded they are handed
|
||||
# off to a special payload set. The payload set, in turn, will
|
||||
# automatically create all the permutations after all the payload
|
||||
# modules have been loaded.
|
||||
dup = module_set_by_type[type].add_module(mod, name, modinfo)
|
||||
|
||||
if (type != Msf::MODULE_PAYLOAD)
|
||||
# Add the module class to the list of modules and add it to the
|
||||
# type separated set of module classes
|
||||
add_module(mod, name, modinfo)
|
||||
end
|
||||
# Automatically subscribe a wrapper around this module to the necessary
|
||||
# event providers based on whatever events it wishes to receive.
|
||||
auto_subscribe_module(dup)
|
||||
|
||||
module_set_by_type[type].add_module(mod, name, modinfo)
|
||||
# Notify the framework that a module was loaded
|
||||
framework.events.on_module_load(name, dup)
|
||||
|
||||
dup
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -33,8 +33,9 @@ class Msf::ModuleSet < Hash
|
|||
|
||||
# Create an instance of the supplied module by its name
|
||||
#
|
||||
# @param [String] name the module reference name.
|
||||
# @return [Msf::Module] instance of the named module.
|
||||
# @param name [String] The module reference name.
|
||||
# @return [Msf::Module,nil] Instance of the named module or nil if it
|
||||
# could not be created.
|
||||
def create(name)
|
||||
klass = fetch(name, nil)
|
||||
instance = nil
|
||||
|
@ -42,15 +43,7 @@ class Msf::ModuleSet < Hash
|
|||
# If there is no module associated with this class, then try to demand
|
||||
# load it.
|
||||
if klass.nil? or klass == Msf::SymbolicModule
|
||||
# If we are the root module set, then we need to try each module
|
||||
# type's demand loading until we find one that works for us.
|
||||
if module_type.nil?
|
||||
Msf::MODULE_TYPES.each { |type|
|
||||
framework.modules.load_cached_module(type, name)
|
||||
}
|
||||
else
|
||||
framework.modules.load_cached_module(module_type, name)
|
||||
end
|
||||
|
||||
recalculate
|
||||
|
||||
|
@ -168,17 +161,6 @@ class Msf::ModuleSet < Hash
|
|||
def on_module_reload(mod)
|
||||
end
|
||||
|
||||
# @!attribute [rw] postpone_recalc
|
||||
# Whether or not recalculations should be postponed. This is used
|
||||
# from the context of the {#each_module_list} handler in order to
|
||||
# prevent the demand loader from calling recalc for each module if
|
||||
# it's possible that more than one module may be loaded. This field
|
||||
# is not initialized until used.
|
||||
#
|
||||
# @return [true] if {#recalculate} should not be called immediately
|
||||
# @return [false] if {#recalculate} should be called immediately
|
||||
attr_accessor :postpone_recalculate
|
||||
|
||||
# Dummy placeholder to recalculate aliases and other fun things.
|
||||
#
|
||||
# @return [void]
|
||||
|
@ -194,8 +176,6 @@ class Msf::ModuleSet < Hash
|
|||
(self[name]) ? true : false
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Adds a module with a the supplied name.
|
||||
#
|
||||
# @param [Class] mod The module class: a subclass of Msf::Module.
|
||||
|
@ -226,25 +206,24 @@ class Msf::ModuleSet < Hash
|
|||
mod
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Load all modules that are marked as being symbolic.
|
||||
#
|
||||
# @return [void]
|
||||
def demand_load_modules
|
||||
found_symbolics = false
|
||||
# Pre-scan the module list for any symbolic modules
|
||||
self.each_pair { |name, mod|
|
||||
if (mod == Msf::SymbolicModule)
|
||||
self.postpone_recalculate = true
|
||||
|
||||
found_symbolics = true
|
||||
mod = create(name)
|
||||
|
||||
next if (mod.nil?)
|
||||
end
|
||||
}
|
||||
|
||||
# If we found any symbolic modules, then recalculate.
|
||||
if (self.postpone_recalculate)
|
||||
self.postpone_recalculate = false
|
||||
|
||||
if (found_symbolics)
|
||||
recalculate
|
||||
end
|
||||
end
|
||||
|
|
|
@ -63,15 +63,17 @@ class Msf::Modules::Loader::Base
|
|||
# Regex that can distinguish regular ruby source from unit test source.
|
||||
UNIT_TEST_REGEX = /rb\.(ut|ts)\.rb$/
|
||||
|
||||
# @param [Msf::ModuleManager] module_manager The module manager that caches the loaded modules.
|
||||
# @param [Msf::ModuleManager] module_manager The module manager that
|
||||
# caches the loaded modules.
|
||||
def initialize(module_manager)
|
||||
@module_manager = module_manager
|
||||
end
|
||||
|
||||
# Returns whether the path can be loaded this module loader.
|
||||
#
|
||||
# @abstract Override and determine from properties of the path or the file to which the path points whether it is
|
||||
# loadable using {#load_modules} for the subclass.
|
||||
# @abstract Override and determine from properties of the path or the
|
||||
# file to which the path points whether it is loadable using
|
||||
# {#load_modules} for the subclass.
|
||||
#
|
||||
# @param path (see #load_modules)
|
||||
# @return [Boolean]
|
||||
|
@ -81,22 +83,33 @@ class Msf::Modules::Loader::Base
|
|||
|
||||
# Loads a module from the supplied path and module_reference_name.
|
||||
#
|
||||
# @param [String] parent_path The path under which the module exists. This is not necessarily the same path as passed
|
||||
# to {#load_modules}: it may just be derived from that path.
|
||||
# @param [String] parent_path The path under which the module exists.
|
||||
# This is not necessarily the same path as passed to
|
||||
# {#load_modules}: it may just be derived from that path.
|
||||
# @param [String] type The type of module.
|
||||
# @param [String] module_reference_name The canonical name for referring to the module.
|
||||
# @param [Hash] options Options used to force loading and track statistics
|
||||
# @option options [Hash{String => Integer}] :count_by_type Maps the module type to the number of module loaded
|
||||
# @option options [Boolean] :force (false) whether to force loading of the module even if the module has not changed.
|
||||
# @option options [Hash{String => Boolean}] :recalculate_by_type Maps type to whether its
|
||||
# {Msf::ModuleManager::ModuleSets#module_set} needs to be recalculated.
|
||||
# @param [String] module_reference_name The canonical name for
|
||||
# referring to the module.
|
||||
# @param [Hash] options Options used to force loading and track
|
||||
# statistics
|
||||
# @option options [Hash{String => Integer}] :count_by_type Maps the
|
||||
# module type to the number of module loaded
|
||||
# @option options [Boolean] :force (false) whether to force loading of
|
||||
# the module even if the module has not changed.
|
||||
# @option options [Hash{String => Boolean}] :recalculate_by_type Maps
|
||||
# type to whether its {Msf::ModuleManager::ModuleSets#module_set}
|
||||
# needs to be recalculated.
|
||||
# @option options [Boolean] :reload (false) whether this is a reload.
|
||||
#
|
||||
# @return [false] if :force is false and parent_path has not changed.
|
||||
# @return [false] if exception encountered while parsing module content
|
||||
# @return [false] if the module is incompatible with the Core or API version.
|
||||
# @return [false] if the module does not implement a Metasploit(\d+) class.
|
||||
# @return [false] if exception encountered while parsing module
|
||||
# content
|
||||
# @return [false] if the module is incompatible with the Core or API
|
||||
# version.
|
||||
# @return [false] if the module does not implement a Metasploit(\d+)
|
||||
# class.
|
||||
# @return [false] if the module's is_usable method returns false.
|
||||
# @return [true] if all those condition pass and the module is successfully loaded.
|
||||
# @return [true] if all those condition pass and the module is
|
||||
# successfully loaded.
|
||||
#
|
||||
# @see #read_module_content
|
||||
# @see Msf::ModuleManager::Loading#file_changed?
|
||||
|
@ -227,12 +240,15 @@ class Msf::Modules::Loader::Base
|
|||
|
||||
# Loads all of the modules from the supplied path.
|
||||
#
|
||||
# @note Only paths where {#loadable?} returns true should be passed to this method.
|
||||
# @note Only paths where {#loadable?} returns true should be passed to
|
||||
# this method.
|
||||
#
|
||||
# @param [String] path Path under which there are modules
|
||||
# @param [Hash] options
|
||||
# @option options [Boolean] force (false) whether to force loading of the module even if the module has not changed.
|
||||
# @return [Hash{String => Integer}] Maps module type to number of modules loaded
|
||||
# @option options [Boolean] force (false) Whether to force loading of
|
||||
# the module even if the module has not changed.
|
||||
# @return [Hash{String => Integer}] Maps module type to number of
|
||||
# modules loaded
|
||||
def load_modules(path, options={})
|
||||
options.assert_valid_keys(:force)
|
||||
|
||||
|
@ -480,11 +496,14 @@ class Msf::Modules::Loader::Base
|
|||
namespace_module_name
|
||||
end
|
||||
|
||||
# Returns an Array of names to make a fully qualified module name to wrap the Metasploit(1|2|3) class so that it
|
||||
# doesn't overwrite other (metasploit) module's classes. Invalid module name characters are escaped by using 'H*'
|
||||
# unpacking and prefixing each code with X so the code remains a valid module name when it starts with a digit.
|
||||
# Returns an Array of names to make a fully qualified module name to
|
||||
# wrap the Metasploit(1|2|3) class so that it doesn't overwrite other
|
||||
# (metasploit) module's classes. Invalid module name characters are
|
||||
# escaped by using 'H*' unpacking and prefixing each code with X so
|
||||
# the code remains a valid module name when it starts with a digit.
|
||||
#
|
||||
# @param [String] uniq_module_reference_name The unique canonical name for the module including type.
|
||||
# @param [String] uniq_module_reference_name The unique canonical name
|
||||
# for the module including type.
|
||||
# @return [Array<String>] {NAMESPACE_MODULE_NAMES} + <derived-constant-safe names>
|
||||
#
|
||||
# @see namespace_module
|
||||
|
@ -513,8 +532,9 @@ class Msf::Modules::Loader::Base
|
|||
end
|
||||
|
||||
namespace_module = create_namespace_module(namespace_module_names)
|
||||
# Get the parent module from the created module so that restore_namespace_module can remove namespace_module's
|
||||
# constant if needed.
|
||||
# Get the parent module from the created module so that
|
||||
# restore_namespace_module can remove namespace_module's constant if
|
||||
# needed.
|
||||
parent_module = namespace_module.parent
|
||||
|
||||
begin
|
||||
|
|
|
@ -20,12 +20,9 @@ class PayloadSet < ModuleSet
|
|||
# Creates an instance of a payload set which is just a specialized module
|
||||
# set class that has custom handling for payloads.
|
||||
#
|
||||
def initialize(manager)
|
||||
def initialize
|
||||
super(MODULE_PAYLOAD)
|
||||
|
||||
# A reference to the ModuleManager instance
|
||||
self.manager = manager
|
||||
|
||||
# A hash of each of the payload types that holds an array
|
||||
# for all of the associated modules
|
||||
self.payload_type_modules = {}
|
||||
|
@ -74,60 +71,36 @@ class PayloadSet < ModuleSet
|
|||
# of singles, stagers, and stages.
|
||||
#
|
||||
def recalculate
|
||||
# Reset the current hash associations for all non-symbolic modules
|
||||
self.each_pair { |key, v|
|
||||
manager.delete(key) if (v != SymbolicModule)
|
||||
}
|
||||
old_keys = self.keys
|
||||
new_keys = []
|
||||
|
||||
self.delete_if { |k, v|
|
||||
v != SymbolicModule
|
||||
}
|
||||
|
||||
# Initialize a temporary hash
|
||||
_temp = {}
|
||||
|
||||
# Populate the temporary hash
|
||||
_singles.each_pair { |name, op|
|
||||
_temp[name] = op
|
||||
}
|
||||
# Recalculate single payloads
|
||||
_temp.each_pair { |name, op|
|
||||
_singles.each_pair { |name, op|
|
||||
mod, handler = op
|
||||
|
||||
# Build the payload dupe using the determined handler
|
||||
# and module
|
||||
p = build_payload(handler, mod)
|
||||
|
||||
# Sets the modules derived name
|
||||
p.refname = name
|
||||
|
||||
# Add it to the set
|
||||
add_single(p, name, op[5])
|
||||
new_keys.push name
|
||||
|
||||
# Cache the payload's size
|
||||
begin
|
||||
sizes[name] = p.new.size
|
||||
|
||||
# Don't cache generic payload sizes.
|
||||
rescue NoCompatiblePayloadError
|
||||
end
|
||||
}
|
||||
|
||||
# Initialize a temporary hash
|
||||
_temp = {}
|
||||
|
||||
# Populate the temporary hash
|
||||
_stagers.each_pair { |stager_name, op|
|
||||
_temp[stager_name] = op
|
||||
}
|
||||
# Recalculate staged payloads
|
||||
_temp.each_pair { |stager_name, op|
|
||||
mod, handler = op
|
||||
_stagers.each_pair { |stager_name, op|
|
||||
stager_mod, handler, stager_platform, stager_arch, stager_inst = op
|
||||
|
||||
# Walk the array of stages
|
||||
_stages.each_pair { |stage_name, ip|
|
||||
stage_mod, junk, stage_platform, stage_arch, stage_inst = ip
|
||||
stage_mod, _, stage_platform, stage_arch, stage_inst = ip
|
||||
|
||||
# No intersection between platforms on the payloads?
|
||||
if ((stager_platform) and
|
||||
|
@ -179,12 +152,20 @@ class PayloadSet < ModuleSet
|
|||
'files' => op[5]['files'] + ip[5]['files'],
|
||||
'paths' => op[5]['paths'] + ip[5]['paths'],
|
||||
'type' => op[5]['type']})
|
||||
new_keys.push combined
|
||||
|
||||
# Cache the payload's size
|
||||
sizes[combined] = p.new.size
|
||||
}
|
||||
}
|
||||
|
||||
# Blow away anything that was cached but didn't exist during the
|
||||
# recalculation
|
||||
self.delete_if do |k, v|
|
||||
next if v == SymbolicModule
|
||||
!!(old_keys.include?(k) and not new_keys.include?(k))
|
||||
end
|
||||
|
||||
flush_blob_cache
|
||||
end
|
||||
|
||||
|
@ -276,8 +257,7 @@ class PayloadSet < ModuleSet
|
|||
# returns an instance of that payload.
|
||||
#
|
||||
def find_payload_from_set(set, platform, arch, handler, session, payload_type)
|
||||
set.each do |m|
|
||||
name,mod = m
|
||||
set.each do |name, mod|
|
||||
p = mod.new
|
||||
|
||||
# We can't substitute one generic with another one.
|
||||
|
@ -303,6 +283,8 @@ class PayloadSet < ModuleSet
|
|||
#
|
||||
def add_single(p, name, modinfo)
|
||||
p.framework = framework
|
||||
p.refname = name
|
||||
p.file_path = modinfo['files'][0]
|
||||
|
||||
# Associate this class with the single payload's name
|
||||
self[name] = p
|
||||
|
@ -310,9 +292,6 @@ class PayloadSet < ModuleSet
|
|||
# Add the singles hash
|
||||
singles[name] = p
|
||||
|
||||
# Add it to the global module set
|
||||
manager.add_module(p, name, modinfo)
|
||||
|
||||
dlog("Built single payload #{name}.", 'core', LEV_2)
|
||||
end
|
||||
|
||||
|
@ -322,13 +301,12 @@ class PayloadSet < ModuleSet
|
|||
#
|
||||
def add_stage(p, full_name, stage_name, handler_type, modinfo)
|
||||
p.framework = framework
|
||||
p.refname = full_name
|
||||
p.file_path = modinfo['files'][0]
|
||||
|
||||
# Associate this stage's full name with the payload class in the set
|
||||
self[full_name] = p
|
||||
|
||||
# Add the full name association in the global module set
|
||||
manager.add_module(p, full_name, modinfo)
|
||||
|
||||
# Create the hash entry for this stage and then create
|
||||
# the associated entry for the handler type
|
||||
stages[stage_name] = {} if (!stages[stage_name])
|
||||
|
@ -445,7 +423,7 @@ protected
|
|||
return klass
|
||||
end
|
||||
|
||||
attr_accessor :manager, :payload_type_modules # :nodoc:
|
||||
attr_accessor :payload_type_modules # :nodoc:
|
||||
attr_writer :stages, :singles, :sizes # :nodoc:
|
||||
attr_accessor :_instances # :nodoc:
|
||||
|
||||
|
|
Loading…
Reference in New Issue