lib/rex/post/meterpreter/extensions/stdapi/railgun/library_helper.rb
# -*- coding: binary -*-
# Copyright (c) 2010, patrickHVE@googlemail.com
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * The names of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL patrickHVE@googlemail.com BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
module Rex
module Post
module Meterpreter
module Extensions
module Stdapi
module Railgun
#
# shared functions
#
#
module LibraryHelper
# converts ruby string to zero-terminated ASCII string
def str_to_ascii_z(str)
return str + "\x00"
end
# converts 0-terminated ASCII string to ruby string
def asciiz_to_str(asciiz)
zero_byte_idx = asciiz.index("\x00")
if zero_byte_idx != nil
return asciiz[0, zero_byte_idx]
else
return asciiz
end
end
# converts ruby string to zero-terminated WCHAR string
def str_to_uni_z(str)
enc = str.encode('UTF-16LE').force_encoding('binary')
enc += "\x00\x00"
return enc
end
# converts 0-terminated UTF16 to ruby string
def uniz_to_str(uniz)
uniz.force_encoding('UTF-16LE').encode('UTF-8')
end
# parses a number param and returns the value
# raises an exception if the param cannot be converted to a number
# examples:
# nil => 0
# 3 => 3
# "MB_OK" => 0
# "SOME_CONSTANT | OTHER_CONSTANT" => 17
# "tuna" => !!!!!!!!!!Exception
#
# Parameter "consts_mgr" is a ConstantManager
def param_to_number(v, consts_mgr = @consts_mgr)
if v.class == NilClass then
return 0
elsif v.kind_of? Integer then
return v # ok, it's already a number
elsif v.kind_of? String then
dw = consts_mgr.parse(v) # might raise an exception
if dw != nil
return dw
else
raise ArgumentError, "Param #{v} (class #{v.class}) cannot be converted to a number. It's a string but matches no constants I know."
end
else
raise "Param #{v} (class #{v.class}) should be a number but isn't"
end
end
# assembles the buffers "in" and "inout"
def assemble_buffer(direction, function, args, arch)
layout = {} # paramName => BufferItem
blob = ""
#puts " building buffer: #{direction}"
function.params.each_with_index do |param_desc, param_idx|
#puts " processing #{param_desc[0]} #{param_desc[1]} #{param_desc[2]}"
# we care only about inout buffers
if param_desc[2] == direction
buffer = nil
# Special case:
# The user can choose to supply a Null pointer instead of a buffer
# in this case we don't need space in any heap buffer
if param_desc[0][0,1] == 'P' # type is a pointer
if args[param_idx] == nil
next
end
end
case param_desc[0] # required argument type
when 'PULONG_PTR'
val = param_to_number(args[param_idx])
buffer = [val].pack(arch == ARCH_X64 ? 'Q<' : 'V')
when "PDWORD"
val = param_to_number(args[param_idx])
buffer = [val].pack('V')
when "PWCHAR"
next if args[param_idx].is_a?(Integer) && direction == 'in'
raise "param #{param_desc[1]}: string or integer expected" unless args[param_idx].class == String
buffer = str_to_uni_z(args[param_idx])
when "PCHAR"
next if args[param_idx].is_a?(Integer) && direction == 'in'
raise "param #{param_desc[1]}: string or integer expected" unless args[param_idx].class == String
buffer = str_to_ascii_z(args[param_idx])
when "PBLOB"
next if args[param_idx].is_a?(Integer) && direction == 'in'
args[param_idx] = args[param_idx].to_binary_s if args[param_idx].is_a?(BinData::Struct)
raise "param #{param_desc[1]}: string or integer expected" unless args[param_idx].class == String
buffer = args[param_idx]
# other types (non-pointers) don't reference buffers
# and don't need any treatment here
end
unless buffer.nil?
#puts " adding #{buffer.length} bytes to heap blob"
layout[param_desc[1]] = BufferItem.new(param_idx, blob.length, buffer.length, param_desc[0])
blob += buffer
# sf: force 8 byte alignment to satisfy x64, wont matter on x86.
while blob.length % 8 != 0
blob += "\x00"
end
#puts " heap blob size now #{blob.length}"
end
end
end
#puts " built buffer: #{direction}"
return [layout, blob]
end
end
end; end; end; end; end; end;