ronin-support 0.3.0 → 0.4.0.rc1

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.
Files changed (59) hide show
  1. data/ChangeLog.md +77 -7
  2. data/README.md +19 -3
  3. data/gemspec.yml +2 -2
  4. data/lib/ronin/extensions/regexp.rb +50 -2
  5. data/lib/ronin/extensions/string.rb +1 -0
  6. data/lib/ronin/formatting.rb +1 -0
  7. data/lib/ronin/formatting/extensions.rb +1 -0
  8. data/lib/ronin/formatting/extensions/binary/string.rb +56 -5
  9. data/lib/ronin/formatting/extensions/html/string.rb +6 -7
  10. data/lib/ronin/formatting/extensions/sql/string.rb +34 -0
  11. data/lib/ronin/formatting/extensions/text/string.rb +0 -180
  12. data/lib/ronin/fuzzing.rb +21 -0
  13. data/lib/ronin/fuzzing/extensions.rb +20 -0
  14. data/lib/ronin/fuzzing/extensions/string.rb +380 -0
  15. data/lib/ronin/fuzzing/fuzzing.rb +191 -0
  16. data/lib/ronin/network/esmtp.rb +94 -1
  17. data/lib/ronin/network/extensions/esmtp/net.rb +2 -82
  18. data/lib/ronin/network/extensions/http/net.rb +1 -736
  19. data/lib/ronin/network/extensions/imap/net.rb +1 -103
  20. data/lib/ronin/network/extensions/pop3/net.rb +1 -71
  21. data/lib/ronin/network/extensions/smtp/net.rb +2 -157
  22. data/lib/ronin/network/extensions/ssl/net.rb +1 -132
  23. data/lib/ronin/network/extensions/tcp/net.rb +2 -296
  24. data/lib/ronin/network/extensions/telnet/net.rb +1 -135
  25. data/lib/ronin/network/extensions/udp/net.rb +2 -214
  26. data/lib/ronin/network/http/http.rb +750 -5
  27. data/lib/ronin/network/imap.rb +105 -2
  28. data/lib/ronin/network/mixins.rb +1 -1
  29. data/lib/ronin/network/mixins/esmtp.rb +49 -52
  30. data/lib/ronin/network/mixins/http.rb +49 -53
  31. data/lib/ronin/network/mixins/imap.rb +47 -44
  32. data/lib/ronin/network/mixins/mixin.rb +58 -0
  33. data/lib/ronin/network/mixins/pop3.rb +44 -38
  34. data/lib/ronin/network/mixins/smtp.rb +49 -51
  35. data/lib/ronin/network/mixins/tcp.rb +56 -69
  36. data/lib/ronin/network/mixins/telnet.rb +57 -50
  37. data/lib/ronin/network/mixins/udp.rb +48 -52
  38. data/lib/ronin/network/network.rb +1 -0
  39. data/lib/ronin/network/pop3.rb +72 -2
  40. data/lib/ronin/network/smtp/email.rb +1 -0
  41. data/lib/ronin/network/smtp/smtp.rb +159 -3
  42. data/lib/ronin/network/ssl.rb +131 -2
  43. data/lib/ronin/network/tcp.rb +306 -1
  44. data/lib/ronin/network/telnet.rb +136 -2
  45. data/lib/ronin/network/udp.rb +229 -1
  46. data/lib/ronin/support.rb +2 -3
  47. data/lib/ronin/support/support.rb +38 -0
  48. data/lib/ronin/support/version.rb +1 -1
  49. data/lib/ronin/templates/erb.rb +2 -1
  50. data/lib/ronin/ui/output/helpers.rb +35 -1
  51. data/lib/ronin/ui/shell.rb +12 -2
  52. data/lib/ronin/wordlist.rb +157 -0
  53. data/spec/extensions/regexp_spec.rb +38 -0
  54. data/spec/formatting/html/string_spec.rb +1 -1
  55. data/spec/formatting/sql/string_spec.rb +23 -3
  56. data/spec/formatting/text/string_spec.rb +0 -110
  57. data/spec/fuzzing/string_spec.rb +158 -0
  58. data/spec/wordlist_spec.rb +65 -0
  59. metadata +35 -27
@@ -0,0 +1,21 @@
1
+ #
2
+ # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ #
4
+ # This file is part of Ronin Support.
5
+ #
6
+ # Ronin Support is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Ronin Support is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'ronin/fuzzing/extensions'
21
+ require 'ronin/fuzzing/fuzzing'
@@ -0,0 +1,20 @@
1
+ #
2
+ # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ #
4
+ # This file is part of Ronin Support.
5
+ #
6
+ # Ronin Support is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Ronin Support is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'ronin/fuzzing/extensions/string'
@@ -0,0 +1,380 @@
1
+ #
2
+ # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ #
4
+ # This file is part of Ronin Support.
5
+ #
6
+ # Ronin Support is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Ronin Support is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'ronin/extensions/regexp'
21
+ require 'ronin/fuzzing/fuzzing'
22
+
23
+ require 'combinatorics/generator'
24
+ require 'combinatorics/list_comprehension'
25
+ require 'combinatorics/power_set'
26
+ require 'chars'
27
+
28
+ class String
29
+
30
+ #
31
+ # Generate permutations of Strings from a format template.
32
+ #
33
+ # @param [Array(<String,Symbol,Enumerable>, <Integer,Array,Range>)] template
34
+ # The template which defines the string or character sets which will
35
+ # make up parts of the String.
36
+ #
37
+ # @yield [string]
38
+ # The given block will be passed each unique String.
39
+ #
40
+ # @yieldparam [String] string
41
+ # A newly generated String.
42
+ #
43
+ # @return [Enumerator]
44
+ # If no block is given, an Enumerator will be returned.
45
+ #
46
+ # @raise [ArgumentError]
47
+ # A given character set name was unknown.
48
+ #
49
+ # @raise [TypeError]
50
+ # A given string set was not a String, Symbol or Enumerable.
51
+ # A given string set length was not an Integer or Enumerable.
52
+ #
53
+ # @example Generate Strings with ranges of repeating sub-strings.
54
+ #
55
+ # @example Generate Strings with three alpha chars and one numeric chars.
56
+ # String.generate([:alpha, 3], :numeric) do |password|
57
+ # puts password
58
+ # end
59
+ #
60
+ # @example Generate Strings with two to four alpha chars.
61
+ # String.generate([:alpha, 2..4]) do |password|
62
+ # puts password
63
+ # end
64
+ #
65
+ # @example Generate Strings using alpha and punctuation chars.
66
+ # String.generate([Chars.alpha + Chars.punctuation, 4]) do |password|
67
+ # puts password
68
+ # end
69
+ #
70
+ # @example Generate Strings from a custom char set.
71
+ # String.generate([['a', 'b', 'c'], 3], [['1', '2', '3'], 3]) do |password|
72
+ # puts password
73
+ # end
74
+ #
75
+ # @example Generate Strings containing known Strings.
76
+ # String.generate("rock", [:numeric, 4]) do |password|
77
+ # puts password
78
+ # end
79
+ #
80
+ # @example Generate Strings with ranges of repeating sub-strings.
81
+ # String.generate(['/AA', (1..100).step(5)]) do |path|
82
+ # puts path
83
+ # end
84
+ #
85
+ # @since 0.3.0
86
+ #
87
+ # @api public
88
+ #
89
+ def self.generate(*template)
90
+ return enum_for(:generate,*template) unless block_given?
91
+
92
+ sets = []
93
+
94
+ template.each do |pattern|
95
+ set, length = pattern
96
+ set = case set
97
+ when String
98
+ [set].each
99
+ when Symbol
100
+ name = set.to_s.upcase
101
+
102
+ unless Chars.const_defined?(name)
103
+ raise(ArgumentError,"unknown charset #{set.inspect}")
104
+ end
105
+
106
+ Chars.const_get(name).each_char
107
+ when Enumerable
108
+ set
109
+ else
110
+ raise(TypeError,"set must be a String, Symbol or Enumerable")
111
+ end
112
+
113
+ case length
114
+ when Integer
115
+ length.times { sets << set.dup }
116
+ when Array, Range
117
+ sets << Combinatorics::Generator.new do |g|
118
+ length.each do |sublength|
119
+ superset = Array.new(sublength) { set.dup }
120
+
121
+ superset.comprehension { |strings| g.yield strings.join }
122
+ end
123
+ end
124
+ when nil
125
+ sets << set
126
+ else
127
+ raise(TypeError,"length must be an Integer, Range or Array")
128
+ end
129
+ end
130
+
131
+ sets.comprehension do |strings|
132
+ new_string = ''
133
+
134
+ strings.each do |string|
135
+ new_string << case string
136
+ when Integer
137
+ string.chr
138
+ else
139
+ string.to_s
140
+ end
141
+ end
142
+
143
+ yield new_string
144
+ end
145
+ return nil
146
+ end
147
+
148
+ #
149
+ # Repeats the String.
150
+ #
151
+ # @param [Enumerable, Integer] n
152
+ # The number of times to repeat the String.
153
+ #
154
+ # @yield [repeated]
155
+ # The given block will be passed every repeated String.
156
+ #
157
+ # @yieldparam [String] repeated
158
+ # A repeated version of the String.
159
+ #
160
+ # @return [Enumerator]
161
+ # If no block is given, an Enumerator will be returned.
162
+ #
163
+ # @raise [TypeError]
164
+ # `n` must either be Enumerable or an Integer.
165
+ #
166
+ # @example
167
+ # 'A'.repeating(100)
168
+ # # => "AAAAAAAAAAAAA..."
169
+ #
170
+ # @example Generates 100 upto 700 `A`s, increasing by 100 at a time:
171
+ # 'A'.repeating((100..700).step(100)) do |str|
172
+ # # ...
173
+ # end
174
+ #
175
+ # @example Generates 128, 1024, 65536 `A`s:
176
+ # 'A'.repeating([128, 1024, 65536]) do |str|
177
+ # # ...
178
+ # end
179
+ #
180
+ # @api public
181
+ #
182
+ # @since 0.4.0
183
+ #
184
+ def repeating(n)
185
+ if n.kind_of?(Integer)
186
+ # if n is an Integer, simply multiply the String and return
187
+ repeated = (self * n)
188
+
189
+ yield repeated if block_given?
190
+ return repeated
191
+ end
192
+
193
+ return enum_for(:repeating,n) unless block_given?
194
+
195
+ unless n.kind_of?(Enumerable)
196
+ raise(TypeError,"argument must be Enumerable or an Integer")
197
+ end
198
+
199
+ n.each do |length|
200
+ yield(self * length)
201
+ end
202
+
203
+ return self
204
+ end
205
+
206
+ #
207
+ # Incrementally fuzzes the String.
208
+ #
209
+ # @param [Hash{Regexp,String => #each}] substitutions
210
+ # Patterns and their substitutions.
211
+ #
212
+ # @yield [fuzz]
213
+ # The given block will be passed every fuzzed String.
214
+ #
215
+ # @yieldparam [String] fuzz
216
+ # A fuzzed String.
217
+ #
218
+ # @return [Enumerator]
219
+ # If no block is given, an Enumerator will be returned.
220
+ #
221
+ # @example Replace every `e`, `i`, `o`, `u` with `(`, 100 `A`s and a `\0`:
222
+ # "the quick brown fox".fuzz(/[eiou]/ => ['(', ('A' * 100), "\0"]) do |str|
223
+ # p str
224
+ # end
225
+ #
226
+ # @example {String.generate} with {String#fuzz}:
227
+ # "GET /".fuzz('/' => String.generate(['A', 1..100])) do |str|
228
+ # p str
229
+ # end
230
+ #
231
+ # @since 0.3.0
232
+ #
233
+ # @api public
234
+ #
235
+ def fuzz(substitutions={})
236
+ return enum_for(:fuzz,substitutions) unless block_given?
237
+
238
+ substitutions.each do |pattern,substitution|
239
+ pattern = case pattern
240
+ when Regexp
241
+ pattern
242
+ when String
243
+ Regexp.new(Regexp.escape(pattern))
244
+ when Symbol
245
+ Regexp.const_get(pattern.to_s.upcase)
246
+ else
247
+ raise(TypeError,"cannot convert #{pattern.inspect} to a Regexp")
248
+ end
249
+
250
+ substitution = case substitution
251
+ when Enumerable
252
+ substitution
253
+ when Symbol
254
+ Ronin::Fuzzing[substitution]
255
+ else
256
+ raise(TypeError,"substitutions must be Enumerable or a Symbol")
257
+ end
258
+
259
+ scanner = StringScanner.new(self)
260
+ indices = []
261
+
262
+ while scanner.scan_until(pattern)
263
+ indices << [scanner.pos - scanner.matched_size, scanner.matched_size]
264
+ end
265
+
266
+ indices.each do |index,length|
267
+ substitution.each do |substitute|
268
+ substitute = case substitute
269
+ when Proc
270
+ substitute.call(self[index,length])
271
+ when Integer
272
+ substitute.chr
273
+ else
274
+ substitute.to_s
275
+ end
276
+
277
+ fuzzed = dup
278
+ fuzzed[index,length] = substitute
279
+ yield fuzzed
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ #
286
+ # Permutes over every possible mutation of the String.
287
+ #
288
+ # @param [Hash{Regexp,String,Symbol => Symbol,#each}] mutations
289
+ # The patterns and substitutions to mutate the String with.
290
+ #
291
+ # @yield [mutant]
292
+ # The given block will be yielded every possible mutant String.
293
+ #
294
+ # @yieldparam [String] mutant
295
+ # A mutated String.
296
+ #
297
+ # @return [Enumerator]
298
+ # If no block is given, an Enumerator will be returned.
299
+ #
300
+ # @example
301
+ # "hello old dog".mutate('e' => ['3'], 'l' => ['1'], 'o' => ['0']) do |str|
302
+ # puts str
303
+ # end
304
+ #
305
+ # @since 0.4.0
306
+ #
307
+ # @api public
308
+ #
309
+ def mutate(mutations={})
310
+ return enum_for(:mutate,mutations) unless block_given?
311
+
312
+ matches = Set[]
313
+
314
+ mutations.each do |pattern,mutation|
315
+ pattern = case pattern
316
+ when Regexp
317
+ pattern
318
+ when String
319
+ Regexp.new(Regexp.escape(pattern))
320
+ when Symbol
321
+ Regexp.const_get(pattern.to_s.upcase)
322
+ else
323
+ raise(TypeError,"cannot convert #{pattern.inspect} to a Regexp")
324
+ end
325
+
326
+ scanner = StringScanner.new(self)
327
+
328
+ while scanner.scan_until(pattern)
329
+ length = scanner.matched_size
330
+ index = scanner.pos - length
331
+ original = scanner.matched
332
+
333
+ mutator = Combinatorics::Generator.new do |g|
334
+ mutation.each do |mutate|
335
+ g.yield case mutate
336
+ when Proc
337
+ mutate.call(original)
338
+ when Integer
339
+ mutate.chr
340
+ else
341
+ mutate.to_s
342
+ end
343
+ end
344
+ end
345
+
346
+ matches << [index, length, mutator]
347
+ end
348
+ end
349
+
350
+ matches.powerset do |submatches|
351
+ # ignore the empty Set
352
+ next if submatches.empty?
353
+
354
+ # sort the submatches by index
355
+ submatches = submatches.sort_by { |index,length,mutator| index }
356
+ sets = []
357
+ prev_index = 0
358
+
359
+ submatches.each do |index,length,mutator|
360
+ # add the previous substring to the set of Strings
361
+ if index > prev_index
362
+ sets << [self[prev_index,index - prev_index]]
363
+ end
364
+
365
+ # add the mutator to the set of Strings
366
+ sets << mutator
367
+
368
+ prev_index = index + length
369
+ end
370
+
371
+ # add the remaining substring to the set of Strings
372
+ if prev_index < self.length
373
+ sets << [self[prev_index..-1]]
374
+ end
375
+
376
+ sets.comprehension { |strings| yield strings.join }
377
+ end
378
+ end
379
+
380
+ end
@@ -0,0 +1,191 @@
1
+ #
2
+ # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
3
+ #
4
+ # This file is part of Ronin Support.
5
+ #
6
+ # Ronin Support is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Ronin Support is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public License
17
+ # along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+
20
+ require 'set'
21
+
22
+ module Ronin
23
+ module Fuzzing
24
+ SHORT_LENGTHS = SortedSet[1, 100, 500, 1_000, 10_000]
25
+
26
+ LONG_LENGTHS = SortedSet[
27
+ 128, 255, 256, 257, 511, 512, 513, 1023, 1024, 2048, 2049, 4095,
28
+ 4096, 4097, 5_000, 10_000, 20_000, 32762, 32763, 32764, 32765, 32766,
29
+ 32767, 32768, 32769,
30
+ 0xffff-2, 0xffff-1, 0xffff, 0xffff+1, 0xffff+2,
31
+ 99_999, 100_000, 500_000, 1_000_000
32
+ ]
33
+
34
+ # Null bytes in various encodings
35
+ NULL_BYTES = ['%00', '%u0000', "\x00"]
36
+
37
+ # Newline characters
38
+ NEW_LINES = ["\n", "\r", "\n\r"]
39
+
40
+ # Format String flags
41
+ FORMAT_STRINGS = ['%p', '%s', '%n']
42
+
43
+ #
44
+ # Returns a fuzzer method.
45
+ #
46
+ # @param [Symbol] name
47
+ # The name of the fuzzer to return.
48
+ #
49
+ # @return [Enumerator]
50
+ # An Enumerator for the fuzzer method.
51
+ #
52
+ # @api semipublic
53
+ #
54
+ # @since 0.4.0
55
+ #
56
+ def self.[](name)
57
+ if (!Object.respond_to?(name) && respond_to?(name))
58
+ enum_for(name)
59
+ end
60
+ end
61
+
62
+ def self.bad_strings(&block)
63
+ yield ''
64
+
65
+ chars = [
66
+ 'A', 'a', '1', '<', '>', '"', "'", '/', "\\", '?', '=', 'a=', '&',
67
+ '.', ',', '(', ')', ']', '[', '%', '*', '-', '+', '{', '}',
68
+ "\x14", "\xfe", "\xff"
69
+ ]
70
+
71
+ chars.each do |c|
72
+ LONG_LENGTHS.each { |length| yield c * length }
73
+ end
74
+
75
+ yield '!@#$%%^#$%#$@#$%$$@#$%^^**(()'
76
+ yield '%01%02%03%04%0a%0d%0aADSF'
77
+ yield '%01%02%03@%04%0a%0d%0aADSF'
78
+
79
+ NULL_BYTES.each do |c|
80
+ SHORT_LENGTHS.each { |length| yield c * length }
81
+ end
82
+
83
+ yield "%\xfe\xf0%\x00\xff"
84
+ yield "%\xfe\xf0%\x00\xff" * 20
85
+
86
+ SHORT_LENGTHS.each do |length|
87
+ yield "\xde\xad\xbe\xef" * length
88
+ end
89
+
90
+ yield "\n\r" * 100
91
+ yield "<>" * 500
92
+ end
93
+
94
+ def self.format_strings(&block)
95
+ FORMAT_STRINGS.each do |fmt|
96
+ yield fmt
97
+ yield fmt * 100
98
+ yield fmt * 500
99
+ yield "\"#{fmt}\"" * 500
100
+ end
101
+ end
102
+
103
+ def self.bad_paths(&block)
104
+ padding = 'A' * 5_000
105
+
106
+ yield "/.:/#{padding}\x00\x00"
107
+ yield "/.../#{padding}\x00\x00"
108
+
109
+ yield "\\\\*"
110
+ yield "\\\\?\\"
111
+
112
+ yield "/\\" * 5_000
113
+ yield '/.' * 5_000
114
+
115
+ NULL_BYTES.each do |c|
116
+ if c.start_with?('%')
117
+ yield "#{c}/"
118
+ yield "/#{c}"
119
+ yield "/#{c}/"
120
+ end
121
+ end
122
+ end
123
+
124
+ def self.bit_fields(&block)
125
+ ("\x00".."\xff").each do |c|
126
+ yield c
127
+ yield c << c # x2
128
+ yield c << c # x4
129
+ yield c << c # x8
130
+ end
131
+ end
132
+
133
+ def self.signed_bit_fields(&block)
134
+ ("\x80".."\xff").each do |c|
135
+ yield c
136
+ yield c << c # x2
137
+ yield c << c # x4
138
+ yield c << c # x8
139
+ end
140
+ end
141
+
142
+ def self.uint8(&block)
143
+ ("\x00".."\xff").each(&block)
144
+ end
145
+
146
+ def self.uint16
147
+ uint8 { |c| yield c * 2 }
148
+ end
149
+
150
+ def self.uint32
151
+ uint8 { |c| yield c * 4 }
152
+ end
153
+
154
+ def self.uint64
155
+ uint8 { |c| yield c * 8 }
156
+ end
157
+
158
+ def self.int8(&block)
159
+ ("\x00".."\x70").each(&block)
160
+ end
161
+
162
+ def self.int16
163
+ int8 { |c| yield c * 2 }
164
+ end
165
+
166
+ def self.int32
167
+ int8 { |c| yield c * 4 }
168
+ end
169
+
170
+ def self.int64
171
+ int8 { |c| yield c * 8 }
172
+ end
173
+
174
+ def self.sint8(&block)
175
+ ("\x80".."\xff").each(&block)
176
+ end
177
+
178
+ def self.sint16
179
+ sint8 { |c| yield c * 2 }
180
+ end
181
+
182
+ def self.sint32
183
+ sint8 { |c| yield c * 4 }
184
+ end
185
+
186
+ def self.sint64
187
+ sint8 { |c| yield c * 8 }
188
+ end
189
+
190
+ end
191
+ end