Natalie D. Enright Jerger

89 papers A* 28A 6B 5C 1Journal 24Unranked 17
YearRankTypeTitle / Venue / Authors
2023 ed.
ASPLOS (2)
Tor M. Aamodt, Natalie D. Enright Jerger, Michael M. Swift
2023 ed.
ASPLOS (3)
Tor M. Aamodt, Natalie D. Enright Jerger, Michael M. Swift
2023 ed.
ASPLOS (4)
Tor M. Aamodt, Michael M. Swift, Natalie D. Enright Jerger
2022 J jnl
Adv. Comput.
Hossein Farrokhbakht, Shaahin Hessabi, Natalie D. Enright Jerger
2022 ed.
ASPLOS (1)
Tor M. Aamodt, Natalie D. Enright Jerger, Michael M. Swift
2022 A* conf
HPCA
Hossein Farrokhbakht, Paul V. Gratz, Tushar Krishna, Joshua San Miguel, Natalie D. Enright Jerger
2021 J jnl
ACM Comput. Surv.
Phillip Stanley-Marbell, Armin Alaghi, Michael Carbin, Eva Darulova, Lara Dolecek, Andreas Gerstlauer, Ghayoor Gillani, Djordje Jevdjic, Thierry Moreau, Mattia Cacciotti, Alexandros Daglis, Natalie D. Enright Jerger, Babak Falsafi, Sasa Misailovic, Adrian Sampson, Damien Zufferey
2021 conf
ICPP Workshops
Henry Kao, Joshua San Miguel, Natalie D. Enright Jerger
2021 A* conf
HPCA
Hossein Farrokhbakht, Henry Kao, Kamran Hasan, Paul V. Gratz, Tushar Krishna, Joshua San Miguel, Natalie D. Enright Jerger
2021 A conf
SC
Mayank Parasar, Natalie D. Enright Jerger, Paul V. Gratz, Joshua San Miguel, Tushar Krishna
2021 J jnl
Commun. ACM
Natalie D. Enright Jerger
2020 A* conf
HPCA
Mayank Parasar, Hossein Farrokhbakht, Natalie D. Enright Jerger, Paul V. Gratz, Tushar Krishna, Joshua San Miguel
2020 A* conf
HPCA
Jieming Yin, Subhash Sethumurugan, Yasuko Eckert, Chintan Patel, Alan Smith, Eric Morton, Mark Oskin, Natalie D. Enright Jerger, Gabriel H. Loh
2020 A* conf
ISCA
Mario Badr, Carlo Delconte, Isak Edo, Radhika Jagtap, Matteo Andreozzi, Natalie D. Enright Jerger
2019 ch.
Approximate Circuits
Natalie D. Enright Jerger, Joshua San Miguel
2019 J jnl
IEEE Trans. Computers
Xia Zhao, Sheng Ma, Zhiying Wang, Natalie D. Enright Jerger, Lieven Eeckhout
2019 A conf
ISLPED
Hossein Farrokhbakht, Hadi Mardani Kamali, Natalie D. Enright Jerger
2019 A* conf
MICRO
Mayank Parasar, Natalie D. Enright Jerger, Paul V. Gratz, Joshua San Miguel, Tushar Krishna
2019 A* conf
HPCA
Karthik Ganesan, Joshua San Miguel, Natalie D. Enright Jerger
2019 conf
NOCS
Hossein Farrokhbakht, Henry Kao, Natalie D. Enright Jerger
2018 J jnl
IEEE Embed. Syst. Lett.
Thierry Moreau, Joshua San Miguel, Mark Wyse, James Bornholt, Armin Alaghi, Luis Ceze, Natalie D. Enright Jerger, Adrian Sampson
2018 J jnl
Parallel Comput.
Mario Badr, Natalie D. Enright Jerger
2018 J jnl
IEEE Micro
Natalie D. Enright Jerger, Joshua San Miguel
2018 J jnl
CoRR
Phillip Stanley-Marbell, Armin Alaghi, Michael Carbin, Eva Darulova, Lara Dolecek, Andreas Gerstlauer, Ghayoor Gillani, Djordje Jevdjic, Thierry Moreau, Mattia Cacciotti, Alexandros Daglis, Natalie D. Enright Jerger, Babak Falsafi, Sasa Misailovic, Adrian Sampson, Damien Zufferey
2018 J jnl
Computer
Andreas Moshovos, Jorge Albericio, Patrick Judd, Alberto Delmas Lascorz, Sayeh Sharify, Zissis Poulos, Tayler H. Hetherington, Tor M. Aamodt, Natalie D. Enright Jerger
2018 conf
PMAM@PPoPP
Mario Badr, Natalie D. Enright Jerger
2018 conf
NEWCAS
Andreas Moshovos, Jorge Albericio, Patrick Judd, Alberto Delmas, Sayeh Sharify, Mostafa Mahmoud, Tayler H. Hetherington, Milos Nikolic, Dylan Malone Stuart, Kevin Siu, Zissis Poulos, Tor M. Aamodt, Natalie D. Enright Jerger
2018 A* conf
ISCA
Jieming Yin, Zhifeng Lin, Onur Kayiran, Matthew Poremba, Muhammad Shoaib Bin Altaf, Natalie D. Enright Jerger, Gabriel H. Loh
2018 ed.
ReQuEST@ASPLOS
Luis Ceze, Natalie D. Enright Jerger, Babak Falsafi, Grigori Fursin, Anton Lokhmotov, Thierry Moreau, Adrian Sampson, Phillip Stanley-Marbell
2018 J jnl
Parallel Comput.
Patrick Judd, Jorge Albericio, Tayler H. Hetherington, Tor M. Aamodt, Natalie D. Enright Jerger, Raquel Urtasun, Andreas Moshovos
2018 A conf
ISLPED
Hossein Farrokhbakht, Hadi Mardani Kamali, Natalie D. Enright Jerger, Shaahin Hessabi
2018 J jnl
IEEE Comput. Archit. Lett.
Joshua San Miguel, Karthik Ganesan, Mario Badr, Natalie D. Enright Jerger
2018 A* conf
MICRO
Joshua San Miguel, Karthik Ganesan, Mario Badr, Chunqiu Xia, Rose Li, Hsuan Hsiao, Natalie D. Enright Jerger
2018 J jnl
IEEE Micro
Andreas Moshovos, Jorge Albericio, Patrick Judd, Alberto Delmas Lascorz, Sayeh Sharify, Tayler H. Hetherington, Tor M. Aamodt, Natalie D. Enright Jerger
2017 book
Natalie D. Enright Jerger, Tushar Krishna, Li-Shiuan Peh
2016 A* conf
ISCA
Jorge Albericio, Patrick Judd, Tayler H. Hetherington, Tor M. Aamodt, Natalie D. Enright Jerger, Andreas Moshovos
2016 A* conf
HPCA
Jieming Yin, Onur Kayiran, Matthew Poremba, Natalie D. Enright Jerger, Gabriel H. Loh
2016 J jnl
IEEE Micro
Ajaykumar Kannan, Natalie D. Enright Jerger, Gabriel H. Loh
2016 conf
AISTECS@HiPEAC
Robert Hesse, Natalie D. Enright Jerger
2016 A conf
ICS
Patrick Judd, Jorge Albericio, Tayler H. Hetherington, Tor M. Aamodt, Natalie D. Enright Jerger, Andreas Moshovos
2016 A* conf
ISCA
Joshua San Miguel, Natalie D. Enright Jerger
2016 A* conf
MICRO
Joshua San Miguel, Jorge Albericio, Natalie D. Enright Jerger, Aamer Jaleel
2016 A* conf
HPCA
Zimo Li, Joshua San Miguel, Natalie D. Enright Jerger
2015 conf
NOCS
Joshua San Miguel, Natalie D. Enright Jerger
2015 A* conf
MICRO
Joshua San Miguel, Jorge Albericio, Andreas Moshovos, Natalie D. Enright Jerger
2015 A* conf
MICRO
Ajaykumar Kannan, Natalie D. Enright Jerger, Gabriel H. Loh
2015 conf
NOCS
Robert Hesse, Natalie D. Enright Jerger
2015 conf
MEMSYS
Gabriel H. Loh, Natalie D. Enright Jerger, Ajaykumar Kannan, Yasuko Eckert
2015 J jnl
IEEE Trans. Computers
Sheng Ma, Zhiying Wang, Zonglin Liu, Natalie D. Enright Jerger
2015 J jnl
CoRR
Patrick Judd, Jorge Albericio, Tayler H. Hetherington, Tor M. Aamodt, Natalie D. Enright Jerger, Raquel Urtasun, Andreas Moshovos
2014 B conf
ISPASS
Wenbo Dai, Natalie D. Enright Jerger
2014 J jnl
IEEE Trans. Computers
Danyao Wang, Charles Lo, Jasmina Vasiljevic, Natalie D. Enright Jerger, J. Gregory Steffan
2014 A* conf
MICRO
Haofan Yang, Jyoti Tripathi, Natalie D. Enright Jerger, Dan Gibson
2014 conf
ANCS
Andrew Bitar, Jeffrey Cassidy, Natalie D. Enright Jerger, Vaughn Betz
2014 conf
ICSAMOS
Goran Narancic, Patrick Judd, Di Wu, Islam Atta, Michel Elnacouzi, Jason Zebchuk, Jorge Albericio, Natalie D. Enright Jerger, Andreas Moshovos, Kyros Kutulakos, Serag GadelRab
2014 J jnl
IEEE Trans. Computers
Sheng Ma, Natalie D. Enright Jerger, Zhiying Wang, Ming-Che Lai, Libo Huang
2014 A* conf
MICRO
Joshua San Miguel, Mario Badr, Natalie D. Enright Jerger
2014 A* conf
MICRO
Natalie D. Enright Jerger, Ajaykumar Kannan, Zimo Li, Gabriel H. Loh
2014 J jnl
IEEE Trans. Parallel Distributed Syst.
Sheng Ma, Zhiying Wang, Natalie D. Enright Jerger, Li Shen, Nong Xiao
2014 conf
GPGPU@ASPLOS
Tahir Diop, Natalie D. Enright Jerger, Jason Helge Anderson
2014 conf
NOCS
Parisa Khadem Hamedani, Natalie D. Enright Jerger, Shaahin Hessabi
2014 conf
NOCS
Wenbo Dai, Natalie D. Enright Jerger
2014 A* conf
ISCA
Mario Badr, Natalie D. Enright Jerger
2014 A* conf
MICRO
Jorge Albericio, Joshua San Miguel, Natalie D. Enright Jerger, Andreas Moshovos
2013 A conf
DATE
Michel El-Nacouzi, Islam Atta, Myrto Papadopoulou, Jason Zebchuk, Natalie D. Enright Jerger, Andreas Moshovos
2013 B conf
MASCOTS
Tahir Diop, Steven Gurfinkel, Jason Helge Anderson, Natalie D. Enright Jerger
2013 J jnl
Comput. Sci. Eng.
Natalie D. Enright Jerger
2013 J jnl
Int. J. Adapt. Resilient Auton. Syst.
Parisa Khadem Hamedani, Natalie D. Enright Jerger, Shaahin Hessabi, Hamid Sarbazi-Azad
2013 J jnl
ACM Trans. Embed. Comput. Syst.
Matthew Misler, Natalie D. Enright Jerger
2013 B conf
ISPASS
John Matienzo, Natalie D. Enright Jerger
2012 C conf
PDP
Parisa Khadem Hamedani, Shaahin Hessabi, Hamid Sarbazi-Azad, Natalie D. Enright Jerger
2012 conf
NOCS
Robert Hesse, Jeff Nicholls, Natalie D. Enright Jerger
2012 A* conf
HPCA
Sheng Ma, Natalie D. Enright Jerger, Zhiying Wang
2012 A* conf
HPCA
Sheng Ma, Natalie D. Enright Jerger, Zhiying Wang
2011 conf
NOCS
Danyao Wang, Natalie D. Enright Jerger, J. Gregory Steffan
2011 A* conf
ISCA
Sheng Ma, Natalie D. Enright Jerger, Zhiying Wang
2011 J jnl
IEEE Micro
Natalie D. Enright Jerger, Mikko H. Lipasti
2010 B conf
PACT
Matthew Misler, Natalie D. Enright Jerger
2010 A conf
DATE
Natalie D. Enright Jerger
2009 A* conf
ISCA
Dennis Abts, Natalie D. Enright Jerger, John Kim, Dan Gibson, Mikko H. Lipasti
2009 book
Natalie D. Enright Jerger, Li-Shiuan Peh
2009 J jnl
IEEE Trans. Comput. Aided Des. Integr. Circuits Syst.
Radu Marculescu, Ümit Y. Ogras, Li-Shiuan Peh, Natalie D. Enright Jerger, Yatin Vasant Hoskote
2009 A* conf
MICRO
Mitchell Hayenga, Natalie D. Enright Jerger, Mikko H. Lipasti
2008 conf
NOCS
Natalie D. Enright Jerger, Li-Shiuan Peh, Mikko H. Lipasti
2008 A* conf
ISCA
Natalie D. Enright Jerger, Li-Shiuan Peh, Mikko H. Lipasti
2008 A* conf
MICRO
Natalie D. Enright Jerger, Li-Shiuan Peh, Mikko H. Lipasti
2007 conf
IISWC
Natalie D. Enright Jerger, Dana Vantrease, Mikko H. Lipasti
2007 J jnl
IEEE Comput. Archit. Lett.
Natalie D. Enright Jerger, Mikko H. Lipasti, Li-Shiuan Peh
2006 B conf
ISPASS
Natalie D. Enright Jerger, Eric L. Hill, Mikko H. Lipasti
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))