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

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

56 statements  

1import binascii 

2import io 

3import itertools 

4from pathlib import Path 

5from typing import TYPE_CHECKING, cast 

6 

7from structlog import get_logger 

8 

9from unblob.extractor import carve_chunk_to_file 

10from unblob.file_utils import ( 

11 Endian, 

12 File, 

13 InvalidInputFormat, 

14 StructParser, 

15 iterate_file, 

16) 

17from unblob.models import ( 

18 Chunk, 

19 Extractor, 

20 HandlerDoc, 

21 HandlerType, 

22 HexString, 

23 StructHandler, 

24 ValidChunk, 

25) 

26 

27if TYPE_CHECKING: 

28 from collections.abc import Iterable 

29 

30logger = get_logger() 

31CRC_CONTENT_OFFSET = 12 

32 

33TRX_V1_C_DEFINITION = r""" 

34 typedef struct trx_header { 

35 uint32 magic; /* "HDR0" */ 

36 uint32 len; /* header size + data */ 

37 uint32 crc32; /* 32-bit CRC from flag_version to end of file */ 

38 uint16 flags; 

39 uint16 version; 

40 uint32 offsets[3]; /* Offsets of partitions from start of header */ 

41 } trx_header_t; 

42""" 

43 

44TRX_V2_C_DEFINITION = r""" 

45 typedef struct trx_header { 

46 uint32 magic; /* "HDR0" */ 

47 uint32 len; /* header size + data */ 

48 uint32 crc32; /* 32-bit CRC from flag_version to end of file */ 

49 uint16 flags; 

50 uint16 version; 

51 uint32 offsets[4]; /* Offsets of partitions from start of header */ 

52 } trx_header_t; 

53""" 

54 

55 

56class TRXExtractor(Extractor): 

57 def __init__(self, c_definitions: str): 

58 self._struct_parser = StructParser(c_definitions) 

59 

60 def extract(self, inpath: Path, outdir: Path): 

61 with File.from_path(inpath) as file: 

62 header = self._struct_parser.parse("trx_header_t", file, Endian.LITTLE) 

63 file.seek(0, io.SEEK_END) 

64 eof = file.tell() 

65 offsets = sorted( 

66 [ 

67 offset 

68 for offset in [*cast("Iterable", header.offsets), eof] 

69 if offset > 0 

70 ] 

71 ) 

72 for i, (start_offset, end_offset) in enumerate(itertools.pairwise(offsets)): 

73 chunk = Chunk(start_offset=start_offset, end_offset=end_offset) 

74 carve_chunk_to_file(outdir.joinpath(Path(f"part{i}")), file, chunk) 

75 

76 

77class NetgearTRXBase(StructHandler): 

78 HEADER_STRUCT = "trx_header_t" 

79 

80 def calculate_chunk(self, file: File, start_offset: int) -> ValidChunk | None: 

81 header = self.parse_header(file, endian=Endian.LITTLE) 

82 

83 if not self.is_valid_header(header): 

84 raise InvalidInputFormat("Invalid TRX header.") 

85 

86 if not self._is_crc_valid(file, start_offset, header): 

87 raise InvalidInputFormat("Invalid CRC32.") 

88 

89 return ValidChunk( 

90 start_offset=start_offset, end_offset=start_offset + header.len 

91 ) 

92 

93 def is_valid_header(self, header) -> bool: 

94 return header.len >= len(header) 

95 

96 def _is_crc_valid(self, file: File, start_offset: int, header) -> bool: 

97 computed_crc = 0 

98 for chunk in iterate_file( 

99 file, 

100 start_offset + CRC_CONTENT_OFFSET, 

101 header.len - CRC_CONTENT_OFFSET, 

102 ): 

103 computed_crc = binascii.crc32(chunk, computed_crc) 

104 

105 computed_crc = (computed_crc ^ -1) & 0xFFFFFFFF 

106 return header.crc32 == computed_crc 

107 

108 

109class NetgearTRXv1Handler(NetgearTRXBase): 

110 NAME = "trx_v1" 

111 PATTERNS = [ 

112 HexString( 

113 """ 

114 // 00000000: 4844 5230 0010 0000 33b9 d625 0000 0100 HDR0....3..%.... 

115 // 00000010: 1c00 0000 3000 0000 4400 0000 7361 6d70 ....0...D... 

116 48 44 52 30 [10] 01 00 

117 """ 

118 ), 

119 ] 

120 C_DEFINITIONS = TRX_V1_C_DEFINITION 

121 EXTRACTOR = TRXExtractor(TRX_V1_C_DEFINITION) 

122 

123 DOC = HandlerDoc( 

124 name="Netgear TRX v1", 

125 description="Netgear TRX v1 firmware format includes a custom header with partition offsets and a CRC32 checksum for integrity verification. It supports up to three partitions defined in the header.", 

126 handler_type=HandlerType.ARCHIVE, 

127 vendor="Netgear", 

128 references=[], 

129 limitations=[], 

130 ) 

131 

132 

133class NetgearTRXv2Handler(NetgearTRXBase): 

134 NAME = "trx_v2" 

135 PATTERNS = [ 

136 HexString( 

137 """ 

138 // 00000000: 4844 5230 0010 0000 0a3d 4b05 0000 0200 HDR0.....=K..... 

139 // 00000010: 2000 0000 3400 ffff ffff ffff ffff 0000 ...4........... 

140 48 44 52 30 [10] 02 00 

141 """ 

142 ), 

143 ] 

144 C_DEFINITIONS = TRX_V2_C_DEFINITION 

145 EXTRACTOR = TRXExtractor(TRX_V2_C_DEFINITION) 

146 

147 DOC = HandlerDoc( 

148 name="Netgear TRX v2", 

149 description="Netgear TRX v2 firmware format includes a custom header with partition offsets and a CRC32 checksum for integrity verification. It supports up to four partitions defined in the header.", 

150 handler_type=HandlerType.ARCHIVE, 

151 vendor="Netgear", 

152 references=[], 

153 limitations=[], 

154 )