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

55 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 Endian, File, InvalidInputFormat, StructParser 

11from unblob.models import ( 

12 Chunk, 

13 Extractor, 

14 HandlerDoc, 

15 HandlerType, 

16 HexString, 

17 StructHandler, 

18 ValidChunk, 

19) 

20 

21if TYPE_CHECKING: 

22 from collections.abc import Iterable 

23 

24logger = get_logger() 

25CRC_CONTENT_OFFSET = 12 

26 

27TRX_V1_C_DEFINITION = r""" 

28 typedef struct trx_header { 

29 uint32 magic; /* "HDR0" */ 

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

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

32 uint16 flags; 

33 uint16 version; 

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

35 } trx_header_t; 

36""" 

37 

38TRX_V2_C_DEFINITION = r""" 

39 typedef struct trx_header { 

40 uint32 magic; /* "HDR0" */ 

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

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

43 uint16 flags; 

44 uint16 version; 

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

46 } trx_header_t; 

47""" 

48 

49 

50class TRXExtractor(Extractor): 

51 def __init__(self, c_definitions: str): 

52 self._struct_parser = StructParser(c_definitions) 

53 

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

55 with File.from_path(inpath) as file: 

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

57 file.seek(0, io.SEEK_END) 

58 eof = file.tell() 

59 offsets = sorted( 

60 [ 

61 offset 

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

63 if offset > 0 

64 ] 

65 ) 

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

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

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

69 

70 

71class NetgearTRXBase(StructHandler): 

72 HEADER_STRUCT = "trx_header_t" 

73 

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

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

76 

77 if not self.is_valid_header(header): 

78 raise InvalidInputFormat("Invalid TRX header.") 

79 

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

81 raise InvalidInputFormat("Invalid CRC32.") 

82 

83 return ValidChunk( 

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

85 ) 

86 

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

88 return header.len >= len(header) 

89 

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

91 file.seek(start_offset + CRC_CONTENT_OFFSET) 

92 content = bytearray(file.read(header.len - CRC_CONTENT_OFFSET)) 

93 computed_crc = (binascii.crc32(content) ^ -1) & 0xFFFFFFFF 

94 return header.crc32 == computed_crc 

95 

96 

97class NetgearTRXv1Handler(NetgearTRXBase): 

98 NAME = "trx_v1" 

99 PATTERNS = [ 

100 HexString( 

101 """ 

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

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

104 48 44 52 30 [10] 01 00 

105 """ 

106 ), 

107 ] 

108 C_DEFINITIONS = TRX_V1_C_DEFINITION 

109 EXTRACTOR = TRXExtractor(TRX_V1_C_DEFINITION) 

110 

111 DOC = HandlerDoc( 

112 name="Netgear TRX v1", 

113 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.", 

114 handler_type=HandlerType.ARCHIVE, 

115 vendor="Netgear", 

116 references=[], 

117 limitations=[], 

118 ) 

119 

120 

121class NetgearTRXv2Handler(NetgearTRXBase): 

122 NAME = "trx_v2" 

123 PATTERNS = [ 

124 HexString( 

125 """ 

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

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

128 48 44 52 30 [10] 02 00 

129 """ 

130 ), 

131 ] 

132 C_DEFINITIONS = TRX_V2_C_DEFINITION 

133 EXTRACTOR = TRXExtractor(TRX_V2_C_DEFINITION) 

134 

135 DOC = HandlerDoc( 

136 name="Netgear TRX v2", 

137 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.", 

138 handler_type=HandlerType.ARCHIVE, 

139 vendor="Netgear", 

140 references=[], 

141 limitations=[], 

142 )