moved shit around again
git-svn-id: file:///home/svn/incoming/trunk@2498 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
###
|
||||
#
|
||||
# framework-shared
|
||||
# ----------------
|
||||
#
|
||||
# The shared library in the framework contains classes that are
|
||||
# used by various framework subsystems.
|
||||
#
|
||||
###
|
||||
|
||||
# Shared single purpose classes
|
||||
require 'Shared/ReadWriteLock'
|
||||
require 'Shared/Transformer'
|
||||
|
||||
# Logging facilities
|
||||
require 'Shared/Logging/LogSink'
|
||||
require 'Shared/Logging/LogDispatcher'
|
||||
@@ -0,0 +1,35 @@
|
||||
###
|
||||
#
|
||||
# framework-core
|
||||
# --------------
|
||||
#
|
||||
# The core library provides all of the means by which to interact
|
||||
# with the framework insofar as maniuplating encoders, nops,
|
||||
# payloads, exploits, recon, and sessions.
|
||||
#
|
||||
###
|
||||
|
||||
# Unit testing
|
||||
require 'test/unit'
|
||||
require 'Core/UnitTestSuite'
|
||||
|
||||
# framework-core depends on framework-shared
|
||||
require 'Shared'
|
||||
|
||||
# General
|
||||
require 'Core/Constants'
|
||||
require 'Core/Exceptions'
|
||||
require 'Core/DataTypes'
|
||||
require 'Core/EventDispatcher'
|
||||
require 'Core/DataStore'
|
||||
|
||||
# Framework context and core classes
|
||||
require 'Core/Framework'
|
||||
require 'Core/Session'
|
||||
|
||||
# Modules
|
||||
require 'Core/Module'
|
||||
require 'Core/Encoder'
|
||||
require 'Core/Exploit'
|
||||
require 'Core/Nop'
|
||||
require 'Core/Recon'
|
||||
@@ -0,0 +1,177 @@
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Author
|
||||
# ------
|
||||
#
|
||||
# This data type represents an author of a piece of code in either
|
||||
# the framework, a module, a script, or something entirely unrelated.
|
||||
#
|
||||
###
|
||||
class Author
|
||||
|
||||
# Class method that translates a string to an instance of the Author class,
|
||||
# if it's of the right format, and returns the Author class instance
|
||||
def Author.from_s(str)
|
||||
instance = Author.new
|
||||
|
||||
# If the serialization fails...
|
||||
if (instance.from_s(str) == false)
|
||||
return nil
|
||||
end
|
||||
|
||||
return instance
|
||||
end
|
||||
|
||||
def initialize(name = nil, email = nil)
|
||||
self.name = name
|
||||
self.email = email
|
||||
end
|
||||
|
||||
# Serialize the author object to a string in form:
|
||||
#
|
||||
# name <email>
|
||||
def to_s
|
||||
str = "#{name}"
|
||||
|
||||
if (email != nil)
|
||||
str += " <#{email}>"
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
# Translate the author from the supplied string which may
|
||||
# have either just a name or also an email address
|
||||
def from_s(str)
|
||||
|
||||
# List of known framework authors that can be referred by just name
|
||||
known_authors =
|
||||
{
|
||||
'H D Moore' => 'hdm@metasploit.com',
|
||||
'spoonm' => 'spoonm@gmail.com',
|
||||
'skape' => 'mmiller@hick.org',
|
||||
'vlad902' => 'vlad902@gmail.com'
|
||||
}
|
||||
|
||||
# Make fix up this regex to be a bit better...I suck at regex
|
||||
m = /^([A-Za-z0-9 _]*?) <(.*?)>/.match(str)
|
||||
|
||||
if (m != nil)
|
||||
self.name = m[1]
|
||||
self.email = m[2]
|
||||
else
|
||||
self.email = known_authors[str]
|
||||
|
||||
if (self.email != nil)
|
||||
self.name = str
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
attr_accessor :name, :email
|
||||
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# Reference
|
||||
# ---------
|
||||
#
|
||||
# A reference to some sort of information.
|
||||
#
|
||||
###
|
||||
class Reference
|
||||
|
||||
def Reference.from_s(str)
|
||||
return Reference.new(str)
|
||||
end
|
||||
|
||||
def initialize(in_str)
|
||||
self.str = in_str
|
||||
end
|
||||
|
||||
def to_s
|
||||
return self.str
|
||||
end
|
||||
|
||||
def from_s(in_str)
|
||||
self.str = in_str
|
||||
end
|
||||
|
||||
attr_reader :str
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :str
|
||||
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# SiteReference
|
||||
# -------------
|
||||
#
|
||||
# A reference to a website.
|
||||
#
|
||||
###
|
||||
class SiteReference < Reference
|
||||
|
||||
# Class method that translates a URL into a site reference instance
|
||||
def SiteReference.from_s(str)
|
||||
instance = SiteReference.new
|
||||
|
||||
if (instance.from_s(str) == false)
|
||||
return nil
|
||||
end
|
||||
|
||||
return instance
|
||||
end
|
||||
|
||||
# Initialize the site reference
|
||||
def initialize(in_site = nil, in_ctx_id = nil)
|
||||
self.ctx_id = in_ctx_id
|
||||
|
||||
if (in_site == 'OSVDB')
|
||||
self.site = 'http://www.osvdb.org/' + in_ctx_id.to_s
|
||||
elsif (in_site == 'CVE')
|
||||
self.site = 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=' + in_ctx_id.to_s
|
||||
elsif (in_site == 'BID')
|
||||
self.site = 'http://www.securityfocus.com/bid/' + in_ctx_id.to_s
|
||||
elsif (in_site == 'MSB')
|
||||
self.site = 'http://www.microsoft.com/technet/security/bulletin/' + in_ctx_id.to_s + '.mspx'
|
||||
else
|
||||
self.site = in_site
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the absolute site URL
|
||||
def to_s
|
||||
return site || ''
|
||||
end
|
||||
|
||||
# Serializes a site URL string
|
||||
def from_s(str)
|
||||
if (/(http:\/\/|https:\/\/|ftp:\/\/)/.match(str))
|
||||
self.site = str
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
attr_reader :site, :ctx_id
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :site, :ctx_id
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,88 @@
|
||||
module Msf
|
||||
class ModuleLoader
|
||||
|
||||
|
||||
attr_accessor :ext, :base, :namespace, :recursive, :history
|
||||
|
||||
def initialize(namespace, base, opts = { })
|
||||
|
||||
# merge in the defaults
|
||||
opts = {
|
||||
'ext' => '.rb',
|
||||
'recursive' => true
|
||||
}.update(opts)
|
||||
|
||||
self.ext = opts['ext']
|
||||
self.base = base
|
||||
self.namespace = namespace
|
||||
self.recursive = opts['recursive']
|
||||
self.history = { }
|
||||
end
|
||||
|
||||
def mod_from_name(name)
|
||||
obj = Object
|
||||
|
||||
name.split('::').each { |m|
|
||||
obj = obj.const_get(m)
|
||||
}
|
||||
|
||||
return obj
|
||||
end
|
||||
|
||||
def error(msg)
|
||||
puts '[!] ' + msg
|
||||
end
|
||||
|
||||
def clear_history
|
||||
self.history = { }
|
||||
end
|
||||
|
||||
def modload()
|
||||
|
||||
loaded = { }
|
||||
|
||||
mod = mod_from_name(namespace)
|
||||
|
||||
# build the glob to search on
|
||||
glob = base
|
||||
glob += '/**' if(recursive)
|
||||
glob += '/*' + ext
|
||||
|
||||
Dir[glob].each { |file|
|
||||
modold = mod.constants
|
||||
|
||||
begin
|
||||
if !load(file)
|
||||
error('Load failed for ' + file)
|
||||
next
|
||||
end
|
||||
rescue LoadError
|
||||
error('LoadError: ' + $!)
|
||||
next
|
||||
end
|
||||
|
||||
added = mod.constants - modold
|
||||
|
||||
if added.length > 1
|
||||
error('More than one class added in ' + file)
|
||||
next
|
||||
end
|
||||
|
||||
if added.empty?
|
||||
if history[file]
|
||||
added = history[file]
|
||||
else
|
||||
error('Loaded ' + file + ' but no class added')
|
||||
next
|
||||
end
|
||||
else
|
||||
added = mod.const_get(added[0])
|
||||
end
|
||||
|
||||
loaded[file] = added
|
||||
}
|
||||
|
||||
self.history.update(loaded)
|
||||
return loaded.values
|
||||
end
|
||||
end end # ModuleLoader/Msf
|
||||
@@ -0,0 +1,26 @@
|
||||
require 'Core'
|
||||
|
||||
module Msf
|
||||
module Test
|
||||
|
||||
###
|
||||
#
|
||||
# FrameworkCoreTestSuite
|
||||
# ----------------------
|
||||
#
|
||||
# This test suite is used to test the various core components of
|
||||
# framework-core.
|
||||
#
|
||||
###
|
||||
class FrameworkCoreTestSuite
|
||||
def self.suite
|
||||
suite = ::Test::Unit::TestSuite.new
|
||||
|
||||
suite << Msf::Test::DataStoreTestCase.suite
|
||||
|
||||
return suite;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,387 @@
|
||||
require 'resolv'
|
||||
require 'Core'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# DataStoreOption
|
||||
# ---------------
|
||||
#
|
||||
# A data store option is an option that is stored in a data store! It contains
|
||||
# meta information about the type of option being stored, such as type, as
|
||||
# well as the option's actual value
|
||||
#
|
||||
###
|
||||
class DataStoreOption
|
||||
|
||||
def initialize(in_name, attrs = [])
|
||||
self.name = in_name
|
||||
self.advanced = false
|
||||
self.required = attrs[0] || false
|
||||
self.desc = attrs[1] || nil
|
||||
self.default_value = attrs[2] || nil
|
||||
self.value = self.default_value
|
||||
end
|
||||
|
||||
def empty?
|
||||
return (value == nil)
|
||||
end
|
||||
|
||||
def required?
|
||||
return required
|
||||
end
|
||||
|
||||
def advanced?
|
||||
return advanced
|
||||
end
|
||||
|
||||
def valid?
|
||||
return (empty? and required?) ? false : true
|
||||
end
|
||||
|
||||
def type?(in_type)
|
||||
return (type == in_type)
|
||||
end
|
||||
|
||||
def reset
|
||||
value = default_value
|
||||
end
|
||||
|
||||
attr_reader :name, :required, :desc, :default_value, :value
|
||||
attr_writer :name, :value
|
||||
attr_accessor :advanced
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :required, :desc, :default_value
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# Core data store option types. The core supported option types are:
|
||||
#
|
||||
# OptString - Multi-byte character string
|
||||
# OptRaw - Multi-byte raw string
|
||||
# OptBool - Boolean true or false indication
|
||||
# OptPort - TCP/UDP service port
|
||||
# OptAddress - IP address or hostname
|
||||
# OptPath - Path name on disk
|
||||
#
|
||||
###
|
||||
|
||||
class OptString < DataStoreOption
|
||||
def type
|
||||
return 'string'
|
||||
end
|
||||
end
|
||||
|
||||
class OptRaw < DataStoreOption
|
||||
def type
|
||||
return 'raw'
|
||||
end
|
||||
end
|
||||
|
||||
class OptBool < DataStoreOption
|
||||
def type
|
||||
return 'bool'
|
||||
end
|
||||
|
||||
def valid?
|
||||
if ((empty? == false) and
|
||||
(value.match(/^(y|n|t|f|0|1)$/i) == nil))
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def is_true?
|
||||
return (value.match(/^(y|t|1)$/i) != nil) ? true : false
|
||||
end
|
||||
|
||||
def is_false?
|
||||
return !is_true?
|
||||
end
|
||||
end
|
||||
|
||||
class OptPort < DataStoreOption
|
||||
def type
|
||||
return 'port'
|
||||
end
|
||||
|
||||
def valid?
|
||||
if ((empty? == false) and
|
||||
((value.to_i < 0 or value.to_i > 65535)))
|
||||
return false
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
class OptAddress < DataStoreOption
|
||||
def type
|
||||
return 'address'
|
||||
end
|
||||
|
||||
def valid?
|
||||
if (empty? == false)
|
||||
begin
|
||||
Resolv.getaddress(value)
|
||||
rescue
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
class OptPath < DataStoreOption
|
||||
def type
|
||||
return 'path'
|
||||
end
|
||||
|
||||
def valid?
|
||||
if ((empty? == false) and
|
||||
(File.exists?(value) == false))
|
||||
return false
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# DataStore
|
||||
# ---------
|
||||
#
|
||||
# The data store's purpose in life is to associate named options
|
||||
# with arbitrary values at the most simplistic level. Each
|
||||
# module contains a DataStore that is used to hold the
|
||||
# various options that the module depends on. Example of options
|
||||
# that are stored in the DataStore are RHOST and RPORT for
|
||||
# payloads or exploits that need to connect to a host and
|
||||
# port, for instance.
|
||||
#
|
||||
###
|
||||
class DataStore < Hash
|
||||
|
||||
# Merges in the supplied options and converts them to a DataStoreOption
|
||||
# as necessary.
|
||||
def initialize(opts = {})
|
||||
add_options(opts)
|
||||
end
|
||||
|
||||
# Return the value associated with the supplied name
|
||||
def [](name)
|
||||
return get(name)
|
||||
end
|
||||
|
||||
# Set the value associated with the supplied name
|
||||
def set(name, value)
|
||||
option = fetch(name)
|
||||
|
||||
if (option == nil)
|
||||
return false
|
||||
end
|
||||
|
||||
option.value = value
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# Return the option associated with the supplied name
|
||||
def get(name)
|
||||
return fetch(name)
|
||||
end
|
||||
|
||||
# Return the value associated with the supplied name
|
||||
def get_value(name)
|
||||
return fetch(name).value
|
||||
end
|
||||
|
||||
# Adds one or more options
|
||||
def add_options(opts)
|
||||
return false if (opts == nil)
|
||||
|
||||
opts.each_key { |name|
|
||||
option = opts[name]
|
||||
|
||||
# Skip flags
|
||||
next if (name.match(/^_Flag/))
|
||||
|
||||
if (option.kind_of?(Array))
|
||||
option = option.shift.new(name, option)
|
||||
elsif (!option.kind_of?(DataStoreOption))
|
||||
raise ArgumentError,
|
||||
"The option named #{name} did not come in a compatible format.",
|
||||
caller
|
||||
end
|
||||
|
||||
option.name = name
|
||||
|
||||
# If the advanced flag was supplied, flag the new option as being
|
||||
# an advanced option
|
||||
if (opts['_FlagAdvanced'] == true)
|
||||
option.advanced = true
|
||||
end
|
||||
|
||||
self.store(name, option)
|
||||
}
|
||||
end
|
||||
|
||||
# Alias to add advanced options that sets the proper state flag
|
||||
def add_advanced_options(opts = {})
|
||||
opts['_FlagAdvanced'] = true if (opts)
|
||||
|
||||
add_options(opts)
|
||||
end
|
||||
|
||||
# Make sures that each of the options has a value of a compatible
|
||||
# format and that all the required options are set
|
||||
def validate
|
||||
errors = []
|
||||
|
||||
each_pair { |name, option|
|
||||
if (!option.valid?)
|
||||
errors << name
|
||||
end
|
||||
}
|
||||
|
||||
if (errors.empty? == false)
|
||||
raise OptionValidateError.new(errors),
|
||||
"One or more options failed to validate", caller
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# Enumerates each option name
|
||||
def each_option(&block)
|
||||
each_pair(&block)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Test
|
||||
|
||||
###
|
||||
#
|
||||
# DataStoreTestCase
|
||||
# -----------------
|
||||
#
|
||||
# This class implements some testing routines for ensuring that the data
|
||||
# store is operating correctly.
|
||||
#
|
||||
###
|
||||
class DataStoreTestCase < ::Test::Unit::TestCase
|
||||
# Tests the initialization of the DataStore object
|
||||
def test_initialize
|
||||
# Make sure initialization works
|
||||
ds = nil
|
||||
|
||||
assert_block("initialize failed") {
|
||||
ds = DataStore.new(
|
||||
'RHOST' => [ OptAddress, true, 'host.com' ],
|
||||
'RPORT' => [ OptPort, true, 1234 ])
|
||||
|
||||
if (ds == nil)
|
||||
false
|
||||
end
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
# Make sure there are 2 options
|
||||
assert_equal(2, ds.length, "invalid number of options #{ds.length}")
|
||||
|
||||
# Make sure that the constructor raises an argument error when
|
||||
# an invalid option is supplied
|
||||
assert_raise(ArgumentError, "initialize invalid failed") {
|
||||
DataStore.new(
|
||||
'RHOST' => 'invalid');
|
||||
}
|
||||
end
|
||||
|
||||
# Tests getting the value of an option
|
||||
def test_get
|
||||
ds = DataStore.new(
|
||||
'RPORT' => [ OptPort, true, nil, 1234 ])
|
||||
|
||||
assert_equal(1234, ds.get_value('RPORT'),
|
||||
"RPORT does not match")
|
||||
|
||||
ds.set('RPORT', 1235)
|
||||
|
||||
assert_equal(1235, ds.get_value('RPORT'),
|
||||
"RPORT does not match (2)")
|
||||
|
||||
assert_equal('RPORT', ds['RPORT'].name,
|
||||
"option name does not match")
|
||||
end
|
||||
|
||||
# Tests setting the value of an option
|
||||
def test_set
|
||||
assert_block("set failed") {
|
||||
ds = DataStore.new(
|
||||
'RHOST' => [ OptAddress ])
|
||||
|
||||
ds.set('RHOST', 'host.com')
|
||||
|
||||
if (ds.get_value('RHOST') != 'host.com')
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# Tests validation
|
||||
def test_validate
|
||||
# Test validating required options
|
||||
ds = DataStore.new(
|
||||
'RHOST' => [ OptAddress, true ],
|
||||
'RPORT' => [ OptPort, true ],
|
||||
'LIB' => [ OptString ])
|
||||
|
||||
assert_raise(OptionValidateError, "required validation failed") {
|
||||
ds.validate
|
||||
}
|
||||
|
||||
# Test validating the form of individual options
|
||||
ds.set('RHOST', 'www.invalid.host.tldinvalid')
|
||||
ds.set('RPORT', 1234)
|
||||
|
||||
assert_raise(OptionValidateError, "host validation failed") {
|
||||
ds.validate
|
||||
}
|
||||
|
||||
# Make sure address validation does work
|
||||
ds.set('RHOST', 'www.google.com')
|
||||
|
||||
assert_equal(true, ds.validate, "overall validation failed")
|
||||
|
||||
# Make sure port validation does work
|
||||
ds.set('RPORT', 123452)
|
||||
|
||||
assert_raise(OptionValidateError, "port validation failed") {
|
||||
ds.validate
|
||||
}
|
||||
end
|
||||
|
||||
# Make sure advanced additions work
|
||||
def test_advanced
|
||||
ds = DataStore.new
|
||||
|
||||
ds.add_advanced_options(
|
||||
'DONKEY' => [ OptString, false ])
|
||||
|
||||
assert_equal(true, ds.get('DONKEY').advanced?,
|
||||
"advanced option failed")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
###
|
||||
#
|
||||
# This file contains constants that are referenced by the core
|
||||
# framework and by framework modules.
|
||||
#
|
||||
###
|
||||
|
||||
module Msf
|
||||
|
||||
#
|
||||
# Architecture constants
|
||||
#
|
||||
ARCH_IA32 = 'ia32'
|
||||
ARCH_MIPS = 'mips'
|
||||
ARCH_PPC = 'ppc'
|
||||
ARCH_SPARC = 'sparc'
|
||||
|
||||
#
|
||||
# Module types
|
||||
#
|
||||
MODULE_ENCODER = 'encoder'
|
||||
MODULE_EXPLOIT = 'exploit'
|
||||
MODULE_NOP = 'nop'
|
||||
MODULE_RECON = 'recon'
|
||||
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# DataStore
|
||||
# ---------
|
||||
#
|
||||
# The data store is just a bitbucket that holds keyed values.
|
||||
#
|
||||
###
|
||||
class DataStore < Hash
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,264 @@
|
||||
require 'Core'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# EncoderState
|
||||
# ------------
|
||||
#
|
||||
# This class is used to track the state of a single encoding operation
|
||||
# from start to finish.
|
||||
#
|
||||
###
|
||||
class EncoderState
|
||||
|
||||
def initialize(key = nil)
|
||||
reset(key)
|
||||
end
|
||||
|
||||
# Reset the encoder state
|
||||
def reset(key = nil)
|
||||
init_key(key)
|
||||
|
||||
self.encoded = ''
|
||||
end
|
||||
|
||||
# Set the initial encoding key
|
||||
def init_key(key)
|
||||
self.key = key
|
||||
self.orig_key = key
|
||||
end
|
||||
|
||||
attr_accessor :key
|
||||
attr_accessor :orig_key
|
||||
attr_accessor :encoded
|
||||
attr_accessor :context
|
||||
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# Encoder
|
||||
# -------
|
||||
#
|
||||
# This class is the base class that all encoders inherit from.
|
||||
#
|
||||
###
|
||||
class Encoder < Module
|
||||
|
||||
def initialize(info)
|
||||
super(info)
|
||||
end
|
||||
|
||||
#
|
||||
# Encoder information accessors that can be overriden
|
||||
# by derived classes
|
||||
#
|
||||
|
||||
def type
|
||||
return MODULE_ENCODER
|
||||
end
|
||||
|
||||
def decoder_stub
|
||||
return module_info['DecoderStub']
|
||||
end
|
||||
|
||||
def decoder_key_offset
|
||||
return module_info['DecoderKeyOffset']
|
||||
end
|
||||
|
||||
def decoder_key_size
|
||||
return module_info['DecoderKeySize']
|
||||
end
|
||||
|
||||
def decoder_block_size
|
||||
return module_info['DecoderBlockSize']
|
||||
end
|
||||
|
||||
def decoder_key_pack
|
||||
return module_info['DecoderKeyPack'] || 'V'
|
||||
end
|
||||
|
||||
#
|
||||
# Encoding
|
||||
#
|
||||
|
||||
def encode(buf, badchars, state = nil)
|
||||
# Initialize the encoding state and key as necessary
|
||||
if (state == nil)
|
||||
state = EncoderState.new
|
||||
end
|
||||
|
||||
# Prepend data to the buffer as necessary
|
||||
buf = prepend_buf + buf
|
||||
|
||||
# If this encoder is key-based and we don't already have a key, find one
|
||||
if ((decoder_key_size) and
|
||||
(state.key == nil))
|
||||
# Find a key that doesn't contain and wont generate any bad
|
||||
# characters
|
||||
state.init_key(find_key(buf, badchars))
|
||||
|
||||
if (state.key == nil)
|
||||
raise NoKeyException, "A key could not be found for the #{self.name} encoder.", caller
|
||||
end
|
||||
end
|
||||
|
||||
# Call encode_begin to do any encoder specific pre-processing
|
||||
encode_begin(state)
|
||||
|
||||
# Perform the actual encoding operation with the determined state
|
||||
do_encode(buf, badchars, state)
|
||||
|
||||
# Call encoded_end to do any encoder specific post-processing
|
||||
encode_end(state)
|
||||
|
||||
# Return the encoded buffer to the caller
|
||||
return state.encoded
|
||||
end
|
||||
|
||||
def do_encode(buf, badchars, state)
|
||||
# Copy the decoder stub since we may need to modify it
|
||||
stub = decoder_stub.dup
|
||||
|
||||
if (state.key != nil)
|
||||
# Substitute the decoder key in the copy of the decoder stub with the
|
||||
# one that we found
|
||||
stub[decoder_key_offset,decoder_key_size] = [ state.key.to_i ].pack(decoder_key_pack)
|
||||
end
|
||||
|
||||
# Walk the buffer encoding each block along the way
|
||||
offset = 0
|
||||
|
||||
while (offset < buf.length)
|
||||
block = buf[offset, decoder_block_size]
|
||||
|
||||
state.encoded += encode_block(state,
|
||||
block + ("\x00" * (decoder_block_size - block.length)))
|
||||
|
||||
offset += decoder_block_size
|
||||
end
|
||||
|
||||
# Prefix the decoder stub to the encoded buffer
|
||||
state.encoded = stub + state.encoded
|
||||
|
||||
# Last but not least, do one last badchar pass to see if the stub +
|
||||
# encoded payload leads to any bad char issues...
|
||||
if ((badchar_idx = has_badchars?(state.encoded, badchars)) != nil)
|
||||
raise BadcharException.new(state.encoded, badchar_idx, stub.length, badchars[badchar_idx]),
|
||||
"The #{self.name} encoder failed to encode without bad characters.",
|
||||
caller
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Buffer management
|
||||
#
|
||||
|
||||
def prepend_buf
|
||||
return ''
|
||||
end
|
||||
|
||||
#
|
||||
# Pre-processing, post-processing, and block encoding stubs
|
||||
#
|
||||
|
||||
def encode_begin(state)
|
||||
return nil
|
||||
end
|
||||
|
||||
def encode_end(state)
|
||||
return nil
|
||||
end
|
||||
|
||||
def encode_block(state, block)
|
||||
return block
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def find_key(buf, badchars)
|
||||
key_bytes = [ ]
|
||||
cur_key = [ ]
|
||||
bad_keys = find_bad_keys(buf, badchars)
|
||||
found = false
|
||||
|
||||
# Keep chugging until we find something...right
|
||||
while (!found)
|
||||
# Scan each byte position
|
||||
0.upto(decoder_key_size - 1) { |index|
|
||||
cur_key[index] = rand(255)
|
||||
|
||||
# Scan all 255 bytes (wrapping around as necessary)
|
||||
for cur_char in (cur_key[index] .. (cur_key[index] + 255))
|
||||
cur_char = (cur_char % 255) + 1
|
||||
|
||||
# If this is a known bad character at this location in the
|
||||
# key or it doesn't pass the bad character check...
|
||||
if (((bad_keys != nil) and
|
||||
(bad_keys[index][cur_char] == true)) or
|
||||
(badchars.index(cur_char) != nil))
|
||||
next
|
||||
end
|
||||
|
||||
key_bytes[index] = cur_char
|
||||
end
|
||||
}
|
||||
|
||||
# Assume that we're going to rock this shit...
|
||||
found = true
|
||||
|
||||
# Scan each byte and see what we've got going on to make sure
|
||||
# no funny business is happening
|
||||
key_bytes.each { |byte|
|
||||
if (badchars.index(byte) != nil)
|
||||
found = false
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# Do we have all the key bytes accounted for?
|
||||
if (key_bytes.length != decoder_key_size)
|
||||
return nil
|
||||
end
|
||||
|
||||
return key_bytes_to_integer(key_bytes)
|
||||
end
|
||||
|
||||
def find_bad_keys
|
||||
return [ {}, {}, {}, {} ]
|
||||
end
|
||||
|
||||
def has_badchars?(buf, badchars)
|
||||
badchars.each_byte { |badchar|
|
||||
idx = buf.index(badchar)
|
||||
|
||||
if (idx != nil)
|
||||
return idx
|
||||
end
|
||||
}
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
# Convert individual key bytes into a single integer based on the
|
||||
# decoder's key size and packing requirements
|
||||
def key_bytes_to_integer(key_bytes)
|
||||
return key_bytes.pack('C' + decoder_key_size.to_s).unpack(decoder_key_pack)[0]
|
||||
end
|
||||
|
||||
# Convert an integer into the individual key bytes based on the
|
||||
# decoder's key size and packing requirements
|
||||
def integer_to_key_bytes(integer)
|
||||
return [ integer.to_i ].pack(decoder_key_pack).unpack('C' + decoder_key_size.to_s)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
require 'Core/Encoder/Xor'
|
||||
require 'Core/Encoder/XorAdditiveFeedback'
|
||||
@@ -0,0 +1,36 @@
|
||||
require 'Core'
|
||||
|
||||
###
|
||||
#
|
||||
# Xor
|
||||
# ---
|
||||
#
|
||||
# This class provides basic XOR encoding of buffers.
|
||||
#
|
||||
###
|
||||
class Msf::Encoder::Xor < Msf::Encoder
|
||||
|
||||
def encode_block(state, block)
|
||||
return Msf::Encoding::Xor.encode_block(state.key, block, decoder_block_size, decoder_key_pack)
|
||||
end
|
||||
|
||||
def find_bad_keys(buf, badchars)
|
||||
bad_keys = [ {}, {}, {}, {} ]
|
||||
byte_idx = 0
|
||||
|
||||
# Scan through all the badchars and build out the bad_keys array
|
||||
# based on the XOR'd combinations that can occur at certain bytes
|
||||
# to produce bad characters
|
||||
badchars.each_byte { |badchar|
|
||||
buf.each_byte { |byte|
|
||||
bad_keys[byte_idx % decoder_key_size][byte ^ badchar] = true
|
||||
|
||||
byte_idx += 1
|
||||
}
|
||||
}
|
||||
|
||||
return bad_keys
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -0,0 +1,82 @@
|
||||
require 'Core'
|
||||
|
||||
###
|
||||
#
|
||||
# XorAdditiveFeedback
|
||||
# -------------------
|
||||
#
|
||||
# This class performs per-block XOR additive feedback encoding.
|
||||
#
|
||||
###
|
||||
class Msf::Encoder::XorAdditiveFeedback < Msf::Encoder::Xor
|
||||
|
||||
def initialize(info)
|
||||
super(info)
|
||||
end
|
||||
|
||||
def encode_block(state, block)
|
||||
# XOR the key with the current block
|
||||
orig = block.unpack(decoder_key_pack)[0]
|
||||
oblock = orig ^ state.key
|
||||
|
||||
# Add the original block contents to the key
|
||||
state.key = (state.key + orig) % (1 << (decoder_key_size * 8))
|
||||
|
||||
# Return the XOR'd block
|
||||
return [ oblock ].pack(decoder_key_pack)
|
||||
end
|
||||
|
||||
def find_key(buf, badchars)
|
||||
key_bytes = integer_to_key_bytes(super(buf, badchars))
|
||||
state = Msf::EncoderState.new
|
||||
valid = false
|
||||
|
||||
# Save the original key_bytes so we can tell if we loop around
|
||||
orig_key_bytes = key_bytes.dup
|
||||
|
||||
# While we haven't found a valid key, keep trying the encode operation
|
||||
while (!valid)
|
||||
begin
|
||||
# Reset the encoder state's key to the current set of key bytes
|
||||
state.reset(key_bytes_to_integer(key_bytes))
|
||||
|
||||
# If the key itself contains a bad character, throw the bad
|
||||
# character exception with the index of the bad character in the
|
||||
# key. Use a stub_size of zero to bypass the check to in the
|
||||
# rescue block.
|
||||
if ((idx = has_badchars?([state.key.to_i].pack(decoder_key_pack), badchars)) != nil)
|
||||
raise Msf::BadcharException.new(nil, idx, 0, nil)
|
||||
end
|
||||
|
||||
# Perform the encode operation...if it encounters a bad character
|
||||
# an exception will be thrown
|
||||
valid = do_encode(buf, badchars, state)
|
||||
rescue Msf::BadcharException => info
|
||||
# If the decoder stub contains a bad character, then there's not
|
||||
# much we can do about it
|
||||
if (info.index < info.stub_size)
|
||||
raise info, "The #{self.name} decoder stub contains a bad character.", caller
|
||||
end
|
||||
|
||||
# Determine the actual index to the bad character inside the
|
||||
# encoded payload by removing the decoder stub from the index and
|
||||
# modulus off the decoder's key size
|
||||
idx = (info.index - info.stub_size) % (decoder_key_size)
|
||||
|
||||
# Increment the key byte at the index that the bad character was
|
||||
# detected
|
||||
key_bytes[idx] = ((key_bytes[idx] + 1) % 255)
|
||||
|
||||
# If we looped around, then give up.
|
||||
if (key_bytes[idx] == orig_key_bytes[idx])
|
||||
raise info, "The #{self.name} encoder failed to encode without bad characters.",
|
||||
caller
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Return the original key
|
||||
return state.orig_key
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
module Msf
|
||||
module Encoding
|
||||
|
||||
###
|
||||
#
|
||||
# Xor
|
||||
# ---
|
||||
#
|
||||
# This class provides basic XOR encoding facilities and is used
|
||||
# by XOR encoders.
|
||||
#
|
||||
###
|
||||
class Xor
|
||||
|
||||
def Xor.encode_block(key, block, block_size = 4, block_pack = 'V')
|
||||
offset = 0
|
||||
oblock = ''
|
||||
|
||||
while (offset < block.length)
|
||||
cblock = block[offset, block_size].unpack(block_pack)[0]
|
||||
cblock ^= key
|
||||
oblock += [ cblock ].pack(block_pack)
|
||||
end
|
||||
|
||||
return oblock
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end end
|
||||
@@ -0,0 +1,120 @@
|
||||
require 'Core'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# EventDispatcher
|
||||
# ---------------
|
||||
#
|
||||
# This class manages subscriber registration and is the entry point
|
||||
# for dispatching various events that occur for modules, such as
|
||||
# recon discovery and exploit success or failure. The framework
|
||||
# and external modules can register themselves as subscribers to
|
||||
# various events such that they can perform custom actions when
|
||||
# a specific event or events occur.
|
||||
#
|
||||
###
|
||||
class EventDispatcher
|
||||
|
||||
def initialize
|
||||
self.exploit_event_subscribers = []
|
||||
self.session_event_subscribers = []
|
||||
self.recon_event_subscribers = []
|
||||
self.subscribers_rwlock = ReadWriteLock.new
|
||||
end
|
||||
|
||||
#
|
||||
# Subscriber registration
|
||||
#
|
||||
|
||||
def add_recon_subscriber(subscriber)
|
||||
add_event_subscriber(recon_event_subscribers, subscriber)
|
||||
end
|
||||
|
||||
def remove_recon_subscriber(subscriber)
|
||||
remove_event_subscriber(recon_event_subscribers, subscriber)
|
||||
end
|
||||
|
||||
def add_exploit_subscriber(subscriber)
|
||||
add_event_subscriber(exploit_event_subscribers, subscriber)
|
||||
end
|
||||
|
||||
def remove_exploit_subscriber(subscriber)
|
||||
remove_event_subscriber(exploit_event_subscribers, subscriber)
|
||||
end
|
||||
|
||||
def add_session_subscriber(subscriber)
|
||||
add_event_subscriber(session_event_subscribers, subscriber)
|
||||
end
|
||||
|
||||
def remove_session_subscriber(subscriber)
|
||||
remove_event_subscriber(session_event_subscribers, subscriber)
|
||||
end
|
||||
|
||||
#
|
||||
# Event dispatching entry point
|
||||
#
|
||||
|
||||
def on_recon_discovery(group, info)
|
||||
subscribers_rwlock.synchronize_read {
|
||||
recon_event_subscribers.each { |subscriber|
|
||||
subscriber.on_recon_discovery(group, info)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def on_exploit_success(exploit, target)
|
||||
subscribers_rwlock.synchronize_read {
|
||||
exploit_event_subscribers.each { |subscriber|
|
||||
subscriber.on_exploit_success(exploit, target)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def on_exploit_failure(exploit, target)
|
||||
subscribers_rwlock.synchronize_read {
|
||||
exploit_event_subscribers.each { |subscriber|
|
||||
subscriber.on_exploit_failure(exploit, target)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def on_session_open(session)
|
||||
subscribers_rwlock.synchronize_read {
|
||||
session_event_subscribers.each { |subscriber|
|
||||
subscriber.on_session_open(session)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def on_session_close(session)
|
||||
subscribers_rwlock.synchronize_read {
|
||||
session_event_subscribers.each { |subscriber|
|
||||
subscriber.on_session_close(session)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def add_event_subscriber(array, subscriber)
|
||||
subscribers_rwlock.synchronize_write {
|
||||
array << subscriber
|
||||
}
|
||||
end
|
||||
|
||||
def remove_event_subscriber(array, subscriber)
|
||||
subscribers_rwlock.synchronize_write {
|
||||
array.delete(subscriber)
|
||||
}
|
||||
end
|
||||
|
||||
attr_accessor :exploit_event_subscribers
|
||||
attr_accessor :session_event_subscribers
|
||||
attr_accessor :recon_event_subscribers
|
||||
attr_accessor :subscribers_rwlock
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
module Msf
|
||||
|
||||
class EncodingException < RuntimeError
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# NoKeyException
|
||||
# --------------
|
||||
#
|
||||
# Thrown when an encoder fails to find a viable encoding key.
|
||||
#
|
||||
###
|
||||
class NoKeyException < EncodingException
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# BadcharException
|
||||
# ----------------
|
||||
#
|
||||
# Thrown when an encoder fails to encode a buffer due to a bad character.
|
||||
#
|
||||
###
|
||||
class BadcharException < EncodingException
|
||||
def initialize(buf, index, stub_size, char)
|
||||
@buf = buf
|
||||
@index = index
|
||||
@stub_size = stub_size
|
||||
@char = char
|
||||
end
|
||||
|
||||
attr_reader :buf, :index, :stub_size, :char
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# MissingOptionError
|
||||
# ------------------
|
||||
#
|
||||
# This exception is thrown when one or more options failed
|
||||
# to pass data store validation. The list of option names
|
||||
# can be obtained through the options attribute.
|
||||
#
|
||||
###
|
||||
class OptionValidateError < ArgumentError
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
attr_reader :options
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
require 'Core'
|
||||
|
||||
module Msf
|
||||
|
||||
module ExploitEvents
|
||||
|
||||
def on_exploit_success(exploit, target)
|
||||
end
|
||||
|
||||
def on_exploit_failure(exploit, target)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,49 @@
|
||||
require 'Core'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Framework
|
||||
# ---------
|
||||
#
|
||||
# This class is the primary context that modules, scripts, and user
|
||||
# interfaces interact with. It ties everything together.
|
||||
#
|
||||
###
|
||||
class Framework
|
||||
|
||||
include Msf::Logging::LogDispatcher
|
||||
|
||||
def initialize()
|
||||
self.events = EventDispatcher.new
|
||||
# self.encoders = EncoderManager.new
|
||||
# self.exploits = ExploitManager.new
|
||||
# self.nops = NopManager.new
|
||||
# self.payloads = PayloadManager.new
|
||||
# self.recon = ReconManager.new
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
attr_reader :events
|
||||
attr_reader :ui
|
||||
attr_reader :encoders
|
||||
attr_reader :exploits
|
||||
attr_reader :nops
|
||||
attr_reader :payloads
|
||||
attr_reader :recon
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :events
|
||||
attr_writer :ui
|
||||
attr_writer :encoders
|
||||
attr_writer :exploits
|
||||
attr_writer :nops
|
||||
attr_writer :payloads
|
||||
attr_writer :recon
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,115 @@
|
||||
require 'Core'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Module
|
||||
# ------
|
||||
#
|
||||
# The module base class is responsible for providing the common interface
|
||||
# that is used to interact with modules at the most basic levels, such as
|
||||
# by inspecting a given module's attributes (name, dsecription, version,
|
||||
# authors, etc) and by managing the module's data store.
|
||||
#
|
||||
###
|
||||
class Module
|
||||
|
||||
def initialize(info)
|
||||
self.module_info = info || {}
|
||||
|
||||
set_defaults
|
||||
|
||||
# Transform some of the fields to arrays as necessary
|
||||
self.author = Transformer.transform(module_info['Author'], Array,
|
||||
[ Author ], 'Author')
|
||||
self.arch = Transformer.transform(module_info['Arch'], Array,
|
||||
[ String ], 'Arch')
|
||||
self.platform = Transformer.transform(module_info['Platform'], Array,
|
||||
[ String ], 'Platform')
|
||||
self.refs = Transformer.transform(module_info['Ref'], Array,
|
||||
[ SiteReference, Reference ], 'Ref')
|
||||
|
||||
# Create and initialize the datastore for this module
|
||||
self.datastore = DataStore.new
|
||||
self.datastore.add_options(info['Options'])
|
||||
self.datastore.add_advanced_options(info['AdvancedOptions'])
|
||||
end
|
||||
|
||||
# Return the module's name
|
||||
def name
|
||||
return module_info['Name']
|
||||
end
|
||||
|
||||
# Return the module's description
|
||||
def description
|
||||
return module_info['Description']
|
||||
end
|
||||
|
||||
# Return the module's version information
|
||||
def version
|
||||
return module_info['Version']
|
||||
end
|
||||
|
||||
# Return the module's abstract type
|
||||
def type
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# Return a comma separated list of author for this module
|
||||
def author_to_s
|
||||
return author.collect { |author| author.to_s }.join(", ")
|
||||
end
|
||||
|
||||
# Enumerate each author
|
||||
def each_author(&block)
|
||||
author.each(&block)
|
||||
end
|
||||
|
||||
# Return a comma separated list of supported architectures, if any
|
||||
def arch_to_s
|
||||
return arch.join(", ")
|
||||
end
|
||||
|
||||
# Enumerate each architecture
|
||||
def each_arch(&block)
|
||||
arch.each(&block)
|
||||
end
|
||||
|
||||
# Return whether or not the module supports the supplied architecture
|
||||
def arch?(what)
|
||||
return arch.index(what) != nil
|
||||
end
|
||||
|
||||
# Return a comma separated list of supported platforms, if any
|
||||
def platform_to_s
|
||||
return platform.join(", ")
|
||||
end
|
||||
|
||||
attr_reader :author, :arch, :platform, :refs, :datastore
|
||||
|
||||
protected
|
||||
|
||||
# Sets the modules unsupplied info fields to their default values
|
||||
def set_defaults
|
||||
{
|
||||
'Name' => 'No module name',
|
||||
'Description' => 'No module description',
|
||||
'Version' => '0',
|
||||
'Author' => nil,
|
||||
'Arch' => nil,
|
||||
'Platform' => nil,
|
||||
'Ref' => nil
|
||||
}.each_pair { |field, value|
|
||||
if (module_info[field] == nil)
|
||||
module_info[field] = value
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
attr_accessor :module_info
|
||||
attr_writer :author, :arch, :platform, :refs, :datastore
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,40 @@
|
||||
require 'Core'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Nop
|
||||
# ---
|
||||
#
|
||||
# This class acts as the base class for all nop generators.
|
||||
#
|
||||
###
|
||||
class Nop < Msf::Module
|
||||
|
||||
#
|
||||
# Stub method for generating a sled with the provided arguments. Derived
|
||||
# Nop implementations must supply a length and can supply one or more of
|
||||
# the following options:
|
||||
#
|
||||
# - Random (true/false)
|
||||
# Indicates that the caller desires random NOPs (if supported).
|
||||
# - SaveRegisters (array)
|
||||
# The list of registers that should not be clobbered by the NOP
|
||||
# generator.
|
||||
# - Badchars (string)
|
||||
# The list of characters that should be avoided by the NOP
|
||||
# generator.
|
||||
#
|
||||
def generate_sled(length, opts)
|
||||
return nil
|
||||
end
|
||||
|
||||
# Default repetition threshold when find nop characters
|
||||
def nop_repeat_threshold
|
||||
return 10000
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# ReconEvents
|
||||
# -----------
|
||||
#
|
||||
# This interface is called by recon modules to notify the framework when
|
||||
# network elements, services, or other types of things recon modules
|
||||
# might discovery.
|
||||
#
|
||||
###
|
||||
module ReconEvents
|
||||
|
||||
def on_recon_discovery(group, info)
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
require 'Core'
|
||||
|
||||
module Msf
|
||||
|
||||
module SessionEvents
|
||||
|
||||
def on_session_open(session)
|
||||
end
|
||||
|
||||
def on_session_close(session)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# Session
|
||||
# -------
|
||||
#
|
||||
#
|
||||
###
|
||||
class Session
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
module Msf
|
||||
|
||||
#
|
||||
# Log severities
|
||||
#
|
||||
LOG_ERROR = 'error'
|
||||
LOG_DEBUG = 'debug'
|
||||
LOG_INFO = 'info'
|
||||
LOG_WARN = 'warn'
|
||||
LOG_RAW = 'raw'
|
||||
|
||||
#
|
||||
# Log levels
|
||||
#
|
||||
LEV_0 = 0
|
||||
LEV_1 = 1
|
||||
LEV_2 = 2
|
||||
LEV_3 = 3
|
||||
|
||||
end
|
||||
@@ -0,0 +1,163 @@
|
||||
require 'thread'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# ReadWriteLock
|
||||
# -------------
|
||||
#
|
||||
# This class implements a read/write lock synchronization
|
||||
# primitive. It is meant to allow for more efficient access to
|
||||
# resources that are more often read from than written to and many
|
||||
# times can have concurrent reader threads. By allowing the reader
|
||||
# threads to lock the resource concurrently rather than serially,
|
||||
# a large performance boost can be seen. Acquiring a write lock
|
||||
# results in exclusive access to the resource and thereby prevents
|
||||
# any read operations during the time that a write lock is acquired.
|
||||
# Only one write lock may be acquired at a time.
|
||||
#
|
||||
###
|
||||
class ReadWriteLock
|
||||
|
||||
def initialize
|
||||
@read_sync_mutex = Mutex.new
|
||||
@write_sync_mutex = Mutex.new
|
||||
@exclusive_mutex = Mutex.new
|
||||
@readers = 0
|
||||
@writer = false
|
||||
end
|
||||
|
||||
# Acquires the read lock for the calling thread
|
||||
def lock_read
|
||||
read_sync_mutex.lock
|
||||
|
||||
begin
|
||||
# If there are a non-zero number of readers and a
|
||||
# writer is waiting to acquire the exclusive lock,
|
||||
# free up the sync mutex temporarily and lock/unlock
|
||||
# the exclusive lock. This is to give the writer
|
||||
# thread a chance to acquire the lock and prevents
|
||||
# it from being constantly starved.
|
||||
if ((@readers > 0) and
|
||||
(@writer))
|
||||
read_sync_mutex.unlock
|
||||
exclusive_mutex.lock
|
||||
exclusive_mutex.unlock
|
||||
read_sync_mutex.lock
|
||||
end
|
||||
|
||||
# Increment the active reader count
|
||||
@readers += 1
|
||||
|
||||
# If we now have just one reader, acquire the exclusive
|
||||
# lock. Track the thread owner so that we release the
|
||||
# lock from within the same thread context later on.
|
||||
if (@readers == 1)
|
||||
exclusive_mutex.lock
|
||||
|
||||
@owner = Thread.current
|
||||
end
|
||||
ensure
|
||||
read_sync_mutex.unlock
|
||||
end
|
||||
end
|
||||
|
||||
# Releases the read lock for the calling thread
|
||||
def unlock_read
|
||||
read_sync_mutex.lock
|
||||
|
||||
begin
|
||||
unlocked = false
|
||||
|
||||
# Keep looping until we've lost this thread's reader
|
||||
# lock
|
||||
while (!unlocked)
|
||||
# If there are no more readers left after this one
|
||||
if (@readers - 1 == 0)
|
||||
# If the calling thread is the owner of the exclusive
|
||||
# reader lock, then let's release that shit!
|
||||
if (Thread.current == @owner)
|
||||
@owner = nil
|
||||
|
||||
exclusive_mutex.unlock
|
||||
end
|
||||
# If there is more than one reader left and this thread is
|
||||
# the owner of the exclusive lock, then keep looping so that
|
||||
# we can eventually unlock the exclusive mutex in this thread's
|
||||
# context
|
||||
elsif (Thread.current == @owner)
|
||||
read_sync_mutex.unlock
|
||||
|
||||
next
|
||||
end
|
||||
|
||||
# Unlocked!
|
||||
unlocked = true
|
||||
|
||||
# Decrement the active reader count
|
||||
@readers -= 1
|
||||
end
|
||||
ensure
|
||||
read_sync_mutex.unlock
|
||||
end
|
||||
end
|
||||
|
||||
# Acquire the exclusive write lock
|
||||
def lock_write
|
||||
write_sync_mutex.lock
|
||||
|
||||
begin
|
||||
@writer = true
|
||||
|
||||
exclusive_mutex.lock
|
||||
|
||||
@owner = Thread.current
|
||||
ensure
|
||||
write_sync_mutex.unlock
|
||||
end
|
||||
end
|
||||
|
||||
# Release the exclusive write lock
|
||||
def unlock_write
|
||||
# If the caller is not the owner of the write lock, then someone is
|
||||
# doing something broken, let's let them know.
|
||||
if (Thread.current != @owner)
|
||||
raise RuntimeError, "Non-owner calling thread attempted to release write lock", caller
|
||||
end
|
||||
|
||||
# Otherwise, release the exclusive write lock
|
||||
@writer = false
|
||||
|
||||
exclusive_mutex.unlock
|
||||
end
|
||||
|
||||
# Synchronize a block for read access
|
||||
def synchronize_read
|
||||
lock_read
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
unlock_read
|
||||
end
|
||||
end
|
||||
|
||||
# Synchronize a block for write access
|
||||
def synchronize_write
|
||||
lock_write
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
unlock_write
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :read_sync_mutex
|
||||
attr_accessor :write_sync_mutex
|
||||
attr_accessor :exclusive_mutex
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,87 @@
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Transformer - more than meets the eye!
|
||||
# -----------
|
||||
#
|
||||
# This class, aside from having a kickass name, is responsible for translating
|
||||
# object instances of one or more types into a single list instance of one or
|
||||
# more types. This is useful for translating object instances that be can
|
||||
# either strings or an array of strings into an array of strings, for
|
||||
# instance. It lets you make things take a uniform structure in an abstract
|
||||
# manner.
|
||||
#
|
||||
###
|
||||
class Transformer
|
||||
|
||||
# Translates the object instance supplied in src_instance to an instance of
|
||||
# dst_class. The dst_class parameter's instance must support the <<
|
||||
# operator. An example call to this method looks something like:
|
||||
#
|
||||
# Transformer.transform(string, Array, [ String ], target)
|
||||
def Transformer.transform(src_instance, dst_class, supported_classes,
|
||||
target = nil)
|
||||
dst_instance = dst_class.new
|
||||
|
||||
if (src_instance.kind_of?(Array))
|
||||
src_instance.each { |src_inst|
|
||||
Transformer.transform_single(src_inst, dst_instance,
|
||||
supported_classes, target)
|
||||
}
|
||||
elsif (!src_instance.kind_of?(NilClass))
|
||||
Transformer.transform_single(src_instance, dst_instance,
|
||||
supported_classes, target)
|
||||
end
|
||||
|
||||
return dst_instance
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Transform a single source instance.
|
||||
def Transformer.transform_single(src_instance, dst_instance,
|
||||
supported_classes, target)
|
||||
# If the src instance's class is supported, just add it to the dst
|
||||
# instance
|
||||
if (supported_classes.include?(src_instance.class))
|
||||
dst_instance << src_instance
|
||||
# If the source instance is a string, query each of the supported
|
||||
# classes to see if they can serialize it to their particular data
|
||||
# type.
|
||||
elsif (src_instance.kind_of?(String))
|
||||
new_src_instance = nil
|
||||
|
||||
# Walk each supported class calling from_s if exported
|
||||
supported_classes.each { |sup_class|
|
||||
new_src_instance = sup_class.from_s(src_instance)
|
||||
|
||||
if (new_src_instance != nil)
|
||||
dst_instance << new_src_instance
|
||||
break
|
||||
end
|
||||
}
|
||||
|
||||
# If we don't have a valid new src instance, then we suck
|
||||
if (new_src_instance == nil)
|
||||
bomb_translation(src_instance, target)
|
||||
end
|
||||
# Otherwise, bomb translation
|
||||
else
|
||||
bomb_translation(src_instance, target)
|
||||
end
|
||||
end
|
||||
|
||||
def Transformer.bomb_translation(src_instance, target)
|
||||
error = "Invalid source class (#{src_instance.class})"
|
||||
|
||||
if (target != nil)
|
||||
error += " for #{target}"
|
||||
end
|
||||
|
||||
raise ArgumentError, error, caller
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,68 @@
|
||||
require 'Shared'
|
||||
|
||||
module Msf
|
||||
module Logging
|
||||
|
||||
###
|
||||
#
|
||||
# LogDispatcher
|
||||
# -------------
|
||||
#
|
||||
# This interface is included in the Framework class and is used to provide a
|
||||
# common interface for dispatching logs to zero or more registered log sinks.
|
||||
# Log sinks are typically backed against arbitrary storage mediums, such as a
|
||||
# flatfile, a database, or the console. The log dispatcher itself is really
|
||||
# just a log sink that backs itself against zero or more log sinks rather than
|
||||
# a file, database, or other persistent storage.
|
||||
#
|
||||
###
|
||||
module LogDispatcher
|
||||
|
||||
include Msf::Logging::LogSink
|
||||
|
||||
def initialize()
|
||||
initialize_log_dispatcher
|
||||
end
|
||||
|
||||
#
|
||||
# Log sink registration
|
||||
#
|
||||
|
||||
def add_log_sink(sink)
|
||||
log_sinks_rwlock.synchronize_write {
|
||||
log_sinks << sink
|
||||
}
|
||||
end
|
||||
|
||||
def remove_log_sink(sink)
|
||||
log_sinks_rwlock.synchronize_write {
|
||||
sink.cleanup
|
||||
|
||||
log_sinks.delete(sink)
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Log dispatching
|
||||
#
|
||||
protected
|
||||
|
||||
def initialize_log_dispatcher
|
||||
self.log_sinks = []
|
||||
self.log_sinks_rwlock = ReadWriteLock.new
|
||||
end
|
||||
|
||||
def log(sev, level, msg, from)
|
||||
log_sinks_rwlock.synchronize_read {
|
||||
log_sinks.each { |sink|
|
||||
sink.dispatch_log(sev, level, msg, from)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
attr_accessor :log_sinks
|
||||
attr_accessor :log_sinks_rwlock
|
||||
|
||||
end
|
||||
|
||||
end; end
|
||||
@@ -0,0 +1,55 @@
|
||||
require 'Shared/Constants'
|
||||
|
||||
module Msf
|
||||
module Logging
|
||||
|
||||
###
|
||||
#
|
||||
# LogSink
|
||||
# -------
|
||||
#
|
||||
# This abstract interface is what must be implemented by any class
|
||||
# that would like to register as a log sink on a given LogDispatcher
|
||||
# instance, such as the Framework object.
|
||||
#
|
||||
###
|
||||
module LogSink
|
||||
|
||||
def cleanup
|
||||
end
|
||||
|
||||
def dlog(level, msg, from = caller)
|
||||
log(LOG_DEBUG, level, msg, from)
|
||||
end
|
||||
|
||||
def elog(level, msg, from = caller)
|
||||
log(LOG_ERROR, level, msg, from)
|
||||
end
|
||||
|
||||
def wlog(level, msg, from = caller)
|
||||
log(LOG_WARN, level, msg, from)
|
||||
end
|
||||
|
||||
def ilog(level, msg, from = caller)
|
||||
log(LOG_INFO, level, msg, from)
|
||||
end
|
||||
|
||||
def rlog(msg)
|
||||
log(LOG_RAW, 0, msg, nil)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def log(sev, level, msg, from)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def get_current_timestamp
|
||||
return Time.now.strftime("%m/%d/%Y %H:%M:%S")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end; end
|
||||
|
||||
require 'Shared/Logging/Sinks/Flatfile'
|
||||
@@ -0,0 +1,44 @@
|
||||
module Msf
|
||||
module Logging
|
||||
module Sinks
|
||||
|
||||
###
|
||||
#
|
||||
# Flatfile
|
||||
# --------
|
||||
#
|
||||
# This class implements the LogSink interface and backs it against a
|
||||
# file on disk.
|
||||
#
|
||||
###
|
||||
class Flatfile
|
||||
|
||||
include Msf::Logging::LogSink
|
||||
|
||||
def initialize(file)
|
||||
self.fd = File.new(file, "a")
|
||||
|
||||
ilog(0, "Logging initialized.")
|
||||
end
|
||||
|
||||
def cleanup
|
||||
ilog(0, "Logging finished.")
|
||||
|
||||
fd.close
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def log(sev, level, msg, from)
|
||||
if (sev == LOG_RAW)
|
||||
fd.write(msg)
|
||||
else
|
||||
fd.write("[#{get_current_timestamp}] (sev=#{sev},level=#{level}): #{msg}\n")
|
||||
end
|
||||
end
|
||||
|
||||
attr_accessor :fd
|
||||
|
||||
end
|
||||
|
||||
end; end; end
|
||||
Reference in New Issue
Block a user