From f99429138ef96d16596f1a48c93eb92db3702528 Mon Sep 17 00:00:00 2001 From: James Lee Date: Wed, 17 Aug 2011 23:00:28 +0000 Subject: [PATCH] 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 --- .../stdapi/railgun/api_constants.rb | 13 ++- .../extensions/stdapi/railgun/dll.rb | 43 ++++++--- .../extensions/stdapi/railgun/railgun.rb | 92 ++++++++++++------ .../extensions/stdapi/railgun/util.rb | 96 ++++++++++++------- 4 files changed, 164 insertions(+), 80 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/api_constants.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/api_constants.rb index 3386a5fa24..5cc1113f77 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/api_constants.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/api_constants.rb @@ -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) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb index 96ee79f6df..93cba44829 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/dll.rb @@ -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 diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb index aa90f49ef9..f48c126f7a 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb @@ -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) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb b/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb index b6c7fca727..7c8ea5eddc 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb @@ -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