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.
- checksums.yaml +15 -0
- data/.bnsignore +25 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +27 -0
- data/History.txt +8 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +10 -45
- data/experimental/cap2files +21 -0
- data/experimental/cap2yaml +20 -0
- data/experimental/colordif.rb +72 -0
- data/experimental/deezee +91 -0
- data/experimental/feed +225 -0
- data/experimental/fmagic.rb +51 -0
- data/experimental/magicrip.c +92 -0
- data/experimental/magicripper.rb +63 -0
- data/lib/rbkb.rb +2 -48
- data/lib/rbkb/cli.rb +3 -2
- data/lib/rbkb/cli/bgrep.rb +1 -4
- data/lib/rbkb/cli/blit.rb +4 -0
- data/lib/rbkb/cli/crc32.rb +4 -1
- data/lib/rbkb/cli/dedump.rb +1 -0
- data/lib/rbkb/cli/rstrings.rb +1 -7
- data/lib/rbkb/extends.rb +8 -787
- data/lib/rbkb/extends/array.rb +29 -0
- data/lib/rbkb/extends/common.rb +13 -0
- data/lib/rbkb/extends/enumerable.rb +9 -0
- data/lib/rbkb/extends/float.rb +17 -0
- data/lib/rbkb/extends/numeric.rb +83 -0
- data/lib/rbkb/extends/object.rb +7 -0
- data/lib/rbkb/extends/string.rb +624 -0
- data/lib/rbkb/extends/symbol.rb +8 -0
- data/lib/rbkb/plug/blit.rb +21 -1
- data/lib/rbkb/plug/peer.rb +5 -0
- data/lib/rbkb/plug/plug.rb +6 -2
- data/lib/rbkb/version.rb +3 -0
- data/rbkb.gemspec +20 -34
- data/reference/blackbag-0.9.1.tgz +0 -0
- data/reference/note_http_unit_tests +3 -0
- data/spec/spec_helper.rb +5 -14
- data/spec/string_extends_spec.rb +129 -0
- data/test/{test_cli_blit.rb → disabled_test_cli_blit.rb} +0 -0
- data/test/{test_cli_feed.rb → disabled_test_cli_feed.rb} +0 -0
- data/test/{test_cli_telson.rb → disabled_test_cli_telson.rb} +0 -0
- data/test/test_cli_chars.rb +2 -0
- data/test/test_cli_helper.rb +3 -5
- data/test/test_cli_rstrings.rb +2 -0
- data/test/test_helper.rb +8 -0
- metadata +107 -89
- 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
|
-
|
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
|
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
|
169
|
+
unless /^(-?[0-9a-f]+)(?::(-?[0-9a-f]+))?$/i.match(r)
|
169
170
|
raise "invalid range #{r.inspect}"
|
170
171
|
end
|
171
172
|
|
data/lib/rbkb/cli/bgrep.rb
CHANGED
@@ -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
|
|
data/lib/rbkb/cli/crc32.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/rbkb/cli/dedump.rb
CHANGED
data/lib/rbkb/cli/rstrings.rb
CHANGED
@@ -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("\
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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
|
-
|