clean up some rdoc comments. odd that rdoc doesn't appear to pick up ApiConstants at all...

git-svn-id: file:///home/svn/framework3/trunk@13576 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
James Lee 2011-08-17 23:00:28 +00:00
parent a746067089
commit f99429138e
4 changed files with 164 additions and 80 deletions

View File

@ -8,13 +8,21 @@ module Extensions
module Stdapi
module Railgun
#
# A container holding useful Windows API Constants.
#
class ApiConstants
# This will be lazily loaded in self.manager
@@manager = nil
# Mutex to ensure we don't add constants more than once via thread races.
@@manager_semaphore = Mutex.new
# provides a frozen constant manager for the constants defined in self.add_constants
#
# Provides a frozen constant manager for the constants defined in
# self.add_constants
#
def self.manager
# The first check for nil is to potentially skip the need to synchronize
@ -35,6 +43,9 @@ class ApiConstants
return @@manager
end
#
# Slurp in a giant list of known constants.
#
def self.add_constants(win_const_mgr)
win_const_mgr.add_const('MCI_DGV_SETVIDEO_TINT',0x00004003)
win_const_mgr.add_const('EVENT_TRACE_FLAG_PROCESS',0x00000001)

View File

@ -36,7 +36,7 @@ module Stdapi
module Railgun
#
# represents a DLL, e.g. kernel32.dll
# Represents a DLL, e.g. kernel32.dll
#
class DLL
@ -62,6 +62,15 @@ class DLL
return functions[name]
end
#
# Perform a function call in this DLL on the remote system.
#
# Returns a Hash containing the return value, the result of GetLastError(),
# and any +inout+ parameters.
#
# Raises an exception if +func_symbol+ is not a known function in this DLL,
# i.e., it hasn't been defined in a Def.
#
def call_function(func_symbol, args, client)
func_name = func_symbol.to_s
@ -74,22 +83,29 @@ class DLL
return process_function_call(function, args, client)
end
# syntax for params:
# add_function("MessageBoxW", # name
# "DWORD", # return value
# [["DWORD","hWnd","in"], # params
# ["PWCHAR","lpText","in"],
# ["PWCHAR","lpCaption","in"],
# ["DWORD","uType","in"],
# ])
#
# Define a function for this DLL.
#
# Every function argument is described by a tuple (type,name,direction)
#
# windows_name: Use it when the actual windows name is different from the ruby variable
# for example when the actual func name is myFunc@4
# or when you want to create an alternative version of an existing function
# Example:
# add_function("MessageBoxW", # name
# "DWORD", # return value
# [ # params
# ["DWORD","hWnd","in"],
# ["PWCHAR","lpText","in"],
# ["PWCHAR","lpCaption","in"],
# ["DWORD","uType","in"],
# ])
#
# Use +windows_name+ when the actual windows name is different from the
# ruby variable. You might need to do this for example when the actual
# func name is myFunc@4 or when you want to create an alternative version
# of an existing function.
#
# When the new function is called it will return a list containing the
# return value and all inout params. See #call_function.
#
# When new function is called it will return a list containing the return value and all inout params
def add_function(name, return_type, params, windows_name=nil)
if windows_name == nil
windows_name = name
@ -99,7 +115,6 @@ class DLL
private
# called when a function like "MessageBoxW" is called
def process_function_call(function, args, client)
raise "#{function.params.length} arguments expected. #{args.length} arguments provided." unless args.length == function.params.length

View File

@ -53,13 +53,18 @@ module Railgun
# The Railgun class to dynamically expose the Windows API.
#
class Railgun
# If you want to add additional DLL definitions to be preloaded
# create a definition class 'rex/post/meterpreter/extensions/stdapi/railgun/def/'
# Naming is important and should follow convention.
# For example, if your dll's name was "my_dll"
# file name - def_my_dll.rb
# class name - Def_my_dll
# entry below - 'my_dll'
#
# Railgun::DLL's that have builtin definitions.
#
# If you want to add additional DLL definitions to be preloaded create a
# definition class 'rex/post/meterpreter/extensions/stdapi/railgun/def/'.
# Naming is important and should follow convention. For example, if your
# dll's name was "my_dll"
# file name:: def_my_dll.rb
# class name:: Def_my_dll
# entry below:: 'my_dll'
#
BUILTIN_DLLS = [
'kernel32',
'ntdll',
@ -73,27 +78,21 @@ class Railgun
].freeze
##
# dlls
#
# Returns a hash containing DLLs added to this instance with self.add_dll
# as well as references to any frozen cached dlls added directly in self.get_dll
# and copies of any frozen dlls (added directly with self.add_function)
# that the user attempted to modify with self.add_function
# Returns a Hash containing DLLs added to this instance with #add_dll
# as well as references to any frozen cached dlls added directly in #get_dll
# and copies of any frozen dlls (added directly with #add_function)
# that the user attempted to modify with #add_function.
#
# Keys are friendly DLL names and values are the corresponding DLL instance
attr_accessor :dlls
##
# client
#
# Contains a reference to the client that corresponds to this instance of railgun
attr_accessor :client
##
# @@cached_dlls
#
# These DLLs are loaded lazily and then shared amongst all railgun instances.
# For safety reasons this variable should only be read/written within get_dll.
# For safety reasons this variable should only be read/written within #get_dll.
@@cached_dlls = {}
# if you are going to touch @@cached_dlls, wear protection
@ -104,6 +103,9 @@ class Railgun
self.dlls = {}
end
#
# Return this Railgun's Util instance.
#
def util
if @util.nil?
@util = Util.new(self, client.platform)
@ -112,12 +114,19 @@ class Railgun
return @util
end
#
# Return this Railgun's WinConstManager instance, initially populated with
# constants defined in ApiConstants.
#
def constant_manager
# Loads lazily
return ApiConstants.manager
end
# read data from a memory address on the host (useful for working with LPVOID parameters)
#
# Read data from a memory address on the host (useful for working with
# LPVOID parameters)
#
def memread(address, length)
raise "Invalid parameters." if(not address or not length)
@ -135,7 +144,10 @@ class Railgun
return nil
end
# write data to a memory address on the host (useful for working with LPVOID parameters)
#
# Write data to a memory address on the host (useful for working with
# LPVOID parameters)
#
def memwrite(address, data, length)
raise "Invalid parameters." if(not address or not data or not length)
@ -154,9 +166,13 @@ class Railgun
return false
end
# adds a function to an existing DLL-definition.
# if the DLL-definition is frozen (idealy this should be true for all cached dlls)
# an unfrozen copy is created and used henceforth for this instance.
#
# Adds a function to an existing DLL definition.
#
# If the DLL definition is frozen (ideally this should be the case for all
# cached dlls) an unfrozen copy is created and used henceforth for this
# instance.
#
def add_function(dll_name, function_name, return_type, params, windows_name=nil)
unless known_dll_names.include?(dll_name)
@ -177,9 +193,16 @@ class Railgun
dll.add_function(function_name, return_type, params, windows_name)
end
# adds a function to an existing DLL-definition
# you can override the dll name if you want to include a path or the DLL name contains
# non-ruby-approved characters
#
# Adds a DLL to this Railgun.
#
# The +windows_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
# non-ruby-approved characters.
#
# Raises an exception if a dll with the given name has already been
# defined.
#
def add_dll(dll_name, windows_name=dll_name)
if dlls.has_key? dll_name
@ -194,8 +217,11 @@ class Railgun
return BUILTIN_DLLS | dlls.keys
end
# Attempts to provide a DLL instance of the given name. Handles lazy loading and caching
# Note that if a DLL of the given name does not exist, then nil is returned
#
# Attempts to provide a DLL instance of the given name. Handles lazy
# loading and caching. Note that if a DLL of the given name does not
# exist, returns nil
#
def get_dll(dll_name)
# If the DLL is not local, we now either load it from cache or load it lazily.
@ -225,11 +251,13 @@ class Railgun
return dlls[dll_name]
end
# we fake having members like user32 and kernel32.
#
# Fake having members like user32 and kernel32.
# reason is that
# ...user32.MessageBoxW()
# is prettier than
# ...dlls["user32"].functions["MessageBoxW"]()
#
def method_missing(dll_symbol, *args)
dll_name = dll_symbol.to_s
@ -242,12 +270,16 @@ class Railgun
return DLLWrapper.new(dll, client)
end
# Give the programmer access to constants
#
# Return a Windows constant matching +str+.
#
def const(str)
return constant_manager.parse(str)
end
#
# The multi-call shorthand (["kernel32", "ExitProcess", [0]])
#
def multi(functions)
if @multicaller.nil?
@multicaller = MultiCaller.new(client, self)

View File

@ -6,6 +6,10 @@ module Meterpreter
module Extensions
module Stdapi
module Railgun
#
# Utility methods and constants for dealing with most types of variables.
#
class Util
# Bring in some useful string manipulation utility functions
@ -29,8 +33,10 @@ class Util
:wchar_t => 2,
}
# Maps a data type to its corresponding primitive or special type :pointer
# Note, primitive types are mapped to themselves
#
# Maps a data type to its corresponding primitive or special type
# +:pointer+. Note, primitive types are mapped to themselves.
#
# typedef info: http://msdn.microsoft.com/en-us/library/aa383751(v=vs.85).aspx
TYPE_DEFINITIONS = {
##
@ -312,7 +318,9 @@ class Util
@is_64bit = is_64bit_platform?(platform)
end
#
# Given a packed pointer, unpacks it according to architecture
#
def unpack_pointer(packed_pointer)
if is_64bit
# XXX: Only works if attacker and victim are like-endianed
@ -322,14 +330,15 @@ class Util
end
end
###
# Summary: Returns true if pointer will be considered a 'null' pointer
#
# If given nil, returns true
# If given 0, returns true
# If given a string, if 0 after unpacking, returns true
# Returns true if +pointer+ will be considered a 'null' pointer.
#
# If +pointer+ is nil or 0, returns true
# If +pointer+ is a String, if 0 after unpacking, returns true
# false otherwise
##
#
# See #unpack_pointer
#
def is_null_pointer(pointer)
if pointer.class == String
pointer = unpack_pointer(pointer)
@ -338,12 +347,13 @@ class Util
return pointer.nil? || pointer == 0
end
###
# Summary: Reads null-terminated unicode strings from memory.
#
# Given a pointer to a null terminated array of WCHARs, return a ruby string
# Null pointers cause an empty string to be returned
##
# Reads null-terminated unicode strings from memory.
#
# Given a pointer to a null terminated array of WCHARs, return a ruby
# String. If +pointer+ is NULL (see #is_null_pointer) returns an empty
# string.
#
def read_wstring(pointer, length = nil)
# Return an empty string for null pointers
if is_null_pointer(pointer)
@ -364,12 +374,12 @@ class Util
return str
end
###
# Summary: Read a given number of bytes from memory or from a provided buffer.
#
# If 'buffer' is not provided, read 'size' bytes from the client's memory
# If 'buffer' is provided, reads 'size' characters from the index of 'address'
##
# Read a given number of bytes from memory or from a provided buffer.
#
# If +buffer+ is not provided, read +size+ bytes from the client's memory.
# If +buffer+ is provided, reads +size+ characters from the index of +address+.
#
def memread(address, size, buffer = nil)
if buffer.nil?
return railgun.memread(address, size)
@ -378,12 +388,16 @@ class Util
end
end
#
# Read and unpack a pointer from the given buffer at a given offset
#
def read_pointer(buffer, offset = 0)
unpack_pointer(buffer[offset, (offset + pointer_size)])
end
#
# Reads data structures and several windows data types
#
def read_data(type, position, buffer = nil)
if buffer.nil?
buffer = memread(position, sizeof_type(type))
@ -426,8 +440,11 @@ class Util
return raw
end
# Read 'length' number of instances of 'type' from 'bufptr'
# bufptr is an index in 'buffer' or, if buffer is nil, a memory address
#
# Read +length+ number of instances of +type+ from +bufptr+ .
#
# +bufptr+ is an index in +buffer+ or, if +buffer+ is nil, a memory address
#
def read_array(type, length, bufptr, buffer = nil)
if length <= 0
return []
@ -448,8 +465,10 @@ class Util
end
end
# construct the data structure described in 'definition' from 'buffer'
# starting from the index 'offset'
#
# Construct the data structure described in +definition+ from +buffer+
# starting from the index +offset+
#
def read_struct(definition, buffer, offset = 0)
data = {}
@ -520,7 +539,9 @@ class Util
raise "Unable to determine size for type #{type}."
end
# Calculates the size of the struct after alignment
#
# Calculates the size of +struct+ after alignment.
#
def sizeof_struct(struct)
offsets = struct_offsets(struct, 0)
last_data_size = sizeof_type(struct.last[1])
@ -529,9 +550,11 @@ class Util
return size_no_padding + calc_padding(size_no_padding)
end
# Given a description of a data structure, returns an array containing
#
# Given a description of a data structure, returns an Array containing
# the offset from the beginning for each subsequent element, taking into
# consideration alignment and padding
# consideration alignment and padding.
#
def struct_offsets(definition, offset)
padding = 0
offsets = []
@ -558,7 +581,9 @@ class Util
is_64bit ? 8 : 4
end
# Bytes that needed to be added to be aligned
#
# Number of bytes that needed to be added to be aligned.
#
def calc_padding(offset)
align = required_alignment
@ -571,9 +596,11 @@ class Util
end
end
#
# Given an explicit array definition (e.g. BYTE[23]) return size (e.g. 23) and
# and type (e.g. BYTE). If a constant is given, attempt to resolve it
# that constant
# and +type+ (e.g. BYTE). If a constant is given, attempt to resolve it
# that constant.
#
def split_array_type(type)
if type =~ /^(\w+)\[(\w+)\]$/
element_type = $1
@ -595,18 +622,17 @@ class Util
platform =~ /win64/
end
###
# Summary:
# Evaluates a bit field, returning a hash representing the meaning
# and state of each bit.
#
# Evaluates a bit field, returning a hash representing the meaning and
# state of each bit.
#
# Parameters:
# value: a bit field represented by a Fixnum
# mappings: { 'WINAPI_CONSTANT_NAME' => :descriptive_symbol, ... }
# +value+:: a bit field represented by a Fixnum
# +mappings+:: { 'WINAPI_CONSTANT_NAME' => :descriptive_symbol, ... }
#
# Returns:
# { :descriptive_symbol => true/false, ... }
##
#
def judge_bit_field(value, mappings)
flags = {}
rg = railgun