rbkb 0.6.12 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,29 @@
|
|
1
|
+
|
2
|
+
class Array
|
3
|
+
|
4
|
+
# Should be in the std library.
|
5
|
+
#
|
6
|
+
# keys = [:one, :two, :three]
|
7
|
+
# vals = [1, 2, 3]
|
8
|
+
#
|
9
|
+
# keys.zip(vals).to_hash
|
10
|
+
# #=> {:two=>2, :three=>3, :one=>1}})
|
11
|
+
#
|
12
|
+
# keys.to_hash(vals)
|
13
|
+
# #=> {:two=>2, :three=>3, :one=>1}})
|
14
|
+
def to_hash(vals=nil)
|
15
|
+
a = vals ? self.zip(vals) : self
|
16
|
+
a.inject({}) {|hash, i| hash[i[0]] = i[1]; hash}
|
17
|
+
end
|
18
|
+
|
19
|
+
# randomizes the order of contents in the Array (self)
|
20
|
+
def randomize
|
21
|
+
self.sort_by{ rand }
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a randomly chosen element from self.
|
25
|
+
def rand_elem
|
26
|
+
self[rand(self.count)]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "stringio"
|
2
|
+
require 'zlib'
|
3
|
+
require 'open3'
|
4
|
+
require 'enumerator'
|
5
|
+
require 'digest/md5'
|
6
|
+
require 'digest/sha1'
|
7
|
+
require 'digest/sha2'
|
8
|
+
|
9
|
+
module Rbkb
|
10
|
+
DEFAULT_BYTE_ORDER=:big
|
11
|
+
HEXCHARS = [("0".."9").to_a, ("a".."f").to_a].flatten
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rbkb/extends/common'
|
2
|
+
|
3
|
+
class Numeric
|
4
|
+
|
5
|
+
# calculate padding based on alignment(a)
|
6
|
+
def pad(a)
|
7
|
+
raise "bad alignment #{a.inspect}" unless a.kind_of? Numeric and a > 0
|
8
|
+
return self < 1 ? a + self : (a-1) - (self-1) % a
|
9
|
+
end
|
10
|
+
|
11
|
+
# tells you whether a number is within printable range
|
12
|
+
def printable?; self >= 0x20 and self <= 0x7e; end
|
13
|
+
|
14
|
+
# shortcut for packing a single number... wtf...
|
15
|
+
def pack(arg)
|
16
|
+
[self].pack(arg)
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear_bits(c)
|
20
|
+
(self ^ (self & c))
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns an array of chars per 8-bit break-up.
|
24
|
+
# Accepts a block for some transformation on each byte.
|
25
|
+
# (used by to_bytes and to_hex under the hood)
|
26
|
+
#
|
27
|
+
# args:
|
28
|
+
# order: byte order - :big or :little
|
29
|
+
# (only :big has meaning)
|
30
|
+
# siz: pack to this size. larger numbers will wrap
|
31
|
+
def to_chars(order=nil, siz=nil)
|
32
|
+
order ||= Rbkb::DEFAULT_BYTE_ORDER
|
33
|
+
n=self
|
34
|
+
siz ||= self.size
|
35
|
+
ret=[]
|
36
|
+
siz.times do
|
37
|
+
c = (n % 256)
|
38
|
+
if block_given? then (c = yield(c)) end
|
39
|
+
ret << c
|
40
|
+
n=(n >> 8)
|
41
|
+
end
|
42
|
+
return ((order == :big)? ret.reverse : ret)
|
43
|
+
end
|
44
|
+
|
45
|
+
# "packs" a number into bytes using bit-twiddling instead of pack()
|
46
|
+
#
|
47
|
+
# Uses to_chars under the hood. See also: to_hex
|
48
|
+
#
|
49
|
+
# args:
|
50
|
+
# siz: pack to this size. larger numbers will wrap
|
51
|
+
# order: byte order - :big or :little
|
52
|
+
# (only :big has meaning)
|
53
|
+
def to_bytes(order=nil, siz=nil)
|
54
|
+
to_chars(order,siz) {|c| c.chr }.join
|
55
|
+
end
|
56
|
+
|
57
|
+
# Converts a number to hex string with width and endian options.
|
58
|
+
# "packs" a number into bytes using bit-twiddling instead of pack()
|
59
|
+
#
|
60
|
+
# Uses to_chars under the hood. See also: to_bytes
|
61
|
+
#
|
62
|
+
# args:
|
63
|
+
# siz: pack to this size. larger numbers will wrap
|
64
|
+
# order: byte order - :big or :little
|
65
|
+
# (only :big has meaning)
|
66
|
+
#
|
67
|
+
def to_hex(o=nil, s=nil)
|
68
|
+
to_chars(o,s) {|c|
|
69
|
+
Rbkb::HEXCHARS[c.clear_bits(0xf) >> 4]+Rbkb::HEXCHARS[c.clear_bits(0xf0)]
|
70
|
+
}.join
|
71
|
+
end
|
72
|
+
|
73
|
+
# TODO Fix Numeric.to_guid for new to_bytes/char etc.
|
74
|
+
# def to_guid(order=Rbkb::DEFAULT_BYTE_ORDER)
|
75
|
+
# raw = self.to_bytes(order, 16)
|
76
|
+
# a,b,c,d,*e = raw.unpack("VvvnC6").map{|x| x.to_hex}
|
77
|
+
# e = e.join
|
78
|
+
# [a,b,c,d,e].join("-").upcase
|
79
|
+
# end
|
80
|
+
|
81
|
+
end # class Numeric
|
82
|
+
|
83
|
+
|
@@ -0,0 +1,624 @@
|
|
1
|
+
require 'rbkb/extends/common'
|
2
|
+
require 'rbkb/extends/numeric'
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module Rbkb
|
6
|
+
module Extends
|
7
|
+
module String
|
8
|
+
# This is so disgusting... but str.encode('BINARY')
|
9
|
+
# fails hard whenever certain utf-8 characters
|
10
|
+
# present. Try "\xca\xfe\xba\xbe".encode('BINARY')
|
11
|
+
# for kicks.
|
12
|
+
def force_to_binary
|
13
|
+
self.dup.force_encoding('binary')
|
14
|
+
end
|
15
|
+
|
16
|
+
def ishex?
|
17
|
+
(self =~ /\A([a-f0-9]{2}|\n)+\Z/i) == 0
|
18
|
+
end
|
19
|
+
|
20
|
+
# Encode to precent-hexify url encoded string
|
21
|
+
# @param [Hash] opts Optional parameters
|
22
|
+
# @option opts [optional, Boolean] :plus
|
23
|
+
# Treat + as a literal '+', instead of a space
|
24
|
+
def urlenc(opts={})
|
25
|
+
s=self
|
26
|
+
plus = opts[:plus]
|
27
|
+
unless (opts[:rx] ||= /[^A-Za-z0-9_\.~-]/).kind_of? Regexp
|
28
|
+
raise "rx must be a regular expression for a character class"
|
29
|
+
end
|
30
|
+
hx = Rbkb::HEXCHARS
|
31
|
+
|
32
|
+
s.gsub(opts[:rx]) do |c|
|
33
|
+
c=c.ord
|
34
|
+
(plus and c==32)? '+' : "%" + (hx[(c >> 4)] + hx[(c & 0xf )])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Base64 encode
|
39
|
+
def b64(len=nil)
|
40
|
+
ret = [self].pack("m").gsub("\n", "")
|
41
|
+
if len and Numeric === len
|
42
|
+
ret.scan(/.{1,#{len}}/).join("\n") + "\n"
|
43
|
+
else
|
44
|
+
ret
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Base64 decode
|
49
|
+
def d64
|
50
|
+
self.unpack("m").first
|
51
|
+
end
|
52
|
+
|
53
|
+
# right-align to 'a' alignment padded with 'p'
|
54
|
+
def ralign(a, p=' ')
|
55
|
+
p ||= ' '
|
56
|
+
l = self.bytesize
|
57
|
+
pad = l.pad(a)
|
58
|
+
self.rjust(pad+l, p)
|
59
|
+
end
|
60
|
+
|
61
|
+
# left-align to 'a' alignment padded with 'p'
|
62
|
+
def lalign(a, p=' ')
|
63
|
+
p ||= ' '
|
64
|
+
l = self.bytesize
|
65
|
+
pad = l.pad(a)
|
66
|
+
self.ljust(pad+l, p)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Undo percent-hexified url encoded string
|
70
|
+
# @param [Hash] opts Optional parameters
|
71
|
+
# @option opts [optional, Boolean] :noplus
|
72
|
+
# Treat + as a literal '+', instead of a space
|
73
|
+
def urldec(opts=nil)
|
74
|
+
if opts.nil? or opts.empty?
|
75
|
+
CGI.unescape(self)
|
76
|
+
else
|
77
|
+
s=self
|
78
|
+
s.gsub!('+', ' ') unless opts[:noplus]
|
79
|
+
s.gsub(/%([A-Fa-f0-9]{2})/) {$1.hex.chr}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Convert a string to ASCII hex string. Supports a few options for
|
84
|
+
# format:
|
85
|
+
#
|
86
|
+
# @param [Hash] opts
|
87
|
+
#
|
88
|
+
# @option opts [String] :delim
|
89
|
+
# delimter between each hex byte
|
90
|
+
#
|
91
|
+
# @option opts [String] :prefix
|
92
|
+
# prefix before eaech hex byte
|
93
|
+
#
|
94
|
+
# @option opts [String] :suffix
|
95
|
+
# suffix after each hex byte
|
96
|
+
#
|
97
|
+
def hexify(opts={})
|
98
|
+
delim = opts[:delim]
|
99
|
+
pre = (opts[:prefix] || "")
|
100
|
+
suf = (opts[:suffix] || "")
|
101
|
+
|
102
|
+
if (rx=opts[:rx]) and not rx.kind_of? Regexp
|
103
|
+
raise "rx must be a regular expression for a character class"
|
104
|
+
end
|
105
|
+
|
106
|
+
hx=Rbkb::HEXCHARS
|
107
|
+
|
108
|
+
out=Array.new
|
109
|
+
|
110
|
+
self.each_byte do |c|
|
111
|
+
hc = if (rx and not rx.match c.chr)
|
112
|
+
c.chr
|
113
|
+
else
|
114
|
+
pre + (hx[(c >> 4)] + hx[(c & 0xf )]) + suf
|
115
|
+
end
|
116
|
+
out << (hc)
|
117
|
+
end
|
118
|
+
out.join(delim)
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
# Convert ASCII hex string to raw.
|
123
|
+
#
|
124
|
+
# @param [String] d optional 'delimiter' between hex bytes
|
125
|
+
# (zero+ spaces by default)
|
126
|
+
def unhexify(d=/\s*/)
|
127
|
+
self.strip.gsub(/([A-Fa-f0-9]{1,2})#{d}?/) { $1.hex.chr }
|
128
|
+
end
|
129
|
+
|
130
|
+
# Converts a hex value to numeric.
|
131
|
+
#
|
132
|
+
# Parameters:
|
133
|
+
#
|
134
|
+
# order => :big or :little endian (default is :big)
|
135
|
+
#
|
136
|
+
def hex_to_num(order=Rbkb::DEFAULT_BYTE_ORDER)
|
137
|
+
self.unhexify.dat_to_num(order)
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
# A "generalized" lazy bytestring -> numeric converter.
|
142
|
+
#
|
143
|
+
# @param [Symbol] order :big or :little endian (default is :big)
|
144
|
+
#
|
145
|
+
# Bonus: should work seamlessly with really large strings.
|
146
|
+
#
|
147
|
+
# >> ("\xFF"*10).dat_to_num
|
148
|
+
# => 1208925819614629174706175
|
149
|
+
# >> ("\xFF"*20).dat_to_num
|
150
|
+
# => 1461501637330902918203684832716283019655932542975
|
151
|
+
#
|
152
|
+
def dat_to_num(order=Rbkb::DEFAULT_BYTE_ORDER)
|
153
|
+
b = self.bytes
|
154
|
+
b = b.to_a.reverse if order == :little
|
155
|
+
r = 0
|
156
|
+
b.each {|c| r = ((r << 8) | c)}
|
157
|
+
r
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
#### Crypto'ey stuff
|
162
|
+
|
163
|
+
# calculates entropy in string
|
164
|
+
#
|
165
|
+
# TQBF's description:
|
166
|
+
# "I also added a chi-squared test to quickly figure out entropy of a
|
167
|
+
# string, in "bits of randomness per byte". This is useful, so..."
|
168
|
+
def entropy
|
169
|
+
e = 0
|
170
|
+
sz = self.bytesize.to_f
|
171
|
+
b = self.bytes
|
172
|
+
0.upto(255) do |i|
|
173
|
+
x = b.count(i)/sz
|
174
|
+
if x > 0
|
175
|
+
e += - x * (Math.log(x)/Math.log(2))
|
176
|
+
end
|
177
|
+
end
|
178
|
+
e
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
# Produces a character frequency distribution histogram in descending
|
183
|
+
# order. Example:
|
184
|
+
#
|
185
|
+
# pp some_english_text.char_frequency()
|
186
|
+
#
|
187
|
+
# [[" ", 690],
|
188
|
+
# ["e", 354],
|
189
|
+
# ["t", 242],
|
190
|
+
# ["o", 233],
|
191
|
+
# ["i", 218],
|
192
|
+
# ...
|
193
|
+
# ]
|
194
|
+
#
|
195
|
+
def char_frequency
|
196
|
+
hits = {}
|
197
|
+
self.each_byte {|c| hits[c.chr] ||= 0; hits[c.chr] += 1 }
|
198
|
+
hits.to_a.sort {|a,b| b[1] <=> a[1] }
|
199
|
+
end
|
200
|
+
|
201
|
+
# xor against a key. key will be repeated or truncated to self.size.
|
202
|
+
def xor(k)
|
203
|
+
i=0
|
204
|
+
self.bytes.map do |b|
|
205
|
+
x = k.getbyte(i) || k.getbyte(i=0)
|
206
|
+
i+=1
|
207
|
+
(b ^ x).chr
|
208
|
+
end.join
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
# (en|de)ciphers using a substition cipher en/decoder ring in the form of a
|
213
|
+
# hash with orig => substitute mappings
|
214
|
+
def substitution(keymap)
|
215
|
+
split('').map {|c| (sub=keymap[c]) ? sub : c }.join
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
# (en|de)crypts using a substition xor en/decoder ring in the form of
|
220
|
+
# a hash with orig => substitute mappings. Used in conjunction with
|
221
|
+
# char_frequency, this sometimes provides a shorter way to derive a single
|
222
|
+
# character xor key used in conjunction with char_frequency.
|
223
|
+
def substitution_xor(keymap)
|
224
|
+
split('').map {|c| (sub=keymap[c]) ? sub.xor(c) : c }.join
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
# convert bytes to number then xor against another byte-string or number
|
229
|
+
def ^(x)
|
230
|
+
x = x.dat_to_num unless x.is_a? Numeric
|
231
|
+
(self.dat_to_num ^ x)#.to_bytes
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
# Byte rotation as found in lame ciphers.
|
236
|
+
def rotate_bytes(k=0)
|
237
|
+
k = (256 + k) if k < 0
|
238
|
+
self.bytes.map {|c| ((c + k) & 0xff).chr }.join
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
# String randomizer
|
243
|
+
def randomize
|
244
|
+
self.split('').randomize.to_s
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
# In-place string randomizer
|
249
|
+
def randomize!
|
250
|
+
self.replace(randomize)
|
251
|
+
end
|
252
|
+
|
253
|
+
# Does string "start with" dat?
|
254
|
+
# No clue whether/when this is faster than a regex, but it is easier to type.
|
255
|
+
def starts_with?(dat)
|
256
|
+
self.index(dat) == 0
|
257
|
+
end
|
258
|
+
|
259
|
+
# Returns a single null-terminated ascii string from beginning of self.
|
260
|
+
# This will return the entire string if no null is encountered.
|
261
|
+
#
|
262
|
+
# Parameters:
|
263
|
+
#
|
264
|
+
# off = specify an optional beggining offset
|
265
|
+
#
|
266
|
+
def cstring(off=0)
|
267
|
+
self[ off, (self.index("\x00") || self.size) ]
|
268
|
+
end
|
269
|
+
|
270
|
+
# returns CRC32 checksum for the string object
|
271
|
+
def crc32
|
272
|
+
## pure ruby version. slower, but here for reference (found on some forum)
|
273
|
+
# r = 0xFFFFFFFF
|
274
|
+
# self.each_byte do |b|
|
275
|
+
# r ^= b
|
276
|
+
# 8.times do
|
277
|
+
# r = (r>>1) ^ (0xEDB88320 * (r & 1))
|
278
|
+
# end
|
279
|
+
# end
|
280
|
+
# r ^ 0xFFFFFFFF
|
281
|
+
## or... we can just use:
|
282
|
+
Zlib.crc32 self
|
283
|
+
end
|
284
|
+
|
285
|
+
# @return [Digest::MD5] the MD5 digest/checksum for this string.
|
286
|
+
def md5
|
287
|
+
d=Digest::MD5.new()
|
288
|
+
d.update(self)
|
289
|
+
d
|
290
|
+
end
|
291
|
+
alias md5sum md5
|
292
|
+
|
293
|
+
# @return [Digest::SHA1] the SHA1 digest for this string.
|
294
|
+
def sha1
|
295
|
+
d=Digest::SHA1.new()
|
296
|
+
d.update(self)
|
297
|
+
d
|
298
|
+
end
|
299
|
+
|
300
|
+
# @return [Digest::SHA2] the SHA2 digest for this string.
|
301
|
+
def sha2
|
302
|
+
d=Digest::SHA2.new()
|
303
|
+
d.update(self)
|
304
|
+
d
|
305
|
+
end
|
306
|
+
alias sha256 sha2
|
307
|
+
|
308
|
+
# This attempts to identify a blob of data using 'file(1)' via popen3
|
309
|
+
# (using popen3 because IO.popen blows)
|
310
|
+
# Tried doing this with a fmagic ruby extention to libmagic, but it was
|
311
|
+
# a whole lot slower.
|
312
|
+
def pipe_magick(arg="")
|
313
|
+
ret=""
|
314
|
+
Open3.popen3("file #{arg} -") do |w,r,e|
|
315
|
+
w.write self; w.close
|
316
|
+
ret = r.read ; r.close
|
317
|
+
ret.sub!(/^\/dev\/stdin: /, "")
|
318
|
+
end
|
319
|
+
ret
|
320
|
+
end
|
321
|
+
|
322
|
+
# Converts a '_' delimited string to CamelCase like 'foo_class' into
|
323
|
+
# 'FooClass'.
|
324
|
+
# See also: camelize_meth, decamelize
|
325
|
+
def camelize
|
326
|
+
self.gsub(/(^|_)([a-z])/) { $2.upcase }
|
327
|
+
end
|
328
|
+
|
329
|
+
# Converts a '_' delimited string to method style camelCase like 'foo_method'
|
330
|
+
# into 'fooMethod'.
|
331
|
+
# See also: camelize, decamelize
|
332
|
+
def camelize_meth
|
333
|
+
self.gsub(/_([a-z])/) { $1.upcase }
|
334
|
+
end
|
335
|
+
|
336
|
+
|
337
|
+
# Converts a CamelCase or camelCase string into '_' delimited form like
|
338
|
+
# 'FooBar' or 'fooBar' into 'foo_bar'.
|
339
|
+
#
|
340
|
+
# Note: This method only handles camel humps. Strings with consecutive
|
341
|
+
# uppercase chars like 'FooBAR' will be converted to 'foo_bar'
|
342
|
+
#
|
343
|
+
# See also: camelize, camelize_meth
|
344
|
+
def decamelize
|
345
|
+
self.gsub(/(^|[a-z])([A-Z])/) do
|
346
|
+
($1.empty?)? $2 : "#{$1}_#{$2}"
|
347
|
+
end.downcase
|
348
|
+
end
|
349
|
+
|
350
|
+
# convert a string to its idiomatic ruby class name
|
351
|
+
def class_name
|
352
|
+
r = ""
|
353
|
+
up = true
|
354
|
+
each_byte do |c|
|
355
|
+
if c == 95
|
356
|
+
if up
|
357
|
+
r << "::"
|
358
|
+
else
|
359
|
+
up = true
|
360
|
+
end
|
361
|
+
else
|
362
|
+
m = up ? :upcase : :to_s
|
363
|
+
r << (c.chr.send(m))
|
364
|
+
up = false
|
365
|
+
end
|
366
|
+
end
|
367
|
+
r
|
368
|
+
end
|
369
|
+
|
370
|
+
# Returns a reference to actual constant for a given name in namespace
|
371
|
+
# can be used to lookup classes from enums and such
|
372
|
+
def const_lookup(ns=Object)
|
373
|
+
if c=ns.constants.select {|n| n == self.class_name } and not c.empty?
|
374
|
+
ns.const_get(c.first)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
# Return a self encapsulated in a StringIO object. This is handy.
|
379
|
+
def to_stringio
|
380
|
+
StringIO.new(self)
|
381
|
+
end
|
382
|
+
|
383
|
+
# Returns or prints a hexdump in the style of 'hexdump -C'
|
384
|
+
#
|
385
|
+
# :len => optionally specify a length other than 16 for a wider or thinner
|
386
|
+
# dump. If length is an odd number, it will be rounded up.
|
387
|
+
#
|
388
|
+
# :out => optionally specify an alternate IO object for output. By default,
|
389
|
+
# hexdump will output to STDOUT. Pass a StringIO object and it will return
|
390
|
+
# it as a string.
|
391
|
+
#
|
392
|
+
# Example:
|
393
|
+
#
|
394
|
+
# Here's the default behavior done explicitely:
|
395
|
+
#
|
396
|
+
# >> xxd = dat.hexdump(:len => 16, :out => StringIO.new)
|
397
|
+
# => <a string containing hexdump>
|
398
|
+
#
|
399
|
+
# Here's how to change it to STDERR
|
400
|
+
#
|
401
|
+
# >> xxd = dat.hexdump(:len => 16, :out => STDERR)
|
402
|
+
# <prints hexdump on STDERR>
|
403
|
+
# -> nil # return value is nil!
|
404
|
+
#
|
405
|
+
def hexdump(opt={})
|
406
|
+
s=self
|
407
|
+
out = opt[:out] || StringIO.new
|
408
|
+
len = (opt[:len] and opt[:len] > 0)? opt[:len] + (opt[:len] % 2) : 16
|
409
|
+
|
410
|
+
off = opt[:start_addr] || 0
|
411
|
+
offlen = opt[:start_len] || 8
|
412
|
+
|
413
|
+
hlen=len/2
|
414
|
+
|
415
|
+
s.bytes.each_slice(len) do |m|
|
416
|
+
out.write(off.to_s(16).rjust(offlen, "0") + ' ')
|
417
|
+
|
418
|
+
i=0
|
419
|
+
m.each do |c|
|
420
|
+
out.write "%0.2x " % c
|
421
|
+
out.write(' ') if (i+=1) == hlen
|
422
|
+
end
|
423
|
+
|
424
|
+
out.write(" " * (len-i) ) # pad
|
425
|
+
out.write(" ") if i < hlen
|
426
|
+
|
427
|
+
out.write(" |")
|
428
|
+
m.each do |c|
|
429
|
+
if c > 0x19 and c < 0x7f
|
430
|
+
out.write(c.chr)
|
431
|
+
else
|
432
|
+
out.write('.')
|
433
|
+
end
|
434
|
+
end
|
435
|
+
out.write("|\n")
|
436
|
+
off += m.length
|
437
|
+
end
|
438
|
+
|
439
|
+
out.write(off.to_s(16).rjust(offlen,'0') + "\n")
|
440
|
+
|
441
|
+
if out.class == StringIO
|
442
|
+
out.string
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
|
447
|
+
# Converts a hexdump back to binary - takes the same options as hexdump().
|
448
|
+
# Fairly flexible. Should work both with 'xxd' and 'hexdump -C' style dumps.
|
449
|
+
def dehexdump(opt={})
|
450
|
+
s=self
|
451
|
+
out = opt[:out] || StringIO.new
|
452
|
+
len = (opt[:len] and opt[:len] > 0)? opt[:len] : 16
|
453
|
+
|
454
|
+
hcrx = /[A-Fa-f0-9]/
|
455
|
+
dumprx = /^(#{hcrx}+):?\s*((?:#{hcrx}{2}\s*){0,#{len}})/
|
456
|
+
off = opt[:start_addr] || 0
|
457
|
+
|
458
|
+
i=1
|
459
|
+
# iterate each line of hexdump
|
460
|
+
s.split(/\r?\n/).each do |hl|
|
461
|
+
# match and check offset
|
462
|
+
if dumprx.match(hl) and $1.hex == off
|
463
|
+
i+=1
|
464
|
+
# take the data chunk and unhexify it
|
465
|
+
raw = $2.unhexify
|
466
|
+
off += out.write(raw)
|
467
|
+
else
|
468
|
+
raise "Hexdump parse error on line #{i} #{s}"
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
if out.class == StringIO
|
473
|
+
out.string
|
474
|
+
end
|
475
|
+
end
|
476
|
+
alias dedump dehexdump
|
477
|
+
alias undump dehexdump
|
478
|
+
alias unhexdump dehexdump
|
479
|
+
|
480
|
+
|
481
|
+
# Binary grep
|
482
|
+
#
|
483
|
+
# Parameters:
|
484
|
+
#
|
485
|
+
# find : A Regexp or string to search for in self
|
486
|
+
# align : nil | numeric alignment (matches only made if aligned)
|
487
|
+
def bgrep(find, align=nil)
|
488
|
+
if align and (not align.is_a?(Integer) or align < 0)
|
489
|
+
raise "alignment must be a integer >= 0"
|
490
|
+
end
|
491
|
+
|
492
|
+
dat=self
|
493
|
+
if find.kind_of? Regexp
|
494
|
+
search = lambda do |mf, buf|
|
495
|
+
if m = mf.match(buf)
|
496
|
+
mtch = m[0]
|
497
|
+
off,endoff = m.offset(0)
|
498
|
+
return off, endoff, mtch
|
499
|
+
end
|
500
|
+
end
|
501
|
+
else
|
502
|
+
search = lambda do |s, buf|
|
503
|
+
if off = buf.index(s)
|
504
|
+
return off, off+s.size, s
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
ret=[]
|
510
|
+
pos = 0
|
511
|
+
while (res = search.call(find, dat[pos..-1].force_to_binary))
|
512
|
+
off, endoff, match = res
|
513
|
+
if align and ( pad = (pos+off).pad(align) ) != 0
|
514
|
+
pos += pad
|
515
|
+
else
|
516
|
+
hit = [pos+off, pos+endoff, match]
|
517
|
+
if not block_given? or yield([pos+off, pos+endoff, match])
|
518
|
+
ret << hit
|
519
|
+
end
|
520
|
+
pos += endoff
|
521
|
+
end
|
522
|
+
end
|
523
|
+
return ret
|
524
|
+
end
|
525
|
+
|
526
|
+
# A 'strings' method a-la unix strings utility. Finds printable strings in
|
527
|
+
# a binary blob.
|
528
|
+
# Supports ASCII and little endian unicode (though only for ASCII printable
|
529
|
+
# character.)
|
530
|
+
#
|
531
|
+
# === Parameters and options:
|
532
|
+
#
|
533
|
+
# * Use the :minimum parameter to specify minimum number of characters
|
534
|
+
# to match. (default = 6)
|
535
|
+
#
|
536
|
+
# * Use the :encoding parameter as one of :ascii, :unicode, or :both
|
537
|
+
# (default = :ascii)
|
538
|
+
#
|
539
|
+
# * The 'strings' method uses Regexp under the hood. Therefore
|
540
|
+
# you can pass a character class for "valid characters" with :valid
|
541
|
+
# (default = /[\r\n [:print:]]/)
|
542
|
+
#
|
543
|
+
# * Supports an optional block, which will be passed |offset, type, string|
|
544
|
+
# for each match.
|
545
|
+
# The block's boolean return value also determines whether the match
|
546
|
+
# passes or fails (true or false/nil) and gets returned by the function.
|
547
|
+
#
|
548
|
+
# === Return Value:
|
549
|
+
#
|
550
|
+
# Returns an array consisting of matches with the following elements:
|
551
|
+
#
|
552
|
+
# [[start_offset, end_offset, string_type, string], ...]
|
553
|
+
#
|
554
|
+
# * string_type will be one of :ascii or :unicode
|
555
|
+
# * end_offset will include the terminating null character
|
556
|
+
# * end_offset will include all null bytes in unicode strings (including
|
557
|
+
# * both terminating nulls)
|
558
|
+
#
|
559
|
+
# If strings are null terminated, the trailing null *IS* included
|
560
|
+
# in the end_offset. Unicode matches will also include null bytes.
|
561
|
+
#
|
562
|
+
# Todos?
|
563
|
+
# - better unicode support (i.e. not using half-assed unicode)
|
564
|
+
# - support other encodings such as all those the binutils strings does?
|
565
|
+
def strings(opts={})
|
566
|
+
opts[:encoding] ||= :both
|
567
|
+
min = (opts[:minimum] || 6)
|
568
|
+
|
569
|
+
raise "Minimum must be numeric and > 0" unless min.kind_of? Numeric and min > 0
|
570
|
+
|
571
|
+
acc = /[\s[:print:]]/
|
572
|
+
ucc = /(?:#{acc}\x00)/
|
573
|
+
|
574
|
+
arx = /(#{acc}{#{min}}#{acc}*\x00?)/
|
575
|
+
urx = /(#{ucc}{#{min}}#{ucc}*(?:\x00\x00)?)/
|
576
|
+
|
577
|
+
rx = case (opts[:encoding] || :both).to_sym
|
578
|
+
when :ascii
|
579
|
+
mtype_blk = lambda {|x| :ascii }
|
580
|
+
arx
|
581
|
+
when :unicode
|
582
|
+
mtype_blk = lambda {|x| :unicode }
|
583
|
+
urx
|
584
|
+
when :both
|
585
|
+
mtype_blk = lambda {|x| (x[2].nil?)? :ascii : :unicode }
|
586
|
+
|
587
|
+
Regexp.union( arx, urx )
|
588
|
+
else
|
589
|
+
raise "Encoding must be :unicode, :ascii, or :both"
|
590
|
+
end
|
591
|
+
|
592
|
+
ret = []
|
593
|
+
|
594
|
+
# wow ruby 1.9 string encoding is a total cluster
|
595
|
+
self.force_to_binary.scan(rx) do
|
596
|
+
mtch = $~
|
597
|
+
|
598
|
+
stype = mtype_blk.call(mtch)
|
599
|
+
|
600
|
+
startoff, endoff = mtch.offset(0)
|
601
|
+
mret = [startoff, endoff, stype, mtch[0] ]
|
602
|
+
|
603
|
+
# yield to a block for additional criteria
|
604
|
+
next if block_given? and not yield( *mret )
|
605
|
+
|
606
|
+
ret << mret
|
607
|
+
end
|
608
|
+
|
609
|
+
return ret
|
610
|
+
end
|
611
|
+
|
612
|
+
|
613
|
+
end
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
class RbkbString < String
|
618
|
+
include Rbkb::Extends::String
|
619
|
+
end
|
620
|
+
|
621
|
+
def RbkbString(x)
|
622
|
+
RbkbString.new(x)
|
623
|
+
end
|
624
|
+
|