C. S. George Lee

150 papers A* 50A 17B 8C 3Misc 1Journal 58Unranked 12
YearRankTypeTitle / Venue / Authors
2024 conf
CASE
Ye Shi, C. S. George Lee
2024 J jnl
CoRR
Ye Shi, C. S. George Lee
2023 J jnl
CoRR
Yue Cao, C. S. George Lee
2023 conf
AAAI Spring Symposium: MAKE
Yue Cao, C. S. George Lee
2023 J jnl
CoRR
Yue Cao, C. S. George Lee
2022 J jnl
CoRR
Jihoon Moon, Debasmit Das, C. S. George Lee
2022 J jnl
IEEE Trans. Image Process.
Jihoon Moon, Debasmit Das, C. S. George Lee
2022 A conf
IROS
Yue Cao, C. S. George Lee
2021 B conf
SMC
Debasmit Das, C. S. George Lee
2021 J jnl
CoRR
Yan Gu, Yuan Gao, Bin Yao, C. S. George Lee
2020 J jnl
IEEE Trans. Image Process.
Debasmit Das, C. S. George Lee
2020 conf
ISVC (2)
Debasmit Das, J. H. Moon, C. S. George Lee
2020 J jnl
CoRR
Debasmit Das, J. H. Moon, C. S. George Lee
2020 Misc conf
ICASSP
J. H. Moon, Debasmit Das, C. S. George Lee
2020 J jnl
CoRR
J. H. Moon, Debasmit Das, C. S. George Lee
2019 J jnl
CoRR
Debasmit Das, C. S. George Lee
2019 B conf
IJCNN
Debasmit Das, C. S. George Lee
2019 J jnl
CoRR
Debasmit Das, C. S. George Lee
2018 conf
ICANN (3)
Debasmit Das, C. S. George Lee
2018 J jnl
CoRR
Debasmit Das, C. S. George Lee
2018 J jnl
Eng. Appl. Artif. Intell.
Debasmit Das, C. S. George Lee
2018 C conf
ACC
Yan Gu, Bin Yao, C. S. George Lee
2018 B conf
ICIP
Debasmit Das, C. S. George Lee
2018 J jnl
CoRR
Debasmit Das, C. S. George Lee
2017 C conf
ACC
Yan Gu, Bin Yao, C. S. George Lee
2016 J jnl
IEEE Trans. Syst. Man Cybern. Syst.
Kai-Chi Chan, Cheng-Kok Koh, C. S. George Lee
2016 A* conf
ICRA
Yan Gu, Bin Yao, C. S. George Lee
2016 A conf
IROS
Hyungju Andy Park, C. S. George Lee
2015 J jnl
Pattern Recognit.
Rami Alazrai, Yaser Mowafi, C. S. George Lee
2015 B conf
Intelligent Vehicles Symposium
Ashesh Goswami, C. S. George Lee
2015 A* conf
ICRA
Hyungju Andy Park, C. S. George Lee
2015 A* conf
ICRA
Yan Gu, C. S. George Lee, Bin Yao
2015 A conf
IROS
Kai-Chi Chan, Cheng-Kok Koh, C. S. George Lee
2014 J jnl
IEEE Trans. Syst. Man Cybern. Syst.
Kai-Chi Chan, Cheng-Kok Koh, C. S. George Lee
2014 A* conf
ICRA
Yajia Zhang, Jingru Luo, Kris K. Hauser, Hyungju Andy Park, Manas Paldhe, C. S. George Lee, Robert Ellenberg, Brittany Killen, Paul Y. Oh, Jun-Ho Oh, Jungho Lee, Inhyeok Kim
2014 A conf
IROS
Shengwei Yu, C. S. George Lee
2014 A* conf
ICRA
Jingru Luo, Yajia Zhang, Kris K. Hauser, Hyungju Andy Park, Manas Paldhe, C. S. George Lee, Michael X. Grey, Mike Stilman, Jun-Ho Oh, Jungho Lee, Inhyeok Kim, Paul Y. Oh
2014 A* conf
ICRA
Kai-Chi Chan, Cheng-Kok Koh, C. S. George Lee
2013 A* conf
ICRA
Kai-Chi Chan, Cheng-Kok Koh, C. S. George Lee
2013 conf
ROBIO
Kai-Chi Chan, Cheng-Kok Koh, C. S. George Lee
2013 A* conf
ICRA
Hyungju Andy Park, C. S. George Lee
2013 J jnl
Sensors
Hsien-I Lin, C. S. George Lee
2013 conf
TePRA
Yajia Zhang, Jingru Luo, Kris K. Hauser, Robert Ellenberg, Paul Y. Oh, Hyungju Andy Park, Manas Paldhe, C. S. George Lee
2013 A conf
IROS
Kai-Chi Chan, Cheng-Kok Koh, C. S. George Lee
2012 A* conf
ICRA
Rami Alazrai, C. S. George Lee
2012 A conf
IROS
Rami Alazrai, C. S. George Lee
2012 J jnl
Int. J. Humanoid Robotics
Hyungju Andy Park, Muhammad Ahmad Ali, C. S. George Lee
2012 J jnl
IEEE Robotics Autom. Mag.
Tamim Asfour, Kazuhito Yokoi, C. S. George Lee, James Kuffner
2012 J jnl
Robotica
Hsien-I Lin, C. S. George Lee
2012 A* conf
ICRA
Rami Alazrai, C. S. George Lee
2011 C conf
ISADS
Shengwei Yu, C. S. George Lee
2011 B conf
SMC
Shengwei Yu, C. S. George Lee, Jianghai Hu
2011 A* conf
ICRA
Shengwei Yu, C. S. George Lee
2011 A conf
IROS
Shengwei Yu, C. S. George Lee
2010 A conf
IROS
Muhammad Ahmad Ali, Hyungju Andy Park, C. S. George Lee
2010 conf
Humanoids
Hyungju Andy Park, Muhammad Ahmad Ali, C. S. George Lee
2010 conf
Humanoids
Kazuhiko Kawamura, C. S. George Lee
2010 A conf
IROS
Yamini Nimmagadda, Karthik Kumar, Yung-Hsiang Lu, C. S. George Lee
2009 A* conf
ICRA
Hsien-I Lin, C. S. George Lee
2009 A conf
IROS
Hsien-I Lin, C. S. George Lee
2008 A conf
IROS
Hsien-I Lin, C. S. George Lee
2008 A* conf
ICRA
Hsien-I Lin, C. S. George Lee
2007 J jnl
Telecommun. Syst.
Simon G. M. Koo, C. S. George Lee
2007 J jnl
Ad Hoc Sens. Wirel. Networks
Dimitrios Koutsonikolas, Saumitra M. Das, Y. Charlie Hu, Yung-Hsiang Lu, C. S. George Lee
2007 J jnl
J. Commun. Networks
Saumitra M. Das, Y. Charlie Hu, C. S. George Lee, Yung-Hsiang Lu
2007 A conf
IROS
H. Jacky Chang, C. S. George Lee, Y. Charlie Hu, Yung-Hsiang Lu
2007 J jnl
IEEE Trans. Robotics
H. Jacky Chang, C. S. George Lee, Yung-Hsiang Lu, Y. Charlie Hu
2006 conf
ICDCS Workshops
Dimitrios Koutsonikolas, Saumitra M. Das, Y. Charlie Hu, Yung-Hsiang Lu, C. S. George Lee
2006 J jnl
IEEE Trans. Robotics
Yongguo Mei, Yung-Hsiang Lu, Yu Charlie Hu, C. S. George Lee
2006 A* conf
ICRA
Yongguo Mei, Yung-Hsiang Lu, C. S. George Lee, Y. Charlie Hu
2006 J jnl
Future Gener. Comput. Syst.
Simon G. M. Koo, Karthik N. Kannan, C. S. George Lee
2006 A* conf
ICRA
H. Jacky Chang, C. S. George Lee, Yung-Hsiang Lu, Y. Charlie Hu
2006 A* conf
ICRA
Nirut Naksuk, C. S. George Lee
2005 B conf
ICCCN
Simon G. M. Koo, C. S. George Lee, Karthik N. Kannan
2005 A* conf
ICRA
Saumitra M. Das, Y. Charlie Hu, C. S. George Lee, Yung-Hsiang Lu
2005 A* conf
PODC
Simon G. M. Koo, C. S. George Lee, Karthik N. Kannan
2005 A* conf
ICRA
Yongguo Mei, Yung-Hsiang Lu, Y. Charlie Hu, C. S. George Lee
2005 A* conf
ICRA
Saumitra M. Das, Y. Charlie Hu, C. S. George Lee, Yung-Hsiang Lu
2005 A conf
IROS
Yongguo Mei, Yung-Hsiang Lu, Y. Charlie Hu, C. S. George Lee
2005 A* conf
ICRA
Nirut Naksuk, C. S. George Lee
2005 conf
Humanoids
Nirut Naksuk, C. S. George Lee, Shirley Rietdyk
2004 B conf
ICCCN
Simon G. M. Koo, C. S. George Lee, Karthik N. Kannan
2004 A conf
IROS
H. Jacky Chang, C. S. George Lee, Yung-Hsiang Lu, Yu Charlie Hu
2004 A conf
IROS
Yongguo Mei, Yung-Hsiang Lu, Y. Charlie Hu, C. S. George Lee
2004 A* conf
ICRA
Yongguo Mei, Yung-Hsiang Lu, Y. Charlie Hu, C. S. George Lee
2004 A* conf
ICRA
H. Jacky Chang, C. S. George Lee, Yung-Hsiang Lu, Y. Charlie Hu
2004 conf
Humanoids
Nirut Naksuk, Yongguo Mei, C. S. George Lee
2004 J jnl
Adv. Robotics
Junku Yuh, Hajime Asama, C. S. George Lee
2004 A* conf
ICRA
Saumitra M. Das, Y. Charlie Hu, C. S. George Lee, Yung-Hsiang Lu
2003 J jnl
IEEE Trans. Robotics Autom.
Jeen-Shing Wang, C. S. George Lee
2002 A* conf
ICRA
Jeen-Shing Wang, C. S. George Lee
2002 J jnl
IEEE Trans. Fuzzy Syst.
Jeen-Shing Wang, C. S. George Lee
2001 A* conf
ICRA
C. S. George Lee, Jeen-Shing Wang
2001 J jnl
Adv. Robotics
C. S. George Lee, Jeen-Shing Wang, Junku Yuh
2000 B conf
FUZZ-IEEE
Zafar Ali, C. S. George Lee, Arif Ghafoor
2000 J jnl
IEEE J. Sel. Areas Commun.
Zafar Ali, Arif Ghafoor, C. S. George Lee
2000 A* conf
ICRA
Jeen-Shing Wang, C. S. George Lee, Junku Yuh
2000 A conf
IROS
C. S. George Lee, Jeen-Shing Wang
1999 A* conf
ICRA
Jeen-Shing Wang, C. S. George Lee, Junku Yuh
1999 A* conf
ICRA
Junku Yuh, Jing Nie, C. S. George Lee
1999 A conf
IROS
Jeen-Shing Wang, C. S. George Lee, Junku Yuh
1999 J jnl
J. Intell. Fuzzy Syst.
Wynne Hsu, C. S. George Lee
1998 J jnl
IEEE Trans. Robotics Autom.
Gyoung H. Kim, C. S. George Lee
1996 J jnl
IEEE Trans. Robotics Autom.
Shun-Feng Su, C. S. George Lee, Wynne Hsu
1996 A* conf
ICRA
Wynne Hsu, Andrew Lim, C. S. George Lee
1996 A* conf
ICRA
Gyoung H. Kim, C. S. George Lee
1995 J jnl
IEEE Trans. Syst. Man Cybern.
Chin-Teng Lin, C. S. George Lee
1995 J jnl
IEEE Trans. Syst. Man Cybern.
Tang-Kai Yin, C. S. George Lee
1995 A* conf
ICRA
Gyoung H. Kim, C. S. George Lee
1995 A* conf
ICRA
Wynne Hsu, C. S. George Lee
1994 A* conf
ICRA
Sangwook Park, C. S. George Lee
1994 A* conf
ICRA
Gyoung H. Kim, C. S. George Lee
1994 A* conf
ICRA
Ethan Z. Evans, C. S. George Lee
1994 A* conf
ICRA
Shun-Feng Su, C. S. George Lee
1994 J jnl
IEEE Trans. Fuzzy Syst.
Chin-Teng Lin, C. S. George Lee
1993 conf
ICRA (1)
Shun-Feng Su, C. S. George Lee
1993 J jnl
Comput. Aided Des.
Wynne Hsu, C. S. George Lee, Shun-Feng Su
1992 J jnl
IEEE Trans. Syst. Man Cybern.
Y. C. Tang, C. S. George Lee
1992 A* conf
ICRA
Wynne Hsu, C. S. George Lee, Shun-Feng Su
1992 J jnl
IEEE Trans. Syst. Man Cybern.
Shun-Feng Su, C. S. George Lee
1992 J jnl
Computer
John T. Feddema, C. S. George Lee, Owen Robert Mitchell
1992 ch.
Parallel Computation Systems for Robotics
C. S. George Lee
1991 J jnl
IEEE Trans. Syst. Man Cybern.
Chin-Teng Lin, C. S. George Lee
1991 J jnl
IEEE Trans. Computers
Chin-Teng Lin, C. S. George Lee
1991 A* conf
ICRA
Shun-Feng Su, C. S. George Lee
1991 J jnl
IEEE Trans. Robotics Autom.
John T. Feddema, C. S. George Lee, Owen Robert Mitchell
1990 J jnl
IEEE Trans. Syst. Man Cybern.
John T. Feddema, C. S. George Lee
1990 J jnl
IEEE Trans. Syst. Man Cybern.
C. S. George Lee, C. L. Philip Chen
1989 A* conf
ICRA
John T. Feddema, C. S. George Lee, Owen Robert Mitchell
1989 J jnl
IEEE Trans. Syst. Man Cybern.
Tak Bun Yeung, C. S. George Lee
1988 J jnl
IEEE J. Robotics Autom.
C. S. George Lee, Edwin S. H. Hou
1988 A* conf
ICRA
Tak Bun Yeung, C. S. George Lee
1988 J jnl
IEEE Trans. Syst. Man Cybern.
C. S. George Lee, Po Rong Chang
1988 A* conf
ICRA
C. L. Philip Chen, C. S. George Lee, Edwin S. H. Hou
1988 J jnl
IEEE Trans. Syst. Man Cybern.
C. L. Philip Chen, C. S. George Lee, Edwin S. H. Hou
1988 A* conf
ICRA
Po Rong Chang, C. S. George Lee
1987 J jnl
IEEE J. Robotics Autom.
C. S. George Lee, Po Rong Chang
1987 J jnl
IEEE Trans. Syst. Man Cybern.
Bum Hee Lee, C. S. George Lee
1987 J jnl
IEEE Trans. Syst. Man Cybern.
C. S. George Lee, Bum Hee Lee
1987 A* conf
ICRA
C. S. George Lee, Po Rong Chang
1987 A* conf
ICRA
C. S. George Lee, Y. J. Chao
1987 A* conf
ICRA
C. L. Philip Chen, C. S. George Lee, Clare D. McGillem
1987 J jnl
IEEE J. Robotics Autom.
Chun-Erh Chen, C. S. George Lee, Clare D. McGillem
1986 J jnl
IEEE Trans. Syst. Man Cybern.
C. S. George Lee, Po Rong Chang
1986 A* conf
ICRA
C. S. George Lee, Po Rong Chang
1985 A* conf
ICRA
C. S. George Lee, D. Huang
1985 A* conf
ICRA
Ravi Nigam, C. S. George Lee
1985 J jnl
IEEE J. Robotics Autom.
Ravi Nigam, C. S. George Lee
1985 J jnl
Computer
Scott Berman, Parag Parikh, C. S. George Lee
1984 A* conf
ICRA
C. S. George Lee, Moon-Jung Chung, Bum Hee Lee
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