rbkb 0.6.12 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +15 -0
  2. data/.bnsignore +25 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +27 -0
  6. data/History.txt +8 -0
  7. data/LICENSE.txt +22 -0
  8. data/Rakefile +10 -45
  9. data/experimental/cap2files +21 -0
  10. data/experimental/cap2yaml +20 -0
  11. data/experimental/colordif.rb +72 -0
  12. data/experimental/deezee +91 -0
  13. data/experimental/feed +225 -0
  14. data/experimental/fmagic.rb +51 -0
  15. data/experimental/magicrip.c +92 -0
  16. data/experimental/magicripper.rb +63 -0
  17. data/lib/rbkb.rb +2 -48
  18. data/lib/rbkb/cli.rb +3 -2
  19. data/lib/rbkb/cli/bgrep.rb +1 -4
  20. data/lib/rbkb/cli/blit.rb +4 -0
  21. data/lib/rbkb/cli/crc32.rb +4 -1
  22. data/lib/rbkb/cli/dedump.rb +1 -0
  23. data/lib/rbkb/cli/rstrings.rb +1 -7
  24. data/lib/rbkb/extends.rb +8 -787
  25. data/lib/rbkb/extends/array.rb +29 -0
  26. data/lib/rbkb/extends/common.rb +13 -0
  27. data/lib/rbkb/extends/enumerable.rb +9 -0
  28. data/lib/rbkb/extends/float.rb +17 -0
  29. data/lib/rbkb/extends/numeric.rb +83 -0
  30. data/lib/rbkb/extends/object.rb +7 -0
  31. data/lib/rbkb/extends/string.rb +624 -0
  32. data/lib/rbkb/extends/symbol.rb +8 -0
  33. data/lib/rbkb/plug/blit.rb +21 -1
  34. data/lib/rbkb/plug/peer.rb +5 -0
  35. data/lib/rbkb/plug/plug.rb +6 -2
  36. data/lib/rbkb/version.rb +3 -0
  37. data/rbkb.gemspec +20 -34
  38. data/reference/blackbag-0.9.1.tgz +0 -0
  39. data/reference/note_http_unit_tests +3 -0
  40. data/spec/spec_helper.rb +5 -14
  41. data/spec/string_extends_spec.rb +129 -0
  42. data/test/{test_cli_blit.rb → disabled_test_cli_blit.rb} +0 -0
  43. data/test/{test_cli_feed.rb → disabled_test_cli_feed.rb} +0 -0
  44. data/test/{test_cli_telson.rb → disabled_test_cli_telson.rb} +0 -0
  45. data/test/test_cli_chars.rb +2 -0
  46. data/test/test_cli_helper.rb +3 -5
  47. data/test/test_cli_rstrings.rb +2 -0
  48. data/test/test_helper.rb +8 -0
  49. metadata +107 -89
  50. data/test/test_rbkb.rb +0 -19
@@ -0,0 +1,29 @@
1
+
2
+ class Array
3
+
4
+ # Should be in the std library.
5
+ #
6
+ # keys = [:one, :two, :three]
7
+ # vals = [1, 2, 3]
8
+ #
9
+ # keys.zip(vals).to_hash
10
+ # #=> {:two=>2, :three=>3, :one=>1}})
11
+ #
12
+ # keys.to_hash(vals)
13
+ # #=> {:two=>2, :three=>3, :one=>1}})
14
+ def to_hash(vals=nil)
15
+ a = vals ? self.zip(vals) : self
16
+ a.inject({}) {|hash, i| hash[i[0]] = i[1]; hash}
17
+ end
18
+
19
+ # randomizes the order of contents in the Array (self)
20
+ def randomize
21
+ self.sort_by{ rand }
22
+ end
23
+
24
+ # Returns a randomly chosen element from self.
25
+ def rand_elem
26
+ self[rand(self.count)]
27
+ end
28
+ end
29
+
@@ -0,0 +1,13 @@
1
+ require "stringio"
2
+ require 'zlib'
3
+ require 'open3'
4
+ require 'enumerator'
5
+ require 'digest/md5'
6
+ require 'digest/sha1'
7
+ require 'digest/sha2'
8
+
9
+ module Rbkb
10
+ DEFAULT_BYTE_ORDER=:big
11
+ HEXCHARS = [("0".."9").to_a, ("a".."f").to_a].flatten
12
+ end
13
+
@@ -0,0 +1,9 @@
1
+
2
+ module Enumerable
3
+ def each_recursive(&block)
4
+ self.each do |n|
5
+ block.call(n)
6
+ n.each_recursive(&block) if Enumerable === n
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ require 'rbkb/extends/common'
2
+
3
+ module Rbkb
4
+ module Extends
5
+ module Float
6
+ def log2
7
+ Math.log(self)/Math.log(2)
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ # float is a weird "Type"
14
+ class Float
15
+ include Rbkb::Extends::Float
16
+ end
17
+
@@ -0,0 +1,83 @@
1
+ require 'rbkb/extends/common'
2
+
3
+ class Numeric
4
+
5
+ # calculate padding based on alignment(a)
6
+ def pad(a)
7
+ raise "bad alignment #{a.inspect}" unless a.kind_of? Numeric and a > 0
8
+ return self < 1 ? a + self : (a-1) - (self-1) % a
9
+ end
10
+
11
+ # tells you whether a number is within printable range
12
+ def printable?; self >= 0x20 and self <= 0x7e; end
13
+
14
+ # shortcut for packing a single number... wtf...
15
+ def pack(arg)
16
+ [self].pack(arg)
17
+ end
18
+
19
+ def clear_bits(c)
20
+ (self ^ (self & c))
21
+ end
22
+
23
+ # Returns an array of chars per 8-bit break-up.
24
+ # Accepts a block for some transformation on each byte.
25
+ # (used by to_bytes and to_hex under the hood)
26
+ #
27
+ # args:
28
+ # order: byte order - :big or :little
29
+ # (only :big has meaning)
30
+ # siz: pack to this size. larger numbers will wrap
31
+ def to_chars(order=nil, siz=nil)
32
+ order ||= Rbkb::DEFAULT_BYTE_ORDER
33
+ n=self
34
+ siz ||= self.size
35
+ ret=[]
36
+ siz.times do
37
+ c = (n % 256)
38
+ if block_given? then (c = yield(c)) end
39
+ ret << c
40
+ n=(n >> 8)
41
+ end
42
+ return ((order == :big)? ret.reverse : ret)
43
+ end
44
+
45
+ # "packs" a number into bytes using bit-twiddling instead of pack()
46
+ #
47
+ # Uses to_chars under the hood. See also: to_hex
48
+ #
49
+ # args:
50
+ # siz: pack to this size. larger numbers will wrap
51
+ # order: byte order - :big or :little
52
+ # (only :big has meaning)
53
+ def to_bytes(order=nil, siz=nil)
54
+ to_chars(order,siz) {|c| c.chr }.join
55
+ end
56
+
57
+ # Converts a number to hex string with width and endian options.
58
+ # "packs" a number into bytes using bit-twiddling instead of pack()
59
+ #
60
+ # Uses to_chars under the hood. See also: to_bytes
61
+ #
62
+ # args:
63
+ # siz: pack to this size. larger numbers will wrap
64
+ # order: byte order - :big or :little
65
+ # (only :big has meaning)
66
+ #
67
+ def to_hex(o=nil, s=nil)
68
+ to_chars(o,s) {|c|
69
+ Rbkb::HEXCHARS[c.clear_bits(0xf) >> 4]+Rbkb::HEXCHARS[c.clear_bits(0xf0)]
70
+ }.join
71
+ end
72
+
73
+ # TODO Fix Numeric.to_guid for new to_bytes/char etc.
74
+ # def to_guid(order=Rbkb::DEFAULT_BYTE_ORDER)
75
+ # raw = self.to_bytes(order, 16)
76
+ # a,b,c,d,*e = raw.unpack("VvvnC6").map{|x| x.to_hex}
77
+ # e = e.join
78
+ # [a,b,c,d,e].join("-").upcase
79
+ # end
80
+
81
+ end # class Numeric
82
+
83
+
@@ -0,0 +1,7 @@
1
+
2
+ class Object
3
+ ## This is from Topher Cyll's Stupd IRB tricks
4
+ def mymethods
5
+ (self.methods - self.class.superclass.methods).sort
6
+ end
7
+ end
@@ -0,0 +1,624 @@
1
+ require 'rbkb/extends/common'
2
+ require 'rbkb/extends/numeric'
3
+ require 'cgi'
4
+
5
+ module Rbkb
6
+ module Extends
7
+ module String
8
+ # This is so disgusting... but str.encode('BINARY')
9
+ # fails hard whenever certain utf-8 characters
10
+ # present. Try "\xca\xfe\xba\xbe".encode('BINARY')
11
+ # for kicks.
12
+ def force_to_binary
13
+ self.dup.force_encoding('binary')
14
+ end
15
+
16
+ def ishex?
17
+ (self =~ /\A([a-f0-9]{2}|\n)+\Z/i) == 0
18
+ end
19
+
20
+ # Encode to precent-hexify url encoded string
21
+ # @param [Hash] opts Optional parameters
22
+ # @option opts [optional, Boolean] :plus
23
+ # Treat + as a literal '+', instead of a space
24
+ def urlenc(opts={})
25
+ s=self
26
+ plus = opts[:plus]
27
+ unless (opts[:rx] ||= /[^A-Za-z0-9_\.~-]/).kind_of? Regexp
28
+ raise "rx must be a regular expression for a character class"
29
+ end
30
+ hx = Rbkb::HEXCHARS
31
+
32
+ s.gsub(opts[:rx]) do |c|
33
+ c=c.ord
34
+ (plus and c==32)? '+' : "%" + (hx[(c >> 4)] + hx[(c & 0xf )])
35
+ end
36
+ end
37
+
38
+ # Base64 encode
39
+ def b64(len=nil)
40
+ ret = [self].pack("m").gsub("\n", "")
41
+ if len and Numeric === len
42
+ ret.scan(/.{1,#{len}}/).join("\n") + "\n"
43
+ else
44
+ ret
45
+ end
46
+ end
47
+
48
+ # Base64 decode
49
+ def d64
50
+ self.unpack("m").first
51
+ end
52
+
53
+ # right-align to 'a' alignment padded with 'p'
54
+ def ralign(a, p=' ')
55
+ p ||= ' '
56
+ l = self.bytesize
57
+ pad = l.pad(a)
58
+ self.rjust(pad+l, p)
59
+ end
60
+
61
+ # left-align to 'a' alignment padded with 'p'
62
+ def lalign(a, p=' ')
63
+ p ||= ' '
64
+ l = self.bytesize
65
+ pad = l.pad(a)
66
+ self.ljust(pad+l, p)
67
+ end
68
+
69
+ # Undo percent-hexified url encoded string
70
+ # @param [Hash] opts Optional parameters
71
+ # @option opts [optional, Boolean] :noplus
72
+ # Treat + as a literal '+', instead of a space
73
+ def urldec(opts=nil)
74
+ if opts.nil? or opts.empty?
75
+ CGI.unescape(self)
76
+ else
77
+ s=self
78
+ s.gsub!('+', ' ') unless opts[:noplus]
79
+ s.gsub(/%([A-Fa-f0-9]{2})/) {$1.hex.chr}
80
+ end
81
+ end
82
+
83
+ # Convert a string to ASCII hex string. Supports a few options for
84
+ # format:
85
+ #
86
+ # @param [Hash] opts
87
+ #
88
+ # @option opts [String] :delim
89
+ # delimter between each hex byte
90
+ #
91
+ # @option opts [String] :prefix
92
+ # prefix before eaech hex byte
93
+ #
94
+ # @option opts [String] :suffix
95
+ # suffix after each hex byte
96
+ #
97
+ def hexify(opts={})
98
+ delim = opts[:delim]
99
+ pre = (opts[:prefix] || "")
100
+ suf = (opts[:suffix] || "")
101
+
102
+ if (rx=opts[:rx]) and not rx.kind_of? Regexp
103
+ raise "rx must be a regular expression for a character class"
104
+ end
105
+
106
+ hx=Rbkb::HEXCHARS
107
+
108
+ out=Array.new
109
+
110
+ self.each_byte do |c|
111
+ hc = if (rx and not rx.match c.chr)
112
+ c.chr
113
+ else
114
+ pre + (hx[(c >> 4)] + hx[(c & 0xf )]) + suf
115
+ end
116
+ out << (hc)
117
+ end
118
+ out.join(delim)
119
+ end
120
+
121
+
122
+ # Convert ASCII hex string to raw.
123
+ #
124
+ # @param [String] d optional 'delimiter' between hex bytes
125
+ # (zero+ spaces by default)
126
+ def unhexify(d=/\s*/)
127
+ self.strip.gsub(/([A-Fa-f0-9]{1,2})#{d}?/) { $1.hex.chr }
128
+ end
129
+
130
+ # Converts a hex value to numeric.
131
+ #
132
+ # Parameters:
133
+ #
134
+ # order => :big or :little endian (default is :big)
135
+ #
136
+ def hex_to_num(order=Rbkb::DEFAULT_BYTE_ORDER)
137
+ self.unhexify.dat_to_num(order)
138
+ end
139
+
140
+
141
+ # A "generalized" lazy bytestring -> numeric converter.
142
+ #
143
+ # @param [Symbol] order :big or :little endian (default is :big)
144
+ #
145
+ # Bonus: should work seamlessly with really large strings.
146
+ #
147
+ # >> ("\xFF"*10).dat_to_num
148
+ # => 1208925819614629174706175
149
+ # >> ("\xFF"*20).dat_to_num
150
+ # => 1461501637330902918203684832716283019655932542975
151
+ #
152
+ def dat_to_num(order=Rbkb::DEFAULT_BYTE_ORDER)
153
+ b = self.bytes
154
+ b = b.to_a.reverse if order == :little
155
+ r = 0
156
+ b.each {|c| r = ((r << 8) | c)}
157
+ r
158
+ end
159
+
160
+
161
+ #### Crypto'ey stuff
162
+
163
+ # calculates entropy in string
164
+ #
165
+ # TQBF's description:
166
+ # "I also added a chi-squared test to quickly figure out entropy of a
167
+ # string, in "bits of randomness per byte". This is useful, so..."
168
+ def entropy
169
+ e = 0
170
+ sz = self.bytesize.to_f
171
+ b = self.bytes
172
+ 0.upto(255) do |i|
173
+ x = b.count(i)/sz
174
+ if x > 0
175
+ e += - x * (Math.log(x)/Math.log(2))
176
+ end
177
+ end
178
+ e
179
+ end
180
+
181
+
182
+ # Produces a character frequency distribution histogram in descending
183
+ # order. Example:
184
+ #
185
+ # pp some_english_text.char_frequency()
186
+ #
187
+ # [[" ", 690],
188
+ # ["e", 354],
189
+ # ["t", 242],
190
+ # ["o", 233],
191
+ # ["i", 218],
192
+ # ...
193
+ # ]
194
+ #
195
+ def char_frequency
196
+ hits = {}
197
+ self.each_byte {|c| hits[c.chr] ||= 0; hits[c.chr] += 1 }
198
+ hits.to_a.sort {|a,b| b[1] <=> a[1] }
199
+ end
200
+
201
+ # xor against a key. key will be repeated or truncated to self.size.
202
+ def xor(k)
203
+ i=0
204
+ self.bytes.map do |b|
205
+ x = k.getbyte(i) || k.getbyte(i=0)
206
+ i+=1
207
+ (b ^ x).chr
208
+ end.join
209
+ end
210
+
211
+
212
+ # (en|de)ciphers using a substition cipher en/decoder ring in the form of a
213
+ # hash with orig => substitute mappings
214
+ def substitution(keymap)
215
+ split('').map {|c| (sub=keymap[c]) ? sub : c }.join
216
+ end
217
+
218
+
219
+ # (en|de)crypts using a substition xor en/decoder ring in the form of
220
+ # a hash with orig => substitute mappings. Used in conjunction with
221
+ # char_frequency, this sometimes provides a shorter way to derive a single
222
+ # character xor key used in conjunction with char_frequency.
223
+ def substitution_xor(keymap)
224
+ split('').map {|c| (sub=keymap[c]) ? sub.xor(c) : c }.join
225
+ end
226
+
227
+
228
+ # convert bytes to number then xor against another byte-string or number
229
+ def ^(x)
230
+ x = x.dat_to_num unless x.is_a? Numeric
231
+ (self.dat_to_num ^ x)#.to_bytes
232
+ end
233
+
234
+
235
+ # Byte rotation as found in lame ciphers.
236
+ def rotate_bytes(k=0)
237
+ k = (256 + k) if k < 0
238
+ self.bytes.map {|c| ((c + k) & 0xff).chr }.join
239
+ end
240
+
241
+
242
+ # String randomizer
243
+ def randomize
244
+ self.split('').randomize.to_s
245
+ end
246
+
247
+
248
+ # In-place string randomizer
249
+ def randomize!
250
+ self.replace(randomize)
251
+ end
252
+
253
+ # Does string "start with" dat?
254
+ # No clue whether/when this is faster than a regex, but it is easier to type.
255
+ def starts_with?(dat)
256
+ self.index(dat) == 0
257
+ end
258
+
259
+ # Returns a single null-terminated ascii string from beginning of self.
260
+ # This will return the entire string if no null is encountered.
261
+ #
262
+ # Parameters:
263
+ #
264
+ # off = specify an optional beggining offset
265
+ #
266
+ def cstring(off=0)
267
+ self[ off, (self.index("\x00") || self.size) ]
268
+ end
269
+
270
+ # returns CRC32 checksum for the string object
271
+ def crc32
272
+ ## pure ruby version. slower, but here for reference (found on some forum)
273
+ # r = 0xFFFFFFFF
274
+ # self.each_byte do |b|
275
+ # r ^= b
276
+ # 8.times do
277
+ # r = (r>>1) ^ (0xEDB88320 * (r & 1))
278
+ # end
279
+ # end
280
+ # r ^ 0xFFFFFFFF
281
+ ## or... we can just use:
282
+ Zlib.crc32 self
283
+ end
284
+
285
+ # @return [Digest::MD5] the MD5 digest/checksum for this string.
286
+ def md5
287
+ d=Digest::MD5.new()
288
+ d.update(self)
289
+ d
290
+ end
291
+ alias md5sum md5
292
+
293
+ # @return [Digest::SHA1] the SHA1 digest for this string.
294
+ def sha1
295
+ d=Digest::SHA1.new()
296
+ d.update(self)
297
+ d
298
+ end
299
+
300
+ # @return [Digest::SHA2] the SHA2 digest for this string.
301
+ def sha2
302
+ d=Digest::SHA2.new()
303
+ d.update(self)
304
+ d
305
+ end
306
+ alias sha256 sha2
307
+
308
+ # This attempts to identify a blob of data using 'file(1)' via popen3
309
+ # (using popen3 because IO.popen blows)
310
+ # Tried doing this with a fmagic ruby extention to libmagic, but it was
311
+ # a whole lot slower.
312
+ def pipe_magick(arg="")
313
+ ret=""
314
+ Open3.popen3("file #{arg} -") do |w,r,e|
315
+ w.write self; w.close
316
+ ret = r.read ; r.close
317
+ ret.sub!(/^\/dev\/stdin: /, "")
318
+ end
319
+ ret
320
+ end
321
+
322
+ # Converts a '_' delimited string to CamelCase like 'foo_class' into
323
+ # 'FooClass'.
324
+ # See also: camelize_meth, decamelize
325
+ def camelize
326
+ self.gsub(/(^|_)([a-z])/) { $2.upcase }
327
+ end
328
+
329
+ # Converts a '_' delimited string to method style camelCase like 'foo_method'
330
+ # into 'fooMethod'.
331
+ # See also: camelize, decamelize
332
+ def camelize_meth
333
+ self.gsub(/_([a-z])/) { $1.upcase }
334
+ end
335
+
336
+
337
+ # Converts a CamelCase or camelCase string into '_' delimited form like
338
+ # 'FooBar' or 'fooBar' into 'foo_bar'.
339
+ #
340
+ # Note: This method only handles camel humps. Strings with consecutive
341
+ # uppercase chars like 'FooBAR' will be converted to 'foo_bar'
342
+ #
343
+ # See also: camelize, camelize_meth
344
+ def decamelize
345
+ self.gsub(/(^|[a-z])([A-Z])/) do
346
+ ($1.empty?)? $2 : "#{$1}_#{$2}"
347
+ end.downcase
348
+ end
349
+
350
+ # convert a string to its idiomatic ruby class name
351
+ def class_name
352
+ r = ""
353
+ up = true
354
+ each_byte do |c|
355
+ if c == 95
356
+ if up
357
+ r << "::"
358
+ else
359
+ up = true
360
+ end
361
+ else
362
+ m = up ? :upcase : :to_s
363
+ r << (c.chr.send(m))
364
+ up = false
365
+ end
366
+ end
367
+ r
368
+ end
369
+
370
+ # Returns a reference to actual constant for a given name in namespace
371
+ # can be used to lookup classes from enums and such
372
+ def const_lookup(ns=Object)
373
+ if c=ns.constants.select {|n| n == self.class_name } and not c.empty?
374
+ ns.const_get(c.first)
375
+ end
376
+ end
377
+
378
+ # Return a self encapsulated in a StringIO object. This is handy.
379
+ def to_stringio
380
+ StringIO.new(self)
381
+ end
382
+
383
+ # Returns or prints a hexdump in the style of 'hexdump -C'
384
+ #
385
+ # :len => optionally specify a length other than 16 for a wider or thinner
386
+ # dump. If length is an odd number, it will be rounded up.
387
+ #
388
+ # :out => optionally specify an alternate IO object for output. By default,
389
+ # hexdump will output to STDOUT. Pass a StringIO object and it will return
390
+ # it as a string.
391
+ #
392
+ # Example:
393
+ #
394
+ # Here's the default behavior done explicitely:
395
+ #
396
+ # >> xxd = dat.hexdump(:len => 16, :out => StringIO.new)
397
+ # => <a string containing hexdump>
398
+ #
399
+ # Here's how to change it to STDERR
400
+ #
401
+ # >> xxd = dat.hexdump(:len => 16, :out => STDERR)
402
+ # <prints hexdump on STDERR>
403
+ # -> nil # return value is nil!
404
+ #
405
+ def hexdump(opt={})
406
+ s=self
407
+ out = opt[:out] || StringIO.new
408
+ len = (opt[:len] and opt[:len] > 0)? opt[:len] + (opt[:len] % 2) : 16
409
+
410
+ off = opt[:start_addr] || 0
411
+ offlen = opt[:start_len] || 8
412
+
413
+ hlen=len/2
414
+
415
+ s.bytes.each_slice(len) do |m|
416
+ out.write(off.to_s(16).rjust(offlen, "0") + ' ')
417
+
418
+ i=0
419
+ m.each do |c|
420
+ out.write "%0.2x " % c
421
+ out.write(' ') if (i+=1) == hlen
422
+ end
423
+
424
+ out.write(" " * (len-i) ) # pad
425
+ out.write(" ") if i < hlen
426
+
427
+ out.write(" |")
428
+ m.each do |c|
429
+ if c > 0x19 and c < 0x7f
430
+ out.write(c.chr)
431
+ else
432
+ out.write('.')
433
+ end
434
+ end
435
+ out.write("|\n")
436
+ off += m.length
437
+ end
438
+
439
+ out.write(off.to_s(16).rjust(offlen,'0') + "\n")
440
+
441
+ if out.class == StringIO
442
+ out.string
443
+ end
444
+ end
445
+
446
+
447
+ # Converts a hexdump back to binary - takes the same options as hexdump().
448
+ # Fairly flexible. Should work both with 'xxd' and 'hexdump -C' style dumps.
449
+ def dehexdump(opt={})
450
+ s=self
451
+ out = opt[:out] || StringIO.new
452
+ len = (opt[:len] and opt[:len] > 0)? opt[:len] : 16
453
+
454
+ hcrx = /[A-Fa-f0-9]/
455
+ dumprx = /^(#{hcrx}+):?\s*((?:#{hcrx}{2}\s*){0,#{len}})/
456
+ off = opt[:start_addr] || 0
457
+
458
+ i=1
459
+ # iterate each line of hexdump
460
+ s.split(/\r?\n/).each do |hl|
461
+ # match and check offset
462
+ if dumprx.match(hl) and $1.hex == off
463
+ i+=1
464
+ # take the data chunk and unhexify it
465
+ raw = $2.unhexify
466
+ off += out.write(raw)
467
+ else
468
+ raise "Hexdump parse error on line #{i} #{s}"
469
+ end
470
+ end
471
+
472
+ if out.class == StringIO
473
+ out.string
474
+ end
475
+ end
476
+ alias dedump dehexdump
477
+ alias undump dehexdump
478
+ alias unhexdump dehexdump
479
+
480
+
481
+ # Binary grep
482
+ #
483
+ # Parameters:
484
+ #
485
+ # find : A Regexp or string to search for in self
486
+ # align : nil | numeric alignment (matches only made if aligned)
487
+ def bgrep(find, align=nil)
488
+ if align and (not align.is_a?(Integer) or align < 0)
489
+ raise "alignment must be a integer >= 0"
490
+ end
491
+
492
+ dat=self
493
+ if find.kind_of? Regexp
494
+ search = lambda do |mf, buf|
495
+ if m = mf.match(buf)
496
+ mtch = m[0]
497
+ off,endoff = m.offset(0)
498
+ return off, endoff, mtch
499
+ end
500
+ end
501
+ else
502
+ search = lambda do |s, buf|
503
+ if off = buf.index(s)
504
+ return off, off+s.size, s
505
+ end
506
+ end
507
+ end
508
+
509
+ ret=[]
510
+ pos = 0
511
+ while (res = search.call(find, dat[pos..-1].force_to_binary))
512
+ off, endoff, match = res
513
+ if align and ( pad = (pos+off).pad(align) ) != 0
514
+ pos += pad
515
+ else
516
+ hit = [pos+off, pos+endoff, match]
517
+ if not block_given? or yield([pos+off, pos+endoff, match])
518
+ ret << hit
519
+ end
520
+ pos += endoff
521
+ end
522
+ end
523
+ return ret
524
+ end
525
+
526
+ # A 'strings' method a-la unix strings utility. Finds printable strings in
527
+ # a binary blob.
528
+ # Supports ASCII and little endian unicode (though only for ASCII printable
529
+ # character.)
530
+ #
531
+ # === Parameters and options:
532
+ #
533
+ # * Use the :minimum parameter to specify minimum number of characters
534
+ # to match. (default = 6)
535
+ #
536
+ # * Use the :encoding parameter as one of :ascii, :unicode, or :both
537
+ # (default = :ascii)
538
+ #
539
+ # * The 'strings' method uses Regexp under the hood. Therefore
540
+ # you can pass a character class for "valid characters" with :valid
541
+ # (default = /[\r\n [:print:]]/)
542
+ #
543
+ # * Supports an optional block, which will be passed |offset, type, string|
544
+ # for each match.
545
+ # The block's boolean return value also determines whether the match
546
+ # passes or fails (true or false/nil) and gets returned by the function.
547
+ #
548
+ # === Return Value:
549
+ #
550
+ # Returns an array consisting of matches with the following elements:
551
+ #
552
+ # [[start_offset, end_offset, string_type, string], ...]
553
+ #
554
+ # * string_type will be one of :ascii or :unicode
555
+ # * end_offset will include the terminating null character
556
+ # * end_offset will include all null bytes in unicode strings (including
557
+ # * both terminating nulls)
558
+ #
559
+ # If strings are null terminated, the trailing null *IS* included
560
+ # in the end_offset. Unicode matches will also include null bytes.
561
+ #
562
+ # Todos?
563
+ # - better unicode support (i.e. not using half-assed unicode)
564
+ # - support other encodings such as all those the binutils strings does?
565
+ def strings(opts={})
566
+ opts[:encoding] ||= :both
567
+ min = (opts[:minimum] || 6)
568
+
569
+ raise "Minimum must be numeric and > 0" unless min.kind_of? Numeric and min > 0
570
+
571
+ acc = /[\s[:print:]]/
572
+ ucc = /(?:#{acc}\x00)/
573
+
574
+ arx = /(#{acc}{#{min}}#{acc}*\x00?)/
575
+ urx = /(#{ucc}{#{min}}#{ucc}*(?:\x00\x00)?)/
576
+
577
+ rx = case (opts[:encoding] || :both).to_sym
578
+ when :ascii
579
+ mtype_blk = lambda {|x| :ascii }
580
+ arx
581
+ when :unicode
582
+ mtype_blk = lambda {|x| :unicode }
583
+ urx
584
+ when :both
585
+ mtype_blk = lambda {|x| (x[2].nil?)? :ascii : :unicode }
586
+
587
+ Regexp.union( arx, urx )
588
+ else
589
+ raise "Encoding must be :unicode, :ascii, or :both"
590
+ end
591
+
592
+ ret = []
593
+
594
+ # wow ruby 1.9 string encoding is a total cluster
595
+ self.force_to_binary.scan(rx) do
596
+ mtch = $~
597
+
598
+ stype = mtype_blk.call(mtch)
599
+
600
+ startoff, endoff = mtch.offset(0)
601
+ mret = [startoff, endoff, stype, mtch[0] ]
602
+
603
+ # yield to a block for additional criteria
604
+ next if block_given? and not yield( *mret )
605
+
606
+ ret << mret
607
+ end
608
+
609
+ return ret
610
+ end
611
+
612
+
613
+ end
614
+ end
615
+ end
616
+
617
+ class RbkbString < String
618
+ include Rbkb::Extends::String
619
+ end
620
+
621
+ def RbkbString(x)
622
+ RbkbString.new(x)
623
+ end
624
+