230 lines
7.5 KiB
Ruby
230 lines
7.5 KiB
Ruby
# -*- coding: binary -*-
|
|
|
|
module Rex
|
|
module Java
|
|
module Serialization
|
|
module Model
|
|
# This class provides a NewArray (Java Array) representation
|
|
class NewArray < Element
|
|
|
|
include Rex::Java::Serialization::Model::Contents
|
|
|
|
# @!attribute array_description
|
|
# @return [Java::Serialization::Model::ClassDesc] The description of the array
|
|
attr_accessor :array_description
|
|
# @!attribute type
|
|
# @return [String] The type of the array values
|
|
attr_accessor :type
|
|
# @!attribute values
|
|
# @return [Array] The contents of the java array
|
|
attr_accessor :values
|
|
|
|
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
|
|
def initialize(stream = nil)
|
|
super(stream)
|
|
self.array_description = nil
|
|
self.type = ''
|
|
self.values = []
|
|
end
|
|
|
|
# Deserializes a Rex::Java::Serialization::Model::NewArray
|
|
#
|
|
# @param io [IO] the io to read from
|
|
# @return [self] if deserialization succeeds
|
|
# @raise [RuntimeError] if deserialization doesn't succeed
|
|
def decode(io)
|
|
self.array_description = ClassDesc.decode(io, stream)
|
|
stream.add_reference(self) unless stream.nil?
|
|
self.type = array_type
|
|
|
|
values_length = decode_values_length(io)
|
|
|
|
values_length.times do
|
|
value = decode_value(io)
|
|
self.values << value
|
|
end
|
|
|
|
self
|
|
end
|
|
|
|
# Serializes the Rex::Java::Serialization::Model::NewArray
|
|
#
|
|
# @return [String] if serialization succeeds
|
|
# @raise [RuntimeError] if serialization doesn't succeed
|
|
def encode
|
|
unless array_description.class == ClassDesc
|
|
raise ::RuntimeError, 'Failed to serialize NewArray'
|
|
end
|
|
|
|
encoded = ''
|
|
encoded << array_description.encode
|
|
|
|
encoded << [values.length].pack('N')
|
|
|
|
values.each do |value|
|
|
encoded << encode_value(value)
|
|
end
|
|
|
|
encoded
|
|
end
|
|
|
|
# Creates a print-friendly string representation
|
|
#
|
|
# @return [String]
|
|
def to_s
|
|
str = "#{type}, "
|
|
values_data = values.collect {|v| "#{v}"}
|
|
str << "#{values_data}"
|
|
end
|
|
|
|
private
|
|
|
|
# Deserializes the NewArray length
|
|
#
|
|
# @param io [IO] the io to read from
|
|
# @return [Integer] if deserialization succeeds
|
|
# @raise [RuntimeError] if deserialization doesn't succeed
|
|
def decode_values_length(io)
|
|
values_length = io.read(4)
|
|
if values_length.nil? || values_length.length != 4
|
|
raise ::RuntimeError, 'Failed to unserialize NewArray'
|
|
end
|
|
|
|
values_length.unpack('N')[0]
|
|
end
|
|
|
|
# Extracts the NewArray data type
|
|
#
|
|
# @return [String]
|
|
# @raise [RuntimeError] if the NewArray description isn't valid
|
|
# @raise [RuntimeError] if the NewArray type isn't supported
|
|
def array_type
|
|
if array_description.nil?
|
|
raise ::RuntimeError, 'Empty NewArray description'
|
|
end
|
|
|
|
unless array_description.class == ClassDesc
|
|
raise ::RuntimeError, 'Unsupported NewArray description class'
|
|
end
|
|
|
|
desc = array_description.description
|
|
|
|
if desc.class == Reference
|
|
ref = desc.handle - BASE_WIRE_HANDLE
|
|
desc = stream.references[ref]
|
|
end
|
|
|
|
unless desc.class_name.contents[0] == '[' # Array
|
|
raise ::RuntimeError, 'Unsupported NewArray description'
|
|
end
|
|
|
|
decoded_type = desc.class_name.contents[1]
|
|
if PRIMITIVE_TYPE_CODES.keys.include?(decoded_type)
|
|
return PRIMITIVE_TYPE_CODES[decoded_type]
|
|
elsif decoded_type == 'L' # L : Object
|
|
return desc.class_name.contents[2..desc.class_name.contents.index(';')] # Object class
|
|
else
|
|
raise ::RuntimeError, 'Unsupported NewArray Type'
|
|
end
|
|
end
|
|
|
|
# Deserializes a NewArray value
|
|
#
|
|
# @param io [IO] the io to read from
|
|
# @return [Fixnum, Float] if deserialization succeeds
|
|
# @raise [RuntimeError] if deserialization fails
|
|
def decode_value(io)
|
|
value = nil
|
|
|
|
case type
|
|
when 'byte'
|
|
value = io.read(1)
|
|
raise ::RuntimeError, 'Failed to deserialize NewArray value' if value.nil?
|
|
value = value.unpack('c')[0]
|
|
when 'char'
|
|
value = io.read(2)
|
|
unless value && value.length == 2
|
|
raise ::RuntimeError, 'Failed to deserialize NewArray value'
|
|
end
|
|
value = value.unpack('s>')[0]
|
|
when 'double'
|
|
value = io.read(8)
|
|
unless value && value.length == 8
|
|
raise ::RuntimeError, 'Failed to deserialize NewArray value'
|
|
end
|
|
value = value.unpack('G')[0]
|
|
when 'float'
|
|
value = io.read(4)
|
|
unless value && value.length == 4
|
|
raise ::RuntimeError, 'Failed to deserialize NewArray value'
|
|
end
|
|
value = value.unpack('g')[0]
|
|
when 'int'
|
|
value = io.read(4)
|
|
unless value && value.length == 4
|
|
raise ::RuntimeError, 'Failed to deserialize NewArray value'
|
|
end
|
|
value = value.unpack('l>')[0]
|
|
when 'long'
|
|
value = io.read(8)
|
|
unless value && value.length == 8
|
|
raise ::RuntimeError, 'Failed to deserialize NewArray value'
|
|
end
|
|
value = value.unpack('q>')[0]
|
|
when 'short'
|
|
value = io.read(2)
|
|
unless value && value.length == 2
|
|
raise ::RuntimeError, 'Failed to deserialize NewArray value'
|
|
end
|
|
value = value.unpack('s>')[0]
|
|
when 'boolean'
|
|
value = io.read(1)
|
|
raise ::RuntimeError, 'Failed to deserialize NewArray value' if value.nil?
|
|
value = value.unpack('c')[0]
|
|
else # object
|
|
value = decode_content(io, stream)
|
|
end
|
|
|
|
value
|
|
end
|
|
|
|
# Serializes an NewArray value
|
|
#
|
|
# @param value [Fixnum] the value to serialize
|
|
# @param value [Float] the value to serialize
|
|
# @return [String] the serialized value
|
|
# @raise [RuntimeError] if serialization fails
|
|
def encode_value(value)
|
|
res = ''
|
|
|
|
case type
|
|
when 'byte'
|
|
res = [value].pack('c')
|
|
when 'char'
|
|
res = [value].pack('s>')
|
|
when 'double'
|
|
res = [value].pack('G')
|
|
when 'float'
|
|
res = [value].pack('g')
|
|
when 'int'
|
|
res = [value].pack('l>')
|
|
when 'long'
|
|
res = [value].pack('q>')
|
|
when 'short'
|
|
res = [value].pack('s>')
|
|
when 'boolean'
|
|
res = [value].pack('c')
|
|
when Element
|
|
res = value.encode
|
|
else # object
|
|
res = encode_content(value)
|
|
end
|
|
|
|
res
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end |