Kamel Hamrouni

79 papers A 1B 3C 9Misc 10Journal 21Unranked 35
YearRankTypeTitle / Venue / Authors
2022 conf
SSD
Hela Boulehmi, Hela Mahersia, Kamel Hamrouni
2020 Misc conf
ATSIP
Amira Oueslati, Nadia Feddaoui, Safya Belghith, Kamel Hamrouni
2020 conf
SSD
Hela Mahersia, Hela Boulehmi, Kamel Hamrouni
2020 J jnl
Int. J. Intell. Syst. Technol. Appl.
Amira Oueslati, Nadia Feddaoui, Kamel Hamrouni, Safya Belghith
2019 J jnl
Int. J. Medical Eng. Informatics
Hana Ouazzane, Hela Mahersia, Kamel Hamrouni
2019 J jnl
Multim. Tools Appl.
Wael Brahim, Nacim Betrouni, Makram Mestiri, Kamel Hamrouni
2018 conf
ICIAR
Ines Channoufi, Sami Bourouis, Nizar Bouguila, Kamel Hamrouni
2018 conf
SSD
Hela Boulehmi, Hela Mahersia, Kamel Hamrouni
2018 Misc conf
ATSIP
Ines Channoufi, Sami Bourouis, Nizar Bouguila, Kamel Hamrouni
2018 Misc conf
ATSIP
Naouel Boughattas, Maxime Berar, Kamel Hamrouni, Su Ruan
2018 J jnl
Multim. Tools Appl.
Ines Channoufi, Sami Bourouis, Nizar Bouguila, Kamel Hamrouni
2018 J jnl
Comput. Medical Imaging Graph.
Wael Brahim, Makram Mestiri, Nacim Betrouni, Kamel Hamrouni
2018 J jnl
Comput. Methods Programs Biomed.
Marwa Hmida, Kamel Hamrouni, Basel Solaiman, Sana Boussetta
2018 Misc conf
ATSIP
Yosra Ben Salem, Rihab Idoudi, Karim Saheb Ettabaâ, Kamel Hamrouni, Basel Soleiman
2018 C conf
ICISP
Ines Channoufi, Sami Bourouis, Nizar Bouguila, Kamel Hamrouni
2017 conf
TSP
Ouazaa Hibet-Allah, Hejer Jlassi, Kamel Hamrouni
2017 conf
SoCPaR
Amira Oueslati, Nadia Feddaoui, Kamel Hamrouni
2017 Misc conf
ATSIP
Hana Ouazzane, Hela Mahersia, Kamel Hamrouni
2017 C conf
ICMV
Marwa Hmida, Kamel Hamrouni, Basel Solaiman, Sana Boussetta
2017 Misc conf
SAC
Hana Ouazzane, Hela Mahersia, Kamel Hamrouni
2017 C conf
AICCSA
Amira Oueslati, Nadia Feddaoui, Kamel Hamrouni
2017 Misc conf
ATSIP
Wael Brahim, Makram Mestiri, Nacim Betrouni, Kamel Hamrouni
2017 conf
EMCIS
Yosra Ben Salem, Rihab Idoudi, Karim Saheb Ettabaâ, Kamel Hamrouni, Basel Solaiman
2017 C conf
ICMV
Ouazaa Hibet-Allah, Hajer Jlassi, Kamel Hamrouni
2016 J jnl
Int. J. Web Appl.
Rihab Idoudi, Karim Saheb Ettabaâ, Basel Solaiman, Kamel Hamrouni, Najla Mnif
2016 J jnl
IET Image Process.
Nefissa Khiari Hili, Sylvie Lelandais, Christophe Montagne, Corinne Roumes, Kamel Hamrouni, Justin Plantier
2016 C conf
IPAS
Hela Boulehmi, Hela Mahersia, Kamel Hamrouni
2016 conf
ICMCS
Ines Channoufi, Sami Bourouis, Kamel Hamrouni, Nizar Bouguila
2016 J jnl
Comput. Methods Programs Biomed.
Hela Mahersia, Hela Boulehmi, Kamel Hamrouni
2016 conf
ICEIS (1)
Rihab Idoudi, Karim Saheb Ettabaâ, Kamel Hamrouni, Basel Solaiman
2016 conf
SITA
Yosra Ben Salem, Rihab Idoudi, Kamel Hamrouni, Basel Soleiman, Sana Boussetta
2016 C conf
IPAS
Yosra Ben Salem, Kamel Hamrouni, Basel Solaiman
2016 C conf
ICMV
Marwa Hmida, Kamel Hamrouni, Basel Solaiman, Sana Boussetta
2016 B conf
KES
Rihab Idoudi, Karim Saheb Ettabaâ, Basel Solaiman, Kamel Hamrouni
2016 J jnl
Int. J. Comput. Intell. Syst.
Rihab Idoudi, Karim Saheb Ettabaâ, Basel Solaiman, Kamel Hamrouni
2016 conf
VISIGRAPP (4: VISAPP)
Amira Oueslati, Nadia Feddaoui, Kamel Hamrouni
2016 conf
SSD
Takwa Chihaoui, Hejer Jlassi, Rostom Kachouri, Kamel Hamrouni, Mohamed Akil
2016 conf
IPTA
Nefissa Khiari Hili, Christophe Montagne, Sylvie Lelandais, Kamel Hamrouni
2016 C conf
IPAS
Wael Brahim, Makram Mestiri, Nacim Betrouni, Kamel Hamrouni
2016 Misc conf
ATSIP
Ouazaa Hibet-Allah, Hejer Jlassi, Kamel Hamrouni
2015 conf
IPTA
Marwa Hmida, Kamel Hamrouni, Basel Solaiman
2015 conf
IPTA
Takwa Chihaoui, Rostom Kachouri, Hejer Jelassi, Mohamed Akil, Kamel Hamrouni
2015 conf
ICIG (2)
Saïd Ettaïeb, Kamel Hamrouni, Su Ruan
2015 J jnl
IET Comput. Vis.
Amira Belhedi, Adrien Bartoli, Steve Bourgeois, Vincent Gay-Bellile, Kamel Hamrouni, Patrick Sayd
2015 J jnl
Eng. Appl. Artif. Intell.
Hela Mahersia, Kamel Hamrouni
2014 conf
SoCPaR
Sourour Khouaja, Hajer Jlassi, Kamel Hamrouni
2014 C conf
IPAS
Rihab Idoudi, Karim Saheb Ettabaâ, Kamel Hamrouni, Basel Solaiman
2014 B conf
ICIP
Naouel Boughattas, Maxime Berar, Kamel Hamrouni, Su Ruan
2014 Misc conf
ATSIP
Takwa Chihaoui, Sami Bourouis, Kamel Hamrouni
2014 J jnl
Int. J. Data Min. Model. Manag.
Nadia Feddaoui, Kamel Hamrouni
2014 conf
ICMCS
Saïd Ettaïeb, Kamel Hamrouni, Su Ruan
2014 Misc conf
ATSIP
Rihab Idoudi, Kamel Hamrouni, Basel Solaiman
2014 conf
VISAPP (1)
Saïd Ettaïeb, Kamel Hamrouni, Su Ruan
2013 conf
SSD
Ouazzane Hana, Hela Mahersia, Kamel Hamrouni
2013 conf
SSD
Boulehmi Hela, Hela Mahersia, Kamel Hamrouni, Boussetta Sana, Mnif Najla
2013 conf
CIARP (1)
Sami Bourouis, Ines Chennoufi, Kamel Hamrouni
2012 conf
IPTA
Nefissa Khiari Hili, Sylvie Lelandais, Christophe Montagne, Kamel Hamrouni
2012 J jnl
Int. J. Image Graph.
Najla Gribaa, Vincent Noblet, Nawrès Khalifa, Sylvain Faisan, Kamel Hamrouni
2012 conf
BIOSIGNALS
Nefissa Khiari Hili, Sylvie Lelandais, Christophe Montagne, Kamel Hamrouni
2012 A conf
BMVC
Amira Belhedi, Adrien Bartoli, Vincent Gay-Bellile, Steve Bourgeois, Patrick Sayd, Kamel Hamrouni
2012 conf
VISAPP (1)
Nadia Feddaoui, Hela Mahersia, Kamel Hamrouni
2012 J jnl
Int. J. Image Graph.
Nadia Feddaoui, Hela Mahersia, Kamel Hamrouni
2012 conf
ECCV Workshops (3)
Amira Belhedi, Adrien Bartoli, Steve Bourgeois, Kamel Hamrouni, Patrick Sayd, Vincent Gay-Bellile
2012 B conf
ICIP
Amira Belhedi, Steve Bourgeois, Vincent Gay-Bellile, Patrick Sayd, Adrien Bartoli, Kamel Hamrouni
2012 conf
ICMCS
Chadia Khraief, Sami Bourouis, Kamel Hamrouni
2011 conf
VISAPP
Makram Mestiri, Sami Bourouis, Kamel Hamrouni
2011 J jnl
J. Ubiquitous Syst. Pervasive Networks
Sami Bourouis, Kamel Hamrouni
2011 conf
DICTAP (1)
Marwa Hermassi, Hejer Jelassi, Kamel Hamrouni
2010 J jnl
Int. J. Image Graph.
Sami Bourouis, Kamel Hamrouni
2010 J jnl
Int. J. Image Graph.
Hejer Jelassi, Kamel Hamrouni
2010 conf
SoCPaR
Nadia Feddaoui, Kamel Hamrouni
2010 J jnl
Int. Arab J. Inf. Technol.
Nawrès Khalifa, Said Ettaeib, Yosra Wahabi, Kamel Hamrouni
2009 J jnl
Int. J. Biomed. Imaging
Nawrès Khalifa, Najla Gribaa, Imen Mbazaa, Kamel Hamrouni
2008 conf
VISAPP (1)
Sami Bourouis, Kamel Hamrouni
2008 conf
ICIAR
Sami Bourouis, Kamel Hamrouni, Nacim Betrouni
2008 conf
VISAPP (1)
Sami Bourouis, Kamel Hamrouni, Mounir Dhibi
2008 conf
IPCV
Yosra Wahabi, Nawrès Khalifa, Kamel Hamrouni, Noureddine Ellouze
2006 J jnl
Int. Arab J. Inf. Technol.
Kamel Hamrouni, Nawrès Khalifa
2004 conf
ICBA
Emine Krichen, Mohamed Anouar Mellakh, Sonia Garcia-Salicetti, Kamel Hamrouni, Noureddine Ellouze, Bernadette Dorizzi
redb/extractors/decompiler/_archive/DecompileBinja-archive.py
← Index redb/extractors/decompiler/_archive/DecompileBinja-archive.py python
import hashlib
import inspect
import json
import os
import time
import threading
from datetime import datetime, timezone
from typing import Dict, Any, Optional

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

# Import our BinjaDecompiler (conditional)
from redb.extractors.decompiler.bninja.decompiler import BinaryNinjaDecompiler

class DecompileBinja(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)

        # Check if Binary Ninja is available
       # if not BINARYNINJA_AVAILABLE:
        #     self.log.error("Binary Ninja is not available in this container")
        #    raise ImportError(
        #        "Binary Ninja module not found - not available in feature extraction container"
        #    )
        self.analysis_results = None
        self.binja_decompiler = None
        self.filetype = filetype

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

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

    def __enter__(self):
        return self

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

    def calculate_md5(self, input_str):
        """Calculate MD5 hash of a string."""
        return hashlib.md5(input_str.encode("utf-8")).hexdigest()

    def is_dotnet(self):
        """Check if the binary is a .NET assembly.

        Returns:
            bool: True if the file is a .NET assembly, False otherwise
        """
        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:
            # BinaryNinjaDecompiler uses context manager pattern (__enter__/__exit__)
            # Cleanup happens automatically when exiting the 'with' block
            self.binja_decompiler = None

            # Force garbage collection
            import gc

            gc.collect()

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

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

        try:
            # Use BinaryNinjaDecompiler as a context manager to ensure proper setup/cleanup
            with BinaryNinjaDecompiler(
                filepath=self.filepath,
                timeout=self.BINJA_TIMEOUT,
                log=self.log,
                exporters=self.exporters,
                index_prefix=self.index_prefix,
                filetype=self.filetype,
            ) as decompiler:
                self.binja_decompiler = decompiler

                if decompiler.extract():
                    # Store results before context manager exits
                    results = decompiler.analysis_results
                    return results
                else:
                    self.log.error("BinaryNinjaDecompiler extraction failed")
                    return None

        except Exception as e:
            self.log.error(f"Error in Binary Ninja analysis: {e}")
            import traceback
            self.log.error(f"Traceback: {traceback.format_exc()}")
            return None

        finally:
            self.cleanup_run()

    def extract(self):
        """Extract and process all analysis results."""
        self.log.debug(inspect.currentframe().f_code.co_name)

        # Create a flag to track if extraction completed
        extraction_completed = False
        extraction_result = False
        extraction_error = None

        # Define the extraction process as a separate function
        def do_extraction():
            nonlocal extraction_completed, extraction_result, extraction_error
            try:
                results = self.analyze_binary()
                if not results:
                    extraction_result = False
                else:
                    self.analysis_results = results
                    extraction_result = True
            except Exception as e:
                extraction_error = e
                extraction_result = False
            finally:
                extraction_completed = True

        # Start extraction in a separate thread
        extraction_thread = threading.Thread(target=do_extraction)
        extraction_thread.daemon = True
        extraction_thread.start()

        # Wait for the extraction to complete or timeout
        start_time = time.time()
        while (
            not extraction_completed
            and (time.time() - start_time) < self.DECOMPILE_EXTRACTOR_TIMEOUT
        ):
            time.sleep(1)

        if not extraction_completed:
            self.log.error(
                f"Extraction timed out after {self.DECOMPILE_EXTRACTOR_TIMEOUT} seconds"
            )
            # Force cleanup
            self.cleanup_run()
            return None

        if extraction_error:
            self.log.error(f"Error in extraction: {extraction_error}")
            return None

        # Return the actual analysis results, not just a boolean
        return self.analysis_results if extraction_result else None

    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

        # # Delegate to the BinjaDecompiler for consistent export formatting
        # if self.binja_decompiler:
        #     return self.binja_decompiler.prepare_export_data(exporter_type)
        # else:
        #     self.log.error("BinjaDecompiler not available for export preparation")
        #     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

            # Add a helper function to handle empty strings
            def ensure_not_empty(value, default="UNKNOWN"):
                """Ensure a string value is not empty"""
                if value is None or value == "":
                    return default
                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": "code_binja_decompiled_functions_content",
                    "data": [
                        [
                            f["decompiled_function_hash"],
                            f["decompiled_function"],
                            f["function_type"],
                            now,
                        ]
                        for f in self.analysis_results["decompiled"]
                    ],
                    "column_names": [
                        "decompiled_function_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": "code_binja_decompiled_functions_references",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            self.analysis_results["sha1"],
                            self.analysis_results["md5"],
                            f["decompiled_function_hash"],
                            f["disassembled_function_hash"],  # New linking field
                            f["decompiled_function_name"],
                            f["decompiled_function_prototype"],
                            f["decompiled_function_address"],
                            now,
                        ]
                        for f in self.analysis_results["decompiled"]
                    ],
                    "column_names": [
                        "sha256",
                        "sha1",
                        "md5",
                        "decompiled_function_hash",
                        "disassembled_function_hash",  # New linking field
                        "decompiled_function_name",
                        "decompiled_function_prototype",
                        "decompiled_function_address",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(40)",
                        "FixedString(32)",
                        "FixedString(64)",
                        "Nullable(FixedString(64))",  # New linking field
                        "LowCardinality(String)",
                        "LowCardinality(String)",
                        "UInt64",
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "disassembled_content": {
                    "table": "code_binja_disassembled_functions_content",
                    "data": [
                        [
                            f["disassembled_function_hash"],
                            f.get("disassembled_function", ""),
                            f.get("disassembled_function_no_addresses", ""),
                            f.get("function_type", "UNKNOWN"),
                            f.get("instructions_count", 0),
                            prepare_array_field(
                                f.get("instructions_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),
                            f.get("max_block_size", 0),
                            f.get("num_calls", 0),
                            f.get("stack_size", 0),
                            now,
                        ]
                        for f in self.analysis_results["disassembled"]
                    ],
                    "column_names": [
                        "disassembled_function_hash",
                        "disassembled_function",
                        "disassembled_function_no_addresses",
                        "function_type",
                        "instructions_count",
                        "instructions_types",
                        "control_flow_count",
                        "memory_access_pattern",
                        "register_usage",
                        "data_references_count",
                        "max_block_size",
                        "num_calls",
                        "stack_size",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "String",
                        "String",
                        "Enum8('USER'=1, 'LIBRARY'=2, 'THUNK'=3, 'EXTERNAL'=4, 'UNKNOWN'=5)",
                        "UInt32",
                        "Array(LowCardinality(String))",
                        "UInt32",
                        "Array(LowCardinality(String))",
                        "Array(LowCardinality(String))",
                        "UInt32",
                        "Nullable(UInt32)",
                        "Nullable(UInt32)",
                        "Nullable(Int32)",
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "disassembled_refs": {
                    "table": "code_binja_disassembled_functions_references",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            self.analysis_results["sha1"],
                            self.analysis_results["md5"],
                            f["disassembled_function_hash"],
                            f["decompiled_function_hash"],
                            f["disassembled_function_name"],
                            f["disassembled_function_address"],
                            ssdeep_disassembly(
                                f.get("disassembled_function_no_addresses", "")
                            ),
                            tlsh_disassembly(
                                f.get("disassembled_function_no_addresses", "")
                            ),
                            now,
                        ]
                        for f in self.analysis_results["disassembled"]
                    ],
                    "column_names": [
                        "sha256",
                        "sha1",
                        "md5",
                        "disassembled_function_hash",
                        "decompiled_function_hash",
                        "disassembled_function_name",
                        "disassembled_function_address",
                        "ssdeep_disassembly",
                        "tlsh_disassembly",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(40)",
                        "FixedString(32)",
                        "FixedString(64)",
                        "Nullable(FixedString(64))",
                        "LowCardinality(String)",
                        "UInt64",
                        "Nullable(String)",
                        "Nullable(FixedString(72))",
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "cfg_blocks": {
                    "table": "code_binja_cfg_blocks",
                    "data": [
                        [
                            b["block_id"],
                            self.analysis_results["sha256"],
                            self.analysis_results["sha1"],
                            self.analysis_results["md5"],
                            b["function_address"],
                            b["block_start_address"],
                            b["block_end_address"],
                            b["block_size"],
                            b["instructions_count"],
                            b["block_instructions"],  # MD5 hash of instructions
                            b["predecessor_blocks"],
                            b["successor_blocks"],
                            b["depth"],
                            b["position"],
                            b["branch_type"],
                            b["block_type"],
                            b["flags"],
                            b["dominators"],
                            b["post_dominators"],
                            now,
                        ]
                        # Flatten: iterate through all functions, then all blocks in each function
                        for func_cfg in self.analysis_results["cfg"]
                        if func_cfg is not None  # Handle None from failed extractions
                        for b in func_cfg["blocks"]
                    ],
                    "column_names": [
                        "block_id",
                        "sha256",
                        "sha1",
                        "md5",
                        "function_address",
                        "block_start_address",
                        "block_end_address",
                        "block_size",
                        "instructions_count",
                        "block_instructions_hash",
                        "predecessor_blocks",
                        "successor_blocks",
                        "depth",
                        "position",
                        "branch_type",
                        "block_type",
                        "flags",
                        "dominators",
                        "post_dominators",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",  # block_id (SHA256)
                        "FixedString(64)",  # sha256
                        "FixedString(40)",  # sha1
                        "FixedString(32)",  # md5
                        "UInt64",  # function_address
                        "UInt64",  # block_start_address
                        "UInt64",  # block_end_address
                        "UInt32",  # block_size
                        "UInt16",  # instructions_count
                        "FixedString(32)",  # block_instructions_hash (MD5)
                        "Array(UInt64)",  # predecessor_blocks
                        "Array(UInt64)",  # successor_blocks
                        "UInt16",  # depth
                        "UInt16",  # position
                        "Enum8('DIRECT'=1, 'CONDITIONAL'=2, 'CALL'=3, 'RETURN'=4, 'FALLTHROUGH'=5, 'INDIRECT'=6, 'UNKNOWN'=7)",  # branch_type
                        "Enum8('CODE'=1, 'DATA'=2, 'THUNK'=3)",  # block_type
                        "Array(String)",  # flags (EntryBlock, ExitBlock, LoopBlock)
                        "Array(UInt16)",  # dominators
                        "Array(UInt16)",  # post_dominators
                        "DateTime64(3, 'UTC')",  # analysis_date
                    ],
                },
                "function_analysis_errors": {
                    "table": "function_analysis_errors_binja",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            f["function_name"],
                            f["function_address"],
                            f.get("error_location", "unknown"),
                            f.get("error_message", ""),
                            f.get("error_details", ""),
                            f.get("error_type", "unknown"),
                            self.calculate_md5(
                                f"{f['error_message']}{f['function_name']}{f['function_address']}{f['error_location']}"
                            ),
                            "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)",
                        "UInt64",
                        "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


if __name__ == "__main__":
    # Setup basic logging
    import logging

    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger("DecompileBinja")

    # Parse command line arguments
    import argparse

    parser = argparse.ArgumentParser(description="Binary Ninja Decompiler Wrapper")
    parser.add_argument("filepath", help="Path to the binary file to analyze")
    parser.add_argument(
        "--output", "-o", help="Output JSON file path (default: stdout)"
    )
    parser.add_argument(
        "--timeout",
        "-t",
        type=int,
        default=1200,
        help="Analysis timeout in seconds (default: 1200)",
    )
    args = parser.parse_args()

    # Create and run the extractor
    with DecompileBinja(args.filepath, logger) as extractor:
        success = extractor.extract()

        if not success:
            logger.error("Analysis failed")
            exit(1)

        # Output results
        if args.output:
            with open(args.output, "w") as f:
                json.dump(extractor.analysis_results, f)
            logger.info(f"Results written to {args.output}")
        else:
            print(json.dumps(extractor.analysis_results))