Rainer Spang

60 papers A 2B 4Journal 47Unranked 7
YearRankTypeTitle / Venue / Authors
2025 J jnl
CoRR
Rao Muhammad Umer, Daniel Sens, Jonathan Noll, Christian Matek, Lukas Wolfseher, Rainer Spang, Ralf Huss, Johannes Raffler, Sarah Reinke, Wolfram Klapper, Katja Steiger, Kristina Schwamborn, Carsten Marr
2025 J jnl
Künstliche Intell.
Rainer Spang, Bernd Ludwig
2025 J jnl
Bioinform.
Zahra Nozari, Paul Hüttl, Jakob Simeth, Marian Schön, James A. Hutchinson, Rainer Spang
2025 J jnl
Künstliche Intell.
Rudolf Schill, Maren Klever, Kevin Rupp, Y. Linda Hu, Andreas Lösch, Peter Georg, Simon Pfahler, Stefan Vocht, Stefan Hansch, Tilo Wettig, Lars Grasedyck, Rainer Spang
2024 J jnl
J. Comput. Biol.
Rudolf Schill, Maren Klever, Andreas Lösch, Y. Linda Hu, Stefan Vocht, Kevin Rupp, Lars Grasedyck, Rainer Spang, Niko Beerenwinkel
2024 J jnl
Comput. Stat.
Kevin Rupp, Rudolf Schill, Jonas Süskind, Peter Georg, Maren Klever, Andreas Lösch, Lars Grasedyck, Tilo Wettig, Rainer Spang
2024 J jnl
Bioinform.
Kevin Rupp, Andreas Lösch, Y. Linda Hu, Chenxi Nie, Rudolf Schill, Maren Klever, Simon Pfahler, Lars Grasedyck, Tilo Wettig, Niko Beerenwinkel, Rainer Spang
2024 B conf
RECOMB
Rudolf Schill, Maren Klever, Andreas Lösch, Y. Linda Hu, Stefan Vocht, Kevin Rupp, Lars Grasedyck, Rainer Spang, Niko Beerenwinkel
2024 J jnl
Stat. Comput.
Simon Pfahler, Peter Georg, Rudolf Schill, Maren Klever, Lars Grasedyck, Rainer Spang, Tilo Wettig
2024 J jnl
Bioinform.
Jakob Simeth, Paul Hüttl, Marian Schön, Zahra Nozari, Michael Huttner, Tobias Schmidt, Michael Altenbuchinger, Rainer Spang
2024 J jnl
CoRR
Michael Huttner, Jakob Simeth, Renato Liguori, Fulvia Ferrazzi, Rainer Spang
2023 J jnl
Bioinform.
Lena Buck, Tobias Schmidt, Maren Feist, Philipp Schwarzfischer, Dieter Kube, Peter J. Oefner, Helena U. Zacharias, Michael Altenbuchinger, Katja Dettmer, Wolfram Gronwald, Rainer Spang
2022 J jnl
CoRR
Stefan Schrod, Andreas Schäfer, Stefan Solbrig, Robert Lohmayer, Wolfram Gronwald, Peter J. Oefner, Tim Beißbarth, Rainer Spang, Helena U. Zacharias, Michael Altenbuchinger
2022 J jnl
Bioinform.
Stefan Schrod, Andreas Schäfer, Stefan Solbrig, Robert Lohmayer, Wolfram Gronwald, Peter J. Oefner, Tim Beißbarth, Rainer Spang, Helena U. Zacharias, Michael Altenbuchinger
2021 J jnl
CoRR
Kevin Rupp, Rudolf Schill, Jonas Süskind, Peter Georg, Maren Klever, Andreas Lösch, Lars Grasedyck, Tilo Wettig, Rainer Spang
2020 J jnl
J. Comput. Biol.
Marian Schön, Jakob Simeth, Paul Heinrich, Franziska Görtler, Stefan Solbrig, Tilo Wettig, Peter J. Oefner, Michael Altenbuchinger, Rainer Spang
2020 J jnl
J. Comput. Biol.
Franziska Görtler, Marian Schön, Jakob Simeth, Stefan Solbrig, Tilo Wettig, Peter J. Oefner, Rainer Spang, Michael Altenbuchinger
2020 J jnl
CoRR
Peter Georg, Lars Grasedyck, Maren Klever, Rudolf Schill, Rainer Spang, Tilo Wettig
2020 J jnl
Bioinform.
Rudolf Schill, Stefan Solbrig, Tilo Wettig, Rainer Spang
2018 B conf
RECOMB
Franziska Görtler, Stefan Solbrig, Tilo Wettig, Peter J. Oefner, Rainer Spang, Michael Altenbuchinger
2017 J jnl
Bioinform.
Michael Altenbuchinger, Philipp Schwarzfischer, Thorsten Rehberg, Jörg Reinders, Christian W. Kohler, Wolfram Gronwald, Julia Richter, Monika Szczepanowski, Neus Masqué-Soler, Wolfram Klapper, Peter J. Oefner, Rainer Spang
2017 J jnl
Bioinform.
Michael Altenbuchinger, Philipp Schwarzfischer, Thorsten Rehberg, Jörg Reinders, Christian W. Kohler, Wolfram Gronwald, Julia Richter, Monika Szczepanowski, Neus Masqué-Soler, Wolfram Klapper, Peter J. Oefner, Rainer Spang
2017 J jnl
Bioinform.
Michael Altenbuchinger, Thorsten Rehberg, Helena U. Zacharias, Frank Stämmler, K. Dettmer, D. Weber, Andreas Hiergeist, A. Gessner, E. Holler, Peter J. Oefner, Rainer Spang
2016 J jnl
Bioinform.
Martin Pirkl, Elisabeth Hand, Dieter Kube, Rainer Spang
2015 J jnl
Bioinform.
Franziska Taruttis, Rainer Spang, Julia C. Engelmann
2015 J jnl
PLoS Comput. Biol.
Julia C. Engelmann, Thomas Amann, Birgitta Ott-Rötzer, Margit Nützel, Yvonne Reinders, Jörg Reinders, Wolfgang E. Thasler, Theresa Kristl, Andreas Teufel, Christian G. Huber, Peter J. Oefner, Rainer Spang, Claus Hellerbrand
2014 J jnl
Bioinform.
Sebastian Dümcke, Johannes Bräuer, Benedict Anchang, Rainer Spang, Niko Beerenwinkel, Achim Tresch
2013 B conf
RECOMB
Mohammad Javad Sadeh, Giusi Moffa, Rainer Spang
2013 J jnl
J. Comput. Biol.
Mohammad Javad Sadeh, Giusi Moffa, Rainer Spang
2011 J jnl
Bioinform.
Inka J. Appel, Wolfram Gronwald, Rainer Spang
2011 J jnl
Bioinform.
Matthias Maneck, Alexandra Schrader, Dieter Kube, Rainer Spang
2009 J jnl
BMC Bioinform.
Marian Thieme, Claudio Lottaz, Harald Niederstätter, Walther Parson, Rainer Spang, Peter J. Oefner
2008 J jnl
Bioinform.
Holger Fröhlich, Tim Beißbarth, Achim Tresch, Dennis Kostka, Juby Jacob, Rainer Spang, Florian Markowetz
2008 J jnl
Bioinform.
Juby Jacob, Marcel Jentsch, Dennis Kostka, Stefan Bentink, Rainer Spang
2008 J jnl
PLoS Comput. Biol.
Dennis Kostka, Rainer Spang
2007 J jnl
Bioinform.
Claudio Lottaz, Joern Toedling, Rainer Spang
2007 J jnl
J. Comput. Biol.
Stefanie Scheid, Rainer Spang
2007 J jnl
BMC Bioinform.
Florian Markowetz, Rainer Spang
2007 conf
ISMB/ECCB (Supplement of Bioinformatics)
Florian Markowetz, Dennis Kostka, Olga G. Troyanskaya, Rainer Spang
2006 conf
German Conference on Bioinformatics
Claudio Lottaz, Joern Toedling, Rainer Spang
2006 J jnl
BMC Bioinform.
Joern Toedling, Peter Rhein, Richard Ratei, Leonid Karawajew, Rainer Spang
2006 B conf
RECOMB
Stefanie Scheid, Rainer Spang
2006 J jnl
BMC Bioinform.
Jochen Jaeger, Rainer Spang
2006 J jnl
J. Bioinform. Comput. Biol.
Xinan Yang, Stefan Bentink, Stefanie Scheid, Rainer Spang
2005 J jnl
Bioinform.
Claudio Lottaz, Rainer Spang
2005 J jnl
Bioinform.
Florian Markowetz, Jacques Bloch, Rainer Spang
2005 A conf
AISTATS
Florian Markowetz, Steffen Grossmann, Rainer Spang
2005 J jnl
BMC Bioinform.
Claudio Lottaz, Rainer Spang
2005 J jnl
Bioinform.
Stefanie Scheid, Rainer Spang
2004 J jnl
IEEE ACM Trans. Comput. Biol. Bioinform.
Stefanie Scheid, Rainer Spang
2004 conf
ISMB/ECCB (Supplement of Bioinformatics)
Dennis Kostka, Rainer Spang
2002 J jnl
J. Comput. Biol.
Rainer Spang, Marc Rehmsmeier, Jens Stoye
2002 J jnl
Silico Biol.
Rainer Spang, Carrie Blanchette, Harry Zuzan, Jeffrey R. Marks, Joseph Nevins, Mike West
2001 J jnl
Bioinform.
Rainer Spang, Martin Vingron
2001 conf
German Conference on Bioinformatics
Rainer Spang, Carrie Blanchette, Harry Zuzan, Jeffrey R. Marks, Joseph Nevins, Mike West
2001 conf
German Conference on Bioinformatics
Constantin Bannert, Marc Rehmsmeier, Rainer Spang, Jens Stoye
2000 A conf
ISMB
Rainer Spang, Marc Rehmsmeier, Jens Stoye
1999 conf
German Conference on Bioinformatics
Tobias Müller, Rainer Spang, Martin Vingron
1998 J jnl
Bioinform.
Rainer Spang, Martin Vingron
1997 conf
German Conference on Bioinformatics
Rainer Spang, Martin Vingron
redb/extractors/ioc_extractor/ioc_extractor.py
← Index redb/extractors/ioc_extractor/ioc_extractor.py python
"""
IOC Extractor - Extractor class for extracting IOCs from decompilation results.

This extractor works with in-memory data from DecompileBinja, following the
standard Extractor pattern to support both ClickHouse and PrintExporter (dry-run).

Usage:
    # After DecompileBinja completes:
    ioc_extractor = IOCExtractorFromResults(
        analysis_results=decompiler.analysis_results,
        sha256=sha256,
        log=logger,
        exporters=exporters,
        index_prefix=index_prefix
    )
    ioc_extractor.export_data()
"""

import inspect
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, List, Dict, Optional

from redb.extractors.enum import Tag
from redb.extractors.database_exporters import DatabaseExporter

# Import the IOCScraper and related classes from standalone module
from redb.extractors.ioc_extractor.standalone_ioc_extractor import (
    IOCScraper,
    IOCType,
    SourceType,
    ExtractedIOC,
)
from typing import Set


class IOCExtractorFromResults:
    """
    Extracts IOCs from in-memory decompilation results.

    This follows a simplified Extractor pattern but doesn't inherit from Extractor
    since it doesn't read from a binary file - instead it takes already-processed
    analysis results from DecompileBinja.
    """

    def __init__(
        self,
        analysis_results: Dict[str, Any],
        sha256: str,
        log: Any,
        exporters: Optional[List[DatabaseExporter]] = None,
        index_prefix: Optional[str] = None,
        tld_file: Optional[Path] = None,
        suppress_types: Optional[Set[IOCType]] = None,
        js_context: bool = False,
    ):
        """
        Initialize IOC Extractor with analysis results.

        Args:
            analysis_results: Dict containing 'strings' and 'decompiled' lists from DecompileBinja
            sha256: Sample SHA256 hash
            log: Logger instance
            exporters: List of database exporters (ClickHouse, Print, etc.)
            index_prefix: Index prefix for database
            tld_file: Optional path to TLD list file
            js_context: When True, the underlying IOCScraper rejects FQDN
                candidates that match JS object-access syntax (see
                JS_FP_TLDS / JS_FP_SLDS). Set this for the JS pipeline only;
                APK suppresses FQDN entirely via suppress_types and binary
                callers leave it disabled.
        """
        self.log = log
        self.log.debug(f"Creating {self.__class__.__name__}")
        self.analysis_results = analysis_results
        self.sha256 = sha256
        self.exporters = exporters or []
        self.index_prefix = index_prefix
        self.scraper = IOCScraper(
            tld_file, suppress_types=suppress_types, js_context=js_context,
        )
        self.extracted_iocs: List[ExtractedIOC] = []

    def extract(self) -> List[ExtractedIOC]:
        """
        Extract IOCs from strings and decompiled functions in analysis_results.

        Returns:
            List of ExtractedIOC objects
        """
        self.log.debug(inspect.currentframe().f_code.co_name)
        self.extracted_iocs = []

        # Extract from strings
        strings_count = self._extract_from_strings()

        # Extract from decompiled functions
        functions_count = self._extract_from_decompiled()

        # Extract from text-based artefact surfaces (JS, PowerShell, etc.)
        text_count = self._extract_from_text()

        self.log.info(
            f"Extracted {len(self.extracted_iocs)} IOCs for {self.sha256[:16]}... "
            f"(strings: {strings_count}, functions: {functions_count}, "
            f"text: {text_count})"
        )

        return self.extracted_iocs

    def _extract_from_strings(self) -> int:
        """Extract IOCs from sample's strings."""
        count = 0
        strings = self.analysis_results.get("strings", [])

        for s in strings:
            string_value = s.get("string", "")
            string_offset = s.get("string_offset", 0)

            if isinstance(string_value, bytes):
                string_value = string_value.decode('utf-8', errors='replace')

            for ioc in self.scraper.scrape(string_value, SourceType.STRING, str(string_offset)):
                self.extracted_iocs.append(ioc)
                count += 1

        return count

    def _extract_from_decompiled(self) -> int:
        """Extract IOCs from sample's decompiled functions.

        Supports both Binja format (key: "decompiled", fields: "decompiled_function",
        "decompiled_function_hash", "function_type") and APK format (key:
        "decompiled_content", fields: "decompiled_method", "decompiled_method_hash",
        "method_type").
        """
        count = 0

        # Binja format
        decompiled = self.analysis_results.get("decompiled", [])
        for func in decompiled:
            func_type = func.get("function_type", "UNKNOWN")
            if func_type in ("LIBRARY", "THUNK"):
                continue

            func_content = func.get("decompiled_function", "")
            func_hash = func.get("decompiled_function_hash", "unknown")

            if isinstance(func_content, bytes):
                func_content = func_content.decode('utf-8', errors='replace')

            for ioc in self.scraper.scrape(func_content, SourceType.DECOMPILED_FUNCTION, func_hash):
                self.extracted_iocs.append(ioc)
                count += 1

        # APK format (decompiled_content with method-level fields)
        decompiled_content = self.analysis_results.get("decompiled_content", [])
        for func in decompiled_content:
            func_type = func.get("method_type", "UNKNOWN")
            if func_type in ("LIBRARY", "THUNK"):
                continue

            func_content = func.get("decompiled_method", "")
            func_hash = func.get("decompiled_method_hash", "unknown")

            if isinstance(func_content, bytes):
                func_content = func_content.decode('utf-8', errors='replace')

            for ioc in self.scraper.scrape(func_content, SourceType.DECOMPILED_FUNCTION, func_hash):
                self.extracted_iocs.append(ioc)
                count += 1

        return count

    def _extract_from_text(self) -> int:
        """Extract IOCs from text-based artefact surfaces.

        Walks `analysis_results["text_raw"]` and `analysis_results["text_normalized"]`,
        each a list of `{"content": str, "content_hash": str}` dicts. Each
        list is routed through its own SourceType (`TEXT_RAW` /
        `TEXT_NORMALIZED`) so analysts can distinguish IOCs that were already
        present in the raw source from those exposed only after normalisation
        (deobfuscation/beautification). Generic across text-based formats —
        used by JS today, intended for PowerShell, Python, email body,
        extracted PDF/Office text in the future.
        """
        count = 0

        for key, source_type in (
            ("text_raw", SourceType.TEXT_RAW),
            ("text_normalized", SourceType.TEXT_NORMALIZED),
        ):
            for entry in self.analysis_results.get(key, []):
                content = entry.get("content", "")
                content_hash = entry.get("content_hash", "unknown")

                if isinstance(content, bytes):
                    content = content.decode('utf-8', errors='replace')

                for ioc in self.scraper.scrape(content, source_type, content_hash):
                    self.extracted_iocs.append(ioc)
                    count += 1

        return count

    def prepare_export_data(self, exporter_type: str) -> Any:
        """
        Prepare data for specific export type.

        Returns tuple for ClickHouse or list of dicts for Print/Elasticsearch.
        """
        self.log.debug(inspect.currentframe().f_code.co_name)

        if not self.extracted_iocs:
            return None

        now = datetime.now(timezone.utc)

        if exporter_type == "ClickHouseExporter":
            data = [
                [
                    self.sha256,
                    ioc.ioc_type.value,
                    ioc.ioc_value,
                    ioc.source_type.value,
                    ioc.source_identifier,
                    now,
                ]
                for ioc in self.extracted_iocs
            ]

            column_names = [
                "sha256",
                "ioc_type",
                "ioc_value",
                "source_type",
                "source_identifier",
                "extracted_at",
            ]

            column_type_names = [
                "FixedString(64)",
                "Enum8('ipv4'=1, 'ipv6'=2, 'fqdn'=3, 'url'=4, 'email'=5, 'server'=6, "
                "'hash_md5'=10, 'hash_sha1'=11, 'hash_sha256'=12, 'cve'=20, 'cwe'=21, 'cpe'=22, "
                "'crypto_btc'=30, 'crypto_eth'=31, 'crypto_xrp'=32, 'crypto_bch'=33, "
                "'crypto_ada'=34, 'crypto_substrate'=35, 'path_linux'=40, 'path_windows'=41, "
                "'registry_key'=42, 'onion'=50)",
                "String",
                "Enum8('decompiled_function'=1, 'disassembled_function'=2, 'string'=3, "
                "'text_raw'=4, 'text_normalized'=5)",
                "String",
                "DateTime64(3, 'UTC')",
            ]

            return (data, column_names, column_type_names)

        else:
            # For PrintExporter and others - return list of dicts
            return [
                {
                    "sha256": self.sha256,
                    "ioc_type": ioc.ioc_type.value,
                    "ioc_value": ioc.ioc_value,
                    "source_type": ioc.source_type.value,
                    "source_identifier": ioc.source_identifier,
                    "extracted_at": now.isoformat(),
                }
                for ioc in self.extracted_iocs
            ]

    def get_clickhouse_table(self) -> str:
        """Return the ClickHouse table name for IOCs."""
        return "redb_iocs"

    def tag(self) -> str:
        """Return the tag for this extractor."""
        return Tag.IOC.value if hasattr(Tag, 'IOC') else "ioc"

    def export_data(self) -> bool:
        """
        Export extracted IOCs to all configured exporters.

        Returns:
            True if export succeeded, False if failed, None if no data
        """
        self.log.debug(inspect.currentframe().f_code.co_name)

        # First extract the IOCs
        extracted = self.extract()

        if not extracted:
            self.log.debug("No IOCs extracted, skipping export")
            return None

        success = True

        from redb.extractors.database_exporters import PrintExporter, ClickHouseExporter

        for exporter in self.exporters:
            try:
                if isinstance(exporter, PrintExporter):
                    # For PrintExporter, pass the list of dicts
                    export_data = self.prepare_export_data("PrintExporter")
                    success &= exporter.export(export_data)

                elif isinstance(exporter, ClickHouseExporter):
                    # For ClickHouse, pass tuple with table info
                    export_data = self.prepare_export_data("ClickHouseExporter")
                    if export_data:
                        success &= exporter.export(
                            export_data,
                            table=self.get_clickhouse_table(),
                            column_names=export_data[1],
                            column_type_names=export_data[2]
                        )

            except Exception as e:
                self.log.error(f"Error exporting IOCs to {exporter.__class__.__name__}: {e}")
                success = False

        return success