Skip to content

Instantly share code, notes, and snippets.

@stevenswafford
Created June 3, 2015 06:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save stevenswafford/f94a3b0cc8e9309eab24 to your computer and use it in GitHub Desktop.
Save stevenswafford/f94a3b0cc8e9309eab24 to your computer and use it in GitHub Desktop.
Zipdump allows you to inspect ZIP files.
#!/usr/bin/env python
import optparse
import zipfile
import hashlib
import signal
import sys
import os
import cStringIO
import textwrap
STANDARD_MALWARE_SAMPLE_PASSWORD = 'infected'
QUOTE = '"'
def PrintManual():
manual = '''
Manual:
zipdump allows you to inspect ZIP files.
By default, the output is a csv file with the filename, encrypted flag, md5 and timestamp of each file inside the zip file.
Example: zipdump.py test.zip
Output:
zipfilename;encrypted;MD5;Timestamp
file1.txt;0;23ac1d8f3680efd3b31fb988e6438cc5;2014-08-10 14:11:32
file2.txt;0;c749903dd4a3e98b26c7f58ec93fe808;2014-08-10 14:11:50
zipdump also accepts stdin, then you have to use - as filename:
cat test.zip | zipdump.py -
The contents of the compressed files can be dumped (-d), hexdumped (-x) and ascii dumped (-a).
If you do not provide a filename to dump, the first file is dumped.
Example:
zipdump.py -a files.zip
00000000: 46 69 72 73 74 20 66 69 6C 65 21 0D 0A 0D 0A 53 First file!....S
00000010: 6F 6D 65 20 64 61 74 61 2E ome data.
Example with the filename to dump:
zipdump.py -a files.zip file2.txt
00000000: 53 65 63 6F 6E 64 65 20 66 69 6C 65 21 0D 0A 0D Seconde file!...
00000010: 0A 53 6F 6D 65 20 6D 6F 72 65 20 64 61 74 61 2E .Some more data.
You can select another separator than ; for the csv file with option -s.
Option -o allows you to write the csv file to disk.
'''
for line in manual.split('\n'):
print(textwrap.fill(line))
def FixPipe():
try:
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
except:
pass
def ToString(value):
if type(value) == type(''):
return value
else:
return str(value)
def Quote(value, separator, quote):
value = ToString(value)
if separator in value:
return quote + value + quote
else:
return value
def MakeCSVLine(row, separator, quote):
return separator.join([Quote(value, separator, quote) for value in row])
def Print(line, f):
if f == None:
print(line)
else:
f.write(line +'\n')
dumplinelength = 16
# CIC: Call If Callable
def CIC(expression):
if callable(expression):
return expression()
else:
return expression
# IFF: IF Function
def IFF(expression, valueTrue, valueFalse):
if expression:
return CIC(valueTrue)
else:
return CIC(valueFalse)
class cDumpStream():
def __init__(self):
self.text = ''
def Addline(self, line):
if line != '':
self.text += line + '\n'
def Content(self):
return self.text
def HexDump(data):
oDumpStream = cDumpStream()
hexDump = ''
for i, b in enumerate(data):
if i % dumplinelength == 0 and hexDump != '':
oDumpStream.Addline(hexDump)
hexDump = ''
hexDump += IFF(hexDump == '', '', ' ') + '%02X' % ord(b)
oDumpStream.Addline(hexDump)
return oDumpStream.Content()
def CombineHexAscii(hexDump, asciiDump):
if hexDump == '':
return ''
return hexDump + ' ' + (' ' * (3 * (16 - len(asciiDump)))) + asciiDump
def HexAsciiDump(data):
oDumpStream = cDumpStream()
hexDump = ''
asciiDump = ''
for i, b in enumerate(data):
if i % dumplinelength == 0:
if hexDump != '':
oDumpStream.Addline(CombineHexAscii(hexDump, asciiDump))
hexDump = '%08X:' % i
asciiDump = ''
hexDump+= ' %02X' % ord(b)
asciiDump += IFF(ord(b) >= 32 and ord(b), b, '.')
oDumpStream.Addline(CombineHexAscii(hexDump, asciiDump))
return oDumpStream.Content()
#Fix for http://bugs.python.org/issue11395
def StdoutWriteChunked(data):
while data != '':
sys.stdout.write(data[0:10000])
sys.stdout.flush()
data = data[10000:]
def ZIPDump(zipfilename, targetfilename, options):
FixPipe()
if zipfilename == '-':
if sys.platform == 'win32':
import msvcrt
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
oZipfile = zipfile.ZipFile(cStringIO.StringIO(sys.stdin.read()), 'r')
else:
oZipfile = zipfile.ZipFile(zipfilename, 'r')
if options.output:
fOut = open(options.output, 'w')
else:
fOut = None
if options.dump or options.hexdump or options.asciidump:
if options.dump:
DumpFunction = lambda x:x
if sys.platform == 'win32':
import msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
elif options.hexdump:
DumpFunction = HexDump
else:
DumpFunction = HexAsciiDump
for oZipInfo in oZipfile.infolist():
if targetfilename == None or targetfilename == oZipInfo.filename:
file = oZipfile.open(oZipInfo, 'r', STANDARD_MALWARE_SAMPLE_PASSWORD)
StdoutWriteChunked(DumpFunction(file.read()))
file.close()
if targetfilename == None:
break
else:
Print(MakeCSVLine(['zipfilename', 'encrypted', 'MD5', 'Timestamp'], options.separator, QUOTE), fOut)
for oZipInfo in oZipfile.infolist():
if targetfilename == None or targetfilename == oZipInfo.filename:
file = oZipfile.open(oZipInfo, 'r', STANDARD_MALWARE_SAMPLE_PASSWORD)
filehash = hashlib.md5(file.read()).hexdigest()
file.close()
Print(MakeCSVLine([oZipInfo.filename, oZipInfo.flag_bits & 1, filehash, '%04d-%02d-%02d %02d:%02d:%02d' % oZipInfo.date_time], options.separator, QUOTE), fOut)
if fOut:
fOut.close()
oZipfile.close()
def Main():
oParser = optparse.OptionParser(usage='usage: %prog [options] zipfile [filename]\n' + __description__, version='%prog ' + __version__)
oParser.add_option('-s', '--separator', default=';', help='Separator character (default ;)')
oParser.add_option('-o', '--output', help='Output to file')
oParser.add_option('-d', '--dump', action='store_true', default=False, help='perform dump')
oParser.add_option('-x', '--hexdump', action='store_true', default=False, help='perform hex dump')
oParser.add_option('-a', '--asciidump', action='store_true', default=False, help='perform ascii dump')
oParser.add_option('-m', '--man', action='store_true', default=False, help='print manual')
(options, args) = oParser.parse_args()
if options.man:
oParser.print_help()
PrintManual()
return
if len(args) < 1:
oParser.print_help()
print('')
print(' Source code put in the public domain by Didier Stevens, no Copyright')
print(' Use at your own risk')
print(' https://DidierStevens.com')
return
elif len(args) == 1:
ZIPDump(args[0], None, options)
else:
ZIPDump(args[0], args[1], options)
if __name__ == '__main__':
Main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment