Hai Tao

101 papers A* 10A 10B 1C 2Journal 43Unranked 32
YearRankTypeTitle / Venue / Authors
2025 J jnl
Int. J. Commun. Syst.
Fang Qi, Yingkai Zhao, Md. Zakirul Alam Bhuiyan, Hai Tao, Weifeng Yan, Zhe Tang
2025 J jnl
Eng. Appl. Artif. Intell.
Hai Tao, Mohammed Suleman Aldlemy, Mohammed Ayad Saad, Swee Pin Yeap, Atheer Y. Oudah, Omer A. Alawi, Leonardo Goliatt, Shamsad Ahmad, Zaher Mundher Yaseen, Ravinesh C. Deo
2025 J jnl
IEEE Access
Isinka Joseph Chukwudi, Md. Arafatur Rahman, Ali Hamdan Alenezi, Hai Tao, Prashant Pillai
2025 J jnl
Expert Syst. Appl.
Hai Tao, Mohammed Suleman Aldlemy, Iman Ahmadianfar, Leonardo Goliatt, Haydar Abdulameer Marhoon, Raad Z. Homod, Hussein Togun, Zaher Mundher Yaseen
2024 J jnl
Eng. Appl. Artif. Intell.
Hai Tao, Zainab Hasan Ali, Faisal M. Mukhtar, Ahmed W. Al Zand, Haydar Abdulameer Marhoon, Leonardo Goliatt, Zaher Mundher Yaseen
2024 J jnl
Eng. Appl. Artif. Intell.
Hai Tao, Sani Isah Abba, Ahmed M. Al-Areeq, Fredolin Tangang, Sandeep Samantaray, Abinash Sahoo, Hugo Valadares Siqueira, Saman Maroufpoor, Vahdettin Demir, Neeraj Dhanraj Bokde, Leonardo Goliatt, Mehdi Jamei, Iman Ahmadianfar, Suraj Kumar Bhagat, Bijay Halder, Tianli Guo, Daniel S. Helman, Mumtaz Ali, Sabaa Sattar, Zainab Al-Khafaji, Shamsuddin Shahid, Zaher Mundher Yaseen
2024 J jnl
Sensors
Zhongbin Jiang, Hai Tao, Ye Liu
2023 conf
SCDS
Xiaolan Wang, Md. Manjur Ahmed, Mohd Nizam Husen, Hai Tao, Qian Zhao
2022 J jnl
Soft Comput.
Xiaoe Wu, Noritoshi Furukawa, Hai Tao, Hamid Farajian
2022 J jnl
Neurocomputing
Hai Tao, Mohammed Majeed Hameed, Haydar Abdulameer Marhoon, Mohammad Zounemat-Kermani, Salim Heddam, Sungwon Kim, Sadeq Oleiwi Sulaiman, Mou Leong Tan, Zulfaqar Sa'adi, Ali Danandeh Mehr, Mohammed Falah Allawi, Sani Isah Abba, Jasni Mohamad Zain, Mayadah Waheed Falah, Mehdi Jamei, Neeraj Dhanraj Bokde, Maryam Bayatvarkeshi, Mustafa Al-Mukhtar, Suraj Kumar Bhagat, Tiyasha Tiyasha, Khaled Mohamed Khedher, Nadhir Al-Ansari, Shamsuddin Shahid, Zaher Mundher Yaseen
2022 J jnl
Neural Comput. Appl.
Hai Tao, Salih Muhammad Awadh, Sinan Q. Salih, Shafik S. Shafik, Zaher Mundher Yaseen
2022 J jnl
Adv. Eng. Softw.
Hai Tao, Nawfel M. H. Al-Aragi, Iman Ahmadianfar, Maryam H. Naser, Rania H. Shehab, Jasni Mohamad Zain, Bijay Halder, Zaher Mundher Yaseen
2022 J jnl
Comput. Electr. Eng.
Hai Tao, Jasni Mohamad Zain, Shahab B. Band, B. Sundaravadivazhagan, Abdullah Mohamed, Haydar Abdulameer Marhoon, Otuu Obinna Ogbonnia, Praise Young
2021 J jnl
Inf. Sci.
Hai Tao, Maria Habib, Ibrahim Aljarah, Hossam Faris, Haitham Abdulmohsin Afan, Zaher Mundher Yaseen
2021 J jnl
Cogn. Syst. Res.
Md. Munirul Hasan, Md. Arafatur Rahman, Arya Sedigh, Ana U. Khasanah, A. Taufiq Asyhari, Hai Tao, Suraya Abu Bakar
2020 J jnl
IEEE Access
Hai Tao, Sinan Q. Salih, Mandeep Kaur Saggi, Esmaeel Dodangeh, Cyril Voyant, Nadhir Al-Ansari, Zaher Mundher Yaseen, Shamsuddin Shahid
2020 J jnl
IEEE Trans. Fuzzy Syst.
Osama Nayel Al Sayaydeh, Mohammed Falah Mohammed, Essam Alhroob, Hai Tao, Chee Peng Lim
2020 J jnl
Complex.
Hai Tao, Ali Omran Al-Sulttani, Ameen Mohammed Salih Ameen, Zainab Hasan Ali, Nadhir Al-Ansari, Sinan Q. Salih, Reham R. Mostafa
2020 J jnl
IEEE Trans. Ind. Informatics
Hai Tao, Md. Zakirul Alam Bhuiyan, Md. Arafatur Rahman, Tian Wang, Jie Wu, Sinan Q. Salih, Yafeng Li, Thaier Hayajneh
2019 J jnl
IEEE Access
Essam Alhroob, Mohammed Falah Mohammed, Chee Peng Lim, Hai Tao
2019 J jnl
Future Gener. Comput. Syst.
Hai Tao, Md. Zakirul Alam Bhuiyan, Md. Arafatur Rahman, Guojun Wang, Tian Wang, Md. Manjur Ahmed, Jing Li
2019 J jnl
Inf. Sci.
Weitao Li, Hai Tao, Hua Li, Keqiong Chen, Jianping Wang
2019 conf
ICECC
Liu Shuhong, Jing Li, Hai Tao
2019 J jnl
IEEE Internet Things J.
Hai Tao, Md. Zakirul Alam Bhuiyan, Ahmed N. Abdalla, Mohammad Mehedi Hassan, Jasni Mohamad Zain, Thaier Hayajneh
2019 conf
DSA
Faiza Khan, Md. Zakirul Alam Bhuiyan, Md. Monirul Islam, Tian Wang, Aliuz Zaman, Hai Tao
2018 conf
ICDPA
Md. Zakirul Alam Bhuiyan, Aliuz Zaman, Tian Wang, Guojun Wang, Hai Tao, Mohammad Mehedi Hassan
2018 conf
SpaCCS
Wang Wei, Md. Arafatur Rahman, Md Jahan Ali, Md. Zakirul Alam Bhuiyan, Liu Yao, Hai Tao
2018 conf
SGIoT
Md. Zakirul Alam Bhuiyan, Mdaliuz Zaman, Guojun Wang, Tian Wang, Md. Arafatur Rahman, Hai Tao
2015 J jnl
CoRR
Ang Li, Feng Tang, Yanwen Guo, Hai Tao
2012 J jnl
Integr. Comput. Aided Eng.
Hai Tao, Jasni Mohamad Zain, Mohammad Masroor Ahmed, Ahmed N. Abd Alla, Jing Wang
2010 conf
NDT (1)
Hai Tao, Jasni Mohamad Zain, Ahmed N. Abd Alla, Hongwu Qin
2010 J jnl
Int. J. Web Appl.
Hai Tao, Jasni Mohamad Zain, Ahmed N. Abd Alla, Jing Wang
2010 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Qi Zhao, Zhi Yang, Hai Tao
2010 conf
ECCV (3)
Ang Li, Feng Tang, Yanwen Guo, Hai Tao
2010 ch.
Intelligent Multimedia Communication
Yun Fu, Hao Tang, Jilin Tu, Hai Tao, Thomas S. Huang
2010 conf
FGIT-DTA/BSBT
Hai Tao, Jasni Mohamad Zain, Ahmed N. Abd Alla, Hongwu Qin
2009 J jnl
Comput. Vis. Image Underst.
Qi Zhao, Hai Tao
2009 A* conf
CVPR
Feng Tang, Suk Hwan Lim, Nelson L. Chang, Hai Tao
2009 conf
ACCV (1)
Qi Zhao, Zhi Yang, Hai Tao, Wentai Liu
2009 J jnl
Mach. Vis. Appl.
Jilin Tu, Hai Tao, Thomas S. Huang
2008 conf
CVPR Workshops
Feng Tang, Michael Harville, Hai Tao, Ian N. Robinson
2008 J jnl
Int. J. Netw. Secur.
Hai Tao, Carlisle Adams
2008 J jnl
IEEE Trans. Circuits Syst. Video Technol.
Feng Tang, Hai Tao
2008 J jnl
Mach. Vis. Appl.
Hai Tao, Harpreet S. Sawhney
2008 conf
ECCV (1)
Douglas Gray, Hai Tao
2007 A* conf
ICCV
Feng Tang, Shane Brennan, Qi Zhao, Hai Tao
2007 A* conf
ICCV
Qi Zhao, Shane Brennan, Hai Tao
2007 J jnl
Comput. Vis. Image Underst.
Jilin Tu, Hai Tao, Thomas S. Huang
2007 A conf
WACV
Feng Tang, Hai Tao
2007 J jnl
Pattern Recognit. Lett.
Feng Tang, Hai Tao
2007 A* conf
CVPR
Piotr Dollár, Zhuowen Tu, Hai Tao, Serge J. Belongie
2007 conf
ACCV (1)
Qi Zhao, Hai Tao
2007 J jnl
Mach. Vis. Appl.
Mei Han, Wei Xu, Hai Tao, Yihong Gong
2007 A conf
WACV
Feng Tang, Jin Wang, Hai Tao, Qunsheng Peng
2007 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Feng Tang, Ryan Crabb, Hai Tao
2006 conf
ICPR (3)
Dan Kong, Mei Han, Wei Xu, Hai Tao, Yihong Gong
2006 conf
ICPR (3)
Dan Kong, Douglas Gray, Hai Tao
2006 conf
FGR
Jilin Tu, Thomas S. Huang, Hai Tao
2006 A conf
BMVC
Feng Tang, Hai Tao
2006 conf
ICPR (2)
Feng Tang, Hai Tao
2006 conf
ACCV (1)
Jilin Tu, Hai Tao, Thomas S. Huang
2006 conf
ICPR (1)
Qi Zhao, Jinman Kang, Hai Tao, Wei Hua
2006 A conf
BMVC
Dan Kong, Hai Tao
2006 A conf
BMVC
Dan Kong, Mei Han, Wei Xu, Hai Tao, Yihong Gong
2005 conf
WACV/MOTION
Yue Zhou, Wei Xu, Hai Tao, Yihong Gong
2005 A conf
BMVC
Dan Kong, Douglas Gray, Hai Tao
2005 conf
CRV
Jilin Tu, Thomas S. Huang, Hai Tao
2005 A* conf
ICCV
Hai Tao, Ryan Crabb, Feng Tang
2004 A conf
BMVC
Dan Kong, Hai Tao
2004 conf
CVPR (1)
Mei Han, Wei Xu, Hai Tao, Yihong Gong
2003 J jnl
IEEE J. Solid State Circuits
Hai Tao, Derek K. Shaeffer, Min Xu, Saied Benyamin, Vincent Condito, Steffen Kudszus, Qinghung Lee, Adrian Ong, Arvin Shahani, Xiaomin Si, Wayne Wong, Maurice Tarsia
2003 J jnl
IEEE J. Solid State Circuits
Adrian Ong, Saied Benyamin, Jason Cancio, Vincent Condito, Tad Labrie, Qinghung Lee, John Paul Mattia, Derek K. Shaeffer, Arvin Shahani, Xiaomin Si, Hai Tao, Maurice Tarsia, Wayne Wong, Min Xu
2003 A* conf
ICCV
Yue Zhou, Hai Tao
2003 conf
CVPR (2)
Chunxiao Zhou, Hai Tao
2003 conf
CVPR (2)
Jian Sun, Nanning Zheng, Hai Tao, Heung-Yeung Shum
2003 A conf
BMVC
Dan Kong, Hai Tao, Héctor H. González-Baños
2002 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Hai Tao, Harpreet S. Sawhney, Rakesh Kumar
2002 J jnl
Int. J. Comput. Vis.
Hai Tao, Thomas S. Huang
2001 A* conf
ICCV
Hai Tao, Harpreet S. Sawhney, Rakesh Kumar
2001 J jnl
Proc. IEEE
Rakesh Kumar, Harpreet S. Sawhney, Supun Samarasekera, Steven C. Hsu, Hai Tao, Yanlin Guo, Keith J. Hanna, Art Pope, Richard Wildes, David J. Hirvonen, Michael W. Hansen, Peter Burt
2001 conf
ICIP (3)
Ravi Krishnamurthy, Bing-Bing Chai, Hai Tao, Sriram Sethuraman
2001 conf
CVPR (1)
Hai Tao, Harpreet S. Sawhney, Rakesh Kumar
2000 A* conf
CVPR
Hai Tao, Harpreet S. Sawhney, Rakesh Kumar
2000 ch.
Confluence of Computer Vision and Computer Graphics
Hai Tao, Thomas S. Huang
2000 A conf
WACV
Hai Tao, Harpreet S. Sawhney
1999 J jnl
IEEE J. Solid State Circuits
Hai Tao, John M. Khoury
1999 conf
Workshop on Vision Algorithms
Hai Tao, Harpreet S. Sawhney, Rakesh Kumar
1999 J jnl
IEEE Trans. Circuits Syst. Video Technol.
Hai Tao, Homer H. Chen, Wei Wu, Thomas S. Huang
1999 A* conf
CVPR
Hai Tao, Thomas S. Huang
1999
Hai Tao
1998 conf
CAPTECH
Hai Tao, Thomas S. Huang
1998 A* conf
CVPR
Hai Tao, Thomas S. Huang
1998 J jnl
IEEE Commun. Mag.
John Khoury, Hai Tao
1998 conf
ICIP (3)
Hai Tao, Thomas S. Huang
1998 C conf
MMSP
Lawrence S. Chen, Hai Tao, Thomas S. Huang, Tsutomu Miyasato, Ryohei Nakatsu
1998 B conf
FG
Hai Tao, Ricardo López, Thomas S. Huang
1997 C conf
MMSP
Hai Tao, Homer H. Chen, Thomas S. Huang
1997 conf
ICIP (1)
Hai Tao, Thomas S. Huang
1996 conf
ICIP (1)
Hai Tao, Thomas S. Huang
1994 conf
ICIP (3)
Hai Tao, Robert J. Moorhead
1994 A conf
IEEE Visualization
Hai Tao, Robert J. Moorhead
redb/extractors/decompiler/DecompileBinja.py
← Index redb/extractors/decompiler/DecompileBinja.py python
import hashlib
import inspect
import json
import logging
import os
import signal
import time
from datetime import datetime, timezone
from typing import Dict, Any, Optional
from pathlib import Path
import subprocess
import sys

from redb.extractors.enum import Tag
from redb.extractors.extractor import Extractor
import magic
import pefile
from elftools.elf.elffile import ELFFile

# 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,
        decompile_modules=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
        self.decompile_modules = decompile_modules or {"all"}
        self.goresym_data = None
        self.goresym_output_path = None

        # 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
            return False
        except AttributeError as e:
            self.log.error(
                f"AttributeError error dotnet file {self.hash.sha256} Full error : {e}"
            )
            return False

    def is_golang(self):
        """Check if the binary is a Go-compiled binary (heuristic).

        Supports PE and ELF binaries.
        """
        try:
            if self.filetype == "pebin":
                pe = pefile.PE(self.filepath)
                signatures = [b"Go build ID:", b"runtime.main", b"main.main"]

                for section in pe.sections:
                    data = section.get_data()
                    if any(sig in data for sig in signatures):
                        return True

                return False

            elif self.filetype == "elf":
                with open(self.filepath, "rb") as f:
                    elf = ELFFile(f)

                    # 1. Section-based checks
                    section_names = [sec.name for sec in elf.iter_sections()]
                    if any(
                        s in section_names
                        for s in (".note.go.buildid", ".gopclntab")
                    ):
                        return True

                    # 2. String scan in loadable sections
                    signatures = [
                        b"Go build ID:",
                        b"runtime.main",
                        b"runtime.goexit",
                        b"runtime.morestack",
                        b"main.main",
                    ]

                    for sec in elf.iter_sections():
                        if sec["sh_flags"] & 0x2:  # SHF_ALLOC
                            data = sec.data()
                            if any(sig in data for sig in signatures):
                                return True

                return False

            return False

        except Exception as e:
            self.log.error(
                f"Golang detection error {self.hash.sha256}: {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

            # Clean up goresym temp file if it exists (keep in debug mode)
            if self.goresym_output_path and os.path.exists(self.goresym_output_path):
                if self.log.isEnabledFor(logging.DEBUG):
                    self.log.debug(f"Debug mode: keeping goresym output at {self.goresym_output_path}")
                else:
                    os.remove(self.goresym_output_path)
                    self.goresym_output_path = None

            # Force garbage collection
            import gc

            gc.collect()

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

    def run_goresym(self, binary_path, output_json_path):
        """
            Run goresym on a Go binary and export its JSON output to a file.
            binary_path: path to the Go binary to analyze
            output_json_path: path where the JSON output will be saved
            """
        binary_path = str(Path(binary_path).resolve())
        output_json_path = str(Path(output_json_path).resolve())
        goresym_path = os.getenv("GORESYM_PATH", "GoReSym")

        try:
            # Example: goresym -t json /path/to/binary
            self.log.info("DEBUG: starting GoReSym")
            result = subprocess.run(
                [goresym_path, binary_path],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
            )
        except FileNotFoundError:
            self.log.error("Error: 'goresym' not found in PATH. Make sure it is installed.")
            raise
        except subprocess.CalledProcessError as e:
            self.log.error("Error during goresym execution:")
            self.log.error(e.stderr)


        # Assuming goresym emits valid JSON to stdout.
        try:
            parsed = json.loads(result.stdout)
            # Store parsed JSON for later export to ClickHouse
            self.goresym_data = parsed
        except json.JSONDecodeError:
            # If it's not valid JSON, save the raw output instead.
            self.log.error("Warning: goresym output is not valid JSON; saving raw.")
            with open(output_json_path, "w", encoding="utf-8") as f:
                f.write(result.stdout)
            return

        # Save pretty-printed JSON for readability and debugging (used by BinaryNinja)
        with open(output_json_path, "w", encoding="utf-8") as f:
            json.dump(parsed, f, ensure_ascii=False, indent=2)
        self.goresym_output_path = output_json_path

        self.log.debug(f"goresym output saved to: {output_json_path}")

    def analyze_binary(self) -> Optional[Dict[str, Any]]:
        """Run Binary Ninja analysis and return results."""
        self.log.debug("Starting binary analysis")
        # if the binary is dotnet (only PE)
        if self.is_dotnet():
            self.log.debug("Skipping .NET binary - decompilation not supported")
            return None

        output_json_path = None
        ## if golang: run goresym
        try:
            if self.is_golang():
                output_json_path = "./goResym.json"
                self.run_goresym(self.filepath, output_json_path)
        except Exception as e:
            self.log.error(f"Error on GoReSym extraction: {e}")

        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,
                goresym=output_json_path,
                decompile_modules=self.decompile_modules,
            ) 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
                    # Add file hashes from parent Extractor class to analysis results
                    self.analysis_results["sha256"] = self.sha256
                    self.analysis_results["sha1"] = self.sha1
                    self.analysis_results["md5"] = self.md5
                    extraction_result = True
            except Exception as e:
                extraction_error = e
                extraction_result = False
            finally:
                extraction_completed = True

        # Run extraction directly with signal-based timeout (no thread overhead).
        # SIGALRM is delivered by the OS, so there's no GIL contention or polling.
        old_handler = signal.getsignal(signal.SIGALRM)
        def _timeout_handler(signum, frame):
            raise TimeoutError("Extraction timed out")

        signal.signal(signal.SIGALRM, _timeout_handler)
        signal.alarm(self.DECOMPILE_EXTRACTOR_TIMEOUT)
        try:
            do_extraction()
        except TimeoutError:
            self.log.error(
                f"Extraction timed out after {self.DECOMPILE_EXTRACTOR_TIMEOUT} seconds"
            )
            self.cleanup_run()
            return None
        finally:
            signal.alarm(0)
            signal.signal(signal.SIGALRM, old_handler)

        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 prepare_register_usage_map(register_dict):
                """Convert register usage dict to Map format with tuples
                Input: {"rbx": {"reads": 3, "writes": 1}, ...}
                Output: {"rbx": (3, 1), ...}
                """
                if not register_dict:
                    return {}
                return {
                    reg: (info.get("reads", 0), info.get("writes", 0))
                    for reg, info in register_dict.items()
                }


            decompile_modules = getattr(self, "decompile_modules", {"all"})
            run_all = "all" in decompile_modules
            run_decompilation = run_all or "decompilation" in decompile_modules
            run_disassembly = run_all or "disassembly" in decompile_modules
            run_llil = run_all or "llil" in decompile_modules
            run_cfg = run_all or "cfg" in decompile_modules
            run_strings = run_all or "strings" in decompile_modules

            export = {"multi_table": True}

            # Decompilation tables
            if run_decompilation:
                export["decompiled_content"] = {
                    "table": "code_binja_decompiled_functions_content",
                    "data": [
                        [
                            f["decompiled_function_hash"],
                            f["decompiled_function"],
                            f["function_type"],
                            f.get("flattened_score"),
                            f.get("mba_score"),
                            now,
                        ]
                        for f in self.analysis_results["decompiled"]
                    ],
                    "column_names": [
                        "decompiled_function_hash",
                        "decompiled_function",
                        "function_type",
                        "flattened_score",
                        "mba_score",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "String",
                        "Enum8('USER'=1, 'LIBRARY'=2, 'THUNK'=3, 'EXTERNAL'=4, 'UNKNOWN'=5)",
                        "Nullable(Float64)",
                        "Nullable(Float64)",
                        "DateTime64(3, 'UTC')",
                    ],
                }
                export["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.get("disassembled_function_hash"),
                            f["decompiled_function_name"],
                            f["decompiled_function_prototype"],
                            f["decompiled_function_address"],
                            prepare_array_field(f.get("functions_caller"), "Array(String)"),
                            prepare_array_field(f.get("functions_call"), "Array(String)"),
                            now,
                        ]
                        for f in self.analysis_results["decompiled"]
                    ],
                    "column_names": [
                        "sha256",
                        "sha1",
                        "md5",
                        "decompiled_function_hash",
                        "disassembled_function_hash",
                        "decompiled_function_name",
                        "decompiled_function_prototype",
                        "decompiled_function_address",
                        "functions_caller",
                        "functions_call",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(40)",
                        "FixedString(32)",
                        "FixedString(64)",
                        "Nullable(FixedString(64))",
                        "LowCardinality(String)",
                        "LowCardinality(String)",
                        "UInt64",
                        "Array(String)",
                        "Array(String)",
                        "DateTime64(3, 'UTC')",
                    ],
                }

            # Disassembly tables
            if run_disassembly:
                export["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"),
                            f.get("num_calls"),
                            f.get("stack_size"),
                            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')",
                    ],
                }
                export["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.get("decompiled_function_hash"),
                            f["disassembled_function_name"],
                            f["disassembled_function_address"],
                            f.get("tlsh_disassembly"),
                            f.get("tlsh_llil"),
                            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",
                        "tlsh_disassembly",
                        "tlsh_llil",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(40)",
                        "FixedString(32)",
                        "FixedString(64)",
                        "Nullable(FixedString(64))",
                        "LowCardinality(String)",
                        "UInt64",
                        "Nullable(FixedString(72))",
                        "Nullable(FixedString(72))",
                        "DateTime64(3, 'UTC')",
                    ],
                }
                # Function similarity metrics table (derived from disassembly data)
                # Lookup maps to join LLIL/MLIL features by disassembled_function_hash
                llil_by_hash = {
                    l.get("disassembled_function_hash"): l
                    for l in self.analysis_results.get("llil", [])
                    if l and l.get("disassembled_function_hash")
                }
                mlil_by_hash = {
                    m.get("disassembled_function_hash"): m
                    for m in self.analysis_results.get("mlil", [])
                    if m and m.get("disassembled_function_hash")
                }

                export["function_similarity_metrics"] = {
                    "table": "code_binja_function_similarity_metrics",
                    "data": [
                        [
                            f["disassembled_function_hash"],
                            f.get("cyclomatic_complexity"),
                            f.get("tlsh_disassembly"),
                            f.get("tlsh_llil"),
                            prepare_array_field(f.get("minhash"), "Array(UInt8)"),
                            (llil_by_hash.get(f["disassembled_function_hash"]) or {}).get("tlsh_llil"),
                            (llil_by_hash.get(f["disassembled_function_hash"]) or {}).get(
                                "tlsh_instruction_typed_llil"),
                            prepare_array_field(
                                (llil_by_hash.get(f["disassembled_function_hash"]) or {}).get("minhash_llil_skeleton"),
                                "Array(UInt8)",
                            ),
                            prepare_array_field(
                                (llil_by_hash.get(f["disassembled_function_hash"]) or {}).get("minhash_llil_typed"),
                                "Array(UInt8)",
                            ),
                            (mlil_by_hash.get(f["disassembled_function_hash"]) or {}).get("tlsh_mlil_skeleton"),
                            (mlil_by_hash.get(f["disassembled_function_hash"]) or {}).get("tlsh_mlil_typed"),
                            prepare_array_field(
                                (mlil_by_hash.get(f["disassembled_function_hash"]) or {}).get("minhash_mlil_skeleton"),
                                "Array(UInt8)",
                            ),
                            prepare_array_field(
                                (mlil_by_hash.get(f["disassembled_function_hash"]) or {}).get("minhash_mlil_typed"),
                                "Array(UInt8)",
                            ),
                            now,
                        ]
                        for f in self.analysis_results.get("disassembled", [])
                    ],
                    "column_names": [
                        "disassembled_function_hash",
                        "cyclomatic_complexity",
                        "tlsh_disassembly",
                        "tlsh_llil",
                        "minhash",
                        "tlsh_llil_new",
                        "tlsh_instruction_typed_llil",
                        "minhash_llil_skeleton",
                        "minhash_llil_typed",
                        "tlsh_mlil_skeleton",
                        "tlsh_mlil_typed",
                        "minhash_mlil_skeleton",
                        "minhash_mlil_typed",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "Nullable(UInt16)",
                        "Nullable(FixedString(72))",
                        "Nullable(FixedString(72))",
                        "Array(UInt8)",
                        # new
                        "Nullable(FixedString(72))",
                        "Nullable(FixedString(72))",
                        "Array(UInt8)",
                        "Array(UInt8)",
                        "Nullable(FixedString(72))",
                        "Nullable(FixedString(72))",
                        "Array(UInt8)",
                        "Array(UInt8)",
                        "DateTime64(3, 'UTC')",
                    ],
                }


            # LLIL tables
            if run_llil:
                export["llil_content"] = {
                    "table": "code_binja_llil_functions_content",
                    "data": [
                        [
                            f["sha256_llil"],
                            f["function_type"],
                            prepare_array_field(
                                f.get("instructions_types_llil"), "LowCardinality(String)"
                            ),
                            f.get("control_flow_count_llil", 0),
                            prepare_array_field(
                                f.get("memory_access_pattern_llil"), "LowCardinality(String)"
                            ),
                            prepare_register_usage_map(f.get("register_usage", {})),
                            f.get("total_reg_reads", 0),
                            f.get("total_reg_written", 0),
                            f.get("data_references_count", 0),
                            f.get("max_block_size"),
                            f.get("num_calls"),
                            f.get("stack_size"),
                            prepare_array_field(f.get("body_llil_vector"), "Array(Tuple(UInt32, Array(UInt16)))"),
                            now,
                        ]
                        for f in self.analysis_results.get("llil", [])
                    ],
                    "column_names": [
                        "llil_function_hash",
                        "function_type",
                        "instructions_types_llil",
                        "control_flow_count_llil",
                        "memory_access_pattern_llil",
                        "register_usage_llil",
                        "total_reg_reads",
                        "total_reg_written",
                        "data_references_count",
                        "max_block_size",
                        "num_calls",
                        "stack_size",
                        "body_llil_vector",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "Enum8('USER'=1, 'LIBRARY'=2, 'THUNK'=3, 'EXTERNAL'=4, 'UNKNOWN'=5)",
                        "Array(LowCardinality(String))",
                        "UInt32",
                        "Array(LowCardinality(String))",
                        "Map(LowCardinality(String), Tuple(UInt32, UInt32))",
                        "UInt32",
                        "UInt32",
                        "UInt32",
                        "Nullable(UInt32)",
                        "Nullable(UInt32)",
                        "Nullable(Int32)",
                        "Array(Tuple(UInt32, Array(UInt16)))",
                        "DateTime64(3, 'UTC')",
                    ],
                }
                export["llil_refs"] = {
                    "table": "code_binja_llil_functions_references",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            self.analysis_results["sha1"],
                            self.analysis_results["md5"],
                            f.get("sha256_llil"),
                            f.get("disassembled_function_hash"),
                            f.get("function_address"),
                            f.get("tlsh_disassembly"),
                            f.get("tlsh_llil"),
                            now,
                        ]
                        for f in self.analysis_results.get("llil", [])
                    ],
                    "column_names": [
                        "sha256",
                        "sha1",
                        "md5",
                        "llil_function_hash",
                        "disassembled_function_hash",
                        "function_address",
                        "tlsh_disassembly",
                        "tlsh_llil",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(40)",
                        "FixedString(32)",
                        "Nullable(FixedString(64))",
                        "FixedString(64)",
                        "UInt64",
                        "Nullable(FixedString(72))",
                        "Nullable(FixedString(72))",
                        "DateTime64(3, 'UTC')",
                    ],
                }

            # Errors table (always include if per-function loop ran)
            if run_decompilation or run_disassembly or run_llil or run_cfg:
                export["function_analysis_errors"] = {
                    "table": "new_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.get('error_message', '')}{f['function_name']}{f['function_address']}{f.get('error_location', 'unknown')}"
                            ),
                            "new",
                            now,
                        ]
                        for f in self.analysis_results.get("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')",
                    ],
                }

            # Strings table
            if run_strings:
                export["strings_raw"] = {
                    "table": "code_binja_strings_raw",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            s["string"],
                            s["string_raw"],
                            s["string_encoding"],
                            s["string_offset"],
                            s["string_length"],
                            s["string_raw_length"],
                            s["string_entropy"],
                        ]
                        for s in self.analysis_results.get("strings", [])
                    ],
                    "column_names": [
                        "sha256",
                        "string",
                        "string_raw",
                        "string_encoding",
                        "string_offset",
                        "string_length",
                        "string_raw_length",
                        "string_entropy",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "String",
                        "String",
                        "LowCardinality(String)",
                        "UInt64",
                        "UInt32",
                        "UInt32",
                        "Float32",
                    ],
                }

            # CFG function-level features table
            if run_cfg:
                export["cfg_functions"] = {
                    "table": "code_binja_cfg_functions",
                    "data": [
                        [
                            cfg.get("disassembled_function_hash"),
                            cfg["cfg_topology_hash"],
                            cfg["block_count"],
                            cfg["edge_count"],
                            cfg.get("llil_total_operations", 0),
                            cfg.get("call_count", 0),
                            cfg["cyclomatic_complexity"],
                            cfg.get("loop_count", 0),
                            cfg.get("max_depth", 0),
                            cfg.get("max_fan_out", 0),
                            cfg.get("md_index_topdown", 0),
                            cfg.get("md_index_bottomup", 0),
                            cfg.get("prime_product_llil", 0),
                            cfg.get("cfg_feature_tlsh"),
                            cfg.get("wl_minhash", []),
                            cfg.get("bb_features", []),
                            cfg.get("cfg_adjacency", []),
                            now,
                        ]
                        for cfg in self.analysis_results.get("cfg", [])
                        if cfg is not None
                    ],
                    "column_names": [
                        "disassembled_function_hash",
                        "cfg_topology_hash",
                        "block_count",
                        "edge_count",
                        "llil_total_operations",
                        "call_count",
                        "cyclomatic_complexity",
                        "loop_count",
                        "max_depth",
                        "max_fan_out",
                        "md_index_topdown",
                        "md_index_bottomup",
                        "prime_product_llil",
                        "cfg_feature_tlsh",
                        "wl_minhash",
                        "bb_features",
                        "cfg_adjacency",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(16)",
                        "UInt16",
                        "UInt16",
                        "UInt32",
                        "UInt16",
                        "UInt16",
                        "UInt8",
                        "UInt16",
                        "UInt8",
                        "UInt64",
                        "UInt64",
                        "UInt64",
                        "Nullable(FixedString(72))",
                        "Array(UInt8)",
                        "Array(Array(UInt16))",
                        "Array(UInt32)",
                        "DateTime64(3, 'UTC')",
                    ],
                }

            # GoReSym metadata table (only if goresym data exists)
            if self.goresym_data:
                export["golang_metadata"] = {
                    "table": "redb_golang_metadata",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            json.dumps(self.goresym_data),
                            now,
                        ]
                    ],
                    "column_names": [
                        "sha256",
                        "goresym",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "JSON",
                        "DateTime64(3, 'UTC')",
                    ],
                }

            return export

    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
    import time

    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()
    start = time.perf_counter()
    # Create and run the extractor
    with DecompileBinja(args.filepath, logger) as extractor:
        success = extractor.extract()
        end = time.perf_counter()
        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((extractor.analysis_results))
            with open("diff", "w") as f:
                f.write(str(f"{end - start:.3f} seconds"))