Refactor railgun "DLL" references to library

bug/bundler_fix
Spencer McIntyre 2017-06-27 17:34:06 -04:00
parent ea83cb0bb6
commit 0da9f4d64a
4 changed files with 75 additions and 71 deletions

View File

@ -7,10 +7,10 @@ module Windows
module Railgun module Railgun
# Go through each dll and add a corresponding convenience method of the same name # Go through each dll and add a corresponding convenience method of the same name
Rex::Post::Meterpreter::Extensions::Stdapi::Railgun::Railgun::BUILTIN_DLLS['windows'].each do |api| Rex::Post::Meterpreter::Extensions::Stdapi::Railgun::Railgun::BUILTIN_LIBRARIES['windows'].each do |api|
# We will be interpolating within an eval. We exercise due paranoia. # We will be interpolating within an eval. We exercise due paranoia.
unless api.to_s =~ /^\w+$/ unless api.to_s =~ /^\w+$/
print_error 'Something is seriously wrong with Railgun.BUILTIN_DLLS list' print_error 'Something is seriously wrong with Railgun.BUILTIN_LIBRARIES list'
next next
end end

View File

@ -69,7 +69,7 @@ class Railgun
# class name: Def_my_dll # class name: Def_my_dll
# entry below: 'my_dll' # entry below: 'my_dll'
# #
BUILTIN_DLLS = { BUILTIN_LIBRARIES = {
'linux' => [ 'linux' => [
'libc' 'libc'
].freeze, ].freeze,
@ -95,33 +95,34 @@ class Railgun
}.freeze }.freeze
## ##
# Returns a Hash containing DLLs added to this instance with #add_dll # Returns a Hash containing DLLs added to this instance with #add_library as
# as well as references to any frozen cached dlls added directly in #get_dll # well as references to any frozen cached libraries added directly in
# and copies of any frozen dlls (added directly with #add_function) # #get_library and copies of any frozen libraries (added directly with
# that the user attempted to modify with #add_function. # #add_function) that the user attempted to modify with #add_function.
# #
# Keys are friendly DLL names and values are the corresponding DLL instance # Keys are friendly library names and values are the corresponding library instance
attr_accessor :dlls attr_accessor :libraries
## ##
# Contains a reference to the client that corresponds to this instance of railgun # Contains a reference to the client that corresponds to this instance of railgun
attr_accessor :client attr_accessor :client
## ##
# These DLLs are loaded lazily and then shared amongst all railgun instances. # These libraries are loaded lazily and then shared amongst all railgun
# For safety reasons this variable should only be read/written within #get_dll. # instances. For safety reasons this variable should only be read/written
@@cached_dlls = {} # within #get_library.
@@cached_libraries = {}
# if you are going to touch @@cached_dlls, wear protection # if you are going to touch @@cached_libraries, wear protection
@@cache_semaphore = Mutex.new @@cache_semaphore = Mutex.new
def initialize(client) def initialize(client)
self.client = client self.client = client
self.dlls = {} self.libraries = {}
end end
def self.builtin_dlls def self.builtin_libraries
BUILTIN_DLLS[client.platform] BUILTIN_LIBRARIES[client.platform]
end end
# #
@ -196,106 +197,109 @@ class Railgun
end end
# #
# Adds a function to an existing DLL definition. # Adds a function to an existing library definition.
# #
# If the DLL definition is frozen (ideally this should be the case for all # If the library definition is frozen (ideally this should be the case for all
# cached dlls) an unfrozen copy is created and used henceforth for this # cached libraries) an unfrozen copy is created and used henceforth for this
# instance. # instance.
# #
def add_function(dll_name, function_name, return_type, params, remote_name=nil, calling_conv="stdcall") def add_function(lib_name, function_name, return_type, params, remote_name=nil, calling_conv='stdcall')
unless known_library_names.include?(lib_name)
unless known_dll_names.include?(dll_name) raise "Library #{lib_name} not found. Known libraries: #{PP.pp(known_library_names, '')}"
raise "DLL #{dll_name} not found. Known DLLs: #{PP.pp(known_dll_names, "")}"
end end
dll = get_dll(dll_name) lib = get_library(lib_name)
# For backwards compatibility, we ensure the dll is thawed # For backwards compatibility, we ensure the library is thawed
if dll.frozen? if lib.frozen?
# Duplicate not only the dll, but its functions as well. Frozen status will be lost # Duplicate not only the library, but its functions as well, frozen status will be lost
dll = Marshal.load(Marshal.dump(dll)) lib = Marshal.load(Marshal.dump(lib))
# Update local dlls with the modifiable duplicate # Update local libraries with the modifiable duplicate
dlls[dll_name] = dll libraries[lib_name] = lib
end end
dll.add_function(function_name, return_type, params, remote_name, calling_conv) lib.add_function(function_name, return_type, params, remote_name, calling_conv)
end end
# #
# Adds a DLL to this Railgun. # Adds a library to this Railgun.
# #
# The +remote_name+ is the name used on the remote system and should be # The +remote_name+ is the name used on the remote system and should be
# set appropriately if you want to include a path or the DLL name contains # set appropriately if you want to include a path or the library name contains
# non-ruby-approved characters. # non-ruby-approved characters.
# #
# Raises an exception if a dll with the given name has already been # Raises an exception if a library with the given name has already been
# defined. # defined.
# #
def add_dll(dll_name, remote_name=dll_name) def add_library(lib_name, remote_name=lib_name)
if libraries.has_key? lib_name
if dlls.has_key? dll_name raise "A library of name #{lib_name} has already been loaded."
raise "A DLL of name #{dll_name} has already been loaded."
end end
dlls[dll_name] = DLL.new(remote_name, constant_manager) libraries[lib_name] = DLL.new(remote_name, constant_manager)
end end
alias_method :add_dll, :add_library
def known_library_names
def known_dll_names return BUILTIN_LIBRARIES[client.platform] | libraries.keys
return BUILTIN_DLLS[client.platform] | dlls.keys
end end
# #
# Attempts to provide a DLL instance of the given name. Handles lazy # Attempts to provide a library instance of the given name. Handles lazy
# loading and caching. Note that if a DLL of the given name does not # loading and caching. Note that if a library of the given name does not exist
# exist, returns nil # then nil is returned.
# #
def get_dll(dll_name) def get_library(lib_name)
# If the DLL is not local, we now either load it from cache or load it lazily. # If the library is not local, we now either load it from cache or load it
# In either case, a reference to the dll is stored in the collection "dlls" # lazily. In either case, a reference to the library is stored in the
# If the DLL can not be found/created, no actions are taken # collection "libraries". If the library can not be found/created, no
unless dlls.has_key? dll_name # actions are taken.
# We read and write to @@cached_dlls and rely on state consistency unless libraries.has_key? lib_name
# use a platform-specific name for caching to avoid conflicts with
# libraries that exist on multiple platforms, e.g. libc.
cached_lib_name = "#{client.platform}.#{lib_name}"
# We read and write to @@cached_libraries and rely on state consistency
@@cache_semaphore.synchronize do @@cache_semaphore.synchronize do
if @@cached_dlls.has_key? dll_name if @@cached_libraries.has_key? cached_lib_name
dlls[dll_name] = @@cached_dlls[dll_name] libraries[lib_name] = @@cached_libraries[cached_lib_name]
elsif BUILTIN_DLLS[client.platform].include? dll_name elsif BUILTIN_LIBRARIES[client.platform].include? lib_name
# I highly doubt this case will ever occur, but I am paranoid # I highly doubt this case will ever occur, but I am paranoid
if dll_name !~ /^\w+$/ if lib_name !~ /^\w+$/
raise "DLL name #{dll_name} is bad. Correct Railgun::BUILTIN_DLLS" raise "Library name #{lib_name} is bad. Correct Railgun::BUILTIN_LIBRARIES['#{client.platform}']"
end end
require "rex/post/meterpreter/extensions/stdapi/railgun/def/#{client.platform}/def_#{dll_name}" require "rex/post/meterpreter/extensions/stdapi/railgun/def/#{client.platform}/def_#{lib_name}"
dll = Def.const_get("Def_#{client.platform}_#{dll_name}").create_dll(constant_manager).freeze lib = Def.const_get("Def_#{client.platform}_#{lib_name}").create_dll(constant_manager).freeze
@@cached_dlls[dll_name] = dll @@cached_libraries[cached_lib_name] = lib
dlls[dll_name] = dll libraries[lib_name] = lib
end end
end end
end end
return dlls[dll_name] return libraries[lib_name]
end end
alias_method :get_dll, :get_library
# #
# Fake having members like user32 and kernel32. # Fake having members like user32 and kernel32.
# reason is that # reason is that
# ...user32.MessageBoxW() # ...user32.MessageBoxW()
# is prettier than # is prettier than
# ...dlls["user32"].functions["MessageBoxW"]() # ...libraries["user32"].functions["MessageBoxW"]()
# #
def method_missing(dll_symbol, *args) def method_missing(lib_symbol, *args)
dll_name = dll_symbol.to_s lib_name = lib_symbol.to_s
unless known_dll_names.include? dll_name unless known_library_names.include? lib_name
raise "DLL #{dll_name} not found. Known DLLs: #{PP.pp(known_dll_names, '')}" raise "Library #{lib_name} not found. Known libraries: #{PP.pp(known_library_names, '')}"
end end
dll = get_dll(dll_name) lib = get_library(lib_name)
return DLLWrapper.new(dll, client) return DLLWrapper.new(lib, client)
end end
# #

View File

@ -74,8 +74,8 @@ class MetasploitModule < Msf::Post
end end
def init_railgun_defs def init_railgun_defs
unless session.railgun.dlls.has_key?('libgnome_keyring') unless session.railgun.libraries.has_key?('libgnome_keyring')
session.railgun.add_dll('libgnome_keyring', 'libgnome-keyring.so.0') session.railgun.add_library('libgnome_keyring', 'libgnome-keyring.so.0')
end end
session.railgun.add_function( session.railgun.add_function(
'libgnome_keyring', 'libgnome_keyring',

View File

@ -42,7 +42,7 @@ class MetasploitModule < Msf::Post
def add_railgun_urlmon def add_railgun_urlmon
if client.railgun.dlls.find_all {|d| d.first == 'urlmon'}.empty? if client.railgun.libraries.find_all {|d| d.first == 'urlmon'}.empty?
session.railgun.add_dll('urlmon','urlmon') session.railgun.add_dll('urlmon','urlmon')
session.railgun.add_function( session.railgun.add_function(
'urlmon', 'URLDownloadToFileW', 'DWORD', 'urlmon', 'URLDownloadToFileW', 'DWORD',