rbkb 0.6.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|