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

is_64bit[RW]
railgun[RW]

Public Class Methods

new(railgun, platform) click to toggle source

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

calc_padding(offset) click to toggle source

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
is_64bit_platform?(platform) click to toggle source

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
is_array_type?(type) click to toggle source

Returns whether the given type represents an array of another type For example BYTE, BYTE, or even PDWORD

# File lib/rex/post/meterpreter/extensions/stdapi/railgun/util.rb, line 514
def is_array_type?(type)
  return type =~ /^\w+\[\w+\]$/ ? true : false
end
is_null_pointer(pointer) click to toggle source

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
is_pointer_type?(type) click to toggle source

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
is_struct_type?(type) click to toggle source

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
judge_bit_field(value, mappings) click to toggle source

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
memread(address, size, buffer = nil) click to toggle source

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
pointer_size() click to toggle source

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_array(type, length, bufptr, buffer = nil) click to toggle source

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
read_data(type, position, buffer = nil) click to toggle source

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_pointer(buffer, offset = 0) click to toggle source

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
read_string(pointer, length=nil) click to toggle source

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
read_struct(definition, buffer, offset = 0) click to toggle source

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
read_wstring(pointer, length = nil) click to toggle source

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
required_alignment() click to toggle source

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
sizeof_struct(struct) click to toggle source

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
sizeof_type(type) click to toggle source

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
split_array_type(type) click to toggle source

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
struct_offsets(definition, offset) click to toggle source

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
unpack_pointer(packed_pointer) click to toggle source

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