Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/unblob/handlers/filesystem/android/sparse.py: 67%

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

30 statements  

1import io 

2from typing import Optional 

3 

4from structlog import get_logger 

5 

6from unblob.extractors.command import Command 

7 

8from ....file_utils import Endian 

9from ....models import ( 

10 File, 

11 HandlerDoc, 

12 HandlerType, 

13 Reference, 

14 Regex, 

15 StructHandler, 

16 ValidChunk, 

17) 

18 

19logger = get_logger() 

20 

21CHUNK_TYPE_RAW = 0xCAC1 

22CHUNK_TYPE_FILL = 0xCAC2 

23CHUNK_TYPE_DONT_CARE = 0xCAC3 

24CHUNK_TYPE_CRC32 = 0xCAC4 

25 

26VALID_CHUNK_TYPES = [ 

27 CHUNK_TYPE_RAW, 

28 CHUNK_TYPE_FILL, 

29 CHUNK_TYPE_DONT_CARE, 

30 CHUNK_TYPE_CRC32, 

31] 

32 

33 

34class SparseHandler(StructHandler): 

35 NAME = "sparse" 

36 

37 # magic (0xed26ff3a) 

38 # major version (0x1) 

39 # minor version (any) 

40 # file header size (0x1C in v1.0) 

41 # chunk header size (0XC in v1.0) 

42 PATTERNS = [Regex(r"\x3A\xFF\x26\xED\x01\x00[\x00-\xFF]{2}\x1C\x00\x0C\x00")] 

43 

44 C_DEFINITIONS = r""" 

45 typedef struct sparse_header { 

46 uint32 magic; /* 0xed26ff3a */ 

47 uint16 major_version; /* (0x1) - reject images with higher major versions */ 

48 uint16 minor_version; /* (0x0) - allow images with higer minor versions */ 

49 uint16 file_hdr_sz; /* 28 bytes for first revision of the file format */ 

50 uint16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */ 

51 uint32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */ 

52 uint32 total_blks; /* total blocks in the non-sparse output image */ 

53 uint32 total_chunks; /* total chunks in the sparse input image */ 

54 uint32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */ 

55 /* as 0. Standard 802.3 polynomial, use a Public Domain */ 

56 /* table implementation */ 

57 } sparse_header_t; 

58 

59 typedef struct chunk_header { 

60 uint16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ 

61 uint16 reserved1; 

62 uint32 chunk_sz; /* in blocks in output image */ 

63 uint32 total_sz; /* in bytes of chunk input file including chunk header and data */ 

64 } chunk_header_t; 

65 """ 

66 HEADER_STRUCT = "sparse_header_t" 

67 

68 EXTRACTOR = Command("simg2img", "{inpath}", "{outdir}/raw.image") 

69 

70 DOC = HandlerDoc( 

71 name="Android Sparse", 

72 description="Android sparse images are a file format used to efficiently store disk images by representing sequences of zero blocks compactly. The format includes a file header, followed by chunk headers and data, with support for raw, fill, and 'don't care' chunks.", 

73 handler_type=HandlerType.FILESYSTEM, 

74 vendor="Google", 

75 references=[ 

76 Reference( 

77 title="Android Sparse Image Format Documentation", 

78 url="https://formats.kaitai.io/android_sparse/", 

79 ), 

80 Reference( 

81 title="simg2img Tool", 

82 url="https://github.com/anestisb/android-simg2img", 

83 ), 

84 ], 

85 limitations=[], 

86 ) 

87 

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

89 header = self.parse_header(file, Endian.LITTLE) 

90 

91 count = 0 

92 while count < header.total_chunks: 

93 chunk_header = self.cparser_le.chunk_header_t(file) 

94 if chunk_header.chunk_type not in VALID_CHUNK_TYPES: 

95 logger.warning("Invalid chunk type in Android sparse image. Aborting.") 

96 return None 

97 file.seek(chunk_header.total_sz - len(chunk_header), io.SEEK_CUR) 

98 count += 1 

99 

100 return ValidChunk( 

101 start_offset=start_offset, 

102 end_offset=file.tell(), 

103 )