AlexanderMak

7z2johnnew

Jul 30th, 2024 (edited)
41
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 31.21 KB | None | 0 0
  1. #
  2. # $Id$
  3. #
  4. """Read from and write to 7zip format archives.
  5. """
  6.  
  7. from binascii import unhexlify
  8. from datetime import datetime
  9. try:
  10.     import pylzma
  11.  
  12. except ImportError:
  13.     pass
  14. from struct import pack, unpack
  15. from zlib import crc32
  16. import zlib
  17. import bz2
  18. import binascii
  19. from io import BytesIO
  20. import sys
  21. import os
  22.  
  23. try:
  24.     from io import BytesIO
  25. except ImportError:
  26.     from io import StringIO as BytesIO
  27. try:
  28.     from functools import reduce
  29. except ImportError:
  30.     pass
  31.  
  32. try:
  33.     from pytz import UTC
  34. except ImportError:
  35.     from datetime import timedelta, tzinfo
  36.  
  37.     ZERO = timedelta(0)
  38.  
  39.     class UTC(tzinfo):
  40.         """UTC"""
  41.  
  42.     def utcoffset(self, dt):
  43.       return ZERO
  44.  
  45.     def tzname(self, dt):
  46.       return "UTC"
  47.  
  48.     def dst(self, dt):
  49.       return ZERO
  50.  
  51. # Handle unicode conversion for strings
  52. def convert_to_str(s):
  53.   """Converts a string to str, handling Python 2 and 3 compatibility.
  54.  
  55.  Args:
  56.      s: The string to convert.
  57.  
  58.  Returns:
  59.      The str string.
  60.  """
  61.   return str(s)
  62.  
  63. unicode = convert_to_str  # Assign for backwards compatibility (optional)
  64.  
  65. READ_BLOCKSIZE                   = 16384
  66.  
  67. MAGIC_7Z                         = unhexlify('377abcaf271c')  # '7z\xbc\xaf\x27\x1c'
  68.  
  69. PROPERTY_END                     = unhexlify('00')  # '\x00'
  70. PROPERTY_HEADER                  = unhexlify('01')  # '\x01'
  71. PROPERTY_ARCHIVE_PROPERTIES      = unhexlify('02')  # '\x02'
  72. PROPERTY_ADDITIONAL_STREAMS_INFO = unhexlify('03')  # '\x03'
  73. PROPERTY_MAIN_STREAMS_INFO       = unhexlify('04')  # '\x04'
  74. PROPERTY_FILES_INFO              = unhexlify('05')  # '\x05'
  75. PROPERTY_PACK_INFO               = unhexlify('06')  # '\x06'
  76. PROPERTY_UNPACK_INFO             = unhexlify('07')  # '\x07'
  77. PROPERTY_SUBSTREAMS_INFO         = unhexlify('08')  # '\x08'
  78. PROPERTY_SIZE                    = unhexlify('09')  # '\x09'
  79. PROPERTY_CRC                     = unhexlify('0a')  # '\x0a'
  80. PROPERTY_FOLDER                  = unhexlify('0b')  # '\x0b'
  81. PROPERTY_CODERS_UNPACK_SIZE      = unhexlify('0c')  # '\x0c'
  82. PROPERTY_NUM_UNPACK_STREAM       = unhexlify('0d')  # '\x0d'
  83. PROPERTY_EMPTY_STREAM            = unhexlify('0e')  # '\x0e'
  84. PROPERTY_EMPTY_FILE              = unhexlify('0f')  # '\x0f'
  85. PROPERTY_ANTI                    = unhexlify('10')  # '\x10'
  86. PROPERTY_NAME                    = unhexlify('11')  # '\x11'
  87. PROPERTY_CREATION_TIME           = unhexlify('12')  # '\x12'
  88. PROPERTY_LAST_ACCESS_TIME        = unhexlify('13')  # '\x13'
  89. PROPERTY_LAST_WRITE_TIME         = unhexlify('14')  # '\x14'
  90. PROPERTY_ATTRIBUTES              = unhexlify('15')  # '\x15'
  91. PROPERTY_COMMENT                 = unhexlify('16')  # '\x16'
  92. PROPERTY_ENCODED_HEADER          = unhexlify('17')  # '\x17'
  93.  
  94. COMPRESSION_METHOD_COPY          = unhexlify('00')  # '\x00'
  95. COMPRESSION_METHOD_LZMA          = unhexlify('03')  # '\x03'
  96. COMPRESSION_METHOD_CRYPTO        = unhexlify('06')  # '\x06'
  97. COMPRESSION_METHOD_MISC          = unhexlify('04')  # '\x04'
  98. COMPRESSION_METHOD_MISC_ZIP      = unhexlify('0401')  # '\x04\x01'
  99. COMPRESSION_METHOD_MISC_BZIP     = unhexlify('0402')  # '\x04\x02'
  100. COMPRESSION_METHOD_7Z_AES256_SHA256 = unhexlify('06f10701')  # '\x06\xf1\x07\x01'
  101.  
  102. # number of seconds between 1601/01/01 and 1970/01/01 (UTC)
  103. # used to adjust 7z FILETIME to Python timestamp
  104. TIMESTAMP_ADJUST                 = -11644473600
  105.  
  106. def toTimestamp(filetime):
  107.     """Convert 7z FILETIME to Python timestamp."""
  108.     # FILETIME is 100-nanosecond intervals since 1601/01/01 (UTC)
  109.     return (filetime / 10000000.0) + TIMESTAMP_ADJUST
  110.  
  111. class ArchiveError(Exception):
  112.     pass
  113.  
  114. class FormatError(ArchiveError):
  115.     pass
  116.  
  117. class EncryptedArchiveError(ArchiveError):
  118.     pass
  119.  
  120. class UnsupportedCompressionMethodError(ArchiveError):
  121.     pass
  122.  
  123. class DecryptionError(ArchiveError):
  124.     pass
  125.  
  126. class NoPasswordGivenError(DecryptionError):
  127.     pass
  128.  
  129. class WrongPasswordError(DecryptionError):
  130.     pass
  131.  
  132. class ArchiveTimestamp():
  133.     """Windows FILETIME timestamp."""
  134.  
  135.     def __repr__(self):
  136.         return '%s(%d)' % (type(self).__name__, self)
  137.  
  138.     def as_datetime(self):
  139.         """Convert FILETIME to Python datetime object."""
  140.         return datetime.fromtimestamp(toTimestamp(self), UTC)
  141.  
  142. class Base(object):
  143.     """ base class with support for various basic read/write functions """
  144.  
  145.     def _readReal64Bit(self, file):
  146.         res = file.read(8)
  147.         a, b = unpack('<LL', res)
  148.         return b << 32 | a, res
  149.  
  150.     def _read64Bit(self, file):
  151.         b = ord(file.read(1))
  152.         mask = 0x80
  153.         for i in range(8):
  154.             if b & mask == 0:
  155.                 bytes = list(unpack('%dB' % i, file.read(i)))
  156.                 bytes.reverse()
  157.                 value = (bytes and reduce(lambda x, y: x << 8 | y, bytes)) or 0
  158.                 highpart = b & (mask - 1)
  159.                 return value + (highpart << (i * 8))
  160.  
  161.             mask >>= 1
  162.  
  163.     def _readBoolean(self, file, count, checkall=0):
  164.         if checkall:
  165.             alldefined = file.read(1)
  166.             if alldefined != unhexlify('00'):
  167.                 return [True] * count
  168.  
  169.         result = []
  170.         b = 0
  171.         mask = 0
  172.         for i in range(count):
  173.             if mask == 0:
  174.                 b = ord(file.read(1))
  175.                 mask = 0x80
  176.             result.append(b & mask != 0)
  177.             mask >>= 1
  178.  
  179.         return result
  180.  
  181.     def checkcrc(self, crc, data):
  182.         check = crc32(data) & 0xffffffff
  183.         return crc == check
  184.  
  185.  
  186. class PackInfo(Base):
  187.     """ informations about packed streams """
  188.  
  189.     def __init__(self, file):
  190.         self.packpos = self._read64Bit(file)
  191.         self.numstreams = self._read64Bit(file)
  192.         id = file.read(1)
  193.         if id == PROPERTY_SIZE:
  194.             self.packsizes = [self._read64Bit(file) for x in range(self.numstreams)]
  195.             id = file.read(1)
  196.  
  197.             if id == PROPERTY_CRC:
  198.                 self.crcs = [self._read64Bit(file) for x in range(self.numstreams)]
  199.                 id = file.read(1)
  200.  
  201.         if id != PROPERTY_END:
  202.             raise FormatError('end id expected but %s found' % repr(id))
  203.  
  204. class Folder(Base):
  205.     """ a "Folder" represents a stream of compressed data """
  206.  
  207.     def __init__(self, file):
  208.         numcoders = self._read64Bit(file)
  209.         self.numcoders = numcoders
  210.         self.coders = []
  211.         self.digestdefined = False
  212.         totalin = 0
  213.         self.totalout = 0
  214.         for i in range(numcoders):
  215.             while True:
  216.                 b = ord(file.read(1))
  217.                 methodsize = b & 0xf
  218.                 issimple = b & 0x10 == 0
  219.                 noattributes = b & 0x20 == 0
  220.                 last_alternative = b & 0x80 == 0
  221.                 c = {}
  222.                 c['method'] = file.read(methodsize)
  223.                 if not issimple:
  224.                     c['numinstreams'] = self._read64Bit(file)
  225.                     c['numoutstreams'] = self._read64Bit(file)
  226.                 else:
  227.                     c['numinstreams'] = 1
  228.                     c['numoutstreams'] = 1
  229.                 totalin += c['numinstreams']
  230.                 self.totalout += c['numoutstreams']
  231.                 if not noattributes:
  232.                     c['properties'] = file.read(self._read64Bit(file))
  233.                 self.coders.append(c)
  234.                 if last_alternative:
  235.                     break
  236.  
  237.         numbindpairs = self.totalout - 1
  238.         self.bindpairs = []
  239.         for i in range(numbindpairs):
  240.             self.bindpairs.append((self._read64Bit(file), self._read64Bit(file), ))
  241.  
  242.         numpackedstreams = totalin - numbindpairs
  243.         self.numpackedstreams = numpackedstreams
  244.         self.packed_indexes = []
  245.         if numpackedstreams == 1:
  246.             for i in range(totalin):
  247.                 if self.findInBindPair(i) < 0:
  248.                     self.packed_indexes.append(i)
  249.         elif numpackedstreams > 1:
  250.             for i in range(numpackedstreams):
  251.                 self.packed_indexes.append(self._read64Bit(file))
  252.  
  253.     def getUnpackSize(self):
  254.         if not self.unpacksizes:
  255.             return 0
  256.  
  257.         r = list(range(len(self.unpacksizes)))
  258.         r.reverse()
  259.         for i in r:
  260.             if self.findOutBindPair(i):
  261.                 return self.unpacksizes[i]
  262.  
  263.         raise TypeError('not found')
  264.  
  265.     def findInBindPair(self, index):
  266.         for idx in range(len(self.bindpairs)):
  267.             a, b = self.bindpairs[idx]
  268.             if a == index:
  269.                 return idx
  270.         return -1
  271.  
  272.     def findOutBindPair(self, index):
  273.         for idx in range(len(self.bindpairs)):
  274.             a, b = self.bindpairs[idx]
  275.             if b == index:
  276.                 return idx
  277.         return -1
  278.  
  279. class Digests(Base):
  280.     """ holds a list of checksums """
  281.  
  282.     def __init__(self, file, count):
  283.         self.defined = self._readBoolean(file, count, checkall=1)
  284.         self.crcs = [unpack('<L', file.read(4))[0] for x in range(count)]
  285.  
  286. UnpackDigests = Digests
  287.  
  288. class UnpackInfo(Base):
  289.     """ combines multiple folders """
  290.  
  291.     def __init__(self, file):
  292.         id = file.read(1)
  293.         if id != PROPERTY_FOLDER:
  294.             raise FormatError('folder id expected but %s found' % repr(id))
  295.         self.numfolders = self._read64Bit(file)
  296.         self.folders = []
  297.         external = file.read(1)
  298.         if external == unhexlify('00'):
  299.             self.folders = [Folder(file) for x in range(self.numfolders)]
  300.         elif external == unhexlify('01'):
  301.             self.datastreamidx = self._read64Bit(file)
  302.         else:
  303.             raise FormatError('0x00 or 0x01 expected but %s found' % repr(external))
  304.  
  305.         id = file.read(1)
  306.         if id != PROPERTY_CODERS_UNPACK_SIZE:
  307.             raise FormatError('coders unpack size id expected but %s found' % repr(id))
  308.  
  309.         for folder in self.folders:
  310.             folder.unpacksizes = [self._read64Bit(file) for x in range(folder.totalout)]
  311.  
  312.         id = file.read(1)
  313.         if id == PROPERTY_CRC:
  314.             digests = UnpackDigests(file, self.numfolders)
  315.             for idx in range(self.numfolders):
  316.                 folder = self.folders[idx]
  317.                 folder.digestdefined = digests.defined[idx]
  318.                 folder.crc = digests.crcs[idx]
  319.  
  320.             id = file.read(1)
  321.  
  322.         if id != PROPERTY_END:
  323.             raise FormatError('end id expected but %s found' % repr(id))
  324.  
  325. class SubstreamsInfo(Base):
  326.     """ defines the substreams of a folder """
  327.  
  328.     def __init__(self, file, numfolders, folders):
  329.         self.digests = []
  330.         self.digestsdefined = []
  331.         id = file.read(1)
  332.         if id == PROPERTY_NUM_UNPACK_STREAM:
  333.             self.numunpackstreams = [self._read64Bit(file) for x in range(numfolders)]
  334.             id = file.read(1)
  335.         else:
  336.             self.numunpackstreams = []
  337.             for idx in range(numfolders):
  338.                 self.numunpackstreams.append(1)
  339.  
  340.         if id == PROPERTY_SIZE:
  341.             sum = 0
  342.             self.unpacksizes = []
  343.             for i in range(len(self.numunpackstreams)):
  344.                 for j in range(1, self.numunpackstreams[i]):
  345.                     size = self._read64Bit(file)
  346.                     self.unpacksizes.append(size)
  347.                     sum += size
  348.                 self.unpacksizes.append(folders[i].getUnpackSize() - sum)
  349.  
  350.             id = file.read(1)
  351.  
  352.         numdigests = 0
  353.         numdigeststotal = 0
  354.         for i in range(numfolders):
  355.             numsubstreams = self.numunpackstreams[i]
  356.             if numsubstreams != 1 or not folders[i].digestdefined:
  357.                 numdigests += numsubstreams
  358.             numdigeststotal += numsubstreams
  359.  
  360.         if id == PROPERTY_CRC:
  361.             digests = Digests(file, numdigests)
  362.             didx = 0
  363.             for i in range(numfolders):
  364.                 folder = folders[i]
  365.                 numsubstreams = self.numunpackstreams[i]
  366.                 if numsubstreams == 1 and folder.digestdefined:
  367.                     self.digestsdefined.append(True)
  368.                     self.digests.append(folder.crc)
  369.                 else:
  370.                     for j in range(numsubstreams):
  371.                         self.digestsdefined.append(digests.defined[didx])
  372.                         self.digests.append(digests.crcs[didx])
  373.                         didx += 1
  374.  
  375.             id = file.read(1)
  376.  
  377.         if id != PROPERTY_END:
  378.             raise FormatError('end id expected but %r found' % id)
  379.  
  380.         if not self.digestsdefined:
  381.             self.digestsdefined = [False] * numdigeststotal
  382.             self.digests = [0] * numdigeststotal
  383.  
  384. class StreamsInfo(Base):
  385.     """ informations about compressed streams """
  386.  
  387.     def __init__(self, file):
  388.         id = file.read(1)
  389.         if id == PROPERTY_PACK_INFO:
  390.             self.packinfo = PackInfo(file)
  391.             id = file.read(1)
  392.  
  393.         if id == PROPERTY_UNPACK_INFO:
  394.             self.unpackinfo = UnpackInfo(file)
  395.             id = file.read(1)
  396.  
  397.         if id == PROPERTY_SUBSTREAMS_INFO:
  398.             self.substreamsinfo = SubstreamsInfo(file, self.unpackinfo.numfolders, self.unpackinfo.folders)
  399.             id = file.read(1)
  400.  
  401.         if id != PROPERTY_END:
  402.             raise FormatError('end id expected but %s found' % repr(id))
  403.  
  404. class FilesInfo(Base):
  405.     """ holds file properties """
  406.  
  407.     def _readTimes(self, file, files, name):
  408.         defined = self._readBoolean(file, len(files), checkall=1)
  409.  
  410.         # NOTE: the "external" flag is currently ignored, should be 0x00
  411.         external = file.read(1)
  412.         for i in range(len(files)):
  413.             if defined[i]:
  414.                 files[i][name] = ArchiveTimestamp(self._readReal64Bit(file)[0])
  415.             else:
  416.                 files[i][name] = None
  417.  
  418.     def __init__(self, file):
  419.         self.numfiles = self._read64Bit(file)
  420.         self.files = [{'emptystream': False} for x in range(self.numfiles)]
  421.         numemptystreams = 0
  422.         while True:
  423.             typ = self._read64Bit(file)
  424.             if typ > 255:
  425.                 raise FormatError('invalid type, must be below 256, is %d' % typ)
  426.  
  427.             typ = pack('B', typ)
  428.             if typ == PROPERTY_END:
  429.                 break
  430.  
  431.             size = self._read64Bit(file)
  432.             buffer = BytesIO(file.read(size))
  433.             if typ == PROPERTY_EMPTY_STREAM:
  434.                 isempty = self._readBoolean(buffer, self.numfiles)
  435.                 list(map(lambda x, y: x.update({'emptystream': y}), self.files, isempty))
  436.                 for x in isempty:
  437.                     if x: numemptystreams += 1
  438.                 emptyfiles = [False] * numemptystreams
  439.                 antifiles = [False] * numemptystreams
  440.             elif typ == PROPERTY_EMPTY_FILE:
  441.                 emptyfiles = self._readBoolean(buffer, numemptystreams)
  442.             elif typ == PROPERTY_ANTI:
  443.                 antifiles = self._readBoolean(buffer, numemptystreams)
  444.             elif typ == PROPERTY_NAME:
  445.                 external = buffer.read(1)
  446.                 if external != unhexlify('00'):
  447.                     self.dataindex = self._read64Bit(buffer)
  448.                     # XXX: evaluate external
  449.                     raise NotImplementedError
  450.  
  451.                 for f in self.files:
  452.                     name = ''
  453.                     while True:
  454.                         ch = buffer.read(2)
  455.                         if ch == unhexlify('0000'):
  456.                             f['filename'] = name
  457.                             break
  458.                         name += ch.decode('utf-16')
  459.             elif typ == PROPERTY_CREATION_TIME:
  460.                 self._readTimes(buffer, self.files, 'creationtime')
  461.             elif typ == PROPERTY_LAST_ACCESS_TIME:
  462.                 self._readTimes(buffer, self.files, 'lastaccesstime')
  463.             elif typ == PROPERTY_LAST_WRITE_TIME:
  464.                 self._readTimes(buffer, self.files, 'lastwritetime')
  465.             elif typ == PROPERTY_ATTRIBUTES:
  466.                 defined = self._readBoolean(buffer, self.numfiles, checkall=1)
  467.                 for i in range(self.numfiles):
  468.                     f = self.files[i]
  469.                     if defined[i]:
  470.                         f['attributes'] = unpack('<L', buffer.read(4))[0]
  471.                     else:
  472.                         f['attributes'] = None
  473.             else:
  474.                 raise FormatError('invalid type %r' % (typ))
  475.  
  476. class Header(Base):
  477.     """ the archive header """
  478.  
  479.     def __init__(self, file):
  480.         id = file.read(1)
  481.         if id == PROPERTY_ARCHIVE_PROPERTIES:
  482.             #self.properties = ArchiveProperties(file)
  483.             id = file.read(1)
  484.  
  485.         if id == PROPERTY_ADDITIONAL_STREAMS_INFO:
  486.             self.additional_streams = StreamsInfo(file)
  487.             id = file.read(1)
  488.  
  489.         if id == PROPERTY_MAIN_STREAMS_INFO:
  490.             self.main_streams = StreamsInfo(file)
  491.             id = file.read(1)
  492.  
  493.         if id == PROPERTY_FILES_INFO:
  494.             self.files = FilesInfo(file)
  495.             id = file.read(1)
  496.  
  497.         if id != PROPERTY_END:
  498.             raise FormatError('end id expected but %s found' % (repr(id)))
  499.  
  500. class ArchiveFile(Base):
  501.     """ wrapper around a file in the archive """
  502.  
  503.     def __init__(self, info, start, src_start, size, folder, archive, maxsize=None):
  504.         self.digest = None
  505.         self._archive = archive
  506.         self._file = archive._file
  507.         self._start = start
  508.         self._src_start = src_start
  509.         self._folder = folder
  510.         self.size = size
  511.         # maxsize is only valid for solid archives
  512.         self._maxsize = maxsize
  513.         for k, v in info.items():
  514.             setattr(self, k, v)
  515.         self.reset()
  516.         self._decoders = {
  517.             COMPRESSION_METHOD_COPY: '_read_copy',
  518.             COMPRESSION_METHOD_LZMA: '_read_lzma',
  519.             COMPRESSION_METHOD_MISC_ZIP: '_read_zip',
  520.             COMPRESSION_METHOD_MISC_BZIP: '_read_bzip',
  521.             COMPRESSION_METHOD_7Z_AES256_SHA256: '_read_7z_aes256_sha256',
  522.         }
  523.  
  524.     def _is_encrypted(self):
  525.         return COMPRESSION_METHOD_7Z_AES256_SHA256 in [x['method'] for x in self._folder.coders]
  526.  
  527.     def reset(self):
  528.         self.pos = 0
  529.  
  530.     def read(self):
  531.         if not self._folder.coders:
  532.             raise TypeError("file has no coder informations")
  533.  
  534.         data = None
  535.         for coder in self._folder.coders:
  536.             method = coder['method']
  537.             decoder = None
  538.             while method and decoder is None:
  539.                 decoder = self._decoders.get(method, None)
  540.                 method = method[:-1]
  541.  
  542.             if decoder is None:
  543.                 raise UnsupportedCompressionMethodError(repr(coder['method']))
  544.  
  545.             data = getattr(self, decoder)(coder, data)
  546.  
  547.         return data
  548.  
  549.     def _read_copy(self, coder, input):
  550.         if not input:
  551.             self._file.seek(self._src_start)
  552.             input = self._file.read(self.uncompressed)
  553.         return input[self._start:self._start+self.size]
  554.  
  555.     def _read_from_decompressor(self, coder, decompressor, input, checkremaining=False, with_cache=False):
  556.         data = ''
  557.         idx = 0
  558.         cnt = 0
  559.         properties = coder.get('properties', None)
  560.         if properties:
  561.             decompressor.decompress(properties)
  562.         total = self.compressed
  563.         if not input and total is None:
  564.             remaining = self._start+self.size
  565.             out = BytesIO()
  566.             cache = getattr(self._folder, '_decompress_cache', None)
  567.             if cache is not None:
  568.                 data, pos, decompressor = cache
  569.                 out.write(data)
  570.                 remaining -= len(data)
  571.                 self._file.seek(pos)
  572.             else:
  573.                 self._file.seek(self._src_start)
  574.             checkremaining = checkremaining and not self._folder.solid
  575.             while remaining > 0:
  576.                 data = self._file.read(READ_BLOCKSIZE)
  577.                 if checkremaining or (with_cache and len(data) < READ_BLOCKSIZE):
  578.                     tmp = decompressor.decompress(data, remaining)
  579.                 else:
  580.                     tmp = decompressor.decompress(data)
  581.                 assert len(tmp) > 0
  582.                 out.write(tmp)
  583.                 remaining -= len(tmp)
  584.  
  585.             data = out.getvalue()
  586.             if with_cache and self._folder.solid:
  587.                 # don't decompress start of solid archive for next file
  588.                 # TODO: limit size of cached data
  589.                 self._folder._decompress_cache = (data, self._file.tell(), decompressor)
  590.         else:
  591.             if not input:
  592.                 self._file.seek(self._src_start)
  593.                 input = self._file.read(total)
  594.             if checkremaining:
  595.                 data = decompressor.decompress(input, self._start+self.size)
  596.             else:
  597.                 data = decompressor.decompress(input)
  598.         return data[self._start:self._start+self.size]
  599.  
  600.     def _read_lzma(self, coder, input):
  601.         dec = pylzma.decompressobj(maxlength=self._start+self.size)
  602.         try:
  603.             return self._read_from_decompressor(coder, dec, input, checkremaining=True, with_cache=True)
  604.         except ValueError:
  605.             if self._is_encrypted():
  606.                 raise WrongPasswordError('invalid password')
  607.  
  608.             raise
  609.  
  610.     def _read_zip(self, coder, input):
  611.         dec = zlib.decompressobj(-15)
  612.         return self._read_from_decompressor(coder, dec, input, checkremaining=True)
  613.  
  614.     def _read_bzip(self, coder, input):
  615.         dec = bz2.BZ2Decompressor()
  616.         return self._read_from_decompressor(coder, dec, input)
  617.  
  618.     def read_7z_aes256_sha256(self, coder, input):
  619.         if not self._archive.password:
  620.             raise NoPasswordGivenError()
  621.  
  622.         # TODO: this needs some sanity checks
  623.         firstbyte = ord(coder['properties'][0])
  624.         numcyclespower = firstbyte & 0x3f
  625.         if firstbyte & 0xc0 != 0:
  626.             saltsize = (firstbyte >> 7) & 1
  627.             ivsize = (firstbyte >> 6) & 1
  628.  
  629.             secondbyte = ord(coder['properties'][1])
  630.             saltsize += (secondbyte >> 4)
  631.             ivsize += (secondbyte & 0x0f)
  632.  
  633.             assert len(coder['properties']) == 2+saltsize+ivsize
  634.             salt = coder['properties'][2:2+saltsize]
  635.             iv = coder['properties'][2+saltsize:2+saltsize+ivsize]
  636.             assert len(salt) == saltsize
  637.             assert len(iv) == ivsize
  638.             assert numcyclespower <= 24
  639.             if ivsize < 16:
  640.                 iv += '\x00'*(16-ivsize)
  641.         else:
  642.             salt = iv = ''
  643.  
  644.         password = self._archive.password.encode('utf-16-le')
  645.         key = pylzma.calculate_key(password, numcyclespower, salt=salt)
  646.         cipher = pylzma.AESDecrypt(key, iv=iv)
  647.         if not input:
  648.             self._file.seek(self._src_start)
  649.             uncompressed_size = self.uncompressed
  650.             if uncompressed_size & 0x0f:
  651.                 # we need a multiple of 16 bytes
  652.                 uncompressed_size += 16 - (uncompressed_size & 0x0f)
  653.             input = self._file.read(uncompressed_size)
  654.         result = cipher.decrypt(input)
  655.         return result
  656.  
  657.     def checkcrc(self):
  658.         if self.digest is None:
  659.             return True
  660.  
  661.         self.reset()
  662.         data = self.read()
  663.         return super(ArchiveFile, self).checkcrc(self.digest, data)
  664.  
  665. # XXX global state
  666. iv = None
  667. ivSize = None
  668. Salt = None
  669. NumCyclesPower = None
  670. SaltSize = None
  671.  
  672.  
  673. def SetDecoderProperties2(data):
  674.     global iv, ivSize, Salt, NumCyclesPower, SaltSize
  675.     pos = 0
  676.     data = bytearray(data)
  677.     firstByte = data[pos]
  678.     pos = pos + 1
  679.  
  680.     NumCyclesPower = firstByte & 0x3F
  681.     if NumCyclesPower > 24:
  682.         return None
  683.     if ((firstByte & 0xC0) == 0):
  684.         return "S_OK"
  685.     SaltSize = (firstByte >> 7) & 1
  686.     ivSize = (firstByte >> 6) & 1
  687.  
  688.     secondByte = data[pos]
  689.     pos = pos + 1
  690.  
  691.     SaltSize += (secondByte >> 4)
  692.     ivSize += (secondByte & 0x0F)
  693.  
  694.     # get salt
  695.     Salt = data[pos:pos+SaltSize]
  696.     Salt = str(Salt)
  697.     pos = pos + SaltSize
  698.     # get iv
  699.     iv = data[pos:pos+ivSize]
  700.     if len(iv) < 16:
  701.         iv = iv + b"\x00" * (16 - len(iv))
  702.     return "OK"
  703.  
  704.  
  705. class Archive7z(Base):
  706.     """ the archive itself """
  707.  
  708.     def __init__(self, file, password=None):
  709.         self._file = file
  710.         self.password = password
  711.         self.header = file.read(len(MAGIC_7Z))
  712.         if self.header != MAGIC_7Z:
  713.             raise FormatError('not a 7z file')
  714.         self.version = unpack('BB', file.read(2))
  715.  
  716.         self.startheadercrc = unpack('<L', file.read(4))[0]
  717.         self.nextheaderofs, data = self._readReal64Bit(file)
  718.         crc = crc32(data)
  719.         self.nextheadersize, data = self._readReal64Bit(file)
  720.         crc = crc32(data, crc)
  721.         data = file.read(4)
  722.         self.nextheadercrc = unpack('<L', data)[0]
  723.         crc = crc32(data, crc) & 0xffffffff
  724.         if crc != self.startheadercrc:
  725.             raise FormatError('invalid header data')
  726.         self.afterheader = file.tell()
  727.  
  728.         file.seek(self.nextheaderofs, 1)
  729.         buffer = BytesIO(file.read(self.nextheadersize))
  730.         if not self.checkcrc(self.nextheadercrc, buffer.getvalue()):
  731.             raise FormatError('invalid header data')
  732.  
  733.         while True:
  734.             id = buffer.read(1)
  735.             if id == PROPERTY_HEADER:
  736.                
  737.                 print("Archive with unencrypted header")
  738.                 break
  739.  
  740.             if id == PROPERTY_ENCODED_HEADER:
  741.                 # ReadAndDecodePackedStreams (7zIn.cpp)
  742.                 streams = StreamsInfo(buffer)
  743.                 file.seek(self.afterheader + 0)
  744.                 data = bytes('', 'ascii')
  745.                 for folder in streams.unpackinfo.folders:
  746.                     file.seek(streams.packinfo.packpos, 1)
  747.                     props = folder.coders[0]['properties']
  748.                     # decode properties
  749.                     if SetDecoderProperties2(props) == "OK":
  750.                         for idx in range(len(streams.packinfo.packsizes)):
  751.                             tmp = file.read(streams.packinfo.packsizes[idx])
  752.                             fname = os.path.basename(self._file.name)
  753.                             print(
  754.                                 "$7z$0$%s$%s$%s$%s$%s$%s$%s$%s$%s$%s"
  755.                                 % (
  756.                                     NumCyclesPower,
  757.                                     SaltSize,
  758.                                     "",
  759.                                     ivSize,
  760.                                     iv.hex(),
  761.                                     folder.crc,
  762.                                     len(tmp),
  763.                                     folder.unpacksizes[idx],
  764.                                     binascii.hexlify(tmp).decode(),
  765.                                     folder.unpacksizes[::-1][0],
  766.                                 )
  767.                             )
  768.                             size = folder.unpacksizes[idx]
  769.                             if len(folder.unpacksizes) > 1:
  770.                                 sys.stderr.write(
  771.                                     "%s : multiple unpacksizes found, not supported fully yet!\n"
  772.                                     % fname
  773.                                 )
  774.                             return
  775.                     #else:
  776.                         #for idx in range(len(streams.packinfo.packsizes)):
  777.                             tmp = file.read(streams.packinfo.packsizes[idx])
  778.                             data += pylzma.decompress(
  779.                                 props + tmp, maxlength=folder.unpacksizes[idx]
  780.                             )
  781.                     if folder.digestdefined:
  782.                         if not self.checkcrc(folder.crc, data[0:size]):
  783.                             raise FormatError("invalid block data")
  784.                         return
  785.             else:
  786.                 # Handle unencrypted header
  787.                 streams = StreamsInfo(buffer)
  788.                 file.seek(self.afterheader + 0)
  789.                 for folder in streams.unpackinfo.folders:
  790.                     file.seek(streams.packinfo.packpos, 1)
  791.                    
  792.            
  793.                    
  794.                    
  795.  
  796.         # Rest of the code remains the same, handling file extraction for unencrypted archives
  797.         buffer = BytesIO(file.read())
  798.         id = buffer.read(1)
  799.  
  800.         self.files = []
  801.         if not id:
  802.             self.solid = False
  803.             self.numfiles = 0
  804.             self.filenames = []
  805.             return
  806.  
  807.         xx = FilesInfo(buffer)
  808.  
  809.         self.header = Header(buffer)
  810.         files = self.header.files
  811.         folders = self.header.main_streams.unpackinfo.folders
  812.         packinfo = self.header.main_streams.packinfo
  813.         subinfo = self.header.main_streams.substreamsinfo
  814.         packsizes = packinfo.packsizes
  815.         self.solid = packinfo.numstreams == 1
  816.         if hasattr(subinfo, "unpacksizes"):
  817.             unpacksizes = subinfo.unpacksizes
  818.         else:
  819.             unpacksizes = [x.unpacksizes[0] for x in folders]
  820.  
  821.         fidx = 0
  822.         obidx = 0
  823.         src_pos = self.afterheader
  824.         pos = 0
  825.         folder_start = 0
  826.         folder_pos = src_pos
  827.         maxsize = (self.solid and packinfo.packsizes[0]) or None
  828.         for idx in range(files.numfiles):
  829.             info = files.files[idx]
  830.             if info["emptystream"]:
  831.                 continue
  832.  
  833.             folder = folders[fidx]
  834.             folder.solid = subinfo.numunpackstreams[fidx] > 1
  835.             maxsize = (folder.solid and packinfo.packsizes[fidx]) or None
  836.             if folder.solid:
  837.                 info["compressed"] = None
  838.             elif obidx < len(packsizes):
  839.                 info["compressed"] = packsizes[obidx]
  840.             else:
  841.                 info["compressed"] = unpacksizes[obidx]
  842.             info["uncompressed"] = unpacksizes[obidx]
  843.             file = ArchiveFile(
  844.                 info, pos, src_pos, unpacksizes[obidx], folder, self, maxsize=maxsize
  845.             )
  846.             if subinfo.digestsdefined[obidx]:
  847.                 file.digest = subinfo.digests[obidx]
  848.             self.files.append(file)
  849.             if folder.solid:
  850.                 pos += unpacksizes[obidx]
  851.             else:
  852.                 src_pos += info["compressed"]
  853.             obidx += 1
  854.             if idx >= subinfo.numunpackstreams[fidx] + folder_start:
  855.                 folder_pos += packinfo.packsizes[fidx]
  856.                 src_pos = folder_pos
  857.                 folder_start = idx
  858.                 fidx += 1
  859.  
  860.         self.numfiles = len(self.files)
  861.         self.filenames = map(lambda x: x.filename, self.files)
  862.     # interface like TarFile
  863.  
  864.     def getmember(self, name):
  865.         # XXX: store files in dictionary
  866.         for f in self.files:
  867.             if f.filename == name:
  868.                 return f
  869.  
  870.         return None
  871.  
  872.     def getmembers(self):
  873.         return self.files
  874.  
  875.     def getnames(self):
  876.         return self.filenames
  877.  
  878.     def list(self, verbose=True):
  879.         print ('total %d files in %sarchive' % (self.numfiles, (self.solid and 'solid ') or ''))
  880.         if not verbose:
  881.             print ('\n'.join(self.filenames))
  882.             return
  883.  
  884.         for f in self.files:
  885.             extra = (f.compressed and '%10d ' % (f.compressed)) or ' '
  886.             print ('%10d%s%.8x %s' % (f.size, extra, f.digest, f.filename))
  887.  
  888. if __name__ == '__main__':
  889.     if len(sys.argv) < 2:
  890.         sys.stdout.write("Usage: %s < encrypted 7-Zip files >\n" % \
  891.             sys.argv[0])
  892.  
  893.     for filename in sys.argv[1:]:
  894.         f = Archive7z(open(filename, 'rb'))
  895.  
Advertisement
Add Comment
Please, Sign In to add comment