rbkb 0.6.12 → 0.7.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.
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
+