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,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
|
+
|