Ondrej Drbohlav

18 papers A* 4A 1Journal 1Unranked 12
YearRankTypeTitle / Venue / Authors
2023 conf
ICCV (Workshops)
Matej Kristan, Jirí Matas, Martin Danelljan, Michael Felsberg, Hyung Jin Chang, Luka Cehovin Zajc, Alan Lukezic, Ondrej Drbohlav, Zhongqun Zhang, Khanh-Tung Tran, Xuan-Son Vu, Johanna Björklund, Christoph Mayer, Yushan Zhang, Lei Ke, Jie Zhao, Gustavo Fernández, Noor Al-Shakarji, Dong An, Michael Arens, Stefan Becker, Goutam Bhat, Sebastian Bullinger, Antoni B. Chan, Shijie Chang, Hanyuan Chen, Xin Chen, Yan Chen, Zhenyu Chen, Yangming Cheng, Yutao Cui, Chunyuan Deng, Jiahua Dong, Matteo Dunnhofer, Wei Feng, Jianlong Fu, Jie Gao, Ruize Han, Zeqi Hao, Jun-Yan He, Keji He, Zhenyu He, Xiantao Hu, Kaer Huang, Yuqing Huang, Yi Jiang, Ben Kang, Jin-Peng Lan, Hyungjun Lee, Chenyang Li, Jiahao Li, Ning Li, Wangkai Li, Xiaodi Li, Xin Li, Pengyu Liu, Yue Liu, Huchuan Lu, Bin Luo, Ping Luo, Yinchao Ma, Deshui Miao, Christian Micheloni, Kannappan Palaniappan, Hancheol Park, Matthieu Paul, Houwen Peng, Zekun Qian, Gani Rahmon, Norbert Scherer-Negenborn, Pengcheng Shao, Wooksu Shin, Elham Soltani Kazemi, Tianhui Song, Rainer Stiefelhagen, Rui Sun, Chuanming Tang, Zhangyong Tang, Imad Eddine Toubal, Jack Valmadre, Joost van de Weijer, Luc Van Gool, Jash Vira, Stéphane Vujasinovic, Cheng Wan, Jia Wan, Dong Wang, Fei Wang, Feifan Wang, He Wang, Limin Wang, Song Wang, Yaowei Wang, Zhepeng Wang, Gangshan Wu, Jiannan Wu, Qiangqiang Wu, Xiaojun Wu, Anqi Xiao, Jinxia Xie, Chenlong Xu, Min Xu, Tianyang Xu, Yuanyou Xu, Bin Yan, Dawei Yang, Ming-Hsuan Yang, Tianyu Yang, Yi Yang, Zongxin Yang, Xuanwu Yin, Fisher Yu, Hongyuan Yu, Qianjin Yu, Weichen Yu, Yongsheng Yuan, Zehuan Yuan, Jianlin Zhang, Lu Zhang, Tianzhu Zhang, Guodongfang Zhao, Shaochuan Zhao, Yaozong Zheng, Bineng Zhong, Jiawen Zhu, Xuefeng Zhu, Yueting Zhuang, ChengAo Zong, Kunlong Zuo
2022 conf
ECCV Workshops (8)
Matej Kristan, Ales Leonardis, Jirí Matas, Michael Felsberg, Roman P. Pflugfelder, Joni-Kristian Kämäräinen, Hyung Jin Chang, Martin Danelljan, Luka Cehovin Zajc, Alan Lukezic, Ondrej Drbohlav, Johanna Björklund, Yushan Zhang, Zhongqun Zhang, Song Yan, Wenyan Yang, Dingding Cai, Christoph Mayer, Gustavo Fernández, Kang Ben, Goutam Bhat, Hong Chang, Guangqi Chen, Jiaye Chen, Shengyong Chen, Xilin Chen, Xin Chen, Xiuyi Chen, Yiwei Chen, Yu-Hsi Chen, Zhixing Chen, Yangming Cheng, Angelo Ciaramella, Yutao Cui, Benjamin Dzubur, Mohana Murali Dasari, Qili Deng, Debajyoti Dhar, Shangzhe Di, Emanuel Di Nardo, Daniel K. Du, Matteo Dunnhofer, Heng Fan, Zhenhua Feng, Zhihong Fu, Shang Gao, Rama Krishna Sai S. Gorthi, Eric Granger, Q. H. Gu, Himanshu Gupta, Jianfeng He, Keji He, Yan Huang, Deepak Jangid, Rongrong Ji, Cheng Jiang, Yingjie Jiang, Felix Järemo Lawin, Ze Kang, Madhu Kiran, Josef Kittler, Simiao Lai, Xiangyuan Lan, Dongwook Lee, Hyunjeong Lee, Seohyung Lee, Hui Li, Ming Li, Wangkai Li, Xi Li, Xianxian Li, Xiao Li, Zhe Li, Liting Lin, Haibin Ling, Bo Liu, Chang Liu, Si Liu, Huchuan Lu, Rafael M. O. Cruz, Bingpeng Ma, Chao Ma, Jie Ma, Yinchao Ma, Niki Martinel, Alireza Memarmoghadam, Christian Micheloni, Payman Moallem, Le Thanh Nguyen-Meidine, Siyang Pan, Changbeom Park, Danda Pani Paudel, Matthieu Paul, Houwen Peng, Andreas Robinson, Litu Rout, Shiguang Shan, Kristian Simonato, Tianhui Song, Xiaoning Song, Chao Sun, Jingna Sun, Zhangyong Tang, Radu Timofte, Chi-Yi Tsai, Luc Van Gool, Om Prakash Verma, Dong Wang, Fei Wang, Liang Wang, Liangliang Wang, Lijun Wang, Limin Wang, Qiang Wang, Gangshan Wu, Jinlin Wu, Xiaojun Wu, Fei Xie, Tianyang Xu, Wei Xu, Yong Xu, Yuanyou Xu, Wanli Xue, Zizheng Xun, Bin Yan, Dawei Yang, Jinyu Yang, Wankou Yang, Xiaoyun Yang, Yi Yang, Yichun Yang, Zongxin Yang, Botao Ye, Fisher Yu, Hongyuan Yu, Jiaqian Yu, Qianjin Yu, Weichen Yu, Kang Ze, Jiang Zhai, Chengwei Zhang, Chunhu Zhang, Kaihua Zhang, Tianzhu Zhang, Wenkang Zhang, Zhibin Zhang, Zhipeng Zhang, Jie Zhao, Shao-Chuan Zhao, Feng Zheng, Haixia Zheng, Min Zheng, Bineng Zhong, Jiawen Zhu, Xuefeng Zhu, Yueting Zhuang
2021 conf
ICCVW
Matej Kristan, Jirí Matas, Ales Leonardis, Michael Felsberg, Roman P. Pflugfelder, Joni-Kristian Kämäräinen, Hyung Jin Chang, Martin Danelljan, Luka Cehovin Zajc, Alan Lukezic, Ondrej Drbohlav, Jani Käpylä, Gustav Häger, Song Yan, Jinyu Yang, Zhongqun Zhang, Gustavo Fernández, Mohamed H. Abdelpakey, Goutam Bhat, Llukman Cerkezi, Hakan Cevikalp, Shengyong Chen, Xin Chen, Miao Cheng, Ziyi Cheng, Yu-Chen Chiu, Ozgun Cirakman, Yutao Cui, Kenan Dai, Mohana Murali Dasari, Qili Deng, Xingping Dong, Daniel K. Du, Matteo Dunnhofer, Zhenhua Feng, Zhiyong Feng, Zhihong Fu, Shiming Ge, Rama Krishna Sai S. Gorthi, Yuzhang Gu, Bilge Günsel, Qing Guo, Filiz Gurkan, Wencheng Han, Yanyan Huang, Felix Järemo Lawin, Shang-Jhih Jhang, Rongrong Ji, Cheng Jiang, Yingjie Jiang, Felix Juefei-Xu, J. Yin, Xiao Ke, Fahad Shahbaz Khan, Byeong Hak Kim, Josef Kittler, Xiangyuan Lan, Jun Ha Lee, Bastian Leibe, Hui Li, Jianhua Li, Xianxian Li, Yuezhou Li, Bo Liu, Chang Liu, Jingen Liu, Li Liu, Qingjie Liu, Huchuan Lu, Wei Lu, Jonathon Luiten, Jie Ma, Ziang Ma, Niki Martinel, Christoph Mayer, Alireza Memarmoghadam, Christian Micheloni, Yuzhen Niu, Danda Pani Paudel, Houwen Peng, Shoumeng Qiu, Aravindh Rajiv, Muhammad Rana, Andreas Robinson, Hasan Saribas, Ling Shao, Mohamed Shehata, Furao Shen, Jianbing Shen, Kristian Simonato, Xiaoning Song, Zhangyong Tang, Radu Timofte, Philip H. S. Torr, Chi-Yi Tsai, Bedirhan Uzun, Luc Van Gool, Paul Voigtlaender, Dong Wang, Guangting Wang, Liangliang Wang, Lijun Wang, Limin Wang, Linyuan Wang, Yong Wang, Yunhong Wang, Chenyan Wu, Gangshan Wu, Xiaojun Wu, Fei Xie, Tianyang Xu, Xiang Xu, Wanli Xue, Bin Yan, Wankou Yang, Xiaoyun Yang, Yu Ye, Jun Yin, Chengwei Zhang, Chunhui Zhang, Haitao Zhang, Kaihua Zhang, Kangkai Zhang, Xiaohan Zhang, Xiaolin Zhang, Xinyu Zhang, Zhibin Zhang, Shao-Chuan Zhao, Ming Zhen, Bineng Zhong, Jiawen Zhu, Xuefeng Zhu
2020 conf
ECCV Workshops (5)
Matej Kristan, Ales Leonardis, Jiri Matas, Michael Felsberg, Roman P. Pflugfelder, Joni-Kristian Kämäräinen, Martin Danelljan, Luka Cehovin Zajc, Alan Lukezic, Ondrej Drbohlav, Linbo He, Yushan Zhang, Song Yan, Jinyu Yang, Gustavo Fernández, Alexander G. Hauptmann, Alireza Memarmoghadam, Álvaro García-Martín, Andreas Robinson, Anton Varfolomieiev, Awet Haileslassie Gebrehiwot, Bedirhan Uzun, Bin Yan, Bing Li, Chen Qian, Chi-Yi Tsai, Christian Micheloni, Dong Wang, Fei Wang, Fei Xie, Felix Järemo Lawin, Fredrik Gustafsson, Gian Luca Foresti, Goutam Bhat, Guangqi Chen, Haibin Ling, Haitao Zhang, Hakan Cevikalp, Haojie Zhao, Haoran Bai, Hari Chandana Kuchibhotla, Hasan Saribas, Heng Fan, Hossein Ghanei-Yakhdan, Houqiang Li, Houwen Peng, Huchuan Lu, Hui Li, Javad Khaghani, Jesús Bescós, Jianhua Li, Jianlong Fu, Jiaqian Yu, Jingtao Xu, Josef Kittler, Jun Yin, Junhyun Lee, Kaicheng Yu, Kaiwen Liu, Kang Yang, Kenan Dai, Li Cheng, Li Zhang, Lijun Wang, Linyuan Wang, Luc Van Gool, Luca Bertinetto, Matteo Dunnhofer, Miao Cheng, Mohana Murali Dasari, Ning Wang, Pengyu Zhang, Philip H. S. Torr, Qiang Wang, Radu Timofte, Rama Krishna Sai Subrahmanyam Gorthi, Seokeon Choi, Seyed Mojtaba Marvasti-Zadeh, Shao-Chuan Zhao, Shohreh Kasaei, Shoumeng Qiu, Shuhao Chen, Thomas B. Schön, Tianyang Xu, Wei Lu, Weiming Hu, Wengang Zhou, Xi Qiu, Xiao Ke, Xiao-Jun Wu, Xiaolin Zhang, Xiaoyun Yang, Xuefeng Zhu, Yingjie Jiang, Yingming Wang, Yiwei Chen, Yu Ye, Yuezhou Li, Yuncon Yao, Yunsung Lee, Yuzhang Gu, Zezhou Wang, Zhangyong Tang, Zhenhua Feng, Zhijun Mai, Zhipeng Zhang, Zhirong Wu, Ziang Ma
2019 conf
ICCV Workshops
Matej Kristan, Amanda Berg, Linyu Zheng, Litu Rout, Luc Van Gool, Luca Bertinetto, Martin Danelljan, Matteo Dunnhofer, Meng Ni, Min Young Kim, Ming Tang, Ming-Hsuan Yang, Abdelrahman Eldesokey, Naveen Paluru, Niki Martinel, Pengfei Xu, Pengfei Zhang, Pengkun Zheng, Pengyu Zhang, Philip H. S. Torr, Qi Zhang, Qiang Wang, Qing Guo, Radu Timofte, Jani Käpylä, Rama Krishna Sai Subrahmanyam Gorthi, Richard M. Everson, Ruize Han, Ruohan Zhang, Shan You, Shao-Chuan Zhao, Shengwei Zhao, Shihu Li, Shikun Li, Shiming Ge, Gustavo Fernández, Shuai Bai, Shuosen Guan, Tengfei Xing, Tianyang Xu, Tianyu Yang, Ting Zhang, Tomás Vojír, Wei Feng, Weiming Hu, Weizhao Wang, Abel Gonzalez-Garcia, Wenjie Tang, Wenjun Zeng, Wenyu Liu, Xi Chen, Xi Qiu, Xiang Bai, Xiao-Jun Wu, Xiaoyun Yang, Xier Chen, Xin Li, Alireza Memarmoghadam, Xing Sun, Xingyu Chen, Xinmei Tian, Xu Tang, Xuefeng Zhu, Yan Huang, Yanan Chen, Yanchao Lian, Yang Gu, Yang Liu, Andong Lu, Yanjie Chen, Yi Zhang, Yinda Xu, Yingming Wang, Yingping Li, Yu Zhou, Yuan Dong, Yufei Xu, Yunhua Zhang, Yunkun Li, Anfeng He, Zeyu Wang, Zhao Luo, Zhaoliang Zhang, Zhenhua Feng, Zhenyu He, Zhichao Song, Zhihao Chen, Zhipeng Zhang, Zhirong Wu, Zhiwei Xiong, Zhongjian Huang, Anton Varfolomieiev, Zhu Teng, Zihan Ni, Antoni B. Chan, Jirí Matas, Ardhendu Shekhar Tripathi, Arnold W. M. Smeulders, Bala Suraj Pedasingu, Bao Xin Chen, Baopeng Zhang, Baoyuan Wu, Bi Li, Bin He, Bin Yan, Bing Bai, Ales Leonardis, Bing Li, Bo Li, Byeong Hak Kim, Chao Ma, Chen Fang, Chen Qian, Cheng Chen, Chenglong Li, Chengquan Zhang, Chi-Yi Tsai, Michael Felsberg, Chong Luo, Christian Micheloni, Chunhui Zhang, Dacheng Tao, Deepak Gupta, Dejia Song, Dong Wang, Efstratios Gavves, Eunu Yi, Fahad Shahbaz Khan, Roman P. Pflugfelder, Fangyi Zhang, Fei Wang, Fei Zhao, George De Ath, Goutam Bhat, Guangqi Chen, Guangting Wang, Guoxuan Li, Hakan Cevikalp, Hao Du, Joni-Kristian Kämäräinen, Haojie Zhao, Hasan Saribas, Ho Min Jung, Hongliang Bai, Hongyuan Yu, Houwen Peng, Huchuan Lu, Hui Li, Jiakun Li, Luka Cehovin Zajc, Jianhua Li, Jianlong Fu, Jie Chen, Jie Gao, Jie Zhao, Jin Tang, Jing Li, Jingjing Wu, Jingtuo Liu, Jinqiao Wang, Ondrej Drbohlav, Jinqing Qi, Jinyue Zhang, John K. Tsotsos, Jong Hyuk Lee, Joost van de Weijer, Josef Kittler, Jun Ha Lee, Junfei Zhuang, Kangkai Zhang, Kangkang Wang, Alan Lukezic, Kenan Dai, Lei Chen, Lei Liu, Leida Guo, Li Zhang, Liang Wang, Liangliang Wang, Lichao Zhang, Lijun Wang, Lijun Zhou
2014 conf
ACCV Workshops (3)
Jan Macák, Ondrej Drbohlav
2014 conf
VISAPP (1)
Jan Macák, Ondrej Drbohlav
2013 conf
ICIAP (1)
Jan Macák, Ondrej Drbohlav
2010 J jnl
Comput. Vis. Image Underst.
Ondrej Drbohlav, Ales Leonardis
2008 conf
VISAPP (2)
Jean-Yves Guillemaut, Ondrej Drbohlav, John Illingworth, Radim Sára
2007 A* conf
ICCV
Dusan Omercevic, Ondrej Drbohlav, Ales Leonardis
2005 A* conf
ICCV
Ondrej Drbohlav, Mike J. Chantler
2005 A* conf
ICCV
Ondrej Drbohlav, Mike J. Chantler
2005 A conf
BMVC
Ondrej Drbohlav, Mike J. Chantler
2004 conf
3DPVT
Jean-Yves Guillemaut, Ondrej Drbohlav, Radim Sára, John Illingworth
2004 conf
CVPR (1)
Zsolt Jankó, Ondrej Drbohlav, Radim Sára
2002 conf
ECCV (2)
Ondrej Drbohlav, Radim Sára
2001 A* conf
ICCV
Ondrej Drbohlav, Radim Sára
redb/extractors/decompiler/bninja/decompiler.py
← Index redb/extractors/decompiler/bninja/decompiler.py python
import os
import time
import json

from .analysis.medium_level import MediumLevelAnalysis

# disable the plugins set by user for binary ninja
os.environ["BN_DISABLE_USER_PLUGINS"] = "True"
import traceback

# Binary Ninja imports (conditional)
try:
    import binaryninja
    from binaryninja import mainthread, Symbol
    from binaryninja.enums import SymbolType
    BINARYNINJA_AVAILABLE = True
except Exception:
    BINARYNINJA_AVAILABLE = False
    binaryninja = None
    mainthread = None
    Symbol = None
    SymbolType = None

# Support both package and standalone imports
try:
    # Package import (when imported from redb)
    from .utils.hashes import calculate_sha256, calculate_tlsh
    from .utils.license import set_license
    from .utils.logging import setup_default_logger
    from .analysis.strings import StringAnalysis

    # Only import modules that depend on Binary Ninja when available
    if BINARYNINJA_AVAILABLE:
        from .analysis.cfg import CFGAnalysis
        from .analysis.disassembly import DisassemblyAnalysis
        from .analysis.low_level import LowLevelAnalysis
        from .arch.creator import ArchitectureCreator
        from .custom_options import register_custom_analysis_options
        from .function_type import FunctionTypeAnalysis, FunctionType
        from .analysis.scores import ObfuscationScores

except ImportError:
    # Fallback to absolute imports (for multiprocessing spawned processes)
    from redb.extractors.decompiler.bninja.utils.hashes import calculate_sha256, calculate_tlsh
    from redb.extractors.decompiler.bninja.utils.license import set_license
    from redb.extractors.decompiler.bninja.utils.logging import setup_default_logger

    # Only import modules that depend on Binary Ninja when available
    if BINARYNINJA_AVAILABLE:
        from redb.extractors.decompiler.bninja.analysis.cfg import CFGAnalysis
        from redb.extractors.decompiler.bninja.analysis.disassembly import DisassemblyAnalysis
        from redb.extractors.decompiler.bninja.analysis.low_level import LowLevelAnalysis
        from redb.extractors.decompiler.bninja.arch.creator import ArchitectureCreator
        from redb.extractors.decompiler.bninja.custom_options import register_custom_analysis_options
        from redb.extractors.decompiler.bninja.function_type import FunctionTypeAnalysis


class BinaryNinjaDecompiler:
    """A Binary Ninja-based decompiler that replicates the functionality of GhidraDecompilerScript.
    This class extracts decompiled code, disassembly with multiple normalization levels,
    and control flow graph information from binary files.
    """

    MIN_FUNCTION_SIZE = 10  # instructions
    MIN_BLOCK_SIZE = 4  # instructions
    INVALID_STACK_SIZE = -1

    def __init__(
        self,
        filepath,
        timeout,
        log=None,
        exporters=None,
        index_prefix=None,
        filetype=None,
        goresym=None,
        decompile_modules=None,
    ):
        """Initialize the Binary Ninja decompiler.

        Args:
            filepath: Path to the binary file to analyze
            log: Logger object (optional)
            timeout: Maximum time in seconds for analysis (default: 1200)
            exporters: List of exporters for the results (optional)
            index_prefix: Prefix for elastic index (optional)
            filetype: Type of the file (optional)

        """
        self.filepath = filepath
        self.log = log if log else setup_default_logger("BninjaDecompiler")
        self.BNINJA_TIMEOUT = timeout
        self.bv = None
        self.analysis_results = None
        self.errors = []
        self.exporters = exporters
        self.index_prefix = index_prefix
        self.filetype = filetype
        self.goresym = goresym
        self.decompile_modules = decompile_modules or {"all"}

        set_license(binaryninja)

        # Map to track instruction categorization
        mainthread.set_worker_thread_count(3)
        register_custom_analysis_options(binaryninja)

    def log_error(
        self, message, function_name, address, exception=None, error_location="unknown"
    ):
        """Log an error during processing."""
        error_msg = f"Error in function {function_name} at {address}: {message}"
        if exception:
            error_msg += f" - {str(exception)}"

        self.log.error(error_msg)

        # Add to errors list
        error = {
            "function_name": function_name,
            "function_address": str(address),
            "error_location": error_location,
            "error_message": message,
            "error_details": str(exception) if exception else "",
            "error_type": type(exception).__name__ if exception else "Unknown",
            "timestamp": int(time.time() * 1000),
        }
        self.errors.append(error)

    def __enter__(self):
        """Context manager entry point."""
        self.log.info(f"Opening binary file: {self.filepath}")
        binaryninja.BinaryViewType.add_binaryview_initial_analysis_completion_event(
            self.on_analysis_complete
        )

        #self.bv = binaryninja.load(self.filepath, update_analysis=False)
        self.bv = binaryninja.load(self.filepath, update_analysis=True)
        if self.bv is None:
            raise ValueError(f"Failed to open file: {self.filepath}")

        self.log.info("Waiting for analysis to complete...")

        self.log.debug(f"Binja analysis complete: {len(list(self.bv.functions))} functions")

        # set the architecture
        # todo: personalize this for other architectures
        self.arch = ArchitectureCreator("x86").get()

        # apply goresym
        if self.goresym is not None:
            self.__apply_goresym()
        return self

    def on_analysis_complete(self, bv):
        # Request an additional update after analysis is complete to ensure IL generation
        self.bv = bv
        self.bv.update_analysis()

        return

    def __apply_goresym(self):
        file = self.goresym
        data = None
        try:
            data = json.loads(open(file, 'r').read())
        except Exception as e:
            self.log_error(
                "Failed to open file from goresym: ",
                file,
                e,
                "analyze_binary",
            )

        if data is None:
            return

        self.bv.begin_undo_actions()
        if data.get('UserFunctions') is not None:
            user_functions = data['UserFunctions']
            for func in user_functions:
                try:
                    start = int(func['Start'])
                    name = func['FullName']
                    if self.bv.get_function_at(start) is None:
                        self.bv.create_user_function(start)

                    sym = Symbol(SymbolType.FunctionSymbol, start, name, name, name)
                    self.bv.define_user_symbol(sym)
                except Exception as e:
                    self.log.warning(f"Failed to apply GoReSym symbol for UserFunction {func.get('FullName', 'unknown')} at {func.get('Start', 'unknown')}: {e}")

        if data.get('StdFunctions') is not None:
            standard_functions = data['StdFunctions']
            for func in standard_functions:
                try:
                    start = int(func['Start'])
                    name = func['FullName']
                    if self.bv.get_function_at(start) is None:
                        self.bv.create_user_function(start)

                    sym = Symbol(SymbolType.FunctionSymbol, start, name, name, name)
                    self.bv.define_user_symbol(sym)
                except Exception as e:
                    self.log.warning(f"Failed to apply GoReSym symbol for StdFunction {func.get('FullName', 'unknown')} at {func.get('Start', 'unknown')}: {e}")

        self.bv.commit_undo_actions()
        return

    def __exit__(self, exc_type, exc_val, exc_tb):
        """Context manager exit point - clean up resources."""
        if self.bv:
            # Make sure to cancel any pending analysis
            # (if we have no pending analysis, binary ninja will log an error)

            # todo(@nicolo): investigate
            # if hasattr(self.bv, "abort_analysis"):
            #    self.bv.abort_analysis()
            self.bv.file.close()

        self.log.info("Cleanup completed successfully")
        return

    def tag(self):
        """Return the tag for this extractor."""
        return "DECOMPILED"

    def _module_selected(self, module_name):
        """Check if a decompiler sub-module is selected."""
        return "all" in self.decompile_modules or module_name in self.decompile_modules

    def analyze_binary(self):
        """Run Binary Ninja analysis and return results.

        Respects self.decompile_modules to selectively run/skip sub-modules:
        - strings: independent, skipped if not selected
        - decompilation: leaf module, skipped if not selected
        - disassembly: always runs (backbone — provides hash linkage for all others)
        - llil: leaf module, skipped if not selected
        - cfg: leaf module, skipped if not selected

        Only selected modules' results are appended to the results dict for DB insertion.
        Disassembly is always computed for linkage but only inserted when selected.
        """
        try:
            results = {
                "decompiled": [],
                "disassembled": [],
                "cfg": [],
                "llil": [],
                "errors": [],
                "strings": [],
                "mlil": []
            }

            run_all = "all" in self.decompile_modules
            run_strings = run_all or "strings" in self.decompile_modules
            run_decompilation = run_all or "decompilation" in self.decompile_modules
            run_disassembly = run_all or "disassembly" in self.decompile_modules
            run_llil = run_all or "llil" in self.decompile_modules
            run_cfg = run_all or "cfg" in self.decompile_modules

            # Determine if we need the per-function loop at all
            need_per_function = run_decompilation or run_disassembly or run_llil or run_cfg

            #functions_list = list(filter(is_not_ext_lib_function, self.bv.functions))
            functions_list = list(filter(is_lib_or_thunk, self.bv.functions))
            functions_list = list(filter(self.is_too_few_blocks, functions_list))

            # Strings extraction — independent of per-function analysis
            if run_strings:
                results["strings"] = StringAnalysis(self.bv, functions_list).analyze()

            if not need_per_function:
                return results

            for function in functions_list:
                try:
                    # Decompilation (HLIL) — leaf module, skip if not selected
                    hlil_json = self.extract_hlil(function) if run_decompilation else None

                    # Disassembly — always compute (provides hash linkage for others)
                    disass_json = self.extract_disasm(function)

                    # CFG — leaf module, skip if not selected
                    cfg_json = self.extract_cfg(function) if run_cfg else None

                    # LLIL — leaf module, skip if not selected
                    lowlevel_json = self.extract_lowlevel(function) if run_llil else None

                    # we run mlil only if we have cfg
                    mlil_json = self.extract_mediumlevel(function) if run_llil else None

                    # Calculate fuzzy hashes for disassembly using utils.hashes
                    if disass_json:
                        disass_no_addr = disass_json.get("disassembled_function_no_addresses", "")
                        disass_json["tlsh_disassembly"] = calculate_tlsh(disass_no_addr)

                    if hlil_json and disass_json:
                        hlil_json["disassembled_function_hash"] = disass_json[
                            "disassembled_function_hash"
                        ]
                        disass_json["decompiled_function_hash"] = hlil_json[
                            "decompiled_function_hash"
                        ]

                        results["decompiled"].append(hlil_json)
                        if run_disassembly:
                            results["disassembled"].append(disass_json)

                    elif hlil_json:
                        hlil_json["disassembled_function_hash"] = None
                        results["decompiled"].append(hlil_json)
                    elif disass_json:
                        disass_json["decompiled_function_hash"] = None
                        if run_disassembly:
                            results["disassembled"].append(disass_json)

                    # Add bi-directional linkage between LLIL and disassembly with fuzzy hashes
                    # LLIL fuzzy hashes (tlsh_llil) are already calculated in lowlevel_json
                    if lowlevel_json and disass_json:
                        # Add disassembly info to LLIL
                        lowlevel_json["disassembled_function_hash"] = disass_json["disassembled_function_hash"]
                        lowlevel_json["tlsh_disassembly"] = disass_json.get("tlsh_disassembly")

                        # Add LLIL fuzzy hashes to disassembly for easy export access
                        disass_json["tlsh_llil"] = lowlevel_json.get("tlsh_llil")
                        disass_json["minhash"] = lowlevel_json.get("minhash")

                    elif lowlevel_json:
                        lowlevel_json["disassembled_function_hash"] = None
                        lowlevel_json["tlsh_disassembly"] = None
                    elif disass_json:
                        # No LLIL available for this disassembly
                        disass_json["tlsh_llil"] = None
                        disass_json["minhash"] = None

                    if lowlevel_json:
                        results["llil"].append(lowlevel_json)
                        results["mlil"].append(mlil_json)

                    # Add CFG linkage with disassembled_function_hash
                    # Also add cyclomatic_complexity to disass_json for similarity metrics export
                    if cfg_json and disass_json:
                        cfg_json["disassembled_function_hash"] = disass_json["disassembled_function_hash"]
                        disass_json["cyclomatic_complexity"] = cfg_json.get("cyclomatic_complexity")
                        results["cfg"].append(cfg_json)
                    elif cfg_json:
                        cfg_json["disassembled_function_hash"] = None
                        results["cfg"].append(cfg_json)

                    # Ensure cyclomatic_complexity is set even if no cfg_json
                    if disass_json and "cyclomatic_complexity" not in disass_json:
                        disass_json["cyclomatic_complexity"] = None

                except Exception as e:
                    self.log_error(
                        "Failed to process function: ",
                        function.name,
                        function.start,
                        e,
                        "analyze_binary",
                    )

            return results

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

    def extract_mediumlevel(self, function):
        middle_level = MediumLevelAnalysis(function, self.bv, self.log)
        middle_level_result, errors = middle_level.analyze()

        for error in errors:
            self.errors.append(error)

        return middle_level_result


    def extract_lowlevel(self, function):
        low_level = LowLevelAnalysis(function, self.bv, self.log)
        disassembly_json, errors = low_level.analyze()

        for error in errors:
            self.errors.append(error)

        return disassembly_json

    def extract_cfg(self, function):
        try:
            llil = function.llil if hasattr(function, 'llil') else None
            cfg = CFGAnalysis(function, llil_function=llil).extract_function_cfg()
            return cfg
        except Exception as e:
            self.log_error(
                "Fatal error in CFG extraction",
                function.name,
                function.start,
                e,
                "extract_disassembly",
            )
            return None

    def extract_disasm(self, function):
        try:
            disass_analysis = DisassemblyAnalysis(
                self.arch, function, self.bv, self.log
            )

            # Create disassembly JSON
            disassembly_json, errors = disass_analysis.get_json()

            for error in errors:
                self.errors.append(error)

            return disassembly_json

        except Exception as e:
            self.log_error(
                "Fatal error in disassembly extraction",
                function.name,
                function.start,
                e,
                "extract_disassembly",
            )
            return None

    def extract_hlil(self, function):
        """Extract HLIL from a function."""
        try:
            # Access function.hlil directly - this will either return the HLIL or raise an exception
            # Removed hlil_if_available check as it was causing race conditions
            if function.hlil is None:
                return None

            if len(function.hlil.basic_blocks) == 0:
                return None

            # if function has one basic block, then compare the len of the instructions against minimum of our functions
            if len(function.hlil.basic_blocks) == 1:
                block = function.basic_blocks[0]
                if len(list(block.disassembly_text)) < self.MIN_FUNCTION_SIZE:
                    return None

            # Get decompiled code
            function_prototype = str(function)
            decompiled_code = str(function.hlil)
            if not decompiled_code or decompiled_code.strip() == "":
                self.log.warning(f"Empty decompilation result for {function.name}")
                return None

            callers = []
            for caller_site in function.caller_sites:
                if caller_site.hlil:
                    callers.append(str(caller_site.hlil))

            calls = []
            for call in function.call_sites:
                if call.hlil:
                    calls.append(str(call.hlil))

            analysis_score = ObfuscationScores(function.hlil)
            flattened_score = analysis_score.flattened_score()
            mba_score = analysis_score.MBA_score()

            if decompiled_code:
                # Create json object
                function_json = {
                    "decompiled_function_hash": calculate_sha256(decompiled_code),
                    "decompiled_function": decompiled_code,
                    "decompiled_function_name": function.name,
                    "decompiled_function_prototype": function_prototype,
                    "decompiled_function_address": function.start,
                    "function_type": FunctionTypeAnalysis(function)
                    .get_function_type()
                    .name,
                    "functions_caller": list(callers),
                    "functions_call": list(calls),
                    "flattened_score": flattened_score,
                    "mba_score": mba_score,
                }
                return function_json
            else:
                return None

        except Exception as e:
            self.log.warning(
                f"Failed to get HLIL for {function.name} at {function.start}: {str(e)}"
            )

        return None

    def extract(self) -> bool:
        """Extract and process all analysis results.

        Returns:
            bool: True if extraction was successful, False otherwise

        """
        self.log.info(f"Starting binary analysis on {self.filepath}")
        try:
            results = self.analyze_binary()
            if not results:
                self.log.error("Analysis failed to produce results")
                return False

            self.analysis_results = results
            self.log.info(
                f"Successfully analyzed binary: {len(results['decompiled'])} decompiled functions, "
                f"{len(results['disassembled'])} disassembled functions, "
                #f"{len(results['cfg'])} basic blocks, "
                f"{len(results['errors'])} errors"
            )
            return True

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

    def is_too_few_blocks(self, function):
        if function is None:
            return False

        if function.basic_blocks is None:
            return False

        # when binary ninja does not wait for the analysis, it creates function stubs where basic blocks array
        # is not populated yet. Therefore, we disable this heuristic.
        #if len(function.basic_blocks) == 0:
        #    return False

        # if function has one basic block, then compare the len of the instructions against minimum of our functions
        if len(function.basic_blocks) == 1:
            block = function.basic_blocks[0]
            if len(list(block.disassembly_text)) < self.MIN_FUNCTION_SIZE:
                return False

        return True

def is_lib_or_thunk(function):
    function_type = FunctionTypeAnalysis(function).get_function_type()
    return not (function_type == FunctionType.THUNK or function_type == FunctionType.EXTERNAL or function_type == FunctionType.LIBRARY)