Octavian Prostean

67 papers C 2Misc 34Journal 14Unranked 17
YearRankTypeTitle / Venue / Authors
2024 Misc conf
SACI
Cezara-Liliana Rat, Octavian Prostean, Catalin Ichim-Burlacu
2023 Misc conf
SACI
Ioan Filip, Octavian Prostean, Iosif Szeidert, Dorin Bordeasu, Cristian Vasar
2022 Misc conf
SACI
Cezara-Liliana Rat, Octavian Prostean, Ioan Filip, Catalin Ichim-Burlacu
2022 Misc conf
SACI
Dorin Bordeasu, Octavian Prostean, Cristian Vasar, Ali Debes
2020 Misc conf
SACI
Ioan Filip, Cristian Vasar, Iosif Szeidert, Octavian Prostean
2020 J jnl
J. Sens. Actuator Networks
Madalin-Dorin Pop, Octavian Prostean, Gabriela Prostean
2020 J jnl
Sensors
Madalin-Dorin Pop, Octavian Prostean, Tudor-Mihai David, Gabriela Prostean
2020 Misc conf
SACI
Dan Ungureanu-Anghel, Raul Robu, Octavian Prostean
2020 conf
GOL
Madalin-Dorin Pop, Octavian Prostean, Gabriela Prostean
2019 Misc conf
SACI
Iosif Szeidert, Ioan Filip, Octavian Prostean, Cristian Vasar
2019 Misc conf
SACI
Cristian Vasar, Octavian Prostean, Ioan Filip, Iosif Szeidert
2019 conf
ICFNDS
Madalin-Dorin Pop, Octavian Prostean, Gabriela Prostean
2019 Misc conf
SACI
Cezara Rat, Octavian Prostean, Ioan Filip
2019 J jnl
Eur. J. Control
Ioan Filip, Cristian Vasar, Iosif Szeidert, Octavian Prostean
2018 J jnl
CoRR
Ioan Filip, Daniel Curiac, Octavian Prostean, Iosif Szeidert
2018 Misc conf
SACI
Iosif Szeidert, Ioan Filip, Octavian Prostean, Cristian Vasar
2018 J jnl
CoRR
Iosif Szeidert, Ioan Filip, Octavian Prostean, Cristian Vasar
2018 J jnl
CoRR
Ioan Filip, Octavian Prostean, Cristian Vasar, Iosif Szeidert
2018 J jnl
CoRR
Iosif Szeidert, Octavian Prostean, Ioan Filip, Cristian Vasar
2018 J jnl
CoRR
Iosif Szeidert, Octavian Prostean, Ioan Filip, Nicolae Budisan
2018 J jnl
CoRR
Cristian Vasar, Octavian Prostean, Ioan Filip, Iosif Szeidert
2018 J jnl
Trans. Comput. Collect. Intell.
Loredana Ghiormez, Octavian Prostean, Manuela Panoiu, Caius Panoiu
2018 J jnl
CoRR
Octavian Prostean, Iosif Szeidert, Nicolae Budisan, Gabriela Prostean, Cristian Vasar
2018 Misc conf
SACI
Noemi-Clara Rohatinovici, Octavian Prostean, Valentina Emilia Balas
2018 conf
SOFA (1)
Cezara-Liliana Rat, Octavian Prostean, Ioan Filip, Cristian Vasar
2018 J jnl
CoRR
Gabriela Prostean, Octavian Prostean, Iosif Szeidert, Ioan Filip
2018 J jnl
CoRR
Cristian Vasar, Octavian Prostean, Ioan Filip, Iosif Szeidert
2018 Misc conf
SACI
Cristian Vasar, Octavian Prostean, Ioan Filip, Iosif Szeidert
2016 Misc conf
SACI
Iosif Szeidert, Ioan Filip, Cristian Vasar, Octavian Prostean
2016 conf
SOFA (2)
Loredana Ghiormez, Octavian Prostean, Ioan Filip, Manuela Panoiu
2016 conf
ICCCI (1)
Loredana Ghiormez, Octavian Prostean, Manuela Panoiu, Caius Panoiu
2016 conf
SOFA (1)
Ioan Filip, Iosif Szeidert, Octavian Prostean, Cristian Vasar
2015 Misc conf
SACI
Loredana Ghiormez, Octavian Prostean
2015 Misc conf
SACI
Gelu-Ovidiu Tirian, Octavian Prostean, Ioan Filip, Cezara Rat
2015 Misc conf
SACI
Iosif Szeidert, Ioan Filip, Octavian Prostean, Cristian Vasar
2014 Misc conf
SACI
Radu Boraci, Octavian Prostean, Nicolae Budisan, Cristian Vasar
2014 J jnl
Comput. Appl. Eng. Educ.
Ana Daniela Cristea, Octavian Prostean, Thomas Muschalik, Ovidiu Tirian
2014 conf
SOFA (2)
Dan Ungureanu-Anghel, Octavian Prostean, Ioan Filip, Mihai Popescu
2014 conf
SOFA (2)
Ioan Filip, Iosif Szeidert, Octavian Prostean
2014 conf
SOFA (2)
Cristian Vasar, Marius Babescu, Doru-Ionut Petrescu, Octavian Prostean, Radu Boraci
2013 conf
TSP
Calin Cirstea, Octavian Prostean, Cosmin Cirstea
2013 conf
TSP
Calin Cirstea, Octavian Prostean, Cosmin Cirstea
2013 Misc conf
SACI
Loredana Ghiormez, Octavian Prostean
2013 Misc conf
SACI
Cosmin Koch-Ciobotaru, Radu Boraci, Octavian Prostean, Iosif Szeidert
2012 Misc conf
SACI
Alina Madalina Lonea, Daniela E. Popescu, Octavian Prostean
2012 Misc conf
SOFA
Alina Madalina Lonea, Daniela Elena Popescu, Octavian Prostean, Huaglory Tianfield
2012 Misc conf
SOFA
Ovidiu Horia Gana, Octavian Prostean, Marius Babescu
2012 Misc conf
SACI
Ovidiu Horia Gana, Octavian Prostean, Cristian Vasar, Marius Babescu
2012 Misc conf
SACI
Emanuel Ciprian Sasu, Octavian Prostean
2012 Misc conf
SACI
Emanuel Ciprian Sasu, Octavian Prostean
2012 Misc conf
SOFA
Emanuel Ciprian Sasu, Octavian Prostean
2011 conf
EUROCON
Doru Vatau, Flavius Dan Surianu, Sorin Musuroi, Flaviu-Mihai Frigura-Iliasa, Octavian Prostean
2011 Misc conf
SACI
Radu Boraci, Cosmin Koch-Ciobotaru, Octavian Prostean, Nicolae Budisan
2011 Misc conf
SACI
Cosmin Koch-Ciobotaru, Radu Boraci, Octavian Prostean, Nicolae Budisan
2011 conf
EUROCON
Ioan Filip, Octavian Prostean, Dan Ionescu, Cristian Vasar, Iosif Szeidert
2009 conf
ICAT
Cristian Vasar, Octavian Prostean, Ioan Filip, Raul Robu, Dan Popescu
2009 Misc conf
SACI
Gelu-Ovidiu Tirian, Octavian Prostean, Ioan Filip
2009 Misc conf
SACI
Ana Daniela Cristea, Octavian Prostean
2009 Misc conf
SACI
Emanuel Ciprian Sasu, Octavian Prostean, Voicu Groza
2009 Misc conf
SACI
Doru Paunescu, Marius Babescu, Octavian Prostean, Iosif Szeidert
2008 conf
VECIMS
Mircea Trifan, Bogdan Ionescu, Dan Ionescu, Octavian Prostean, Gabriela Prostean
2007 Misc conf
SACI
Dan Ungureanu-Anghel, Octavian Prostean
2007 Misc conf
SACI
Nicolae Budisan, Ioan Filip, Octavian Prostean, Iosif Szeidert, Lucian Mihet-Popa
2007 C conf
ETFA
Lucian Mihet-Popa, Octavian Prostean, Ioan Filip, Iosif Szeidert, Cristian Vasar
2006 C conf
ETFA
Ioan Filip, Octavian Prostean, Iosif Szeidert, Cristian Vasar, Gabriela Prostean
2003 conf
Modelling, Identification and Control
Ioan Filip, Octavian Prostean, Iosif Szeidert, Cristian Vasar
1999 conf
Fuzzy Days
Ioan Filip, Octavian Prostean, Daniel Curiac
tests/unit/test_apk_strings_ioc.py
← Index tests/unit/test_apk_strings_ioc.py python
"""Tests for APK string extraction and IOC extraction integration.

Covers:
- APKCodeAnalyzer._extract_strings() — string extraction from DEX objects
- APKCodeAnalyzer._string_entropy() — Shannon entropy computation
- DecompileAPK strings export table schema
- IOCExtractorFromResults with APK-format analysis_results
- Worker IOC wiring for APK
"""
import os
import tempfile
from dataclasses import asdict
from unittest.mock import MagicMock, patch

import pytest

from redb.extractors.ioc_extractor.ioc_extractor import IOCExtractorFromResults
from redb.extractors.ioc_extractor.standalone_ioc_extractor import (
    IOCType,
    SourceType,
)

pytestmark = [pytest.mark.unit, pytest.mark.apk]


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _make_analyzer(min_instructions=1):
    """Create an APKCodeAnalyzer with mocked dependencies."""
    with patch(
        "redb.extractors.decompiler.apk.analyzer.JADXDecompiler"
    ), patch(
        "redb.extractors.decompiler.apk.analyzer.ApktoolDisassembler"
    ):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
        analyzer = APKCodeAnalyzer("/fake/path.apk")
        analyzer.min_instructions = min_instructions
        return analyzer


def _make_mock_dex(strings):
    """Create a mock DEX object that returns the given strings."""
    dex = MagicMock()
    dex.get_strings.return_value = strings
    return dex


def _create_fake_apk_file():
    """Create a temporary file to act as a fake APK."""
    fd, path = tempfile.mkstemp(suffix=".apk")
    os.write(fd, b"PK\x03\x04fake apk content for hashing")
    os.close(fd)
    return path


# ---------------------------------------------------------------------------
# Tests: _string_entropy
# ---------------------------------------------------------------------------

class TestStringEntropy:
    """Tests for Shannon entropy computation."""

    def test_empty_string(self):
        analyzer = _make_analyzer()
        assert analyzer._string_entropy("") == 0.0

    def test_single_char(self):
        analyzer = _make_analyzer()
        assert analyzer._string_entropy("a") == 0.0

    def test_repeated_char(self):
        analyzer = _make_analyzer()
        assert analyzer._string_entropy("aaaa") == 0.0

    def test_two_equal_chars(self):
        analyzer = _make_analyzer()
        # "ab" -> entropy = 1.0
        assert abs(analyzer._string_entropy("ab") - 1.0) < 0.01

    def test_higher_entropy(self):
        analyzer = _make_analyzer()
        # More variety = higher entropy
        low = analyzer._string_entropy("aabb")
        high = analyzer._string_entropy("abcd")
        assert high > low

    def test_url_string_entropy(self):
        analyzer = _make_analyzer()
        entropy = analyzer._string_entropy("https://evil.com/payload")
        assert entropy > 2.0  # URLs have moderate entropy


# ---------------------------------------------------------------------------
# Tests: _extract_strings
# ---------------------------------------------------------------------------

class TestExtractStrings:
    """Tests for DEX string extraction."""

    def test_basic_extraction(self):
        analyzer = _make_analyzer()
        dex = _make_mock_dex(["hello", "world"])
        result = analyzer._extract_strings([dex])

        assert len(result) == 2
        assert result[0]["string"] == "hello"
        assert result[1]["string"] == "world"

    def test_string_fields_present(self):
        analyzer = _make_analyzer()
        dex = _make_mock_dex(["test_string"])
        result = analyzer._extract_strings([dex])

        entry = result[0]
        assert "string" in entry
        assert "string_encoding" in entry
        assert "string_offset" in entry
        assert "string_length" in entry
        assert "string_entropy" in entry
        assert entry["string_encoding"] == "UTF8"
        assert entry["string_length"] == len("test_string")

    def test_deduplication_across_dex(self):
        """Same string in multiple DEX files is only extracted once."""
        analyzer = _make_analyzer()
        dex1 = _make_mock_dex(["shared", "unique1"])
        dex2 = _make_mock_dex(["shared", "unique2"])
        result = analyzer._extract_strings([dex1, dex2])

        string_values = [s["string"] for s in result]
        assert string_values.count("shared") == 1
        assert "unique1" in string_values
        assert "unique2" in string_values
        assert len(result) == 3

    def test_empty_strings_filtered(self):
        analyzer = _make_analyzer()
        dex = _make_mock_dex(["", "valid", None, "also_valid"])
        result = analyzer._extract_strings([dex])

        string_values = [s["string"] for s in result]
        assert "" not in string_values
        assert None not in string_values
        assert "valid" in string_values
        assert "also_valid" in string_values

    def test_no_dex_files(self):
        analyzer = _make_analyzer()
        result = analyzer._extract_strings([])
        assert result == []

    def test_dex_get_strings_returns_none(self):
        analyzer = _make_analyzer()
        dex = MagicMock()
        dex.get_strings.return_value = None
        result = analyzer._extract_strings([dex])
        assert result == []

    def test_dex_get_strings_exception(self):
        analyzer = _make_analyzer()
        dex = MagicMock()
        dex.get_strings.side_effect = RuntimeError("corrupt DEX")
        result = analyzer._extract_strings([dex])
        assert result == []

    def test_entropy_computed(self):
        analyzer = _make_analyzer()
        dex = _make_mock_dex(["abcdefgh"])
        result = analyzer._extract_strings([dex])
        assert result[0]["string_entropy"] > 0.0

    def test_incremental_offsets(self):
        """String offsets are incremented sequentially."""
        analyzer = _make_analyzer()
        dex = _make_mock_dex(["a", "b", "c"])
        result = analyzer._extract_strings([dex])

        offsets = [s["string_offset"] for s in result]
        assert offsets == [0, 1, 2]

    def test_url_strings_preserved(self):
        """URL strings are extracted without modification."""
        analyzer = _make_analyzer()
        dex = _make_mock_dex([
            "https://evil.com/payload",
            "http://c2.malware.org/gate",
        ])
        result = analyzer._extract_strings([dex])
        strings = [s["string"] for s in result]
        assert "https://evil.com/payload" in strings
        assert "http://c2.malware.org/gate" in strings


# ---------------------------------------------------------------------------
# Tests: strings in extract() integration
# ---------------------------------------------------------------------------

class TestExtractStringsIntegration:
    """Tests that strings are included in extract() results."""

    @patch("redb.extractors.decompiler.apk.analyzer.APKCodeAnalyzer._run_androguard")
    @patch("redb.extractors.decompiler.apk.analyzer.ApktoolDisassembler")
    @patch("redb.extractors.decompiler.apk.analyzer.JADXDecompiler")
    def test_extract_includes_strings(self, mock_jadx, mock_apktool, mock_androguard):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer

        mock_dex = _make_mock_dex(["hello", "world"])
        mock_analysis = MagicMock()
        mock_analysis.get_methods.return_value = []
        mock_androguard.return_value = (MagicMock(), [mock_dex], mock_analysis)

        analyzer = APKCodeAnalyzer("/fake/path.apk", log=MagicMock())
        results = analyzer.extract()

        assert "strings" in results
        assert len(results["strings"]) == 2
        assert results["strings"][0]["string"] == "hello"
        analyzer.cleanup()

    @patch("redb.extractors.decompiler.apk.analyzer.APKCodeAnalyzer._run_androguard")
    @patch("redb.extractors.decompiler.apk.analyzer.ApktoolDisassembler")
    @patch("redb.extractors.decompiler.apk.analyzer.JADXDecompiler")
    def test_extract_strings_error_is_non_fatal(self, mock_jadx, mock_apktool, mock_androguard):
        from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer

        # Return a bad dex list that will cause _extract_strings to fail
        mock_analysis = MagicMock()
        mock_analysis.get_methods.return_value = []
        # dexs is not iterable — triggers the except block
        mock_androguard.return_value = (MagicMock(), 42, mock_analysis)

        analyzer = APKCodeAnalyzer("/fake/path.apk", log=MagicMock())
        results = analyzer.extract()

        # Should not crash, strings should be empty, error recorded
        assert results["strings"] == []
        assert any(
            e.get("error_location") == "strings"
            for e in results["analysis_errors"]
        )
        analyzer.cleanup()


# ---------------------------------------------------------------------------
# Tests: DecompileAPK strings export table
# ---------------------------------------------------------------------------

class TestDecompileAPKStringsExport:
    """Tests for strings table in DecompileAPK.prepare_export_data()."""

    def _make_extractor_with_strings(self, strings_data):
        from redb.extractors.decompiler.DecompileAPK import DecompileAPK
        apk_path = _create_fake_apk_file()
        ext = DecompileAPK(apk_path, MagicMock())
        ext.analysis_results = {
            "sha256": "a" * 64,
            "sha1": "b" * 40,
            "md5": "c" * 32,
            "decompiled_content": [],
            "decompiled_refs": [],
            "smali_content": [],
            "smali_refs": [],
            "similarity_metrics": [],
            "strings": strings_data,
            "analysis_errors": [],
        }
        return ext, apk_path

    def test_strings_table_present(self):
        ext, path = self._make_extractor_with_strings([
            {"string": "hello", "string_encoding": "UTF8",
             "string_offset": 0, "string_length": 5, "string_entropy": 2.32}
        ])
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            assert "strings_raw" in export
        finally:
            os.unlink(path)

    def test_strings_table_schema(self):
        ext, path = self._make_extractor_with_strings([
            {"string": "hello", "string_encoding": "UTF8",
             "string_offset": 0, "string_length": 5, "string_entropy": 2.32}
        ])
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            sr = export["strings_raw"]
            assert sr["table"] == "code_binja_strings_raw"
            assert len(sr["column_names"]) == 8
            assert len(sr["column_type_names"]) == 8
            assert "sha256" in sr["column_names"]
            assert "string" in sr["column_names"]
            assert "string_raw" in sr["column_names"]
            assert "string_encoding" in sr["column_names"]
            assert "string_offset" in sr["column_names"]
            assert "string_length" in sr["column_names"]
            assert "string_raw_length" in sr["column_names"]
            assert "string_entropy" in sr["column_names"]
            assert len(sr["data"]) == 1
            assert len(sr["data"][0]) == 8
        finally:
            os.unlink(path)

    def test_strings_sha256_propagated(self):
        ext, path = self._make_extractor_with_strings([
            {"string": "test", "string_encoding": "UTF8",
             "string_offset": 0, "string_length": 4, "string_entropy": 2.0}
        ])
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            row = export["strings_raw"]["data"][0]
            assert row[0] == "a" * 64  # sha256 is first column
        finally:
            os.unlink(path)

    def test_empty_strings_not_exported(self):
        ext, path = self._make_extractor_with_strings([])
        try:
            export = ext.prepare_export_data("ClickHouseExporter")
            assert "strings_raw" not in export
        finally:
            os.unlink(path)


# ---------------------------------------------------------------------------
# Tests: IOCExtractorFromResults with APK format
# ---------------------------------------------------------------------------

class TestIOCExtractorAPKFormat:
    """Tests that IOCExtractorFromResults handles APK analysis_results."""

    def setup_method(self):
        self.log = MagicMock()

    def test_extract_iocs_from_apk_strings(self):
        """IOCs extracted from APK strings (same format as Binja)."""
        results = {
            "strings": [
                {"string": "https://evil.com/payload", "string_offset": 0},
                {"string": "Contact [email protected]", "string_offset": 1},
            ],
            "decompiled": [],
            "decompiled_content": [],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()

        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        assert len(url_iocs) >= 1
        assert any("evil.com" in i.ioc_value for i in url_iocs)

    def test_extract_iocs_from_apk_decompiled_content(self):
        """IOCs extracted from APK's decompiled_content key."""
        results = {
            "strings": [],
            "decompiled": [],  # Binja format — empty
            "decompiled_content": [  # APK format
                {
                    "decompiled_method": "connect('https://c2.malware.org/gate');",
                    "decompiled_method_hash": "hash123",
                    "method_type": "USER",
                }
            ],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()

        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        assert len(url_iocs) >= 1

    def test_apk_library_methods_skipped(self):
        """LIBRARY methods in decompiled_content are skipped."""
        results = {
            "strings": [],
            "decompiled": [],
            "decompiled_content": [
                {
                    "decompiled_method": "https://should-skip.com",
                    "decompiled_method_hash": "lib_hash",
                    "method_type": "LIBRARY",
                }
            ],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()
        assert len(iocs) == 0

    def test_combined_binja_and_apk_sources(self):
        """Both Binja and APK format decompiled entries are processed."""
        results = {
            "strings": [],
            "decompiled": [
                {
                    "decompiled_function": "call('https://binja.example.com');",
                    "decompiled_function_hash": "binja_hash",
                    "function_type": "USER",
                }
            ],
            "decompiled_content": [
                {
                    "decompiled_method": "send('https://apk.example.com');",
                    "decompiled_method_hash": "apk_hash",
                    "method_type": "USER",
                }
            ],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()

        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        urls = [i.ioc_value for i in url_iocs]
        assert any("binja.example.com" in u for u in urls)
        assert any("apk.example.com" in u for u in urls)

    def test_apk_results_only(self):
        """Works when only APK keys are present (no 'decompiled' key)."""
        results = {
            "strings": [
                {"string": "https://evil.com", "string_offset": 0},
            ],
            "decompiled_content": [
                {
                    "decompiled_method": "x = 1",
                    "decompiled_method_hash": "h",
                    "method_type": "USER",
                }
            ],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()
        # Should not crash, and should find URL from strings
        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        assert len(url_iocs) >= 1

    def test_source_type_is_string_for_strings(self):
        results = {
            "strings": [
                {"string": "https://evil.com/test", "string_offset": 42},
            ],
            "decompiled_content": [],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()
        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        assert url_iocs[0].source_type == SourceType.STRING
        assert url_iocs[0].source_identifier == "42"

    def test_source_type_is_decompiled_for_methods(self):
        results = {
            "strings": [],
            "decompiled_content": [
                {
                    "decompiled_method": "https://evil.com/method",
                    "decompiled_method_hash": "myhash",
                    "method_type": "USER",
                }
            ],
        }
        extractor = IOCExtractorFromResults(results, sha256="a" * 64, log=self.log)
        iocs = extractor.extract()
        url_iocs = [i for i in iocs if i.ioc_type == IOCType.URL]
        assert url_iocs[0].source_type == SourceType.DECOMPILED_FUNCTION
        assert url_iocs[0].source_identifier == "myhash"


# ---------------------------------------------------------------------------
# Tests: Worker IOC wiring for APK
# ---------------------------------------------------------------------------

class TestWorkerAPKIOCWiring:
    """Tests that workers.py wires IOC extraction for APK."""

    def test_worker_source_contains_ioc_for_apk(self):
        """Verify workers.py source has IOC extraction in APK branch."""
        import inspect
        from redb import workers
        source = inspect.getsource(workers)
        # APK branch should import IOCExtractorFromResults
        assert "IOCExtractorFromResults" in source
        # Should be in the APK section (near DecompileAPK)
        apk_section = source[source.index("DecompileAPK("):]
        ioc_pos = apk_section.find("IOCExtractorFromResults")
        assert ioc_pos != -1, "IOCExtractorFromResults not found after DecompileAPK"