11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
# File 'lib/zsteg/cli/cli.rb', line 11
def run
@actions = []
@options = {
verbose: 0,
limit: Checker::DEFAULT_LIMIT,
order: Checker::DEFAULT_ORDER,
step: 1,
ystep: 1,
}
optparser = OptionParser.new do |opts|
opts.banner = "Usage: zsteg [options] filename.png [param_string]"
opts.separator ""
opts.on "-a", "--all", "try all known methods" do
@options[:prime] = :all
@options[:order] = :all
@options[:pixel_align] = :all
@options[:bits] = (1..8).to_a
@options[:extra_checks] = true
end
opts.on "-E", "--extract NAME", "extract specified payload, NAME is like '1b,rgb,lsb'" do |x|
@options[:verbose] = -2 @actions << [:extract, x]
end
opts.separator "\nIteration/extraction params:"
opts.on("-o", "--order X", /all|auto|[bxy,]+/i,
"pixel iteration order (default: '#{@options[:order]}')",
"valid values: ALL,xy,yx,XY,YX,xY,Xy,bY,...",
){ |x| @options[:order] = x.split(',') }
opts.on("-c", "--channels X", /[rgba,1-8]+/,
"channels (R/G/B/A) or any combination, comma separated",
"valid values: r,g,b,a,rg,bgr,rgba,r3g2b3,..."
) do |x|
@options[:channels] = x.split(',')
@options[:extra_checks] = false
end
opts.on("-b", "--bits N", "number of bits, single int value or '1,3,5' or range '1-8'",
"advanced: specify individual bits like '00001110' or '0x88'"
) do |x|
a = []
if x[-1] == 'p'
@options[:pixel_align] = true
x = x[0..-2]
end
x = '1-8' if x == 'all'
x.split(',').each do |x1|
if x1['-']
t = x1.split('-')
a << Range.new(parse_bits(t[0]), parse_bits(t[1])).to_a
else
a << parse_bits(x1)
end
end
@options[:bits] = a.flatten.uniq
@options[:extra_checks] = false
end
opts.on "--lsb", "least significant bit comes first" do
@options[:bit_order] = :lsb
end
opts.on "--msb", "most significant bit comes first" do
@options[:bit_order] = :msb
end
opts.on "-P", "--prime", "analyze/extract only prime bytes/pixels" do
@options[:prime] = true
@options[:extra_checks] = false
end
opts.on("--shift N", Integer, "prepend N zero bits"){ |x| @options[:shift] = x }
opts.on("--invert", "invert bits (XOR 0xff)") { @options[:invert] = true }
opts.on "--pixel-align", "pixel-align hidden data" do
@options[:pixel_align] = true
end
opts.separator "\nAnalysis params:"
opts.on("-l", "--limit N", Integer, "limit bytes checked, 0 = no limit (default: #{@options[:limit]})"){ |n| @options[:limit] = n }
opts.separator ""
opts.on "--[no-]file", "use 'file' command to detect data type (default: YES)" do |x|
@options[:file] = x
end
opts.on "--no-strings", "disable ASCII strings finding (default: enabled)" do |x|
@options[:strings] = false
end
opts.on "-s", "--strings X", %w'first all longest none no',
"ASCII strings find mode: first, all, longest, none",
"(default: first)" do |x|
@options[:strings] = x[0] == 'n' ? false : x.downcase.to_sym
end
opts.on "-n", "--min-str-len X", Integer,
"minimum string length (default: #{Checker::DEFAULT_MIN_STR_LEN})" do |x|
@options[:min_str_len] = x
end
opts.separator ""
opts.on "-v", "--verbose", "Run verbosely (can be used multiple times)" do |v|
@options[:verbose] += 1
end
opts.on "-q", "--quiet", "Silent any warnings (can be used multiple times)" do |v|
@options[:verbose] -= 1
end
opts.on "-C", "--[no-]color", "Force (or disable) color output (default: auto)" do |x|
if defined?(Rainbow) && Rainbow.respond_to?(:enabled=)
Rainbow.enabled = x
else
Sickill::Rainbow.enabled = x
end
end
opts.separator "\nPARAMS SHORTCUT\n"+
"\tzsteg fname.png 2b,b,lsb,xy ==> --bits 2 --channel b --lsb --order xy"
end
if (argv = optparser.parse(@argv)).empty?
puts optparser.help
return
end
@actions = DEFAULT_ACTIONS if @actions.empty?
argv.each do |arg|
if arg[','] && !File.exist?(arg)
@options.merge!(decode_param_string(arg))
argv.delete arg
end
end
argv.each_with_index do |fname,idx|
if argv.size > 1 && @options[:verbose] >= 0
puts if idx > 0
puts "[.] #{fname}".green
end
next unless @img=load_image(@fname=fname)
@actions.each do |action|
if action.is_a?(Array)
self.send(*action) if self.respond_to?(action.first)
else
self.send(action) if self.respond_to?(action)
end
end
end
rescue Errno::EPIPE
end
|