#!/usr/bin/env ruby

require 'rex/post/process'
require 'rex/post/meterpreter/packet'
require 'rex/post/meterpreter/client'
require 'rex/post/meterpreter/extensions/stdapi/constants'
require 'rex/post/meterpreter/extensions/stdapi/stdapi'
require 'rex/post/meterpreter/extensions/stdapi/sys/registry_subsystem/registry_key'
require 'rex/post/meterpreter/extensions/stdapi/sys/registry_subsystem/registry_value'

module Rex
module Post
module Meterpreter
module Extensions
module Stdapi
module Sys

###
#
# This class provides access to the Windows registry on the remote
# machine.
#
###
class Registry

	class << self
		attr_accessor :client
	end

	##
	#
	# Registry key interaction
	#
	##

	#
	# Opens the supplied registry key relative to the root key with
	# the supplied permissions.  Right now this is merely a wrapper around
	# create_key.
	#
	def Registry.open_key(root_key, base_key, perm = KEY_READ)
		# If no base key was provided, just return the root_key.
		if (base_key == nil or base_key.length == 0)
			return RegistrySubsystem::RegistryKey.new(client, root_key, base_key, perm, root_key)
		end

		return self.create_key(root_key, base_key, perm)
	end

	#
	# Creates the supplied registry key or opens it if it already exists.
	#
	def Registry.create_key(root_key, base_key, perm = KEY_READ)
		request = Packet.create_request('stdapi_registry_create_key')

		request.add_tlv(TLV_TYPE_ROOT_KEY, root_key)
		request.add_tlv(TLV_TYPE_BASE_KEY, base_key)
		request.add_tlv(TLV_TYPE_PERMISSION, perm)

		response = client.send_request(request)

		return Rex::Post::Meterpreter::Extensions::Stdapi::Sys::RegistrySubsystem::RegistryKey.new(
				client, root_key, base_key, perm, response.get_tlv(TLV_TYPE_HKEY).value)
	end

	#
	# Deletes the supplied registry key.
	#
	def Registry.delete_key(root_key, base_key, recursive = true)
		request = Packet.create_request('stdapi_registry_delete_key')
		flags   = 0

		if (recursive)
			flags |= DELETE_KEY_FLAG_RECURSIVE
		end

		request.add_tlv(TLV_TYPE_ROOT_KEY, root_key)
		request.add_tlv(TLV_TYPE_BASE_KEY, base_key)
		request.add_tlv(TLV_TYPE_FLAGS, flags)

		if (client.send_request(request) != nil)
			return true
		end

		return false
	end

	#
	# Closes the supplied registry key.
	#
	def Registry.close_key(hkey)
		request = Packet.create_request('stdapi_registry_close_key')

		request.add_tlv(TLV_TYPE_HKEY, hkey)

		client.send_packet(request)

		return true
	end

	#
	# Enumerates the supplied registry key returning an array of key names.
	#
	def Registry.enum_key(hkey)
		keys    = []
		request = Packet.create_request('stdapi_registry_enum_key')

		request.add_tlv(TLV_TYPE_HKEY, hkey)

		response = client.send_request(request)

		# Enumerate through all of the registry keys
		response.each(TLV_TYPE_KEY_NAME) { |key_name|
			keys << key_name.value
		}

		return keys
	end

	##
	#
	# Registry value interaction
	#
	##

	#
	# Sets the registry value relative to the supplied hkey.
	#
	def Registry.set_value(hkey, name, type, data)
		request = Packet.create_request('stdapi_registry_set_value')

		request.add_tlv(TLV_TYPE_HKEY, hkey)
		request.add_tlv(TLV_TYPE_VALUE_NAME, name)
		request.add_tlv(TLV_TYPE_VALUE_TYPE, type)

		if (type == REG_SZ)
			data += "\x00"
		elsif (type == REG_DWORD)
			data = [ data.to_i ].pack("V")
		end

		request.add_tlv(TLV_TYPE_VALUE_DATA, data)

		response = client.send_request(request)

		return true
	end

	#
	# Queries the registry value supplied in name and returns an
	# initialized RegistryValue instance if a match is found.
	#
	def Registry.query_value(hkey, name)
		request = Packet.create_request('stdapi_registry_query_value')

		request.add_tlv(TLV_TYPE_HKEY, hkey)
		request.add_tlv(TLV_TYPE_VALUE_NAME, name)

		response = client.send_request(request)

		data = response.get_tlv(TLV_TYPE_VALUE_DATA).value;
		type = response.get_tlv(TLV_TYPE_VALUE_TYPE).value;

		if (type == REG_SZ)
			data = data[0..-2]
		elsif (type == REG_DWORD)
			data = data.unpack("N")[0]
		end

		return Rex::Post::Meterpreter::Extensions::Stdapi::Sys::RegistrySubsystem::RegistryValue.new(
				client, hkey, name, type, data)
	end

	#
	# Deletes the registry value supplied in name from the supplied
	# registry key.
	#
	def Registry.delete_value(hkey, name)
		request = Packet.create_request('stdapi_registry_delete_value')

		request.add_tlv(TLV_TYPE_HKEY, hkey)
		request.add_tlv(TLV_TYPE_VALUE_NAME, name)

		if (client.send_request(request) != nil)
			return true
		end

		return false
	end

	#
	# Queries the registry class name and returns a string
	#
	def Registry.query_class(hkey)
		request = Packet.create_request('stdapi_registry_query_class')

		request.add_tlv(TLV_TYPE_HKEY, hkey)

		response = client.send_request(request)
		cls = response.get_tlv(TLV_TYPE_VALUE_DATA)
		return nil if not cls
		data = cls.value.gsub(/\x00.*/, '')
		return data
	end

	#
	# Enumerates all of the values at the supplied hkey including their
	# names.  An array of RegistryValue's is returned.
	#
	def Registry.enum_value(hkey)
		request = Packet.create_request('stdapi_registry_enum_value')
		values  = []

		request.add_tlv(TLV_TYPE_HKEY, hkey)

		response = client.send_request(request)

		response.each(TLV_TYPE_VALUE_NAME) { |value_name|
			values << Rex::Post::Meterpreter::Extensions::Stdapi::Sys::RegistrySubsystem::RegistryValue.new(
					client, hkey, value_name.value)
		}

		return values
	end

	#
	# Return the key value associated with the supplied string.  This is useful
	# for converting HKLM as a string into its actual integer representation.
	#
	def self.key2str(key)
		if (key == 'HKLM' or key == 'HKEY_LOCAL_MACHINE')
			return HKEY_LOCAL_MACHINE
		elsif (key == 'HKCU' or key == 'HKEY_CURRENT_USER')
			return HKEY_CURRENT_USER
		elsif (key == 'HKU' or key == 'HKEY_USERS')
			return HKEY_USERS
		elsif (key == 'HKCR' or key == 'HKEY_CLASSES_ROOT')
			return HKEY_CLASSES_ROOT
		elsif (key == 'HKEY_CURRENT_CONFIG')
			return HKEY_CURRENT_CONFIG
		elsif (key == 'HKEY_PERFORMANCE_DATA')
			return HKEY_PERFORMANCE_DATA
		elsif (key == 'HKEY_DYN_DATA')
			return HKEY_DYN_DATA
		else
			raise ArgumentError, "Unknown key: #{key}"
		end
	end

	#
	# Returns the integer value associated with the supplied registry value
	# type (like REG_SZ).
	#
	def self.type2str(type)
		return REG_SZ if (type == 'REG_SZ')
		return REG_DWORD if (type == 'REG_DWORD')
		return REG_BINARY if (type == 'REG_BINARY')
		return REG_EXPAND_SZ if (type == 'REG_EXPAND_SZ')
		return REG_NONE if (type == 'REG_NONE')
		return nil
	end

	#
	# Split the supplied full registry key into its root key and base key.  For
	# instance, passing HKLM\Software\Dog will return [ HKEY_LOCAL_MACHINE,
	# 'Software\Dog' ]
	#
	def self.splitkey(str)
		if (str =~ /^(.+?)\\(.*)$/)
			[ key2str($1), $2 ]
		else
			[ key2str(str), nil ]
		end
	end

end

end; end; end; end; end; end

