rex 2.0.4 → 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rex/arch/x86.rb +16 -0
  3. data/lib/rex/constants.rb +1 -0
  4. data/lib/rex/constants/windows.rb +147 -0
  5. data/lib/rex/encoder/xdr.rb +3 -2
  6. data/lib/rex/exceptions.rb +37 -5
  7. data/lib/rex/exploitation/cmdstager/bourne.rb +9 -1
  8. data/lib/rex/exploitation/cmdstager/tftp.rb +5 -5
  9. data/lib/rex/java.rb +3 -0
  10. data/lib/rex/java/serialization.rb +54 -0
  11. data/lib/rex/java/serialization/model.rb +20 -0
  12. data/lib/rex/java/serialization/model/annotation.rb +69 -0
  13. data/lib/rex/java/serialization/model/block_data.rb +70 -0
  14. data/lib/rex/java/serialization/model/block_data_long.rb +72 -0
  15. data/lib/rex/java/serialization/model/class_desc.rb +64 -0
  16. data/lib/rex/java/serialization/model/contents.rb +156 -0
  17. data/lib/rex/java/serialization/model/element.rb +44 -0
  18. data/lib/rex/java/serialization/model/end_block_data.rb +12 -0
  19. data/lib/rex/java/serialization/model/field.rb +172 -0
  20. data/lib/rex/java/serialization/model/long_utf.rb +48 -0
  21. data/lib/rex/java/serialization/model/new_array.rb +225 -0
  22. data/lib/rex/java/serialization/model/new_class_desc.rb +155 -0
  23. data/lib/rex/java/serialization/model/new_enum.rb +79 -0
  24. data/lib/rex/java/serialization/model/new_object.rb +223 -0
  25. data/lib/rex/java/serialization/model/null_reference.rb +12 -0
  26. data/lib/rex/java/serialization/model/reference.rb +61 -0
  27. data/lib/rex/java/serialization/model/reset.rb +12 -0
  28. data/lib/rex/java/serialization/model/stream.rb +123 -0
  29. data/lib/rex/java/serialization/model/utf.rb +69 -0
  30. data/lib/rex/mime/message.rb +9 -14
  31. data/lib/rex/payloads.rb +1 -0
  32. data/lib/rex/payloads/meterpreter.rb +2 -0
  33. data/lib/rex/payloads/meterpreter/patch.rb +136 -0
  34. data/lib/rex/payloads/win32/kernel/stager.rb +26 -25
  35. data/lib/rex/post/meterpreter/client.rb +50 -60
  36. data/lib/rex/post/meterpreter/client_core.rb +18 -25
  37. data/lib/rex/post/meterpreter/extensions/extapi/adsi/adsi.rb +102 -8
  38. data/lib/rex/post/meterpreter/extensions/extapi/tlv.rb +24 -14
  39. data/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb +18 -0
  40. data/lib/rex/post/meterpreter/extensions/stdapi/tlv.rb +1 -0
  41. data/lib/rex/post/meterpreter/packet_dispatcher.rb +1 -1
  42. data/lib/rex/post/meterpreter/ui/console.rb +1 -1
  43. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb +43 -1
  44. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/incognito.rb +1 -1
  45. data/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb +9 -0
  46. data/lib/rex/proto/dcerpc/svcctl.rb +2 -0
  47. data/lib/rex/proto/dcerpc/svcctl/packet.rb +304 -0
  48. data/lib/rex/proto/kademlia.rb +8 -0
  49. data/lib/rex/proto/kademlia/bootstrap_request.rb +19 -0
  50. data/lib/rex/proto/kademlia/bootstrap_response.rb +79 -0
  51. data/lib/rex/proto/kademlia/message.rb +72 -0
  52. data/lib/rex/proto/kademlia/ping.rb +19 -0
  53. data/lib/rex/proto/kademlia/pong.rb +41 -0
  54. data/lib/rex/proto/kademlia/util.rb +22 -0
  55. data/lib/rex/proto/natpmp/packet.rb +30 -2
  56. data/lib/rex/proto/quake.rb +3 -0
  57. data/lib/rex/proto/quake/message.rb +73 -0
  58. data/lib/rex/proto/smb/client.rb +1 -0
  59. data/lib/rex/proto/smb/simpleclient.rb +4 -0
  60. data/lib/rex/proto/sunrpc/client.rb +14 -3
  61. data/lib/rex/socket/comm/local.rb +10 -7
  62. data/lib/rex/socket/ssl_tcp_server.rb +79 -40
  63. data/lib/rex/ui/text/input/readline.rb +33 -6
  64. data/lib/rex/ui/text/output/file.rb +2 -2
  65. data/lib/rex/ui/text/output/stdio.rb +70 -14
  66. data/rex.gemspec +1 -1
  67. metadata +38 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 79a1308c4fbc5e37cc531c402b45228f0480314a
4
- data.tar.gz: 55b5f247534fdac3322b929d4c46e4846c9ec9bb
3
+ metadata.gz: 31837db98ac01dead4073a5d9785ee8c3885772a
4
+ data.tar.gz: 0976f856604fdeee60805d11fe36faeee0e4656b
5
5
  SHA512:
6
- metadata.gz: 49e7741c0bc14e27943de1291b38e69300173bdffd3a1598372d5db5d1338040201ef011e67680bf42ba611948dd18cc5ad0627388a80d1079ef58c80088bd3e
7
- data.tar.gz: 10161b1c288d4c7aab84399ad7f66048ef276d7225b4a701b8ac8eac784ada87786499137dad1fed0f652c4dea529b65c7678f7e2b0b958be5e29dc232da480b
6
+ metadata.gz: f35c3bb765efa30161a507f41e8e1016296bacb8bb8b80eb0220c8cfc18092957f479f3ea17b1994fc4e38e58e60eed8bcb3e545f6a3f62cebe54a44afca9099
7
+ data.tar.gz: 89e3d3600fd209b95a8bf231df5d4ab8d839ca6b1b8e1178e23b676cddf1d1afd7461985c2689b54d71d437fb3839e12497c9f7bde7b5f1f9a59a1655fda82ed
data/lib/rex/arch/x86.rb CHANGED
@@ -520,6 +520,22 @@ module X86
520
520
  return nil
521
521
  end
522
522
 
523
+ #
524
+ # Parse a list of registers as a space or command delimited
525
+ # string and return the internal register IDs as an array
526
+ #
527
+ def self.register_names_to_ids(str)
528
+ register_ids = []
529
+ str.to_s.strip.split(/[,\s]/).
530
+ map {|reg| reg.to_s.strip.upcase }.
531
+ select {|reg| reg.length > 0 }.
532
+ uniq.each do |reg|
533
+ next unless self.const_defined?(reg.intern)
534
+ register_ids << self.const_get(reg.intern)
535
+ end
536
+ register_ids
537
+ end
538
+
523
539
  end
524
540
 
525
541
  end end
data/lib/rex/constants.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # -*- coding: binary -*-
2
+
2
3
  #
3
4
  # Log severities
4
5
  #
@@ -0,0 +1,147 @@
1
+ module Rex::Constants
2
+ module Windows
3
+
4
+ ##
5
+ #
6
+ # Access Types
7
+ # winnt.h
8
+ #
9
+ ##
10
+
11
+ STANDARD_RIGHTS_REQUIRED = 0x000F0000
12
+
13
+ ##
14
+ #
15
+ # Errors
16
+ #
17
+ ##
18
+
19
+ ERROR_SUCCESS = 0x0
20
+ ERROR_FILE_NOT_FOUND = 0x2
21
+ ERROR_ACCESS_DENIED = 0x5
22
+ ERROR_SERVICE_REQUEST_TIMEOUT = 0x41D
23
+ ERROR_SERVICE_EXISTS = 0x431
24
+
25
+ ##
26
+ #
27
+ # SVCCTL Protocol Functions
28
+ # http://msdn.microsoft.com/en-us/library/cc245920.aspxa
29
+ #
30
+ ##
31
+
32
+ CLOSE_SERVICE_HANDLE = 0x00
33
+ CONTROL_SERVICE = 0x01
34
+ DELETE_SERVICE = 0x02
35
+ QUERY_SERVICE_STATUS = 0x05
36
+ CHANGE_SERVICE_CONFIG_W = 0x0b
37
+ CREATE_SERVICE_W = 0x0c
38
+ OPEN_SC_MANAGER_W = 0x0f
39
+ OPEN_SERVICE_W = 0x10
40
+ CHANGE_SERVICE_CONFIG2_W = 0x25
41
+
42
+ ##
43
+ #
44
+ # Services
45
+ # winsvc.h
46
+ ##
47
+
48
+ SERVICE_WIN32_OWN_PROCESS = 0x10
49
+ SERVICE_INTERACTIVE_PROCESS = 0x100
50
+
51
+ SERVICE_BOOT_START = 0x00
52
+ SERVICE_SYSTEM_START = 0x01
53
+ SERVICE_AUTO_START = 0x02
54
+ SERVICE_DEMAND_START = 0x03
55
+ SERVICE_DISABLED = 0x04
56
+
57
+ SERVICE_ERROR_IGNORE = 0x0
58
+
59
+ SERVICE_NO_CHANGE = 0xffffffff
60
+ SERVICE_ACTIVE = 0x00000001
61
+ SERVICE_INACTIVE = 0x00000002
62
+ SERVICE_STATE_ALL = (SERVICE_ACTIVE |
63
+ SERVICE_INACTIVE)
64
+ SERVICE_CONTROL_STOP = 0x00000001
65
+ SERVICE_CONTROL_PAUSE = 0x00000002
66
+ SERVICE_CONTROL_CONTINUE = 0x00000003
67
+ SERVICE_CONTROL_INTERROGATE = 0x00000004
68
+ SERVICE_CONTROL_SHUTDOWN = 0x00000005
69
+ SERVICE_CONTROL_PARAMCHANGE = 0x00000006
70
+ SERVICE_CONTROL_NETBINDADD = 0x00000007
71
+ SERVICE_CONTROL_NETBINDREMOVE = 0x00000008
72
+ SERVICE_CONTROL_NETBINDENABLE = 0x00000009
73
+ SERVICE_CONTROL_NETBINDDISABLE = 0x0000000A
74
+ SERVICE_CONTROL_DEVICEEVENT = 0x0000000B
75
+ SERVICE_CONTROL_HARDWAREPROFILECHANGE =0x0000000C
76
+ SERVICE_CONTROL_POWEREVENT = 0x0000000D
77
+ SERVICE_CONTROL_SESSIONCHANGE = 0x0000000E
78
+ SERVICE_CONTROL_PRESHUTDOWN = 0x0000000F
79
+ SERVICE_CONTROL_TIMECHANGE = 0x00000010
80
+ SERVICE_CONTROL_TRIGGEREVENT = 0x00000020
81
+ SERVICE_STOPPED = 0x00000001
82
+ SERVICE_START_PENDING = 0x00000002
83
+ SERVICE_STOP_PENDING = 0x00000003
84
+ SERVICE_RUNNING = 0x00000004
85
+ SERVICE_CONTINUE_PENDING = 0x00000005
86
+ SERVICE_PAUSE_PENDING = 0x00000006
87
+ SERVICE_PAUSED = 0x00000007
88
+ SERVICE_ACCEPT_STOP = 0x00000001
89
+ SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002
90
+ SERVICE_ACCEPT_SHUTDOWN = 0x00000004
91
+ SERVICE_ACCEPT_PARAMCHANGE = 0x00000008
92
+ SERVICE_ACCEPT_NETBINDCHANGE = 0x00000010
93
+ SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x00000020
94
+ SERVICE_ACCEPT_POWEREVENT = 0x00000040
95
+ SERVICE_ACCEPT_SESSIONCHANGE = 0x00000080
96
+ SERVICE_ACCEPT_PRESHUTDOWN = 0x00000100
97
+ SERVICE_ACCEPT_TIMECHANGE = 0x00000200
98
+ SERVICE_ACCEPT_TRIGGEREVENT = 0x00000400
99
+ SC_MANAGER_CONNECT = 0x0001
100
+ SC_MANAGER_CREATE_SERVICE = 0x0002
101
+ SC_MANAGER_ENUMERATE_SERVICE = 0x0004
102
+ SC_MANAGER_LOCK = 0x0008
103
+ SC_MANAGER_QUERY_LOCK_STATUS = 0x0010
104
+ SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020
105
+
106
+ SC_MANAGER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
107
+ SC_MANAGER_CONNECT |
108
+ SC_MANAGER_CREATE_SERVICE |
109
+ SC_MANAGER_ENUMERATE_SERVICE |
110
+ SC_MANAGER_LOCK |
111
+ SC_MANAGER_QUERY_LOCK_STATUS |
112
+ SC_MANAGER_MODIFY_BOOT_CONFIG)
113
+
114
+ SERVICE_QUERY_CONFIG = 0x0001
115
+ SERVICE_CHANGE_CONFIG = 0x0002
116
+ SERVICE_QUERY_STATUS = 0x0004
117
+ SERVICE_ENUMERATE_DEPENDENTS = 0x0008
118
+ SERVICE_START = 0x0010
119
+ SERVICE_STOP = 0x0020
120
+ SERVICE_PAUSE_CONTINUE = 0x0040
121
+ SERVICE_INTERROGATE = 0x0080
122
+ SERVICE_USER_DEFINED_CONTROL = 0x0100
123
+ SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | \
124
+ SERVICE_QUERY_CONFIG | \
125
+ SERVICE_CHANGE_CONFIG | \
126
+ SERVICE_QUERY_STATUS | \
127
+ SERVICE_ENUMERATE_DEPENDENTS | \
128
+ SERVICE_START | \
129
+ SERVICE_STOP | \
130
+ SERVICE_PAUSE_CONTINUE | \
131
+ SERVICE_INTERROGATE | \
132
+ SERVICE_USER_DEFINED_CONTROL)
133
+
134
+ SERVICE_RUNS_IN_SYSTEM_PROCESS = 0x00000001
135
+ SERVICE_CONFIG_DESCRIPTION = 1
136
+ SERVICE_CONFIG_FAILURE_ACTIONS = 2
137
+ SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3
138
+ SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 4
139
+ SERVICE_CONFIG_SERVICE_SID_INFO = 5
140
+ SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 6
141
+ SERVICE_CONFIG_PRESHUTDOWN_INFO = 7
142
+ SERVICE_CONFIG_TRIGGER_INFO = 8
143
+ SERVICE_CONFIG_PREFERRED_NODE = 9
144
+ SERVICE_CONFIG_LAUNCH_PROTECTED = 12
145
+
146
+ end
147
+ end
@@ -16,8 +16,9 @@ module XDR
16
16
  end
17
17
 
18
18
  def XDR.decode_int!(data)
19
- return data.slice!(0..3).unpack('N')[0] if data
20
- data = 0
19
+ raise ArgumentError, 'XDR: No Integer data to decode' unless data
20
+ raise ArgumentError, "XDR: Too little data to decode (#{data.size})" if data.size < 4
21
+ return data.slice!(0..3).unpack('N')[0]
21
22
  end
22
23
 
23
24
  def XDR.encode_lchar(char)
@@ -213,25 +213,57 @@ class ConnectionTimeout < ConnectionError
213
213
  end
214
214
  end
215
215
 
216
+ ###
217
+ #
218
+ # This connection error is raised when an attempt is made to connect
219
+ # to a broadcast or network address.
220
+ #
221
+ ###
222
+ class InvalidDestination < ConnectionError
223
+ include SocketError
224
+ include HostCommunicationError
225
+
226
+ def to_s
227
+ "The destination is invalid: #{addr_to_s}."
228
+ end
229
+ end
216
230
 
217
231
  ###
218
232
  #
219
233
  # This exception is raised when an attempt to use an address or port that is
220
- # already in use occurs, such as binding to a host on a given port that is
221
- # already in use. Note that Windows raises this in some cases when attempting
222
- # to connect to addresses that it can't handle, e.g. "0.0.0.0". Thus, this is
223
- # a ConnectionError.
234
+ # already in use or onot available occurs. such as binding to a host on a
235
+ # given port that is already in use, or when a bind address is specified that
236
+ # is not available to the host.
224
237
  #
225
238
  ###
239
+ class BindFailed < ::ArgumentError
240
+ include SocketError
241
+ include HostCommunicationError
242
+
243
+ def to_s
244
+ "The address is already in use or unavailable: #{addr_to_s}."
245
+ end
246
+ end
247
+
248
+ ##
249
+ #
250
+ # This exception is listed for backwards compatibility. We had been
251
+ # using AddressInUse as the exception for both bind errors and connection
252
+ # errors triggered by connection attempts to broadcast and network addresses.
253
+ # The two classes above have split this into their respective sources, but
254
+ # callers may still expect the old behavior.
255
+ #
256
+ ##
226
257
  class AddressInUse < ConnectionError
227
258
  include SocketError
228
259
  include HostCommunicationError
229
260
 
230
261
  def to_s
231
- "The address is already in use #{addr_to_s}."
262
+ "The address is already in use or unavailable: #{addr_to_s}."
232
263
  end
233
264
  end
234
265
 
266
+
235
267
  ###
236
268
  #
237
269
  # This exception is raised when an unsupported internet protocol is specified.
@@ -85,7 +85,15 @@ class CmdStagerBourne < CmdStagerBase
85
85
  def compress_commands(cmds, opts)
86
86
  # Make it all happen
87
87
  cmds << "chmod +x #{@tempdir}#{@var_decoded}.bin"
88
- cmds << "#{@tempdir}#{@var_decoded}.bin"
88
+ # Background the process, allowing the cleanup code to continue and delete the data
89
+ # while allowing the original shell to continue to function since it isn't waiting
90
+ # on the payload to exit. The 'sleep' is required as '&' is a command terminator
91
+ # and having & and the cmds delimiter ';' next to each other is invalid.
92
+ if opts[:background]
93
+ cmds << "#{@tempdir}#{@var_decoded}.bin & sleep 2"
94
+ else
95
+ cmds << "#{@tempdir}#{@var_decoded}.bin"
96
+ end
89
97
 
90
98
  # Clean up after unless requested not to..
91
99
  if (not opts[:nodelete])
@@ -31,14 +31,14 @@ class CmdStagerTFTP < CmdStagerBase
31
31
  end
32
32
 
33
33
  def setup(mod)
34
- tftp = Rex::Proto::TFTP::Server.new
35
- tftp.register_file(Rex::Text.rand_text_alphanumeric(8), exe)
36
- tftp.start
37
- mod.add_socket(tftp) # Hating myself for doing it... but it's just a first demo
34
+ self.tftp = Rex::Proto::TFTP::Server.new
35
+ self.tftp.register_file(Rex::Text.rand_text_alphanumeric(8), exe)
36
+ self.tftp.start
37
+ mod.add_socket(self.tftp) # Hating myself for doing it... but it's just a first demo
38
38
  end
39
39
 
40
40
  def teardown(mod = nil)
41
- tftp.stop
41
+ self.tftp.stop
42
42
  end
43
43
 
44
44
  #
data/lib/rex/java.rb ADDED
@@ -0,0 +1,3 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/java/serialization'
@@ -0,0 +1,54 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module Java
5
+ # Include constants defining terminal and constant
6
+ # values expected in a stream.
7
+ module Serialization
8
+ STREAM_MAGIC = 0xaced
9
+ STREAM_VERSION = 5
10
+ TC_NULL = 0x70
11
+ TC_REFERENCE = 0x71
12
+ TC_CLASSDESC = 0x72
13
+ TC_OBJECT = 0x73
14
+ TC_STRING = 0x74
15
+ TC_ARRAY = 0x75
16
+ TC_CLASS = 0x76
17
+ TC_BLOCKDATA = 0x77
18
+ TC_ENDBLOCKDATA = 0x78
19
+ TC_RESET = 0x79
20
+ TC_BLOCKDATALONG = 0x7A
21
+ TC_EXCEPTION = 0x7B
22
+ TC_LONGSTRING = 0x7C
23
+ TC_PROXYCLASSDESC = 0x7D
24
+ TC_ENUM = 0x7E
25
+ BASE_WIRE_HANDLE = 0x7E0000
26
+
27
+ SC_WRITE_METHOD = 0x01 # if SC_SERIALIZABLE
28
+ SC_BLOCK_DATA = 0x08 # if SC_EXTERNALIZABLE
29
+ SC_SERIALIZABLE = 0x02
30
+ SC_EXTERNALIZABLE = 0x04
31
+ SC_ENUM = 0x10
32
+
33
+ PRIMITIVE_TYPE_CODES = {
34
+ 'B' => 'byte',
35
+ 'C' => 'char',
36
+ 'D' => 'double',
37
+ 'F' => 'float',
38
+ 'I' => 'int',
39
+ 'J' => 'long',
40
+ 'S' => 'short',
41
+ 'Z' => 'boolean'
42
+ }
43
+
44
+ OBJECT_TYPE_CODES = {
45
+ '[' => 'array',
46
+ 'L' => 'object'
47
+ }
48
+
49
+ TYPE_CODES = PRIMITIVE_TYPE_CODES.merge(OBJECT_TYPE_CODES)
50
+ end
51
+ end
52
+ end
53
+
54
+ require 'rex/java/serialization/model'
@@ -0,0 +1,20 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/java/serialization/model/element'
4
+ require 'rex/java/serialization/model/null_reference'
5
+ require 'rex/java/serialization/model/reference'
6
+ require 'rex/java/serialization/model/reset'
7
+ require 'rex/java/serialization/model/utf'
8
+ require 'rex/java/serialization/model/long_utf'
9
+ require 'rex/java/serialization/model/block_data'
10
+ require 'rex/java/serialization/model/block_data_long'
11
+ require 'rex/java/serialization/model/end_block_data'
12
+ require 'rex/java/serialization/model/contents'
13
+ require 'rex/java/serialization/model/new_enum'
14
+ require 'rex/java/serialization/model/field'
15
+ require 'rex/java/serialization/model/new_array'
16
+ require 'rex/java/serialization/model/annotation'
17
+ require 'rex/java/serialization/model/class_desc'
18
+ require 'rex/java/serialization/model/new_class_desc'
19
+ require 'rex/java/serialization/model/new_object'
20
+ require 'rex/java/serialization/model/stream'
@@ -0,0 +1,69 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module Java
5
+ module Serialization
6
+ module Model
7
+ # This class provides an annotation representation. It's used for both class
8
+ # annotations (classAnnotation) and object annotations (objectAnnotation).
9
+ class Annotation < Element
10
+
11
+ include Rex::Java::Serialization::Model::Contents
12
+
13
+ # @!attribute contents
14
+ # @return [Array] The annotation contents
15
+ attr_accessor :contents
16
+
17
+ # @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
18
+ def initialize(stream = nil)
19
+ super(stream)
20
+ self.contents = []
21
+ end
22
+
23
+ # Deserializes a Rex::Java::Serialization::Model::Annotation
24
+ #
25
+ # @param io [IO] the io to read from
26
+ # @return [self] if deserialization succeeds
27
+ # @raise [RuntimeError] if deserialization doesn't succeed
28
+ def decode(io)
29
+ loop do
30
+ content = decode_content(io, stream)
31
+ self.contents << content
32
+ return self if content.class == EndBlockData
33
+ end
34
+
35
+ self
36
+ end
37
+
38
+ # Serializes the Rex::Java::Serialization::Model::Annotation
39
+ #
40
+ # @return [String] if serialization suceeds
41
+ # @raise [RuntimeError] if serialization doesn't succeed
42
+ def encode
43
+ raise ::RuntimeError, 'Failed to serialize Annotation with empty contents' if contents.empty?
44
+
45
+ encoded = ''
46
+
47
+ contents.each do |content|
48
+ encoded << encode_content(content)
49
+ end
50
+
51
+ encoded
52
+ end
53
+
54
+ # Creates a print-friendly string representation
55
+ #
56
+ # @return [String]
57
+ def to_s
58
+ str = '[ '
59
+ contents_data = contents.collect {|content| "#{print_content(content)}"}
60
+ str << contents_data.join(', ')
61
+ str << ' ]'
62
+ str
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end