diff --git a/fact_extractor/plugins/unpacking/uboot/code/fit.py b/fact_extractor/plugins/unpacking/uboot/code/fit.py new file mode 100644 index 00000000..0d54bcde --- /dev/null +++ b/fact_extractor/plugins/unpacking/uboot/code/fit.py @@ -0,0 +1,53 @@ +""" +This plugin unpacks Flattened Image Trees. +""" + +from pathlib import Path + +import libfdt as fdt +from common_helper_files import write_binary_to_file + +NAME = 'FIT' +MIME_PATTERNS = ['linux/device-tree'] +VERSION = '0.1' + + +def unpack_function(file_path, tmp_dir): + """ + file_path specifies the input file. + tmp_dir must be used to store the extracted files. + Optional: Return a dict with meta information + """ + + file = Path(file_path) + + try: + with file.open('rb') as f: + fit_data = f.read() + + dtb = fdt.Fdt(fit_data) + root_offset = dtb.path_offset('/') + subnode_offset = dtb.first_subnode(root_offset) + while True: + try: + component_offset = dtb.first_subnode(subnode_offset) + while True: + try: + outfile = Path(tmp_dir) / dtb.get_name(component_offset) + write_binary_to_file(bytes(dtb.getprop(component_offset, 'data')), outfile) + component_offset = dtb.next_subnode(component_offset) + except fdt.FdtException: + break + subnode_offset = dtb.next_subnode(subnode_offset) + except fdt.FdtException: + break + except OSError as io_error: + return {'output': f'failed to read file: {io_error!s}'} + + return {'output': 'successfully unpacked FIT image'} + + +# ----> 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/uboot/test/data/fit.itb b/fact_extractor/plugins/unpacking/uboot/test/data/fit.itb new file mode 100644 index 00000000..ec911f8c Binary files /dev/null and b/fact_extractor/plugins/unpacking/uboot/test/data/fit.itb differ diff --git a/fact_extractor/plugins/unpacking/uboot/test/test_plugin_fit.py b/fact_extractor/plugins/unpacking/uboot/test/test_plugin_fit.py new file mode 100644 index 00000000..139f2ba0 --- /dev/null +++ b/fact_extractor/plugins/unpacking/uboot/test/test_plugin_fit.py @@ -0,0 +1,23 @@ +from pathlib import Path + +from test.unit.unpacker.test_unpacker import TestUnpackerBase + +TEST_DATA_DIR = Path(__file__).parent / 'data' + +EXPECTED_FILE_COUNT = 3 + + +class TestFITUnpacker(TestUnpackerBase): + def test_unpacker_selection_generic(self): + self.check_unpacker_selection('linux/device-tree', 'FIT') + + def test_extraction(self): + test_file_path = Path(TEST_DATA_DIR) / 'fit.itb' + extracted_files, meta_data = self.unpacker.extract_files_from_file(str(test_file_path), self.tmp_dir.name) + + assert meta_data['plugin_used'] == 'FIT', 'wrong plugin applied' + + assert len(extracted_files) == EXPECTED_FILE_COUNT, 'not all files extracted' + assert all( + Path(element).name in ['kernel', 'fdt', 'rootfs'] for element in extracted_files + ), 'not all files extracted' diff --git a/requirements-unpackers.txt b/requirements-unpackers.txt index 85a3b75d..234c6387 100644 --- a/requirements-unpackers.txt +++ b/requirements-unpackers.txt @@ -36,3 +36,4 @@ bincopy~=20.0.0 extract-dtb~=1.2.3 # uefi uefi-firmware~=1.11 +pylibfdt ~= 1.7.1