2024-08-27 13:16:45 +07:00
|
|
|
import io
|
|
|
|
from .matcher import SignatureMatcher, Match
|
|
|
|
|
|
|
|
class Zip(SignatureMatcher):
|
|
|
|
"""
|
|
|
|
Zip files are read from the bottom
|
|
|
|
The signature PK is the local file header
|
|
|
|
|
|
|
|
https://medium.com/@felixstridsberg/the-zip-file-format-6c8a160d1c34
|
|
|
|
"""
|
|
|
|
def __init__(self, file):
|
|
|
|
self.name = "Zip"
|
|
|
|
self.signature = b'PK\x03\x04'
|
|
|
|
super().__init__(file)
|
|
|
|
|
|
|
|
def is_valid(self):
|
|
|
|
for match in self.search():
|
|
|
|
start = match
|
|
|
|
header = io.BytesIO(self.file[start:start+4*4 + 2*7])
|
|
|
|
magic = header.read(4)
|
|
|
|
min_version = header.read(2)
|
|
|
|
bitflag = header.read(2)
|
|
|
|
compression_method = header.read(2)
|
|
|
|
last_modification_time = header.read(2)
|
|
|
|
last_modification_data = header.read(2)
|
|
|
|
crc = header.read(4)
|
|
|
|
compressed_size = header.read(4)
|
|
|
|
uncompressed_size = header.read(4)
|
|
|
|
file_name_length = header.read(2)
|
|
|
|
extra_field_length = header.read(2)
|
|
|
|
|
2024-08-30 21:23:14 +07:00
|
|
|
as_num = lambda x: int.from_bytes(x, 'little')
|
|
|
|
|
|
|
|
file_name_length = as_num(file_name_length)
|
|
|
|
extra_field_length = as_num(extra_field_length)
|
|
|
|
compressed_size = as_num(compressed_size)
|
2024-08-27 13:16:45 +07:00
|
|
|
|
|
|
|
header_size = 4*4 + 2*7
|
|
|
|
data = {
|
|
|
|
'name': self.file[start+header_size:start+header_size+file_name_length]
|
|
|
|
}
|
|
|
|
|
|
|
|
size = 4*4 + 2*7 + file_name_length + extra_field_length + compressed_size
|
|
|
|
self.matches += [Match(start, size, data)]
|
|
|
|
|
|
|
|
return len(self.matches) != 0
|