# -*- coding: binary -*-
require 'rex/post/meterpreter/extensions/stdapi/railgun/dll_helper'

module Rex
module Post
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
	include DLLHelper

	# Data type size info: http://msdn.microsoft.com/en-us/library/s3f49ktz(v=vs.80).aspx
	PRIMITIVE_TYPE_SIZES = {
		:int => 4,
		:__int8 => 1,
		:__int16 => 2,
		:__int32 => 4,
		:__int64 => 8,
		:bool => 1,
		:char => 1,
		:short => 2,
		:long => 4,
		:long_long => 8,
		:float => 4,
		:double => 8,
		:long_double => 8,
		:wchar_t => 2,
	}

	#
	# 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 = {
		##
		# Primitives
		##
		:int => :int,
		:__int8 => :__int8,
		:__int16 => :__int16,
		:__int32 => :__int32,
		:__int64 => :__int64,
		:bool => :bool,
		:char => :char,
		:short => :short,
		:long => :long,
		:long_long => :long_long,
		:float => :float,
		:double => :double,
		:long_double => :long_double,
		:wchar_t => :wchar_t,
		##
		# Non-pointers
		##
		#typedef WORD ATOM;
		:ATOM => :short,
		#typedef int BOOL;
		:BOOL => :int,
		#typedef BYTE BOOLEAN;
		:BOOLEAN => :char,
		#typedef unsigned char BYTE;
		:BYTE => :char,
		#typedef char CHAR;
		:CHAR => :char,
		#typedef DWORD COLORREF;
		:COLORREF => :long,
		#typedef unsigned long DWORD;
		:DWORD => :long,
		#typedef unsigned int DWORD32;
		:DWORD32 => :int,
		#typedef unsigned __int64 DWORD64;
		:DWORD64 => :__int64,
		#typedef float FLOAT;
		:FLOAT => :float,
		#typedef int HFILE;
		:HFILE => :int,
		#typedef LONG HRESULT;
		:HRESULT => :long,
		#typedef int INT;
		:INT => :int,
		#typedef signed int INT32;
		:INT32 => :int,
		#typedef signed __int64 INT64;
		:INT64 => :__int64,
		#typedef WORD LANGID;
		:LANGID => :short,
		#typedef DWORD LCID;
		:LCID => :long,
		#typedef DWORD LCTYPE;
		:LCTYPE => :long,
		#typedef DWORD LGRPID;
		:LGRPID => :long,
		#typedef long LONG;
		:LONG => :long,
		#typedef signed int LONG32;
		:LONG32 => :int,
		#typedef __int64 LONG64;
		:LONG64 => :__int64,
		#typedef PDWORD PLCID;
		:PLCID => :pointer,
		#typedef LPVOID SC_LOCK;
		:SC_LOCK => :pointer,
		#typedef short SHORT;
		:SHORT => :short,
		#typedef unsigned char UCHAR;
		:UCHAR => :char,
		#typedef unsigned int UINT;
		:UINT => :int,
		#typedef unsigned int UINT32;
		:UINT32 => :int,
		#typedef unsigned long ULONG;
		:ULONG => :long,
		#typedef unsigned int ULONG32;
		:ULONG32 => :int,
		#typedef unsigned __int64 ULONG64;
		:ULONG64 => :__int64,
		#typedef unsigned short USHORT;
		:USHORT => :short,
		#typedef wchar_t WCHAR;
		:WCHAR => :wchar_t,
		#typedef unsigned short WORD;
		:WORD => :short,
		##
		# Pointers declared with *
		##
		#typedef DWORD* LPCOLORREF;
		:LPCOLORREF => :pointer,
		#typedef void* LPCVOID;
		:LPCVOID => :pointer,
		#typedef WCHAR* LPCWSTR;
		:LPCWSTR => :pointer,
		#typedef DWORD* LPDWORD;
		:LPDWORD => :pointer,
		#typedef HANDLE* LPHANDLE;
		:LPHANDLE => :pointer,
		#typedef int* LPINT;
		:LPINT => :pointer,
		#typedef long* LPLONG;
		:LPLONG => :pointer,
		#typedef CHAR* LPSTR;
		:LPSTR => :pointer,
		#typedef void* LPVOID;
		:LPVOID => :pointer,
		#typedef WORD* LPWORD;
		:LPWORD => :pointer,
		#typedef WCHAR* LPWSTR;
		:LPWSTR => :pointer,
		#typedef BOOL* PBOOL;
		:PBOOL => :pointer,
		#typedef BOOLEAN* PBOOLEAN;
		:PBOOLEAN => :pointer,
		#typedef BYTE* PBYTE;
		:PBYTE => :pointer,
		#typedef CHAR* PCHAR;
		:PCHAR => :pointer,
		#typedef CHAR* PCSTR;
		:PCSTR => :pointer,
		#typedef WCHAR* PCWSTR;
		:PCWSTR => :pointer,
		#typedef DWORD* PDWORD;
		:PDWORD => :pointer,
		#typedef DWORDLONG* PDWORDLONG;
		:PDWORDLONG => :pointer,
		#typedef DWORD_PTR* PDWORD_PTR;
		:PDWORD_PTR => :pointer,
		#typedef DWORD32* PDWORD32;
		:PDWORD32 => :pointer,
		#typedef DWORD64* PDWORD64;
		:PDWORD64 => :pointer,
		#typedef FLOAT* PFLOAT;
		:PFLOAT => :pointer,
		#typedef HANDLE* PHANDLE;
		:PHANDLE => :pointer,
		#typedef HKEY* PHKEY;
		:PHKEY => :pointer,
		#typedef int* PINT;
		:PINT => :pointer,
		#typedef INT_PTR* PINT_PTR;
		:PINT_PTR => :pointer,
		#typedef INT32* PINT32;
		:PINT32 => :pointer,
		#typedef INT64* PINT64;
		:PINT64 => :pointer,
		#typedef LONG* PLONG;
		:PLONG => :pointer,
		#typedef LONGLONG* PLONGLONG;
		:PLONGLONG => :pointer,
		#typedef LONG_PTR* PLONG_PTR;
		:PLONG_PTR => :pointer,
		#typedef LONG32* PLONG32;
		:PLONG32 => :pointer,
		#typedef LONG64* PLONG64;
		:PLONG64 => :pointer,
		#typedef SHORT* PSHORT;
		:PSHORT => :pointer,
		#typedef SIZE_T* PSIZE_T;
		:PSIZE_T => :pointer,
		#typedef SSIZE_T* PSSIZE_T;
		:PSSIZE_T => :pointer,
		#typedef CHAR* PSTR;
		:PSTR => :pointer,
		#typedef TBYTE* PTBYTE;
		:PTBYTE => :pointer,
		#typedef TCHAR* PTCHAR;
		:PTCHAR => :pointer,
		#typedef UCHAR* PUCHAR;
		:PUCHAR => :pointer,
		#typedef UINT* PUINT;
		:PUINT => :pointer,
		#typedef UINT_PTR* PUINT_PTR;
		:PUINT_PTR => :pointer,
		#typedef UINT32* PUINT32;
		:PUINT32 => :pointer,
		#typedef UINT64* PUINT64;
		:PUINT64 => :pointer,
		#typedef ULONG* PULONG;
		:PULONG => :pointer,
		#typedef ULONGLONG* PULONGLONG;
		:PULONGLONG => :pointer,
		#typedef ULONG_PTR* PULONG_PTR;
		:PULONG_PTR => :pointer,
		#typedef ULONG32* PULONG32;
		:PULONG32 => :pointer,
		#typedef ULONG64* PULONG64;
		:PULONG64 => :pointer,
		#typedef USHORT* PUSHORT;
		:PUSHORT => :pointer,
		#typedef void* PVOID;
		:PVOID => :pointer,
		#typedef WCHAR* PWCHAR;
		:PWCHAR => :pointer,
		#typedef WORD* PWORD;
		:PWORD => :pointer,
		#typedef WCHAR* PWSTR;
		:PWSTR => :pointer,
		#typedef HANDLE HACCEL;
		:HACCEL => :pointer,
		##
		#  Handles
		##
		#typedef PVOID HANDLE;
		:HANDLE => :pointer,
		#typedef HANDLE HBITMAP;
		:HBITMAP => :pointer,
		#typedef HANDLE HBRUSH;
		:HBRUSH => :pointer,
		#typedef HANDLE HCOLORSPACE;
		:HCOLORSPACE => :pointer,
		#typedef HANDLE HCONV;
		:HCONV => :pointer,
		#typedef HANDLE HCONVLIST;
		:HCONVLIST => :pointer,
		#typedef HANDLE HDC;
		:HDC => :pointer,
		#typedef HANDLE HDDEDATA;
		:HDDEDATA => :pointer,
		#typedef HANDLE HDESK;
		:HDESK => :pointer,
		#typedef HANDLE HDROP;
		:HDROP => :pointer,
		#typedef HANDLE HDWP;
		:HDWP => :pointer,
		#typedef HANDLE HENHMETAFILE;
		:HENHMETAFILE => :pointer,
		#typedef HANDLE HFONT;
		:HFONT => :pointer,
		#typedef HANDLE HGDIOBJ;
		:HGDIOBJ => :pointer,
		#typedef HANDLE HGLOBAL;
		:HGLOBAL => :pointer,
		#typedef HANDLE HHOOK;
		:HHOOK => :pointer,
		#typedef HANDLE HICON;
		:HICON => :pointer,
		#typedef HANDLE HINSTANCE;
		:HINSTANCE => :pointer,
		#typedef HANDLE HKEY;
		:HKEY => :pointer,
		#typedef HANDLE HKL;
		:HKL => :pointer,
		#typedef HANDLE HLOCAL;
		:HLOCAL => :pointer,
		#typedef HANDLE HMENU;
		:HMENU => :pointer,
		#typedef HANDLE HMETAFILE;
		:HMETAFILE => :pointer,
		#typedef HANDLE HPALETTE;
		:HPALETTE => :pointer,
		#typedef HANDLE HPEN;
		:HPEN => :pointer,
		#typedef HANDLE HRGN;
		:HRGN => :pointer,
		#typedef HANDLE HRSRC;
		:HRSRC => :pointer,
		#typedef HANDLE HSZ;
		:HSZ => :pointer,
		#typedef HANDLE WINSTA;
		:WINSTA => :pointer,
		#typedef HANDLE HWND;
		:HWND => :pointer,
		#typedef HANDLE SC_HANDLE;
		:SC_HANDLE => :pointer,
		#typedef HANDLE SERVICE_STATUS_HANDLE;
		:SERVICE_STATUS_HANDLE => :pointer,
	}

	# param 'railgun' is a Railgun instance.
	# param 'platform' is a value like client.platform
	def initialize(railgun, platform)
		@railgun = railgun
		@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
			packed_pointer.unpack('Q')[0]
		else
			packed_pointer.unpack('V')[0]
		end
	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
	#
	def is_null_pointer(pointer)
		if pointer.class == String
			pointer = unpack_pointer(pointer)
		end

		return pointer.nil? || pointer == 0
	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.
	#
	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

	#
	# 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)
		else
			return buffer[address .. (address + size - 1)]
		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))
			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('l').first == 1
		when :LONG
			return raw.unpack('l').first
		end

 		#If nothing worked thus far, return it raw
		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
	#
	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

	#
	# Construct the data structure described in +definition+ from +buffer+
	# starting from the index +offset+
	#
	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


	# Returns true if the data type is a pointer, false otherwise
	def is_pointer_type?(type)
		return TYPE_DEFINITIONS[type] == :pointer
	end

	# Returns whether the given type represents an array of another type
	# For example BYTE[3], BYTE[ENCRYPTED_PWLEN], or even PDWORD[23]
	def is_array_type?(type)
		return type =~ /^\w+\[\w+\]$/ ? true : false
	end

	# Returns true if the type passed describes a data structure, false otherwise
	def is_struct_type?(type)
		return type.class == Array
	end


	# Returns the pointer size for this architecture
	def pointer_size
		is_64bit ? 8 : 4
	end

	# Return the size, in bytes, of the given type
	def sizeof_type(type)
		if is_pointer_type?(type)
			return pointer_size
		end

		if is_array_type?(type)
			element_type, length = split_array_type(type)

			return length * sizeof_type(element_type)
		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

	#
	# Calculates the size of +struct+ after alignment.
	#
	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

	#
	# 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.
	#
	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

	# http://en.wikipedia.org/wiki/Data_structure_alignment
	def required_alignment
		is_64bit ? 8 : 4
	end

	#
	# Number of bytes that needed to be added to be aligned.
	#
	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

	#
	# 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.
	#
	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, length
		else
			raise "Can not split non-array type #{type}"
		end
	end

	# Returns true if given platform has 64bit architecture
	# expects client.platform
	def is_64bit_platform?(platform)
		platform =~ /win64/
	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, ... }
	#
	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

	protected

	attr_accessor :railgun, :is_64bit
end # Util
end # Railgun
end # Stdapi
end # Extensions
end # Meterpreter
end # Post
end # Rex
