zipography 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1827f4d1fd43192ac37e029981ab04de876a87d8a9e91b044873d671de1738cc
4
+ data.tar.gz: c2de37c58542aca50915c71712cda9f420cc2dded28cf95a9af1c369465774f7
5
+ SHA512:
6
+ metadata.gz: 960794c6d6a3cdb205c54e4b9ef1c38d840ca061e6fd547340c1122c88310312a19f2ff40e481a01d365bc8e76076fe01ee6d8a1fe25e243ce7e086c271fd24a
7
+ data.tar.gz: 381072f98336e90d7dc953821a7c5812cf736b09a0f19216915acdd5c82a498e7fb1f4e4c911c2519f8222484a26305fbd1becac4ee9fa15907adc61c2a00b66
@@ -0,0 +1,78 @@
1
+ # Zipography
2
+
3
+ Steganography with zip archives: hide a blob of data within an
4
+ archive. For typical file archivers (7-zip, WinRAR, File Roller, &c) or
5
+ file managers (Windows Explorer), the blob is invisible.
6
+
7
+ $ gem install zipography
8
+
9
+ Limitations:
10
+
11
+ * single blob only (but you can just add another .zip as a blob);
12
+ * doesn't work w/ zip64 files (this means 4GB max for an archive+blob
13
+ combo);
14
+ * corrupts zip spec v6.2+ files that use *central directory*
15
+ encryption;
16
+ * memory hungry, for it's just a toy, not a serious spy tool.
17
+
18
+ ## Usage
19
+
20
+ Say we have a .zip that contains 2 files:
21
+
22
+ ~~~
23
+ $ du orig.zip
24
+ 12K orig.zip
25
+
26
+ $ bsdtar tf orig.zip
27
+ The Celebrated Jumping Frog of Calaveras County.txt
28
+ What You Want.txt
29
+ ~~~
30
+
31
+ Inject a picture into the archive:
32
+
33
+ $ zipography-inject orig.zip blob1.png > 1.zip
34
+
35
+ It it visible? No:
36
+
37
+ ~~~
38
+ $ bsdtar tf 1.zip
39
+ The Celebrated Jumping Frog of Calaveras County.txt
40
+ What You Want.txt
41
+
42
+ $ du 1.zip
43
+ 30K 1.zip
44
+ ~~~
45
+
46
+ (`unzip -l` prints the same, but in a much verbose form.)
47
+
48
+ Check if we have the picture in the archive:
49
+
50
+ ~~~
51
+ $ zipography-info 1.zip
52
+ Payload size: 18313
53
+ Adler32: 0x6812d9f
54
+ Version: 1
55
+ Valid: true
56
+ ~~~
57
+
58
+ Extract it:
59
+
60
+ ~~~
61
+ $ zipography-extract 1.zip > 1.png
62
+ $ xdg-open !$
63
+ ~~~
64
+
65
+ <img src='test/blob1.png'>
66
+
67
+ ## How does it work?
68
+
69
+ A blob is injected after a file section right before the 1st *central
70
+ directory header*. After that, a pointer in an *end of central
71
+ directory* record is updated to compensate the shift of the *central
72
+ directory header*.
73
+
74
+ <img src='doc/zip.svg'>
75
+
76
+ ## License
77
+
78
+ MIT.
data/lib.rb ADDED
@@ -0,0 +1,80 @@
1
+ require 'zlib'
2
+ require 'bindata'
3
+
4
+ module Zipography
5
+
6
+ HEADER_SIZE = 9 # bytes
7
+ class HiddenBlob < BinData::Record
8
+ endian :little
9
+
10
+ uint32 :checksum
11
+ uint32 :len
12
+ uint8 :version, initial_value: 1
13
+ end
14
+
15
+ def checksum s; Zlib.adler32(s); end
16
+
17
+ def blob_make file
18
+ payload = File.read file
19
+ header = HiddenBlob.new len: File.stat(file).size,
20
+ checksum: checksum(payload)
21
+ [payload.force_encoding('ASCII-8BIT'), header.to_binary_s].join
22
+ end
23
+
24
+ class MyZip
25
+ def initialize file
26
+ @file = file
27
+ @buf = File.read file
28
+ @eocd = end_of_central_dir
29
+ @first_cdh = first_central_dir_header
30
+ end
31
+
32
+ def first_central_dir_header
33
+ @buf.index [0x02014b50].pack('V')
34
+ end
35
+
36
+ def end_of_central_dir
37
+ pos = @buf.rindex [0x06054b50].pack('V')
38
+ fail 'not a zip file' unless pos
39
+ pos
40
+ end
41
+
42
+ # start of central dir offset
43
+ def offset
44
+ @buf.slice(@eocd+16, 4).unpack('V').first
45
+ end
46
+
47
+ # very crude: instead of an intelligent parsing of eocd, modifying
48
+ # an offset, replacing eocd, we just change the offset. this won't
49
+ # fly for zip64 files
50
+ def repack data
51
+ [
52
+ @buf.slice(0, @first_cdh),
53
+ data,
54
+ # just before the offset of start of central dir
55
+ @buf.slice(@first_cdh, (@eocd+16) - @first_cdh),
56
+ # inject new offset
57
+ [offset + data.bytesize].pack('V'),
58
+ # the rest
59
+ @buf.slice(@eocd+16+4, @buf.size)
60
+ ]
61
+ end
62
+
63
+ def blob
64
+ payload = ''
65
+ header = {}
66
+ File.open(@file) do |f|
67
+ f.seek(offset-HEADER_SIZE)
68
+ header = HiddenBlob.read f
69
+ f.seek(offset-HEADER_SIZE-header.len)
70
+ payload = f.read header.len
71
+ end
72
+ { header: header, payload: payload }
73
+ end
74
+
75
+ def payload_valid? blob
76
+ blob[:header][:checksum] == checksum(blob[:payload])
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative './lib'
4
+ include Zipography
5
+
6
+ z = MyZip.new ARGV[0]
7
+ blob = z.blob
8
+ z.payload_valid?(blob) ? $stdout.write(blob[:payload]) : exit(1)
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative './lib'
4
+ include Zipography
5
+
6
+ z = MyZip.new ARGV[0]
7
+ blob = z.blob
8
+
9
+ puts "Payload size: #{blob[:header][:len]}"
10
+ puts "Adler32: 0x" + blob[:header][:checksum].to_i.to_s(16)
11
+ puts "Version: #{blob[:header][:version]}"
12
+ puts "Valid: #{z.payload_valid?(blob)}"
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative './lib'
4
+ include Zipography
5
+
6
+ abort "Usage: #{File.basename __FILE__} old.zip blob > new.zip" if ARGV.size < 2
7
+
8
+ z = MyZip.new ARGV[0]
9
+ data = blob_make ARGV[1]
10
+ z.repack(data).each {|buf| $stdout.write buf }
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zipography
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Gromnitsky
8
+ autorequire:
9
+ bindir: "."
10
+ cert_chain: []
11
+ date: 2020-08-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bindata
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.4.8
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.4.8
27
+ description: |
28
+ Steganography with zip archives: hide a blob of data within an
29
+ archive. For typical file archivers or file managers, the blob remains
30
+ invisible.
31
+ email: alexander.gromnitsky@gmail.com
32
+ executables:
33
+ - zipography-extract
34
+ - zipography-info
35
+ - zipography-inject
36
+ extensions: []
37
+ extra_rdoc_files: []
38
+ files:
39
+ - "./zipography-extract"
40
+ - "./zipography-info"
41
+ - "./zipography-inject"
42
+ - README.md
43
+ - lib.rb
44
+ - zipography-extract
45
+ - zipography-info
46
+ - zipography-inject
47
+ homepage: https://github.com/gromnitsky/zipography
48
+ licenses:
49
+ - MIT
50
+ metadata: {}
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 2.4.0
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubygems_version: 3.1.2
67
+ signing_key:
68
+ specification_version: 4
69
+ summary: Steganography with zip archives
70
+ test_files: []