Skip to content

Commit eb2e735

Browse files
committed
Merge pull request rapid7#1865 from limhoff-r7/bug/module-load-cache-update
Update in-memory cache to fix file_changed? bug.
2 parents 4f6d80c + cc60c95 commit eb2e735

File tree

12 files changed

+509
-139
lines changed

12 files changed

+509
-139
lines changed

lib/msf/base/simple/framework.rb

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: binary -*-
22
require 'msf/base/simple'
3+
require 'msf/base/simple/framework/module_paths'
34

45
module Msf
56
module Simple
@@ -12,6 +13,7 @@ module Simple
1213
#
1314
###
1415
module Framework
16+
include Msf::Simple::Framework::ModulePaths
1517

1618
###
1719
#
@@ -155,33 +157,6 @@ def save_config
155157
self.datastore.to_file(Msf::Config.config_file, 'framework/core')
156158
end
157159

158-
#
159-
# Initialize the module paths
160-
#
161-
def init_module_paths
162-
163-
# Ensure the module cache is accurate
164-
self.modules.refresh_cache_from_database
165-
166-
# Initialize the default module search paths
167-
if (Msf::Config.module_directory)
168-
self.modules.add_module_path(Msf::Config.module_directory)
169-
end
170-
171-
# Initialize the user module search path
172-
if (Msf::Config.user_module_directory)
173-
self.modules.add_module_path(Msf::Config.user_module_directory)
174-
end
175-
176-
# If additional module paths have been defined globally, then load them.
177-
# They should be separated by semi-colons.
178-
if self.datastore['MsfModulePaths']
179-
self.datastore['MsfModulePaths'].split(";").each { |path|
180-
self.modules.add_module_path(path)
181-
}
182-
end
183-
end
184-
185160
#
186161
# Statistics.
187162
#
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
module Msf
2+
module Simple
3+
module Framework
4+
module ModulePaths
5+
# Initialize the module paths
6+
#
7+
# @return [void]
8+
def init_module_paths
9+
# Ensure the module cache is accurate
10+
self.modules.refresh_cache_from_database
11+
12+
# Initialize the default module search paths
13+
if (Msf::Config.module_directory)
14+
self.modules.add_module_path(Msf::Config.module_directory)
15+
end
16+
17+
# Initialize the user module search path
18+
if (Msf::Config.user_module_directory)
19+
self.modules.add_module_path(Msf::Config.user_module_directory)
20+
end
21+
22+
# If additional module paths have been defined globally, then load them.
23+
# They should be separated by semi-colons.
24+
if self.datastore['MsfModulePaths']
25+
self.datastore['MsfModulePaths'].split(";").each { |path|
26+
self.modules.add_module_path(path)
27+
}
28+
end
29+
end
30+
end
31+
end
32+
end
33+
end

lib/msf/core/module_manager.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,9 @@ def initialize(framework, types=Msf::MODULE_TYPES)
144144
# aux modules may wish to run such that they can collect more
145145
# information about the host that was detected.
146146
#
147-
# @param mod [Class] A subclass of Msf::Module
147+
# @param klass [Class<Msf::Module>] The module class
148148
# @return [void]
149-
def auto_subscribe_module(mod)
149+
def auto_subscribe_module(klass)
150150
# If auto-subscribe has been disabled
151151
if (framework.datastore['DisableAutoSubscribe'] and
152152
framework.datastore['DisableAutoSubscribe'] =~ /^(y|1|t)/)
@@ -160,15 +160,15 @@ def auto_subscribe_module(mod)
160160
#
161161
# Exploit event subscriber check
162162
#
163-
if (mod.include?(Msf::ExploitEvent) == true)
164-
framework.events.add_exploit_subscriber((inst) ? inst : (inst = mod.new))
163+
if (klass.include?(Msf::ExploitEvent) == true)
164+
framework.events.add_exploit_subscriber((inst) ? inst : (inst = klass.new))
165165
end
166166

167167
#
168168
# Session event subscriber check
169169
#
170-
if (mod.include?(Msf::SessionEvent) == true)
171-
framework.events.add_session_subscriber((inst) ? inst : (inst = mod.new))
170+
if (klass.include?(Msf::SessionEvent) == true)
171+
framework.events.add_session_subscriber((inst) ? inst : (inst = klass.new))
172172
end
173173
end
174174

lib/msf/core/module_manager/cache.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,55 @@ def cache_empty?
1515
module_info_by_path.empty?
1616
end
1717

18+
# @note path, reference_name, and type must be passed as options because when +class_or_module+ is a payload Module,
19+
# those attributes will either not be set or not exist on the module.
20+
#
21+
# Updates the in-memory cache so that {#file_changed?} will report +false+ if
22+
# the module is loaded again.
23+
#
24+
# @param class_or_module [Class<Msf::Module>, ::Module] either a module Class
25+
# or a payload Module.
26+
# @param options [Hash{Symbol => String}]
27+
# @option options [String] :path the path to the file from which
28+
# +class_or_module+ was loaded.
29+
# @option options [String] :reference_name the reference name for
30+
# +class_or_module+.
31+
# @option options [String] :type the module type
32+
# @return [void]
33+
# @raise [KeyError] unless +:path+ is given.
34+
# @raise [KeyError] unless +:reference_name+ is given.
35+
# @raise [KeyError] unless +:type+ is given.
36+
def cache_in_memory(class_or_module, options={})
37+
options.assert_valid_keys(:path, :reference_name, :type)
38+
39+
path = options.fetch(:path)
40+
41+
begin
42+
modification_time = File.mtime(path)
43+
rescue Errno::ENOENT => error
44+
log_lines = []
45+
log_lines << "Could not find the modification of time of #{path}:"
46+
log_lines << error.class.to_s
47+
log_lines << error.to_s
48+
log_lines << "Call stack:"
49+
log_lines += error.backtrace
50+
51+
log_message = log_lines.join("\n")
52+
elog(log_message)
53+
else
54+
parent_path = class_or_module.parent.parent_path
55+
reference_name = options.fetch(:reference_name)
56+
type = options.fetch(:type)
57+
58+
module_info_by_path[path] = {
59+
:modification_time => modification_time,
60+
:parent_path => parent_path,
61+
:reference_name => reference_name,
62+
:type => type
63+
}
64+
end
65+
end
66+
1867
# Forces loading of the module with the given type and module reference name from the cache.
1968
#
2069
# @param [String] type the type of the module.

lib/msf/core/module_manager/loading.rb

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,39 @@ def file_changed?(path)
5353

5454
attr_accessor :module_load_error_by_path
5555

56-
# Called when a module is initially loaded such that it can be
57-
# categorized accordingly.
58-
#
59-
def on_module_load(mod, type, name, modinfo)
60-
dup = module_set_by_type[type].add_module(mod, name, modinfo)
61-
62-
# Automatically subscribe a wrapper around this module to the necessary
63-
# event providers based on whatever events it wishes to receive.
64-
auto_subscribe_module(dup)
65-
66-
# Notify the framework that a module was loaded
67-
framework.events.on_module_load(name, dup)
68-
69-
dup
70-
end
56+
# Called when a module is initially loaded such that it can be categorized
57+
# accordingly.
58+
#
59+
# @param class_or_module [Class<Msf::Module>, ::Module] either a module Class
60+
# or a payload Module.
61+
# @param type [String] The module type.
62+
# @param reference_name The module reference name.
63+
# @param info [Hash{String => Array}] additional information about the module
64+
# @option info [Array<String>] 'files' List of paths to the ruby source files
65+
# where +class_or_module+ is defined.
66+
# @option info [Array<String>] 'paths' List of module reference names.
67+
# @option info [String] 'type' The module type, should match positional
68+
# +type+ argument.
69+
# @return [void]
70+
def on_module_load(class_or_module, type, reference_name, info={})
71+
module_set = module_set_by_type[type]
72+
module_set.add_module(class_or_module, reference_name, info)
73+
74+
path = info['files'].first
75+
cache_in_memory(
76+
class_or_module,
77+
:path => path,
78+
:reference_name => reference_name,
79+
:type => type
80+
)
81+
82+
# Automatically subscribe a wrapper around this module to the necessary
83+
# event providers based on whatever events it wishes to receive.
84+
auto_subscribe_module(class_or_module)
85+
86+
# Notify the framework that a module was loaded
87+
framework.events.on_module_load(reference_name, class_or_module)
88+
end
7189

7290
protected
7391

lib/msf/core/module_set.rb

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,26 @@ def [](name)
3131
super
3232
end
3333

34-
# Create an instance of the supplied module by its name
34+
# Create an instance of the supplied module by its reference name
3535
#
36-
# @param name [String] The module reference name.
36+
# @param reference_name [String] The module reference name.
3737
# @return [Msf::Module,nil] Instance of the named module or nil if it
3838
# could not be created.
39-
def create(name)
40-
klass = fetch(name, nil)
39+
def create(reference_name)
40+
klass = fetch(reference_name, nil)
4141
instance = nil
4242

4343
# If there is no module associated with this class, then try to demand
4444
# load it.
4545
if klass.nil? or klass == Msf::SymbolicModule
46-
framework.modules.load_cached_module(module_type, name)
46+
framework.modules.load_cached_module(module_type, reference_name)
4747

4848
recalculate
4949

50-
klass = fetch(name, nil)
50+
klass = fetch(reference_name, nil)
5151
end
5252

53-
# If the klass is valid for this name, try to create it
53+
# If the klass is valid for this reference_name, try to create it
5454
unless klass.nil? or klass == Msf::SymbolicModule
5555
instance = klass.new
5656
end
@@ -67,7 +67,7 @@ def create(name)
6767
# "can't add a new key into hash during iteration"
6868
#
6969
# @yield [module_reference_name, module]
70-
# @yieldparam [String] module_reference_name the name of the module.
70+
# @yieldparam [String] module_reference_name the reference_name of the module.
7171
# @yieldparam [Class] module The module class: a subclass of Msf::Module.
7272
# @return [void]
7373
def each(&block)
@@ -167,43 +167,47 @@ def on_module_reload(mod)
167167
def recalculate
168168
end
169169

170-
# Checks to see if the supplied module name is valid.
170+
# Checks to see if the supplied module reference name is valid.
171171
#
172+
# @param reference_name [String] The module reference name.
172173
# @return [true] if the module can be {#create created} and cached.
173174
# @return [false] otherwise
174-
def valid?(name)
175-
create(name)
176-
(self[name]) ? true : false
175+
def valid?(reference_name)
176+
create(reference_name)
177+
(self[reference_name]) ? true : false
177178
end
178179

179-
# Adds a module with a the supplied name.
180+
# Adds a module with a the supplied reference_name.
180181
#
181-
# @param [Class] mod The module class: a subclass of Msf::Module.
182-
# @param [String] name The module reference name
183-
# @param [Hash{String => Object}] modinfo optional module information
184-
# @return [Class] The mod parameter modified to have {Msf::Module#framework}, {Msf::Module#refname},
185-
# {Msf::Module#file_path}, and {Msf::Module#orig_cls} set.
186-
def add_module(mod, name, modinfo = nil)
187-
# Set the module's name so that it can be referenced when
182+
# @param [Class<Msf::Module>] klass The module class.
183+
# @param [String] reference_name The module reference name.
184+
# @param [Hash{String => Object}] info optional module information.
185+
# @option info [Array<String>] 'files' List of paths to files that defined
186+
# +klass+.
187+
# @return [Class] The klass parameter modified to have
188+
# {Msf::Module#framework}, {Msf::Module#refname}, {Msf::Module#file_path},
189+
# and {Msf::Module#orig_cls} set.
190+
def add_module(klass, reference_name, info = {})
191+
# Set the module's reference_name so that it can be referenced when
188192
# instances are created.
189-
mod.framework = framework
190-
mod.refname = name
191-
mod.file_path = ((modinfo and modinfo['files']) ? modinfo['files'][0] : nil)
192-
mod.orig_cls = mod
193+
klass.framework = framework
194+
klass.refname = reference_name
195+
klass.file_path = ((info and info['files']) ? info['files'][0] : nil)
196+
klass.orig_cls = klass
193197

194198
# don't want to trigger a create, so use fetch
195-
cached_module = self.fetch(name, nil)
199+
cached_module = self.fetch(reference_name, nil)
196200

197201
if (cached_module and cached_module != Msf::SymbolicModule)
198-
ambiguous_module_reference_name_set.add(name)
202+
ambiguous_module_reference_name_set.add(reference_name)
199203

200204
# TODO this isn't terribly helpful since the refnames will always match, that's why they are ambiguous.
201-
wlog("The module #{mod.refname} is ambiguous with #{self[name].refname}.")
205+
wlog("The module #{klass.refname} is ambiguous with #{self[reference_name].refname}.")
202206
end
203207

204-
self[name] = mod
208+
self[reference_name] = klass
205209

206-
mod
210+
klass
207211
end
208212

209213
protected

0 commit comments

Comments
 (0)