Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/unblob/handlers/archive/dmg.py: 100%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

16 statements  

1from typing import Optional 

2 

3from structlog import get_logger 

4 

5from unblob.extractors.command import Command 

6 

7from ...file_utils import Endian 

8from ...models import ( 

9 File, 

10 HandlerDoc, 

11 HandlerType, 

12 HexString, 

13 Reference, 

14 StructHandler, 

15 ValidChunk, 

16) 

17 

18logger = get_logger() 

19 

20 

21class DMGHandler(StructHandler): 

22 NAME = "dmg" 

23 

24 PATTERNS = [ 

25 HexString( 

26 """ 

27 // 'koly' magic, followed by version from 1 to 4, followed by fixed header size of 512 

28 6b 6f 6c 79 00 00 00 04 00 00 02 00 

29 """ 

30 ) 

31 ] 

32 

33 C_DEFINITIONS = r""" 

34 // source: http://newosxbook.com/DMG.html 

35 typedef struct dmg_header { 

36 char Signature[4]; // Magic ('koly') 

37 uint32 Version; // Current version is 4 

38 uint32 HeaderSize; // sizeof(this), always 512 

39 uint32 Flags; // Flags 

40 uint64 RunningDataForkOffset; // 

41 uint64 DataForkOffset; // Data fork offset (usually 0, beginning of file) 

42 uint64 DataForkLength; // Size of data fork (usually up to the XMLOffset, below) 

43 uint64 RsrcForkOffset; // Resource fork offset, if any 

44 uint64 RsrcForkLength; // Resource fork length, if any 

45 uint32 SegmentNumber; // Usually 1, may be 0 

46 uint32 SegmentCount; // Usually 1, may be 0 

47 uchar SegmentID[16]; // 128-bit GUID identifier of segment (if SegmentNumber !=0) 

48 

49 uint32 DataChecksumType; // Data fork 

50 uint32 DataChecksumSize; // Checksum Information 

51 uint32 DataChecksum[32]; // Up to 128-bytes (32 x 4) of checksum 

52 

53 uint64 XMLOffset; // Offset of property list in DMG, from beginning 

54 uint64 XMLLength; // Length of property list 

55 uint8 Reserved1[120]; // 120 reserved bytes - zeroed 

56 

57 uint32 ChecksumType; // Master 

58 uint32 ChecksumSize; // Checksum information 

59 uint32 Checksum[32]; // Up to 128-bytes (32 x 4) of checksum 

60 

61 uint32 ImageVariant; // Commonly 1 

62 uint64 SectorCount; // Size of DMG when expanded, in sectors 

63 

64 uint32 reserved2; // 0 

65 uint32 reserved3; // 0 

66 uint32 reserved4; // 0 

67 

68 } dmg_header_t; 

69 """ 

70 HEADER_STRUCT = "dmg_header_t" 

71 

72 # This directory is here for simulating hardlinks and other metadata 

73 # but the files will be extracted anyway, and we only care about the content 

74 # See more about this problem: https://newbedev.com/hfs-private-directory-data 

75 EXTRACTOR = Command( 

76 "7z", "x", "-xr!*HFS+ Private Data*", "-y", "{inpath}", "-o{outdir}" 

77 ) 

78 

79 DOC = HandlerDoc( 

80 name="DMG", 

81 description="Apple Disk Image (DMG) files are commonly used on macOS for software distribution and disk image storage.", 

82 handler_type=HandlerType.ARCHIVE, 

83 vendor="Apple", 

84 references=[ 

85 Reference( 

86 title="Apple Disk Image Format Documentation", 

87 url="http://newosxbook.com/DMG.html", 

88 ), 

89 ], 

90 limitations=[], 

91 ) 

92 

93 def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]: 

94 header = self.parse_header(file, endian=Endian.BIG) 

95 

96 # NOTE: the koly block is a trailer 

97 # ┌─────────────┐ │ 

98 # │Data fork │ │DataForkLength 

99 # │contains │ │ 

100 # │disk blocks │ │ 

101 # │ │ │ 

102 # │ │ ▼ 

103 # ├─────────────┤ │ 

104 # │XML plist │ │XMLLength 

105 # ├─────────────┤ ▼ 

106 # │koly trailer │ 

107 # └─────────────┘ 

108 # 

109 

110 return ValidChunk( 

111 start_offset=start_offset - header.XMLLength - header.DataForkLength, 

112 end_offset=start_offset + len(header), 

113 )