CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/core/module_set.rb
Views: 11778
1
# -*- coding: binary -*-
2
require 'pathname'
3
4
###
5
#
6
# A module set contains zero or more named module classes of an arbitrary
7
# type.
8
#
9
###
10
class Msf::ModuleSet < Hash
11
include Msf::Framework::Offspring
12
13
# Wrapper that detects if a symbolic module is in use. If it is, it creates an instance to demand load the module
14
# and then returns the now-loaded class afterwards.
15
#
16
# @param [String] name the module reference name
17
# @return [Msf::Module] Class of the of the Msf::Module with the given reference name
18
def [](name)
19
module_class = super
20
if module_class.nil?
21
load_module_class(name)
22
end
23
24
super
25
end
26
27
# Create an instance of the supplied module by its reference name
28
#
29
# @param reference_name [String] The module reference name.
30
# @return [Msf::Module,nil] Instance of the named module or nil if it
31
# could not be created.
32
def create(reference_name, cache_type: Msf::ModuleManager::Cache::FILESYSTEM)
33
klass = load_module_class(reference_name, cache_type: cache_type)
34
instance = nil
35
# If the klass is valid for this reference_name, try to create it
36
unless klass.nil?
37
instance = klass.new
38
end
39
40
# Notify any general subscribers of the creation event
41
if instance
42
self.framework.events.on_module_created(instance)
43
else
44
self.delete(reference_name)
45
end
46
47
instance
48
end
49
50
# Overrides the builtin 'each' operator to avoid the following exception on Ruby 1.9.2+
51
# "can't add a new key into hash during iteration"
52
#
53
# @yield [module_reference_name, module]
54
# @yieldparam [String] module_reference_name the reference_name of the module.
55
# @yieldparam [Class] module The module class: a subclass of Msf::Module.
56
# @return [void]
57
def each(&block)
58
list = []
59
module_metadata.keys.sort.each do |sidx|
60
list << [sidx, self[sidx]]
61
end
62
list.each(&block)
63
end
64
65
# Enumerates each module class in the set.
66
#
67
# @param opts (see #each_module_list)
68
# @yield (see #each_module_list)
69
# @yieldparam (see #each_module_list)
70
# @return (see #each_module_list)
71
def each_module(opts = {}, &block)
72
self.mod_sorted = module_metadata.sort
73
74
each_module_list(mod_sorted, opts, &block)
75
end
76
77
# Custom each_module filtering if an advanced set supports doing extended filtering.
78
#
79
# @param opts (see #each_module_list)
80
# @param [String] name the module reference name
81
# @param [Array<String, Class>] entry pair of the module reference name and the module class.
82
# @return [false] if the module should not be filtered; it should be yielded by {#each_module_list}.
83
# @return [true] if the module should be filtered; it should not be yielded by {#each_module_list}.
84
def each_module_filter(opts, name, entry)
85
return false
86
end
87
88
# Enumerates each module class in the set based on their relative ranking to one another. Modules that are ranked
89
# higher are shown first.
90
#
91
# @param opts (see #each_module_list)
92
# @yield (see #each_module_list)
93
# @yieldparam (see #each_module_list)
94
# @return (see #each_module_list)
95
def each_module_ranked(opts = {}, &block)
96
each_module_list(rank_modules, opts, &block)
97
end
98
99
# Forces all modules in this set to be loaded.
100
#
101
# @return [void]
102
def force_load_set
103
each_module { |name, mod| }
104
end
105
106
# Initializes a module set that will contain modules of a specific type and expose the mechanism necessary to create
107
# instances of them.
108
#
109
# @param [String] type The type of modules cached by this {Msf::ModuleSet}.
110
def initialize(type = nil)
111
#
112
# Defaults
113
#
114
self.ambiguous_module_reference_name_set = Set.new
115
# Hashes that convey the supported architectures and platforms for a
116
# given module
117
self.architectures_by_module = {}
118
self.platforms_by_module = {}
119
self.mod_sorted = nil
120
self.mod_extensions = []
121
122
#
123
# Arguments
124
#
125
self.module_type = type
126
end
127
128
# @!attribute [r] module_type
129
# The type of modules stored by this {Msf::ModuleSet}.
130
#
131
# @return [String] type of modules
132
attr_reader :module_type
133
134
# Gives the module set an opportunity to handle a module reload event
135
#
136
# @param [Class] mod the module class: a subclass of Msf::Module
137
# @return [void]
138
def on_module_reload(mod)
139
end
140
141
# Dummy placeholder to recalculate aliases and other fun things.
142
#
143
# @return [void]
144
def recalculate
145
end
146
147
# Checks to see if the supplied module reference name is valid.
148
#
149
# @param reference_name [String] The module reference name.
150
# @return [true] if the module can be {#create created} and cached.
151
# @return [false] otherwise
152
def valid?(reference_name)
153
(self[reference_name]) ? true : false
154
end
155
156
# Adds a module with a the supplied reference_name.
157
#
158
# @param [Class<Msf::Module>] klass The module class.
159
# @param [String] reference_name The module reference name.
160
# @param [Hash{String => Object}] info optional module information.
161
# @option info [Array<String>] 'files' List of paths to files that defined
162
# +klass+.
163
# @return [Class] The klass parameter modified to have
164
# Msf::Module.framework, Msf::Module#refname, Msf::Module#file_path,
165
# and Msf::Module#orig_cls set.
166
def add_module(klass, reference_name, info = {})
167
# Set the module's reference_name so that it can be referenced when
168
# instances are created.
169
klass.framework = framework
170
klass.refname = reference_name
171
klass.file_path = ((info and info['files']) ? info['files'][0] : nil)
172
klass.orig_cls = klass
173
174
# don't want to trigger a create, so use fetch
175
cached_module = self.fetch(reference_name, nil)
176
177
if cached_module
178
ambiguous_module_reference_name_set.add(reference_name)
179
180
# TODO this isn't terribly helpful since the refnames will always match, that's why they are ambiguous.
181
wlog("The module #{klass.refname} is ambiguous with #{self[reference_name].refname}.")
182
end
183
184
self[reference_name] = klass
185
186
klass
187
end
188
189
def module_refnames
190
module_metadata.keys
191
end
192
193
protected
194
195
# Enumerates the modules in the supplied array with possible limiting factors.
196
#
197
# @param [Array<Array<String, Class>>] ary Array of module reference name and module class pairs
198
# @param [Hash{String => Object}] opts
199
# @option opts [Array<String>] 'Arch' List of 1 or more architectures that the module must support. The module need
200
# only support one of the architectures in the array to be included, not all architectures.
201
# @option opts [Array<String>] 'Platform' List of 1 or more platforms that the module must support. The module need
202
# only support one of the platforms in the array to be include, not all platforms.
203
# @yield [module_reference_name, module]
204
# @yieldparam [String] module_reference_name the name of module
205
# @yieldparam [Class] module The module class: a subclass of {Msf::Module}.
206
# @return [void]
207
def each_module_list(ary, opts, &block)
208
ary.each do |entry|
209
name, module_metadata = entry
210
211
# Filter out incompatible architectures
212
if (opts['Arch'])
213
if (!architectures_by_module[name])
214
architectures_by_module[name] = Array.wrap(module_metadata.arch)
215
end
216
217
next if ((architectures_by_module[name] & opts['Arch']).empty? == true)
218
end
219
220
# Filter out incompatible platforms
221
if (opts['Platform'])
222
if (!platforms_by_module[name])
223
platforms_by_module[name] = module_metadata.platform_list
224
end
225
226
next if ((platforms_by_module[name] & opts['Platform']).empty? == true)
227
end
228
229
# Custom filtering
230
next if (each_module_filter(opts, name, entry) == true)
231
232
mod = self[name]
233
next if mod.nil?
234
235
block.call(name, mod)
236
end
237
end
238
239
# @!attribute [rw] ambiguous_module_reference_name_set
240
# Set of module reference names that are ambiguous because two or more paths have modules with the same reference
241
# name
242
#
243
# @return [Set<String>] set of module reference names loaded from multiple paths.
244
attr_accessor :ambiguous_module_reference_name_set
245
# @!attribute [rw] architectures_by_module
246
# Maps a module to the list of architectures it supports.
247
#
248
# @return [Hash{Class => Array<String>}] Maps module class to Array of architecture Strings.
249
attr_accessor :architectures_by_module
250
attr_accessor :mod_extensions
251
# @!attribute [rw] platforms_by_module
252
# Maps a module to the list of platforms it supports.
253
#
254
# @return [Hash{Class => Array<String>}] Maps module class to Array of platform Strings.
255
attr_accessor :platforms_by_module
256
# @!attribute [rw] mod_sorted
257
# Array of module names and module classes ordered by their names.
258
#
259
# @return [Array<Array<String, Class>>] Array of arrays where the inner array is a pair of the module reference
260
# name and the module class.
261
attr_accessor :mod_sorted
262
# @!attribute [w] module_type
263
# The type of modules stored by this {Msf::ModuleSet}.
264
#
265
# @return [String] type of modules
266
attr_writer :module_type
267
268
# Ranks modules based on their constant rank value, if they have one. Modules without a Rank are treated as if they
269
# had {Msf::NormalRanking} for Rank.
270
#
271
# @return [Array<Array<String, Class>>] Array of arrays where the inner array is a pair of the module reference name
272
# and the module class.
273
def rank_modules
274
module_metadata.sort_by do |refname, metadata|
275
[metadata.rank || Msf::NormalRanking, refname]
276
end.reverse!
277
end
278
279
def module_metadata
280
Msf::Modules::Metadata::Cache.instance.module_metadata(module_type)
281
end
282
283
def load_module_class(reference_name, cache_type: Msf::ModuleManager::Cache::FILESYSTEM)
284
klass = fetch(reference_name, nil)
285
286
# If there is no module associated with this class, then try to demand load it.
287
if klass.nil?
288
framework.modules.load_cached_module(module_type, reference_name, cache_type: cache_type)
289
klass = fetch(reference_name, nil)
290
end
291
klass
292
end
293
end
294
295