rbkb 0.6.10
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.
- data/History.txt +74 -0
- data/README.rdoc +149 -0
- data/Rakefile +47 -0
- data/bin/b64 +5 -0
- data/bin/bgrep +5 -0
- data/bin/blit +5 -0
- data/bin/c +5 -0
- data/bin/crc32 +5 -0
- data/bin/d64 +5 -0
- data/bin/dedump +5 -0
- data/bin/feed +5 -0
- data/bin/hexify +5 -0
- data/bin/len +5 -0
- data/bin/plugsrv +271 -0
- data/bin/rex +10 -0
- data/bin/rstrings +5 -0
- data/bin/slice +5 -0
- data/bin/telson +5 -0
- data/bin/unhexify +5 -0
- data/bin/urldec +5 -0
- data/bin/urlenc +5 -0
- data/bin/xor +5 -0
- data/cli_usage.rdoc +285 -0
- data/doctor-bag.jpg +0 -0
- data/lib/rbkb.rb +51 -0
- data/lib/rbkb/cli.rb +219 -0
- data/lib/rbkb/cli/b64.rb +35 -0
- data/lib/rbkb/cli/bgrep.rb +86 -0
- data/lib/rbkb/cli/blit.rb +89 -0
- data/lib/rbkb/cli/chars.rb +24 -0
- data/lib/rbkb/cli/crc32.rb +35 -0
- data/lib/rbkb/cli/d64.rb +28 -0
- data/lib/rbkb/cli/dedump.rb +52 -0
- data/lib/rbkb/cli/feed.rb +229 -0
- data/lib/rbkb/cli/hexify.rb +65 -0
- data/lib/rbkb/cli/len.rb +76 -0
- data/lib/rbkb/cli/rstrings.rb +108 -0
- data/lib/rbkb/cli/slice.rb +47 -0
- data/lib/rbkb/cli/telson.rb +87 -0
- data/lib/rbkb/cli/unhexify.rb +50 -0
- data/lib/rbkb/cli/urldec.rb +35 -0
- data/lib/rbkb/cli/urlenc.rb +35 -0
- data/lib/rbkb/cli/xor.rb +43 -0
- data/lib/rbkb/extends.rb +725 -0
- data/lib/rbkb/http.rb +21 -0
- data/lib/rbkb/http/base.rb +172 -0
- data/lib/rbkb/http/body.rb +214 -0
- data/lib/rbkb/http/common.rb +74 -0
- data/lib/rbkb/http/headers.rb +370 -0
- data/lib/rbkb/http/parameters.rb +104 -0
- data/lib/rbkb/http/request.rb +58 -0
- data/lib/rbkb/http/response.rb +86 -0
- data/lib/rbkb/plug.rb +9 -0
- data/lib/rbkb/plug/blit.rb +222 -0
- data/lib/rbkb/plug/cli.rb +83 -0
- data/lib/rbkb/plug/feed_import.rb +74 -0
- data/lib/rbkb/plug/peer.rb +67 -0
- data/lib/rbkb/plug/plug.rb +215 -0
- data/lib/rbkb/plug/proxy.rb +26 -0
- data/lib/rbkb/plug/unix_domain.rb +75 -0
- data/lib_usage.rdoc +176 -0
- data/rbkb.gemspec +38 -0
- data/spec/rbkb_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/test/test_cli_b64.rb +35 -0
- data/test/test_cli_bgrep.rb +137 -0
- data/test/test_cli_blit.rb +11 -0
- data/test/test_cli_chars.rb +21 -0
- data/test/test_cli_crc32.rb +108 -0
- data/test/test_cli_d64.rb +22 -0
- data/test/test_cli_dedump.rb +118 -0
- data/test/test_cli_feed.rb +11 -0
- data/test/test_cli_helper.rb +96 -0
- data/test/test_cli_hexify.rb +63 -0
- data/test/test_cli_len.rb +96 -0
- data/test/test_cli_rstrings.rb +15 -0
- data/test/test_cli_slice.rb +73 -0
- data/test/test_cli_telson.rb +11 -0
- data/test/test_cli_unhexify.rb +43 -0
- data/test/test_cli_urldec.rb +50 -0
- data/test/test_cli_urlenc.rb +44 -0
- data/test/test_cli_xor.rb +71 -0
- data/test/test_helper.rb +5 -0
- data/test/test_http.rb +27 -0
- data/test/test_http_helper.rb +60 -0
- data/test/test_http_request.rb +136 -0
- data/test/test_http_response.rb +222 -0
- data/test/test_rbkb.rb +19 -0
- metadata +238 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rbkb/cli'
|
2
|
+
|
3
|
+
# Copyright 2009 emonti at matasano.com
|
4
|
+
# See README.rdoc for license information
|
5
|
+
#
|
6
|
+
# urlenc converts a string or raw data to a url percent-encoded string
|
7
|
+
# Input can be supplied via stdin, a string argument, or a file (with -f).
|
8
|
+
# (url percent-encoding is just fancy hex encoding)
|
9
|
+
class Rbkb::Cli::Urlenc < Rbkb::Cli::Executable
|
10
|
+
def make_parser()
|
11
|
+
super()
|
12
|
+
add_std_file_opt(:indat)
|
13
|
+
arg = @oparse
|
14
|
+
arg.banner += " <data | blank for stdin>"
|
15
|
+
|
16
|
+
arg.on("-p", "--[no-]plus",
|
17
|
+
"Convert spaces to '+' (default: false)") do |p|
|
18
|
+
@opts[:plus] = p
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse(*args)
|
23
|
+
super(*args)
|
24
|
+
parse_string_argument(:indat)
|
25
|
+
parse_catchall()
|
26
|
+
end
|
27
|
+
|
28
|
+
def go(*args)
|
29
|
+
super(*args)
|
30
|
+
# Default to standard input
|
31
|
+
@opts[:indat] ||= @stdin.read()
|
32
|
+
@stdout << @opts[:indat].urlenc(:plus => @opts[:plus]) + "\n"
|
33
|
+
self.exit(0)
|
34
|
+
end
|
35
|
+
end
|
data/lib/rbkb/cli/xor.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rbkb/cli'
|
3
|
+
|
4
|
+
# Copyright 2009 emonti at matasano.com
|
5
|
+
# See README.rdoc for license information
|
6
|
+
#
|
7
|
+
# Repeating string xor. Takes input from a string, stdin, or a file (-f).
|
8
|
+
class Rbkb::Cli::Xor < Rbkb::Cli::Executable
|
9
|
+
def make_parser()
|
10
|
+
super()
|
11
|
+
add_std_file_opt(:indat)
|
12
|
+
arg = @oparse
|
13
|
+
arg.banner += " -k|-s <key> <data | stdin>"
|
14
|
+
|
15
|
+
arg.separator " Key options (you must specify one of the following):"
|
16
|
+
arg.on("-s", "--strkey STRING", "xor against STRING") do |s|
|
17
|
+
bail "only one key option can be specified with -s or -x" if @opts[:key]
|
18
|
+
@opts[:key] = s
|
19
|
+
end
|
20
|
+
|
21
|
+
arg.on("-x", "--hexkey HEXSTR", "xor against binary HEXSTR") do |x|
|
22
|
+
bail "only one key option can be specified with -s or -x" if @opts[:key]
|
23
|
+
x.sub!(/^0[xX]/, '')
|
24
|
+
bail "Unable to parse hex string" unless @opts[:key] = x.unhexify
|
25
|
+
end
|
26
|
+
return arg
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse(*args)
|
30
|
+
super(*args)
|
31
|
+
bail("You must specify a key with -s or -x\n#{@oparse}") unless @opts[:key]
|
32
|
+
parse_string_argument(:indat)
|
33
|
+
parse_catchall()
|
34
|
+
end
|
35
|
+
|
36
|
+
def go(*args)
|
37
|
+
super(*args)
|
38
|
+
@opts[:indat] ||= @stdin.read
|
39
|
+
@stdout << @opts[:indat].xor(@opts[:key])
|
40
|
+
self.exit(0)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
data/lib/rbkb/extends.rb
ADDED
@@ -0,0 +1,725 @@
|
|
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
|
+
|
8
|
+
module Rbkb
|
9
|
+
DEFAULT_BYTE_ORDER=:big
|
10
|
+
HEXCHARS = [("0".."9").to_a, ("a".."f").to_a].flatten
|
11
|
+
end
|
12
|
+
|
13
|
+
# Generates a random alphanumeric string of 'size' bytes (8 by default)
|
14
|
+
def random_alphanum(size = 8)
|
15
|
+
chars = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a
|
16
|
+
(1..size).collect{|a| chars[rand(chars.size)]}.join
|
17
|
+
end
|
18
|
+
|
19
|
+
# Generates a random string of 'size' bytes (8 by default)
|
20
|
+
def random_string(size = 8)
|
21
|
+
chars = (0..255).map {|c| c.chr }
|
22
|
+
(1..size).collect {|a| chars[rand(chars.size)]}
|
23
|
+
end
|
24
|
+
|
25
|
+
# Simple syntactic sugar to pass any object to a block
|
26
|
+
def with(x)
|
27
|
+
yield x if block_given?; x
|
28
|
+
end if not defined? with
|
29
|
+
|
30
|
+
|
31
|
+
#-----------------------------------------------------------------------------
|
32
|
+
|
33
|
+
# Mixins and class-specific items
|
34
|
+
|
35
|
+
class String
|
36
|
+
# shortcut for hex sanity with regex
|
37
|
+
def ishex? ; (self =~ /^[a-f0-9]+$/i)? true : false ; end
|
38
|
+
|
39
|
+
# Encode into percent-hexify url encoding format
|
40
|
+
def urlenc(opts={})
|
41
|
+
s=self
|
42
|
+
plus = opts[:plus]
|
43
|
+
unless (opts[:rx] ||= /[^A-Za-z0-9_\.~-]/).kind_of? Regexp
|
44
|
+
raise "rx must be a regular expression for a character class"
|
45
|
+
end
|
46
|
+
hx = Rbkb::HEXCHARS
|
47
|
+
|
48
|
+
s.gsub(opts[:rx]) do |c|
|
49
|
+
c=c[0]
|
50
|
+
(plus and c==32)? '+' : "%" + (hx[(c >> 4)] + hx[(c & 0xf )])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Undo percent-hexified url encoding data
|
55
|
+
def urldec(opts={})
|
56
|
+
s=self
|
57
|
+
s.gsub!('+', ' ') unless opts[:noplus]
|
58
|
+
s.gsub(/%([A-Fa-f0-9]{2})/) {$1.hex.chr}
|
59
|
+
end
|
60
|
+
|
61
|
+
# Base64 encode
|
62
|
+
def b64(len=nil)
|
63
|
+
ret = [self].pack("m").gsub("\n", "")
|
64
|
+
if len and Numeric === len
|
65
|
+
ret.scan(/.{1,#{len}}/).join("\n") + "\n"
|
66
|
+
else
|
67
|
+
ret
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Base64 decode
|
72
|
+
def d64; self.unpack("m")[0]; end
|
73
|
+
|
74
|
+
# right-align to 'a' alignment padded with 'p'
|
75
|
+
def ralign(a, p=' ')
|
76
|
+
s=self
|
77
|
+
p ||= ' '
|
78
|
+
l = s.length
|
79
|
+
pad = l.pad(a)
|
80
|
+
s.rjust(pad+l, p)
|
81
|
+
end
|
82
|
+
|
83
|
+
# left-align to 'a' alignment padded with 'p'
|
84
|
+
def lalign(a, p=' ')
|
85
|
+
s=self
|
86
|
+
p ||= ' '
|
87
|
+
l = s.length
|
88
|
+
pad = l.pad(a)
|
89
|
+
s.ljust(pad+l, p)
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
# Convert a string to ASCII hex string. Supports a few options for format:
|
94
|
+
#
|
95
|
+
# :delim - delimter between each hex byte
|
96
|
+
# :prefix - prefix before each hex byte
|
97
|
+
# :suffix - suffix after each hex byte
|
98
|
+
#
|
99
|
+
def hexify(opts={})
|
100
|
+
s=self
|
101
|
+
delim = opts[:delim]
|
102
|
+
pre = (opts[:prefix] || "")
|
103
|
+
suf = (opts[:suffix] || "")
|
104
|
+
|
105
|
+
if (rx=opts[:rx]) and not rx.kind_of? Regexp
|
106
|
+
raise "rx must be a regular expression for a character class"
|
107
|
+
end
|
108
|
+
|
109
|
+
hx=Rbkb::HEXCHARS
|
110
|
+
|
111
|
+
out=Array.new
|
112
|
+
|
113
|
+
s.each_byte do |c|
|
114
|
+
hc = if (rx and not rx.match c.chr)
|
115
|
+
c.chr
|
116
|
+
else
|
117
|
+
pre + (hx[(c >> 4)] + hx[(c & 0xf )]) + suf
|
118
|
+
end
|
119
|
+
out << (hc)
|
120
|
+
end
|
121
|
+
out.join(delim)
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# Convert ASCII hex string to raw.
|
126
|
+
#
|
127
|
+
# Parameters:
|
128
|
+
#
|
129
|
+
# d = optional 'delimiter' between hex bytes (zero+ spaces by default)
|
130
|
+
def unhexify(d=/\s*/)
|
131
|
+
self.strip.gsub(/([A-Fa-f0-9]{1,2})#{d}?/) { $1.hex.chr }
|
132
|
+
end
|
133
|
+
|
134
|
+
# Converts a hex value to numeric.
|
135
|
+
#
|
136
|
+
# Parameters:
|
137
|
+
#
|
138
|
+
# order => :big or :little endian (default is :big)
|
139
|
+
#
|
140
|
+
def hex_to_num(order=:big)
|
141
|
+
s=self
|
142
|
+
raise "invalid hex value: '#{s.inspect}'" unless s.ishex?
|
143
|
+
|
144
|
+
r = if order == :little
|
145
|
+
s.scan(/.{2}/).reverse.join
|
146
|
+
elsif order == :big
|
147
|
+
s
|
148
|
+
else
|
149
|
+
raise "Invalid byte order #{order.inspect}"
|
150
|
+
end.hex
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
# A "generalized" lazy bytestring -> numeric converter.
|
155
|
+
#
|
156
|
+
# Parameters:
|
157
|
+
#
|
158
|
+
# order => :big or :little endian (default is :big)
|
159
|
+
#
|
160
|
+
# Bonus: should work seamlessly with really large strings.
|
161
|
+
#
|
162
|
+
# >> ("\xFF"*10).dat_to_num
|
163
|
+
# => 1208925819614629174706175
|
164
|
+
# >> ("\xFF"*20).dat_to_num
|
165
|
+
# => 1461501637330902918203684832716283019655932542975
|
166
|
+
#
|
167
|
+
def dat_to_num(order=:big)
|
168
|
+
s=self
|
169
|
+
s.reverse! if order == :little
|
170
|
+
r = 0
|
171
|
+
s.each_byte {|c| r = ((r << 8) | c)}
|
172
|
+
r
|
173
|
+
end
|
174
|
+
alias lazy_to_n dat_to_num
|
175
|
+
alias lazy_to_num dat_to_num
|
176
|
+
alias dat_to_n dat_to_num
|
177
|
+
|
178
|
+
|
179
|
+
#### Crypto'ey stuff
|
180
|
+
|
181
|
+
# calculates entropy in string
|
182
|
+
#
|
183
|
+
# TQBF's description:
|
184
|
+
# "I also added a chi-squared test to quickly figure out entropy of a
|
185
|
+
# string, in "bits of randomness per byte". This is useful, so..."
|
186
|
+
def entropy
|
187
|
+
e = 0
|
188
|
+
0.upto(255) do |i|
|
189
|
+
x = count(i.chr)/size.to_f
|
190
|
+
if x > 0
|
191
|
+
e += - x * x.log2
|
192
|
+
end
|
193
|
+
end
|
194
|
+
e
|
195
|
+
end
|
196
|
+
|
197
|
+
# xor against a key. key will be repeated or truncated to self.size.
|
198
|
+
def xor(k)
|
199
|
+
s=self
|
200
|
+
out=StringIO.new ; i=0;
|
201
|
+
s.each_byte do |x|
|
202
|
+
out.write((x ^ (k[i] || k[i=0]) ).chr)
|
203
|
+
i+=1
|
204
|
+
end
|
205
|
+
out.string
|
206
|
+
end
|
207
|
+
|
208
|
+
# convert bytes to number then xor against another byte-string or number
|
209
|
+
def ^(x)
|
210
|
+
x = x.dat_to_num unless x.is_a? Numeric
|
211
|
+
(self.dat_to_num ^ x)#.to_bytes
|
212
|
+
end
|
213
|
+
|
214
|
+
# Byte rotation as found in lame ciphers.
|
215
|
+
# This was cribbed from Timur Duehr with only a minor change.
|
216
|
+
def rotate_bytes(k=0)
|
217
|
+
r = self.dup
|
218
|
+
i=0
|
219
|
+
self.each_byte do |b|
|
220
|
+
r[i] = ((b + k) % 384).chr
|
221
|
+
i+=1
|
222
|
+
end
|
223
|
+
return r
|
224
|
+
end
|
225
|
+
|
226
|
+
# String randomizer
|
227
|
+
def randomize ; self.split('').randomize.to_s ; end
|
228
|
+
|
229
|
+
# In-place string randomizer
|
230
|
+
def randomize! ; self.replace(randomize) end
|
231
|
+
|
232
|
+
|
233
|
+
# Returns or prints a hexdump in the style of 'hexdump -C'
|
234
|
+
#
|
235
|
+
# :len => optionally specify a length other than 16 for a wider or thinner
|
236
|
+
# dump. If length is an odd number, it will be rounded up.
|
237
|
+
#
|
238
|
+
# :out => optionally specify an alternate IO object for output. By default,
|
239
|
+
# hexdump will output to STDOUT. Pass a StringIO object and it will return
|
240
|
+
# it as a string.
|
241
|
+
#
|
242
|
+
# Example:
|
243
|
+
#
|
244
|
+
# Here's the default behavior done explicitely:
|
245
|
+
#
|
246
|
+
# >> xxd = dat.hexdump(:len => 16, :out => StringIO.new)
|
247
|
+
# => <a string containing hexdump>
|
248
|
+
#
|
249
|
+
# Here's how to change it to STDERR
|
250
|
+
#
|
251
|
+
# >> xxd = dat.hexdump(:len => 16, :out => STDERR)
|
252
|
+
# <prints hexdump on STDERR>
|
253
|
+
# -> nil # return value is nil!
|
254
|
+
#
|
255
|
+
def hexdump(opt={})
|
256
|
+
s=self
|
257
|
+
out = opt[:out] || StringIO.new
|
258
|
+
len = (opt[:len] and opt[:len] > 0)? opt[:len] + (opt[:len] % 2) : 16
|
259
|
+
|
260
|
+
off = opt[:start_addr] || 0
|
261
|
+
offlen = opt[:start_len] || 8
|
262
|
+
|
263
|
+
hlen=len/2
|
264
|
+
|
265
|
+
s.scan(/(?:.|\n){1,#{len}}/) do |m|
|
266
|
+
out.write(off.to_s(16).rjust(offlen, "0") + ' ')
|
267
|
+
|
268
|
+
i=0
|
269
|
+
m.each_byte do |c|
|
270
|
+
out.write c.to_s(16).rjust(2,"0") + " "
|
271
|
+
out.write(' ') if (i+=1) == hlen
|
272
|
+
end
|
273
|
+
|
274
|
+
out.write(" " * (len-i) ) # pad
|
275
|
+
out.write(" ") if i < hlen
|
276
|
+
|
277
|
+
out.write(" |" + m.tr("\0-\37\177-\377", '.') + "|\n")
|
278
|
+
off += m.length
|
279
|
+
end
|
280
|
+
|
281
|
+
out.write(off.to_s(16).rjust(offlen,'0') + "\n")
|
282
|
+
|
283
|
+
if out.class == StringIO
|
284
|
+
out.string
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
|
289
|
+
# Converts a hexdump back to binary - takes the same options as hexdump().
|
290
|
+
# Fairly flexible. Should work both with 'xxd' and 'hexdump -C' style dumps.
|
291
|
+
def dehexdump(opt={})
|
292
|
+
s=self
|
293
|
+
out = opt[:out] || StringIO.new
|
294
|
+
len = (opt[:len] and opt[:len] > 0)? opt[:len] : 16
|
295
|
+
|
296
|
+
hcrx = /[A-Fa-f0-9]/
|
297
|
+
dumprx = /^(#{hcrx}+):?\s*((?:#{hcrx}{2}\s*){0,#{len}})/
|
298
|
+
off = opt[:start_addr] || 0
|
299
|
+
|
300
|
+
i=1
|
301
|
+
# iterate each line of hexdump
|
302
|
+
s.split(/\r?\n/).each do |hl|
|
303
|
+
# match and check offset
|
304
|
+
if m = dumprx.match(hl) and $1.hex == off
|
305
|
+
i+=1
|
306
|
+
# take the data chunk and unhexify it
|
307
|
+
raw = $2.unhexify
|
308
|
+
off += out.write(raw)
|
309
|
+
else
|
310
|
+
raise "Hexdump parse error on line #{i} #{s}"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
if out.class == StringIO
|
315
|
+
out.string
|
316
|
+
end
|
317
|
+
end
|
318
|
+
alias dedump dehexdump
|
319
|
+
alias undump dehexdump
|
320
|
+
alias unhexdump dehexdump
|
321
|
+
|
322
|
+
|
323
|
+
# Binary grep
|
324
|
+
#
|
325
|
+
# Parameters:
|
326
|
+
#
|
327
|
+
# find : A Regexp or string to search for in self
|
328
|
+
# align : nil | numeric alignment (matches only made if aligned)
|
329
|
+
def bgrep(find, align=nil)
|
330
|
+
if align and (not align.is_a?(Integer) or align < 0)
|
331
|
+
raise "alignment must be a integer >= 0"
|
332
|
+
end
|
333
|
+
|
334
|
+
dat=self
|
335
|
+
if find.kind_of? Regexp
|
336
|
+
search = lambda do |m, buf|
|
337
|
+
if m = m.match(buf)
|
338
|
+
mtch = m[0]
|
339
|
+
off,endoff = m.offset(0)
|
340
|
+
return off, endoff, mtch
|
341
|
+
end
|
342
|
+
end
|
343
|
+
else
|
344
|
+
search = lambda do |s, buf|
|
345
|
+
if off = buf.index(s)
|
346
|
+
return off, off+s.size, s
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
ret=[]
|
352
|
+
pos = 0
|
353
|
+
while (res = search.call(find, dat[pos..-1]))
|
354
|
+
off, endoff, match = res
|
355
|
+
if align and ( pad = (pos+off).pad(align) ) != 0
|
356
|
+
pos += pad
|
357
|
+
else
|
358
|
+
hit = [pos+off, pos+endoff, match]
|
359
|
+
if not block_given? or yield([pos+off, pos+endoff, match])
|
360
|
+
ret << hit
|
361
|
+
end
|
362
|
+
pos += endoff
|
363
|
+
end
|
364
|
+
end
|
365
|
+
return ret
|
366
|
+
end
|
367
|
+
|
368
|
+
# A 'strings' method a-la unix strings utility. Finds printable strings in
|
369
|
+
# a binary blob.
|
370
|
+
# Supports ASCII and little endian unicode (though only for ASCII printable
|
371
|
+
# character.)
|
372
|
+
#
|
373
|
+
# === Parameters and options:
|
374
|
+
#
|
375
|
+
# * Use the :minimum parameter to specify minimum number of characters
|
376
|
+
# to match. (default = 6)
|
377
|
+
#
|
378
|
+
# * Use the :encoding parameter as one of :ascii, :unicode, or :both
|
379
|
+
# (default = :ascii)
|
380
|
+
#
|
381
|
+
# * The 'strings' method uses Regexp under the hood. Therefore
|
382
|
+
# you can pass a character class for "valid characters" with :valid
|
383
|
+
# (default = /[\r\n [:print:]]/)
|
384
|
+
#
|
385
|
+
# * Supports an optional block, which will be passed |offset, type, string|
|
386
|
+
# for each match.
|
387
|
+
# The block's boolean return value also determines whether the match
|
388
|
+
# passes or fails (true or false/nil) and gets returned by the function.
|
389
|
+
#
|
390
|
+
# === Return Value:
|
391
|
+
#
|
392
|
+
# Returns an array consisting of matches with the following elements:
|
393
|
+
#
|
394
|
+
# [[start_offset, end_offset, string_type, string], ...]
|
395
|
+
#
|
396
|
+
# * string_type will be one of :ascii or :unicode
|
397
|
+
# * end_offset will include the terminating null character
|
398
|
+
# * end_offset will include all null bytes in unicode strings (including
|
399
|
+
# * both terminating nulls)
|
400
|
+
#
|
401
|
+
# If strings are null terminated, the trailing null *IS* included
|
402
|
+
# in the end_offset. Unicode matches will also include null bytes.
|
403
|
+
#
|
404
|
+
# Todos?
|
405
|
+
# - better unicode support (i.e. not using half-assed unicode)
|
406
|
+
# - support other encodings such as all those the binutils strings does?
|
407
|
+
def strings(opts={})
|
408
|
+
opts[:encoding] ||= :both
|
409
|
+
prx = (opts[:valid] || /[\r\n [:print:]]/)
|
410
|
+
min = (opts[:minimum] || 6)
|
411
|
+
align = opts[:align]
|
412
|
+
|
413
|
+
raise "Minimum must be numeric and > 0" unless min.kind_of? Numeric and min > 0
|
414
|
+
|
415
|
+
arx = /(#{prx}{#{min}}?#{prx}*\x00?)/
|
416
|
+
urx = /((?:#{prx}\x00){#{min}}(?:#{prx}\x00)*(?:\x00\x00)?)/
|
417
|
+
|
418
|
+
rx = case (opts[:encoding] || :both).to_sym
|
419
|
+
when :ascii
|
420
|
+
arx
|
421
|
+
when :unicode
|
422
|
+
urx
|
423
|
+
when :both
|
424
|
+
Regexp.union( arx, urx )
|
425
|
+
else
|
426
|
+
raise "Encoding must be :unicode, :ascii, or :both"
|
427
|
+
end
|
428
|
+
|
429
|
+
off=0
|
430
|
+
ret = []
|
431
|
+
|
432
|
+
while mtch = rx.match(self[off..-1])
|
433
|
+
# calculate relative offsets
|
434
|
+
rel_off = mtch.offset(0)
|
435
|
+
startoff = off + rel_off[0]
|
436
|
+
endoff = off + rel_off[1]
|
437
|
+
off += rel_off[1]
|
438
|
+
|
439
|
+
if align and (pad=startoff.pad(align)) != 0
|
440
|
+
off = startoff + pad
|
441
|
+
next
|
442
|
+
end
|
443
|
+
|
444
|
+
stype = if mtch[1]
|
445
|
+
:ascii
|
446
|
+
elsif mtch[2]
|
447
|
+
:unicode
|
448
|
+
end
|
449
|
+
|
450
|
+
|
451
|
+
mret = [startoff, endoff, stype, mtch[0] ]
|
452
|
+
|
453
|
+
# yield to a block for additional criteria
|
454
|
+
next if block_given? and not yield( *mret )
|
455
|
+
|
456
|
+
ret << mret
|
457
|
+
end
|
458
|
+
|
459
|
+
return ret
|
460
|
+
end
|
461
|
+
|
462
|
+
# Does string "start with" dat?
|
463
|
+
# No clue whether/when this is faster than a regex, but it is easier to type.
|
464
|
+
def starts_with?(dat)
|
465
|
+
self[0,dat.size] == dat
|
466
|
+
end
|
467
|
+
|
468
|
+
# Returns a single null-terminated ascii string from beginning of self.
|
469
|
+
# This will return the entire string if no null is encountered.
|
470
|
+
#
|
471
|
+
# Parameters:
|
472
|
+
#
|
473
|
+
# off = specify an optional beggining offset
|
474
|
+
#
|
475
|
+
def cstring(off=0)
|
476
|
+
self[ off, self.index("\x00") || self.size ]
|
477
|
+
end
|
478
|
+
|
479
|
+
# returns CRC32 checksum for the string object
|
480
|
+
def crc32
|
481
|
+
## pure ruby version. slower, but here for reference (found on some forum)
|
482
|
+
# r = 0xFFFFFFFF
|
483
|
+
# self.each_byte do |b|
|
484
|
+
# r ^= b
|
485
|
+
# 8.times do
|
486
|
+
# r = (r>>1) ^ (0xEDB88320 * (r & 1))
|
487
|
+
# end
|
488
|
+
# end
|
489
|
+
# r ^ 0xFFFFFFFF
|
490
|
+
## or... we can just use:
|
491
|
+
Zlib.crc32 self
|
492
|
+
end
|
493
|
+
|
494
|
+
# This attempts to identify a blob of data using 'file(1)' via popen3
|
495
|
+
# (using popen3 because IO.popen blows)
|
496
|
+
# Tried doing this with a fmagic ruby extention to libmagic, but it was
|
497
|
+
# a whole lot slower.
|
498
|
+
def pipe_magick(arg="")
|
499
|
+
ret=""
|
500
|
+
Open3.popen3("file #{arg} -") do |w,r,e|
|
501
|
+
w.write self; w.close
|
502
|
+
ret = r.read ; r.close
|
503
|
+
ret.sub!(/^\/dev\/stdin: /, "")
|
504
|
+
end
|
505
|
+
ret
|
506
|
+
end
|
507
|
+
|
508
|
+
# Converts a '_' delimited string to CamelCase like 'foo_class' into
|
509
|
+
# 'FooClass'.
|
510
|
+
# See also: camelize_meth, decamelize
|
511
|
+
def camelize
|
512
|
+
self.gsub(/(^|_)([a-z])/) { $2.upcase }
|
513
|
+
end
|
514
|
+
|
515
|
+
# Converts a '_' delimited string to method style camelCase like 'foo_method'
|
516
|
+
# into 'fooMethod'.
|
517
|
+
# See also: camelize, decamelize
|
518
|
+
def camelize_meth
|
519
|
+
self.gsub(/_([a-z])/) { $1.upcase }
|
520
|
+
end
|
521
|
+
|
522
|
+
|
523
|
+
# Converts a CamelCase or camelCase string into '_' delimited form like
|
524
|
+
# 'FooBar' or 'fooBar' into 'foo_bar'.
|
525
|
+
#
|
526
|
+
# Note: This method only handles camel humps. Strings with consecutive
|
527
|
+
# uppercase chars like 'FooBAR' will be converted to 'foo_bar'
|
528
|
+
#
|
529
|
+
# See also: camelize, camelize_meth
|
530
|
+
def decamelize
|
531
|
+
self.gsub(/(^|[a-z])([A-Z])/) do
|
532
|
+
($1.empty?)? $2 : "#{$1}_#{$2}"
|
533
|
+
end.downcase
|
534
|
+
end
|
535
|
+
|
536
|
+
# convert a string to its idiomatic ruby class name
|
537
|
+
def class_name
|
538
|
+
r = ""
|
539
|
+
up = true
|
540
|
+
each_byte do |c|
|
541
|
+
if c == 95
|
542
|
+
if up
|
543
|
+
r << "::"
|
544
|
+
else
|
545
|
+
up = true
|
546
|
+
end
|
547
|
+
else
|
548
|
+
m = up ? :upcase : :to_s
|
549
|
+
r << (c.chr.send(m))
|
550
|
+
up = false
|
551
|
+
end
|
552
|
+
end
|
553
|
+
r
|
554
|
+
end
|
555
|
+
|
556
|
+
|
557
|
+
|
558
|
+
# Returns a reference to actual constant for a given name in namespace
|
559
|
+
# can be used to lookup classes from enums and such
|
560
|
+
def const_lookup(ns=Object)
|
561
|
+
if c=ns.constants.select {|n| n == self.class_name } and not c.empty?
|
562
|
+
ns.const_get(c.first)
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
# Return a self encapsulated in a StringIO object. This is handy.
|
567
|
+
def to_stringio
|
568
|
+
StringIO.new(self)
|
569
|
+
end
|
570
|
+
|
571
|
+
end # class String
|
572
|
+
|
573
|
+
|
574
|
+
class Symbol
|
575
|
+
# looks up this symbol as a constant defined in 'ns' (Object by default)
|
576
|
+
def const_lookup(ns=Object)
|
577
|
+
self.to_s.const_lookup(ns)
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
class Array
|
582
|
+
# randomizes the order of contents in the Array (self)
|
583
|
+
def randomize ; self.sort_by { rand } ; end
|
584
|
+
|
585
|
+
# Returns a randomly chosen element from self.
|
586
|
+
# Drew *is* sparta.
|
587
|
+
def rand_elem; self[rand(self.length)] ; end
|
588
|
+
end
|
589
|
+
|
590
|
+
class Float
|
591
|
+
def log2; Math.log(self)/Math.log(2); end
|
592
|
+
end
|
593
|
+
|
594
|
+
|
595
|
+
class Numeric
|
596
|
+
|
597
|
+
# calculate padding based on alignment(a)
|
598
|
+
def pad(a)
|
599
|
+
raise "bad alignment #{a.inspect}" unless a.kind_of? Numeric and a > 0
|
600
|
+
return self < 1 ? a + self : (a-1) - (self-1) % a
|
601
|
+
end
|
602
|
+
|
603
|
+
# tells you whether a number is within printable range
|
604
|
+
def printable?; self >= 0x20 and self <= 0x7e; end
|
605
|
+
|
606
|
+
# just to go with the flow
|
607
|
+
def randomize ; rand(self) ; end
|
608
|
+
|
609
|
+
# shortcut for packing a single number... wtf...
|
610
|
+
def pack(arg) ; [self].pack(arg) ; end
|
611
|
+
|
612
|
+
def clear_bits(c) ; (self ^ (self & c)) ; end
|
613
|
+
|
614
|
+
# Returns an array of chars per 8-bit break-up.
|
615
|
+
# Accepts a block for some transformation on each byte.
|
616
|
+
# (used by to_bytes and to_hex under the hood)
|
617
|
+
#
|
618
|
+
# args:
|
619
|
+
# order: byte order - :big or :little
|
620
|
+
# (only :big has meaning)
|
621
|
+
# siz: pack to this size. larger numbers will wrap
|
622
|
+
def to_chars(order=nil, siz=nil)
|
623
|
+
order ||= Rbkb::DEFAULT_BYTE_ORDER
|
624
|
+
n=self
|
625
|
+
siz ||= self.size
|
626
|
+
ret=[]
|
627
|
+
siz.times do
|
628
|
+
c = (n % 256)
|
629
|
+
if block_given? then (c = yield(c)) end
|
630
|
+
ret << c
|
631
|
+
n=(n >> 8)
|
632
|
+
end
|
633
|
+
return ((order == :big)? ret.reverse : ret)
|
634
|
+
end
|
635
|
+
|
636
|
+
# "packs" a number into bytes using bit-twiddling instead of pack()
|
637
|
+
#
|
638
|
+
# Uses to_chars under the hood. See also: to_hex
|
639
|
+
#
|
640
|
+
# args:
|
641
|
+
# siz: pack to this size. larger numbers will wrap
|
642
|
+
# order: byte order - :big or :little
|
643
|
+
# (only :big has meaning)
|
644
|
+
def to_bytes(order=nil, siz=nil)
|
645
|
+
to_chars(order,siz) {|c| c.chr }.join
|
646
|
+
end
|
647
|
+
|
648
|
+
# Converts a number to hex string with width and endian options.
|
649
|
+
# "packs" a number into bytes using bit-twiddling instead of pack()
|
650
|
+
#
|
651
|
+
# Uses to_chars under the hood. See also: to_bytes
|
652
|
+
#
|
653
|
+
# args:
|
654
|
+
# siz: pack to this size. larger numbers will wrap
|
655
|
+
# order: byte order - :big or :little
|
656
|
+
# (only :big has meaning)
|
657
|
+
#
|
658
|
+
def to_hex(o=nil, s=nil)
|
659
|
+
to_chars(o,s) {|c|
|
660
|
+
Rbkb::HEXCHARS[c.clear_bits(0xf) >> 4]+Rbkb::HEXCHARS[c.clear_bits(0xf0)]
|
661
|
+
}.join
|
662
|
+
end
|
663
|
+
|
664
|
+
# TODO Fix Numeric.to_guid for new to_bytes/char etc.
|
665
|
+
# def to_guid(order=Rbkb::DEFAULT_BYTE_ORDER)
|
666
|
+
# raw = self.to_bytes(order, 16)
|
667
|
+
# a,b,c,d,*e = raw.unpack("VvvnC6").map{|x| x.to_hex}
|
668
|
+
# e = e.join
|
669
|
+
# [a,b,c,d,e].join("-").upcase
|
670
|
+
# end
|
671
|
+
|
672
|
+
end # class Numeric
|
673
|
+
|
674
|
+
|
675
|
+
# some extra features for zlib... more to come?
|
676
|
+
module Zlib
|
677
|
+
OSMAP = {
|
678
|
+
OS_MSDOS => :msdos,
|
679
|
+
OS_AMIGA => :amiga,
|
680
|
+
OS_VMS => :vms,
|
681
|
+
OS_UNIX => :unix,
|
682
|
+
OS_ATARI => :atari,
|
683
|
+
OS_OS2 => :os2,
|
684
|
+
OS_TOPS20 => :tops20,
|
685
|
+
OS_WIN32 => :win32,
|
686
|
+
OS_VMCMS => :vmcms,
|
687
|
+
OS_ZSYSTEM => :zsystem,
|
688
|
+
OS_CPM => :cpm,
|
689
|
+
OS_RISCOS => :riscos,
|
690
|
+
OS_UNKNOWN => :unknown
|
691
|
+
}
|
692
|
+
|
693
|
+
# Helpers for Zlib::GzipFile... more to come?
|
694
|
+
class GzipFile
|
695
|
+
|
696
|
+
## extra info dump for gzipped files
|
697
|
+
def get_xtra_info
|
698
|
+
info = {
|
699
|
+
:file_crc => crc.to_hex,
|
700
|
+
:file_comment => comment,
|
701
|
+
:file_name => orig_name,
|
702
|
+
:level => level,
|
703
|
+
:mtime => mtime,
|
704
|
+
:os => (Zlib::OSMAP[os_code] || os_code)
|
705
|
+
}
|
706
|
+
end
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
710
|
+
class Object
|
711
|
+
## This is from Topher Cyll's Stupd IRB tricks
|
712
|
+
def mymethods
|
713
|
+
(self.methods - self.class.superclass.methods).sort
|
714
|
+
end
|
715
|
+
end
|
716
|
+
|
717
|
+
module Enumerable
|
718
|
+
def each_recursive(&block)
|
719
|
+
self.each do |n|
|
720
|
+
block.call(n)
|
721
|
+
n.each_recursive(&block) if n.kind_of? Array or n.kind_of? Hash
|
722
|
+
end
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|