Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion fact_extractor/plugins/unpacking/sfx/code/sfx.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from __future__ import annotations

import logging
from pathlib import Path

import lief

from plugins.unpacking.sevenz.code.sevenz import unpack_function as sevenz

NAME = 'SFX'
Expand All @@ -11,7 +14,7 @@
'application/x-executable',
'application/x-pie-executable',
]
VERSION = '0.2.1'
VERSION = '0.3.0'

EXCLUDED_FILE_NAMES_1 = {'.bss', '.data', '.text'}
EXCLUDED_FILE_NAMES_2 = {str(i) for i in range(20)}
Expand All @@ -31,6 +34,8 @@ def unpack_function(file_path, tmp_dir):

if _extraction_result_is_invalid(extraction_dir):
clean_directory(extraction_dir)
if output := _try_to_extract_piggy_data(file_path, tmp_dir):
return {'output': output}
meta['output'] = (
"Normal executable files will not be extracted.\n\nPlease report if it's a self extracting archive"
)
Expand All @@ -46,6 +51,27 @@ def clean_directory(directory: Path):
clean_directory(child)


def _try_to_extract_piggy_data(file_path: str, tmp_dir: str) -> str | None:
"""
Some build tools put the compressed kernel image in a .piggydata section in the ELF file
"""
try:
if (elf := lief.ELF.parse(file_path)) is None:
return None
sections = {s.name: s for s in elf.sections}
if '.piggydata' not in sections:
return None
piggy = sections['.piggydata']
output_file = Path(tmp_dir) / '.piggydata'
with Path(file_path).open('rb') as fp_in, output_file.open('wb') as fp_out:
fp_in.seek(piggy.offset)
fp_out.write(fp_in.read(piggy.size))
return f'Saved .piggydata section from offset {piggy.offset} with size {piggy.size} to .piggydata'
except Exception as err:
logging.exception(f'Error when trying to extract .piggydata section from ELF: {err}')
return None


# ----> Do not edit below this line <----
def setup(unpack_tool):
for item in MIME_PATTERNS:
Expand Down
Binary file not shown.
6 changes: 6 additions & 0 deletions fact_extractor/plugins/unpacking/sfx/test/test_sfx.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ def test_self_extracting_archives(self):
self.check_unpacking_of_standard_unpack_set(
TEST_DATA_DIR / file, additional_prefix_folder='get_files_test', output=True
)

def test_piggy_extraction(self):
test_file = TEST_DATA_DIR / 'piggy.elf'
files, meta_data = self.unpacker.extract_files_from_file(str(test_file), self.tmp_dir.name)
assert len(files) == 1
assert Path(files[0]).read_bytes() == b'foobar\\ntest 1234\n'
2 changes: 2 additions & 0 deletions requirements-unpackers.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ pylibfdt ~= 1.7.1
structlog~=25.1.0
# unblob
unblob~=25.1.8
# SFX unpacker
lief~=0.17.1
Loading