Skip to content

Commit e351da4

Browse files
authored
Merge pull request #1 from msutovsky-r7/collab/fix/payload-reload
Collab/fix/payload reload
2 parents 8bf1c40 + b9e8c9d commit e351da4

File tree

1 file changed

+55
-61
lines changed

1 file changed

+55
-61
lines changed
Lines changed: 55 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: binary -*-
2-
32
# Concerns reloading modules
3+
44
module Msf::ModuleManager::Reloading
55
# Reloads the module specified in mod. This can either be an instance of a module or a module class.
66
#
@@ -10,110 +10,103 @@ def reload_module(mod)
1010
# if it's an instance, then get its class
1111
if mod.is_a? Msf::Module
1212
metasploit_class = mod.class
13-
original_instance = mod
1413
else
1514
metasploit_class = mod
16-
original_instance = nil
1715
end
1816

19-
# Handle aliases cleanup
20-
if (aliased_as = inv_aliases[metasploit_class.fullname])
17+
if aliased_as = self.inv_aliases[metasploit_class.fullname]
2118
aliased_as.each do |a|
22-
aliases.delete a
19+
self.aliases.delete a
2320
end
24-
inv_aliases.delete metasploit_class.fullname
21+
self.inv_aliases.delete metasploit_class.fullname
2522
end
2623

27-
# Special handling for payload modules
28-
if mod&.payload?
29-
return reload_payload_module(metasploit_class, original_instance)
24+
if mod.payload?
25+
return reload_payload_module(mod)
3026
end
31-
32-
# Standard module reloading for non-payloads
27+
3328
namespace_module = metasploit_class.module_parent
34-
29+
3530
# Check if the namespace module has a loader
3631
unless namespace_module.respond_to?(:loader)
37-
raise "Module #{metasploit_class.fullname} namespace does not have a loader"
32+
elog("Module does not have loader")
33+
return mod
3834
end
3935

4036
loader = namespace_module.loader
4137
loader.reload_module(mod)
4238
end
43-
44-
private
45-
46-
# Reloads a payload module. This must be done by reloading the entire parent
47-
# directory to ensure the framework's complex payload "stitching" process
48-
# (combining stages, stagers, and mixins) is correctly executed. This is slower
49-
# but guarantees a fully-functional reloaded module.
50-
def reload_payload_module(metasploit_class, original_instance = nil)
51-
# Step 1: Get all necessary identifiers from the original module class.
52-
fullname = metasploit_class.fullname
53-
refname = metasploit_class.refname
54-
type = metasploit_class.type
55-
file_path = metasploit_class.file_path
56-
57-
# Get a reference to the original datastore now, but do not copy it yet.
58-
original_datastore = original_instance&.datastore
59-
60-
# Step 2: Manually purge the old module from the framework's caches.
61-
if (module_set = module_set_by_type.fetch(type, nil))
62-
module_set.delete(refname)
63-
end
64-
if (aliases_for_fullname = inv_aliases[fullname])
65-
aliases_for_fullname.each { |a| aliases.delete(a) }
66-
inv_aliases.delete(fullname)
39+
# Reload payload module, separately from other categories. This is due to complexity of payload module and due to the fact they don't follow class structure as rest of the modules.
40+
# @param [Msf::Module, Class] mod either an instance of a module or a module class
41+
# @return (see Msf::Modules::Loader::Base#reload_module)
42+
def reload_payload_module(mod)
43+
if mod.is_a? Msf::Module
44+
metasploit_class = mod.class
45+
original_instance = mod
46+
else
47+
metasploit_class = mod
48+
original_instance = nil
49+
end
50+
if (module_set = self.module_set_by_type.fetch(metasploit_class.type, nil))
51+
module_set.delete(metasploit_class.refname)
6752
end
6853

69-
# Step 3: Get the module's parent directory path.
70-
module_info = module_info_by_path[file_path]
54+
module_info = self.module_info_by_path[metasploit_class.file_path]
7155
unless module_info && (parent_path = module_info[:parent_path])
72-
raise Msf::LoadError, "Could not find cached module information for path: #{file_path}"
56+
elog("Failed to get parent_path from module object")
57+
return mod
7358
end
7459

75-
# Step 4: Use the core framework loader to reload the entire parent directory.
76-
# This is the only way to reliably trigger the payload stitching logic.
77-
load_modules(parent_path, force: true)
60+
case original_instance&.payload_type
61+
when Msf::Payload::Type::Single
62+
prepend_path = 'singles'
63+
when Msf::Payload::Type::Stager
64+
prepend_path = 'stagers'
65+
when Msf::Payload::Type::Stage
66+
prepend_path = 'stages'
67+
when Msf::Payload::Type::Adapter
68+
prepend_path = 'adapters'
69+
end
70+
71+
full_reference_name = File.join(prepend_path, module_info[:reference_name])
72+
self.loaders.each { |loader| loader.load_module(parent_path,module_info[:type], full_reference_name, {:force => true}) }
73+
7874

79-
# Step 5: Now that the framework has completed its full reload process,
80-
# use the public API to get a new instance of our reloaded module.
81-
new_instance = framework.modules.create(fullname)
75+
# Get reloaded module
76+
new_instance = framework.modules.create(metasploit_class.fullname)
8277

8378
if new_instance.blank?
84-
raise Msf::LoadError, "Failed to create a new instance of #{fullname} after reloading. The module file may be broken."
79+
elog("Failed create new instance")
80+
return mod
8581
end
8682

87-
# Step 6: Restore the datastore to the new, fully-functional instance.
88-
# Now we perform the copy, which is a method on the new datastore's merge function.
89-
if original_datastore
90-
new_instance.datastore.merge!(original_datastore)
91-
end
83+
# Restore the datastore
84+
new_instance.datastore.merge!(original_instance.datastore)
85+
9286

9387
# Return the new instance, which the framework will make the active module.
9488
return new_instance
9589
rescue StandardError => e
96-
elog("Failed to reload payload #{fullname}", error: e)
97-
raise Msf::LoadError, e.message
90+
elog("Failed to reload payload #{fullname}: #{e.message}")
91+
return mod
9892
end
9993

100-
public
101-
10294
# Reloads modules from all module paths
10395
#
10496
# @return (see Msf::ModuleManager::Loading#load_modules)
10597
def reload_modules
106-
enablement_by_type.each_key do |type|
98+
self.enablement_by_type.each_key do |type|
10799
module_set_by_type[type].clear
108100
init_module_set(type)
109101
end
110-
111-
aliases.clear
112-
inv_aliases.clear
102+
self.aliases.clear
103+
self.inv_aliases.clear
113104

114105
# default the count to zero the first time a type is accessed
115106
count_by_type = Hash.new(0)
116107

108+
framework.init_module_paths unless framework.module_paths_inited
109+
117110
module_paths.each do |path|
118111
path_count_by_type = load_modules(path, force: true)
119112

@@ -124,6 +117,7 @@ def reload_modules
124117
end
125118

126119
refresh_cache_from_module_files
120+
127121
count_by_type
128122
end
129123
end

0 commit comments

Comments
 (0)