zsteg 0.2.11 → 0.2.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/Gemfile.lock +34 -33
  4. data/README.md +14 -25
  5. data/Rakefile +1 -1
  6. data/VERSION +1 -1
  7. data/cmp_png.rb +1 -1
  8. data/lib/zsteg/checker/steganography_png.rb +36 -0
  9. data/lib/zsteg/checker.rb +65 -32
  10. data/lib/zsteg/cli/cli.rb +47 -34
  11. data/lib/zsteg/extractor/byte_extractor.rb +5 -2
  12. data/lib/zsteg/extractor/color_extractor.rb +5 -5
  13. data/lib/zsteg.rb +0 -4
  14. data/spec/checker_spec.rb +3 -3
  15. data/spec/spec_helper.rb +2 -1
  16. data/spec/steganography_png_spec.rb +13 -0
  17. data/spec/zlib_spec.rb +1 -1
  18. data/zsteg.gemspec +10 -38
  19. metadata +9 -37
  20. data/pngsteg.gemspec +0 -65
  21. data/samples/hackquest/crypt.bmp +0 -0
  22. data/samples/hackquest/square.bmp +0 -0
  23. data/samples/newbiecontest/alph1-surprise.bmp.7z +0 -0
  24. data/samples/wbstego/wbsteg_blowfish_pass_1.bmp +0 -0
  25. data/samples/wbstego/wbsteg_cast128_pass_1.bmp +0 -0
  26. data/samples/wbstego/wbsteg_enc_pass_pass.bmp +0 -0
  27. data/samples/wbstego/wbsteg_enc_pass_pass_even.bmp +0 -0
  28. data/samples/wbstego/wbsteg_mix_pass_1.bmp +0 -0
  29. data/samples/wbstego/wbsteg_mix_pass_1_even.bmp +0 -0
  30. data/samples/wbstego/wbsteg_mix_pass_foobar.bmp +0 -0
  31. data/samples/wbstego/wbsteg_mix_pass_pass.bmp +0 -0
  32. data/samples/wbstego/wbsteg_mixenc_pass_pass_even.bmp +0 -0
  33. data/samples/wbstego/wbsteg_noenc.bmp +0 -0
  34. data/samples/wbstego/wbsteg_noenc.png +0 -0
  35. data/samples/wbstego/wbsteg_noenc_.bmp +0 -0
  36. data/samples/wbstego/wbsteg_noenc_17.bmp +0 -0
  37. data/samples/wbstego/wbsteg_noenc__.bmp +0 -0
  38. data/samples/wbstego/wbsteg_noenc_even.bmp +0 -0
  39. data/samples/wbstego/wbsteg_noenc_even2.bmp +0 -0
  40. data/samples/wbstego/wbsteg_noenc_even_17.bmp +0 -0
  41. data/samples/wbstego/wbsteg_noenc_even_17_.bmp +0 -0
  42. data/samples/wbstego/wbsteg_noenc_ext_ABC.bmp +0 -0
  43. data/samples/wbstego/wbsteg_rijndael_pass_1.bmp +0 -0
  44. data/samples/wbstego/wbsteg_rijndael_pass_pass.bmp +0 -0
  45. data/samples/wbstego/wbsteg_rijndael_pass_pass_even.bmp +0 -0
  46. data/samples/wbstego/wbsteg_twofish_pass_1.bmp +0 -0
  47. data/samples/wechall/5ZMGcCLxpcpsru03.g00000010.png +0 -0
  48. data/samples/wechall/5ZMGcCLxpcpsru03.png +0 -0
  49. data/samples/wechall/stegano1.bmp +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 492cf8819fa642e0485ae66c7b0942746f785b25fa10c3d0b6029ad93078783a
4
- data.tar.gz: cde21ec01b7e5b3ea7a85e6817df0497ac2f91fb145739ae6702dcf7b3ee6832
3
+ metadata.gz: 05bf462c6bc2696efead731d0ce3bda8cf371435cc9448b100892b7084364c64
4
+ data.tar.gz: c12f610fe42706271d763620100a71572477696c07010d9323c1bb97f7440fbe
5
5
  SHA512:
6
- metadata.gz: e0894f6e7d76dee86646251dd6768c0887c654ae0f5d560be4c65726666a4b7cf6028fb003ab0468cb8f33706fdecaabcd3b19b36e25e39537b7539482bb6455
7
- data.tar.gz: ac61068f1b9d4c914f9543a81623c387e826d996e35a57ed5db6fa449cff9a303345e44d7d1e0070e70aacdf03509441589824d6978208ce5b63957c0adb99de
6
+ metadata.gz: 3340516e94d8248446ddb5df7a64269aed8fb4b09a1bf10057d603581b352e39adc595104ae4b75fa042d8895459532dac21c2304cc49bfe222ffc0df358fa55
7
+ data.tar.gz: d4bd9eaf13356a534eeb3be2807b524c73549dc0d3fbe44f349393930f0f4a31be1430b434d4ef8fb42ea2c2eda42dd13eb013222fc37714ce9801973358ba8e
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem "zpng", ">= 0.4.3"
4
- gem "iostruct"
3
+ gem "zpng", ">= 0.4.5"
4
+ gem "iostruct", ">= 0.0.5"
5
5
  gem "prime"
6
6
 
7
7
  group :development do
data/Gemfile.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- addressable (2.8.0)
5
- public_suffix (>= 2.0.2, < 5.0)
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.0)
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)
@@ -30,8 +30,9 @@ GEM
30
30
  faraday-patron (1.0.0)
31
31
  faraday-rack (1.0.0)
32
32
  faraday-retry (1.0.3)
33
- forwardable (1.3.2)
34
- git (1.11.0)
33
+ forwardable (1.3.3)
34
+ git (1.13.2)
35
+ addressable (~> 2.8)
35
36
  rchardet (~> 1.8)
36
37
  github_api (0.19.0)
37
38
  addressable (~> 2.4)
@@ -40,8 +41,8 @@ GEM
40
41
  hashie (~> 3.5, >= 3.5.2)
41
42
  oauth2 (~> 1.0)
42
43
  hashie (3.6.0)
43
- highline (2.0.3)
44
- iostruct (0.0.4)
44
+ highline (2.1.0)
45
+ iostruct (0.0.5)
45
46
  juwelier (2.4.9)
46
47
  builder
47
48
  bundler
@@ -54,65 +55,65 @@ GEM
54
55
  rake
55
56
  rdoc
56
57
  semver2
57
- jwt (2.4.1)
58
+ jwt (2.7.0)
58
59
  kamelcase (0.0.2)
59
60
  semver2 (~> 3)
60
- mini_portile2 (2.8.0)
61
+ mini_portile2 (2.8.1)
61
62
  multi_json (1.15.0)
62
63
  multi_xml (0.6.0)
63
- multipart-post (2.2.3)
64
- nokogiri (1.13.6)
64
+ multipart-post (2.3.0)
65
+ nokogiri (1.14.2)
65
66
  mini_portile2 (~> 2.8.0)
66
67
  racc (~> 1.4)
67
- oauth2 (1.4.9)
68
+ oauth2 (1.4.11)
68
69
  faraday (>= 0.17.3, < 3.0)
69
70
  jwt (>= 1.0, < 3.0)
70
71
  multi_json (~> 1.3)
71
72
  multi_xml (~> 0.5)
72
- rack (>= 1.2, < 3)
73
+ rack (>= 1.2, < 4)
73
74
  prime (0.1.2)
74
75
  forwardable
75
76
  singleton
76
- psych (4.0.4)
77
+ psych (5.1.0)
77
78
  stringio
78
- public_suffix (4.0.7)
79
- racc (1.6.0)
80
- rack (2.2.3.1)
79
+ public_suffix (5.0.1)
80
+ racc (1.6.2)
81
+ rack (3.0.4.1)
81
82
  rainbow (3.1.1)
82
83
  rake (13.0.6)
83
84
  rchardet (1.8.0)
84
- rdoc (6.4.0)
85
+ rdoc (6.5.0)
85
86
  psych (>= 4.0.0)
86
- rspec (3.11.0)
87
- rspec-core (~> 3.11.0)
88
- rspec-expectations (~> 3.11.0)
89
- rspec-mocks (~> 3.11.0)
90
- rspec-core (3.11.0)
91
- rspec-support (~> 3.11.0)
92
- rspec-expectations (3.11.0)
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)
93
94
  diff-lcs (>= 1.2.0, < 2.0)
94
- rspec-support (~> 3.11.0)
95
- rspec-mocks (3.11.1)
95
+ rspec-support (~> 3.12.0)
96
+ rspec-mocks (3.12.3)
96
97
  diff-lcs (>= 1.2.0, < 2.0)
97
- rspec-support (~> 3.11.0)
98
- rspec-support (3.11.0)
98
+ rspec-support (~> 3.12.0)
99
+ rspec-support (3.12.0)
99
100
  ruby2_keywords (0.0.5)
100
101
  semver2 (3.4.2)
101
102
  singleton (0.1.1)
102
- stringio (3.0.2)
103
+ stringio (3.0.5)
103
104
  thread_safe (0.3.6)
104
- zpng (0.4.3)
105
+ zpng (0.4.5)
105
106
  rainbow (~> 3.1.1)
106
107
 
107
108
  PLATFORMS
108
109
  ruby
109
110
 
110
111
  DEPENDENCIES
111
- iostruct
112
+ iostruct (>= 0.0.5)
112
113
  juwelier
113
114
  prime
114
115
  rspec
115
- zpng (>= 0.4.3)
116
+ zpng (>= 0.4.5)
116
117
 
117
118
  BUNDLED WITH
118
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 BIT comes first
37
- --msb most significant BIT comes first
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
- -a, --all try all known methods
41
- -o, --order X pixel iteration order (default: 'auto')
42
- valid values: ALL,xy,yx,XY,YX,xY,Xy,bY,...
43
- -E, --extract NAME extract specified payload, NAME is like '1b,rgb,lsb'
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/Rakefile CHANGED
@@ -24,7 +24,7 @@ Juwelier::Tasks.new do |gem|
24
24
  #gem.executables = %w'zsteg'
25
25
  gem.files.include "lib/**/*.rb"
26
26
  gem.files.include "bin/*"
27
- gem.files.exclude "samples/*"
27
+ gem.files.exclude "samples/**/*"
28
28
  # dependencies defined in Gemfile
29
29
  end
30
30
  Juwelier::RubygemsDotOrgTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.11
1
+ 0.2.13
data/cmp_png.rb CHANGED
@@ -5,7 +5,7 @@ require 'awesome_print'
5
5
  images = ARGV.map{ |fname| ZPNG::Image.load(fname) }
6
6
  raise "need at least 2 images" if images.size < 2
7
7
 
8
- limit = 20
8
+ limit = 100
9
9
  alpha_used = images.any?(&:alpha_used?)
10
10
  channels = alpha_used ? %w'r g b a' : %w'r g b'
11
11
 
@@ -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
- # catch Kernel#print for easier verbosity handling
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
- p1 = @params.merge :bits => bits, :order => order, :prime => prime
96
- if order[/b/i]
97
- # byte iterator does not need channels
98
- check_channels nil, p1
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
- channels.each{ |c| check_channels c, p1 }
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
- if @cache[data]
272
- if verbose > 1
273
- puts "[same as #{@cache[data].inspect}]".gray
274
- return true
275
- else
276
- # silent return
277
- return false
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
- # TODO: store hash of data for large datas
282
- @cache[data] = params[:title]
311
+ # TODO: store hash of data for large datas
312
+ @cache[data] = params[:title]
283
313
 
284
- if result = data2result(data, params)
285
- @results << result
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
- :verbose => 0,
15
- :limit => Checker::DEFAULT_LIMIT,
16
- :order => Checker::DEFAULT_ORDER
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 BIT comes first" do
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 BIT comes first" do
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
- # opts.on "--pixel-align", "pixel-align hidden data (EasyBMP)" do
70
- # @options[:pixel_align] = true
71
- # end
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 "-a", "--all", "try all known methods" do
74
- @options[:prime] = :all
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
- opts.on("-o", "--order X", /all|auto|[bxy,]+/i,
82
- "pixel iteration order (default: '#{@options[:order]}')",
83
- "valid values: ALL,xy,yx,XY,YX,xY,Xy,bY,...",
84
- ){ |x| @options[:order] = x.split(',') }
99
+ #################################################################################
100
+ opts.separator "\nAnalysis params:"
101
+ #################################################################################
85
102
 
86
- opts.on "-E", "--extract NAME", "extract specified payload, NAME is like '1b,rgb,lsb'" do |x|
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
@@ -201,8 +211,11 @@ module ZSteg
201
211
  h[:bit_order] = :lsb
202
212
  when 'msb'
203
213
  h[:bit_order] = :msb
204
- when /^(\d)b$/, /^b(\d)$/
214
+ when /^(\d)b$/, /^b(\d+)$/
215
+ h[:bits] = parse_bits($1)
216
+ when /^(\d)bp$/, /^b(\d+)p$/
205
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
- #pixel_align = params[:pixel_align]
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
- #puts a.join
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
- #a.clear if pixel_align
66
+ a.clear if pixel_align && a.size < 8
67
67
  end
68
68
  end
69
69
  end
data/lib/zsteg.rb CHANGED
@@ -9,10 +9,6 @@ require 'zsteg/checker'
9
9
  require 'zsteg/result'
10
10
  require 'zsteg/file_cmd'
11
11
 
12
- require 'zsteg/checker/wbstego'
13
- require 'zsteg/checker/scanline_checker'
14
- require 'zsteg/checker/zlib'
15
-
16
12
  require 'zsteg/masker'
17
13
 
18
14
  require 'zsteg/analyzer'
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
@@ -40,6 +40,7 @@ def cli *args
40
40
  else
41
41
  ZSteg::CLI
42
42
  end
43
+ args << "-qqq"
43
44
  args << "--no-color" unless args.any?{|x| x['color']}
44
45
  orig_stdout, out = $stdout, ""
45
46
  begin
@@ -48,7 +49,7 @@ def cli *args
48
49
  ensure
49
50
  $stdout = orig_stdout
50
51
  end
51
- out.strip
52
+ out
52
53
  end
53
54
  end
54
55
 
@@ -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/spec/zlib_spec.rb CHANGED
@@ -16,7 +16,7 @@ sample("ndh2k12_sp113.bmp") do |fname|
16
16
  subject{ cli(fname, "--extract b1,rgb,lsb,yx,zlib") }
17
17
 
18
18
  it { should include "%PDF-1.4" }
19
- it { subject.size.should == 202383 }
19
+ it { subject.size.should == 202386 }
20
20
  end
21
21
  end
22
22
  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.11 ruby lib
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.11"
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 = "2022-08-29"
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",
@@ -47,36 +48,6 @@ Gem::Specification.new do |s|
47
48
  "lib/zsteg/file_cmd.rb",
48
49
  "lib/zsteg/masker.rb",
49
50
  "lib/zsteg/result.rb",
50
- "pngsteg.gemspec",
51
- "samples/hackquest/crypt.bmp",
52
- "samples/hackquest/square.bmp",
53
- "samples/newbiecontest/alph1-surprise.bmp.7z",
54
- "samples/wbstego/wbsteg_blowfish_pass_1.bmp",
55
- "samples/wbstego/wbsteg_cast128_pass_1.bmp",
56
- "samples/wbstego/wbsteg_enc_pass_pass.bmp",
57
- "samples/wbstego/wbsteg_enc_pass_pass_even.bmp",
58
- "samples/wbstego/wbsteg_mix_pass_1.bmp",
59
- "samples/wbstego/wbsteg_mix_pass_1_even.bmp",
60
- "samples/wbstego/wbsteg_mix_pass_foobar.bmp",
61
- "samples/wbstego/wbsteg_mix_pass_pass.bmp",
62
- "samples/wbstego/wbsteg_mixenc_pass_pass_even.bmp",
63
- "samples/wbstego/wbsteg_noenc.bmp",
64
- "samples/wbstego/wbsteg_noenc.png",
65
- "samples/wbstego/wbsteg_noenc_.bmp",
66
- "samples/wbstego/wbsteg_noenc_17.bmp",
67
- "samples/wbstego/wbsteg_noenc__.bmp",
68
- "samples/wbstego/wbsteg_noenc_even.bmp",
69
- "samples/wbstego/wbsteg_noenc_even2.bmp",
70
- "samples/wbstego/wbsteg_noenc_even_17.bmp",
71
- "samples/wbstego/wbsteg_noenc_even_17_.bmp",
72
- "samples/wbstego/wbsteg_noenc_ext_ABC.bmp",
73
- "samples/wbstego/wbsteg_rijndael_pass_1.bmp",
74
- "samples/wbstego/wbsteg_rijndael_pass_pass.bmp",
75
- "samples/wbstego/wbsteg_rijndael_pass_pass_even.bmp",
76
- "samples/wbstego/wbsteg_twofish_pass_1.bmp",
77
- "samples/wechall/5ZMGcCLxpcpsru03.g00000010.png",
78
- "samples/wechall/5ZMGcCLxpcpsru03.png",
79
- "samples/wechall/stegano1.bmp",
80
51
  "spec/bin_spec.rb",
81
52
  "spec/camouflage_spec.rb",
82
53
  "spec/cats_spec.rb",
@@ -93,6 +64,7 @@ Gem::Specification.new do |s|
93
64
  "spec/r3g2b3_spec.rb",
94
65
  "spec/simple_spec.rb",
95
66
  "spec/spec_helper.rb",
67
+ "spec/steganography_png_spec.rb",
96
68
  "spec/wbstego_spec.rb",
97
69
  "spec/wechall_spec.rb",
98
70
  "spec/zlib_spec.rb",
@@ -103,7 +75,7 @@ Gem::Specification.new do |s|
103
75
  ]
104
76
  s.homepage = "http://github.com/zed-0xff/zsteg".freeze
105
77
  s.licenses = ["MIT".freeze]
106
- s.rubygems_version = "3.1.6".freeze
78
+ s.rubygems_version = "3.3.7".freeze
107
79
  s.summary = "Detect stegano-hidden data in PNG & BMP files.".freeze
108
80
 
109
81
  if s.respond_to? :specification_version then
@@ -111,14 +83,14 @@ Gem::Specification.new do |s|
111
83
  end
112
84
 
113
85
  if s.respond_to? :add_runtime_dependency then
114
- s.add_runtime_dependency(%q<zpng>.freeze, [">= 0.4.3"])
115
- 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"])
116
88
  s.add_runtime_dependency(%q<prime>.freeze, [">= 0"])
117
89
  s.add_development_dependency(%q<rspec>.freeze, [">= 0"])
118
90
  s.add_development_dependency(%q<juwelier>.freeze, [">= 0"])
119
91
  else
120
- s.add_dependency(%q<zpng>.freeze, [">= 0.4.3"])
121
- 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"])
122
94
  s.add_dependency(%q<prime>.freeze, [">= 0"])
123
95
  s.add_dependency(%q<rspec>.freeze, [">= 0"])
124
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.11
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: 2022-08-29 00:00:00.000000000 Z
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.3
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.3
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: '0'
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: '0'
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
@@ -119,36 +120,6 @@ files:
119
120
  - lib/zsteg/file_cmd.rb
120
121
  - lib/zsteg/masker.rb
121
122
  - lib/zsteg/result.rb
122
- - pngsteg.gemspec
123
- - samples/hackquest/crypt.bmp
124
- - samples/hackquest/square.bmp
125
- - samples/newbiecontest/alph1-surprise.bmp.7z
126
- - samples/wbstego/wbsteg_blowfish_pass_1.bmp
127
- - samples/wbstego/wbsteg_cast128_pass_1.bmp
128
- - samples/wbstego/wbsteg_enc_pass_pass.bmp
129
- - samples/wbstego/wbsteg_enc_pass_pass_even.bmp
130
- - samples/wbstego/wbsteg_mix_pass_1.bmp
131
- - samples/wbstego/wbsteg_mix_pass_1_even.bmp
132
- - samples/wbstego/wbsteg_mix_pass_foobar.bmp
133
- - samples/wbstego/wbsteg_mix_pass_pass.bmp
134
- - samples/wbstego/wbsteg_mixenc_pass_pass_even.bmp
135
- - samples/wbstego/wbsteg_noenc.bmp
136
- - samples/wbstego/wbsteg_noenc.png
137
- - samples/wbstego/wbsteg_noenc_.bmp
138
- - samples/wbstego/wbsteg_noenc_17.bmp
139
- - samples/wbstego/wbsteg_noenc__.bmp
140
- - samples/wbstego/wbsteg_noenc_even.bmp
141
- - samples/wbstego/wbsteg_noenc_even2.bmp
142
- - samples/wbstego/wbsteg_noenc_even_17.bmp
143
- - samples/wbstego/wbsteg_noenc_even_17_.bmp
144
- - samples/wbstego/wbsteg_noenc_ext_ABC.bmp
145
- - samples/wbstego/wbsteg_rijndael_pass_1.bmp
146
- - samples/wbstego/wbsteg_rijndael_pass_pass.bmp
147
- - samples/wbstego/wbsteg_rijndael_pass_pass_even.bmp
148
- - samples/wbstego/wbsteg_twofish_pass_1.bmp
149
- - samples/wechall/5ZMGcCLxpcpsru03.g00000010.png
150
- - samples/wechall/5ZMGcCLxpcpsru03.png
151
- - samples/wechall/stegano1.bmp
152
123
  - spec/bin_spec.rb
153
124
  - spec/camouflage_spec.rb
154
125
  - spec/cats_spec.rb
@@ -165,6 +136,7 @@ files:
165
136
  - spec/r3g2b3_spec.rb
166
137
  - spec/simple_spec.rb
167
138
  - spec/spec_helper.rb
139
+ - spec/steganography_png_spec.rb
168
140
  - spec/wbstego_spec.rb
169
141
  - spec/wechall_spec.rb
170
142
  - spec/zlib_spec.rb
@@ -191,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
191
163
  - !ruby/object:Gem::Version
192
164
  version: '0'
193
165
  requirements: []
194
- rubygems_version: 3.1.6
166
+ rubygems_version: 3.3.7
195
167
  signing_key:
196
168
  specification_version: 4
197
169
  summary: Detect stegano-hidden data in PNG & BMP files.
data/pngsteg.gemspec DELETED
@@ -1,65 +0,0 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
5
-
6
- Gem::Specification.new do |s|
7
- s.name = "pngsteg"
8
- s.version = "0.0.0"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Andrey \"Zed\" Zaikin"]
12
- s.date = "2013-01-06"
13
- s.email = "zed.0xff@gmail.com"
14
- s.executables = ["pngsteg"]
15
- s.files = [
16
- "Gemfile",
17
- "Gemfile.lock",
18
- "Rakefile",
19
- "VERSION",
20
- "bin/pngsteg",
21
- "lib/pngsteg.rb",
22
- "lib/pngsteg/checker.rb",
23
- "lib/pngsteg/cli.rb",
24
- "lib/pngsteg/extractor.rb",
25
- "samples/flower.png",
26
- "samples/flower_rgb1.png",
27
- "samples/flower_rgb2.png",
28
- "samples/flower_rgb3.png",
29
- "samples/flower_rgb4.png",
30
- "samples/flower_rgb5.png",
31
- "samples/flower_rgb6.png",
32
- "spec/samples_spec.rb",
33
- "spec/spec_helper.rb"
34
- ]
35
- s.homepage = "http://github.com/zed-0xff/pngsteg"
36
- s.licenses = ["MIT"]
37
- s.require_paths = ["lib"]
38
- s.rubygems_version = "1.8.24"
39
- s.summary = "Detect stegano-hidden data in PNG files"
40
-
41
- if s.respond_to? :specification_version then
42
- s.specification_version = 3
43
-
44
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
- s.add_runtime_dependency(%q<zpng>, [">= 0.2.1"])
46
- s.add_development_dependency(%q<rspec>, [">= 2.8.0"])
47
- s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
48
- s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
49
- s.add_development_dependency(%q<awesome_print>, [">= 0"])
50
- else
51
- s.add_dependency(%q<zpng>, [">= 0.2.1"])
52
- s.add_dependency(%q<rspec>, [">= 2.8.0"])
53
- s.add_dependency(%q<bundler>, [">= 1.0.0"])
54
- s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
55
- s.add_dependency(%q<awesome_print>, [">= 0"])
56
- end
57
- else
58
- s.add_dependency(%q<zpng>, [">= 0.2.1"])
59
- s.add_dependency(%q<rspec>, [">= 2.8.0"])
60
- s.add_dependency(%q<bundler>, [">= 1.0.0"])
61
- s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
62
- s.add_dependency(%q<awesome_print>, [">= 0"])
63
- end
64
- end
65
-
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file