zsteg 0.0.0 → 0.0.1
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/Gemfile +2 -7
- data/Gemfile.lock +2 -4
- data/README.md +72 -1
- data/README.md.tpl +23 -0
- data/Rakefile +5 -3
- data/TODO +5 -2
- data/VERSION +1 -1
- data/bin/zsteg-mask +7 -0
- data/lib/zsteg/checker/wbstego.rb +69 -14
- data/lib/zsteg/checker.rb +137 -34
- data/lib/zsteg/cli.rb +92 -35
- data/lib/zsteg/extractor/byte_extractor.rb +36 -21
- data/lib/zsteg/extractor/color_extractor.rb +68 -34
- data/lib/zsteg/extractor.rb +36 -1
- data/lib/zsteg/file_cmd.rb +64 -1
- data/lib/zsteg/mask_cli.rb +268 -0
- data/lib/zsteg/masker.rb +52 -0
- data/lib/zsteg/result.rb +27 -32
- data/lib/zsteg.rb +2 -0
- data/samples/hackquest/crypt.bmp +0 -0
- data/samples/hackquest/square.bmp +0 -0
- data/samples/wbstego/wbsteg_blowfish_pass_1.bmp +0 -0
- data/samples/wbstego/wbsteg_cast128_pass_1.bmp +0 -0
- data/samples/wbstego/wbsteg_enc_pass_pass.bmp +0 -0
- data/samples/wbstego/wbsteg_enc_pass_pass_even.bmp +0 -0
- data/samples/wbstego/wbsteg_mix_pass_1.bmp +0 -0
- data/samples/wbstego/wbsteg_mix_pass_1_even.bmp +0 -0
- data/samples/wbstego/wbsteg_mix_pass_foobar.bmp +0 -0
- data/samples/wbstego/wbsteg_mix_pass_pass.bmp +0 -0
- data/samples/wbstego/wbsteg_mixenc_pass_pass_even.bmp +0 -0
- data/samples/{wbsteg_noenc.bmp → wbstego/wbsteg_noenc.bmp} +0 -0
- data/samples/wbstego/wbsteg_noenc.png +0 -0
- data/samples/wbstego/wbsteg_noenc_.bmp +0 -0
- data/samples/{wbsteg_noenc_17.bmp → wbstego/wbsteg_noenc_17.bmp} +0 -0
- data/samples/wbstego/wbsteg_noenc__.bmp +0 -0
- data/samples/{wbsteg_noenc_even.bmp → wbstego/wbsteg_noenc_even.bmp} +0 -0
- data/samples/wbstego/wbsteg_noenc_even2.bmp +0 -0
- data/samples/{wbsteg_noenc_even_17.bmp → wbstego/wbsteg_noenc_even_17.bmp} +0 -0
- data/samples/wbstego/wbsteg_noenc_even_17_.bmp +0 -0
- data/samples/wbstego/wbsteg_noenc_ext_ABC.bmp +0 -0
- data/samples/wbstego/wbsteg_rijndael_pass_1.bmp +0 -0
- data/samples/wbstego/wbsteg_rijndael_pass_pass.bmp +0 -0
- data/samples/wbstego/wbsteg_rijndael_pass_pass_even.bmp +0 -0
- data/samples/wbstego/wbsteg_twofish_pass_1.bmp +0 -0
- data/samples/wechall/5ZMGcCLxpcpsru03.g00000010.png +0 -0
- data/samples/wechall/5ZMGcCLxpcpsru03.png +0 -0
- data/samples/wechall/stegano1.bmp +0 -0
- data/spec/checker_spec.rb +47 -0
- data/spec/easybmp_spec.rb +9 -0
- data/spec/hackquest_spec.rb +18 -0
- data/spec/mask_spec.rb +14 -0
- data/spec/polictf2012_spec.rb +48 -0
- data/spec/prime_spec.rb +9 -0
- data/spec/r3g2b3_spec.rb +9 -0
- data/spec/spec_helper.rb +21 -4
- data/spec/wbstego_spec.rb +21 -3
- data/spec/wechall_spec.rb +26 -0
- data/tmp/.keep +0 -0
- data/zsteg.gemspec +121 -0
- metadata +47 -43
- data/samples/06_enc.png +0 -0
- data/samples/Code.png +0 -0
- data/samples/README +0 -4
- data/samples/camouflage-password.png +0 -0
- data/samples/camouflage.png +0 -0
- data/samples/cats.png +0 -0
- data/samples/flower.png +0 -0
- data/samples/flower_rgb1.png +0 -0
- data/samples/flower_rgb2.png +0 -0
- data/samples/flower_rgb3.png +0 -0
- data/samples/flower_rgb4.png +0 -0
- data/samples/flower_rgb5.png +0 -0
- data/samples/flower_rgb6.png +0 -0
- data/samples/montenach-enc.png +0 -0
- data/samples/ndh2k12_sp113.bmp.7z +0 -0
- data/samples/openstego_q2.png +0 -0
- data/samples/openstego_send.png +0 -0
- data/samples/stg300.png +0 -0
data/lib/zsteg/cli.rb
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|
require 'optparse'
|
2
|
-
require 'awesome_print'
|
3
2
|
|
4
3
|
module ZSteg
|
5
4
|
class CLI
|
6
5
|
DEFAULT_ACTIONS = %w'check'
|
7
|
-
DEFAULT_LIMIT = 256
|
8
|
-
DEFAULT_ORDER = 'auto'
|
9
6
|
|
10
7
|
def initialize argv = ARGV
|
11
8
|
@argv = argv
|
@@ -15,30 +12,35 @@ module ZSteg
|
|
15
12
|
@actions = []
|
16
13
|
@options = {
|
17
14
|
:verbose => 0,
|
18
|
-
:limit => DEFAULT_LIMIT,
|
19
|
-
:
|
20
|
-
:order => DEFAULT_ORDER
|
15
|
+
:limit => Checker::DEFAULT_LIMIT,
|
16
|
+
:order => Checker::DEFAULT_ORDER
|
21
17
|
}
|
22
18
|
optparser = OptionParser.new do |opts|
|
23
|
-
opts.banner = "Usage: zsteg [options] filename.png"
|
19
|
+
opts.banner = "Usage: zsteg [options] filename.png [param_string]"
|
24
20
|
opts.separator ""
|
25
21
|
|
26
|
-
opts.on("-c", "--channels X", /[rgba,]+/,
|
22
|
+
opts.on("-c", "--channels X", /[rgba,1-8]+/,
|
27
23
|
"channels (R/G/B/A) or any combination, comma separated",
|
28
|
-
"valid values: r,g,b,a,rg,
|
24
|
+
"valid values: r,g,b,a,rg,bgr,rgba,r3g2b3,..."
|
29
25
|
){ |x| @options[:channels] = x.split(',') }
|
30
26
|
|
31
27
|
opts.on("-l", "--limit N", Integer,
|
32
|
-
"limit bytes checked, 0 = no limit (default: #{
|
28
|
+
"limit bytes checked, 0 = no limit (default: #{@options[:limit]})"
|
33
29
|
){ |n| @options[:limit] = n }
|
34
30
|
|
35
|
-
opts.on("-b", "--bits N",
|
36
|
-
"
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
31
|
+
opts.on("-b", "--bits N", "number of bits, single int value or '1,3,5' or range '1-8'",
|
32
|
+
"advanced: specify individual bits like '00001110' or '0x88'"
|
33
|
+
) do |x|
|
34
|
+
a = []
|
35
|
+
x.split(',').each do |x1|
|
36
|
+
if x1['-']
|
37
|
+
t = x1.split('-')
|
38
|
+
a << Range.new(parse_bits(t[0]), parse_bits(t[1])).to_a
|
39
|
+
else
|
40
|
+
a << parse_bits(x1)
|
41
|
+
end
|
41
42
|
end
|
43
|
+
@options[:bits] = a.flatten.uniq
|
42
44
|
end
|
43
45
|
|
44
46
|
opts.on "--lsb", "least significant BIT comes first" do
|
@@ -48,8 +50,20 @@ module ZSteg
|
|
48
50
|
@options[:bit_order] = :msb
|
49
51
|
end
|
50
52
|
|
53
|
+
opts.on "-P", "--prime", "analyze/extract only prime bytes/pixels" do
|
54
|
+
@options[:prime] = true
|
55
|
+
end
|
56
|
+
# opts.on "--pixel-align", "pixel-align hidden data (EasyBMP)" do
|
57
|
+
# @options[:pixel_align] = true
|
58
|
+
# end
|
59
|
+
|
60
|
+
opts.on "-a", "--all", "try all known methods" do
|
61
|
+
@options[:prime] = :all
|
62
|
+
@options[:order] = :all
|
63
|
+
end
|
64
|
+
|
51
65
|
opts.on("-o", "--order X", /all|auto|[bxy,]+/i,
|
52
|
-
"pixel iteration order (default: '#{
|
66
|
+
"pixel iteration order (default: '#{@options[:order]}')",
|
53
67
|
"valid values: ALL,xy,yx,XY,YX,xY,Xy,bY,...",
|
54
68
|
){ |x| @options[:order] = x.split(',') }
|
55
69
|
|
@@ -64,6 +78,11 @@ module ZSteg
|
|
64
78
|
opts.on "-q", "--quiet", "Silent any warnings (can be used multiple times)" do |v|
|
65
79
|
@options[:verbose] -= 1
|
66
80
|
end
|
81
|
+
opts.on "-C", "--[no-]color", "Force (or disable) color output (default: auto)" do |x|
|
82
|
+
Sickill::Rainbow.enabled = x
|
83
|
+
end
|
84
|
+
opts.separator "\nPARAMS SHORTCUT\n"+
|
85
|
+
"\tzsteg fname.png 2b,b,lsb,xy ==> --bits 2 --channel b --lsb --order xy"
|
67
86
|
end
|
68
87
|
|
69
88
|
if (argv = optparser.parse(@argv)).empty?
|
@@ -73,12 +92,19 @@ module ZSteg
|
|
73
92
|
|
74
93
|
@actions = DEFAULT_ACTIONS if @actions.empty?
|
75
94
|
|
95
|
+
argv.each do |arg|
|
96
|
+
if arg[','] && !File.exist?(arg)
|
97
|
+
@options.merge!(decode_param_string(arg))
|
98
|
+
argv.delete arg
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
76
102
|
argv.each_with_index do |fname,idx|
|
77
103
|
if argv.size > 1 && @options[:verbose] >= 0
|
78
104
|
puts if idx > 0
|
79
105
|
puts "[.] #{fname}".green
|
80
106
|
end
|
81
|
-
@fname
|
107
|
+
next unless @img=load_image(@fname=fname)
|
82
108
|
|
83
109
|
@actions.each do |action|
|
84
110
|
if action.is_a?(Array)
|
@@ -93,39 +119,70 @@ module ZSteg
|
|
93
119
|
# prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message
|
94
120
|
end
|
95
121
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
122
|
+
def load_image fname
|
123
|
+
if File.directory?(fname)
|
124
|
+
puts "[?] #{fname} is a directory".yellow
|
125
|
+
else
|
126
|
+
ZPNG::Image.load(fname)
|
127
|
+
end
|
128
|
+
rescue ZPNG::Exception, Errno::ENOENT
|
129
|
+
puts "[!] #{$!.inspect}".red
|
101
130
|
end
|
102
131
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
132
|
+
def parse_bits x
|
133
|
+
case x
|
134
|
+
when '1', 1 # catch NOT A BINARY MASK early
|
135
|
+
1
|
136
|
+
when /^0x[0-9a-f]+$/i # hex, mask
|
137
|
+
0x100 + x.to_i(16)
|
138
|
+
when /^(?:0b)?[01]+$/i # binary, mask
|
139
|
+
0x100 + x.to_i(2)
|
140
|
+
when /^\d+$/ # decimal, number of bits
|
141
|
+
x.to_i
|
142
|
+
else
|
143
|
+
raise "invalid bits value: #{x.inspect}"
|
108
144
|
end
|
145
|
+
end
|
109
146
|
|
147
|
+
def decode_param_string s
|
110
148
|
h = {}
|
111
|
-
|
149
|
+
s.split(',').each do |x|
|
112
150
|
case x
|
113
151
|
when 'lsb'
|
114
152
|
h[:bit_order] = :lsb
|
115
153
|
when 'msb'
|
116
154
|
h[:bit_order] = :msb
|
117
|
-
when
|
118
|
-
h[:bits] = $1
|
155
|
+
when /^(\d)b$/, /^b(\d)$/
|
156
|
+
h[:bits] = parse_bits($1)
|
119
157
|
when /\A[rgba]+\Z/
|
120
|
-
h[:channels] = x
|
121
|
-
when /\Axy|yx\Z/i
|
158
|
+
h[:channels] = [x]
|
159
|
+
when /\Axy|yx|yb|by\Z/i
|
122
160
|
h[:order] = x
|
161
|
+
when 'prime'
|
162
|
+
h[:prime] = true
|
123
163
|
else
|
124
164
|
raise "uknown param #{x.inspect}"
|
125
165
|
end
|
126
166
|
end
|
127
|
-
h
|
128
|
-
|
167
|
+
h
|
168
|
+
end
|
169
|
+
|
170
|
+
###########################################################################
|
171
|
+
# actions
|
172
|
+
|
173
|
+
def check
|
174
|
+
Checker.new(@img, @options).check
|
175
|
+
end
|
176
|
+
|
177
|
+
def extract name
|
178
|
+
if ['extradata', 'data after IEND'].include?(name)
|
179
|
+
print @img.extradata
|
180
|
+
return
|
181
|
+
end
|
182
|
+
|
183
|
+
h = decode_param_string name
|
184
|
+
h[:limit] = @options[:limit] if @options[:limit] != Checker::DEFAULT_LIMIT
|
185
|
+
print Extractor.new(@img, @options).extract(h)
|
129
186
|
end
|
130
187
|
|
131
188
|
end
|
@@ -5,22 +5,23 @@ module ZSteg
|
|
5
5
|
module ByteExtractor
|
6
6
|
|
7
7
|
def byte_extract params = {}
|
8
|
-
|
9
|
-
limit = 2**32 if limit <= 0
|
10
|
-
|
11
|
-
bits = params[:bits]
|
12
|
-
raise "invalid bits value #{bits.inspect}" unless (1..8).include?(bits)
|
13
|
-
mask = 2**bits - 1
|
8
|
+
masks = bits2masks params[:bits]
|
14
9
|
|
10
|
+
if params[:prime]
|
11
|
+
pregenerate_primes(
|
12
|
+
:max => @image.scanlines[0].size * @image.height,
|
13
|
+
:count => (@limit*8.0/masks.size).ceil
|
14
|
+
)
|
15
|
+
end
|
15
16
|
|
16
17
|
data = ''.force_encoding('binary')
|
17
18
|
a = []
|
18
|
-
byte_iterator(params
|
19
|
+
byte_iterator(params) do |x,y|
|
19
20
|
sl = @image.scanlines[y]
|
20
21
|
|
21
|
-
value = sl.decoded_bytes
|
22
|
-
|
23
|
-
a << ((value &
|
22
|
+
value = sl.decoded_bytes.getbyte(x)
|
23
|
+
masks.each do |mask|
|
24
|
+
a << ((value & mask) == 0 ? 0 : 1)
|
24
25
|
end
|
25
26
|
|
26
27
|
if a.size >= 8
|
@@ -32,8 +33,8 @@ module ZSteg
|
|
32
33
|
end
|
33
34
|
#printf "[d] %02x %08b\n", byte, byte
|
34
35
|
data << byte.chr
|
35
|
-
if data.size >= limit
|
36
|
-
print "[limit
|
36
|
+
if data.size >= @limit
|
37
|
+
print "[limit #@limit]".gray if @verbose > 1
|
37
38
|
break
|
38
39
|
end
|
39
40
|
end
|
@@ -51,7 +52,8 @@ module ZSteg
|
|
51
52
|
# ...
|
52
53
|
# 'xY': b=0, y=MAX; b=1, y=MAX; b=2, y=MAX; ...
|
53
54
|
# 'XY': b=MAX,y=MAX; b=MAX-1,y=MAX; b=MAX-2,y=MAX; ...
|
54
|
-
def byte_iterator
|
55
|
+
def byte_iterator params
|
56
|
+
type = params[:order]
|
55
57
|
if type.nil? || type == 'auto'
|
56
58
|
type = @image.format == :bmp ? 'bY' : 'by'
|
57
59
|
end
|
@@ -59,6 +61,7 @@ module ZSteg
|
|
59
61
|
|
60
62
|
sl0 = @image.scanlines.first
|
61
63
|
|
64
|
+
# XXX don't try to run it on interlaced PNGs!
|
62
65
|
x0,x1,xstep =
|
63
66
|
if type.index('b')
|
64
67
|
[0, sl0.decoded_bytes.size-1, 1]
|
@@ -73,19 +76,31 @@ module ZSteg
|
|
73
76
|
[@image.height-1, 0, -1]
|
74
77
|
end
|
75
78
|
|
79
|
+
# cannot join these lines from ByteExtractor and ColorExtractor into
|
80
|
+
# one method for performance reason:
|
81
|
+
# it will require additional yield() for EACH BYTE iterated
|
82
|
+
|
76
83
|
if type[0,1].downcase == 'b'
|
77
84
|
# ROW iterator
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
85
|
+
if params[:prime]
|
86
|
+
idx = 0
|
87
|
+
y0.step(y1,ystep){ |y| x0.step(x1,xstep){ |x|
|
88
|
+
yield(x,y) if @primes.include?(idx)
|
89
|
+
idx += 1
|
90
|
+
}}
|
91
|
+
else
|
92
|
+
y0.step(y1,ystep){ |y| x0.step(x1,xstep){ |x| yield(x,y) }}
|
82
93
|
end
|
83
94
|
else
|
84
95
|
# COLUMN iterator
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
96
|
+
if params[:prime]
|
97
|
+
idx = 0
|
98
|
+
x0.step(x1,xstep){ |x| y0.step(y1,ystep){ |y|
|
99
|
+
yield(x,y) if @primes.include?(idx)
|
100
|
+
idx += 1
|
101
|
+
}}
|
102
|
+
else
|
103
|
+
x0.step(x1,xstep){ |x| y0.step(y1,ystep){ |y| yield(x,y) }}
|
89
104
|
end
|
90
105
|
end
|
91
106
|
end
|
@@ -4,40 +4,61 @@ module ZSteg
|
|
4
4
|
module ColorExtractor
|
5
5
|
|
6
6
|
def color_extract params = {}
|
7
|
-
channels =
|
7
|
+
channels = Array(params[:channels])
|
8
|
+
#pixel_align = params[:pixel_align]
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
ch_masks = []
|
11
|
+
case channels.first.size
|
12
|
+
when 1
|
13
|
+
# ['r', 'g', 'b']
|
14
|
+
channels.each{ |c| ch_masks << [c[0], bits2masks(params[:bits])] }
|
15
|
+
when 2
|
16
|
+
# ['r3', 'g2', 'b3']
|
17
|
+
channels.each{ |c| ch_masks << [c[0], bits2masks(c[1].to_i)] }
|
18
|
+
else
|
19
|
+
raise "invalid channels: #{channels.inspect}"
|
20
|
+
end
|
11
21
|
|
12
|
-
bits =
|
13
|
-
|
14
|
-
mask = 2**bits - 1
|
22
|
+
# total number of bits = sum of all channels bits
|
23
|
+
nbits = ch_masks.map{ |x| x[1].size }.inject(&:+)
|
15
24
|
|
25
|
+
if params[:prime]
|
26
|
+
pregenerate_primes(
|
27
|
+
:max => @image.width * @image.height,
|
28
|
+
:count => (@limit*8.0/nbits/channels.size).ceil
|
29
|
+
)
|
30
|
+
end
|
16
31
|
|
17
32
|
data = ''.force_encoding('binary')
|
18
33
|
a = []
|
19
|
-
|
20
|
-
|
34
|
+
#puts
|
35
|
+
catch :limit do
|
36
|
+
coord_iterator(params) do |x,y|
|
37
|
+
color = @image[x,y]
|
21
38
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
39
|
+
ch_masks.each do |c,masks|
|
40
|
+
value = color.send(c)
|
41
|
+
masks.each do |mask|
|
42
|
+
a << ((value & mask) == 0 ? 0 : 1)
|
43
|
+
end
|
26
44
|
end
|
27
|
-
|
45
|
+
#p [x,y,a.size,a]
|
28
46
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
47
|
+
while a.size >= 8
|
48
|
+
byte = 0
|
49
|
+
#puts a.join
|
50
|
+
if params[:bit_order] == :msb
|
51
|
+
8.times{ |i| byte |= (a.shift<<i)}
|
52
|
+
else
|
53
|
+
8.times{ |i| byte |= (a.shift<<(7-i))}
|
54
|
+
end
|
55
|
+
#printf "[d] %02x %08b\n", byte, byte
|
56
|
+
data << byte.chr
|
57
|
+
if data.size >= @limit
|
58
|
+
print "[limit #@limit]".gray if @verbose > 1
|
59
|
+
throw :limit
|
60
|
+
end
|
61
|
+
#a.clear if pixel_align
|
41
62
|
end
|
42
63
|
end
|
43
64
|
end
|
@@ -54,7 +75,8 @@ module ZSteg
|
|
54
75
|
# ...
|
55
76
|
# 'xY': x=0, y=MAX; x=1, y=MAX; x=2, y=MAX; ...
|
56
77
|
# 'XY': x=MAX,y=MAX; x=MAX-1,y=MAX; x=MAX-2,y=MAX; ...
|
57
|
-
def coord_iterator
|
78
|
+
def coord_iterator params
|
79
|
+
type = params[:order]
|
58
80
|
if type.nil? || type == 'auto'
|
59
81
|
type = @image.format == :bmp ? 'xY' : 'xy'
|
60
82
|
end
|
@@ -74,19 +96,31 @@ module ZSteg
|
|
74
96
|
[@image.height-1, 0, -1]
|
75
97
|
end
|
76
98
|
|
99
|
+
# cannot join these lines from ByteExtractor and ColorExtractor into
|
100
|
+
# one method for performance reason:
|
101
|
+
# it will require additional yield() for EACH BYTE iterated
|
102
|
+
|
77
103
|
if type[0,1].downcase == 'x'
|
78
104
|
# ROW iterator
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
105
|
+
if params[:prime]
|
106
|
+
idx = 0
|
107
|
+
y0.step(y1,ystep){ |y| x0.step(x1,xstep){ |x|
|
108
|
+
yield(x,y) if @primes.include?(idx)
|
109
|
+
idx += 1
|
110
|
+
}}
|
111
|
+
else
|
112
|
+
y0.step(y1,ystep){ |y| x0.step(x1,xstep){ |x| yield(x,y) }}
|
83
113
|
end
|
84
114
|
else
|
85
115
|
# COLUMN iterator
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
116
|
+
if params[:prime]
|
117
|
+
idx = 0
|
118
|
+
x0.step(x1,xstep){ |x| y0.step(y1,ystep){ |y|
|
119
|
+
yield(x,y) if @primes.include?(idx)
|
120
|
+
idx += 1
|
121
|
+
}}
|
122
|
+
else
|
123
|
+
x0.step(x1,xstep){ |x| y0.step(y1,ystep){ |y| yield(x,y) }}
|
90
124
|
end
|
91
125
|
end
|
92
126
|
end
|
data/lib/zsteg/extractor.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'prime'
|
2
|
+
require 'set'
|
3
|
+
|
1
4
|
module ZSteg
|
2
5
|
class Extractor
|
3
6
|
|
@@ -7,15 +10,47 @@ module ZSteg
|
|
7
10
|
# image can be either filename or ZPNG::Image
|
8
11
|
def initialize image, params = {}
|
9
12
|
@image = image.is_a?(ZPNG::Image) ? image : ZPNG::Image.load(image)
|
10
|
-
@verbose = params[:verbose]
|
13
|
+
@verbose = params[:verbose] || 0
|
11
14
|
end
|
12
15
|
|
13
16
|
def extract params = {}
|
17
|
+
@limit = params[:limit].to_i
|
18
|
+
@limit = 2**32 if @limit <= 0
|
19
|
+
|
14
20
|
if params[:order] =~ /b/i
|
15
21
|
byte_extract params
|
16
22
|
else
|
17
23
|
color_extract params
|
18
24
|
end
|
19
25
|
end
|
26
|
+
|
27
|
+
def pregenerate_primes h
|
28
|
+
@primes ||= Set.new
|
29
|
+
return if @primes.size >= h[:count]
|
30
|
+
|
31
|
+
count = h[:count]
|
32
|
+
Prime.each(h[:max]) do |prime|
|
33
|
+
@primes << prime
|
34
|
+
break if @primes.size >= count
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def bits2masks bits
|
39
|
+
masks = []
|
40
|
+
if (1..8).include?(bits)
|
41
|
+
# number of bits
|
42
|
+
bits.times do |i|
|
43
|
+
masks << (1<<(bits-i-1))
|
44
|
+
end
|
45
|
+
else
|
46
|
+
# mask
|
47
|
+
bits &= 0xff
|
48
|
+
8.times do |i|
|
49
|
+
mask = (1<<(bits-i-1))
|
50
|
+
masks << mask if (bits & mask) != 0
|
51
|
+
end
|
52
|
+
end
|
53
|
+
masks
|
54
|
+
end
|
20
55
|
end
|
21
56
|
end
|