require 'msf/core' module Msf ### # # The module base class is responsible for providing the common interface # that is used to interact with modules at the most basic levels, such as # by inspecting a given module's attributes (name, dsecription, version, # authors, etc) and by managing the module's data store. # ### class Module # Modules can subscribe to a user-interface, and as such they include the # UI subscriber module. This provides methods like print, print_line, etc. # User interfaces are designed to be medium independent, and as such the # user interface subscribes are designed to provide a flexible way of # interacting with the user, n stuff. include Rex::Ui::Subscriber # Make include public so we can runtime extend public_class_method :include class < 'No module name', 'Description' => 'No module description', 'Version' => '0', 'Author' => nil, 'Arch' => nil, # No architectures by default. 'Platform' => [], # No platforms by default. 'Ref' => nil, 'Privileged' => false, 'License' => MSF_LICENSE, }.update(self.module_info) end # # This method initializes the module's compatibility hashes by normalizing # them into one single hash. As it stands, modules can define # compatibility in their supplied info hash through: # # Compat - direct compat definitions # PayloadCompat - payload compatibilities # EncoderCompat - encoder compatibilities # NopCompat - nop compatibilities # # In the end, the module specific compatibilities are merged as sub-hashes # of the primary Compat hash key to make checks more uniform. # def init_compat c = module_info['Compat'] if (c == nil) c = module_info['Compat'] = Hash.new end # Initialize the module sub compatibilities c['Payload'] = Hash.new if (c['Payload'] == nil) c['Encoder'] = Hash.new if (c['Encoder'] == nil) c['Nop'] = Hash.new if (c['Nop'] == nil) # Update the compat-derived module specific compatibilities from # the specific ones to make a uniform view of compatibilities c['Payload'].update(module_info['PayloadCompat'] || {}) c['Encoder'].update(module_info['EncoderCompat'] || {}) c['Nop'].update(module_info['NopCompat'] || {}) end # # Register options with a specific owning class. # def register_options(options, owner = self.class) self.options.add_options(options, owner) self.datastore.import_options(self.options, 'self', true) end # # Register advanced options with a specific owning class. # def register_advanced_options(options, owner = self.class) self.options.add_advanced_options(options, owner) self.datastore.import_options(self.options, 'self', true) end # # Register evasion options with a specific owning class. # def register_evasion_options(options, owner = self.class) self.options.add_evasion_options(options, owner) self.datastore.import_options(self.options, 'self', true) end # # Removes the supplied options from the module's option container # and data store. # def deregister_options(*names) names.each { |name| self.options.remove_option(name) self.datastore.delete(name) } end # # Checks to see if a derived instance of a given module implements a method # beyond the one that is provided by a base class. This is a pretty lame # way of doing it, but I couldn't find a better one, so meh. # def derived_implementor?(parent, method_name) (self.method(method_name).to_s.match(/#{parent}[^:]/)) ? false : true end # # Merges options in the info hash in a sane fashion, as some options # require special attention. # def merge_info(info, opts) opts.each_pair { |name, val| merge_check_key(info, name, val) } return info end # # Updates information in the supplied info hash and merges other # information. This method is used to override things like Name, Version, # and Description without losing the ability to merge architectures, # platforms, and options. # def update_info(info, opts) opts.each_pair { |name, val| # If the supplied option name is one of the ones that we should # override by default if (UpdateableOptions.include?(name) == true) # Only if the entry is currently nil do we use our value if (info[name] == nil) info[name] = val end # Otherwise, perform the merge operation like normal else merge_check_key(info, name, val) end } return info end # # Checks and merges the supplied key/value pair in the supplied hash. # def merge_check_key(info, name, val) if (self.respond_to?("merge_info_#{name.downcase}")) eval("merge_info_#{name.downcase}(info, val)") else # If the info hash already has an entry for this name if (info[name]) # If it's not an array, convert it to an array and merge the # two if (info[name].kind_of?(Array) == false) curr = info[name] info[name] = [ curr ] end # If the value being merged is an array, add each one if (val.kind_of?(Array) == true) val.each { |v| if (info[name].include?(v) == false) info[name] << v end } # Otherwise just add the value elsif (info[name].include?(val) == false) info[name] << val end # Otherwise, just set the value equal if no current value # exists else info[name] = val end end end # # Merge aliases with an underscore delimiter. # def merge_info_alias(info, val) merge_info_string(info, 'Alias', val, '_') end # # Merges the module name. # def merge_info_name(info, val) merge_info_string(info, 'Name', val, ', ', true) end # # Merges the module description. # def merge_info_description(info, val) merge_info_string(info, 'Description', val) end # # Merge the module version. # def merge_info_version(info, val) merge_info_string(info, 'Version', val) end # # Merges a given key in the info hash with a delimiter. # def merge_info_string(info, key, val, delim = ', ', inverse = false) if (info[key]) if (inverse == true) info[key] = info[key] + delim + val else info[key] = val + delim + info[key] end else info[key] = val end end # # Merges options. # def merge_info_options(info, val, advanced = false, evasion = false) key_name = ((advanced) ? 'Advanced' : (evasion) ? 'Evasion' : '') + 'Options' new_cont = OptionContainer.new new_cont.add_options(val, advanced, evasion) cur_cont = OptionContainer.new cur_cont.add_options(info[key_name] || [], advanced, evasion) new_cont.each_option { |name, option| next if (cur_cont.get(name)) info[key_name] = [] if (!info[key_name]) info[key_name] << option } end # # Merges advanced options. # def merge_info_advanced_options(info, val) merge_info_options(info, val, true, false) end # # Merges advanced options. # def merge_info_evasion_options(info, val) merge_info_options(info, val, false, true) end attr_accessor :module_info # :nodoc: attr_writer :author, :arch, :platform, :references, :datastore, :options # :nodoc: attr_writer :privileged # :nodoc: attr_writer :license # :nodoc: end # # Alias the data types so people can reference them just by Msf:: and not # Msf::Module:: # Author = Msf::Module::Author Reference = Msf::Module::Reference SiteReference = Msf::Module::SiteReference Platform = Msf::Module::Platform Target = Msf::Module::Target end