Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SFX unpacker fix for binaries without section headers #135

Merged
merged 1 commit into from
Jan 13, 2025
Merged
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
32 changes: 23 additions & 9 deletions fact_extractor/plugins/unpacking/sfx/code/sfx.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
from __future__ import annotations

from pathlib import Path

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

NAME = 'SFX'
MIME_PATTERNS = ['application/x-executable', 'application/x-dosexec']
VERSION = '0.1'
MIME_PATTERNS = [
'application/x-dosexec',
'application/x-executable',
'application/x-pie-executable',
]
VERSION = '0.2.0'

EXCLUDED_FILE_NAMES_1 = {'.bss', '.data', '.text'}
EXCLUDED_FILE_NAMES_2 = {str(i) for i in range(20)}


def _extraction_result_is_invalid(extraction_dir: Path) -> bool:
extracted_files = [f.name for f in extraction_dir.iterdir()]
if any(f in EXCLUDED_FILE_NAMES_1 for f in extracted_files):
return True
return all(f in EXCLUDED_FILE_NAMES_2 for f in extracted_files)


def unpack_function(file_path, tmp_dir):
meta = sevenz(file_path, tmp_dir)

extraction_dir = Path(tmp_dir)

for child_path in extraction_dir.iterdir():
if child_path.name in ['.text', '.data']:
clean_directory(extraction_dir)
meta['output'] = (
'Normal executable files will not be extracted.' "\n\nPlease report if it's a self extracting archive"
)
break
if _extraction_result_is_invalid(extraction_dir):
clean_directory(extraction_dir)
meta['output'] = (
"Normal executable files will not be extracted.\n\nPlease report if it's a self extracting archive"
)

return meta

Expand Down
Binary file not shown.
32 changes: 15 additions & 17 deletions fact_extractor/plugins/unpacking/sfx/test/test_sfx.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os
from pathlib import Path

from test.unit.unpacker.test_unpacker import TestUnpackerBase

TEST_DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')
TEST_DATA_DIR = Path(__file__).parent / 'data'


class TestSfxUnpacker(TestUnpackerBase):
Expand All @@ -11,23 +11,21 @@ def test_unpacker_selection_generic(self):
self.check_unpacker_selection(mime, 'SFX')

def test_normal_elf_is_skipped(self):
files, meta_data = self.unpacker.extract_files_from_file(
os.path.join(TEST_DATA_DIR, 'test_elf_normal'), self.tmp_dir.name
)
assert not files, 'no file should be extracted'
assert 'will not be extracted' in meta_data['output']
self._assert_not_unpacked(TEST_DATA_DIR / 'test_elf_normal')

def test_normal_pe_with_rsrc_directory(self):
files, meta_data = self.unpacker.extract_files_from_file(
os.path.join(TEST_DATA_DIR, 'test_rsrc'), self.tmp_dir.name
)
self._assert_not_unpacked(TEST_DATA_DIR / 'test_rsrc')

def test_no_section_headers(self):
self._assert_not_unpacked(TEST_DATA_DIR / 'no_section_header.elf')

def _assert_not_unpacked(self, test_file: Path):
files, meta_data = self.unpacker.extract_files_from_file(test_file, self.tmp_dir.name)
assert not files, 'no file should be extracted'
assert 'will not be extracted' in meta_data['output']

def test_with_self_extracting_archives(self):
self.check_unpacking_of_standard_unpack_set(
os.path.join(TEST_DATA_DIR, 'test_elf_sfx'), additional_prefix_folder='get_files_test', output=True
)
self.check_unpacking_of_standard_unpack_set(
os.path.join(TEST_DATA_DIR, 'test_pe_sfx'), additional_prefix_folder='get_files_test', output=True
)
def test_self_extracting_archives(self):
for file in ['test_elf_sfx', 'test_pe_sfx']:
self.check_unpacking_of_standard_unpack_set(
TEST_DATA_DIR / file, additional_prefix_folder='get_files_test', output=True
)
9 changes: 6 additions & 3 deletions fact_extractor/unpacker/unpackBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
from os import getgid, getuid
from subprocess import PIPE, Popen
from time import time
from typing import Callable, Dict, List, Tuple
from typing import TYPE_CHECKING, Callable, Dict, List, Tuple

from common_helper_files import get_files_in_dir

from helperFunctions import magic
from helperFunctions.config import read_list_from_config
from helperFunctions.plugin import import_plugins

if TYPE_CHECKING:
from pathlib import Path


class UnpackBase:
"""
Expand Down Expand Up @@ -52,9 +55,9 @@ def get_unpacker(self, mime_type: str):
return self.unpacker_plugins[mime_type]
return self.unpacker_plugins['generic/carver']

def extract_files_from_file(self, file_path: str, tmp_dir) -> Tuple[List, Dict]:
def extract_files_from_file(self, file_path: str | Path, tmp_dir) -> Tuple[List, Dict]:
current_unpacker = self.get_unpacker(magic.from_file(file_path, mime=True))
return self._extract_files_from_file_using_specific_unpacker(file_path, tmp_dir, current_unpacker)
return self._extract_files_from_file_using_specific_unpacker(str(file_path), tmp_dir, current_unpacker)

def unpacking_fallback(self, file_path, tmp_dir, old_meta, fallback_plugin_mime) -> Tuple[List, Dict]:
fallback_plugin = self.unpacker_plugins[fallback_plugin_mime]
Expand Down
Loading