framework plugins

git-svn-id: file:///home/svn/incoming/trunk@3053 4d416f70-5f16-0410-b530-b9f4589650da
unstable
Matt Miller 2005-11-19 16:25:26 +00:00
parent 0e9c5bab5b
commit 09a41d4d5c
7 changed files with 274 additions and 0 deletions

View File

@ -37,6 +37,7 @@ class Config < Hash
'ModuleDirectory' => "modules",
'LogDirectory' => "logs",
'SessionLogDirectory' => "logs/sessions",
'PluginDirectory' => "plugins"
}
##
@ -73,6 +74,13 @@ class Config < Hash
self.new.log_directory
end
#
# Calls the instance method.
#
def self.plugin_directory
self.new.plugin_directory
end
#
# Calls the instance method.
#
@ -157,6 +165,13 @@ class Config < Hash
config_directory + FileSep + self['LogDirectory']
end
#
# Returns the directory that plugins are stored in.
#
def plugin_directory
install_root + FileSep + self['PluginDirectory']
end
#
# Returns the directory in which session log files are to reside.
#

View File

@ -28,6 +28,7 @@ require 'msf/core/option_container'
require 'msf/core/framework'
require 'msf/core/session_manager'
require 'msf/core/session'
require 'msf/core/plugin_manager'
# Wrappers
require 'msf/core/encoded_payload'

View File

@ -45,6 +45,7 @@ class Framework
self.reconmgr = ReconManager.new(self)
self.datastore = DataStore.new
self.jobs = Rex::JobContainer.new
self.plugins = PluginManager.new(self)
end
#
@ -112,6 +113,12 @@ class Framework
# of the framework.
#
attr_reader :jobs
#
# The framework instance's plugin manager. The plugin manager is
# responsible for exposing an interface that allows for the loading and
# unloading of plugins.
#
attr_reader :plugins
protected
@ -121,6 +128,7 @@ protected
attr_writer :datastore # :nodoc:
attr_writer :reconmgr # :nodoc:
attr_writer :jobs # :nodoc:
attr_writer :plugins # :nodoc:
end

70
lib/msf/core/plugin.rb Normal file
View File

@ -0,0 +1,70 @@
require 'rex/sync/ref'
module Msf
###
#
# This module represents an abstract plugin that can be loaded into the
# context of a framework instance. Plugins are meant to provide an easy way
# to augment the feature set of the framework by being able to load and unload
# them during the course of a framework's lifetime. For instance, a plugin
# could be loaded to alter the default behavior of new sessions, such as by
# scripting meterpreter sessions that are created. The possiblities are
# endless!
#
# All plugins must exist under the Msf::Plugin namespace. Plugins are
# reference counted to allow them to be loaded more than once if they're a
# singleton.
#
###
class Plugin
include Framework::Offspring
include Rex::Ref
#
# Create an instance of the plugin using the supplied framework instance.
# We use create instead of new directly so that singleton plugins can just
# return their singleton instance.
#
def self.create(framework)
new(framework)
end
#
# Initializes the plugin instance with the supplied framework instance.
#
def initialize(framework)
self.framework = framework
refinit
end
#
# Allows the plugin to clean up as it is being unloaded.
#
def cleanup
end
##
#
# Accessors
#
##
#
# Returns the name of the plugin.
#
def name
"unnamed"
end
#
# A short description of the plugin.
#
def desc
end
end
end

View File

@ -0,0 +1,93 @@
require 'msf/core/plugin'
module Msf
###
#
# This class manages the loading and unloading plugins. All plugins must
# implement the Plugin base class interface.
#
###
class PluginManager < Array
include Framework::Offspring
#
# The hash of path names to classes that is used during load.
#
@@path_hash = {}
#
# Check the hash using the supplied path name to see if there is already a
# class association.
#
def self.check_path_hash(path)
@@path_hash[path]
end
#
# Set the class that's associated with the supplied hash.
#
def self.set_path_hash(path, klass)
@@path_hash[path] = klass
end
#
# Initializes the plugin manager.
#
def initialize(framework)
self.framework = framework
end
#
# Loads a plugin from the supplied path and returns the instance that is
# created as a result.
#
def load(path)
# Check to see if a plugin from this path has already been loaded
# before.
if ((klass = self.class.check_path_hash(path)) == nil)
old = Msf::Plugin.constants
require(path)
new = Msf::Plugin.constants
# No new classes added?
if ((diff = new - old).empty?)
raise RuntimeError, "No classes were loaded from #{path} in the Msf::Plugin namespace."
end
# Grab the class
klass = Msf::Plugin.const_get(diff[0])
# Cache the path to class association for future reference
self.class.set_path_hash(path, klass)
end
# Create an instance of the plugin and let it initialize
instance = klass.create(framework)
# Add it to the list of plugins
if (self.member?(instance) == false)
self.unshift(instance)
end
# Return the instance to the caller
instance
end
#
# Unloads a plugin using the instance that was returned from a previous
# call to load.
#
def unload(inst)
# If the reference count drops to zero, remove it from the list of
# loaded plugins. This will indirectly call the cleanup method on the
# plugin.
if (inst.deref == true)
delete(inst)
end
end
end
end

View File

@ -46,6 +46,7 @@ class Core
"help" => "Help menu",
"info" => "Displays information about one or more module",
"jobs" => "Displays and manages jobs",
"load" => "Load a framework plugin",
"persist" => "Persist or restore framework state information",
"quit" => "Exit the console",
"route" => "Route traffic through a session",
@ -55,6 +56,7 @@ class Core
"set" => "Sets a variable to a value",
"setg" => "Sets a global variable to a value",
"show" => "Displays modules of a given type, or all modules",
"unload" => "Unload a framework plugin",
"unset" => "Unsets one or more variables",
"unsetg" => "Unsets one or more global variables",
"use" => "Selects a module by name",
@ -185,6 +187,32 @@ class Core
}
end
#
# Loads a plugin from the supplied path. If no absolute path is supplied,
# the framework root plugin directory is used.
#
def cmd_load(*args)
if (args.length == 0)
print(
"Usage: load <path>\n\n" +
"Load a plugin from the supplied path.")
return false
end
# Default to the supplied argument path.
path = args[0]
# If no absolute path was supplied, use the plugin directory as a base.
path = Msf::Config.plugin_directory + File::SEPARATOR + path if (path !~ /#{File::SEPARATOR}/)
# Load that plugin!
if ((inst = framework.plugins.load(path)))
print_status("Successfully loaded plugin: #{inst.name}")
else
print_error("Failed to load plugin from #{arg[0]}")
end
end
#
# This method persists or restores framework state from a persistent
# storage medium, such as a flatfile.
@ -598,6 +626,31 @@ class Core
else
print_error("No module selected.")
end
when "plugins"
show_plugins
end
}
end
#
# Unloads a plugin by its name.
#
def cmd_unload(*args)
if (args.length == 0)
print(
"Usage: unload [plugin name]\n\n" +
"Unloads a plugin by its symbolic name.")
return false
end
# Walk the plugins array
framework.plugins.each { |plugin|
# Unload the plugin if it matches the name we're searching for
if (plugin.name == args[0])
print("Unloading plugin #{args[0]}...")
framework.plugins.unload(plugin)
print_line("unloaded.")
break
end
}
end
@ -829,6 +882,16 @@ protected
end
end
def show_plugins # :nodoc:
tbl = generate_module_table("Plugins")
framework.plugins.each { |plugin|
tbl << [ plugin.name, plugin.desc ]
}
print(tbl.to_s)
end
def show_module_set(type, module_set) # :nodoc:
tbl = generate_module_table(type)

24
plugins/sample.rb Normal file
View File

@ -0,0 +1,24 @@
module Msf
###
#
# This class illustrates a sample plugin.
#
###
class Plugin::Sample < Msf::Plugin
def initialize(framework) # :nodoc:
super
end
def name # :nodoc:
"sample"
end
def desc # :nodoc:
"Demonstrates using framework plugins"
end
end
end