From 27e55d2e6dcc3b70ea5348eade055b717ad5e942 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 29 Oct 2023 19:47:11 +0100 Subject: [PATCH] patterns/zip: Added parsing of extra field (#182) * zip pattern: Improved fallback method for finding eocd. Added test data to cover this edge case * zip pattern: added parsing of extra field --- includes/std/time.pat | 9 +++++ includes/type/time.pat | 9 +++++ patterns/zip.hexpat | 79 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/includes/std/time.pat b/includes/std/time.pat index 388192e..6756b8b 100644 --- a/includes/std/time.pat +++ b/includes/std/time.pat @@ -155,6 +155,15 @@ namespace std::time { return converter.time; }; + /** + Converts a FILETIME to unix time. + @param value The value to convert. + @return Timestamp formatted as unix time. + */ + fn filetime_to_unix(u64 value) { + return value / 10000000 - 11644473600; + }; + /** Formats a time according to the specified format string. @param time The time to format. diff --git a/includes/type/time.pat b/includes/type/time.pat index a42beef..180bbaa 100644 --- a/includes/type/time.pat +++ b/includes/type/time.pat @@ -34,6 +34,11 @@ namespace type { */ using DOSTime = u16 [[format("type::impl::format_dostime")]]; + /** + A 64bit FILETIME value + */ + using FILETIME = u64 [[format("type::impl::format_filetime_as_unix")]]; + namespace impl { fn format_time_t(u128 value) { @@ -48,6 +53,10 @@ namespace type { return std::time::format_dos_time(std::time::to_dos_time(value)); }; + fn format_filetime_as_unix(u64 value) { + return std::time::filetime_to_unix(value); + }; + } } diff --git a/patterns/zip.hexpat b/patterns/zip.hexpat index 0a4e836..d095318 100644 --- a/patterns/zip.hexpat +++ b/patterns/zip.hexpat @@ -4,6 +4,7 @@ #include #include +#include struct EndOfCentralDirectory { u32 headerSignature [[comment("EoCD magic"), name("EoCD PK\\5\\6")]]; @@ -17,6 +18,78 @@ struct EndOfCentralDirectory { char coment[commentLength] [[name("Comment")]]; }; + +namespace extra { + + bitfield Flags { + bool modification_time_set : 1; + bool access_time_set : 1; + bool creation_time_set : 1; + reserved: 5; //reserved for additional timestamps; not set + }; + + struct X5455_ExtendedTimestamp { + Flags Flags; + if (Flags.modification_time_set){ + u32 ModTime; + } + if (Flags.access_time_set){ + u32 AcTime; + } + if (Flags.creation_time_set){ + u32 CrTime; + } + }; + + struct X000A_NTFS { + u32 reserved; + u16 tag; + u16 TSize; + //Timestamps are in FILETIME format. Converted to Unix for easier handling. + type::FILETIME ModTime; + type::FILETIME AcTime; + type::FILETIME CrTime; + }; + + struct X7875_NewUnix { + u16 tag; + u16 TSize; + u8 version; + u8 UIDSize; + u8 UID[UIDSize]; + u8 GIDSize; + u8 GID[GIDSize]; + }; + + struct X5855_InfoZipUnix { + u32 AcTime; + u32 ModTime; + if (parent.TSize > 8){ + u16 UID; + } + if (parent.TSize > 10){ + u16 GID; + } + }; + + struct ExtraField { + u16 tag; + u16 TSize; + if (tag == 0x5455){ + extra::X5455_ExtendedTimestamp x5455_ExtendedTimestamp; + }else if (tag == 0x000a){ + extra::X000A_NTFS x000A_NTFS; + }else if (tag == 0x7875){ + extra::X7875_NewUnix x7875_NewUnix; + }else if (tag == 0x5855){ + extra::X5855_InfoZipUnix x5855_InfoZipUnix; + }else{ + std::print("Unsupported tag 0x{:02X}", tag); + padding[TSize]; + } + }; +} + fn find_eocd() { // If there is no zip comment, which is the common case, // the end-of-central-directory record will be 22 bytes long @@ -96,7 +169,8 @@ struct LocalFileHeader { u16 fileNameLength [[ comment("File name length (n)") ]]; u16 extraFieldLength [[ comment("Extra field length (m)") ]]; char fileName[fileNameLength] [[ comment("File Name") ]]; - char extraField[extraFieldLength] [[ comment("Extra Field") ]]; + u64 extraEnd = $ + extraFieldLength; + extra::ExtraField extraFields[while ($ < extraEnd)] [[comment("Extra Fields")]]; u8 data[compressedSize] [[name("File Data")]]; }; @@ -124,7 +198,8 @@ struct CentralDirectoryFileHeader { u32 externalFileAttributes; File file; char fileName[fileNameLength]; - u8 extraField[extraFieldLength]; + u64 extraEnd = $ + extraFieldLength; + extra::ExtraField extraFields[while ($ < extraEnd)] [[comment("Extra Fields")]]; char comment[fileCommentLength]; };