Module: Msf::ModuleManager::Cache

Extended by:
ActiveSupport::Concern
Included in:
Msf::ModuleManager
Defined in:
lib/msf/core/module_manager/cache.rb

Overview

Concerns the module cache maintained by the Msf::ModuleManager.

Constant Summary collapse

MEMORY =
:memory
FILESYSTEM =
:filesystem

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#module_info_by_pathObject (protected)



153
154
155
# File 'lib/msf/core/module_manager/cache.rb', line 153

def module_info_by_path
  @module_info_by_path
end

Instance Method Details

#cache_empty?true, false

Returns whether the cache is empty

Returns:

  • (true)

    if the cache has no entries.

  • (false)

    if the cache has any entries.



17
18
19
# File 'lib/msf/core/module_manager/cache.rb', line 17

def cache_empty?
  module_info_by_path.empty?
end

#cache_in_memory(class_or_module, options = {}) ⇒ void

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.

This method returns an undefined value.

Updates the in-memory cache so that Loading#file_changed? will report false if the module is loaded again.

Parameters:

  • class_or_module (Class<Msf::Module>, ::Module)

    either a module Class or a payload Module.

  • options (Hash{Symbol => String}) (defaults to: {})

Options Hash (options):

  • :path (String)

    the path to the file from which class_or_module was loaded.

  • :reference_name (String)

    the reference name for class_or_module.

  • :type (String)

    the module type

Raises:

  • (KeyError)

    unless :path is given.

  • (KeyError)

    unless :reference_name is given.

  • (KeyError)

    unless :type is given.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/msf/core/module_manager/cache.rb', line 39

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.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

#get_parent_path(module_path, type) ⇒ Object (protected)



203
204
205
206
207
# File 'lib/msf/core/module_manager/cache.rb', line 203

def get_parent_path(module_path, type)
  # 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], '')
  module_path.split(type_dir)[0..-2].join(type_dir) # TODO: rewrite
end

#load_cached_module(type, reference_name, cache_type: Msf::ModuleManager::Cache::MEMORY) ⇒ false, true

Forces loading of the module with the given type and module reference name from the cache.

Parameters:

  • type (String)

    the type of the module.

  • reference_name (String)

    the module reference name.

Returns:

  • (false)

    if a module with the given type and reference name does not exist in the cache.

  • (false)

    if :force is false and parent_path has not changed.

  • (false)

    if exception encountered while parsing module content

  • (false)

    if the module is incompatible with the Core or API version.

  • (false)

    if the module does not implement a Metasploit class.

  • (false)

    if the module's is_usable method returns false.

  • (true)

    if all those condition pass and the module is successfully loaded.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/msf/core/module_manager/cache.rb', line 76

def load_cached_module(type, reference_name, cache_type: Msf::ModuleManager::Cache::MEMORY)
  case cache_type
  when Msf::ModuleManager::Cache::FILESYSTEM
     = Msf::Modules::Metadata::Cache.instance.get_module_reference(type: type, reference_name: reference_name)
    return false unless 

    parent_path = get_parent_path(.path, type)
  when Msf::ModuleManager::Cache::MEMORY
     = nil
    module_info = self.module_info_by_path.values.find { |inner_info|
      inner_info[:type] == type and inner_info[:reference_name] == reference_name
    }
    return false unless module_info

    parent_path = module_info[:parent_path]
  else
    raise ArgumentError, "#{cache_type} is not a valid cache type."
  end

  try_load_module(parent_path, reference_name, type, cached_metadata: )
end

#module_info_by_path_from_database!(allowed_paths = [""]) ⇒ Hash{String => Hash{Symbol => Object}} (protected)

Note:

Also sets module_set(module_type) to nil if it is not already set.

Return a module info from Msf::Modules::Metadata::Obj.

Returns:

  • (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, :reference_name.



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/msf/core/module_manager/cache.rb', line 162

def module_info_by_path_from_database!(allowed_paths=[""])
  self.module_info_by_path = {}

  allowed_paths = allowed_paths.map{|x| x + "/"}

   = Msf::Modules::Metadata::Cache.instance.
  .each do ||
    path = .path
    type = .type
    reference_name = .ref_name

    # Skip cached modules that are not in our allowed load paths
    next if allowed_paths.select{|x| path.index(x) == 0}.empty?

    parent_path = get_parent_path(path, type)

    module_info_by_path[path] = {
        :reference_name => reference_name,
        :type => type,
        :parent_path => parent_path,
        :modification_time => .mod_time
    }
    .aliases.each do |a|
      self.aliases[a] = .fullname
    end
    self.inv_aliases[.fullname] = .aliases unless .aliases.empty?

    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}.
    next unless typed_module_set
    unless typed_module_set.has_key?(reference_name)
      typed_module_set[reference_name] = nil
    end
  end

  self.module_info_by_path
end

#refresh_cache_from_database(allowed_paths = [""]) ⇒ void

This method returns an undefined value.

Refreshes the in-memory cache from the database cache.



145
146
147
# File 'lib/msf/core/module_manager/cache.rb', line 145

def refresh_cache_from_database(allowed_paths=[""])
  self.module_info_by_path_from_database!(allowed_paths)
end

#refresh_cache_from_module_filesvoid #refresh_cache_from_module_files(module_class_or_instance) ⇒ void

Overloads:

  • #refresh_cache_from_module_filesvoid

    This method returns an undefined value.

    Rebuilds module metadata store and in-memory cache for all modules.

  • #refresh_cache_from_module_files(module_class_or_instance) ⇒ void

    This method returns an undefined value.

    Rebuilds database and in-memory cache for given module_class_or_instance.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/msf/core/module_manager/cache.rb', line 122

def refresh_cache_from_module_files(module_class_or_instance = nil)
  if module_class_or_instance
    Msf::Modules::Metadata::Cache.instance.(module_class_or_instance)
  else
    module_sets =
        [
            ['exploit', @framework.exploits],
            ['auxiliary', @framework.auxiliary],
            ['post', @framework.post],
            ['payload', @framework.payloads],
            ['encoder', @framework.encoders],
            ['nop', @framework.nops],
            ['evasion', @framework.evasion]
        ]
    @framework.payloads.recalculate # Ensure all payloads are calculated before refreshing metadata
    Msf::Modules::Metadata::Cache.instance.(module_sets)
  end
  refresh_cache_from_database(self.module_paths)
end

#try_load_module(parent_path, reference_name, type, cached_metadata: nil) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/msf/core/module_manager/cache.rb', line 98

def try_load_module(parent_path, reference_name, type, cached_metadata: nil)
  loaded = false
  # XXX borked
  loaders.each do |loader|
    next unless  || loader.loadable_module?(parent_path, type, reference_name)

    loaded = loader.load_module(parent_path, type, reference_name, force: true, cached_metadata: )

    break if loaded
  end

  loaded
end