zipography 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +78 -0
- data/lib.rb +80 -0
- data/zipography-extract +8 -0
- data/zipography-info +12 -0
- data/zipography-inject +10 -0
- metadata +70 -0
checksums.yaml
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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
|
data/zipography-extract
ADDED
data/zipography-info
ADDED
@@ -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)}"
|
data/zipography-inject
ADDED
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: []
|