class Rex::Post::Meterpreter::Extensions::Stdapi::Railgun::Util
Utility methods and constants for dealing with most types of variables.
Constants
- PRIMITIVE_TYPE_SIZES
Data type size info: msdn.microsoft.com/en-us/library/s3f49ktz(v=vs.80).aspx
- TYPE_DEFINITIONS
Maps a data type to its corresponding primitive or special type
:pointer
. Note, primitive types are mapped to themselves.typedef info: msdn.microsoft.com/en-us/library/aa383751(v=vs.85).aspx
Attributes
Public Class Methods
param 'railgun' is a Railgun
instance. param 'platform' is a value like client.platform
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 316 def initialize(railgun, platform) @railgun = railgun @is_64bit = is_64bit_platform?(platform) end
Public Instance Methods
Number of bytes that needed to be added to be aligned.
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 607 def calc_padding(offset) align = required_alignment # If offset is not aligned... if (offset % align) != 0 # Calculate padding needed to be aligned align - (offset & (align - 1)) else 0 end end
Returns true if given platform has 64bit architecture expects client.platform
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 640 def is_64bit_platform?(platform) platform =~ /win64/ end
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
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 342 def is_null_pointer(pointer) if pointer.kind_of? String pointer = unpack_pointer(pointer) end return pointer.nil? || pointer == 0 end
Returns true if the data type is a pointer, false otherwise
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 508 def is_pointer_type?(type) return TYPE_DEFINITIONS[type] == :pointer end
Returns true if the type passed describes a data structure, false otherwise
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 519 def is_struct_type?(type) return type.kind_of? Array end
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, ... }
Returns:
{ :descriptive_symbol => true/false, ... }
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 655 def judge_bit_field(value, mappings) flags = {} rg = railgun mappings.each do |constant_name, key| flags[key] = (value & rg.const(constant_name)) != 0 end flags end
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
.
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 403 def memread(address, size, buffer = nil) if buffer.nil? return railgun.memread(address, size) else return buffer[address .. (address + size - 1)] end end
Returns the pointer size for this architecture
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 525 def pointer_size is_64bit ? 8 : 4 end
Read length
number of instances of type
from bufptr
.
bufptr
is an index in buffer
or, if buffer
is nil, a memory address
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 468 def read_array(type, length, bufptr, buffer = nil) if length <= 0 return [] end size = sizeof_type(type) # Grab the bytes that the array consists of buffer = memread(bufptr, size * length, buffer) offset = 0 1.upto(length).map do |n| data = read_data(type, offset, buffer) offset = offset + size data end end
Reads data structures and several windows data types
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 421 def read_data(type, position, buffer = nil) if buffer.nil? buffer = memread(position, sizeof_type(type)) position = 0 end # If we're asked to read a data structure, deligate to read_struct if is_struct_type?(type) return read_struct(type, buffer, position) end # If the type is an array with a given size... # BYTE[3] for example or BYTE[ENCRYPTED_PWLEN] or even PDWORD[23] if is_array_type?(type) # Separate the element type from the size of the array element_type, length = split_array_type(type) # Have read_array take care of the rest return read_array(element_type, length, position, buffer) end size = sizeof_type(type) raw = memread(position, size, buffer) # read/unpack data for the types we have hard-coded support for case type when :LPWSTR # null-terminated string of 16-bit Unicode characters return read_wstring(read_pointer(raw)) when :DWORD # Both on x86 and x64, DWORD is 32 bits return raw.unpack('V').first when :BOOL return raw.unpack('V').first == 1 when :LONG return raw.unpack('V').first end #If nothing worked thus far, return it raw return raw end
Read and unpack a pointer from the given buffer at a given offset
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 414 def read_pointer(buffer, offset = 0) unpack_pointer(buffer[offset, (offset + pointer_size)]) end
Reads null-terminated ASCII strings from memory.
Given a pointer to a null terminated array of CHARs, return a ruby String. If pointer
is NULL (see is_null_pointer
) returns an empty string.
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 384 def read_string(pointer, length=nil) if is_null_pointer(pointer) return '' end unless length length = railgun.kernel32.lstrlenA(pointer)['return'] end chars = read_array(:CHAR, length, pointer) return chars.join('') end
Construct the data structure described in definition
from buffer
starting from the index offset
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 492 def read_struct(definition, buffer, offset = 0) data = {} offsets = struct_offsets(definition, offset) definition.each do |mapping| key, data_type = mapping data[key] = read_data(data_type, offsets.shift, buffer) end data end
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.
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 357 def read_wstring(pointer, length = nil) # Return an empty string for null pointers if is_null_pointer(pointer) return '' end # If length not provided, use lstrlenW if length.nil? length = railgun.kernel32.lstrlenW(pointer)['return'] end # Retrieve the array of characters chars = read_array(:WCHAR, length, pointer) # Concatenate the characters and convert to a ruby string str = uniz_to_str(chars.join('')) return str end
en.wikipedia.org/wiki/Data_structure_alignment
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 600 def required_alignment is_64bit ? 8 : 4 end
Calculates the size of struct
after alignment.
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 568 def sizeof_struct(struct) offsets = struct_offsets(struct, 0) last_data_size = sizeof_type(struct.last[1]) size_no_padding = offsets.last + last_data_size return size_no_padding + calc_padding(size_no_padding) end
Return the size, in bytes, of the given type
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 530 def sizeof_type(type) if is_pointer_type?(type) return pointer_size end if type.kind_of? String if is_array_type?(type) element_type, length = split_array_type(type) return length * sizeof_type(element_type) else return sizeof_type(type.to_sym) end end if is_struct_type?(type) return sizeof_struct(type) end if TYPE_DEFINITIONS.has_key?(type) primitive = TYPE_DEFINITIONS[type] if primitive == :pointer return pointer_size end if PRIMITIVE_TYPE_SIZES.has_key?(primitive) return PRIMITIVE_TYPE_SIZES[primitive] else raise "Type #{type} was mapped to non-existent primitive #{primitive}" end end raise "Unable to determine size for type #{type}." end
Given an explicit array definition (e.g. BYTE) return size (e.g. 23) and and type
(e.g. BYTE). If a constant is given, attempt to resolve it that constant.
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 624 def split_array_type(type) if type =~ /^(\w+)\[(\w+)\]$/ element_type = $1 length = $2 unless length =~ /^\d+$/ length = railgun.const(length) end return element_type.to_sym, length.to_i else raise "Can not split non-array type #{type}" end end
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.
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 581 def struct_offsets(definition, offset) padding = 0 offsets = [] definition.each do |mapping| key, data_type = mapping if sizeof_type(data_type) > padding offset = offset + padding end offsets.push(offset) offset = offset + sizeof_type(data_type) padding = calc_padding(offset) end offsets end
Given a packed pointer, unpacks it according to architecture
# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 324 def unpack_pointer(packed_pointer) if is_64bit # Assume little endian packed_pointer.unpack('Q<')[0] else packed_pointer.unpack('V')[0] end end