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,51 @@
1
+ #----------------------------------------------------------------------
2
+ # Optional extensions based on dependencies below:
3
+ #----------------------------------------------------------------------
4
+
5
+ begin
6
+ # magick signatures: attempt to identify a buffer with magic(5)
7
+ # using the same library as file(1)
8
+ #
9
+ # Extends strings with a 'magic' method using FileMagic
10
+ #
11
+ # To use, do the following (for MacOS X)
12
+ #
13
+ # * need macports. and the macports version of 'file'
14
+ # $ sudo port install file
15
+ #
16
+ # * Install ruby-filemagic
17
+ # $ wget http://raa.ruby-lang.org/cache/filemagic/
18
+ #
19
+ # * untar and build
20
+ # $ cd <untarred-directory>
21
+ # $ env ARCHFLAGS="-arch i386" ruby extconf.rb --with-magic-dir=/opt/local
22
+ # $ make
23
+ # $ sudo make install
24
+ #
25
+ # Example:
26
+ #
27
+ # irb(main):001:0> "foo".magic
28
+ # => "ASCII text, with no line terminators"
29
+ # irb(main):002:0> "\x1f\x8b".magic
30
+ # => "gzip compressed data"
31
+ #
32
+ # XXX this is horribly slow on large chunks of data, but then most everything
33
+ # in ruby is...
34
+ require 'filemagic'
35
+ class String
36
+ @@fmagick = nil
37
+ @@fmagick_opts = nil
38
+
39
+ def magick(opts=FileMagic::MAGIC_NONE)
40
+ if @@fmagick.nil? or @@fmagick_opts != opts
41
+ @@fmagick = FileMagic.new(opts)
42
+ @@fmagick_opts = opts
43
+ end
44
+ @@fmagick.buffer self
45
+ end
46
+ end
47
+ rescue
48
+ # nop
49
+ end
50
+
51
+
@@ -0,0 +1,92 @@
1
+ /*
2
+ * Matasano Security LLC (emonti at matasano) 2008
3
+ *
4
+ * magicripper:
5
+ * Rips through input attempting to identify contents with magic(5)
6
+ *
7
+ * This is, ofcourse, verrrry slow... and verrry prone to false positives..
8
+ *
9
+ * At this point this is still just a half-baked idea being played with.
10
+ * The idea here is to use specialized libmagic databases to look for specific
11
+ * things.
12
+ *
13
+ * Requires libmagic (included with 'file(1)' >= 4.20)
14
+ *
15
+ * Build on OS X with:
16
+ * $ sudo port install file
17
+ * $ gcc -o magicrip magicrip.c -lmagic -L/opt/local/lib -I/opt/local/include
18
+ *
19
+ */
20
+
21
+ #include <stdio.h>
22
+ #include <stdlib.h>
23
+ #include <string.h>
24
+ #include <fcntl.h>
25
+ #include <sys/stat.h>
26
+ #include <sys/mman.h>
27
+ #include <magic.h>
28
+
29
+ #define MAGIK_CHUNKLEN 1024
30
+
31
+ int magic_ripper(char *buf, size_t siz)
32
+ {
33
+ const char *magik;
34
+ char *pbuf = buf;
35
+ magic_t cookie;
36
+ size_t off, left;
37
+
38
+ while ((off = pbuf - buf) < siz)
39
+ {
40
+ if ( ((cookie = magic_open(MAGIC_NONE)) == NULL) ||
41
+ ((magic_load(cookie, NULL)) != 0) )
42
+ return(-1);
43
+
44
+ left = siz-off;
45
+ magik = magic_buffer(cookie, pbuf,
46
+ ((left=siz-off) > MAGIK_CHUNKLEN? MAGIK_CHUNKLEN : left) );
47
+
48
+ if ((magik) && ((memcmp(magik, "data\0", 5)) != 0))
49
+ printf("%0.8x: %s\n", off, magik);
50
+
51
+ magic_close(cookie);
52
+
53
+ pbuf++;
54
+ }
55
+ }
56
+
57
+ int main(int argc, char *argv[])
58
+ {
59
+ char *buf, *filename;
60
+ int fd, len;
61
+ struct stat stb;
62
+
63
+ if (argc != 2) {
64
+ fprintf(stderr, "usage: %s file\n", argv[0]);
65
+ exit(1);
66
+ }
67
+
68
+
69
+ if ( ((fd = open(argv[1], O_RDONLY)) < 0) || (fstat(fd, &stb)) ) {
70
+ fprintf(stderr, "Can't open %s\n", argv[1]);
71
+ exit(1);
72
+ }
73
+
74
+
75
+ // read the file to memory
76
+
77
+ buf = (char *) malloc(stb.st_size);
78
+ if ((len = read(fd, buf, stb.st_size)) < 0) {
79
+ fprintf(stderr, "memory error\n");
80
+ close(fd);
81
+ exit(1);
82
+ }
83
+
84
+ close(fd);
85
+
86
+ magic_ripper(buf, stb.st_size);
87
+
88
+ free(buf);
89
+ exit(0);
90
+
91
+ }
92
+
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ # (emonti at matasano) Matasano Security LLC 2008
3
+
4
+ require 'rbkb'
5
+ require 'rbkb/command_line'
6
+ require 'stringio'
7
+
8
+ include RBkB::CommandLine
9
+
10
+ first = 0
11
+ last = nil
12
+ chunksz = 8192
13
+
14
+ #------------------------------------------------------------------------------
15
+ # Init options and arg parsing
16
+ OPTS = {:len => 16}
17
+ arg = bkb_stdargs(nil, OPTS)
18
+
19
+ arg.banner += " <input-file | blank for stdin>"
20
+
21
+ arg.on("-s", "--start=OFF", Numeric, "Start at offset") {|s| first=s}
22
+ arg.on("-e", "--end=OFF", Numeric, "End at offset") {|e| last=e}
23
+ arg.on("-c", "--chunks=SIZE", Numeric, "Size at a time") {|c| chunksz=c}
24
+
25
+ begin
26
+ #----------------------------------------------------------------------------
27
+ # Parse arguments
28
+
29
+ arg.parse!(ARGV)
30
+
31
+ inp = nil
32
+
33
+ if a=ARGV.shift
34
+ inp=File.open(a, "rb") rescue "Error: Can't open file '#{a}'"
35
+ end
36
+
37
+ # catchall
38
+ if ARGV.length != 0
39
+ raise "bad arguments - #{ARGV.join(' ')}"
40
+ end
41
+
42
+ inp ||= StringIO.new(STDIN.read())
43
+
44
+ #----------------------------------------------------------------------------
45
+ # Do stuff
46
+
47
+ off = inp.pos = first
48
+ until inp.eof? or (last and inp.pos >= last)
49
+ off = inp.pos
50
+ dat = inp.read(chunksz)
51
+
52
+ ## XXX uncomment the next line to use a non-lib find call to Unix
53
+ #mg = dat.pipe_magick.chomp
54
+ mg = dat.magick.chomp
55
+
56
+ inp.pos = off + 1
57
+ puts "#{off.to_hex(4)}: #{mg}" unless mg == "data"
58
+ GC.start
59
+ end
60
+ rescue
61
+ bail $!
62
+ end
63
+
data/lib/rbkb.rb CHANGED
@@ -1,51 +1,5 @@
1
+ require "rbkb/version"
1
2
 
2
3
  module Rbkb
3
4
 
4
- # :stopdoc:
5
- VERSION = '0.6.12'
6
- LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
7
- PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
8
- # :startdoc:
9
-
10
- # Returns the version string for the library.
11
- #
12
- def self.version
13
- VERSION
14
- end
15
-
16
- # Returns the library path for the module. If any arguments are given,
17
- # they will be joined to the end of the libray path using
18
- # <tt>File.join</tt>.
19
- #
20
- def self.libpath( *args )
21
- args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
22
- end
23
-
24
- # Returns the lpath for the module. If any arguments are given,
25
- # they will be joined to the end of the path using
26
- # <tt>File.join</tt>.
27
- #
28
- def self.path( *args )
29
- args.empty? ? PATH : ::File.join(PATH, args.flatten)
30
- end
31
-
32
- # Utility method used to require all files ending in .rb that lie in the
33
- # directory below this file that has the same name as the filename passed
34
- # in. Optionally, a specific _directory_ name can be passed in such that
35
- # the _filename_ does not have to be equivalent to the directory.
36
- #
37
- def self.require_all_libs_relative_to( fname, dir = nil )
38
- dir ||= ::File.basename(fname, '.*')
39
- search_me = ::File.expand_path(
40
- ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
41
-
42
- Dir.glob(search_me).sort.each {|rb| require rb}
43
- end
44
-
45
- end # module Rbkb
46
-
47
- #Rbkb.require_all_libs_relative_to(__FILE__)
48
-
49
- require 'rbkb/extends'
50
-
51
- # EOF
5
+ end
data/lib/rbkb/cli.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rbkb'
2
+ require 'rbkb/extends'
2
3
  require 'optparse'
3
4
 
4
5
  # Copyright 2009 emonti at matasano.com
@@ -151,7 +152,7 @@ module Rbkb::Cli
151
152
  raise "-x and -r are mutually exclusive" if @parser_got_range
152
153
  @parser_got_range=true
153
154
 
154
- unless m=/^(-?[0-9]+)(?::(-?[0-9]+))?$/.match(r)
155
+ unless /^(-?[0-9]+)(?::(-?[0-9]+))?$/.match(r)
155
156
  raise "invalid range #{r.inspect}"
156
157
  end
157
158
 
@@ -165,7 +166,7 @@ module Rbkb::Cli
165
166
  raise "-x and -r are mutually exclusive" if @parser_got_range
166
167
  @parser_got_range=true
167
168
 
168
- unless m=/^(-?[0-9a-f]+)(?::(-?[0-9a-f]+))?$/i.match(r)
169
+ unless /^(-?[0-9a-f]+)(?::(-?[0-9a-f]+))?$/i.match(r)
169
170
  raise "invalid range #{r.inspect}"
170
171
  end
171
172
 
@@ -71,10 +71,7 @@ class Rbkb::Cli::Bgrep < Rbkb::Cli::Executable
71
71
  dat.bgrep(@find, @opts[:align]) do |hit_start, hit_end, match|
72
72
  @stdout.write "#{fname}:" if fname and @opts[:include_fname]
73
73
 
74
- @stdout.write(
75
- "#{(hit_start).to_hex.rjust(8,"0")}:"+
76
- "#{(hit_end).to_hex.rjust(8,"0")}:b:"+
77
- "#{match.inspect}\n")
74
+ @stdout.write("%0.8x:%0.8x:b:#{match.inspect}\n" %[hit_start, hit_end])
78
75
  end
79
76
 
80
77
  break unless fname=@argv.shift
data/lib/rbkb/cli/blit.rb CHANGED
@@ -31,6 +31,10 @@ class Rbkb::Cli::Blit < Rbkb::Cli::Executable
31
31
  @opts[:b_proto] = t.upcase.to_sym
32
32
  end
33
33
 
34
+ arg.on("-S", "--starttls", "Start TLS handshake for the peer index (-i)") do |s|
35
+ @blit_msg = Plug::Blit.make_starttls(@opts[:b_peeridx])
36
+ end
37
+
34
38
  arg.on("-b", "--blitsrv=ADDR:PORT",
35
39
  "Where to send blit messages") do |b|
36
40
 
@@ -27,7 +27,10 @@ class Rbkb::Cli::Crc32 < Rbkb::Cli::Executable
27
27
  def go(*args)
28
28
  super(*args)
29
29
  @opts[:indat] ||= @stdin.read()
30
- @stdout.puts @opts[:indat][ @opts[:first] .. @opts[:last] ].crc32.to_hex
30
+ dat = opts[:indat].force_to_binary
31
+ dat = dat[ @opts[:first] .. @opts[:last] ]
32
+ dat ||= ""
33
+ @stdout.puts( "%0.8x" % dat.force_to_binary.crc32 )
31
34
  self.exit(0)
32
35
  end
33
36
  end
@@ -1,4 +1,5 @@
1
1
  require 'rbkb/cli'
2
+ require 'rbkb/extends'
2
3
 
3
4
  # Copyright 2009 emonti at matasano.com
4
5
  # See README.rdoc for license information
@@ -12,7 +12,6 @@ class Rbkb::Cli::Rstrings < Rbkb::Cli::Executable
12
12
  :end_off => -1,
13
13
  :encoding => :both,
14
14
  :minimum => 6,
15
- :align => nil,
16
15
  :indat => Array.new,
17
16
  :fnames => Array.new,
18
17
  }.each {|k,v| this.opts[k] ||= v }
@@ -49,11 +48,6 @@ class Rbkb::Cli::Rstrings < Rbkb::Cli::Executable
49
48
  @opts[:minimum] = l
50
49
  end
51
50
 
52
- arg.on("-a", "--align=ALIGNMENT", Numeric,
53
- "Match only on alignment (default=none)") do |a|
54
- (@opts[:align] = a) > 0 or bail "bad alignment '#{a}'"
55
- end
56
-
57
51
  return arg
58
52
  end
59
53
 
@@ -97,7 +91,7 @@ class Rbkb::Cli::Rstrings < Rbkb::Cli::Executable
97
91
  end
98
92
  @stdout << "#{(off+start_off).to_hex.rjust(8,"0")}:"+
99
93
  "#{(len+start_off).to_hex.rjust(8,"0")}:"+
100
- "#{type.to_s[0,1]}:#{str.delete("\000").inspect}\n"
94
+ "#{type.to_s[0,1]}:#{str.delete("\x00").inspect}\n"
101
95
  end
102
96
  i+=1
103
97
  end
data/lib/rbkb/extends.rb CHANGED
@@ -1,791 +1,12 @@
1
- # Copyright 2009 emonti at matasano.com
2
- # See README.rdoc for license information
3
- #
4
- require "stringio"
5
- require 'zlib'
6
- require 'open3'
7
- require 'enumerator'
8
1
 
9
- module Rbkb
10
- DEFAULT_BYTE_ORDER=:big
11
- HEXCHARS = [("0".."9").to_a, ("a".."f").to_a].flatten
12
- end
13
-
14
- # Generates a random alphanumeric string of 'size' bytes (8 by default)
15
- def random_alphanum(size = 8)
16
- chars = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
17
- (1..size).collect{|a| chars[rand(chars.size)]}.join
18
- end
19
-
20
- # Generates a random string of 'size' bytes (8 by default)
21
- def random_string(size = 8)
22
- chars = (0..255).map {|c| c.chr }
23
- (1..size).collect {|a| chars[rand(chars.size)]}.join
24
- end
25
-
26
- # Simple syntactic sugar to pass any object to a block
27
- def with(x)
28
- yield x if block_given?; x
29
- end if not defined? with
30
-
31
-
32
- #-----------------------------------------------------------------------------
33
-
34
- # Mixins and class-specific items
2
+ require_relative 'extends/array'
3
+ require_relative 'extends/enumerable'
4
+ require_relative 'extends/float'
5
+ require_relative 'extends/numeric'
6
+ require_relative 'extends/object'
7
+ require_relative 'extends/string'
8
+ require_relative 'extends/symbol'
35
9
 
36
10
  class String
37
- # fake the ruby 1.9 String#bytes method if we don't have one
38
- def bytes
39
- ::Enumerable::Enumerator.new(self, :each_byte)
40
- end if not defined?("".bytes)
41
-
42
- # fake the ruby 1.9 String#getbyte method if we don't have one
43
- def getbyte(i)
44
- self[i]
45
- end if RUBY_VERSION.to_f < 1.9 and not defined?("".getbyte)
46
-
47
- # fake the ruby 1.9 String#ord method if we don't have one
48
- def ord
49
- getbyte(0)
50
- end if not defined?("".ord)
51
-
52
- # Works just like each_with_index, but with each_byte
53
- def each_byte_with_index
54
- bytes.each_with_index {|b,i| yield(b,i) }
55
- end
56
-
57
- # shortcut for hex sanity with regex
58
- def ishex? ; (self =~ /^[a-f0-9]+$/i) != nil ; end
59
-
60
- # Encode into percent-hexify url encoding format
61
- def urlenc(opts={})
62
- s=self
63
- plus = opts[:plus]
64
- unless (opts[:rx] ||= /[^A-Za-z0-9_\.~-]/).kind_of? Regexp
65
- raise "rx must be a regular expression for a character class"
66
- end
67
- hx = Rbkb::HEXCHARS
68
-
69
- s.gsub(opts[:rx]) do |c|
70
- c=c.ord
71
- (plus and c==32)? '+' : "%" + (hx[(c >> 4)] + hx[(c & 0xf )])
72
- end
73
- end
74
-
75
- # Undo percent-hexified url encoding data
76
- def urldec(opts={})
77
- s=self
78
- s.gsub!('+', ' ') unless opts[:noplus]
79
- s.gsub(/%([A-Fa-f0-9]{2})/) {$1.hex.chr}
80
- end
81
-
82
- # Base64 encode
83
- def b64(len=nil)
84
- ret = [self].pack("m").gsub("\n", "")
85
- if len and Numeric === len
86
- ret.scan(/.{1,#{len}}/).join("\n") + "\n"
87
- else
88
- ret
89
- end
90
- end
91
-
92
- # Base64 decode
93
- def d64; self.unpack("m").first ; end
94
-
95
- # right-align to 'a' alignment padded with 'p'
96
- def ralign(a, p=' ')
97
- p ||= ' '
98
- l = self.length
99
- pad = l.pad(a)
100
- self.rjust(pad+l, p)
101
- end
102
-
103
- # left-align to 'a' alignment padded with 'p'
104
- def lalign(a, p=' ')
105
- p ||= ' '
106
- l = self.length
107
- pad = l.pad(a)
108
- self.ljust(pad+l, p)
109
- end
110
-
111
-
112
- # Convert a string to ASCII hex string. Supports a few options for format:
113
- #
114
- # :delim - delimter between each hex byte
115
- # :prefix - prefix before each hex byte
116
- # :suffix - suffix after each hex byte
117
- #
118
- def hexify(opts={})
119
- delim = opts[:delim]
120
- pre = (opts[:prefix] || "")
121
- suf = (opts[:suffix] || "")
122
-
123
- if (rx=opts[:rx]) and not rx.kind_of? Regexp
124
- raise "rx must be a regular expression for a character class"
125
- end
126
-
127
- hx=Rbkb::HEXCHARS
128
-
129
- out=Array.new
130
-
131
- self.each_byte do |c|
132
- hc = if (rx and not rx.match c.chr)
133
- c.chr
134
- else
135
- pre + (hx[(c >> 4)] + hx[(c & 0xf )]) + suf
136
- end
137
- out << (hc)
138
- end
139
- out.join(delim)
140
- end
141
-
142
-
143
- # Convert ASCII hex string to raw.
144
- #
145
- # Parameters:
146
- #
147
- # d = optional 'delimiter' between hex bytes (zero+ spaces by default)
148
- def unhexify(d=/\s*/)
149
- self.strip.gsub(/([A-Fa-f0-9]{1,2})#{d}?/) { $1.hex.chr }
150
- end
151
-
152
- # Converts a hex value to numeric.
153
- #
154
- # Parameters:
155
- #
156
- # order => :big or :little endian (default is :big)
157
- #
158
- def hex_to_num(order=:big)
159
- s=self
160
- raise "invalid hex value: '#{s.inspect}'" unless s.ishex?
161
-
162
- r = if order == :little
163
- s.scan(/.{2}/).reverse.join
164
- elsif order == :big
165
- s
166
- else
167
- raise "Invalid byte order #{order.inspect}"
168
- end.hex
169
- end
170
-
171
-
172
- # A "generalized" lazy bytestring -> numeric converter.
173
- #
174
- # Parameters:
175
- #
176
- # order => :big or :little endian (default is :big)
177
- #
178
- # Bonus: should work seamlessly with really large strings.
179
- #
180
- # >> ("\xFF"*10).dat_to_num
181
- # => 1208925819614629174706175
182
- # >> ("\xFF"*20).dat_to_num
183
- # => 1461501637330902918203684832716283019655932542975
184
- #
185
- def dat_to_num(order=:big)
186
- s=self
187
- s.reverse! if order == :little
188
- r = 0
189
- s.each_byte {|c| r = ((r << 8) | c)}
190
- r
191
- end
192
- alias lazy_to_n dat_to_num
193
- alias lazy_to_num dat_to_num
194
- alias dat_to_n dat_to_num
195
-
196
-
197
- #### Crypto'ey stuff
198
-
199
- # calculates entropy in string
200
- #
201
- # TQBF's description:
202
- # "I also added a chi-squared test to quickly figure out entropy of a
203
- # string, in "bits of randomness per byte". This is useful, so..."
204
- def entropy
205
- e = 0
206
- 0.upto(255) do |i|
207
- x = count(i.chr)/size.to_f
208
- if x > 0
209
- e += - x * x.log2
210
- end
211
- end
212
- e
213
- end
214
-
215
-
216
- # Produces a character frequency distribution histogram in descending
217
- # order. Example:
218
- #
219
- # pp some_english_text.char_frequency()
220
- #
221
- # [[" ", 690],
222
- # ["e", 354],
223
- # ["t", 242],
224
- # ["o", 233],
225
- # ["i", 218],
226
- # ...
227
- # ]
228
- #
229
- def char_frequency
230
- hits = {}
231
- self.each_byte {|c| hits[c.chr] ||= 0; hits[c.chr] += 1 }
232
- hits.to_a.sort {|a,b| b[1] <=> a[1] }
233
- end
234
-
235
- # xor against a key. key will be repeated or truncated to self.size.
236
- def xor(k)
237
- i=0
238
- self.bytes.map do |b|
239
- x = k.getbyte(i) || k.getbyte(i=0)
240
- i+=1
241
- (b ^ x).chr
242
- end.join
243
- end
244
-
245
-
246
- # (en|de)ciphers using a substition cipher en/decoder ring in the form of a
247
- # hash with orig => substitute mappings
248
- def substitution(keymap)
249
- split('').map {|c| (sub=keymap[c]) ? sub : c }.join
250
- end
251
-
252
-
253
- # (en|de)crypts using a substition xor en/decoder ring in the form of
254
- # a hash with orig => substitute mappings. Used in conjunction with
255
- # char_frequency, this sometimes provides a shorter way to derive a single
256
- # character xor key used in conjunction with char_frequency.
257
- def substitution_xor(keymap)
258
- split('').map {|c| (sub=keymap[c]) ? sub.xor(c) : c }.join
259
- end
260
-
261
-
262
- # convert bytes to number then xor against another byte-string or number
263
- def ^(x)
264
- x = x.dat_to_num unless x.is_a? Numeric
265
- (self.dat_to_num ^ x)#.to_bytes
266
- end
267
-
268
-
269
- # Byte rotation as found in lame ciphers.
270
- def rotate_bytes(k=0)
271
- k = (256 + k) if k < 0
272
- self.bytes.map {|c| ((c + k) & 0xff).chr }.join
273
- end
274
-
275
-
276
- # String randomizer
277
- def randomize ; self.split('').randomize.to_s ; end
278
-
279
-
280
- # In-place string randomizer
281
- def randomize! ; self.replace(randomize) end
282
-
283
-
284
- # Returns or prints a hexdump in the style of 'hexdump -C'
285
- #
286
- # :len => optionally specify a length other than 16 for a wider or thinner
287
- # dump. If length is an odd number, it will be rounded up.
288
- #
289
- # :out => optionally specify an alternate IO object for output. By default,
290
- # hexdump will output to STDOUT. Pass a StringIO object and it will return
291
- # it as a string.
292
- #
293
- # Example:
294
- #
295
- # Here's the default behavior done explicitely:
296
- #
297
- # >> xxd = dat.hexdump(:len => 16, :out => StringIO.new)
298
- # => <a string containing hexdump>
299
- #
300
- # Here's how to change it to STDERR
301
- #
302
- # >> xxd = dat.hexdump(:len => 16, :out => STDERR)
303
- # <prints hexdump on STDERR>
304
- # -> nil # return value is nil!
305
- #
306
- def hexdump(opt={})
307
- s=self
308
- out = opt[:out] || StringIO.new
309
- len = (opt[:len] and opt[:len] > 0)? opt[:len] + (opt[:len] % 2) : 16
310
-
311
- off = opt[:start_addr] || 0
312
- offlen = opt[:start_len] || 8
313
-
314
- hlen=len/2
315
-
316
- s.scan(/(?:.|\n){1,#{len}}/) do |m|
317
- out.write(off.to_s(16).rjust(offlen, "0") + ' ')
318
-
319
- i=0
320
- m.each_byte do |c|
321
- out.write c.to_s(16).rjust(2,"0") + " "
322
- out.write(' ') if (i+=1) == hlen
323
- end
324
-
325
- out.write(" " * (len-i) ) # pad
326
- out.write(" ") if i < hlen
327
-
328
- out.write(" |#{m.tr("\0-\37\177-\377", '.')}|\n")
329
- off += m.length
330
- end
331
-
332
- out.write(off.to_s(16).rjust(offlen,'0') + "\n")
333
-
334
- if out.class == StringIO
335
- out.string
336
- end
337
- end
338
-
339
-
340
- # Converts a hexdump back to binary - takes the same options as hexdump().
341
- # Fairly flexible. Should work both with 'xxd' and 'hexdump -C' style dumps.
342
- def dehexdump(opt={})
343
- s=self
344
- out = opt[:out] || StringIO.new
345
- len = (opt[:len] and opt[:len] > 0)? opt[:len] : 16
346
-
347
- hcrx = /[A-Fa-f0-9]/
348
- dumprx = /^(#{hcrx}+):?\s*((?:#{hcrx}{2}\s*){0,#{len}})/
349
- off = opt[:start_addr] || 0
350
-
351
- i=1
352
- # iterate each line of hexdump
353
- s.split(/\r?\n/).each do |hl|
354
- # match and check offset
355
- if m = dumprx.match(hl) and $1.hex == off
356
- i+=1
357
- # take the data chunk and unhexify it
358
- raw = $2.unhexify
359
- off += out.write(raw)
360
- else
361
- raise "Hexdump parse error on line #{i} #{s}"
362
- end
363
- end
364
-
365
- if out.class == StringIO
366
- out.string
367
- end
368
- end
369
- alias dedump dehexdump
370
- alias undump dehexdump
371
- alias unhexdump dehexdump
372
-
373
-
374
- # Binary grep
375
- #
376
- # Parameters:
377
- #
378
- # find : A Regexp or string to search for in self
379
- # align : nil | numeric alignment (matches only made if aligned)
380
- def bgrep(find, align=nil)
381
- if align and (not align.is_a?(Integer) or align < 0)
382
- raise "alignment must be a integer >= 0"
383
- end
384
-
385
- dat=self
386
- if find.kind_of? Regexp
387
- search = lambda do |m, buf|
388
- if m = m.match(buf)
389
- mtch = m[0]
390
- off,endoff = m.offset(0)
391
- return off, endoff, mtch
392
- end
393
- end
394
- else
395
- search = lambda do |s, buf|
396
- if off = buf.index(s)
397
- return off, off+s.size, s
398
- end
399
- end
400
- end
401
-
402
- ret=[]
403
- pos = 0
404
- while (res = search.call(find, dat[pos..-1]))
405
- off, endoff, match = res
406
- if align and ( pad = (pos+off).pad(align) ) != 0
407
- pos += pad
408
- else
409
- hit = [pos+off, pos+endoff, match]
410
- if not block_given? or yield([pos+off, pos+endoff, match])
411
- ret << hit
412
- end
413
- pos += endoff
414
- end
415
- end
416
- return ret
417
- end
418
-
419
- # A 'strings' method a-la unix strings utility. Finds printable strings in
420
- # a binary blob.
421
- # Supports ASCII and little endian unicode (though only for ASCII printable
422
- # character.)
423
- #
424
- # === Parameters and options:
425
- #
426
- # * Use the :minimum parameter to specify minimum number of characters
427
- # to match. (default = 6)
428
- #
429
- # * Use the :encoding parameter as one of :ascii, :unicode, or :both
430
- # (default = :ascii)
431
- #
432
- # * The 'strings' method uses Regexp under the hood. Therefore
433
- # you can pass a character class for "valid characters" with :valid
434
- # (default = /[\r\n [:print:]]/)
435
- #
436
- # * Supports an optional block, which will be passed |offset, type, string|
437
- # for each match.
438
- # The block's boolean return value also determines whether the match
439
- # passes or fails (true or false/nil) and gets returned by the function.
440
- #
441
- # === Return Value:
442
- #
443
- # Returns an array consisting of matches with the following elements:
444
- #
445
- # [[start_offset, end_offset, string_type, string], ...]
446
- #
447
- # * string_type will be one of :ascii or :unicode
448
- # * end_offset will include the terminating null character
449
- # * end_offset will include all null bytes in unicode strings (including
450
- # * both terminating nulls)
451
- #
452
- # If strings are null terminated, the trailing null *IS* included
453
- # in the end_offset. Unicode matches will also include null bytes.
454
- #
455
- # Todos?
456
- # - better unicode support (i.e. not using half-assed unicode)
457
- # - support other encodings such as all those the binutils strings does?
458
- def strings(opts={})
459
- opts[:encoding] ||= :both
460
- prx = (opts[:valid] || /[\r\n [:print:]]/)
461
- min = (opts[:minimum] || 6)
462
- align = opts[:align]
463
-
464
- raise "Minimum must be numeric and > 0" unless min.kind_of? Numeric and min > 0
465
-
466
- arx = /(#{prx}{#{min}}?#{prx}*\x00?)/
467
- urx = /((?:#{prx}\x00){#{min}}(?:#{prx}\x00)*(?:\x00\x00)?)/
468
-
469
- rx = case (opts[:encoding] || :both).to_sym
470
- when :ascii
471
- arx
472
- when :unicode
473
- urx
474
- when :both
475
- Regexp.union( arx, urx )
476
- else
477
- raise "Encoding must be :unicode, :ascii, or :both"
478
- end
479
-
480
- off=0
481
- ret = []
482
-
483
- while mtch = rx.match(self[off..-1])
484
- # calculate relative offsets
485
- rel_off = mtch.offset(0)
486
- startoff = off + rel_off[0]
487
- endoff = off + rel_off[1]
488
- off += rel_off[1]
489
-
490
- if align and (pad=startoff.pad(align)) != 0
491
- off = startoff + pad
492
- next
493
- end
494
-
495
- stype = if mtch[1]
496
- :ascii
497
- elsif mtch[2]
498
- :unicode
499
- end
500
-
501
-
502
- mret = [startoff, endoff, stype, mtch[0] ]
503
-
504
- # yield to a block for additional criteria
505
- next if block_given? and not yield( *mret )
506
-
507
- ret << mret
508
- end
509
-
510
- return ret
511
- end
512
-
513
- # Does string "start with" dat?
514
- # No clue whether/when this is faster than a regex, but it is easier to type.
515
- def starts_with?(dat)
516
- self[0,dat.size] == dat
517
- end
518
-
519
- # Returns a single null-terminated ascii string from beginning of self.
520
- # This will return the entire string if no null is encountered.
521
- #
522
- # Parameters:
523
- #
524
- # off = specify an optional beggining offset
525
- #
526
- def cstring(off=0)
527
- self[ off, self.index("\x00") || self.size ]
528
- end
529
-
530
- # returns CRC32 checksum for the string object
531
- def crc32
532
- ## pure ruby version. slower, but here for reference (found on some forum)
533
- # r = 0xFFFFFFFF
534
- # self.each_byte do |b|
535
- # r ^= b
536
- # 8.times do
537
- # r = (r>>1) ^ (0xEDB88320 * (r & 1))
538
- # end
539
- # end
540
- # r ^ 0xFFFFFFFF
541
- ## or... we can just use:
542
- Zlib.crc32 self
543
- end
544
-
545
- # This attempts to identify a blob of data using 'file(1)' via popen3
546
- # (using popen3 because IO.popen blows)
547
- # Tried doing this with a fmagic ruby extention to libmagic, but it was
548
- # a whole lot slower.
549
- def pipe_magick(arg="")
550
- ret=""
551
- Open3.popen3("file #{arg} -") do |w,r,e|
552
- w.write self; w.close
553
- ret = r.read ; r.close
554
- ret.sub!(/^\/dev\/stdin: /, "")
555
- end
556
- ret
557
- end
558
-
559
- # Converts a '_' delimited string to CamelCase like 'foo_class' into
560
- # 'FooClass'.
561
- # See also: camelize_meth, decamelize
562
- def camelize
563
- self.gsub(/(^|_)([a-z])/) { $2.upcase }
564
- end
565
-
566
- # Converts a '_' delimited string to method style camelCase like 'foo_method'
567
- # into 'fooMethod'.
568
- # See also: camelize, decamelize
569
- def camelize_meth
570
- self.gsub(/_([a-z])/) { $1.upcase }
571
- end
572
-
573
-
574
- # Converts a CamelCase or camelCase string into '_' delimited form like
575
- # 'FooBar' or 'fooBar' into 'foo_bar'.
576
- #
577
- # Note: This method only handles camel humps. Strings with consecutive
578
- # uppercase chars like 'FooBAR' will be converted to 'foo_bar'
579
- #
580
- # See also: camelize, camelize_meth
581
- def decamelize
582
- self.gsub(/(^|[a-z])([A-Z])/) do
583
- ($1.empty?)? $2 : "#{$1}_#{$2}"
584
- end.downcase
585
- end
586
-
587
- # convert a string to its idiomatic ruby class name
588
- def class_name
589
- r = ""
590
- up = true
591
- each_byte do |c|
592
- if c == 95
593
- if up
594
- r << "::"
595
- else
596
- up = true
597
- end
598
- else
599
- m = up ? :upcase : :to_s
600
- r << (c.chr.send(m))
601
- up = false
602
- end
603
- end
604
- r
605
- end
606
-
607
-
608
- # Returns a reference to actual constant for a given name in namespace
609
- # can be used to lookup classes from enums and such
610
- def const_lookup(ns=Object)
611
- if c=ns.constants.select {|n| n == self.class_name } and not c.empty?
612
- ns.const_get(c.first)
613
- end
614
- end
615
-
616
- # Return a self encapsulated in a StringIO object. This is handy.
617
- def to_stringio
618
- StringIO.new(self)
619
- end
620
-
621
- end # class String
622
-
623
-
624
- class Symbol
625
- # looks up this symbol as a constant defined in 'ns' (Object by default)
626
- def const_lookup(ns=Object)
627
- self.to_s.const_lookup(ns)
628
- end
629
- end
630
-
631
- class Array
632
-
633
- # Should be in the std library.
634
- #
635
- # keys = [:one, :two, :three]
636
- # vals = [1, 2, 3]
637
- #
638
- # keys.zip(vals).to_hash
639
- # #=> {:two=>2, :three=>3, :one=>1}})
640
- #
641
- # keys.to_hash(vals)
642
- # #=> {:two=>2, :three=>3, :one=>1}})
643
- def to_hash(vals=nil)
644
- a = vals ? self.zip(vals) : self
645
- a.inject({}) {|hash, i| hash[i[0]] = i[1]; hash}
646
- end
647
-
648
- # randomizes the order of contents in the Array (self)
649
- def randomize ; self.sort_by { rand } ; end
650
-
651
- # Returns a randomly chosen element from self.
652
- # Drew *is* sparta.
653
- def rand_elem; self[rand(self.length)] ; end
654
- end
655
-
656
- class Float
657
- def log2; Math.log(self)/Math.log(2); end
658
- end
659
-
660
-
661
- class Numeric
662
-
663
- # calculate padding based on alignment(a)
664
- def pad(a)
665
- raise "bad alignment #{a.inspect}" unless a.kind_of? Numeric and a > 0
666
- return self < 1 ? a + self : (a-1) - (self-1) % a
667
- end
668
-
669
- # tells you whether a number is within printable range
670
- def printable?; self >= 0x20 and self <= 0x7e; end
671
-
672
- # just to go with the flow
673
- def randomize ; rand(self) ; end
674
-
675
- # shortcut for packing a single number... wtf...
676
- def pack(arg) ; [self].pack(arg) ; end
677
-
678
- def clear_bits(c) ; (self ^ (self & c)) ; end
679
-
680
- # Returns an array of chars per 8-bit break-up.
681
- # Accepts a block for some transformation on each byte.
682
- # (used by to_bytes and to_hex under the hood)
683
- #
684
- # args:
685
- # order: byte order - :big or :little
686
- # (only :big has meaning)
687
- # siz: pack to this size. larger numbers will wrap
688
- def to_chars(order=nil, siz=nil)
689
- order ||= Rbkb::DEFAULT_BYTE_ORDER
690
- n=self
691
- siz ||= self.size
692
- ret=[]
693
- siz.times do
694
- c = (n % 256)
695
- if block_given? then (c = yield(c)) end
696
- ret << c
697
- n=(n >> 8)
698
- end
699
- return ((order == :big)? ret.reverse : ret)
700
- end
701
-
702
- # "packs" a number into bytes using bit-twiddling instead of pack()
703
- #
704
- # Uses to_chars under the hood. See also: to_hex
705
- #
706
- # args:
707
- # siz: pack to this size. larger numbers will wrap
708
- # order: byte order - :big or :little
709
- # (only :big has meaning)
710
- def to_bytes(order=nil, siz=nil)
711
- to_chars(order,siz) {|c| c.chr }.join
712
- end
713
-
714
- # Converts a number to hex string with width and endian options.
715
- # "packs" a number into bytes using bit-twiddling instead of pack()
716
- #
717
- # Uses to_chars under the hood. See also: to_bytes
718
- #
719
- # args:
720
- # siz: pack to this size. larger numbers will wrap
721
- # order: byte order - :big or :little
722
- # (only :big has meaning)
723
- #
724
- def to_hex(o=nil, s=nil)
725
- to_chars(o,s) {|c|
726
- Rbkb::HEXCHARS[c.clear_bits(0xf) >> 4]+Rbkb::HEXCHARS[c.clear_bits(0xf0)]
727
- }.join
728
- end
729
-
730
- # TODO Fix Numeric.to_guid for new to_bytes/char etc.
731
- # def to_guid(order=Rbkb::DEFAULT_BYTE_ORDER)
732
- # raw = self.to_bytes(order, 16)
733
- # a,b,c,d,*e = raw.unpack("VvvnC6").map{|x| x.to_hex}
734
- # e = e.join
735
- # [a,b,c,d,e].join("-").upcase
736
- # end
737
-
738
- end # class Numeric
739
-
740
-
741
- # some extra features for zlib... more to come?
742
- module Zlib
743
- OSMAP = {
744
- OS_MSDOS => :msdos,
745
- OS_AMIGA => :amiga,
746
- OS_VMS => :vms,
747
- OS_UNIX => :unix,
748
- OS_ATARI => :atari,
749
- OS_OS2 => :os2,
750
- OS_TOPS20 => :tops20,
751
- OS_WIN32 => :win32,
752
- OS_VMCMS => :vmcms,
753
- OS_ZSYSTEM => :zsystem,
754
- OS_CPM => :cpm,
755
- OS_RISCOS => :riscos,
756
- OS_UNKNOWN => :unknown
757
- }
758
-
759
- # Helpers for Zlib::GzipFile... more to come?
760
- class GzipFile
761
-
762
- ## extra info dump for gzipped files
763
- def get_xtra_info
764
- info = {
765
- :file_crc => crc.to_hex,
766
- :file_comment => comment,
767
- :file_name => orig_name,
768
- :level => level,
769
- :mtime => mtime,
770
- :os => (Zlib::OSMAP[os_code] || os_code)
771
- }
772
- end
773
- end
774
- end
775
-
776
- class Object
777
- ## This is from Topher Cyll's Stupd IRB tricks
778
- def mymethods
779
- (self.methods - self.class.superclass.methods).sort
780
- end
11
+ include Rbkb::Extends::String
781
12
  end
782
-
783
- module Enumerable
784
- def each_recursive(&block)
785
- self.each do |n|
786
- block.call(n)
787
- n.each_recursive(&block) if n.kind_of? Array or n.kind_of? Hash
788
- end
789
- end
790
- end
791
-