rex-java 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +3 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +52 -0
- data/Gemfile +4 -0
- data/README.md +36 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rex/java.rb +10 -0
- data/lib/rex/java/serialization.rb +57 -0
- data/lib/rex/java/serialization/builder.rb +94 -0
- data/lib/rex/java/serialization/decode_error.rb +11 -0
- data/lib/rex/java/serialization/encode_error.rb +11 -0
- data/lib/rex/java/serialization/model.rb +33 -0
- data/lib/rex/java/serialization/model/annotation.rb +69 -0
- data/lib/rex/java/serialization/model/block_data.rb +70 -0
- data/lib/rex/java/serialization/model/block_data_long.rb +72 -0
- data/lib/rex/java/serialization/model/class_desc.rb +64 -0
- data/lib/rex/java/serialization/model/contents.rb +163 -0
- data/lib/rex/java/serialization/model/element.rb +44 -0
- data/lib/rex/java/serialization/model/end_block_data.rb +12 -0
- data/lib/rex/java/serialization/model/field.rb +173 -0
- data/lib/rex/java/serialization/model/long_utf.rb +48 -0
- data/lib/rex/java/serialization/model/new_array.rb +229 -0
- data/lib/rex/java/serialization/model/new_class.rb +57 -0
- data/lib/rex/java/serialization/model/new_class_desc.rb +154 -0
- data/lib/rex/java/serialization/model/new_enum.rb +79 -0
- data/lib/rex/java/serialization/model/new_object.rb +235 -0
- data/lib/rex/java/serialization/model/null_reference.rb +12 -0
- data/lib/rex/java/serialization/model/proxy_class_desc.rb +109 -0
- data/lib/rex/java/serialization/model/reference.rb +61 -0
- data/lib/rex/java/serialization/model/reset.rb +12 -0
- data/lib/rex/java/serialization/model/stream.rb +123 -0
- data/lib/rex/java/serialization/model/utf.rb +69 -0
- data/lib/rex/java/version.rb +5 -0
- data/rex-java.gemspec +24 -0
- metadata +199 -0
- metadata.gz.sig +1 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
module Rex
|
4
|
+
module Java
|
5
|
+
module Serialization
|
6
|
+
module Model
|
7
|
+
# This class provides a NewEnum (Java Enum) representation
|
8
|
+
class NewEnum < Element
|
9
|
+
|
10
|
+
include Rex::Java::Serialization::Model::Contents
|
11
|
+
|
12
|
+
# @!attribute enum_description
|
13
|
+
# @return [Rex::Java::Serialization::Model::ClassDescription] The description of the enum
|
14
|
+
attr_accessor :enum_description
|
15
|
+
# @!attribute constant_name
|
16
|
+
# @return [Rex::Java::Serialization::Model::Utf] The constant value in the Java Enum
|
17
|
+
attr_accessor :constant_name
|
18
|
+
|
19
|
+
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
|
20
|
+
def initialize(stream = nil)
|
21
|
+
super(stream)
|
22
|
+
self.enum_description = nil
|
23
|
+
self.constant_name = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Deserializes a Rex::Java::Serialization::Model::NewEnum
|
27
|
+
#
|
28
|
+
# @param io [IO] the io to read from
|
29
|
+
# @return [self] if deserialization succeeds
|
30
|
+
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
|
31
|
+
def decode(io)
|
32
|
+
self.enum_description = ClassDesc.decode(io, stream)
|
33
|
+
stream.add_reference(self) unless stream.nil?
|
34
|
+
self.constant_name = decode_constant_name(io)
|
35
|
+
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
# Serializes the Rex::Java::Serialization::Model::NewEnum
|
40
|
+
#
|
41
|
+
# @return [String] if serialization succeeds
|
42
|
+
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
|
43
|
+
def encode
|
44
|
+
unless enum_description.kind_of?(ClassDesc) &&
|
45
|
+
constant_name.kind_of?(Utf)
|
46
|
+
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize EnumDescription'
|
47
|
+
end
|
48
|
+
|
49
|
+
encoded = ''
|
50
|
+
encoded << enum_description.encode
|
51
|
+
encoded << encode_content(constant_name)
|
52
|
+
encoded
|
53
|
+
end
|
54
|
+
|
55
|
+
# Creates a print-friendly string representation
|
56
|
+
#
|
57
|
+
# @return [String]
|
58
|
+
def to_s
|
59
|
+
constant_name.to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Deserializes the NewEnum constant name
|
65
|
+
#
|
66
|
+
# @param io [IO] the io to read from
|
67
|
+
# @return [Rex::Java::Serialization::Model::Utf] if deserialization succeeds
|
68
|
+
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succed
|
69
|
+
def decode_constant_name(io)
|
70
|
+
content = decode_content(io, stream)
|
71
|
+
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize NewEnum' unless content.kind_of?(Rex::Java::Serialization::Model::Utf)
|
72
|
+
|
73
|
+
content
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
module Rex
|
4
|
+
module Java
|
5
|
+
module Serialization
|
6
|
+
module Model
|
7
|
+
# This class provides a NewObject (Java Object) representation
|
8
|
+
class NewObject < Element
|
9
|
+
|
10
|
+
include Rex::Java::Serialization::Model::Contents
|
11
|
+
|
12
|
+
# @!attribute class_desc
|
13
|
+
# @return [Rex::Java::Serialization::Model::ClassDesc] The description of the object
|
14
|
+
attr_accessor :class_desc
|
15
|
+
# @!attribute class_data
|
16
|
+
# @return [Array] The data of the object
|
17
|
+
attr_accessor :class_data
|
18
|
+
|
19
|
+
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
|
20
|
+
def initialize(stream = nil)
|
21
|
+
super(stream)
|
22
|
+
self.class_desc = nil
|
23
|
+
self.class_data = []
|
24
|
+
end
|
25
|
+
|
26
|
+
# Deserializes a Rex::Java::Serialization::Model::NewObject
|
27
|
+
#
|
28
|
+
# @param io [IO] the io to read from
|
29
|
+
# @return [self] if deserialization succeeds
|
30
|
+
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
|
31
|
+
def decode(io)
|
32
|
+
self.class_desc = ClassDesc.decode(io, stream)
|
33
|
+
stream.add_reference(self) unless stream.nil?
|
34
|
+
|
35
|
+
case class_desc.description
|
36
|
+
when NewClassDesc
|
37
|
+
self.class_data = decode_class_data(io, class_desc.description)
|
38
|
+
when Reference
|
39
|
+
ref = class_desc.description.handle - BASE_WIRE_HANDLE
|
40
|
+
self.class_data = decode_class_data(io, stream.references[ref])
|
41
|
+
end
|
42
|
+
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Serializes the Rex::Java::Serialization::Model::NewObject
|
47
|
+
#
|
48
|
+
# @return [String] if serialization succeeds
|
49
|
+
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
|
50
|
+
def encode
|
51
|
+
unless class_desc.kind_of?(ClassDesc)
|
52
|
+
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize NewObject'
|
53
|
+
end
|
54
|
+
|
55
|
+
encoded = ''
|
56
|
+
encoded << class_desc.encode
|
57
|
+
|
58
|
+
class_data.each do |value|
|
59
|
+
if value.kind_of?(Array)
|
60
|
+
encoded << encode_value(value)
|
61
|
+
else
|
62
|
+
encoded << encode_content(value)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
encoded
|
67
|
+
end
|
68
|
+
|
69
|
+
# Creates a print-friendly string representation
|
70
|
+
#
|
71
|
+
# @return [String]
|
72
|
+
def to_s
|
73
|
+
str = ''
|
74
|
+
|
75
|
+
case class_desc.description
|
76
|
+
when NewClassDesc
|
77
|
+
str << class_desc.description.class_name.to_s
|
78
|
+
when ProxyClassDesc
|
79
|
+
str << class_desc.description.interfaces.collect { |iface| iface.contents }.join(',')
|
80
|
+
when Reference
|
81
|
+
str << (class_desc.description.handle - BASE_WIRE_HANDLE).to_s(16)
|
82
|
+
end
|
83
|
+
|
84
|
+
str << ' => { '
|
85
|
+
data_str = class_data.collect { |data| data.to_s }
|
86
|
+
str << data_str.join(', ')
|
87
|
+
str << ' }'
|
88
|
+
|
89
|
+
str
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
# Deserializes the class_data for a class_desc and its super classes
|
95
|
+
#
|
96
|
+
# @param io [IO] the io to read from
|
97
|
+
# @param my_class_desc [Rex::Java::Serialization::Model::NewClassDesc] the class description whose data is being extracted
|
98
|
+
# @return [Array] class_data values if deserialization succeeds
|
99
|
+
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
|
100
|
+
def decode_class_data(io, my_class_desc)
|
101
|
+
values = []
|
102
|
+
|
103
|
+
unless my_class_desc.super_class.description.class == NullReference
|
104
|
+
if my_class_desc.super_class.description.class == Reference
|
105
|
+
ref = my_class_desc.super_class.description.handle - BASE_WIRE_HANDLE
|
106
|
+
values += decode_class_data(io, stream.references[ref])
|
107
|
+
else
|
108
|
+
values += decode_class_data(io, my_class_desc.super_class.description)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
values += decode_class_fields(io, my_class_desc)
|
113
|
+
|
114
|
+
values
|
115
|
+
end
|
116
|
+
|
117
|
+
# Deserializes the fields data for a class_desc
|
118
|
+
#
|
119
|
+
# @param io [IO] the io to read from
|
120
|
+
# @param my_class_desc [Rex::Java::Serialization::Model::NewClassDesc] the class description whose data is being extracted
|
121
|
+
# @return [Array] class_data values if deserialization succeeds
|
122
|
+
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
|
123
|
+
def decode_class_fields(io, my_class_desc)
|
124
|
+
values = []
|
125
|
+
|
126
|
+
my_class_desc.fields.each do |field|
|
127
|
+
if field.is_primitive?
|
128
|
+
values << decode_value(io, field.type)
|
129
|
+
else
|
130
|
+
content = decode_content(io, stream)
|
131
|
+
values << content
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
values
|
136
|
+
end
|
137
|
+
|
138
|
+
# Deserializes a class_data value
|
139
|
+
#
|
140
|
+
# @param io [IO] the io to read from
|
141
|
+
# @param type [String] the type of the value to deserialize
|
142
|
+
# @return [Array(String, <Fixnum, Float>)] type and value if deserialization succeeds
|
143
|
+
# @raise [Rex::Java::Serialization::DecodeError] if deserialization fails
|
144
|
+
def decode_value(io, type)
|
145
|
+
value = []
|
146
|
+
|
147
|
+
case type
|
148
|
+
when 'byte'
|
149
|
+
value_raw = io.read(1)
|
150
|
+
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value_raw.nil?
|
151
|
+
value.push('byte', value_raw.unpack('c')[0])
|
152
|
+
when 'char'
|
153
|
+
value_raw = io.read(2)
|
154
|
+
unless value_raw && value_raw.length == 2
|
155
|
+
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
|
156
|
+
end
|
157
|
+
value.push('char', value_raw.unpack('s>')[0])
|
158
|
+
when 'double'
|
159
|
+
value_raw = io.read(8)
|
160
|
+
unless value_raw && value_raw.length == 8
|
161
|
+
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
|
162
|
+
end
|
163
|
+
value.push('double', value = value_raw.unpack('G')[0])
|
164
|
+
when 'float'
|
165
|
+
value_raw = io.read(4)
|
166
|
+
unless value_raw && value_raw.length == 4
|
167
|
+
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
|
168
|
+
end
|
169
|
+
value.push('float', value_raw.unpack('g')[0])
|
170
|
+
when 'int'
|
171
|
+
value_raw = io.read(4)
|
172
|
+
unless value_raw && value_raw.length == 4
|
173
|
+
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
|
174
|
+
end
|
175
|
+
value.push('int', value_raw.unpack('l>')[0])
|
176
|
+
when 'long'
|
177
|
+
value_raw = io.read(8)
|
178
|
+
unless value_raw && value_raw.length == 8
|
179
|
+
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
|
180
|
+
end
|
181
|
+
value.push('long', value_raw.unpack('q>')[0])
|
182
|
+
when 'short'
|
183
|
+
value_raw = io.read(2)
|
184
|
+
unless value_raw && value_raw.length == 2
|
185
|
+
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
|
186
|
+
end
|
187
|
+
value.push('short', value_raw.unpack('s>')[0])
|
188
|
+
when 'boolean'
|
189
|
+
value_raw = io.read(1)
|
190
|
+
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value_raw.nil?
|
191
|
+
value.push('boolean', value_raw.unpack('c')[0])
|
192
|
+
else
|
193
|
+
raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray type'
|
194
|
+
end
|
195
|
+
|
196
|
+
value
|
197
|
+
end
|
198
|
+
|
199
|
+
# Serializes an class_data value
|
200
|
+
#
|
201
|
+
# @param value [Array] the type and value to serialize
|
202
|
+
# @return [String] the serialized value
|
203
|
+
# @raise [Rex::Java::Serialization::EncodeError] if serialization fails
|
204
|
+
def encode_value(value)
|
205
|
+
res = ''
|
206
|
+
|
207
|
+
case value[0]
|
208
|
+
when 'byte'
|
209
|
+
res = [value[1]].pack('c')
|
210
|
+
when 'char'
|
211
|
+
res = [value[1]].pack('s>')
|
212
|
+
when 'double'
|
213
|
+
res = [value[1]].pack('G')
|
214
|
+
when 'float'
|
215
|
+
res = [value[1]].pack('g')
|
216
|
+
when 'int'
|
217
|
+
res = [value[1]].pack('l>')
|
218
|
+
when 'long'
|
219
|
+
res = [value[1]].pack('q>')
|
220
|
+
when 'short'
|
221
|
+
res = [value[1]].pack('s>')
|
222
|
+
when 'boolean'
|
223
|
+
res = [value[1]].pack('c')
|
224
|
+
else
|
225
|
+
raise Rex::Java::Serialization::EncodeError, 'Unsupported NewArray type'
|
226
|
+
end
|
227
|
+
|
228
|
+
res
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
module Rex
|
4
|
+
module Java
|
5
|
+
module Serialization
|
6
|
+
module Model
|
7
|
+
# This class provides a ProxyClassDesc representation
|
8
|
+
class ProxyClassDesc < Element
|
9
|
+
|
10
|
+
include Rex::Java::Serialization
|
11
|
+
|
12
|
+
# @!attribute interfaces
|
13
|
+
# @return [Array] An array of interface names
|
14
|
+
attr_accessor :interfaces
|
15
|
+
# @!attribute class_annotation
|
16
|
+
# @return [Rex::Java::Serialization::Model::Annotation] The java class annotations
|
17
|
+
attr_accessor :class_annotation
|
18
|
+
# @!attribute super_class
|
19
|
+
# @return [Rex::Java::Serialization::Model::ClassDesc] The java class superclass description
|
20
|
+
attr_accessor :super_class
|
21
|
+
|
22
|
+
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
|
23
|
+
def initialize(stream = nil)
|
24
|
+
super(stream)
|
25
|
+
self.interfaces = []
|
26
|
+
self.class_annotation = nil
|
27
|
+
self.super_class = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# Deserializes a Rex::Java::Serialization::Model::ProxyClassDesc
|
31
|
+
#
|
32
|
+
# @param io [IO] the io to read from
|
33
|
+
# @return [self] if deserialization succeeds
|
34
|
+
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
|
35
|
+
def decode(io)
|
36
|
+
stream.add_reference(self) unless stream.nil?
|
37
|
+
|
38
|
+
interfaces_length = decode_interfaces_length(io)
|
39
|
+
interfaces_length.times do
|
40
|
+
interface = Utf.decode(io, stream)
|
41
|
+
self.interfaces << interface
|
42
|
+
end
|
43
|
+
self.class_annotation = Annotation.decode(io, stream)
|
44
|
+
self.super_class = ClassDesc.decode(io, stream)
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
# Serializes the Rex::Java::Serialization::Model::ProxyClassDesc
|
50
|
+
#
|
51
|
+
# @return [String] if serialization succeeds
|
52
|
+
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
|
53
|
+
def encode
|
54
|
+
unless class_annotation.class == Rex::Java::Serialization::Model::Annotation ||
|
55
|
+
super_class.class == Rex::Java::Serialization::Model::ClassDesc
|
56
|
+
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize ProxyClassDesc'
|
57
|
+
end
|
58
|
+
encoded = ''
|
59
|
+
encoded << [interfaces.length].pack('N')
|
60
|
+
interfaces.each do |interface|
|
61
|
+
encoded << interface.encode
|
62
|
+
end
|
63
|
+
encoded << class_annotation.encode
|
64
|
+
encoded << super_class.encode
|
65
|
+
|
66
|
+
encoded
|
67
|
+
end
|
68
|
+
|
69
|
+
# Creates a print-friendly string representation
|
70
|
+
#
|
71
|
+
# @return [String]
|
72
|
+
def to_s
|
73
|
+
str = '[ '
|
74
|
+
interfaces_str = []
|
75
|
+
interfaces.each do |interface|
|
76
|
+
interfaces_str << interface.to_s
|
77
|
+
end
|
78
|
+
str << "#{interfaces_str.join(', ')} ]"
|
79
|
+
|
80
|
+
case super_class.description
|
81
|
+
when NewClassDesc
|
82
|
+
str << ", @super_class: #{super_class.description.class_name.to_s}"
|
83
|
+
when Reference
|
84
|
+
str << ", @super_class: #{super_class.description.to_s}"
|
85
|
+
end
|
86
|
+
|
87
|
+
str
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Deserializes the number of interface names
|
93
|
+
#
|
94
|
+
# @param io [IO] the io to read from
|
95
|
+
# @return [Fixnum] if deserialization is possible
|
96
|
+
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
|
97
|
+
def decode_interfaces_length(io)
|
98
|
+
fields_length = io.read(4)
|
99
|
+
if fields_length.nil? || fields_length.length != 4
|
100
|
+
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ProxyClassDesc'
|
101
|
+
end
|
102
|
+
|
103
|
+
fields_length.unpack('N')[0]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
module Rex
|
4
|
+
module Java
|
5
|
+
module Serialization
|
6
|
+
module Model
|
7
|
+
# This class provides a Java Reference representation.
|
8
|
+
class Reference < Element
|
9
|
+
|
10
|
+
# @!attribute contents
|
11
|
+
# @return [Fixnum] The stream handle being referenced
|
12
|
+
attr_accessor :handle
|
13
|
+
|
14
|
+
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
|
15
|
+
def initialize(stream = nil)
|
16
|
+
super(stream)
|
17
|
+
self.handle = 0
|
18
|
+
end
|
19
|
+
|
20
|
+
# Deserializes a Rex::Java::Serialization::Model::Reference
|
21
|
+
#
|
22
|
+
# @param io [IO] the io to read from
|
23
|
+
# @return [self] if deserialization succeeds
|
24
|
+
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
|
25
|
+
def decode(io)
|
26
|
+
handle_raw = io.read(4)
|
27
|
+
unless handle_raw && handle_raw.length == 4
|
28
|
+
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Reference'
|
29
|
+
end
|
30
|
+
|
31
|
+
self.handle = handle_raw.unpack('N')[0]
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
# Serializes the Rex::Java::Serialization::Model::Reference
|
37
|
+
#
|
38
|
+
# @return [String] if serialization succeeds
|
39
|
+
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
|
40
|
+
def encode
|
41
|
+
if handle < BASE_WIRE_HANDLE
|
42
|
+
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Reference'
|
43
|
+
end
|
44
|
+
|
45
|
+
encoded = ''
|
46
|
+
encoded << [handle].pack('N')
|
47
|
+
|
48
|
+
encoded
|
49
|
+
end
|
50
|
+
|
51
|
+
# Creates a print-friendly string representation
|
52
|
+
#
|
53
|
+
# @return [String]
|
54
|
+
def to_s
|
55
|
+
"0x#{handle.to_s(16)}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|