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

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

45 statements  

1from pathlib import Path 

2from typing import Optional 

3 

4from structlog import get_logger 

5 

6from unblob.extractor import carve_chunk_to_file 

7from unblob.file_utils import Endian, File, InvalidInputFormat, StructParser 

8from unblob.models import ( 

9 Chunk, 

10 Extractor, 

11 HandlerDoc, 

12 HandlerType, 

13 HexString, 

14 StructHandler, 

15 ValidChunk, 

16) 

17 

18logger = get_logger() 

19 

20C_DEFINITIONS = r""" 

21 typedef struct bneg_header { 

22 uint32 magic; /* BNEG */ 

23 uint32 major; 

24 uint32 minor; 

25 uint32 partition_1_size; 

26 uint32 partition_2_size; 

27 } bneg_header_t; 

28""" 

29 

30 

31class BNEGExtractor(Extractor): 

32 def __init__(self): 

33 self._struct_parser = StructParser(C_DEFINITIONS) 

34 

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

36 with File.from_path(inpath) as file: 

37 header = self._struct_parser.parse("bneg_header_t", file, Endian.LITTLE) 

38 header_end_offset = len(header) 

39 

40 start_offset = header_end_offset 

41 end_offset = header_end_offset + header.partition_1_size 

42 

43 logger.debug( 

44 "extracting partition 1", 

45 start_offset=start_offset, 

46 end_offset=end_offset, 

47 _verbosity=3, 

48 ) 

49 carve_chunk_to_file( 

50 file=file, 

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

52 carve_path=outdir.joinpath("part1"), 

53 ) 

54 

55 start_offset = end_offset 

56 end_offset = end_offset + header.partition_2_size 

57 

58 logger.debug( 

59 "extracting partition 2", 

60 start_offset=start_offset, 

61 end_offset=end_offset, 

62 _verbosity=3, 

63 ) 

64 carve_chunk_to_file( 

65 file=file, 

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

67 carve_path=outdir.joinpath("part2"), 

68 ) 

69 

70 

71class BNEGHandler(StructHandler): 

72 NAME = "bneg" 

73 

74 PATTERNS = [HexString("42 4E 45 47")] 

75 

76 C_DEFINITIONS = C_DEFINITIONS 

77 HEADER_STRUCT = "bneg_header_t" 

78 EXTRACTOR = BNEGExtractor() 

79 

80 DOC = HandlerDoc( 

81 name="Instar BNEG", 

82 description="BNEG firmware files consist of a custom header followed by two partitions containing firmware components. The header specifies metadata such as magic value, version, and partition sizes.", 

83 handler_type=HandlerType.ARCHIVE, 

84 vendor="Instar", 

85 references=[], 

86 limitations=[], 

87 ) 

88 

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

90 if header.partition_1_size == 0: 

91 return False 

92 if header.partition_2_size == 0: 

93 return False 

94 if header.major != 0x1: 

95 return False 

96 if header.minor != 0x1: # noqa: SIM103 

97 return False 

98 

99 return True 

100 

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

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

103 

104 if not self.is_valid_header(header): 

105 raise InvalidInputFormat("Invalid bneg header.") 

106 

107 return ValidChunk( 

108 start_offset=start_offset, 

109 end_offset=start_offset 

110 + len(header) 

111 + header.partition_1_size 

112 + header.partition_2_size, 

113 )