1from typing import Optional
2
3from structlog import get_logger
4
5from ...extractors import Command
6from ...models import (
7 File,
8 HandlerDoc,
9 HandlerType,
10 HexString,
11 Reference,
12 StructHandler,
13 ValidChunk,
14)
15
16logger = get_logger()
17
18
19class CABHandler(StructHandler):
20 NAME = "cab"
21
22 PATTERNS = [HexString("4D 53 43 46 00 00 00 00 // MSCF then reserved dword")]
23 C_DEFINITIONS = r"""
24 typedef struct cab_header
25 {
26 char signature[4]; /* cabinet file signature contains the characters 'M','S','C','F' (bytes 0x4D, 0x53, 0x43, 0x46). */
27 /* This field is used to assure that the file is a cabinet file. */
28 uint32 reserved1; /* reserved */
29 uint32 cbCabinet; /* size of this cabinet file in bytes */
30 uint32 reserved2; /* reserved */
31 uint32 coffFiles; /* offset of the first CFFILE entry */
32 uint32 reserved3; /* reserved */
33 uint8 versionMinor; /* cabinet file format version, minor */
34 uint8 versionMajor; /* cabinet file format version, major */
35 uint16 cFolders; /* number of CFFOLDER entries in this cabinet */
36 uint16 cFiles; /* number of CFFILE entries in this cabinet */
37 uint16 flags; /* cabinet file option indicators */
38 uint16 setID; /* must be the same for all cabinets in a set*/
39 uint16 iCabinet; /* number of this cabinet file in a set */
40 uint16 cbCFHeader; /* (optional) size of per-cabinet reserved area */
41 uint8 cbCFFolder; /* (optional) size of per-folder reserved area */
42 uint8 cbCFData; /* (optional) size of per-datablock reserved area */
43 uint8 szCabinetPrev[]; /* (optional) name of previous cabinet file */
44 uint8 szDiskPrev[]; /* (optional) name of previous disk */
45 uint8 szCabinetNext[]; /* (optional) name of next cabinet file */
46 uint8 szDiskNext[]; /* (optional) name of next disk */
47 } cab_header_t;
48 """
49 HEADER_STRUCT = "cab_header_t"
50
51 EXTRACTOR = Command("7z", "x", "-y", "{inpath}", "-o{outdir}")
52
53 DOC = HandlerDoc(
54 name="CAB",
55 description="Microsoft Cabinet (CAB) archive files are used for compressed file storage and software installation.",
56 handler_type=HandlerType.ARCHIVE,
57 vendor="Microsoft",
58 references=[
59 Reference(
60 title="Microsoft Cabinet File Format Documentation",
61 url="https://en.wikipedia.org/wiki/Cabinet_(file_format)",
62 ),
63 Reference(
64 title="Ubuntu Manual - cabextract",
65 url="https://manpages.ubuntu.com/manpages/focal/man1/cabextract.1.html",
66 ),
67 ],
68 limitations=[],
69 )
70
71 def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]:
72 header = self.parse_header(file)
73
74 if header.cbCabinet < len(header):
75 return None
76
77 return ValidChunk(
78 start_offset=start_offset,
79 end_offset=start_offset + header.cbCabinet,
80 )