Adds Java serialization support, lands #4327

bug/bundler_fix
HD Moore 2014-12-13 17:47:53 -06:00
commit 00590f9f26
No known key found for this signature in database
GPG Key ID: 22015B93FA604913
35 changed files with 3662 additions and 0 deletions

3
lib/rex/java.rb Normal file
View File

@ -0,0 +1,3 @@
# -*- coding: binary -*-
require 'rex/java/serialization'

View File

@ -0,0 +1,54 @@
# -*- coding: binary -*-
module Rex
module Java
# Include constants defining terminal and constant
# values expected in a stream.
module Serialization
STREAM_MAGIC = 0xaced
STREAM_VERSION = 5
TC_NULL = 0x70
TC_REFERENCE = 0x71
TC_CLASSDESC = 0x72
TC_OBJECT = 0x73
TC_STRING = 0x74
TC_ARRAY = 0x75
TC_CLASS = 0x76
TC_BLOCKDATA = 0x77
TC_ENDBLOCKDATA = 0x78
TC_RESET = 0x79
TC_BLOCKDATALONG = 0x7A
TC_EXCEPTION = 0x7B
TC_LONGSTRING = 0x7C
TC_PROXYCLASSDESC = 0x7D
TC_ENUM = 0x7E
BASE_WIRE_HANDLE = 0x7E0000
SC_WRITE_METHOD = 0x01 # if SC_SERIALIZABLE
SC_BLOCK_DATA = 0x08 # if SC_EXTERNALIZABLE
SC_SERIALIZABLE = 0x02
SC_EXTERNALIZABLE = 0x04
SC_ENUM = 0x10
PRIMITIVE_TYPE_CODES = {
'B' => 'byte',
'C' => 'char',
'D' => 'double',
'F' => 'float',
'I' => 'int',
'J' => 'long',
'S' => 'short',
'Z' => 'boolean'
}
OBJECT_TYPE_CODES = {
'[' => 'array',
'L' => 'object'
}
TYPE_CODES = PRIMITIVE_TYPE_CODES.merge(OBJECT_TYPE_CODES)
end
end
end
require 'rex/java/serialization/model'

View File

@ -0,0 +1,20 @@
# -*- coding: binary -*-
require 'rex/java/serialization/model/element'
require 'rex/java/serialization/model/null_reference'
require 'rex/java/serialization/model/reference'
require 'rex/java/serialization/model/reset'
require 'rex/java/serialization/model/utf'
require 'rex/java/serialization/model/long_utf'
require 'rex/java/serialization/model/block_data'
require 'rex/java/serialization/model/block_data_long'
require 'rex/java/serialization/model/end_block_data'
require 'rex/java/serialization/model/contents'
require 'rex/java/serialization/model/new_enum'
require 'rex/java/serialization/model/field'
require 'rex/java/serialization/model/new_array'
require 'rex/java/serialization/model/annotation'
require 'rex/java/serialization/model/class_desc'
require 'rex/java/serialization/model/new_class_desc'
require 'rex/java/serialization/model/new_object'
require 'rex/java/serialization/model/stream'

View File

@ -0,0 +1,69 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides an annotation representation. It's used for both class
# annotations (classAnnotation) and object annotations (objectAnnotation).
class Annotation < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute contents
# @return [Array] The annotation contents
attr_accessor :contents
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.contents = []
end
# Deserializes a Rex::Java::Serialization::Model::Annotation
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode(io)
loop do
content = decode_content(io, stream)
self.contents << content
return self if content.class == EndBlockData
end
self
end
# Serializes the Rex::Java::Serialization::Model::Annotation
#
# @return [String] if serialization suceeds
# @raise [RuntimeError] if serialization doesn't succeed
def encode
raise ::RuntimeError, 'Failed to serialize Annotation with empty contents' if contents.empty?
encoded = ''
contents.each do |content|
encoded << encode_content(content)
end
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = '[ '
contents_data = contents.collect {|content| "#{print_content(content)}"}
str << contents_data.join(', ')
str << ' ]'
str
end
end
end
end
end
end

View File

@ -0,0 +1,70 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a block data representation
class BlockData < Element
# @!attribute length
# @return [Integer] the length of the block
attr_accessor :length
# @!attribute contents
# @return [String] the contents of the block
attr_accessor :contents
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
# @param contents [String] the contents of the block
def initialize(stream = nil, contents = '')
super(stream)
self.contents = contents
self.length = contents.length
end
# Deserializes a Rex::Java::Serialization::Model::BlockData
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(1)
raise RuntimeError, 'Failed to unserialize BlockData' if raw_length.nil?
self.length = raw_length.unpack('C')[0]
if length == 0
self.contents = ''
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise RuntimeError, 'Failed to unserialize BlockData'
end
end
self
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
contents_hex = []
contents.each_byte {|byte| contents_hex << "0x#{byte.to_s(16)}" }
"[ #{contents_hex.join(', ')} ]"
end
# Serializes the Rex::Java::Serialization::Model::BlockData
#
# @return [String]
def encode
encoded = [length].pack('C')
encoded << contents
encoded
end
end
end
end
end
end

View File

@ -0,0 +1,72 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a block data (long) representation
class BlockDataLong < Element
# @!attribute length
# @return [Integer] the length of the block
attr_accessor :length
# @!attribute contents
# @return [String] the contents of the block
attr_accessor :contents
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
# @param contents [String] the contents of the block
def initialize(stream = nil, contents = '')
super(stream)
self.contents = contents
self.length = contents.length
end
# Deserializes a Rex::Java::Serialization::Model::BlockDataLong
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(4)
if raw_length.nil? || raw_length.length != 4
raise ::RuntimeError, 'Failed to unserialize BlockDataLong'
end
self.length = raw_length.unpack('N')[0]
if length == 0
self.contents = ''
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise ::RuntimeError, 'Failed to unserialize BlockData'
end
end
self
end
# Serializes the Rex::Java::Serialization::Model::BlockDataLong
#
# @return [String]
def encode
encoded = [length].pack('N')
encoded << contents
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
contents_hex = []
contents.each_byte {|byte| contents_hex << "0x#{byte.to_s(16)}" }
"[ #{contents_hex.join(', ')} ]"
end
end
end
end
end
end

View File

@ -0,0 +1,64 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a Java classDesc representation
class ClassDesc < Element
include Rex::Java::Serialization::Model::Contents
attr_accessor :description
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.description = nil
end
# Deserializes a Rex::Java::Serialization::Model::ClassDesc
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode(io)
content = decode_content(io, stream)
allowed_contents = [NullReference, NewClassDesc, Reference]
unless allowed_contents.include?(content.class)
raise ::RuntimeError, 'ClassDesc unserialize failed'
end
self.description = content
self
end
# Serializes the Rex::Java::Serialization::Model::ClassDesc
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
def encode
encoded = ''
allowed_contents = [NullReference, NewClassDesc, Reference]
unless allowed_contents.include?(description.class)
raise ::RuntimeError, 'Failed to serialize ClassDesc'
end
encoded << encode_content(description)
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
print_content(description)
end
end
end
end
end
end

View File

@ -0,0 +1,156 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
module Contents
include Rex::Java::Serialization
# Deserializes a content
#
# @param io [IO] the io to read from
# @return [Rex::Java::Serialization::Model::Element] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed or unsupported content
def decode_content(io, stream)
opcode = io.read(1)
raise ::RuntimeError, 'Failed to unserialize content' if opcode.nil?
opcode = opcode.unpack('C')[0]
content = nil
case opcode
when TC_BLOCKDATA
content = BlockData.decode(io, stream)
when TC_BLOCKDATALONG
content = BlockDataLong.decode(io, stream)
when TC_ENDBLOCKDATA
content = EndBlockData.decode(io, stream)
when TC_OBJECT
content = NewObject.decode(io, stream)
when TC_CLASS
content = ClassDesc.decode(io, stream)
when TC_ARRAY
content = NewArray.decode(io, stream)
when TC_STRING
content = Utf.decode(io, stream)
stream.add_reference(content) unless stream.nil?
when TC_LONGSTRING
content = LongUtf.decode(io, stream)
stream.add_reference(content) unless stream.nil?
when TC_ENUM
content = NewEnum.decode(io, stream)
when TC_CLASSDESC
content = NewClassDesc.decode(io, stream)
when TC_PROXYCLASSDESC
raise ::RuntimeError, 'Failed to unserialize unsupported TC_PROXYCLASSDESC content'
when TC_REFERENCE
content = Reference.decode(io, stream)
when TC_NULL
content = NullReference.decode(io, stream)
when TC_EXCEPTION
raise ::RuntimeError, 'Failed to unserialize unsupported TC_EXCEPTION content'
when TC_RESET
content = Reset.decode(io, stream)
else
raise ::RuntimeError, 'Failed to unserialize content'
end
content
end
# Serializes a content
#
# @param content [Rex::Java::Serialization::Model::Element] the content to serialize
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
def encode_content(content)
encoded = ''
case content
when BlockData
encoded << [TC_BLOCKDATA].pack('C')
when BlockDataLong
encoded << [TC_BLOCKDATALONG].pack('C')
when EndBlockData
encoded << [TC_ENDBLOCKDATA].pack('C')
when NewObject
encoded << [TC_OBJECT].pack('C')
when ClassDesc
encoded << [TC_CLASS].pack('C')
when NewArray
encoded << [TC_ARRAY].pack('C')
when Utf
encoded << [TC_STRING].pack('C')
when LongUtf
encoded << [TC_LONGSTRING].pack('C')
when NewEnum
encoded << [TC_ENUM].pack('C')
when NewClassDesc
encoded << [TC_CLASSDESC].pack('C')
when NullReference
encoded << [TC_NULL].pack('C')
when Reset
encoded << [TC_RESET].pack('C')
when Reference
encoded << [TC_REFERENCE].pack('C')
else
raise ::RuntimeError, 'Failed to serialize content'
end
encoded << content.encode
encoded
end
# Creates a print-friendly string representation
#
# @param content [Rex::Java::Serialization::Model::Element] the content to print
# @return [String]
def print_content(content)
str = ''
case content
when BlockData
str << "#{print_class(content)} { #{content.to_s} }"
when BlockDataLong
str << "#{print_class(content)} { #{content.to_s} }"
when EndBlockData
str << "#{print_class(content)}"
when NewObject
str << "#{print_class(content)} { #{content.to_s} }"
when ClassDesc
str << "#{print_class(content)} { #{content.to_s} }"
when NewArray
str << "#{print_class(content)} { #{content.to_s} }"
when Utf
str << "#{print_class(content)} { #{content.to_s} }"
when LongUtf
str << "#{print_class(content)} { #{content.to_s} } "
when NewEnum
str << "#{print_class(content)} { #{content.to_s} }"
when NewClassDesc
str << "#{print_class(content)} { #{content.to_s} }"
when NullReference
str << "#{print_class(content)}"
when Reset
str << "#{print_class(content)}"
when Reference
str << "#{print_class(content)} { #{content.to_s} }"
else
raise ::RuntimeError, 'Failed to serialize content'
end
str
end
# Creates a print-friendly string representation of the content class
#
# @param content [Rex::Java::Serialization::Model::Element] the content
# @return [String]
def print_class(content)
content.class.name.split('::').last
end
end
end
end
end
end

View File

@ -0,0 +1,44 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
class Element
attr_accessor :stream
# Deserializes a Rex::Java::Serialization::Model::Element
#
# @param io [IO] the io to read from
# @return [Rex::Java::Serialization::Model::Element] if deserialization succeeds
# @return [nil] if deserialization doesn't succeed
def self.decode(io, stream = nil)
elem = self.new(stream)
elem.decode(io)
end
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
self.stream = stream
end
def decode(io)
self
end
def encode
''
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
self.class.name.split('::').last
end
end
end
end
end
end

View File

@ -0,0 +1,12 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
class EndBlockData < Element
end
end
end
end
end

View File

@ -0,0 +1,172 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a field description representation (fieldDesc). It's used for
# both primitive descriptions (primitiveDesc) and object descriptions (objectDesc).
class Field < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute type
# @return [String] The type of the field.
attr_accessor :type
# @!attribute name
# @return [Rex::Java::Serialization::Model::Utf] The name of the field.
attr_accessor :name
# @!attribute field_type
# @return [Rex::Java::Serialization::Model::Utf] The type of the field on object types.
attr_accessor :field_type
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.type = ''
self.name = nil
self.field_type = nil
end
# Deserializes a Rex::Java::Serialization::Model::Field
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @faise [RuntimeError] if deserialization doesn't succeed
def decode(io)
code = io.read(1)
unless code && is_valid?(code)
raise ::RuntimeError, 'Failed to unserialize Field'
end
self.type = TYPE_CODES[code]
self.name = Utf.decode(io, stream)
if is_object?
self.field_type = decode_field_type(io)
end
self
end
# Serializes the Rex::Java::Serialization::Model::Field
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
def encode
unless name.class == Rex::Java::Serialization::Model::Utf
raise ::RuntimeError, 'Failed to serialize Field'
end
unless is_type_valid?
raise ::RuntimeError, 'Failed to serialize Field'
end
encoded = ''
encoded << TYPE_CODES.key(type)
encoded << name.encode
if is_object?
encoded << encode_field_type
end
encoded
end
# Whether the field type is valid.
#
# @return [Boolean]
def is_type_valid?
if TYPE_CODES.values.include?(type)
return true
end
false
end
# Whether the field type is a primitive one.
#
# @return [Boolean]
def is_primitive?
if PRIMITIVE_TYPE_CODES.values.include?(type)
return true
end
false
end
# Whether the field type is an object one.
#
# @return [Boolean]
def is_object?
if OBJECT_TYPE_CODES.values.include?(type)
return true
end
false
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = "#{name} "
if is_primitive?
str << "(#{type})"
else
str << "(#{field_type})"
end
str
end
private
# Whether the type opcode is a valid one.
#
# @param code [String] A type opcode
# @return [Boolean]
def is_valid?(code)
if TYPE_CODES.keys.include?(code)
return true
end
false
end
# Serializes the `field_type` attribute.
#
# @return [String]
def encode_field_type
allowed_contents = [Utf, Reference]
unless allowed_contents.include?(field_type.class)
raise ::RuntimeError, 'Failed to serialize Field'
end
encoded = encode_content(field_type)
encoded
end
# Deserializes the `field_type` value.
#
# @param io [IO] the io to read from
# @return [Java::Serialization::Model::Utf]
# @raise [RuntimeError] if unserialization doesn't succeed
def decode_field_type(io)
allowed_contents = [Utf, Reference]
type = decode_content(io, stream)
unless allowed_contents.include?(type.class)
raise ::RuntimeError, 'Failed to unserialize Field field_type'
end
type
end
end
end
end
end
end

View File

@ -0,0 +1,48 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a Long Utf string representation
class LongUtf < Utf
# Deserializes a Rex::Java::Serialization::Model::LongUtf
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @return [nil] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(8)
if raw_length.nil? || raw_length.length != 8
raise ::RuntimeError, 'Failed to unserialize LongUtf'
end
self.length = raw_length.unpack('Q>')[0]
if length == 0
self.contents = ''
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise ::RuntimeError, 'Failed to unserialize LongUtf'
end
end
self
end
# Serializes the Rex::Java::Serialization::Model::LongUtf
#
# @return [String]
def encode
encoded = [length].pack('Q>')
encoded << contents
encoded
end
end
end
end
end
end

View File

@ -0,0 +1,225 @@
# -*- 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
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

View File

@ -0,0 +1,155 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a newClassDesc representation
class NewClassDesc < Element
include Rex::Java::Serialization
# @!attribute class_name
# @return [Rex::Java::Serialization::Model::Utf] The name of the class
attr_accessor :class_name
# @!attribute name
# @return [Integer] The java class serial version
attr_accessor :serial_version
# @!attribute flags
# @return [Integer] The java class flags
attr_accessor :flags
# @!attribute fields
# @return [Array] The java class fields
attr_accessor :fields
# @!attribute fields
# @return [Rex::Java::Serialization::Model::Annotation] The java class annotations
attr_accessor :class_annotation
# @!attribute super_class
# @return [Rex::Java::Serialization::Model::ClassDesc] The java class superclass description
attr_accessor :super_class
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.class_name = nil
self.serial_version = 0
self.flags = 0
self.fields = []
self.class_annotation = nil
self.super_class = nil
end
# Deserializes a Rex::Java::Serialization::Model::ClassDescription
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode(io)
self.class_name = Utf.decode(io, stream)
self.serial_version = decode_serial_version(io)
stream.add_reference(self) unless stream.nil?
self.flags = decode_flags(io)
fields_length = decode_fields_length(io)
fields_length.times do
field = Field.decode(io, stream)
self.fields << field
end
self.class_annotation = Annotation.decode(io, stream)
self.super_class = ClassDesc.decode(io, stream)
self
end
# Serializes the Rex::Java::Serialization::Model::ClassDescription
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
def encode
unless class_name.class == Rex::Java::Serialization::Model::Utf &&
class_annotation.class == Rex::Java::Serialization::Model::Annotation &&
super_class.class == Rex::Java::Serialization::Model::ClassDesc
raise ::RuntimeError, 'Filed to serialize NewClassDesc'
end
encoded = ''
encoded << class_name.encode
encoded << [serial_version].pack('Q>')
stream.add_reference(self) unless stream.nil?
encoded << [flags].pack('C')
encoded << [fields.length].pack('n')
fields.each do |field|
encoded << field.encode
end
encoded << class_annotation.encode
encoded << super_class.encode
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = "#{class_name}, [ "
fields_str = []
fields.each do |field|
fields_str << field.to_s
end
str << "#{fields_str.join(', ')} ]"
case super_class.description
when NewClassDesc
str << ", @super_class: #{super_class.description.class_name.to_s}"
when Reference
str << ", @super_class: #{super_class.description.to_s}"
end
str
end
private
# Deserializes a class serial version
#
# @param io [IO] the io to read from
# @return [Integer] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode_serial_version(io)
raw_serial = io.read(8)
if raw_serial.nil? || raw_serial.length != 8
raise ::RuntimeError, 'Failed to unserialize ClassDescription'
end
raw_serial.unpack('Q>')[0]
end
# Deserializes a class flags
#
# @param io [IO] the io to read from
# @return [Integer] if deserialization is possible
# @raise [RuntimeError] if deserialization doesn't succeed
def decode_flags(io)
raw_flags = io.read(1)
raise ::RuntimeError, 'Failed to unserialize ClassDescription' if raw_flags.nil?
raw_flags.unpack('C')[0]
end
# Deserializes a class fields length
#
# @param io [IO] the io to read from
# @return [Integer] if deserialization is possible
# @raise [RuntimeError] if deserialization doesn't succeed
def decode_fields_length(io)
fields_length = io.read(2)
if fields_length.nil? || fields_length.length != 2
raise ::RuntimeError, 'Failed to unserialize ClassDescription'
end
fields_length.unpack('n')[0]
end
end
end
end
end
end

View File

@ -0,0 +1,79 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a NewEnum (Java Enum) representation
class NewEnum < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute enum_description
# @return [Rex::Java::Serialization::Model::ClassDescription] The description of the enum
attr_accessor :enum_description
# @!attribute constant_name
# @return [Rex::Java::Serialization::Model::Utf] The constant value in the Java Enum
attr_accessor :constant_name
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.enum_description = nil
self.constant_name = nil
end
# Deserializes a Rex::Java::Serialization::Model::NewEnum
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode(io)
self.enum_description = ClassDesc.decode(io, stream)
stream.add_reference(self) unless stream.nil?
self.constant_name = decode_constant_name(io)
self
end
# Serializes the Rex::Java::Serialization::Model::NewEnum
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
def encode
unless enum_description.class == ClassDesc &&
constant_name.class == Utf
raise ::RuntimeError, 'Failed to serialize EnumDescription'
end
encoded = ''
encoded << enum_description.encode
encoded << encode_content(constant_name)
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
constant_name.to_s
end
private
# Deserializes the NewEnum constant name
#
# @param io [IO] the io to read from
# @return [Rex::Java::Serialization::Model::Utf] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succed
def decode_constant_name(io)
content = decode_content(io, stream)
raise ::RuntimeError, 'Failed to unserialize NewEnum' unless content.class == Rex::Java::Serialization::Model::Utf
content
end
end
end
end
end
end

View File

@ -0,0 +1,223 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a NewObject (Java Object) representation
class NewObject < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute class_desc
# @return [Rex::Java::Serialization::Model::ClassDesc] The description of the object
attr_accessor :class_desc
# @!attribute class_data
# @return [Array] The data of the object
attr_accessor :class_data
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.class_desc = nil
self.class_data = []
end
# Deserializes a Rex::Java::Serialization::Model::NewObject
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode(io)
self.class_desc = ClassDesc.decode(io, stream)
stream.add_reference(self) unless stream.nil?
if class_desc.description.class == NewClassDesc
self.class_data = decode_class_data(io, class_desc.description)
elsif class_desc.description.class == Reference
ref = class_desc.description.handle - BASE_WIRE_HANDLE
self.class_data = decode_class_data(io, stream.references[ref])
end
self
end
# Serializes the Rex::Java::Serialization::Model::NewObject
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
def encode
unless class_desc.class == ClassDesc
raise ::RuntimeError, 'Failed to serialize NewObject'
end
encoded = ''
encoded << class_desc.encode
class_data.each do |value|
if value.class == Array
encoded << encode_value(value)
else
encoded << encode_content(value)
end
end
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = ''
if class_desc.description.class == NewClassDesc
str << class_desc.description.class_name.to_s
elsif class_desc.description.class == Reference
str << (class_desc.description.handle - BASE_WIRE_HANDLE).to_s(16)
end
str << ' => { '
data = class_data.collect { |data| data.to_s }
str << data.join(', ')
str << ' }'
end
private
# Deserializes the class_data for a class_desc and its super classes
#
# @param io [IO] the io to read from
# @param my_class_desc [Rex::Java::Serialization::Model::NewClassDesc] the class description whose data is being extracted
# @return [Array] class_data values if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode_class_data(io, my_class_desc)
values = []
unless my_class_desc.super_class.description.class == NullReference
values += decode_class_data(io, my_class_desc.super_class.description)
end
values += decode_class_fields(io, my_class_desc)
values
end
# Deserializes the fields data for a class_desc
#
# @param io [IO] the io to read from
# @param my_class_desc [Rex::Java::Serialization::Model::NewClassDesc] the class description whose data is being extracted
# @return [Array] class_data values if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode_class_fields(io, my_class_desc)
values = []
my_class_desc.fields.each do |field|
if field.is_primitive?
values << decode_value(io, field.type)
else
content = decode_content(io, stream)
values << content
end
end
values
end
# Deserializes a class_data value
#
# @param io [IO] the io to read from
# @param type [String] the type of the value to deserialize
# @return [Array(String, <Fixnum, Float>)] type and value if deserialization succeeds
# @raise [RuntimeError] if deserialization fails
def decode_value(io, type)
value = []
case type
when 'byte'
value_raw = io.read(1)
raise ::RuntimeError, 'Failed to deserialize NewArray value' if value_raw.nil?
value.push('byte', value_raw.unpack('c')[0])
when 'char'
value_raw = io.read(2)
unless value_raw && value_raw.length == 2
raise ::RuntimeError, 'Failed to deserialize NewArray value'
end
value.push('char', value_raw.unpack('s>')[0])
when 'double'
value_raw = io.read(8)
unless value_raw && value_raw.length == 8
raise ::RuntimeError, 'Failed to deserialize NewArray value'
end
value.push('double', value = value_raw.unpack('G')[0])
when 'float'
value_raw = io.read(4)
unless value_raw && value_raw.length == 4
raise ::RuntimeError, 'Failed to deserialize NewArray value'
end
value.push('float', value_raw.unpack('g')[0])
when 'int'
value_raw = io.read(4)
unless value_raw && value_raw.length == 4
raise ::RuntimeError, 'Failed to deserialize NewArray value'
end
value.push('int', value_raw.unpack('l>')[0])
when 'long'
value_raw = io.read(8)
unless value_raw && value_raw.length == 8
raise ::RuntimeError, 'Failed to deserialize NewArray value'
end
value.push('long', value_raw.unpack('q>')[0])
when 'short'
value_raw = io.read(2)
unless value_raw && value_raw.length == 2
raise ::RuntimeError, 'Failed to deserialize NewArray value'
end
value.push('short', value_raw.unpack('s>')[0])
when 'boolean'
value_raw = io.read(1)
raise ::RuntimeError, 'Failed to deserialize NewArray value' if value_raw.nil?
value.push('boolean', value_raw.unpack('c')[0])
else
raise ::RuntimeError, 'Unsupported NewArray type'
end
value
end
# Serializes an class_data value
#
# @param value [Array] the type and value to serialize
# @return [String] the serialized value
# @raise [RuntimeError] if serialization fails
def encode_value(value)
res = ''
case value[0]
when 'byte'
res = [value[1]].pack('c')
when 'char'
res = [value[1]].pack('s>')
when 'double'
res = [value[1]].pack('G')
when 'float'
res = [value[1]].pack('g')
when 'int'
res = [value[1]].pack('l>')
when 'long'
res = [value[1]].pack('q>')
when 'short'
res = [value[1]].pack('s>')
when 'boolean'
res = [value[1]].pack('c')
else
raise ::RuntimeError, 'Unsupported NewArray type'
end
res
end
end
end
end
end
end

View File

@ -0,0 +1,12 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
class NullReference < Element
end
end
end
end
end

View File

@ -0,0 +1,61 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a Java Reference representation.
class Reference < Element
# @!attribute contents
# @return [Fixnum] The stream handle being referenced
attr_accessor :handle
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.handle = 0
end
# Deserializes a Rex::Java::Serialization::Model::Reference
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode(io)
handle_raw = io.read(4)
unless handle_raw && handle_raw.length == 4
raise ::RuntimeError, 'Failed to unserialize Reference'
end
self.handle = handle_raw.unpack('N')[0]
self
end
# Serializes the Rex::Java::Serialization::Model::Reference
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
def encode
if handle < BASE_WIRE_HANDLE
raise ::RuntimeError, 'Failed to serialize Reference'
end
encoded = ''
encoded << [handle].pack('N')
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
"0x#{handle.to_s(16)}"
end
end
end
end
end
end

View File

@ -0,0 +1,12 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
class Reset < Element
end
end
end
end
end

View File

@ -0,0 +1,123 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a Java Stream representation
class Stream < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute magic
# @return [Fixnum] The stream signature
attr_accessor :magic
# @!attribute version
# @return [Fixnum] The stream version
attr_accessor :version
# @!attribute contents
# @return [Array] The stream contents
attr_accessor :contents
# @!attribute references
# @return [Array] The stream objects to be referenced through handles
attr_accessor :references
def initialize(stream = nil)
super(nil)
self.magic = STREAM_MAGIC
self.version = STREAM_VERSION
self.contents = []
self.references = []
end
# Deserializes a Rex::Java::Serialization::Model::Stream
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode(io)
self.magic = decode_magic(io)
self.version = decode_version(io)
until io.eof?
content = decode_content(io, self)
self.contents << content
end
self
end
# Serializes the Rex::Java::Serialization::Model::Stream
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
def encode
encoded = ''
encoded << [magic].pack('n')
encoded << [version].pack('n')
contents.each do |content|
encoded << encode_content(content)
end
encoded
end
# Adds an element to the references array
#
# @param io [Rex::Java::Serialization::Model::Element] the object to save as reference dst
def add_reference(ref)
self.references.push(ref)
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = "@magic: 0x#{magic.to_s(16)}\n"
str << "@version: #{version}\n"
str << "@contents: [\n"
contents.each do |content|
str << " #{print_content(content)}\n"
end
str << "]\n"
str << "@references: [\n"
references.each do |ref|
str << " [#{(references.index(ref) + BASE_WIRE_HANDLE).to_s(16)}] #{print_content(ref)}\n"
end
str << "]\n"
end
private
# Deserializes the magic stream value
#
# @param io [IO] the io to read from
# @return [String] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode_magic(io)
magic = io.read(2)
unless magic && magic.length == 2 && magic.unpack('n')[0] == STREAM_MAGIC
raise ::RuntimeError, 'Failed to unserialize Stream'
end
STREAM_MAGIC
end
# Deserializes the version stream
#
# @param io [IO] the io to read from
# @return [Fixnum] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode_version(io)
version = io.read(2)
unless version && version.unpack('n')[0] == STREAM_VERSION
raise ::RuntimeError, 'Failed to unserialize Stream'
end
STREAM_VERSION
end
end
end
end
end
end

View File

@ -0,0 +1,69 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a Utf string representation
class Utf < Element
# @!attribute length
# @return [Integer] the length of the string
attr_accessor :length
# @!attribute contents
# @return [String] the contents of the string
attr_accessor :contents
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
# @param contents [String] the contents of the utf string
def initialize(stream = nil, contents = '')
super(stream)
self.contents = contents
self.length = contents.length
end
# Deserializes a Rex::Java::Serialization::Model::Utf
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(2)
if raw_length.nil? || raw_length.length != 2
raise ::RuntimeError, 'Failed to unserialize Utf'
end
self.length = raw_length.unpack('n')[0]
if length == 0
self.contents = ''
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise ::RuntimeError, 'Failed to unserialize Utf'
end
end
self
end
# Serializes the Rex::Java::Serialization::Model::Utf
#
# @return [String]
def encode
encoded = [length].pack('n')
encoded << contents
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
contents
end
end
end
end
end
end

View File

@ -0,0 +1,105 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::Annotation do
subject(:annotation) do
described_class.new
end
let(:empty_contents) { "\x78" }
let(:empty_contents_io) { StringIO.new(empty_contents) }
let(:contents) { "\x77\x05\x01\x02\x03\x04\x05\x7a\x00\x00\x00\x05\x01\x02\x03\x04\x05\x78" }
let(:contents_io) { StringIO.new(contents) }
describe ".new" do
it "Rex::Java::Serialization::Model::Annotation" do
expect(annotation).to be_a(Rex::Java::Serialization::Model::Annotation)
end
it "initializes contents with empty array" do
expect(annotation.contents).to be_empty
end
end
describe "#encode" do
context "when empty contents" do
it do
annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
expect(annotation.encode).to eq(empty_contents)
end
end
context "when block data contents" do
it do
annotation.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x01\x02\x03\x04\x05")
annotation.contents << Rex::Java::Serialization::Model::BlockDataLong.new(nil, "\x01\x02\x03\x04\x05")
annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
expect(annotation.encode).to eq(contents)
end
end
end
describe "#decode" do
context "when empty contents" do
it "returns a Rex::Java::Serialization::Model::Annotation" do
expect(annotation.decode(empty_contents_io)).to be_a(Rex::Java::Serialization::Model::Annotation)
end
it "unserializes one content" do
annotation.decode(empty_contents_io)
expect(annotation.contents.length).to eq(1)
end
it "unserializes one EndBlockData content" do
annotation.decode(empty_contents_io)
expect(annotation.contents[0]).to be_a(Rex::Java::Serialization::Model::EndBlockData)
end
end
context "when block data contents" do
it "returns a Rex::Java::Serialization::Model::Annotation" do
expect(annotation.decode(contents_io)).to be_a(Rex::Java::Serialization::Model::Annotation)
end
it "deserializes contents" do
annotation.decode(contents_io)
expect(annotation.contents.length).to eq(3)
end
it "deserializes block data contents" do
annotation.decode(contents_io)
expect(annotation.contents[0]).to be_a_kind_of(Rex::Java::Serialization::Model::BlockData)
end
it "deserializes block data long contents" do
annotation.decode(contents_io)
expect(annotation.contents[1]).to be_a_kind_of(Rex::Java::Serialization::Model::BlockDataLong)
end
it "deserializes end block data" do
annotation.decode(contents_io)
expect(annotation.contents[2]).to be_a_kind_of(Rex::Java::Serialization::Model::EndBlockData)
end
end
end
describe "#to_s" do
it "prints an empty annotation" do
annotation.decode(empty_contents_io)
expect(annotation.to_s).to eq('[ EndBlockData ]')
end
it "prints an annotation with contents" do
annotation.decode(contents_io)
expect(annotation.to_s).to eq('[ BlockData { [ 0x1, 0x2, 0x3, 0x4, 0x5 ] }, BlockDataLong { [ 0x1, 0x2, 0x3, 0x4, 0x5 ] }, EndBlockData ]')
end
end
end

View File

@ -0,0 +1,107 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::BlockDataLong do
subject(:block) do
described_class.new
end
let(:sample_block) { "\x00\x00\x00\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" }
let(:sample_block_io) { StringIO.new(sample_block) }
let(:empty_block) { "\x00\x00\x00\x00" }
let(:empty_block_io) { StringIO.new(empty_block) }
let(:incomplete_block) { "\x00\x00\x00\x10\x01\x02\x03\x04\x05" }
let(:incomplete_block_io) { StringIO.new(incomplete_block) }
let(:empty_io) { StringIO.new('') }
describe ".new" do
it "Rex::Java::Serialization::Model::BlockDataLong" do
expect(block).to be_a(Rex::Java::Serialization::Model::BlockDataLong)
end
it "initializes length to 0" do
expect(block.length).to eq(0)
end
it "initializes contents with empty string" do
expect(block.contents).to be_empty
end
end
describe "#encode" do
context "when empty block" do
it { expect(block.encode).to eq(empty_block) }
end
context "when filled block" do
it do
block.length = 16
block.contents = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
expect(block.encode).to eq(sample_block)
end
end
end
describe "#decode" do
context "when stream contains empty string" do
it "returns nil" do
expect { block.decode(empty_io) }.to raise_error(::RuntimeError)
end
end
context "when stream contains empty block" do
it "returns a Rex::Java::Serialization::Model::BlockDataLong" do
expect(block.decode(empty_block_io)).to be_a(Rex::Java::Serialization::Model::BlockDataLong)
end
it "sets length to 0" do
block.decode(empty_block_io)
expect(block.length).to eq(0)
end
it "sets contents to empty string" do
block.decode(empty_block_io)
expect(block.contents).to be_empty
end
end
context "when stream contains incomplete block" do
it "returns nil" do
expect { block.decode(incomplete_block_io) }.to raise_error(::RuntimeError)
end
end
context "when stream contains correct block" do
it "returns a Rex::Java::Serialization::Model::BlockDataLong" do
expect(block.decode(sample_block_io)).to be_a(Rex::Java::Serialization::Model::BlockDataLong)
end
it "sets length to 0" do
block.decode(sample_block_io)
expect(block.length).to eq(16)
end
it "sets contents to sample string" do
block.decode(sample_block_io)
expect(block.contents).to eq("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10")
end
end
describe "#to_s" do
it "prints a block with contents" do
block.decode(sample_block_io)
expect(block.to_s).to eq('[ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10 ]')
end
it "prints an empty string for an empty block" do
block.decode(empty_block_io)
expect(block.to_s).to eq('[ ]')
end
end
end
end

View File

@ -0,0 +1,106 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::BlockData do
subject(:block) do
described_class.new
end
let(:sample_block) { "\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" }
let(:sample_block_io) { StringIO.new(sample_block) }
let(:empty_block) { "\x00" }
let(:empty_block_io) { StringIO.new(empty_block) }
let(:incomplete_block) { "\x10\x01\x02\x03\x04\x05" }
let(:incomplete_block_io) { StringIO.new(incomplete_block) }
let(:empty_io) { StringIO.new('') }
describe ".new" do
it "Rex::Java::Serialization::Model::BlockData" do
expect(block).to be_a(Rex::Java::Serialization::Model::BlockData)
end
it "initializes length to 0" do
expect(block.length).to eq(0)
end
it "initializes contents with empty string" do
expect(block.contents).to be_empty
end
end
describe "#encode" do
context "when empty block" do
it { expect(block.encode).to eq(empty_block) }
end
context "when filled block" do
it do
block.length = 16
block.contents = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
expect(block.encode).to eq(sample_block)
end
end
end
describe "#decode" do
context "when stream contains empty string" do
it "returns nil" do
expect { block.decode(empty_io) }.to raise_error(::RuntimeError)
end
end
context "when stream contains empty block" do
it "returns a Rex::Java::Serialization::Model::BlockData" do
expect(block.decode(empty_block_io)).to be_a(Rex::Java::Serialization::Model::BlockData)
end
it "sets length to 0" do
block.decode(empty_block_io)
expect(block.length).to eq(0)
end
it "sets contents to empty string" do
block.decode(empty_block_io)
expect(block.contents).to be_empty
end
end
context "when stream contains incomplete block" do
it "returns nil" do
expect { block.decode(incomplete_block_io) }.to raise_error(::RuntimeError)
end
end
context "when stream contains correct block" do
it "returns a Rex::Java::Serialization::Model::BlockData" do
expect(block.decode(sample_block_io)).to be_a(Rex::Java::Serialization::Model::BlockData)
end
it "sets length to 0" do
block.decode(sample_block_io)
expect(block.length).to eq(16)
end
it "sets contents to sample string" do
block.decode(sample_block_io)
expect(block.contents).to eq("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10")
end
end
end
describe "#to_s" do
it "prints a block with contents" do
block.decode(sample_block_io)
expect(block.to_s).to eq('[ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10 ]')
end
it "prints an empty string for an empty block" do
block.decode(empty_block_io)
expect(block.to_s).to eq('[ ]')
end
end
end

View File

@ -0,0 +1,81 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::ClassDesc do
subject(:class_desc) do
described_class.new
end
let(:sample) do
"\x72\x00\x0e\x6a\x61\x76\x61\x2e\x6c\x61\x6e" +
"\x67\x2e\x42\x79\x74\x65\x9c\x4e\x60\x84\xee\x50\xf5\x1c\x02\x00" +
"\x01\x42\x00\x05\x76\x61\x6c\x75\x65\x78\x72\x00\x10\x6a\x61\x76" +
"\x61\x2e\x6c\x61\x6e\x67\x2e\x4e\x75\x6d\x62\x65\x72\x86\xac\x95" +
"\x1d\x0b\x94\xe0\x8b\x02\x00\x00\x78\x70"
end
let(:sample_io) { StringIO.new(sample) }
describe ".new" do
it "Rex::Java::Serialization::Model::NewClassDesc" do
expect(class_desc).to be_a(Rex::Java::Serialization::Model::ClassDesc)
end
it "initializes description with nil" do
expect(class_desc.description).to be_nil
end
end
describe "#decode" do
it "returns a Rex::Java::Serialization::Model::ClassDesc" do
expect(class_desc.decode(sample_io)).to be_a(Rex::Java::Serialization::Model::ClassDesc)
end
it "deserializes the description correctly" do
class_desc.decode(sample_io)
expect(class_desc.description).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
end
describe "#encode" do
it "serializes a ClassDesc" do
super_class_desc_new = Rex::Java::Serialization::Model::NewClassDesc.new
super_class_desc_new.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.Number')
super_class_desc_new.serial_version = 0x86ac951d0b94e08b
super_class_desc_new.flags = 2
super_class_desc_new.class_annotation = Rex::Java::Serialization::Model::Annotation.new
super_class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
super_class_desc_new.super_class = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc_new.super_class.description = Rex::Java::Serialization::Model::NullReference.new
super_class_desc = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc.description = super_class_desc_new
class_desc_new = Rex::Java::Serialization::Model::NewClassDesc.new
class_desc_new.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.Byte')
class_desc_new.serial_version = 0x9c4e6084ee50f51c
class_desc_new.flags = 2
field = Rex::Java::Serialization::Model::Field.new
field.type = 'byte'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'value')
class_desc_new.fields << field
class_desc_new.class_annotation = Rex::Java::Serialization::Model::Annotation.new
class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
class_desc_new.super_class = super_class_desc
class_desc.description = class_desc_new
expect(class_desc.encode.unpack("C*")).to eq(sample.unpack("C*"))
end
end
describe "#to_s" do
it "prints a sample ClassDesc" do
class_desc.decode(sample_io)
expect(class_desc.to_s).to be_a(String)
end
end
end

View File

@ -0,0 +1,108 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::Field do
subject(:field) do
described_class.new
end
let(:sample_primitive) { "I\x00\x06number" }
let(:sample_primitive_io) { StringIO.new(sample_primitive) }
let(:sample_object) { "[\x00\x0atest_arrayt\x00\x0b[LEmployee;" }
let(:sample_object_io) { StringIO.new(sample_object) }
describe ".new" do
it "Rex::Java::Serialization::Model::Field" do
expect(field).to be_a(Rex::Java::Serialization::Model::Field)
end
it "initializes code with empty string" do
expect(field.type).to be_empty
end
it "initializes name with nil" do
expect(field.name).to be_nil
end
it "initializes field_type with nil" do
expect(field.field_type).to be_nil
end
end
describe "#encode" do
context "when empty field" do
it { expect { field.encode }.to raise_error(::RuntimeError) }
end
context "when primitive field" do
it do
field.type = 'int'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'number')
expect(field.encode).to eq(sample_primitive)
end
end
context "when object field" do
it do
field.type = 'array'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'test_array')
field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[LEmployee;')
expect(field.encode).to eq(sample_object)
end
end
end
describe "#decode" do
context "when stream contains a primitive field" do
it "returns a Rex::Java::Serialization::Model::Field" do
expect(field.decode(sample_primitive_io)).to be_a(Rex::Java::Serialization::Model::Field)
end
it "deserializes field type" do
field.decode(sample_primitive_io)
expect(field.type).to eq('int')
end
it "deserializes field name as Utf" do
field.decode(sample_primitive_io)
expect(field.name.contents).to eq('number')
end
end
context "when stream contains an object field" do
it "returns a Rex::Java::Serialization::Model::Field" do
expect(field.decode(sample_object_io)).to be_a(Rex::Java::Serialization::Model::Field)
end
it "deserializes field type" do
field.decode(sample_object_io)
expect(field.type).to eq('array')
end
it "deserializes field name" do
field.decode(sample_object_io)
expect(field.name.contents).to eq('test_array')
end
it "deserializes field_type string" do
field.decode(sample_object_io)
expect(field.field_type.contents).to eq('[LEmployee;')
end
end
end
describe "#to_s" do
it "prints an stream containing a primitive field" do
field.decode(sample_primitive_io)
expect(field.to_s).to eq('number (int)')
end
it "prints an stream containing an object field" do
field.decode(sample_object_io)
expect(field.to_s).to eq('test_array ([LEmployee;)')
end
end
end

View File

@ -0,0 +1,107 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::LongUtf do
subject(:long_utf) do
described_class.new
end
let(:sample_utf) { "\x00\x00\x00\x00\x00\x00\x00\x10java.lang.Number" }
let(:sample_utf_io) { StringIO.new(sample_utf) }
let(:empty_utf) { "\x00\x00\x00\x00\x00\x00\x00\x00" }
let(:empty_utf_io) { StringIO.new(empty_utf) }
let(:incomplete_utf) { "\x00\x00\x00\x00\x00\x00\x00\x10java.lang.Numb" }
let(:incomplete_utf_io) { StringIO.new(incomplete_utf) }
let(:empty_io) { StringIO.new('') }
describe ".new" do
it "Rex::Java::Serialization::Model::LongUtf" do
expect(long_utf).to be_a(Rex::Java::Serialization::Model::LongUtf)
end
it "initializes length to 0" do
expect(long_utf.length).to eq(0)
end
it "initializes contents with empty string" do
expect(long_utf.contents).to be_empty
end
end
describe "#encode" do
context "when empty long_utf" do
it { expect(long_utf.encode).to eq(empty_utf) }
end
context "when filled utf" do
it do
long_utf.length = 16
long_utf.contents = 'java.lang.Number'
expect(long_utf.encode).to eq(sample_utf)
end
end
end
describe "#decode" do
context "when stream contains empty string" do
it "raises RuntimeError" do
expect { long_utf.decode(empty_io) }.to raise_error(::RuntimeError)
end
end
context "when stream contains empty long_utf" do
it "returns a Rex::Java::Serialization::Model::LongUtf" do
expect(long_utf.decode(empty_utf_io)).to be_a(Rex::Java::Serialization::Model::LongUtf)
end
it "sets length to 0" do
long_utf.decode(empty_utf_io)
expect(long_utf.length).to eq(0)
end
it "sets contents to empty string" do
long_utf.decode(empty_utf_io)
expect(long_utf.contents).to be_empty
end
end
context "when stream contains incomplete long_utf" do
it "returns nil" do
expect { long_utf.decode(incomplete_utf_io) }.to raise_error(::RuntimeError)
end
end
context "when stream contains correct long_utf" do
it "returns a Rex::Java::Serialization::Model::LongUtf" do
expect(long_utf.decode(sample_utf_io)).to be_a(Rex::Java::Serialization::Model::LongUtf)
end
it "sets length to 0" do
long_utf.decode(sample_utf_io)
expect(long_utf.length).to eq(16)
end
it "sets contents to sample string" do
long_utf.decode(sample_utf_io)
expect(long_utf.contents).to eq('java.lang.Number')
end
end
describe "#to_s" do
it "prints an stream containing a sample long utf" do
long_utf.decode(sample_utf_io)
expect(long_utf.to_s).to eq('java.lang.Number')
end
it "prints an stream containing an empty long utf" do
long_utf.decode(empty_utf_io)
expect(long_utf.to_s).to eq('')
end
end
end
end

View File

@ -0,0 +1,469 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::NewArray do
subject(:new_array) do
described_class.new
end
let(:boolean_array) do
"\x72\x00\x02\x5b\x5a\x57\x8f\x20" +
"\x39\x14\xb8\x5d\xe2\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x0a\x01\x00" +
"\x01\x01\x01\x01\x01\x01\x01\x00"
end
let(:boolean_array_io) { StringIO.new(boolean_array) }
let(:byte_array) do
"\x72\x00\x02\x5b\x42\xac\xf3\x17" +
"\xf8\x06\x08\x54\xe0\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\xec\x41"
end
let(:byte_array_io) { StringIO.new(byte_array) }
let(:char_array) do
"\x72\x00\x02\x5b\x43\xb0\x26\x66" +
"\xb0\xe2\x5d\x84\xac\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\x00\x61" +
"\x00\x62"
end
let(:char_array_io) { StringIO.new(char_array) }
let(:short_array) do
"\x72\x00\x02\x5b\x53\xef\x83\x2e" +
"\x06\xe5\x5d\xb0\xfa\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\xff\xec" +
"\x00\x41"
end
let(:short_array_io) { StringIO.new(short_array) }
let(:double_array) do
"\x72\x00\x02\x5b\x44\x3e\xa6\x8c" +
"\x14\xab\x63\x5a\x1e\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\x3f\xd0" +
"\x00\x00\x00\x00\x00\x00\x3f\xca" +
"\xe1\x47\xae\x14\x7a\xe1"
end
let(:double_array_io) { StringIO.new(double_array) }
let(:float_array) do
"\x72\x00\x02\x5b\x46\x0b\x9c\x81" +
"\x89\x22\xe0\x0c\x42\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\x3f\x80" +
"\x00\x00\x40\x00\x00\x00"
end
let(:float_array_io) { StringIO.new(float_array) }
let(:int_array) do
"\x72\x00\x02\x5b\x49\x4d\xba\x60" +
"\x26\x76\xea\xb2\xa5\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\xff\xff" +
"\xff\xec\x00\x00\x00\x41"
end
let(:int_array_io) { StringIO.new(int_array) }
let(:long_array) do
"\x72\x00\x02\x5b\x4a\x78\x20\x04" +
"\xb5\x12\xb1\x75\x93\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\xff\xff" +
"\xff\xff\xff\xff\xff\xec\x00\x00" +
"\x00\x00\x00\x00\x00\x41"
end
let(:long_array_io) { StringIO.new(long_array) }
let(:string_array) do
"\x72\x00\x13\x5b\x4c\x6a\x61\x76" +
"\x61\x2e\x6c\x61\x6e\x67\x2e\x53" +
"\x74\x72\x69\x6e\x67\x3b\xad\xd2" +
"\x56\xe7\xe9\x1d\x7b\x47\x02\x00" +
"\x00\x78\x70\x00\x00\x00\x01\x74" +
"\x00\x03\x6d\x73\x66"
end
let(:string_array_io) { StringIO.new(string_array) }
describe ".new" do
it "Rex::Java::Serialization::Model::NewArray" do
expect(new_array).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "initializes array_description with nil" do
expect(new_array.array_description).to be_nil
end
it "initializes type with an empty String" do
expect(new_array.type).to be_empty
end
it "initializes values with an empty Array" do
expect(new_array.values).to be_empty
end
end
describe "#decode" do
context "when boolean Array" do
it "deserializes Array" do
expect(new_array.decode(boolean_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(boolean_array_io)
expect(new_array.type).to eq('boolean')
end
it "deserializes values correctly" do
new_array.decode(boolean_array_io)
expect(new_array.values).to eq([1, 0, 1, 1, 1, 1, 1, 1, 1, 0])
end
end
context "when byte Array" do
it "deserializes Array" do
expect(new_array.decode(byte_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(byte_array_io)
expect(new_array.type).to eq('byte')
end
it "deserializes values correctly" do
new_array.decode(byte_array_io)
expect(new_array.values).to eq([-20, 65])
end
end
context "when char Array" do
it "deserializes Array" do
expect(new_array.decode(char_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(char_array_io)
expect(new_array.type).to eq('char')
end
it "deserializes values correctly" do
new_array.decode(char_array_io)
expect(new_array.values).to eq([97, 98])
end
end
context "when short Array" do
it "deserializes Array" do
expect(new_array.decode(short_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(short_array_io)
expect(new_array.type).to eq('short')
end
it "deserializes values correctly" do
new_array.decode(short_array_io)
expect(new_array.values).to eq([-20, 65])
end
end
context "when double Array" do
it "deserializes Array" do
expect(new_array.decode(double_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(double_array_io)
expect(new_array.type).to eq('double')
end
it "deserializes values correctly" do
new_array.decode(double_array_io)
expect(new_array.values).to eq([0.25, 0.21])
end
end
context "when float Array" do
it "deserializes a float Array" do
expect(new_array.decode(float_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(float_array_io)
expect(new_array.type).to eq('float')
end
it "deserializes values correctly" do
new_array.decode(float_array_io)
expect(new_array.values).to eq([1.0, 2.0])
end
end
context "when int Array" do
it "deserializes Array" do
expect(new_array.decode(int_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(int_array_io)
expect(new_array.type).to eq('int')
end
it "deserializes values correctly" do
new_array.decode(int_array_io)
expect(new_array.values).to eq([-20, 65])
end
end
context "when long Array" do
it "deserializes Array" do
expect(new_array.decode(long_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(long_array_io)
expect(new_array.type).to eq('long')
end
it "deserializes values correctly" do
new_array.decode(long_array_io)
expect(new_array.values).to eq([-20, 65])
end
end
context "when Strings (Objects) array" do
it "deserializes the array" do
expect(new_array.decode(string_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(string_array_io)
expect(new_array.type).to eq('java.lang.String;')
end
it "deserializes number of members correctly" do
new_array.decode(string_array_io)
expect(new_array.values.length).to eq(1)
end
it "deserializes the members correctly" do
new_array.decode(string_array_io)
expect(new_array.values[0].contents).to eq('msf')
end
end
end
describe "#encode" do
it "serializes a boolean Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Z')
new_class_desc.serial_version = 0x578f203914b85de2
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'boolean'
new_array.values = [1, 0, 1, 1, 1, 1, 1, 1, 1, 0]
expect(new_array.encode.unpack("C*")).to eq(boolean_array.unpack("C*"))
end
it "serializes a byte Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[B')
new_class_desc.serial_version = 0xacf317f8060854e0
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'byte'
new_array.values = [-20, 65]
expect(new_array.encode.unpack("C*")).to eq(byte_array.unpack("C*"))
end
it "serializes a char Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[C')
new_class_desc.serial_version = 0xb02666b0e25d84ac
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'char'
new_array.values = [97, 98]
expect(new_array.encode.unpack("C*")).to eq(char_array.unpack("C*"))
end
it "serializes a short Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[S')
new_class_desc.serial_version = 0xef832e06e55db0fa
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'short'
new_array.values = [-20, 65]
expect(new_array.encode.unpack("C*")).to eq(short_array.unpack("C*"))
end
it "serializes a double Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[D')
new_class_desc.serial_version = 0x3ea68c14ab635a1e
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'double'
new_array.values = [0.25, 0.21]
expect(new_array.encode.unpack("C*")).to eq(double_array.unpack("C*"))
end
it "serializes a float Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[F')
new_class_desc.serial_version = 0xb9c818922e00c42
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'float'
new_array.values = [1.0, 2.0]
expect(new_array.encode.unpack("C*")).to eq(float_array.unpack("C*"))
end
it "serializes a int Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[I')
new_class_desc.serial_version = 0x4dba602676eab2a5
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'int'
new_array.values = [-20, 65]
expect(new_array.encode.unpack("C*")).to eq(int_array.unpack("C*"))
end
it "serializes a long Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[J')
new_class_desc.serial_version = 0x782004b512b17593
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'long'
new_array.values = [-20, 65]
expect(new_array.encode.unpack("C*")).to eq(long_array.unpack("C*"))
end
it "serializes a String (Objects) Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;')
new_class_desc.serial_version = 0xadd256e7e91d7b47
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'java.lang.String;'
new_array.values = [ Rex::Java::Serialization::Model::Utf.new(nil, 'msf') ]
expect(new_array.encode.unpack("C*")).to eq(string_array.unpack("C*"))
end
end
describe "#to_s" do
it "prints a boolean array stream" do
new_array.decode(boolean_array_io)
expect(new_array.to_s).to eq('boolean, ["1", "0", "1", "1", "1", "1", "1", "1", "1", "0"]')
end
it "prints a byte array stream" do
new_array.decode(byte_array_io)
expect(new_array.to_s).to eq('byte, ["-20", "65"]')
end
it "prints a char array stream" do
new_array.decode(char_array_io)
expect(new_array.to_s).to eq('char, ["97", "98"]')
end
it "prints a short array stream" do
new_array.decode(short_array_io)
expect(new_array.to_s).to eq('short, ["-20", "65"]')
end
it "prints a double array stream" do
new_array.decode(double_array_io)
expect(new_array.to_s).to eq('double, ["0.25", "0.21"]')
end
it "prints a float array stream" do
new_array.decode(float_array_io)
expect(new_array.to_s).to eq('float, ["1.0", "2.0"]')
end
it "prints a int array stream" do
new_array.decode(int_array_io)
expect(new_array.to_s).to eq('int, ["-20", "65"]')
end
it "prints a long array stream" do
new_array.decode(long_array_io)
expect(new_array.to_s).to eq('long, ["-20", "65"]')
end
it "prints a string array stream" do
new_array.decode(string_array_io)
expect(new_array.to_s).to eq('java.lang.String;, ["msf"]')
end
end
end

View File

@ -0,0 +1,143 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::NewClassDesc do
subject(:class_desc_new) do
described_class.new
end
let(:sample) do
"\x00\x0e\x6a\x61\x76\x61\x2e\x6c\x61\x6e" +
"\x67\x2e\x42\x79\x74\x65\x9c\x4e\x60\x84\xee\x50\xf5\x1c\x02\x00" +
"\x01\x42\x00\x05\x76\x61\x6c\x75\x65\x78\x72\x00\x10\x6a\x61\x76" +
"\x61\x2e\x6c\x61\x6e\x67\x2e\x4e\x75\x6d\x62\x65\x72\x86\xac\x95" +
"\x1d\x0b\x94\xe0\x8b\x02\x00\x00\x78\x70"
end
let(:sample_io) { StringIO.new(sample) }
describe ".new" do
it "Rex::Java::Serialization::Model::NewClassDesc" do
expect(class_desc_new).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
it "initializes class_name with nil" do
expect(class_desc_new.class_name).to be_nil
end
it "initializes serial_version with 0" do
expect(class_desc_new.serial_version).to eq(0)
end
it "initializes flags with 0" do
expect(class_desc_new.flags).to eq(0)
end
it "initializes fields with empty Array" do
expect(class_desc_new.fields).to be_empty
end
it "initializes class_annotation with nil" do
expect(class_desc_new.class_annotation).to be_nil
end
it "initializes super_class with nil" do
expect(class_desc_new.super_class).to be_nil
end
end
describe "#decode" do
it "returns a Rex::Java::Serialization::Model::NewClassDesc" do
expect(class_desc_new.decode(sample_io)).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
it "deserializes class_name as Utf" do
class_desc_new.decode(sample_io)
expect(class_desc_new.class_name).to be_a(Rex::Java::Serialization::Model::Utf)
end
it "deserializes class_name contents correctly" do
class_desc_new.decode(sample_io)
expect(class_desc_new.class_name.contents).to eq('java.lang.Byte')
end
it "deserializes serial_version correctly" do
class_desc_new.decode(sample_io)
expect(class_desc_new.serial_version).to eq(0x9c4e6084ee50f51c)
end
it "deserializes flags correctly" do
class_desc_new.decode(sample_io)
expect(class_desc_new.flags).to eq(2)
end
it "deserializes fields" do
class_desc_new.decode(sample_io)
expect(class_desc_new.fields.length).to eq(1)
end
it "deserializes fields contents correctly" do
class_desc_new.decode(sample_io)
expect(class_desc_new.fields[0].type).to eq('byte')
end
it "deserializes class annotation correctly" do
class_desc_new.decode(sample_io)
expect(class_desc_new.class_annotation).to be_a(Rex::Java::Serialization::Model::Annotation)
end
it "deserializes class annotation contents" do
class_desc_new.decode(sample_io)
expect(class_desc_new.class_annotation.contents[0]).to be_a(Rex::Java::Serialization::Model::EndBlockData)
end
it "deserializes super_class" do
class_desc_new.decode(sample_io)
expect(class_desc_new.super_class).to be_a(Rex::Java::Serialization::Model::ClassDesc)
end
it "deserializes super class description" do
class_desc_new.decode(sample_io)
expect(class_desc_new.super_class.description).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
end
describe "#encode" do
it "serializes a NewClassDesc" do
super_class_desc_new = Rex::Java::Serialization::Model::NewClassDesc.new
super_class_desc_new.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.Number')
super_class_desc_new.serial_version = 0x86ac951d0b94e08b
super_class_desc_new.flags = 2
super_class_desc_new.class_annotation = Rex::Java::Serialization::Model::Annotation.new
super_class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
super_class_desc_new.super_class = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc_new.super_class.description = Rex::Java::Serialization::Model::NullReference.new
super_class_desc = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc.description = super_class_desc_new
class_desc_new.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.Byte')
class_desc_new.serial_version = 0x9c4e6084ee50f51c
class_desc_new.flags = 2
field = Rex::Java::Serialization::Model::Field.new
field.type = 'byte'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'value')
class_desc_new.fields << field
class_desc_new.class_annotation = Rex::Java::Serialization::Model::Annotation.new
class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
class_desc_new.super_class = super_class_desc
expect(class_desc_new.encode.unpack("C*")).to eq(sample.unpack("C*"))
end
end
describe "#to_s" do
it "prints a sample NewClassDesc stream" do
class_desc_new.decode(sample_io)
expect(class_desc_new.to_s).to eq('java.lang.Byte, [ value (byte) ], @super_class: java.lang.Number')
end
end
end

View File

@ -0,0 +1,83 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::NewEnum do
subject(:new_enum) do
described_class.new
end
let(:sample_enum) do
"\x72\x00\x09\x45\x6e\x75\x6d\x73" +
"\x24\x44\x61\x79\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x12\x00\x00\x78" +
"\x72\x00\x0e\x6a\x61\x76\x61\x2e" +
"\x6c\x61\x6e\x67\x2e\x45\x6e\x75" +
"\x6d\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x12\x00\x00\x78\x70\x74\x00" +
"\x06\x53\x55\x4e\x44\x41\x59"
end
let(:sample_enum_io) { StringIO.new(sample_enum) }
describe ".new" do
it "Rex::Java::Serialization::Model::NewEnum" do
expect(new_enum).to be_a(Rex::Java::Serialization::Model::NewEnum)
end
it "initializes enum_description with nil" do
expect(new_enum.enum_description).to be_nil
end
it "initializes constant_name with nil" do
expect(new_enum.constant_name).to be_nil
end
end
describe "#decode" do
it "deserializes an Enum" do
expect(new_enum.decode(sample_enum_io)).to be_a(Rex::Java::Serialization::Model::NewEnum)
end
it "deserializes the constant_name correctly" do
new_enum.decode(sample_enum_io)
expect(new_enum.constant_name.contents).to eq('SUNDAY')
end
end
describe "#encode" do
it "serializes an Enum" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'Enums$Day')
new_class_desc.serial_version = 0
new_class_desc.flags = 18
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.super_class.description.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.Enum')
new_class_desc.super_class.description.serial_version = 0
new_class_desc.super_class.description.flags = 18
new_class_desc.super_class.description.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.super_class.description.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class.description.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_enum.enum_description = Rex::Java::Serialization::Model::ClassDesc.new
new_enum.enum_description.description = new_class_desc
new_enum.constant_name = Rex::Java::Serialization::Model::Utf.new(nil, 'SUNDAY')
expect(new_enum.encode.unpack("C*")).to eq(sample_enum.unpack("C*"))
end
end
describe "#to_s" do
it "prints a sample NewEnum stream" do
new_enum.decode(sample_enum_io)
expect(new_enum.to_s).to eq('SUNDAY')
end
end
end

View File

@ -0,0 +1,82 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::NewObject do
subject(:new_object) do
described_class.new
end
let(:easy_object) do
"\x72\x00\x04\x45\x61\x73\x79\x74" +
"\x1d\xe1\xbc\xbb\x2f\xcb\xaa\x02" +
"\x00\x01\x49\x00\x03\x53\x53\x4e" +
"\x78\x70\x41\x42\x43\x44"
end
let(:easy_object_io) { StringIO.new(easy_object) }
describe ".new" do
it "Rex::Java::Serialization::Model::NewObject" do
expect(new_object).to be_a(Rex::Java::Serialization::Model::NewObject)
end
it "initializes class_desc with nil" do
expect(new_object.class_desc).to be_nil
end
it "initializes class_data with empty array" do
expect(new_object.class_data).to be_empty
end
end
describe "#decode" do
it "deserializes an object" do
expect(new_object.decode(easy_object_io)).to be_a(Rex::Java::Serialization::Model::NewObject)
end
it "deserializes the object class fields correctly" do
new_object.decode(easy_object_io)
expect(new_object.class_desc.description.fields.length).to eq(1)
end
it "deserializes the object class data correctly" do
new_object.decode(easy_object_io)
expect(new_object.class_data).to eq([['int', 0x41424344]])
end
end
describe "#encode" do
it "serializes an Object" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'Easy')
new_class_desc.serial_version = 0x741de1bcbb2fcbaa
new_class_desc.flags = 2
field = Rex::Java::Serialization::Model::Field.new
field.type = 'int'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'SSN')
new_class_desc.fields << field
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
new_object.class_desc.description = new_class_desc
new_object.class_data = [['int', 0x41424344]]
expect(new_object.encode.unpack("C*")).to eq(easy_object.unpack("C*"))
end
end
describe "#to_s" do
it "prints a sample Object stream" do
new_object.decode(easy_object_io)
expect(new_object.to_s).to eq('Easy => { ["int", 1094861636] }')
end
end
end

View File

@ -0,0 +1,264 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::Stream do
subject(:stream) do
described_class.new
end
let(:easy_object_stream) do
"\xac\xed\x00\x05\x73\x72\x00\x04" +
"\x45\x61\x73\x79\x74\x1d\xe1\xbc" +
"\xbb\x2f\xcb\xaa\x02\x00\x01\x49" +
"\x00\x03\x53\x53\x4e\x78\x70\x41" +
"\x42\x43\x44"
end
let(:easy_object_stream_io) { StringIO.new(easy_object_stream) }
let(:easy_object_stream_to_s) {
<<-EOS
@magic: 0xaced
@version: 5
@contents: [
NewObject { Easy => { ["int", 1094861636] } }
]
@references: [
[7e0000] NewClassDesc { Easy, [ SSN (int) ] }
[7e0001] NewObject { Easy => { ["int", 1094861636] } }
]
EOS
}
let(:char_array_stream) do
"\xac\xed\x00\x05\x75\x72\x00\x02" +
"\x5b\x43\xb0\x26\x66\xb0\xe2\x5d" +
"\x84\xac\x02\x00\x00\x78\x70\x00" +
"\x00\x00\x02\x00\x61\x00\x62"
end
let(:char_array_stream_io) { StringIO.new(char_array_stream) }
let(:char_array_stream_to_s) {
<<-EOS
@magic: 0xaced
@version: 5
@contents: [
NewArray { char, ["97", "98"] }
]
@references: [
[7e0000] NewClassDesc { [C, [ ] }
[7e0001] NewArray { char, ["97", "98"] }
]
EOS
}
let(:complex_stream) do
"\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" +
"\xf6\xb6\x89\x8d\x8b\xf2\x86\x43\x75\x72\x00\x18\x5b\x4c\x6a\x61" +
"\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x4f\x62" +
"\x6a\x49\x44\x3b\x87\x13\x00\xb8\xd0\x2c\x64\x7e\x02\x00\x00\x70" +
"\x78\x70\x00\x00\x00\x01\x73\x72\x00\x15\x6a\x61\x76\x61\x2e\x72" +
"\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x4f\x62\x6a\x49\x44\xa7" +
"\x5e\xfa\x12\x8d\xdc\xe5\x5c\x02\x00\x02\x4a\x00\x06\x6f\x62\x6a" +
"\x4e\x75\x6d\x4c\x00\x05\x73\x70\x61\x63\x65\x74\x00\x15\x4c\x6a" +
"\x61\x76\x61\x2f\x72\x6d\x69\x2f\x73\x65\x72\x76\x65\x72\x2f\x55" +
"\x49\x44\x3b\x70\x78\x70\x0d\xc1\x1e\x2a\x94\x5e\x2f\xb2\x73\x72" +
"\x00\x13\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65" +
"\x72\x2e\x55\x49\x44\x0f\x12\x70\x0d\xbf\x36\x4f\x12\x02\x00\x03" +
"\x53\x00\x05\x63\x6f\x75\x6e\x74\x4a\x00\x04\x74\x69\x6d\x65\x49" +
"\x00\x06\x75\x6e\x69\x71\x75\x65\x70\x78\x70\x80\x16\x00\x00\x01" +
"\x49\xb5\xe4\x92\x78\xd2\x4f\xdf\x47\x77\x08\x80\x00\x00\x00\x00" +
"\x00\x00\x01\x73\x72\x00\x12\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e" +
"\x64\x67\x63\x2e\x4c\x65\x61\x73\x65\xb0\xb5\xe2\x66\x0c\x4a\xdc" +
"\x34\x02\x00\x02\x4a\x00\x05\x76\x61\x6c\x75\x65\x4c\x00\x04\x76" +
"\x6d\x69\x64\x74\x00\x13\x4c\x6a\x61\x76\x61\x2f\x72\x6d\x69\x2f" +
"\x64\x67\x63\x2f\x56\x4d\x49\x44\x3b\x70\x78\x70\x00\x00\x00\x00" +
"\x00\x09\x27\xc0\x73\x72\x00\x11\x6a\x61\x76\x61\x2e\x72\x6d\x69" +
"\x2e\x64\x67\x63\x2e\x56\x4d\x49\x44\xf8\x86\x5b\xaf\xa4\xa5\x6d" +
"\xb6\x02\x00\x02\x5b\x00\x04\x61\x64\x64\x72\x74\x00\x02\x5b\x42" +
"\x4c\x00\x03\x75\x69\x64\x71\x00\x7e\x00\x03\x70\x78\x70\x75\x72" +
"\x00\x02\x5b\x42\xac\xf3\x17\xf8\x06\x08\x54\xe0\x02\x00\x00\x70" +
"\x78\x70\x00\x00\x00\x08\x6b\x02\xc7\x72\x60\x1c\xc7\x95\x73\x71" +
"\x00\x7e\x00\x05\x80\x01\x00\x00\x01\x49\xb5\xf8\x00\xea\xe9\x62" +
"\xc1\xc0"
end
let(:complex_stream_io) { StringIO.new(complex_stream) }
let(:complex_stream_to_s) {
<<-EOS
@magic: 0xaced
@version: 5
@contents: [
BlockData { [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf6, 0xb6, 0x89, 0x8d, 0x8b, 0xf2, 0x86, 0x43 ] }
NewArray { java.rmi.server.ObjID;, ["java.rmi.server.ObjID => { [\\"long\\", 991106561224880050], java.rmi.server.UID => { [\\"short\\", -32746], [\\"long\\", 1416095896184], [\\"int\\", -766517433] } }"] }
BlockData { [ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 ] }
NewObject { java.rmi.dgc.Lease => { ["long", 600000], java.rmi.dgc.VMID => { byte, ["107", "2", "-57", "114", "96", "28", "-57", "-107"], 5 => { ["short", -32767], ["long", 1416097169642], ["int", -379403840] } } } }
]
@references: [
[7e0000] NewClassDesc { [Ljava.rmi.server.ObjID;, [ ] }
[7e0001] NewArray { java.rmi.server.ObjID;, ["java.rmi.server.ObjID => { [\\"long\\", 991106561224880050], java.rmi.server.UID => { [\\"short\\", -32746], [\\"long\\", 1416095896184], [\\"int\\", -766517433] } }"] }
[7e0002] NewClassDesc { java.rmi.server.ObjID, [ objNum (long), space (Ljava/rmi/server/UID;) ] }
[7e0003] Utf { Ljava/rmi/server/UID; }
[7e0004] NewObject { java.rmi.server.ObjID => { ["long", 991106561224880050], java.rmi.server.UID => { ["short", -32746], ["long", 1416095896184], ["int", -766517433] } } }
[7e0005] NewClassDesc { java.rmi.server.UID, [ count (short), time (long), unique (int) ] }
[7e0006] NewObject { java.rmi.server.UID => { ["short", -32746], ["long", 1416095896184], ["int", -766517433] } }
[7e0007] NewClassDesc { java.rmi.dgc.Lease, [ value (long), vmid (Ljava/rmi/dgc/VMID;) ] }
[7e0008] Utf { Ljava/rmi/dgc/VMID; }
[7e0009] NewObject { java.rmi.dgc.Lease => { ["long", 600000], java.rmi.dgc.VMID => { byte, ["107", "2", "-57", "114", "96", "28", "-57", "-107"], 5 => { ["short", -32767], ["long", 1416097169642], ["int", -379403840] } } } }
[7e000a] NewClassDesc { java.rmi.dgc.VMID, [ addr ([B), uid (0x7e0003) ] }
[7e000b] Utf { [B }
[7e000c] NewObject { java.rmi.dgc.VMID => { byte, ["107", "2", "-57", "114", "96", "28", "-57", "-107"], 5 => { ["short", -32767], ["long", 1416097169642], ["int", -379403840] } } }
[7e000d] NewClassDesc { [B, [ ] }
[7e000e] NewArray { byte, ["107", "2", "-57", "114", "96", "28", "-57", "-107"] }
[7e000f] NewObject { 5 => { ["short", -32767], ["long", 1416097169642], ["int", -379403840] } }
]
EOS
}
describe ".new" do
it "Rex::Java::Serialization::Model::Stream" do
expect(stream).to be_a(Rex::Java::Serialization::Model::Stream)
end
it "initializes magic with java serialized stream signature" do
expect(stream.magic).to eq(Rex::Java::Serialization::STREAM_MAGIC)
end
it "initializes version with java serialized stream default version " do
expect(stream.version).to eq(Rex::Java::Serialization::STREAM_VERSION)
end
it "initializes references as empty array " do
expect(stream.references).to be_empty
end
it "initializes stream to nil by default" do
expect(stream.stream).to be_nil
end
end
describe "#decode" do
context "when deserializing a simple Object stream" do
it "deserializes an Stream" do
expect(stream.decode(easy_object_stream_io)).to be_a(Rex::Java::Serialization::Model::Stream)
end
it "deserializes the signature correctly" do
stream.decode(easy_object_stream_io)
expect(stream.magic).to eq(Rex::Java::Serialization::STREAM_MAGIC)
end
it "deserializes all the contents" do
stream.decode(easy_object_stream_io)
expect(stream.contents.length).to eq(1)
end
it "deserializes a simple object correctly" do
stream.decode(easy_object_stream_io)
expect(stream.contents[0]).to be_an(Rex::Java::Serialization::Model::NewObject)
end
end
context "when deserializing a char array" do
it "deserializes an Stream" do
expect(stream.decode(char_array_stream_io)).to be_a(Rex::Java::Serialization::Model::Stream)
end
it "deserializes the char array correctly" do
stream.decode(char_array_stream_io)
expect(stream.contents[0]).to be_an(Rex::Java::Serialization::Model::NewArray)
end
end
context "when deserializing a complex stream with references" do
it "deserializes an Stream" do
expect(stream.decode(complex_stream_io)).to be_a(Rex::Java::Serialization::Model::Stream)
end
it "deserializes all the contents in the Stream" do
stream.decode(complex_stream_io)
expect(stream.contents.length).to eq(4)
end
it "deserializes object contents" do
stream.decode(complex_stream_io)
expect(stream.contents[3]).to be_a(Rex::Java::Serialization::Model::NewObject)
end
end
end
describe "#to_s" do
it "prints a simple Object stream" do
stream.decode(easy_object_stream_io)
expect(stream.to_s).to eq(easy_object_stream_to_s)
end
it "prints a char array stream" do
stream.decode(char_array_stream_io)
expect(stream.to_s).to eq(char_array_stream_to_s)
end
it "prints a complex stream with references" do
stream.decode(complex_stream_io)
expect(stream.to_s).to eq(complex_stream_to_s)
end
end
describe "#encode" do
context "when serializing a simple Object stream" do
it "serializes the Stream" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'Easy')
new_class_desc.serial_version = 0x741de1bcbb2fcbaa
new_class_desc.flags = 2
field = Rex::Java::Serialization::Model::Field.new
field.type = 'int'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'SSN')
new_class_desc.fields << field
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_object = Rex::Java::Serialization::Model::NewObject.new
new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
new_object.class_desc.description = new_class_desc
new_object.class_data = [['int', 0x41424344]]
stream.contents << new_object
expect(stream.encode.unpack("C*")).to eq(easy_object_stream.unpack("C*"))
end
end
context "when serializing a char array" do
it "serializes the Stream" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[C')
new_class_desc.serial_version = 0xb02666b0e25d84ac
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array = Rex::Java::Serialization::Model::NewArray.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'char'
new_array.values = [97, 98]
stream.contents << new_array
expect(stream.encode.unpack("C*")).to eq(char_array_stream.unpack("C*"))
end
end
context "when reserializing a complex stream" do
it "reserializes the original stream" do
stream.decode(complex_stream_io)
expect(stream.encode.unpack("C*")).to eq(complex_stream.unpack("C*"))
end
end
end
end

View File

@ -0,0 +1,107 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::Utf do
subject(:utf) do
described_class.new
end
let(:sample_utf) { "\x00\x10java.lang.Number" }
let(:sample_utf_io) { StringIO.new(sample_utf) }
let(:empty_utf) { "\x00\x00" }
let(:empty_utf_io) { StringIO.new(empty_utf) }
let(:incomplete_utf) { "\x00\x10java.lang.Numb" }
let(:incomplete_utf_io) { StringIO.new(incomplete_utf) }
let(:empty_io) { StringIO.new('') }
describe ".new" do
it "Rex::Java::Serialization::Model::Utf" do
expect(utf).to be_a(Rex::Java::Serialization::Model::Utf)
end
it "initializes length to 0" do
expect(utf.length).to eq(0)
end
it "initializes contents with empty string" do
expect(utf.contents).to be_empty
end
end
describe "#encode" do
context "when empty utf" do
it { expect(utf.encode).to eq(empty_utf) }
end
context "when filled utf" do
it do
utf.length = 16
utf.contents = 'java.lang.Number'
expect(utf.encode).to eq(sample_utf)
end
end
end
describe "#decode" do
context "when stream contains empty string" do
it "raises RuntimeError" do
expect { utf.decode(empty_io) }.to raise_error(::RuntimeError)
end
end
context "when stream contains empty utf" do
it "returns a Rex::Java::Serialization::Model::Utf" do
expect(utf.decode(empty_utf_io)).to be_a(Rex::Java::Serialization::Model::Utf)
end
it "sets length to 0" do
utf.decode(empty_utf_io)
expect(utf.length).to eq(0)
end
it "sets contents to empty string" do
utf.decode(empty_utf_io)
expect(utf.contents).to be_empty
end
end
context "when stream contains incomplete utf" do
it "raises RuntimeError" do
expect { utf.decode(incomplete_utf_io) }.to raise_error(::RuntimeError)
end
end
context "when stream contains correct utf" do
it "returns a Rex::Java::Serialization::Model::Utf" do
expect(utf.decode(sample_utf_io)).to be_a(Rex::Java::Serialization::Model::Utf)
end
it "sets length to 0" do
utf.decode(sample_utf_io)
expect(utf.length).to eq(16)
end
it "sets contents to sample string" do
utf.decode(sample_utf_io)
expect(utf.contents).to eq('java.lang.Number')
end
end
end
describe "#to_s" do
it "prints an stream containing a sample utf" do
utf.decode(sample_utf_io)
expect(utf.to_s).to eq('java.lang.Number')
end
it "prints an stream containing an empty utf" do
utf.decode(empty_utf_io)
expect(utf.to_s).to eq('')
end
end
end

View File

@ -0,0 +1,74 @@
require 'rex/java'
require 'stringio'
load Metasploit::Framework.root.join('tools/java_deserializer.rb').to_path
describe JavaDeserializer do
before(:all) do
@out = $stdout
@err = $stderr
$stdout = StringIO.new
$stderr = StringIO.new
end
after(:all) do
$stdout = @out
$stderr = @err
end
subject(:deserializer) do
described_class.new
end
let(:valid_stream) do
"\xac\xed\x00\x05\x75\x72\x00\x02" +
"\x5b\x43\xb0\x26\x66\xb0\xe2\x5d" +
"\x84\xac\x02\x00\x00\x78\x70\x00" +
"\x00\x00\x02\x00\x61\x00\x62"
end
describe ".new" do
it "returns a JavaDeserializer instance" do
expect(deserializer).to be_a(JavaDeserializer)
end
it "initializes file to nil" do
expect(deserializer.file).to be_nil
end
end
describe "#run" do
context "when file is nil" do
it "returns nil" do
expect(deserializer.run).to be_nil
end
end
context "when file contains a valid stream" do
it "prints the stream contents" do
expect(File).to receive(:new) do
contents = valid_stream
StringIO.new(contents)
end
deserializer.file = 'sample'
deserializer.run
expect($stdout.string).to include('[7e0001] NewArray { char, ["97", "98"] }')
end
end
context "when file contains an invalid stream" do
it "prints the error while deserializing" do
expect(File).to receive(:new) do
contents = 'invalid_stream'
StringIO.new(contents)
end
deserializer.file = 'sample'
deserializer.run
expect($stdout.string).to include('[-] Failed to unserialize Stream')
end
end
end
end

83
tools/java_deserializer.rb Executable file
View File

@ -0,0 +1,83 @@
#!/usr/bin/env ruby
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
msf_base = __FILE__
while File.symlink?(msf_base)
msf_base = File.expand_path(File.readlink(msf_base), File.dirname(msf_base))
end
$:.unshift(File.expand_path(File.join(File.dirname(msf_base), '..', 'lib')))
require 'rex/java/serialization'
require 'pp'
# This class allows to deserialize Java Streams from
# files
class JavaDeserializer
# @!attribute file
# @return [String] the file's path to deserialize
attr_accessor :file
# @param file [String] the file's path to deserialize
def initialize(file = nil)
self.file = file
end
# Deserializes a Java stream from a file and prints the result.
#
# @return [Rex::Java::Serialization::Model::Stream] if succeeds
# @return [nil] if error
def run
if file.nil?
print_error("file path with serialized java stream required")
return
end
print_status("Deserializing...")
print_line
begin
f = File.new(file, 'rb')
stream = Rex::Java::Serialization::Model::Stream.decode(f)
f.close
rescue ::Exception => e
print_exception(e)
return
end
puts stream
end
private
# @param [String] string to print as status
def print_status(msg='')
$stdout.puts "[*] #{msg}"
end
# @param [String] string to print as error
def print_error(msg='')
$stdout.puts "[-] #{msg}"
end
# @param [Exception] exception to print
def print_exception(e)
print_error(e.message)
e.backtrace.each do |line|
$stdout.puts("\t#{line}")
end
end
def print_line
$stdout.puts("\n")
end
end
if __FILE__ == $PROGRAM_NAME
deserializer = JavaDeserializer.new(ARGV[0])
deserializer.run
end