M. Reha Civanlar

85 papers A 1B 9C 5Misc 14Journal 34Unranked 22
YearRankTypeTitle / Venue / Authors
2021 J jnl
IEEE Commun. Lett.
Ali Arsal, M. Reha Civanlar, Murat Uysal
2020 B ed.
NOSSDAV
M. Reha Civanlar, Lucile Sassatelli, Jong-Seok Lee
2011 J jnl
J. Netw. Comput. Appl.
Istemi Ekin Akkus, Öznur Özkasap, M. Reha Civanlar
2010 J jnl
IEEE Trans. Multim.
Hulya Seferoglu, Athina Markopoulou, Ulas C. Kozat, M. Reha Civanlar, James Kempf
2009 conf
CCNC
Oztan Harmanci, Sandeep Kanumuri, Ulas C. Kozat, Mehmet Umut Demircin, M. Reha Civanlar
2009 J jnl
IEEE Trans. Multim.
Ulas C. Kozat, Oztan Harmanci, Sandeep Kanumuri, Mehmet Umut Demircin, M. Reha Civanlar
2008 J jnl
Proc. IEEE
Pascal Frossard, Juan Carlos De Martin, M. Reha Civanlar
2008 B conf
ICIP
Ankur Saxena, Onur G. Guleryuz, M. Reha Civanlar
2007 J jnl
IEEE Signal Process. Mag.
A. Murat Tekalp, Engin Kurutepe, M. Reha Civanlar
2007 J jnl
J. Sel. Topics Signal Processing
Emrah Akyol, A. Murat Tekalp, M. Reha Civanlar
2007 J jnl
IEEE Trans. Circuits Syst. Video Technol.
Engin Kurutepe, M. Reha Civanlar, A. Murat Tekalp
2007 J jnl
EURASIP J. Adv. Signal Process.
Emrah Akyol, A. Murat Tekalp, M. Reha Civanlar
2007 J jnl
IEEE J. Sel. Areas Commun.
Tanir Ozcelebi, M. Oguz Sunay, A. Murat Tekalp, M. Reha Civanlar
2007 J jnl
IEEE Trans. Multim.
Tanir Ozcelebi, A. Murat Tekalp, M. Reha Civanlar
2007 J jnl
J. Netw. Comput. Appl.
M. Can Filibeli, Öznur Özkasap, M. Reha Civanlar
2007 J jnl
Signal Process. Image Commun.
Anil Aksay, Selen Pehlivan, Engin Kurutepe, Cagdas Bilen, Tanir Ozcelebi, Gozde Bozdagi Akar, M. Reha Civanlar, A. Murat Tekalp
2007 conf
ICIP (1)
Emrah Akyol, Onur G. Guleryuz, M. Reha Civanlar
2007 conf
ICIP (3)
Engin Kurutepe, M. Reha Civanlar, A. Murat Tekalp
2007 J jnl
Signal Process. Image Commun.
M. Reha Civanlar, Jörn Ostermann, Haldun M. Özaktas, Aljoscha Smolic, John Watson
2007 J jnl
IEEE Trans. Circuits Syst. Video Technol.
Gozde Bozdagi Akar, A. Murat Tekalp, Christoph Fehn, M. Reha Civanlar
2006 B conf
ICIP
Emrah Akyol, A. Murat Tekalp, M. Reha Civanlar
2006 conf
ICC
Tanir Ozcelebi, Fabio De Vito, A. Murat Tekalp, M. Reha Civanlar, M. Oguz Sunay, Juan Carlos De Martin
2006 B conf
ICIP
Tanir Ozcelebi, M. Oguz Sunay, M. Reha Civanlar, A. Murat Tekalp
2006 A conf
ICME
Selen Pehlivan, Anil Aksay, Cagdas Bilen, Gozde Bozdagi Akar, M. Reha Civanlar
2006 conf
MRCS
Engin Kurutepe, M. Reha Civanlar, A. Murat Tekalp
2006 B conf
ICIP
Istemi Ekin Akkus, M. Reha Civanlar, Öznur Özkasap
2006 conf
ICC
Ali C. Begen, Mehmet A. Begen, Yucel Altunbasak, M. Reha Civanlar
2006 C conf
ISM
Burak Gorkemli, M. Reha Civanlar
2006 conf
MRCS
Istemi Ekin Akkus, Öznur Özkasap, M. Reha Civanlar
2006 conf
EUSIPCO
Anil Aksay, Cagdas Bilen, Engin Kurutepe, Tanir Ozcelebi, Gozde Bozdagi Akar, M. Reha Civanlar, A. Murat Tekalp
2005 conf
EUSIPCO
Tanir Ozcelebi, A. Murat Tekalp, M. Reha Civanlar, M. Oguz Sunay
2005 conf
EUSIPCO
Engin Kurutepe, M. Reha Civanlar, A. Murat Tekalp
2005 conf
VLBV
Tanir Ozcelebi, Fabio De Vito, M. Oguz Sunay, A. Murat Tekalp, M. Reha Civanlar, Juan Carlos De Martin
2005 B conf
GLOBECOM
Tanir Özçelebi, M. Oguz Sunay, A. Murat Tekalp, M. Reha Civanlar
2005 conf
Parallel and Distributed Computing and Networks
M. Can Filibeli, Öznur Özkasap, M. Reha Civanlar
2005 conf
ICIP (1)
Ali C. Begen, Yucel Altunbasak, M. Reha Civanlar, Gükçe Görbil
2005 conf
ICIP (1)
Tanir Ozcelebi, M. Reha Civanlar, A. Murat Tekalp
2005 conf
EUSIPCO
Emrah Akyol, A. Murat Tekalp, M. Reha Civanlar
2005 conf
Parallel and Distributed Computing and Networks
Tahir Celebi, M. Reha Civanlar, Öznur Özkasap
2005 J jnl
Signal Process. Image Commun.
M. Reha Civanlar, Öznur Özkasap, Tahir Celebi
2005 conf
VLBV
Fabio De Vito, Tanir Ozcelebi, M. Reha Civanlar, A. Murat Tekalp, Juan Carlos De Martin
2005 conf
EUSIPCO
Bugra Tari, Yücel Yemez, Öznur Özkasap, M. Reha Civanlar
2005 conf
ICIP (3)
Emrah Akyol, A. Murat Tekalp, M. Reha Civanlar
2004 conf
ICIAR (1)
Çagatay Dikici, H. Isil Bozma, M. Reha Civanlar
2004 B conf
ICIP
Emrah Akyol, A. Murat Tekalp, M. Reha Civanlar
2004 C conf
MMSP
Tanir Özçelebi, A. Murat Tekalp, M. Reha Civanlar
2004 B conf
ICIP
Tanir Ozcelebi, A. Murat Tekalp, M. Reha Civanlar
2004 B conf
ICIP
M. Reha Civanlar, Öznur Özkasap, Tahir Celebi
2003 J jnl
Signal Process. Image Commun.
Yucel Altunbasak, Chang Wen Chen, M. Reha Civanlar, King N. Ngan
2002 conf
ISCAS (1)
Jörn Ostermann, Jürgen Rurainsky, M. Reha Civanlar
2002 J jnl
Signal Process. Image Commun.
Yucel Altunbasak, Chang Wen Chen, M. Reha Civanlar, King Ngi Ngan
2001 C conf
VCIP
Atul Puri, Robert L. Schmidt, Andrea Basso, M. Reha Civanlar
2001 J jnl
IEEE Trans. Circuits Syst. Video Technol.
M. Reha Civanlar, Ajay Luthra, Stephan Wenger, Wenwu Zhu
2001 J jnl
Eur. Trans. Telecommun.
Glenn L. Cash, M. Reha Civanlar, Barry G. Haskell, David A. Kapilow, Richard V. Kollarits, Baldine B. Paul, Cassandra T. Swain
2000 J jnl
IEEE Signal Process. Mag.
M. Reha Civanlar, Glenn L. Cash, Richard V. Kollarits, Baldine B. Paul, Cassandra T. Swain, Barry G. Haskell, D. A. Kapilov
2000 J jnl
RFC
M. Reha Civanlar, Glenn L. Cash
1999 J jnl
Signal Process. Image Commun.
Andrea Basso, Glenn L. Cash, M. Reha Civanlar
1999 C conf
MMSP
Baldine B. Paul, David C. Gibbon, Glenn L. Cash, M. Reha Civanlar
1998 J jnl
RFC
M. Reha Civanlar, Glenn L. Cash, Barry G. Haskell
1998 J jnl
IEEE Trans. Circuits Syst. Video Technol.
Barry G. Haskell, Paul G. Howard, Yann LeCun, Atul Puri, Jörn Ostermann, M. Reha Civanlar, Lawrence R. Rabiner, Léon Bottou, Patrick Haffner
1998 Misc conf
ICASSP
M. Reha Civanlar
1998 J jnl
RFC
M. Reha Civanlar, Glenn L. Cash, Barry G. Haskell
1998 J jnl
RFC
Don Hoffman, Gerard Fernando, Vivek K. Goyal, M. Reha Civanlar
1998 C conf
MMSP
Baldine B. Paul, M. Reha Civanlar
1997 J jnl
J. VLSI Signal Process.
M. Reha Civanlar, Robert D. Gaglianello, Glenn L. Cash
1997 J jnl
IEEE Signal Process. Mag.
M. Reha Civanlar, Amy R. Reibman
1996 J jnl
Signal Process. Image Commun.
M. Reha Civanlar, Glenn L. Cash
1996 J jnl
IEEE Trans. Circuits Syst. Video Technol.
R. Aravind, M. Reha Civanlar, Amy R. Reibman
1995 J jnl
IEEE Trans. Commun.
Ramin A. Nobakht, M. Reha Civanlar
1994 conf
ICIP (1)
Homer H. Chen, M. Reha Civanlar, Barry G. Haskell
1991 Misc conf
ICASSP
Patrick L. Combettes, M. Reha Civanlar
1990 Misc conf
ICASSP
M. Reha Civanlar, Hayder Radha
1988 Misc conf
ICASSP
M. Reha Civanlar, Ramin A. Nobakht
1988 Misc conf
ICASSP
Sarah A. Rajala, M. Reha Civanlar, Wonrae M. Lee
1987 Misc conf
ICASSP
Sarah A. Rajala, M. Reha Civanlar, Wonrae M. Lee
1987 Misc conf
ICASSP
H. Joel Trussell, M. Reha Civanlar
1987 Misc conf
ICASSP
H. Joel Trussell, Hatice Örün Öztürk, M. Reha Civanlar
1986 Misc conf
ICASSP
M. Reha Civanlar, H. Joel Trussell
1986 J jnl
IEEE Trans. Acoust. Speech Signal Process.
M. Reha Civanlar, Henry Joel Trussell
1985 Misc conf
ICASSP
M. Reha Civanlar, P. Santago
1985 Misc conf
ICASSP
H. Joel Trussell, M. Reha Civanlar
1985 Misc conf
ICASSP
M. Reha Civanlar, H. Joel Trussell
1985 J jnl
IEEE Trans. Acoust. Speech Signal Process.
H. Joel Trussell, M. Reha Civanlar
1984 Misc conf
ICASSP
H. Joel Trussell, M. Reha Civanlar
1983 Misc conf
ICASSP
H. Joel Trussell, M. Reha Civanlar
redb/extractors/decompiler/_archive/DecompileGhidra.py
← Index redb/extractors/decompiler/_archive/DecompileGhidra.py python
from hashlib import sha256, md5
import inspect
import subprocess
import json
import os
import time
from datetime import datetime, timezone
from typing import Dict, List, Any, Optional

from dotenv import load_dotenv
from redb.extractors.enum import Tag
from redb.extractors.extractor import Extractor
import magic
import pefile
import ppdeep
import tlsh


class DecompileGhidra(Extractor):
    def __init__(
        self,
        filepath,
        log,
        exporters=None,
        index_prefix=None,
        elastic_index=None,
        known_benign=False,
        known_malicious=False,
        filetype=None,
    ):
        super().__init__(
            filepath,
            log,
            exporters,
            index_prefix,
            elastic_index,
            known_benign,
            known_malicious,
        )
        self.log.debug(inspect.currentframe().f_code.co_name)
        self.ghidra_path = os.getenv("GHIDRA_PATH", "/opt/ghidra")
        self.java_script_path = os.getenv(
            "GHIDRA_SCRIPT_PATH",
            "/opt/ghidra/Ghidra/Features/Base/ghidra_scripts/GhidraDecompilerScript.java",
        )
        self.analysis_results = None
        self.ghidra_process = None  # Track the current process
        self.project_path = None
        self.filetype = filetype

        # Convert TIMEOUT to integer with a default of 1200 seconds (20 minutes)
        try:
            self.TIMEOUT = int(os.getenv("GHIDRA_TIMEOUT", "1200"))
        except ValueError:
            self.log.warning(
                "Invalid GHIDRA_TIMEOUT value, using default of 1200 seconds"
            )
            self.TIMEOUT = 1200

        self.initialize_project()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cleanup_run()

    def is_dotnet(self):
        try:
            if self.filetype == "pebin":
                file_type = magic.from_buffer(self.binary)
                if ".Net" in file_type:
                    return True
                pe = pefile.PE(self.filepath)
                for entry in pe.OPTIONAL_HEADER.DATA_DIRECTORY:
                    # IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR is typically 14
                    if (
                        entry.name == "IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR"
                        and entry.Size > 0
                    ):
                        return True
                return False
        except AttributeError as e:
            self.log.error(
                f"AttributeError error dotnet file {self.hash.sha256} Full error : {e}"
            )
            return False

    def cleanup_run(self):
        """Clean up after analysis."""
        try:
            if self.ghidra_process and self.ghidra_process.poll() is None:
                self.ghidra_process.terminate()
                try:
                    self.ghidra_process.wait(timeout=5)
                except subprocess.TimeoutExpired:
                    self.ghidra_process.kill()

            # Clean up project directory
            if self.project_path and os.path.exists(self.project_path):
                import shutil

                shutil.rmtree(self.project_path)
                self.log.debug(f"Cleaned up project directory: {self.project_path}")

            # Force garbage collection
            import gc

            gc.collect()
        except Exception as e:
            self.log.error(f"Error in cleanup: {e}")

    # @classmethod
    # def cleanup_batch(cls):
    #     """Clean up the persistent project at the end of a batch."""
    #     print(f"Cleaning up Ghidra project for batch")
    #     if cls._project_path and os.path.exists(cls._project_path):
    #         try:
    #             import shutil
    #             shutil.rmtree(cls._project_path)
    #             cls._project_initialized = False
    #             cls._project_path = None
    #         except Exception as e:
    #             print(f"Error cleaning up project: {e}")

    def _get_environment(self):
        """Setup and return the environment for Ghidra."""
        env = os.environ.copy()
        java_home = os.getenv("GHIDRA_JAVA_HOME", "/usr/lib/jvm/java-17-openjdk-amd64")
        env.update(
            {
                "JAVA_HOME": java_home,
                "PATH": f"{java_home}/bin:{env['PATH']}",
                "LD_LIBRARY_PATH": f"{java_home}/lib:{env.get('LD_LIBRARY_PATH', '')}",
            }
        )
        # Print environment variables for debugging
        self.log.debug(f"JAVA_HOME: {env['JAVA_HOME']}")
        self.log.debug(f"PATH: {env['PATH']}")
        self.log.debug(f"LD_LIBRARY_PATH: {env['LD_LIBRARY_PATH']}")

        return env

    def initialize_project(self):
        """Initialize a temporary Ghidra project for this file."""
        # Create unique project directory
        self.project_path = f"/tmp/ghidra_{os.path.basename(self.filepath)}_{str(int(time.time()))}_{os.getpid()}"
        os.makedirs(self.project_path, exist_ok=True)
        self.log.debug(f"Created temporary project at {self.project_path}")

        # Create a minimal initialization file
        init_file = os.path.join(self.project_path, ".init")
        with open(init_file, "wb") as f:
            f.write(bytes([0x7F, 0x45, 0x4C, 0x46]))  # Valid ELF header magic bytes

        # Initialize project with minimal file
        env = self._get_environment()
        cmd = [
            f"{self.ghidra_path}/support/analyzeHeadless",
            self.project_path,
            "TempProject",
            "-import",
            init_file,
        ]

        try:
            result = subprocess.run(cmd, env=env, capture_output=True, text=True)
            if result.returncode != 0:
                self.log.error(f"Failed to initialize project: {result.stderr}")
                raise RuntimeError("Project initialization failed")

            # Clean up initialization file
            os.remove(init_file)
            self.log.debug("Project initialized successfully")

        except Exception as e:
            self.log.error(f"Error initializing project: {e}")
            raise

    def analyze_binary(self) -> Optional[Dict[str, Any]]:
        """Run Ghidra analysis and return results."""
        self.log.debug("Starting binary analysis")

        # # Check if packed
        # if self.check_binary_protection():
        #     self.log.warning("Skipping protected binary")
        #     return None

        # Check for .NET only if needed
        # if self.is_dotnet():
        #     self.MAX_NAMED_ARG_WARNINGS = 10000  # Higher threshold for .NET
        #     self.log.info("Adjusting parameters for .NET binary")
        # else:
        #     self.MAX_NAMED_ARG_WARNINGS = 1000  # Normal threshold

        if not os.path.exists(self.java_script_path):
            self.log.error(f"Java script not found: {self.java_script_path}")
            return None

        env = self._get_environment()

        try:
            base_cmd = [
                f"{self.ghidra_path}/support/analyzeHeadless",
                self.project_path,
                "TempProject",
                "-import",
                self.filepath,
                "-scriptPath",
                os.path.dirname(self.java_script_path),
                "-postScript",
                self.java_script_path,
                self.sha256,
                self.filepath,
            ]
            return self.run_ghidra(base_cmd, env)

        except Exception as e:
            self.log.error(f"Error in Ghidra analysis: {e}")
            return None

        finally:
            self.cleanup_run()

    def run_ghidra(
        self, cmd: list, env: Optional[Dict[str, str]] = None
    ) -> Optional[Dict[str, Any]]:
        """Run Ghidra process and capture JSON output with improved logging separation."""
        process = None
        try:
            self.log.info(f"Starting Ghidra analysis: {' '.join(cmd)}")
            start_time = time.time()

            process = subprocess.Popen(
                cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
            )
            self.ghidra_process = process

            warning_counter = 0
            named_arg_counter = 0
            # Read all output lines
            json_output = None
            while True:
                line = process.stdout.readline()
                if not line and process.poll() is not None:
                    break

                stripped_line = line.strip()
                if not stripped_line:
                    continue

                # if 'Invalid FieldOrProp value in NamedArg' in stripped_line:
                #     named_arg_counter += 1
                #     if named_arg_counter > self.MAX_NAMED_ARG_WARNINGS:
                #         self.log.error(f"Too many NamedArg warnings ({named_arg_counter}), possible protected file.")
                #         self.ghidra_process.kill()
                #         return None
                if (
                    stripped_line.startswith("{")
                    and '"sha256"' in stripped_line
                    and '"decompiled"' in stripped_line
                ):
                    # This is our actual JSON output from GhidraDecompilerScript
                    json_output = stripped_line
                elif any(level in stripped_line for level in ["INFO", "WARN", "ERROR"]):
                    # Ghidra framework logging
                    log_level = (
                        "debug"
                        if "INFO" in stripped_line
                        else "warning"
                        if "WARN" in stripped_line
                        else "error"
                    )
                    if log_level == "warning" and any(
                        expected in stripped_line
                        for expected in [
                            "Unable to disassemble EXTERNAL block",
                            "Failed to markup ELF Note",
                            "Invalid FieldOrProp value in NamedArg",
                            "Unable to resolve constructor",
                            "Could not follow disassembly flow into non-existing memory",
                            "Unable to read bytes at ram",
                        ]
                    ):
                        # Skip expected warnings
                        continue

                    getattr(self.log, log_level)(f"Ghidra info: {stripped_line}")

            # Process completion and stderr
            try:
                stderr = process.stderr.read()
                process.wait(timeout=self.TIMEOUT)

                if stderr:
                    for line in stderr.splitlines():
                        stripped_line = line.strip()
                        if not stripped_line:
                            continue
                        if "ERROR" in stripped_line:
                            self.log.error(f"Ghidra stderr: {stripped_line}")
                        elif "WARN" in stripped_line:
                            self.log.warning(f"Ghidra stderr: {stripped_line}")
                        else:
                            self.log.debug(f"Ghidra stderr: {stripped_line}")

            except subprocess.TimeoutExpired:
                process.kill()
                self.log.error("Ghidra analysis timed out")
                return None

            elapsed_time = time.time() - start_time
            self.log.debug(f"Ghidra analysis completed in {elapsed_time:.2f}s")

            # Parse JSON output if we found it
            if json_output:
                try:
                    result = json.loads(json_output)
                    # Validate the required structure
                    if not isinstance(result, dict) or not all(
                        k in result
                        for k in ["sha256", "decompiled", "disassembled", "cfg"]
                    ):
                        self.log.error("Invalid JSON structure from Ghidra")
                        return None
                    return result
                except json.JSONDecodeError as e:
                    self.log.error(f"Failed to parse Ghidra JSON output: {e}")
                    return None
            else:
                self.log.error("No JSON output received from Ghidra")
                return None

        except Exception as e:
            self.log.error(f"Error running Ghidra: {str(e)}")
            if hasattr(e, "__traceback__"):
                import traceback

                self.log.debug(
                    f"Traceback: {''.join(traceback.format_tb(e.__traceback__))}"
                )
            return None

        finally:
            if process:
                try:
                    # Ensure pipes are closed
                    if process.stdout:
                        process.stdout.close()
                    if process.stderr:
                        process.stderr.close()
                    # Terminate process if still running
                    if process.poll() is None:
                        process.terminate()
                        try:
                            process.wait(timeout=5)
                        except subprocess.TimeoutExpired:
                            process.kill()
                except Exception as e:
                    self.log.error(f"Error cleaning up Ghidra process: {e}")

    def extract(self) -> bool:
        """Extract and process all analysis results."""
        self.log.debug(inspect.currentframe().f_code.co_name)
        try:
            results = self.analyze_binary()
            if not results:
                return False

            self.analysis_results = results
            return True

        except Exception as e:
            self.log.error(f"Error in extraction: {e}")
            return False

    def prepare_export_data(self, exporter_type: str) -> Any:
        """Prepare data for database export."""
        self.log.debug(inspect.currentframe().f_code.co_name)
        if not self.analysis_results:
            return None

        if exporter_type == "ClickHouseExporter":
            now = datetime.now(timezone.utc)

            def prepare_array_field(value, array_type):
                """Helper to prepare array fields with proper null handling"""
                if value is None:
                    return []
                return value

            def ssdeep_disassembly(func):
                try:
                    if len(func) > 1:
                        return ppdeep.hash(func)
                    return ""
                except Exception as e:
                    self.log.error(f"Error in disassembly ssdeep hash calculation: {e}")
                    return ""

            def tlsh_disassembly(func):
                try:
                    if len(func) >= 50:
                        return tlsh.hash(func.encode("utf-8"))
                    return ""
                except Exception as e:
                    self.log.error(f"Error in disassembly tlsh hash calculation: {e}")
                    return ""

            return {
                "multi_table": True,
                "decompiled_content": {
                    "table": "decompiled_functions_content",
                    "data": [
                        [
                            f["decompiled_content_hash"],
                            f["decompiled_function"],
                            f["function_type"],
                            now,
                        ]
                        for f in self.analysis_results["decompiled"]
                    ],
                    "column_names": [
                        "decompiled_content_hash",
                        "decompiled_function",
                        "function_type",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "String",
                        "Enum8('USER'=1, 'LIBRARY'=2, 'THUNK'=3, 'EXTERNAL'=4, 'UNKNOWN'=5)",
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "decompiled_refs": {
                    "table": "decompiled_functions_references",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            f["decompiled_content_hash"],
                            f["decompiled_function_name"],
                            f["decompiled_function_address"],
                            now,
                        ]
                        for f in self.analysis_results["decompiled"]
                    ],
                    "column_names": [
                        "sha256",
                        "decompiled_content_hash",
                        "decompiled_function_name",
                        "decompiled_function_address",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(64)",
                        "LowCardinality(String)",
                        "String",
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "disassembled_content": {
                    "table": "disassembled_functions_content",
                    "data": [
                        [
                            f["disassembled_content_hash"],
                            f["fully_normalized_content_hash"],
                            f["api_normalized_content_hash"],
                            f["category_normalized_content_hash"],
                            f.get("disassembled_function", ""),
                            f.get("fully_normalized_disassembly", ""),
                            f.get("api_normalized_disassembly", ""),
                            f.get("category_normalized_disassembly", ""),
                            ssdeep_disassembly(f.get("disassembled_function", "")),
                            tlsh_disassembly(f.get("disassembled_function", "")),
                            ssdeep_disassembly(
                                f.get("fully_normalized_disassembly", "")
                            ),
                            tlsh_disassembly(f.get("fully_normalized_disassembly", "")),
                            f.get("function_type", "UNKNOWN"),
                            f.get("instruction_count", 0),
                            prepare_array_field(
                                f.get("instruction_types"), "LowCardinality(String)"
                            ),
                            f.get("control_flow_count", 0),
                            prepare_array_field(
                                f.get("memory_access_pattern"), "LowCardinality(String)"
                            ),
                            prepare_array_field(
                                f.get("register_usage"), "LowCardinality(String)"
                            ),
                            f.get("data_references_count", 0),
                            # prepare_array_field(f.get('opcode_frequency_vector'), 'Float32'),
                            # prepare_array_field(f.get('api_calls_vector'), 'Float32'),
                            # prepare_array_field(f.get('minhash_signature'), 'UInt64'),
                            # f.get('pic_hash', ''),
                            f.get("max_block_size", 0),
                            f.get("num_calls", 0),
                            f.get("stack_size", 0),
                            # prepare_array_field(f.get('instruction_type_ratios'), 'Float32'),
                            # prepare_array_field(f.get('instruction_embedding'), 'Float32'),
                            now,
                        ]
                        for f in self.analysis_results["disassembled"]
                    ],
                    "column_names": [
                        "disassembled_content_hash",
                        "fully_normalized_content_hash",
                        "api_normalized_content_hash",
                        "category_normalized_content_hash",
                        "disassembled_function",
                        "fully_normalized_disassembly",
                        "api_normalized_disassembly",
                        "category_normalized_disassembly",
                        "ssdeep_disassembly",
                        "tlsh_disassembly",
                        "ssdeep_fully_normalized",
                        "tlsh_fully_normalized",
                        "function_type",
                        "instruction_count",
                        "instruction_types",
                        "control_flow_count",
                        "memory_access_pattern",
                        "register_usage",
                        "data_references_count",
                        # 'opcode_frequency_vector',
                        # 'api_calls_vector',
                        # 'minhash_signature',
                        # 'pic_hash',
                        "max_block_size",
                        "num_calls",
                        "stack_size",
                        # 'instruction_type_ratios',
                        # 'instruction_embedding',
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(64)",
                        "FixedString(64)",
                        "FixedString(64)",
                        "String",
                        "String",
                        "String",
                        "String",
                        "Nullable(String)",
                        "Nullable(FixedString(72))",
                        "Nullable(String)",
                        "Nullable(FixedString(72))",
                        "Enum8('USER'=1, 'LIBRARY'=2, 'THUNK'=3, 'EXTERNAL'=4, 'UNKNOWN'=5)",
                        "UInt32",
                        "Array(LowCardinality(String))",
                        "UInt32",
                        "Array(LowCardinality(String))",
                        "Array(LowCardinality(String))",
                        "UInt32",
                        # 'Array(Float32)',
                        # 'Array(Float32)',
                        # 'Array(UInt64)',
                        # 'Nullable(FixedString(16))',
                        "Nullable(UInt32)",
                        "Nullable(UInt32)",
                        "Nullable(Int32)",
                        # 'Array(Float32)',
                        # 'Array(Float32)',
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "disassembled_refs": {
                    "table": "disassembled_functions_references",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            f["disassembled_content_hash"],
                            f["fully_normalized_content_hash"],
                            f["api_normalized_content_hash"],
                            f["category_normalized_content_hash"],
                            f["disassembled_function_name"],
                            f["disassembled_function_address"],
                            now,
                        ]
                        for f in self.analysis_results["disassembled"]
                    ],
                    "column_names": [
                        "sha256",
                        "disassembled_content_hash",
                        "fully_normalized_content_hash",
                        "api_normalized_content_hash",
                        "category_normalized_content_hash",
                        "disassembled_function_name",
                        "disassembled_function_address",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(64)",
                        "FixedString(64)",
                        "FixedString(64)",
                        "FixedString(64)",
                        "LowCardinality(String)",
                        "String",
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "cfg_blocks": {
                    "table": "cfg_blocks",
                    "data": [
                        [
                            b["block_id"],
                            self.analysis_results["sha256"],
                            b["function_address"],
                            b["block_start_address"],
                            b["block_end_address"],
                            b["block_size"],
                            b["block_instructions"],
                            b["fully_normalized_instructions"],
                            b["api_normalized_instructions"],
                            b["category_normalized_instructions"],
                            b.get("predecessor_blocks", []),
                            b.get("successor_blocks", []),  # Use empty array as default
                            b.get("is_entry_block", False),
                            b.get("is_exit_block", False),
                            b.get("branch_type", "UNKNOWN"),
                            b.get("referenced_constants", []),
                            b.get("sign", 1),  # Use 1 as default for sign
                            now,
                        ]
                        for b in self.analysis_results["cfg"]
                    ],
                    "column_names": [
                        "block_id",
                        "sha256",
                        "function_address",
                        "block_start_address",
                        "block_end_address",
                        "block_size",
                        "block_instructions",
                        "fully_normalized_instructions",
                        "api_normalized_instructions",
                        "category_normalized_instructions",
                        "predecessor_blocks",
                        "successor_blocks",
                        "is_entry_block",
                        "is_exit_block",
                        "branch_type",
                        "referenced_constants",
                        "sign",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(64)",
                        "String",
                        "String",
                        "String",
                        "UInt32",
                        "String",
                        "Nullable(String)",
                        "Nullable(String)",
                        "Nullable(String)",
                        "Array(String)",
                        "Array(String)",
                        "Bool",
                        "Bool",
                        "Enum8('DIRECT'=1, 'CONDITIONAL'=2, 'CALL'=3, 'RETURN'=4, 'FALLTHROUGH'=5, 'UNKNOWN'=6)",
                        "Array(String)",
                        "Int8",
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "function_analysis_errors": {
                    "table": "function_analysis_errors",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            f["function_name"],
                            f["function_address"],
                            f["error_location"],
                            f.get(
                                "error_message", ""
                            ),  # it could be empty, how to handle it?
                            f.get("error_details", ""),
                            f.get("error_type", "unknown"),
                            md5(
                                f"{f['error_message']}{f['function_name']}{f['function_address']}{f['error_location']}".encode()
                            ).hexdigest(),
                            "new",
                            now,
                        ]
                        for f in self.analysis_results["errors"]
                    ],
                    "column_names": [
                        "sha256",
                        "function_name",
                        "function_address",
                        "error_location",
                        "error_message",
                        "error_details",
                        "error_type",
                        "error_hash",
                        "status",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "Nullable(String)",
                        "String",
                        "LowCardinality(String)",
                        "Nullable(String)",
                        "Nullable(String)",
                        "Nullable(String)",
                        "FixedString(32)",
                        "Enum8('new'=1, 'investigating'=2, 'fixed'=3, 'wontfix'=4)",
                        "DateTime64(3, 'UTC')",
                    ],
                },
            }

    def tag(self) -> str:
        """Return the tag for this extractor."""
        return Tag.DECOMPILED.value

    def get_clickhouse_table(self) -> str:
        """Not used directly as we're handling multiple tables."""
        pass