Skip to content

Commit

Permalink
Merge pull request #820 from onekey-sec/819-zip64
Browse files Browse the repository at this point in the history
fix(handler): add support for zip64 values in Central Directory Headers
  • Loading branch information
nyuware authored Apr 4, 2024
2 parents fb8c965 + 29a8188 commit 48b7ddb
Show file tree
Hide file tree
Showing 11 changed files with 63 additions and 6 deletions.
3 changes: 3 additions & 0 deletions tests/integration/archive/zip/zip64/__input__/colors.zip
Git LFS file not shown
Git LFS file not shown
3 changes: 3 additions & 0 deletions tests/integration/archive/zip/zip64/__input__/zero_edited.zip
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
Git LFS file not shown
39 changes: 33 additions & 6 deletions unblob/handlers/archive/zip.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ class ZIPHandler(StructHandler):
uint16 internal_file_attr;
uint32 external_file_attr;
uint32 relative_offset_local_header;
char file_name[file_name_length];
char extra_field[extra_field_length];
} cd_file_header_t;
// char file_name[file_name_length];
// char extra_field[extra_field_length];
} partial_cd_file_header_t;
typedef struct end_of_central_directory
{
Expand Down Expand Up @@ -94,8 +94,12 @@ def has_encrypted_files(
) -> bool:
file.seek(start_offset + end_of_central_directory.offset_of_cd, io.SEEK_SET)
for _ in range(end_of_central_directory.total_entries):
cd_header = self.cparser_le.cd_file_header_t(file)
if cd_header.flags & self.ENCRYPTED_FLAG:
file_header = self.cparser_le.partial_cd_file_header_t(file)
file.seek(
file_header.file_name_length + file_header.extra_field_length,
io.SEEK_CUR,
)
if file_header.flags & self.ENCRYPTED_FLAG:
return True
return False

Expand All @@ -111,6 +115,14 @@ def is_zip64_eocd(end_of_central_directory: Instance):
or end_of_central_directory.offset_of_cd == 0xFFFFFFFF
)

@staticmethod
def is_zip64_cd_file(file_header: Instance):
# see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT section 4.3.9.2
return (
file_header.file_size == 0xFFFFFFFF
or file_header.compress_size == 0xFFFFFFFF
)

def _parse_zip64(self, file: File, start_offset: int, offset: int) -> Instance:
file.seek(start_offset, io.SEEK_SET)
for eocd_locator_offset in iterate_patterns(
Expand All @@ -137,6 +149,20 @@ def _parse_zip64(self, file: File, start_offset: int, offset: int) -> Instance:
"Missing ZIP64 EOCD locator record header in ZIP chunk."
)

def is_zip64(self, file, start_offset, offset, end_of_central_directory):
absolute_offset_of_cd = start_offset + end_of_central_directory.offset_of_cd

if 0 < absolute_offset_of_cd < offset:
file.seek(absolute_offset_of_cd, io.SEEK_SET)
file_header = self.cparser_le.partial_cd_file_header_t(file)
if self.is_zip64_cd_file(file_header):
return True

# some values in the CD can be FFFF, indicating its a zip64
# if the offset of the CD is 0xFFFFFFFF, its definitely one
# otherwise we check every other header indicating zip64
return self.is_zip64_eocd(end_of_central_directory)

def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]:
has_encrypted_files = False
file.seek(start_offset, io.SEEK_SET)
Expand All @@ -147,7 +173,8 @@ def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]
file.seek(offset, io.SEEK_SET)
end_of_central_directory = self.parse_header(file)

if self.is_zip64_eocd(end_of_central_directory):
if self.is_zip64(file, start_offset, offset, end_of_central_directory):
file.seek(offset, io.SEEK_SET)
end_of_central_directory = self._parse_zip64(file, start_offset, offset)
break

Expand Down

0 comments on commit 48b7ddb

Please sign in to comment.