Imad Eddine Toubal

19 papers A* 1Journal 6Unranked 12
YearRankTypeTitle / Venue / Authors
2026 J jnl
IEEE Trans. Medical Imaging
Rina Bao, Anna N. Foster, Ya'Nan Song, Rutvi Vyas, Ankush Kesri, Imad Eddine Toubal, Elham Soltani Kazemi, Gani Rahmon, Taci Kucukpinar, Mohamed Almansour, Mai-Lan Ho, Kannappan Palaniappan, Dean Ninalga, Chiranjeewee Prasad Koirala, Sovesh Mohapatra, Gottfried Schlaug, Marek Wodzinski, Henning Müller, David G. Ellis, Michele R. Aizenberg, M. Arda Aydin, Elvin Abdinli, Gozde Unal, Nazanin Tahmasebi, Kumaradevan Punithakumar, Tian Song, Yun Peng, Sara V. Bates, Randy Hirschtick, Patricia Ellen Grant, Yangming Ou
2026 J jnl
Int. J. Comput. Vis.
Elham Soltani Kazemi, Imad Eddine Toubal, Gani Rahmon, Juan David Mogollon, Kannappan Palaniappan
2025 J jnl
Informatics
Mohammad Beheshti, Imad Eddine Toubal, Khuder Alaboud, Mohammed Almalaysha, Olabode B. Ogundele, Hamza Turabieh, Nader Abdalnabi, Suzanne A. Boren, Grant J. Scott, Butros M. Dahu
2025 J jnl
CoRR
Elham Soltani Kazemi, Imad Eddine Toubal, Gani Rahmon, Jaired Collins, Kannappan Palaniappan
2025 conf
ICCVW
Matei Kristan, Jirí Matas, Pavel Tokmakov, Alan Lukezic, Michael Felsberg, Luka Cehovin Zajc, Khanh-Tung Tran, Xuan-Son Vu, Johanna Björklund, Michal Neoral, Hyung Jin Chang, Gustavo Fernández, Minasadat Attari, Matteo Dunnhofer, Wei Feng, Zhenhua Feng, Jin Gao, Yameng Gu, Ruize Han, Jiawei He, Zhenyu He, Junhui Hou, Weiming Hu, Xiantao Hu, Xingsen Huang, Yuqing Huang, Gleb Kirichenko, Josef Kittler, Yutong Kou, Simiao Lai, Bing Li, Xin Li, Shubo Lin, Huchuan Lu, Deshui Miao, Christian Micheloni, Juan David Mogollon, Moritz Nottebaum, Kannappan Palaniappan, Ziqi Pang, Zekun Qian, Gani Rahmon, Aleksandr Romanov, Liangtao Shi, Roman A. Solovyev, Elham Soltani Kazemi, Imad Eddine Toubal, Jovana Videnovic, Dong Wang, Yaowei Wang, Yu-Xiong Wang, Zhixiang Wang, Xiaojun Wu, Jinxia Xie, Tianyang Xu, Chaocan Xue, Yuanliang Xue, Ming-Hsuan Yang, Dmitriy Yurtov, Chunui Zhang, Xiangqun Zhang, Yunfei Zhang, Qingfang Zheng, Bineng Zhong, Fuan Zhong, Jinglin Zhou, Jingmeng Zhou, Junbao Zhou, Yong Zhou, Xuefeng Zhu
2024 J jnl
Int. J. Comput. Vis.
Gani Rahmon, Kannappan Palaniappan, Imad Eddine Toubal, Filiz Bunyak, Raghuveer Rao, Guna Seetharaman
2024 J jnl
CoRR
Imad Eddine Toubal, Aditya Avinash, Neil Gordon Alldrin, Jan Dlabal, Wenlei Zhou, Enming Luo, Otilia Stretcu, Hao Xiong, Chun-Ta Lu, Howard Zhou, Ranjay Krishna, Ariel Fuxman, Tom Duerig
2024 A* conf
CVPR
Imad Eddine Toubal, Aditya Avinash, Neil Gordon Alldrin, Jan Dlabal, Wenlei Zhou, Enming Luo, Otilia Stretcu, Hao Xiong, Chun-Ta Lu, Howard Zhou, Ranjay Krishna, Ariel Fuxman, Tom Duerig
2024 conf
ECCV Workshops (7)
Matej Kristan, Jirí Matas, Pavel Tokmakov, Michael Felsberg, Luka Cehovin Zajc, Alan Lukezic, Khanh-Tung Tran, Xuan-Son Vu, Johanna Björklund, Hyung Jin Chang, Gustavo Fernández, Minasadat Attari, Antoni B. Chan, Liang Chen, Xin Chen, Jaired Collins, Yutao Cui, Ganesh Sai Manas Devarapu, Yinglong Du, Heng Fan, Wan-Cyuan Fan, Zhenhua Feng, Mingqi Gao, Rama Krishna Sai S. Gorthi, Raghav Goyal, Jungong Han, Bijaya Kumar Hatuwal, Zhenyu He, Xiantao Hu, Xingsen Huang, Yuqing Huang, Dongmei Jiang, Ben Kang, Kannappan Palaniappan, Josef Kittler, Simiao Lai, Ning Li, Xiaohai Li, Xin Li, Cheng Liang, Liting Lin, Haibin Ling, Ting Liu, Ziquan Liu, Huchuan Lu, Yifei Luo, Deshui Miao, Juan David Mogollon, Ziqi Pang, Jaswanth Reddy Pochimireddy, Viktor Prutyanov, Gani Rahmon, Aleksandr Romanov, Liangtao Shi, Mennatullah Siam, Leonid Sigal, Arun Kumar Sivapuram, Roman A. Solovyev, Elham Soltani Kazemi, Imad Eddine Toubal, Jia Wan, Limin Wang, Xinying Wang, Yaowei Wang, Yu-Xiong Wang, Zhiquan Wang, Gangshan Wu, Qiangqiang Wu, Xiaojun Wu, Zihao Xia, Jinxia Xie, Chenlong Xu, Tianyang Xu, Yong Xu, Chaocan Xue, Chao Yang, Jinyu Yang, Ming-Hsuan Yang, Chenyang Yu, Ke Yu, Chunhui Zhang, Jiaming Zhang, Zhipeng Zhang, Feng Zheng, Yaozong Zheng, Bineng Zhong, Jinglin Zhou, Junbao Zhou, Yong Zhou, Zikun Zhou, Guibo Zhu, Jiawen Zhu, Xuefeng Zhu, Vladimir V. Zunin
2023 conf
BONBID-HIE@MICCAI
Elham Soltani Kazemi, Imad Eddine Toubal, Gani Rahmon, Taci Kucukpinar, Mohamed Almansour, Mai-Lan Ho, Kannappan Palaniappan
2023 conf
BONBID-HIE@MICCAI
Imad Eddine Toubal, Elham Soltani Kazemi, Gani Rahmon, Taci Kucukpinar, Mohamed Almansour, Mai-Lan Ho, Kannappan Palaniappan
2023 conf
AIPR
Gani Rahmon, Imad Eddine Toubal, D. D. W. Cornelison, Kannappan Palaniappan
2023 conf
AIPR
Elham Soltanikazemi, Ashwin Dhakal, Bijaya Kumar Hatuwal, Imad Eddine Toubal, Armstrong Aboah, Kannappan Palaniappan
2023 conf
ICCV (Workshops)
Matej Kristan, Jirí Matas, Martin Danelljan, Michael Felsberg, Hyung Jin Chang, Luka Cehovin Zajc, Alan Lukezic, Ondrej Drbohlav, Zhongqun Zhang, Khanh-Tung Tran, Xuan-Son Vu, Johanna Björklund, Christoph Mayer, Yushan Zhang, Lei Ke, Jie Zhao, Gustavo Fernández, Noor Al-Shakarji, Dong An, Michael Arens, Stefan Becker, Goutam Bhat, Sebastian Bullinger, Antoni B. Chan, Shijie Chang, Hanyuan Chen, Xin Chen, Yan Chen, Zhenyu Chen, Yangming Cheng, Yutao Cui, Chunyuan Deng, Jiahua Dong, Matteo Dunnhofer, Wei Feng, Jianlong Fu, Jie Gao, Ruize Han, Zeqi Hao, Jun-Yan He, Keji He, Zhenyu He, Xiantao Hu, Kaer Huang, Yuqing Huang, Yi Jiang, Ben Kang, Jin-Peng Lan, Hyungjun Lee, Chenyang Li, Jiahao Li, Ning Li, Wangkai Li, Xiaodi Li, Xin Li, Pengyu Liu, Yue Liu, Huchuan Lu, Bin Luo, Ping Luo, Yinchao Ma, Deshui Miao, Christian Micheloni, Kannappan Palaniappan, Hancheol Park, Matthieu Paul, Houwen Peng, Zekun Qian, Gani Rahmon, Norbert Scherer-Negenborn, Pengcheng Shao, Wooksu Shin, Elham Soltani Kazemi, Tianhui Song, Rainer Stiefelhagen, Rui Sun, Chuanming Tang, Zhangyong Tang, Imad Eddine Toubal, Jack Valmadre, Joost van de Weijer, Luc Van Gool, Jash Vira, Stéphane Vujasinovic, Cheng Wan, Jia Wan, Dong Wang, Fei Wang, Feifan Wang, He Wang, Limin Wang, Song Wang, Yaowei Wang, Zhepeng Wang, Gangshan Wu, Jiannan Wu, Qiangqiang Wu, Xiaojun Wu, Anqi Xiao, Jinxia Xie, Chenlong Xu, Min Xu, Tianyang Xu, Yuanyou Xu, Bin Yan, Dawei Yang, Ming-Hsuan Yang, Tianyu Yang, Yi Yang, Zongxin Yang, Xuanwu Yin, Fisher Yu, Hongyuan Yu, Qianjin Yu, Weichen Yu, Yongsheng Yuan, Zehuan Yuan, Jianlin Zhang, Lu Zhang, Tianzhu Zhang, Guodongfang Zhao, Shaochuan Zhao, Yaozong Zheng, Bineng Zhong, Jiawen Zhu, Xuefeng Zhu, Yueting Zhuang, ChengAo Zong, Kunlong Zuo
2022 conf
EMBC
Linquan Lyu, Imad Eddine Toubal, Kannappan Palaniappan
2021 conf
AIPR
Gani Rahmon, Imad Eddine Toubal, Kannappan Palaniappan
2021 conf
CAIP (2)
Imad Eddine Toubal, Linquan Lyu, Dan Lin, Kannappan Palaniappan
2020 conf
AIPR
Imad Eddine Toubal, Ye Duan, Deshan Yang
2020 conf
AIPR
John K. Lewis, Imad Eddine Toubal, Helen Chen, Vishal Sandesera, Michael Lomnitz, Zigfried Hampel-Arias, Prasad Calyam, Kannappan Palaniappan
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