diff --git a/fact_extractor/plugins/unpacking/xiaomi_hdr/__init__.py b/fact_extractor/plugins/unpacking/xiaomi_hdr/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fact_extractor/plugins/unpacking/xiaomi_hdr/code/__init__.py b/fact_extractor/plugins/unpacking/xiaomi_hdr/code/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fact_extractor/plugins/unpacking/xiaomi_hdr/code/xiaomi_hdr.py b/fact_extractor/plugins/unpacking/xiaomi_hdr/code/xiaomi_hdr.py new file mode 100644 index 0000000..56e7a89 --- /dev/null +++ b/fact_extractor/plugins/unpacking/xiaomi_hdr/code/xiaomi_hdr.py @@ -0,0 +1,50 @@ +""" +This plugin uses unblob to unpack Xiaomi HDR1/2 images. +""" + +from __future__ import annotations + +import logging +from pathlib import Path + +import structlog +from structlog.testing import capture_logs +from unblob.handlers.archive.xiaomi.hdr import HDRExtractor + +NAME = 'Xiaomi HDR' +MIME_PATTERNS = ['firmware/xiaomi-hdr1', 'firmware/xiaomi-hdr2'] +VERSION = '0.1.0' + +structlog.configure( + wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG), +) + + +def unpack_function(file_path: str, tmp_dir: str) -> dict: + path = Path(file_path) + with path.open('rb') as fp: + magic = fp.read(4) + if magic in [b'HDR1', b'HDR2']: + extractor = HDRExtractor(f'{magic.decode().lower()}_header_t') + else: + return {'output': ''} + + # unblob uses structlog for logging, but we can capture the logs with this convenient testing function + with capture_logs() as log_list: + extractor.extract(path, Path(tmp_dir)) + return {'output': _format_logs(log_list)} + + +def _format_logs(logs: list[dict]) -> str: + output = '' + for entry in logs: + output += '\n'.join(f'{key}: {value}' for key, value in entry.items() if key not in {'_verbosity', 'log_level'}) + return output + + +# ----> Do not edit below this line <---- + + +def setup(unpack_tool): + for item in MIME_PATTERNS: + unpack_tool.register_plugin(item, (unpack_function, NAME, VERSION)) diff --git a/fact_extractor/plugins/unpacking/xiaomi_hdr/test/__init__.py b/fact_extractor/plugins/unpacking/xiaomi_hdr/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fact_extractor/plugins/unpacking/xiaomi_hdr/test/data/test.hdr1 b/fact_extractor/plugins/unpacking/xiaomi_hdr/test/data/test.hdr1 new file mode 100644 index 0000000..3d97faa Binary files /dev/null and b/fact_extractor/plugins/unpacking/xiaomi_hdr/test/data/test.hdr1 differ diff --git a/fact_extractor/plugins/unpacking/xiaomi_hdr/test/test_hdr.py b/fact_extractor/plugins/unpacking/xiaomi_hdr/test/test_hdr.py new file mode 100644 index 0000000..2fb11f5 --- /dev/null +++ b/fact_extractor/plugins/unpacking/xiaomi_hdr/test/test_hdr.py @@ -0,0 +1,22 @@ +from pathlib import Path + +from plugins.unpacking.xiaomi_hdr.code.xiaomi_hdr import MIME_PATTERNS +from test.unit.unpacker.test_unpacker import TestUnpackerBase + +TEST_DATA_DIR = Path(__file__).parent / 'data' + + +class TestXiaomiHdrUnpacker(TestUnpackerBase): + def test_unpacker_selection_generic(self): + for mime in MIME_PATTERNS: + self.check_unpacker_selection(mime, 'Xiaomi HDR') + + def test_extraction_hdr(self): + in_file = TEST_DATA_DIR / 'test.hdr1' + assert in_file.is_file(), 'test file is missing' + meta = self.check_unpacking_of_standard_unpack_set( + in_file, + output=True, + ) + assert 'output' in meta + assert 'testfile1' in meta['output']