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

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

44 statements  

1from pathlib import Path 

2 

3from structlog import get_logger 

4 

5from unblob.extractor import carve_chunk_to_file 

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

7from unblob.models import ( 

8 Chunk, 

9 Extractor, 

10 HandlerDoc, 

11 HandlerType, 

12 HexString, 

13 StructHandler, 

14 ValidChunk, 

15) 

16 

17logger = get_logger() 

18 

19C_DEFINITIONS = r""" 

20 typedef struct bneg_header { 

21 uint32 magic; /* BNEG */ 

22 uint32 major; 

23 uint32 minor; 

24 uint32 partition_1_size; 

25 uint32 partition_2_size; 

26 } bneg_header_t; 

27""" 

28 

29 

30class BNEGExtractor(Extractor): 

31 def __init__(self): 

32 self._struct_parser = StructParser(C_DEFINITIONS) 

33 

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

35 with File.from_path(inpath) as file: 

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

37 header_end_offset = len(header) 

38 

39 start_offset = header_end_offset 

40 end_offset = header_end_offset + header.partition_1_size 

41 

42 logger.debug( 

43 "extracting partition 1", 

44 start_offset=start_offset, 

45 end_offset=end_offset, 

46 _verbosity=3, 

47 ) 

48 carve_chunk_to_file( 

49 file=file, 

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

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

52 ) 

53 

54 start_offset = end_offset 

55 end_offset = end_offset + header.partition_2_size 

56 

57 logger.debug( 

58 "extracting partition 2", 

59 start_offset=start_offset, 

60 end_offset=end_offset, 

61 _verbosity=3, 

62 ) 

63 carve_chunk_to_file( 

64 file=file, 

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

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

67 ) 

68 

69 

70class BNEGHandler(StructHandler): 

71 NAME = "bneg" 

72 

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

74 

75 C_DEFINITIONS = C_DEFINITIONS 

76 HEADER_STRUCT = "bneg_header_t" 

77 EXTRACTOR = BNEGExtractor() 

78 

79 DOC = HandlerDoc( 

80 name="Instar BNEG", 

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

82 handler_type=HandlerType.ARCHIVE, 

83 vendor="Instar", 

84 references=[], 

85 limitations=[], 

86 ) 

87 

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

89 if header.partition_1_size == 0: 

90 return False 

91 if header.partition_2_size == 0: 

92 return False 

93 if header.major != 0x1: 

94 return False 

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

96 return False 

97 

98 return True 

99 

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

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

102 

103 if not self.is_valid_header(header): 

104 raise InvalidInputFormat("Invalid bneg header.") 

105 

106 return ValidChunk( 

107 start_offset=start_offset, 

108 end_offset=start_offset 

109 + len(header) 

110 + header.partition_1_size 

111 + header.partition_2_size, 

112 )