zsteg 0.2.12 → 0.2.13
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 +4 -4
- data/Gemfile +2 -2
- data/Gemfile.lock +26 -26
- data/README.md +14 -25
- data/VERSION +1 -1
- data/cmp_png.rb +1 -1
- data/lib/zsteg/checker/steganography_png.rb +36 -0
- data/lib/zsteg/checker.rb +65 -32
- data/lib/zsteg/cli/cli.rb +46 -33
- data/lib/zsteg/extractor/byte_extractor.rb +5 -2
- data/lib/zsteg/extractor/color_extractor.rb +5 -5
- data/lib/zsteg.rb +0 -4
- data/spec/checker_spec.rb +3 -3
- data/spec/spec_helper.rb +1 -0
- data/spec/steganography_png_spec.rb +13 -0
- data/zsteg.gemspec +9 -7
- metadata +8 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 05bf462c6bc2696efead731d0ce3bda8cf371435cc9448b100892b7084364c64
|
|
4
|
+
data.tar.gz: c12f610fe42706271d763620100a71572477696c07010d9323c1bb97f7440fbe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3340516e94d8248446ddb5df7a64269aed8fb4b09a1bf10057d603581b352e39adc595104ae4b75fa042d8895459532dac21c2304cc49bfe222ffc0df358fa55
|
|
7
|
+
data.tar.gz: d4bd9eaf13356a534eeb3be2807b524c73549dc0d3fbe44f349393930f0f4a31be1430b434d4ef8fb42ea2c2eda42dd13eb013222fc37714ce9801973358ba8e
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
GEM
|
|
2
2
|
remote: http://rubygems.org/
|
|
3
3
|
specs:
|
|
4
|
-
addressable (2.8.
|
|
5
|
-
public_suffix (>= 2.0.2, <
|
|
4
|
+
addressable (2.8.1)
|
|
5
|
+
public_suffix (>= 2.0.2, < 6.0)
|
|
6
6
|
builder (3.2.4)
|
|
7
7
|
descendants_tracker (0.0.4)
|
|
8
8
|
thread_safe (~> 0.3, >= 0.3.1)
|
|
9
9
|
diff-lcs (1.5.0)
|
|
10
|
-
faraday (1.10.
|
|
10
|
+
faraday (1.10.3)
|
|
11
11
|
faraday-em_http (~> 1.0)
|
|
12
12
|
faraday-em_synchrony (~> 1.0)
|
|
13
13
|
faraday-excon (~> 1.1)
|
|
@@ -31,7 +31,7 @@ GEM
|
|
|
31
31
|
faraday-rack (1.0.0)
|
|
32
32
|
faraday-retry (1.0.3)
|
|
33
33
|
forwardable (1.3.3)
|
|
34
|
-
git (1.13.
|
|
34
|
+
git (1.13.2)
|
|
35
35
|
addressable (~> 2.8)
|
|
36
36
|
rchardet (~> 1.8)
|
|
37
37
|
github_api (0.19.0)
|
|
@@ -41,8 +41,8 @@ GEM
|
|
|
41
41
|
hashie (~> 3.5, >= 3.5.2)
|
|
42
42
|
oauth2 (~> 1.0)
|
|
43
43
|
hashie (3.6.0)
|
|
44
|
-
highline (2.0
|
|
45
|
-
iostruct (0.0.
|
|
44
|
+
highline (2.1.0)
|
|
45
|
+
iostruct (0.0.5)
|
|
46
46
|
juwelier (2.4.9)
|
|
47
47
|
builder
|
|
48
48
|
bundler
|
|
@@ -55,13 +55,13 @@ GEM
|
|
|
55
55
|
rake
|
|
56
56
|
rdoc
|
|
57
57
|
semver2
|
|
58
|
-
jwt (2.
|
|
58
|
+
jwt (2.7.0)
|
|
59
59
|
kamelcase (0.0.2)
|
|
60
60
|
semver2 (~> 3)
|
|
61
61
|
mini_portile2 (2.8.1)
|
|
62
62
|
multi_json (1.15.0)
|
|
63
63
|
multi_xml (0.6.0)
|
|
64
|
-
multipart-post (2.
|
|
64
|
+
multipart-post (2.3.0)
|
|
65
65
|
nokogiri (1.14.2)
|
|
66
66
|
mini_portile2 (~> 2.8.0)
|
|
67
67
|
racc (~> 1.4)
|
|
@@ -74,46 +74,46 @@ GEM
|
|
|
74
74
|
prime (0.1.2)
|
|
75
75
|
forwardable
|
|
76
76
|
singleton
|
|
77
|
-
psych (
|
|
77
|
+
psych (5.1.0)
|
|
78
78
|
stringio
|
|
79
|
-
public_suffix (
|
|
79
|
+
public_suffix (5.0.1)
|
|
80
80
|
racc (1.6.2)
|
|
81
81
|
rack (3.0.4.1)
|
|
82
82
|
rainbow (3.1.1)
|
|
83
83
|
rake (13.0.6)
|
|
84
84
|
rchardet (1.8.0)
|
|
85
|
-
rdoc (6.
|
|
85
|
+
rdoc (6.5.0)
|
|
86
86
|
psych (>= 4.0.0)
|
|
87
|
-
rspec (3.
|
|
88
|
-
rspec-core (~> 3.
|
|
89
|
-
rspec-expectations (~> 3.
|
|
90
|
-
rspec-mocks (~> 3.
|
|
91
|
-
rspec-core (3.
|
|
92
|
-
rspec-support (~> 3.
|
|
93
|
-
rspec-expectations (3.
|
|
87
|
+
rspec (3.12.0)
|
|
88
|
+
rspec-core (~> 3.12.0)
|
|
89
|
+
rspec-expectations (~> 3.12.0)
|
|
90
|
+
rspec-mocks (~> 3.12.0)
|
|
91
|
+
rspec-core (3.12.1)
|
|
92
|
+
rspec-support (~> 3.12.0)
|
|
93
|
+
rspec-expectations (3.12.2)
|
|
94
94
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
95
|
-
rspec-support (~> 3.
|
|
96
|
-
rspec-mocks (3.
|
|
95
|
+
rspec-support (~> 3.12.0)
|
|
96
|
+
rspec-mocks (3.12.3)
|
|
97
97
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
98
|
-
rspec-support (~> 3.
|
|
99
|
-
rspec-support (3.
|
|
98
|
+
rspec-support (~> 3.12.0)
|
|
99
|
+
rspec-support (3.12.0)
|
|
100
100
|
ruby2_keywords (0.0.5)
|
|
101
101
|
semver2 (3.4.2)
|
|
102
102
|
singleton (0.1.1)
|
|
103
|
-
stringio (3.0.
|
|
103
|
+
stringio (3.0.5)
|
|
104
104
|
thread_safe (0.3.6)
|
|
105
|
-
zpng (0.4.
|
|
105
|
+
zpng (0.4.5)
|
|
106
106
|
rainbow (~> 3.1.1)
|
|
107
107
|
|
|
108
108
|
PLATFORMS
|
|
109
109
|
ruby
|
|
110
110
|
|
|
111
111
|
DEPENDENCIES
|
|
112
|
-
iostruct
|
|
112
|
+
iostruct (>= 0.0.5)
|
|
113
113
|
juwelier
|
|
114
114
|
prime
|
|
115
115
|
rspec
|
|
116
|
-
zpng (>= 0.4.
|
|
116
|
+
zpng (>= 0.4.5)
|
|
117
117
|
|
|
118
118
|
BUNDLED WITH
|
|
119
119
|
2.3.12
|
data/README.md
CHANGED
|
@@ -28,26 +28,32 @@ Usage
|
|
|
28
28
|
|
|
29
29
|
Usage: zsteg [options] filename.png [param_string]
|
|
30
30
|
|
|
31
|
+
-a, --all try all known methods
|
|
32
|
+
-E, --extract NAME extract specified payload, NAME is like '1b,rgb,lsb'
|
|
33
|
+
|
|
34
|
+
Iteration/extraction params:
|
|
35
|
+
-o, --order X pixel iteration order (default: 'auto')
|
|
36
|
+
valid values: ALL,xy,yx,XY,YX,xY,Xy,bY,...
|
|
31
37
|
-c, --channels X channels (R/G/B/A) or any combination, comma separated
|
|
32
38
|
valid values: r,g,b,a,rg,bgr,rgba,r3g2b3,...
|
|
33
|
-
-l, --limit N limit bytes checked, 0 = no limit (default: 256)
|
|
34
39
|
-b, --bits N number of bits, single int value or '1,3,5' or range '1-8'
|
|
35
40
|
advanced: specify individual bits like '00001110' or '0x88'
|
|
36
|
-
--lsb least significant
|
|
37
|
-
--msb most significant
|
|
41
|
+
--lsb least significant bit comes first
|
|
42
|
+
--msb most significant bit comes first
|
|
38
43
|
-P, --prime analyze/extract only prime bytes/pixels
|
|
44
|
+
--shift N prepend N zero bits
|
|
45
|
+
--step N step
|
|
39
46
|
--invert invert bits (XOR 0xff)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
-
|
|
47
|
+
--pixel-align pixel-align hidden data
|
|
48
|
+
|
|
49
|
+
Analysis params:
|
|
50
|
+
-l, --limit N limit bytes checked, 0 = no limit (default: 256)
|
|
44
51
|
|
|
45
52
|
--[no-]file use 'file' command to detect data type (default: YES)
|
|
46
53
|
--no-strings disable ASCII strings finding (default: enabled)
|
|
47
54
|
-s, --strings X ASCII strings find mode: first, all, longest, none
|
|
48
55
|
(default: first)
|
|
49
56
|
-n, --min-str-len X minimum string length (default: 8)
|
|
50
|
-
--shift N prepend N zero bits
|
|
51
57
|
|
|
52
58
|
-v, --verbose Run verbosely (can be used multiple times)
|
|
53
59
|
-q, --quiet Silent any warnings (can be used multiple times)
|
|
@@ -85,23 +91,6 @@ Examples
|
|
|
85
91
|
|
|
86
92
|
# zsteg wbstego/wbsteg_noenc_even.bmp 1b,lsb,bY -v
|
|
87
93
|
|
|
88
|
-
imagedata .. file: FoxPro FPT, blocks size 1, next free block index 65537
|
|
89
|
-
00000000: 00 01 00 01 00 00 00 01 00 00 00 00 00 00 00 00 |................|
|
|
90
|
-
00000010: 00 00 00 00 00 00 00 00 00 00 00 01 00 01 01 00 |................|
|
|
91
|
-
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
|
92
|
-
00000030: 00 01 01 01 00 01 00 00 00 00 00 00 01 01 00 01 |................|
|
|
93
|
-
00000040: 01 00 01 01 00 01 00 01 00 01 01 01 01 00 00 00 |................|
|
|
94
|
-
00000050: 00 00 00 01 01 01 01 00 01 00 01 00 00 00 00 01 |................|
|
|
95
|
-
00000060: 00 00 01 01 01 00 00 01 00 01 01 01 00 01 00 00 |................|
|
|
96
|
-
00000070: 01 01 01 00 01 00 00 00 00 00 01 01 01 00 00 00 |................|
|
|
97
|
-
00000080: 00 01 00 01 00 00 01 01 01 01 00 00 00 01 01 00 |................|
|
|
98
|
-
00000090: 00 01 00 01 00 01 01 00 01 00 00 01 00 01 00 00 |................|
|
|
99
|
-
000000a0: 00 01 01 01 00 01 00 01 01 01 00 01 00 00 00 01 |................|
|
|
100
|
-
000000b0: 01 00 01 00 00 01 00 01 00 01 01 01 00 00 00 00 |................|
|
|
101
|
-
000000c0: 01 00 00 00 00 01 00 00 01 01 00 00 01 00 00 00 |................|
|
|
102
|
-
000000d0: 00 00 01 00 00 01 01 01 00 01 01 00 00 01 00 01 |................|
|
|
103
|
-
000000e0: 01 01 01 01 01 01 01 00 00 00 00 00 01 00 00 00 |................|
|
|
104
|
-
000000f0: 00 01 01 01 00 00 01 00 00 00 01 01 00 01 00 01 |................|
|
|
105
94
|
b1,lsb,bY .. <wbStego size=22, data="xtSuperSecretMessage\n", even=true, mix=true, controlbyte="t">
|
|
106
95
|
00000000: 51 00 00 16 00 00 74 0d b5 78 1e a1 39 74 e8 38 |Q.....t..x..9t.8|
|
|
107
96
|
00000010: 53 c6 56 94 75 d1 a5 70 84 c8 27 65 fe 08 72 35 |S.V.u..p..'e..r5|
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.2.
|
|
1
|
+
0.2.13
|
data/cmp_png.rb
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# coding: binary
|
|
2
|
+
module ZSteg
|
|
3
|
+
class Checker
|
|
4
|
+
module SteganographyPNG
|
|
5
|
+
|
|
6
|
+
URL = "https://github.com/pedrooaugusto/steganography-png"
|
|
7
|
+
|
|
8
|
+
# https://github.com/pedrooaugusto/steganography-png/blob/2a0e038c135e41438b4c2c93821227a2289b4203/scanlines/scanlines.go#L234
|
|
9
|
+
#
|
|
10
|
+
# The secret metadata is stored in the last bytes of the last scanline in the form of:
|
|
11
|
+
# 17 107 [bitloss] [secret size - 4 bytes] [secret type] [secret type length]
|
|
12
|
+
# 17 107 1 4096 "text/plain" 10
|
|
13
|
+
|
|
14
|
+
class Result < IOStruct.new "nCNa*", :magic, :bitloss, :secret_size, :secret_type
|
|
15
|
+
def valid?
|
|
16
|
+
magic == 0x116b && (1..8).include?(bitloss)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_s
|
|
20
|
+
super.sub('#<struct ZSteg::Checker::SteganographyPNG::Result', 'SteganographyPNG').sub(/>$/,'').bright_red
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.check_image image, _params = {}
|
|
25
|
+
ls = image.scanlines.last
|
|
26
|
+
data = ls.decoded_bytes
|
|
27
|
+
secret_type_length = data[-1].ord
|
|
28
|
+
return nil if secret_type_length > data.size - 8
|
|
29
|
+
data = data[ -secret_type_length-8 .. -2 ]
|
|
30
|
+
# data.size to prevent "want 8 bytes, got 7" IOStruct warning when secret_type_length == 0
|
|
31
|
+
r = Result.read(data, data.size)
|
|
32
|
+
r.valid? && [r, URL]
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/zsteg/checker.rb
CHANGED
|
@@ -3,6 +3,11 @@ require 'stringio'
|
|
|
3
3
|
require 'zlib'
|
|
4
4
|
require 'set'
|
|
5
5
|
|
|
6
|
+
require 'zsteg/checker/scanline_checker'
|
|
7
|
+
require 'zsteg/checker/steganography_png'
|
|
8
|
+
require 'zsteg/checker/wbstego'
|
|
9
|
+
require 'zsteg/checker/zlib'
|
|
10
|
+
|
|
6
11
|
module ZSteg
|
|
7
12
|
class Checker
|
|
8
13
|
attr_accessor :params, :channels, :verbose, :results
|
|
@@ -46,20 +51,20 @@ module ZSteg
|
|
|
46
51
|
|
|
47
52
|
private
|
|
48
53
|
|
|
49
|
-
|
|
50
|
-
def print *args
|
|
51
|
-
Kernel.print(*args) if @verbose >= 0
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# catch Kernel#printf for easier verbosity handling
|
|
55
|
-
def printf *args
|
|
56
|
-
Kernel.printf(*args) if @verbose >= 0
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# catch Kernel#puts for easier verbosity handling
|
|
60
|
-
def puts *args
|
|
61
|
-
Kernel.puts(*args) if @verbose >= 0
|
|
62
|
-
end
|
|
54
|
+
# # catch Kernel#print for easier verbosity handling
|
|
55
|
+
# def print *args
|
|
56
|
+
# Kernel.print(*args) if @verbose >= 0
|
|
57
|
+
# end
|
|
58
|
+
#
|
|
59
|
+
# # catch Kernel#printf for easier verbosity handling
|
|
60
|
+
# def printf *args
|
|
61
|
+
# Kernel.printf(*args) if @verbose >= 0
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
# # catch Kernel#puts for easier verbosity handling
|
|
65
|
+
# def puts *args
|
|
66
|
+
# Kernel.puts(*args) if @verbose >= 0
|
|
67
|
+
# end
|
|
63
68
|
|
|
64
69
|
public
|
|
65
70
|
|
|
@@ -92,12 +97,26 @@ module ZSteg
|
|
|
92
97
|
Array(params[:order]).uniq.each do |order|
|
|
93
98
|
(params[:prime] == :all ? [false,true] : [params[:prime]]).each do |prime|
|
|
94
99
|
Array(params[:bits]).uniq.each do |bits|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
if params[:pixel_align] == :all
|
|
101
|
+
[false, true].each do |pixel_align|
|
|
102
|
+
# skip cases when output will be identical for pixel_align true/false
|
|
103
|
+
next if pixel_align && (8%bits) == 0
|
|
104
|
+
p1 = @params.merge bits: bits, order: order, prime: prime, pixel_align: pixel_align
|
|
105
|
+
if order[/b/i]
|
|
106
|
+
# byte iterator does not need channels
|
|
107
|
+
check_channels nil, p1
|
|
108
|
+
else
|
|
109
|
+
channels.each{ |c| check_channels c, p1 }
|
|
110
|
+
end
|
|
111
|
+
end
|
|
99
112
|
else
|
|
100
|
-
|
|
113
|
+
p1 = @params.merge bits: bits, order: order, prime: prime
|
|
114
|
+
if order[/b/i]
|
|
115
|
+
# byte iterator does not need channels
|
|
116
|
+
check_channels nil, p1
|
|
117
|
+
else
|
|
118
|
+
channels.each{ |c| check_channels c, p1 }
|
|
119
|
+
end
|
|
101
120
|
end
|
|
102
121
|
end
|
|
103
122
|
end
|
|
@@ -150,6 +169,13 @@ module ZSteg
|
|
|
150
169
|
show_title title, :bright_red
|
|
151
170
|
process_result data, :special => true, :title => title
|
|
152
171
|
end
|
|
172
|
+
|
|
173
|
+
if r = SteganographyPNG.check_image(@image, @params)
|
|
174
|
+
@found_anything = true
|
|
175
|
+
title = "image"
|
|
176
|
+
show_title title, :bright_red
|
|
177
|
+
process_result nil, title: title, result: r
|
|
178
|
+
end
|
|
153
179
|
end
|
|
154
180
|
|
|
155
181
|
def check_metadata
|
|
@@ -220,6 +246,8 @@ module ZSteg
|
|
|
220
246
|
end
|
|
221
247
|
end
|
|
222
248
|
|
|
249
|
+
bits_tag << "p" if params[:pixel_align]
|
|
250
|
+
|
|
223
251
|
title = [
|
|
224
252
|
bits_tag,
|
|
225
253
|
channels,
|
|
@@ -268,21 +296,26 @@ module ZSteg
|
|
|
268
296
|
def process_result data, params
|
|
269
297
|
verbose = params[:special] ? [@verbose,1.5].max : @verbose
|
|
270
298
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
299
|
+
result = nil
|
|
300
|
+
if data
|
|
301
|
+
if @cache[data]
|
|
302
|
+
if verbose > 1
|
|
303
|
+
puts "[same as #{@cache[data].inspect}]".gray
|
|
304
|
+
return true
|
|
305
|
+
else
|
|
306
|
+
# silent return
|
|
307
|
+
return false
|
|
308
|
+
end
|
|
278
309
|
end
|
|
279
|
-
end
|
|
280
310
|
|
|
281
|
-
|
|
282
|
-
|
|
311
|
+
# TODO: store hash of data for large datas
|
|
312
|
+
@cache[data] = params[:title]
|
|
283
313
|
|
|
284
|
-
|
|
285
|
-
|
|
314
|
+
if result = data2result(data, params)
|
|
315
|
+
@results << result
|
|
316
|
+
end
|
|
317
|
+
elsif !(result = params[:result])
|
|
318
|
+
raise "[?] No data nor result"
|
|
286
319
|
end
|
|
287
320
|
|
|
288
321
|
case verbose
|
|
@@ -309,7 +342,7 @@ module ZSteg
|
|
|
309
342
|
else
|
|
310
343
|
show_result result, params
|
|
311
344
|
end
|
|
312
|
-
if data.size > 0 && !result.is_a?(Result::OneChar) && !result.is_a?(Result::WholeText)
|
|
345
|
+
if data && data.size > 0 && !result.is_a?(Result::OneChar) && !result.is_a?(Result::WholeText)
|
|
313
346
|
# newline if no results and want hexdump
|
|
314
347
|
puts if !result || result == []
|
|
315
348
|
limit = (params[:limit] || @params[:limit]).to_i
|
data/lib/zsteg/cli/cli.rb
CHANGED
|
@@ -11,14 +11,39 @@ module ZSteg
|
|
|
11
11
|
def run
|
|
12
12
|
@actions = []
|
|
13
13
|
@options = {
|
|
14
|
-
:
|
|
15
|
-
:
|
|
16
|
-
:
|
|
14
|
+
verbose: 0,
|
|
15
|
+
limit: Checker::DEFAULT_LIMIT,
|
|
16
|
+
order: Checker::DEFAULT_ORDER,
|
|
17
|
+
step: 1,
|
|
18
|
+
ystep: 1,
|
|
17
19
|
}
|
|
18
20
|
optparser = OptionParser.new do |opts|
|
|
19
21
|
opts.banner = "Usage: zsteg [options] filename.png [param_string]"
|
|
20
22
|
opts.separator ""
|
|
21
23
|
|
|
24
|
+
opts.on "-a", "--all", "try all known methods" do
|
|
25
|
+
@options[:prime] = :all
|
|
26
|
+
@options[:order] = :all
|
|
27
|
+
@options[:pixel_align] = :all
|
|
28
|
+
@options[:bits] = (1..8).to_a
|
|
29
|
+
# specifying --all on command line explicitly enables extra checks
|
|
30
|
+
@options[:extra_checks] = true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
opts.on "-E", "--extract NAME", "extract specified payload, NAME is like '1b,rgb,lsb'" do |x|
|
|
34
|
+
@options[:verbose] = -2 # silent ZPNG warnings
|
|
35
|
+
@actions << [:extract, x]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#################################################################################
|
|
39
|
+
opts.separator "\nIteration/extraction params:"
|
|
40
|
+
#################################################################################
|
|
41
|
+
|
|
42
|
+
opts.on("-o", "--order X", /all|auto|[bxy,]+/i,
|
|
43
|
+
"pixel iteration order (default: '#{@options[:order]}')",
|
|
44
|
+
"valid values: ALL,xy,yx,XY,YX,xY,Xy,bY,...",
|
|
45
|
+
){ |x| @options[:order] = x.split(',') }
|
|
46
|
+
|
|
22
47
|
opts.on("-c", "--channels X", /[rgba,1-8]+/,
|
|
23
48
|
"channels (R/G/B/A) or any combination, comma separated",
|
|
24
49
|
"valid values: r,g,b,a,rg,bgr,rgba,r3g2b3,..."
|
|
@@ -28,14 +53,14 @@ module ZSteg
|
|
|
28
53
|
@options[:extra_checks] = false
|
|
29
54
|
end
|
|
30
55
|
|
|
31
|
-
opts.on("-l", "--limit N", Integer,
|
|
32
|
-
"limit bytes checked, 0 = no limit (default: #{@options[:limit]})"
|
|
33
|
-
){ |n| @options[:limit] = n }
|
|
34
|
-
|
|
35
56
|
opts.on("-b", "--bits N", "number of bits, single int value or '1,3,5' or range '1-8'",
|
|
36
57
|
"advanced: specify individual bits like '00001110' or '0x88'"
|
|
37
58
|
) do |x|
|
|
38
59
|
a = []
|
|
60
|
+
if x[-1] == 'p'
|
|
61
|
+
@options[:pixel_align] = true
|
|
62
|
+
x = x[0..-2]
|
|
63
|
+
end
|
|
39
64
|
x = '1-8' if x == 'all'
|
|
40
65
|
x.split(',').each do |x1|
|
|
41
66
|
if x1['-']
|
|
@@ -50,10 +75,10 @@ module ZSteg
|
|
|
50
75
|
@options[:extra_checks] = false
|
|
51
76
|
end
|
|
52
77
|
|
|
53
|
-
opts.on "--lsb", "least significant
|
|
78
|
+
opts.on "--lsb", "least significant bit comes first" do
|
|
54
79
|
@options[:bit_order] = :lsb
|
|
55
80
|
end
|
|
56
|
-
opts.on "--msb", "most significant
|
|
81
|
+
opts.on "--msb", "most significant bit comes first" do
|
|
57
82
|
@options[:bit_order] = :msb
|
|
58
83
|
end
|
|
59
84
|
|
|
@@ -62,31 +87,20 @@ module ZSteg
|
|
|
62
87
|
# specifying prime on command line disables extra checks
|
|
63
88
|
@options[:extra_checks] = false
|
|
64
89
|
end
|
|
65
|
-
opts.on "--invert", "invert bits (XOR 0xff)" do
|
|
66
|
-
@options[:invert] = true
|
|
67
|
-
end
|
|
68
90
|
|
|
69
|
-
|
|
70
|
-
#
|
|
71
|
-
|
|
91
|
+
opts.on("--shift N", Integer, "prepend N zero bits"){ |x| @options[:shift] = x }
|
|
92
|
+
#opts.on("--step N", Integer, "step") { |x| @options[:step] = x }
|
|
93
|
+
opts.on("--invert", "invert bits (XOR 0xff)") { @options[:invert] = true }
|
|
72
94
|
|
|
73
|
-
opts.on "-
|
|
74
|
-
@options[:
|
|
75
|
-
@options[:order] = :all
|
|
76
|
-
@options[:bits] = (1..8).to_a
|
|
77
|
-
# specifying --all on command line explicitly enables extra checks
|
|
78
|
-
@options[:extra_checks] = true
|
|
95
|
+
opts.on "--pixel-align", "pixel-align hidden data" do
|
|
96
|
+
@options[:pixel_align] = true
|
|
79
97
|
end
|
|
80
98
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
){ |x| @options[:order] = x.split(',') }
|
|
99
|
+
#################################################################################
|
|
100
|
+
opts.separator "\nAnalysis params:"
|
|
101
|
+
#################################################################################
|
|
85
102
|
|
|
86
|
-
opts.on
|
|
87
|
-
@options[:verbose] = -2 # silent ZPNG warnings
|
|
88
|
-
@actions << [:extract, x]
|
|
89
|
-
end
|
|
103
|
+
opts.on("-l", "--limit N", Integer, "limit bytes checked, 0 = no limit (default: #{@options[:limit]})"){ |n| @options[:limit] = n }
|
|
90
104
|
|
|
91
105
|
opts.separator ""
|
|
92
106
|
|
|
@@ -112,10 +126,6 @@ module ZSteg
|
|
|
112
126
|
@options[:min_str_len] = x
|
|
113
127
|
end
|
|
114
128
|
|
|
115
|
-
opts.on "--shift N", Integer, "prepend N zero bits" do |x|
|
|
116
|
-
@options[:shift] = x
|
|
117
|
-
end
|
|
118
|
-
|
|
119
129
|
opts.separator ""
|
|
120
130
|
opts.on "-v", "--verbose", "Run verbosely (can be used multiple times)" do |v|
|
|
121
131
|
@options[:verbose] += 1
|
|
@@ -203,6 +213,9 @@ module ZSteg
|
|
|
203
213
|
h[:bit_order] = :msb
|
|
204
214
|
when /^(\d)b$/, /^b(\d+)$/
|
|
205
215
|
h[:bits] = parse_bits($1)
|
|
216
|
+
when /^(\d)bp$/, /^b(\d+)p$/
|
|
217
|
+
h[:bits] = parse_bits($1)
|
|
218
|
+
h[:pixel_align] = true
|
|
206
219
|
when /\A[rgba]+\Z/
|
|
207
220
|
h[:channels] = [x]
|
|
208
221
|
when /\Axy|yx|yb|by\Z/i
|
|
@@ -32,8 +32,8 @@ module ZSteg
|
|
|
32
32
|
else
|
|
33
33
|
8.times{ |i| byte |= (a.shift<<(7-i))}
|
|
34
34
|
end
|
|
35
|
-
#printf "[d] %02x %08b\n", byte, byte
|
|
36
35
|
data << byte.chr
|
|
36
|
+
#a = []
|
|
37
37
|
if data.size >= @limit
|
|
38
38
|
print "[limit #@limit]".gray if @verbose > 1
|
|
39
39
|
break
|
|
@@ -77,12 +77,15 @@ module ZSteg
|
|
|
77
77
|
[@image.height-1, 0, -1]
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
xstep *= params[:step] if params[:step]
|
|
81
|
+
ystep *= params[:ystep] if params[:ystep]
|
|
82
|
+
|
|
80
83
|
# cannot join these lines from ByteExtractor and ColorExtractor into
|
|
81
84
|
# one method for performance reason:
|
|
82
85
|
# it will require additional yield() for EACH BYTE iterated
|
|
83
86
|
|
|
84
87
|
if type[0,1].downcase == 'b'
|
|
85
|
-
# ROW iterator
|
|
88
|
+
# ROW iterator (natural)
|
|
86
89
|
if params[:prime]
|
|
87
90
|
idx = 0
|
|
88
91
|
y0.step(y1,ystep){ |y| x0.step(x1,xstep){ |x|
|
|
@@ -6,7 +6,7 @@ module ZSteg
|
|
|
6
6
|
|
|
7
7
|
def color_extract params = {}
|
|
8
8
|
channels = Array(params[:channels])
|
|
9
|
-
|
|
9
|
+
pixel_align = params[:pixel_align]
|
|
10
10
|
|
|
11
11
|
ch_masks = []
|
|
12
12
|
case channels.first.size
|
|
@@ -42,28 +42,28 @@ module ZSteg
|
|
|
42
42
|
color = @image[x,y]
|
|
43
43
|
|
|
44
44
|
ch_masks.each do |c,bidxs|
|
|
45
|
+
bidxs = bidxs[a.size-8..] if pixel_align && a.size + bidxs.size > 8
|
|
45
46
|
value = color.send(c)
|
|
46
47
|
bidxs.each do |bidx|
|
|
47
48
|
a << value[bidx]
|
|
48
49
|
end
|
|
49
50
|
end
|
|
50
|
-
#p [x,y,a.size,a]
|
|
51
51
|
|
|
52
52
|
while a.size >= 8
|
|
53
53
|
byte = 0
|
|
54
|
-
#
|
|
54
|
+
# a0 = a.dup
|
|
55
55
|
if params[:bit_order] == :msb
|
|
56
56
|
8.times{ |i| byte |= (a.shift<<i)}
|
|
57
57
|
else
|
|
58
58
|
8.times{ |i| byte |= (a.shift<<(7-i))}
|
|
59
59
|
end
|
|
60
|
-
#printf "[d] %02x %08b\n", byte, byte
|
|
60
|
+
# printf "[d] %-10s -> %-10s : %s %02x %08b x=%d y=%d\n", a0.join, a.join, byte.chr.inspect, byte, byte, x, y
|
|
61
61
|
data << byte.chr
|
|
62
62
|
if data.size >= @limit
|
|
63
63
|
print "[limit #@limit]".gray if @verbose > 1
|
|
64
64
|
throw :limit
|
|
65
65
|
end
|
|
66
|
-
|
|
66
|
+
a.clear if pixel_align && a.size < 8
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
end
|
data/lib/zsteg.rb
CHANGED
data/spec/checker_spec.rb
CHANGED
|
@@ -17,9 +17,9 @@ describe Checker do
|
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
it "should be quiet by default" do
|
|
21
|
-
@out.should == ""
|
|
22
|
-
end
|
|
20
|
+
# it "should be quiet by default" do
|
|
21
|
+
# @out.should == ""
|
|
22
|
+
# end
|
|
23
23
|
|
|
24
24
|
it "returned results should be equal to #results" do
|
|
25
25
|
@results.should == @checker.results
|
data/spec/spec_helper.rb
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
# https://github.com/pedrooaugusto/steganography-png
|
|
4
|
+
each_sample("steganography-png/*.png") do |fname|
|
|
5
|
+
describe fname do
|
|
6
|
+
it "should reveal secret message" do
|
|
7
|
+
r = cli fname, "--limit", "64", "-a", "--no-file"
|
|
8
|
+
r.should include("SteganographyPNG")
|
|
9
|
+
r.should include(ZSteg::Checker::SteganographyPNG::URL)
|
|
10
|
+
r.should_not include("ZSteg::Checker::SteganographyPNG")
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
data/zsteg.gemspec
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
|
5
|
-
# stub: zsteg 0.2.
|
|
5
|
+
# stub: zsteg 0.2.13 ruby lib
|
|
6
6
|
|
|
7
7
|
Gem::Specification.new do |s|
|
|
8
8
|
s.name = "zsteg".freeze
|
|
9
|
-
s.version = "0.2.
|
|
9
|
+
s.version = "0.2.13"
|
|
10
10
|
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
|
12
12
|
s.require_paths = ["lib".freeze]
|
|
13
13
|
s.authors = ["Andrey \"Zed\" Zaikin".freeze]
|
|
14
|
-
s.date = "2023-02-
|
|
14
|
+
s.date = "2023-02-19"
|
|
15
15
|
s.email = "zed.0xff@gmail.com".freeze
|
|
16
16
|
s.executables = ["zsteg".freeze, "zsteg-mask".freeze, "zsteg-reflow".freeze]
|
|
17
17
|
s.extra_rdoc_files = [
|
|
@@ -36,6 +36,7 @@ Gem::Specification.new do |s|
|
|
|
36
36
|
"lib/zsteg/analyzer.rb",
|
|
37
37
|
"lib/zsteg/checker.rb",
|
|
38
38
|
"lib/zsteg/checker/scanline_checker.rb",
|
|
39
|
+
"lib/zsteg/checker/steganography_png.rb",
|
|
39
40
|
"lib/zsteg/checker/wbstego.rb",
|
|
40
41
|
"lib/zsteg/checker/zlib.rb",
|
|
41
42
|
"lib/zsteg/cli/cli.rb",
|
|
@@ -63,6 +64,7 @@ Gem::Specification.new do |s|
|
|
|
63
64
|
"spec/r3g2b3_spec.rb",
|
|
64
65
|
"spec/simple_spec.rb",
|
|
65
66
|
"spec/spec_helper.rb",
|
|
67
|
+
"spec/steganography_png_spec.rb",
|
|
66
68
|
"spec/wbstego_spec.rb",
|
|
67
69
|
"spec/wechall_spec.rb",
|
|
68
70
|
"spec/zlib_spec.rb",
|
|
@@ -81,14 +83,14 @@ Gem::Specification.new do |s|
|
|
|
81
83
|
end
|
|
82
84
|
|
|
83
85
|
if s.respond_to? :add_runtime_dependency then
|
|
84
|
-
s.add_runtime_dependency(%q<zpng>.freeze, [">= 0.4.
|
|
85
|
-
s.add_runtime_dependency(%q<iostruct>.freeze, [">= 0"])
|
|
86
|
+
s.add_runtime_dependency(%q<zpng>.freeze, [">= 0.4.5"])
|
|
87
|
+
s.add_runtime_dependency(%q<iostruct>.freeze, [">= 0.0.5"])
|
|
86
88
|
s.add_runtime_dependency(%q<prime>.freeze, [">= 0"])
|
|
87
89
|
s.add_development_dependency(%q<rspec>.freeze, [">= 0"])
|
|
88
90
|
s.add_development_dependency(%q<juwelier>.freeze, [">= 0"])
|
|
89
91
|
else
|
|
90
|
-
s.add_dependency(%q<zpng>.freeze, [">= 0.4.
|
|
91
|
-
s.add_dependency(%q<iostruct>.freeze, [">= 0"])
|
|
92
|
+
s.add_dependency(%q<zpng>.freeze, [">= 0.4.5"])
|
|
93
|
+
s.add_dependency(%q<iostruct>.freeze, [">= 0.0.5"])
|
|
92
94
|
s.add_dependency(%q<prime>.freeze, [">= 0"])
|
|
93
95
|
s.add_dependency(%q<rspec>.freeze, [">= 0"])
|
|
94
96
|
s.add_dependency(%q<juwelier>.freeze, [">= 0"])
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zsteg
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.13
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrey "Zed" Zaikin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-02-
|
|
11
|
+
date: 2023-02-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: zpng
|
|
@@ -16,28 +16,28 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.4.
|
|
19
|
+
version: 0.4.5
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.4.
|
|
26
|
+
version: 0.4.5
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: iostruct
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
33
|
+
version: 0.0.5
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
40
|
+
version: 0.0.5
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: prime
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,6 +108,7 @@ files:
|
|
|
108
108
|
- lib/zsteg/analyzer.rb
|
|
109
109
|
- lib/zsteg/checker.rb
|
|
110
110
|
- lib/zsteg/checker/scanline_checker.rb
|
|
111
|
+
- lib/zsteg/checker/steganography_png.rb
|
|
111
112
|
- lib/zsteg/checker/wbstego.rb
|
|
112
113
|
- lib/zsteg/checker/zlib.rb
|
|
113
114
|
- lib/zsteg/cli/cli.rb
|
|
@@ -135,6 +136,7 @@ files:
|
|
|
135
136
|
- spec/r3g2b3_spec.rb
|
|
136
137
|
- spec/simple_spec.rb
|
|
137
138
|
- spec/spec_helper.rb
|
|
139
|
+
- spec/steganography_png_spec.rb
|
|
138
140
|
- spec/wbstego_spec.rb
|
|
139
141
|
- spec/wechall_spec.rb
|
|
140
142
|
- spec/zlib_spec.rb
|