Kai Wei

99 papers A* 8A 2B 5C 2Misc 8Journal 52Unranked 22
YearRankTypeTitle / Venue / Authors
2026 J jnl
CoRR
Yuning Wu, Ke Wang, Devin Chen, Kai Wei
2026 J jnl
Entropy
Yicheng Yu, Kai Wei, Kun Qi, Wangyu Wu
2026 J jnl
Reliab. Eng. Syst. Saf.
Haoyu Li, Kai Wei
2026 J jnl
Entropy
Yicheng Yu, Kai Wei, Hongtu Li, Kai Zhang
2025 J jnl
CoRR
Yuwen Cui, Guangjing Wang, Khanh Vu, Kai Wei, Kehan Shen, Zhengyuan Jiang, Xiao Han, Ning Wang, Zhuo Lu, Yao Liu
2025 J jnl
CoRR
Zhijie Duan, Kai Wei, Zhaoqian Xue, Jiayan Zhou, Shu Yang, Siyuan Ma, Jin Jin, Lingyao Li
2025 J jnl
IEEE Trans. Instrum. Meas.
Dinghui Gong, Zitong Xu, Xiaofei Huang, Di Gong, Chang Liu, Xing Heng, Fan Wang, Weiyi Wang, Kai Wei, Wei Quan
2025 conf
VTC2025-Spring
Kai Wei, Wei Xu, Renjie Xie
2025 J jnl
CoRR
Yiquan Wang, Yahui Ma, Yuhan Chang, Jiayao Yan, Jialin Zhang, Minnuo Cai, Kai Wei
2025 J jnl
J. Comput. Biol.
Shunqin Zhang, Wei Kong, Shuaiqun Wang, Kai Wei, Kun Liu, Gen Wen, Yaling Yu
2025 J jnl
J. Chem. Inf. Model.
Yiquan Wang, Minnuo Cai, Yuhua Dong, Yahui Ma, Kai Wei
2025 conf
CogMI
Saranya Venkatraman, Thanh Tran, Shunyan Luo, Devin Chen, Yuning Wu, Kai Wei
2025 conf
CogMI
Yuning Wu, Kai Wei
2025 A* conf
CCS
Kai Wei, Guangjing Wang
2025 J jnl
Int. J. Secur. Networks
Hu Li, Kai Wei, Xiaoqing Zhang
2025 J jnl
IEEE Trans. Veh. Technol.
Kai Wei, Renjie Xie, Wei Xu, Zhaohua Lu, Huahua Xiao
2025 J jnl
IEEE Trans. Instrum. Meas.
Saixin Zhou, Kai Wei, Xiaofei Huang, Yang rui, Xing Heng, Jie Zheng, Wei Quan
2025 J jnl
CoRR
Yaoning Yu, Ye Yu, Kai Wei, Haojing Luo, Haohan Wang
2025 J jnl
CoRR
Yaoning Yu, Kaimin Chang, Ye Yu, Kai Wei, Haojing Luo, Haohan Wang
2025 J jnl
CoRR
Zhaoqian Xue, Guanhong Liu, Kai Wei, Chong Zhang, Qingcheng Zeng, Songhua Hu, Wenyue Hua, Lizhou Fan, Yongfeng Zhang, Lingyao Li
2025 J jnl
IEEE Trans. Computational Imaging
Xiaolong Qian, Qi Jiang, Yao Gao, Shaohua Gao, Zhonghua Yi, Lei Sun, Kai Wei, Haifeng Li, Kailun Yang, Kaiwei Wang, Jian Bai
2024 J jnl
IEEE Trans. Instrum. Meas.
Zitong Xu, Kai Wei, Chang Liu, Xing Heng, Xiaofei Huang, Dinghui Gong, Fan Wang, Yueyang Zhai, Wei Quan
2024 J jnl
J. Biomed. Informatics
Jin Deng, Kai Wei, Jiana Fang, Ying Li
2024 conf
WASA (1)
Zequn Fan, Qingxian Pan, Zhaolong Gao, Peng Luan, Kai Wei, Jinru Li
2024 J jnl
Remote. Sens.
Yali Shi, Tianhe Xu, Min Li, Kai Wei, Shuai Wang, Dixing Wang
2024 J jnl
Remote. Sens.
Kai Wei, Xiaoxia Huang, Hongga Li
2024 J jnl
CoRR
Xiaolong Qian, Qi Jiang, Yao Gao, Shaohua Gao, Zhonghua Yi, Lei Sun, Kai Wei, Haifeng Li, Kailun Yang, Kaiwei Wang, Jian Bai
2023 J jnl
IEEE Trans. Instrum. Meas.
Zitong Xu, Kai Wei, Xiaofei Huang, Dinghui Gong, Weiyi Wang, Chang Liu, Xing Heng, Fan Wang, Yueyang Zhai, Wei Quan
2023 conf
EDGE
Sensen Zhang, Kai Wei, Xin Jiang
2023 B conf
TrustCom
Shu Yan, Sirui Zhang, Ailin Lv, Bo Yuan, Kai Wei
2023 Misc conf
ICASSP
Feng-Ju Chang, Thejaswi Muniyappa, Kanthashree Mysore Sathyendra, Kai Wei, Grant P. Strimel, Ross McGowan
2023 J jnl
CoRR
Feng-Ju Chang, Thejaswi Muniyappa, Kanthashree Mysore Sathyendra, Kai Wei, Grant P. Strimel, Ross McGowan
2023 Misc conf
ICASSP
Jixuan Wang, Martin Radfar, Kai Wei, Clement Chung
2023 J jnl
CoRR
Jixuan Wang, Martin Radfar, Kai Wei, Clement Chung
2023 J jnl
Sensors
Jun Xu, Zuning Huang, Liangyuan Liu, Xinghua Li, Kai Wei
2023 J jnl
IEEE Trans. Instrum. Meas.
Chang Liu, Kai Wei, Zitong Xu, Yichao Tian, Chencheng Tang, Di Gong, Xinda Song, Yueyang Zhai
2023 J jnl
J. Glob. Inf. Manag.
Boqiang Lin, Kai Wei
2023 B conf
TrustCom
Weiwei Pang, Kai Wei, Yihui Zhang, Chunyu Jiang, Yang Cheng, Qi Zhang, Bin Liu, Lifeng Zhang, Tingting Liu, Yinqian Wu
2023 B conf
TrustCom
Yang Cheng, Kai Wei, Yihui Zhang, Chunyu Jiang, Weiwei Pang, Qi Zhang, Bin Liu, Lifeng Zhang, Tingting Liu, Yinqian Wu
2023 J jnl
IEEE Trans. Instrum. Meas.
Xing Heng, Kai Wei, Tian Zhao, Zitong Xu, Qian Cao, Xiaofei Huang, Yueyang Zhai, Mao Ye, Wei Quan
2022 J jnl
CoRR
Min Yang, Cheng Cui, Xuetong Xue, Hui Ren, Kai Wei
2022 Misc conf
ICASSP
Kai Wei, Dillon Knox, Martin Radfar, Thanh Tran, Markus Müller, Grant P. Strimel, Nathan Susanj, Athanasios Mouchtaris, Maurizio Omologo
2022 J jnl
CoRR
Kai Wei, Dillon Knox, Martin Radfar, Thanh Tran, Markus Müller, Grant P. Strimel, Nathan Susanj, Athanasios Mouchtaris, Maurizio Omologo
2022 A* conf
AAAI
Thanh Tran, Kai Wei, Weitong Ruan, Ross McGowan, Nathan Susanj, Grant P. Strimel
2022 J jnl
Remote. Sens.
Min Li, Tianhe Xu, Yali Shi, Kai Wei, Xianming Fei, Dixing Wang
2022 J jnl
Medical Biol. Eng. Comput.
Kai Wei, Wei Kong, Shuaiqun Wang
2022 J jnl
Sensors
Di Zhang, Kai Wei, Yi Yao, Jiacheng Yang, Guolong Zheng, Qing Li
2022 J jnl
IEEE Commun. Lett.
Kai Wei, Jindan Xu, Wei Xu, Ning Wang, Dong Chen
2022 J jnl
CoRR
Kai Wei, Jindan Xu, Wei Xu, Ning Wang, Dong Chen
2022 conf
iThings/GreenCom/CPSCom/SmartData/Cybermatics
Rongli Gai, Kai Wei, Huatian Zhang, Xiangzhou Kong
2022 Misc conf
ICASSP
Xuandi Fu, Feng-Ju Chang, Martin Radfar, Kai Wei, Jing Liu, Grant P. Strimel, Kanthashree Mysore Sathyendra
2022 J jnl
CoRR
Xuandi Fu, Feng-Ju Chang, Martin Radfar, Kai Wei, Jing Liu, Grant P. Strimel, Kanthashree Mysore Sathyendra
2021 A conf
SDM
Chandrashekhar Lavania, Kai Wei, Rishabh K. Iyer, Jeff A. Bilmes
2021 J jnl
IEEE Access
Kai Wei, Wei Kong, Shuaiqun Wang
2021 C conf
ASRU
Kai Wei, Thanh Tran, Feng-Ju Chang, Kanthashree Mysore Sathyendra, Thejaswi Muniyappa, Jing Liu, Anirudh Raju, Ross McGowan, Nathan Susanj, Ariya Rastrow, Grant P. Strimel
2021 J jnl
CoRR
Kai Wei, Thanh Tran, Feng-Ju Chang, Kanthashree Mysore Sathyendra, Thejaswi Muniyappa, Jing Liu, Anirudh Raju, Ross McGowan, Nathan Susanj, Ariya Rastrow, Grant P. Strimel
2021 A* conf
AAAI
Jixuan Wang, Kai Wei, Martin Radfar, Weiwei Zhang, Clement Chung
2021 J jnl
J. Supercomput.
Qingchun Bai, Kai Wei, Jie Zhou, Chao Xiong, Yuanbin Wu, Xin Lin, Liang He
2021 J jnl
IEEE Trans. Instrum. Meas.
Zitong Xu, Kai Wei, Tian Zhao, Qian Cao, Ying Liu, Dong Hu, Yueyang Zhai
2021 B conf
TrustCom
Pengwei Ma, Kai Wei, Chunyu Jiang, Junyi Li, Jiafeng Tian, Siyuan Liu, Minjing Zhong
2021 B conf
TrustCom
Zhuo Wang, Kai Wei, Chunyu Jiang, Jiafeng Tian, Minjing Zhong, Yuan Liu, Yanmei Liu
2021 J jnl
Reliab. Eng. Syst. Saf.
Zhonghui Shen, Kai Wei
2020 conf
ISPA/BDCloud/SocialCom/SustainCom
Han Liu, Chunyu Jiang, Miaoqiong Wang, Kai Wei, Shu Yan
2020 conf
ISPA/BDCloud/SocialCom/SustainCom
Miaoqiong Wang, Siyuan Liu, Kai Wei, Han Liu, Chunyu Jiang, Shu Yan, Pengwei Ma
2020 J jnl
CoRR
Jixuan Wang, Kai Wei, Martin Radfar, Weiwei Zhang, Clement Chung
2020 A* conf
WWW
Kai Wei, Yu-Ru Lin, Muheng Yan
2020 J jnl
CoRR
Cheng Cui, Zhi Ye, Yangxi Li, Xinjian Li, Min Yang, Kai Wei, Bing Dai, Yanmei Zhao, Zhongji Liu, Rong Pang
2019 conf
ICCMS
Pengwei Ma, Kai Wei, Chunyu Jiang
2019 conf
AISI
Meijie Liu, Guangfu Wang, Kai Wei, Hong Gang, Hao Wang
2019 J jnl
Sensors
Yongsong Li, Zhengzhou Li, Kai Wei, Weiqi Xiong, Jiangpeng Yu, Bo Qi
2019 conf
SmartBlock
Sude Qing, Yihui Zhang, Hongnan Liu, Tao He, Baixue Yang, Kai Wei
2018 J jnl
CoRR
Rishabh K. Iyer, John T. Halloran, Kai Wei
2018 conf
KSEM (1)
Qingchun Bai, Kai Wei, Mengwei Chen, Qinmin Hu, Liang He
2018 C conf
DATA
Chengcheng Liu, Miaoqiong Wang, Pengwei Ma, Kai Wei, Chunyu Jiang, Shu Yan
2018 J jnl
CoRR
John Moore, Joel Pfeiffer, Kai Wei, Rishabh K. Iyer, Denis Charles, Ran Gilad-Bachrach, Levi Boyles, Eren Manavoglu
2018 conf
WiSNet
Francis T. Pantano, Kai Wei, Tianchi Sun, Afshin S. Daryoush
2017 J jnl
Comput. Oper. Res.
Xiangyong Li, Kai Wei, Yash P. Aneja, Peng Tian, Youzhi Cui
2017 conf
DASFAA Workshops
Kai Wei, Pingfang Tian, Jinguang Gu, Li Huang
2016 A* conf
ICML
Wenruo Bai, Rishabh K. Iyer, Kai Wei, Jeff A. Bilmes
2016 conf
ICSAI
Jintao Wang, Kai Wei, Jingyue Zhang, Lin Tong, Xuesong Bao
2016 conf
SocInfo (1)
Wen-Ting Chung, Kai Wei, Yu-Ru Lin, Xidao Wen
2015 J jnl
CoRR
Kai Wei, Rishabh K. Iyer, Shengjie Wang, Wenruo Bai, Jeff A. Bilmes
2015 conf
NIPS
Kai Wei, Rishabh K. Iyer, Shengjie Wang, Wenruo Bai, Jeff A. Bilmes
2015 A* conf
ICML
Kai Wei, Rishabh K. Iyer, Jeff A. Bilmes
2015 J jnl
计算机科学
Wenchao Fan, Xiaoyu Li, Kai Wei, Xinglin Chen
2014 J jnl
Ad Hoc Sens. Wirel. Networks
Feng Zhao, Kai Wei, Hongbin Chen
2014 A* conf
KDD
Deepak Agarwal, Souvik Ghosh, Kai Wei, Siyu You
2014 A* conf
ICML
Kai Wei, Rishabh K. Iyer, Jeff A. Bilmes
2014 Misc conf
ICASSP
Kai Wei, Yuzong Liu, Katrin Kirchhoff, Chris D. Bartels, Jeff A. Bilmes
2014 Misc conf
ICASSP
Kai Wei, Yuzong Liu, Katrin Kirchhoff, Jeff A. Bilmes
2013 conf
INCoS
Kai Wei, Juanqiong Gou, Rui Chai, Wei Dai
2013 Misc conf
ICASSP
Yuzong Liu, Kai Wei, Katrin Kirchhoff, Yisong Song, Jeff A. Bilmes
2013 conf
HLT-NAACL
Kai Wei, Yuzong Liu, Katrin Kirchhoff, Jeff A. Bilmes
2012 conf
BIFE
Daosheng Wei, Kai Wei
2010 Misc conf
ICMLC
Xinxin Liu, Shaohua Tang, Kai Wei
2009 conf
CIS (2)
Kai Wei, Shaohua Tang
2006 A conf
ICDCS
Kai Wei, Alan J. Smith, Yih-Farn Robin Chen, Binh Vo
2003 conf
ICEIS (3)
Kai Wei, Zhaohui Wu
1993 J jnl
IEEE Trans. Autom. Control.
Kai Wei
redb/extractors/hashes.py
← Index redb/extractors/hashes.py python
from dataclasses import asdict
from typing import Any
from datetime import datetime, timezone

import hashlib
import inspect
from struct import pack

from magika import Magika
from signify.fingerprinter import AuthenticodeFingerprinter
import ppdeep
import tlsh

import pefile
from elftools.elf.elffile import ELFFile
from elftools.common.exceptions import ELFError

from redb.extractors.enum import Tag
from redb.models.dataclasses import Hashes
from redb.extractors.extractor import Extractor


class HashExtractor(Extractor):

    def __init__(
        self,
        filepath,
        log,
        exporters=None,
        index_prefix=None,
        elastic_index=None,
        known_benign=False,
        known_malicious=False,
        macho=None,
    ):
        super().__init__(
            filepath, log, exporters, index_prefix, elastic_index, known_benign, known_malicious
        )
        self.hashes = None
        self.elastic_index = self.index_prefix + "-hashes"
        self.filetype = Magika().identify_bytes(self.binary).output.label
        self.pe = None
        self.elf = None
        self.macho = macho
        if self.filetype == "pebin":  # else None
            try:
                self.pe = pefile.PE(self.filepath)
            except Exception as e:
                self.log.error(f"Failed to initialize PE file object: {e}")
                self.pe = None
        elif self.filetype == "elf":
            # ELF file will be created when needed in _extract_elf_hashes
            pass

    def _extract_hashes(self):
        self.log.debug(inspect.currentframe().f_code.co_name)
        
        # Initialize hash values with None
        md5 = None
        sha1 = None
        sha256 = None
        ssdeep_hash = None
        tlsh_hash = None
        
        # Calculate basic hashes with error handling
        try:
            md5 = hashlib.md5(self.binary).hexdigest()
        except Exception as e:
            self.log.error(f"Failed to calculate MD5 hash: {e}")
            
        try:
            sha1 = hashlib.sha1(self.binary).hexdigest()
        except Exception as e:
            self.log.error(f"Failed to calculate SHA1 hash: {e}")
            
        try:
            sha256 = hashlib.sha256(self.binary).hexdigest()
        except Exception as e:
            self.log.error(f"Failed to calculate SHA256 hash: {e}")
            
        try:
            ssdeep_hash = ppdeep.hash_from_file(self.filepath)
        except Exception as e:
            self.log.error(f"Failed to calculate ssdeep hash: {e}")
            
        try:
            tlsh_hash = tlsh.hash(self.binary)
        except Exception as e:
            self.log.error(f"Failed to calculate TLSH hash: {e}")
        
        # Create Hashes object with available values
        self.hashes = Hashes(
            md5 or "",
            sha1 or "",
            sha256 or "",
            ssdeep_hash or "",
            tlsh_hash or "",
        )

        if self.filetype == "pebin" and self.pe is not None:
            self.log.debug("Computing PEBIN related hash values")
            
            # Authentihash
            try:
                with open(self.filepath, 'rb') as f:
                    fingerprinter = AuthenticodeFingerprinter(f)
                    fingerprinter.add_authenticode_hashers(hashlib.sha256)
                    self.hashes.authentihash = fingerprinter.hash()['sha256'].hex()
            except Exception as e:
                self.log.error(f"Failed to calculate Authentihash: {e}")
                self.hashes.authentihash = None
                
            # Imphash
            try:
                self.hashes.imphash = self.pe.get_imphash()
            except Exception as e:
                self.log.error(f"Failed to calculate Imphash: {e}")
                self.hashes.imphash = None
                
            # Rich header hashes
            try:
                if self.pe.parse_rich_header():
                    self.log.debug("Computing RichHeader related hash values")
                    
                    richhash = self._compute_richhash()
                    if richhash:
                        self.hashes.richhash = richhash
                        
                    richpe_hash = self._compute_richpe_hash()
                    if richpe_hash:
                        self.hashes.richpe_hash = richpe_hash
                        
                    richpv_result = self._compute_richpv_hash()
                    if richpv_result:
                        self.hashes.richpv_hash, self.hashes.richpv_hash_sorted = richpv_result
            except Exception as e:
                self.log.error(f"Failed to calculate Rich header hashes: {e}")
        elif self.filetype == "pebin" and self.pe is None:
            self.log.warning("PE file type detected but PE object initialization failed - skipping PE-specific hashes")

        elif self.filetype == "elf":
            self.log.debug("Computing ELF related hash values")
            self._extract_elf_hashes()
        elif self.filetype == "macho":
            self.log.debug("Computing Mach-O related hash values")
            self._extract_macho_hashes()
        elif self.filetype == "apk":
            self.log.debug("Computing APK related hash values")
            self._extract_apk_hashes()
        else:
            pass
        self.log.debug(f"Hashes dump: {asdict(self.hashes)}")
        return self.hashes

    def _compute_richhash(self):
        self.log.debug(inspect.currentframe().f_code.co_name)
        try:
            rich_header = self.pe.parse_rich_header()
            if not rich_header:
                return ""
            data = rich_header["clear_data"]
            return hashlib.md5(data).hexdigest().lower()
        except:
            return ""

    def _compute_richpe_hash(self):
        """
        Computes the RichPE hash given a file path or PE object.

        RichPE hash is includes RichHeader CompID and count, as well as
        fields from IMAGE_FILE_HEADER and IMAGE_OPTIONAL_HEADER

        Parameters:
        input: it can be either a file path or a PE object

        Returns:
        richpe_hash: md5 hash of the RichPE value
        None: if no Rich Header present
        """
        try:
            # Attempt to parse Rich header
            self.log.debug(inspect.currentframe().f_code.co_name)
            rich_header = self.pe.parse_rich_header()
            if rich_header is None:
                return None

            # Get list of @Comp.IDs and counts from Rich header
            # Elements in rich_fields at even indices are @Comp.IDs
            # Elements in rich_fields at odd indices are counts
            rich_fields = rich_header.get("values", None)
            if not rich_fields or len(rich_fields) % 2 != 0:
                return None

            md5 = hashlib.md5()

            # Update hash using @Comp.IDs and masked counts from Rich header
            while len(rich_fields):
                compid = rich_fields.pop(0)
                count = rich_fields.pop(0)
                mask = 2 ** (count.bit_length() // 2 + 1) - 1
                count |= mask
                md5.update(pack("<L", compid))
                md5.update(pack("<L", count))

            # Update hash using metadata from the PE header
            md5.update(pack("<L", self.pe.FILE_HEADER.Machine))
            md5.update(pack("<L", self.pe.FILE_HEADER.Characteristics))
            md5.update(pack("<L", self.pe.OPTIONAL_HEADER.Subsystem))
            md5.update(pack("<B", self.pe.OPTIONAL_HEADER.MajorLinkerVersion))
            md5.update(pack("<B", self.pe.OPTIONAL_HEADER.MinorLinkerVersion))
            md5.update(pack("<L", self.pe.OPTIONAL_HEADER.MajorOperatingSystemVersion))
            md5.update(pack("<L", self.pe.OPTIONAL_HEADER.MinorOperatingSystemVersion))
            md5.update(pack("<L", self.pe.OPTIONAL_HEADER.MajorImageVersion))
            md5.update(pack("<L", self.pe.OPTIONAL_HEADER.MinorImageVersion))
            md5.update(pack("<L", self.pe.OPTIONAL_HEADER.MajorSubsystemVersion))
            md5.update(pack("<L", self.pe.OPTIONAL_HEADER.MinorSubsystemVersion))

            return md5.hexdigest()
        except Exception as e:
            self.log.error(f"Failed to compute RichPE hash: {e}")
            return None

    def _compute_richpv_hash(self):
        """
        Compute the RichPV hash values, sorted and unsorted.
        RichPV excludes the most volatile Rich Header field from the MD5 input data,
        the Product Count (pC) field.

        Returns:
        richpv_hash_unsorted: md5 hash of the RichPV value unsorted
        richpv_hash_sorted: md5 hash of the RichPV value sorted
        None: if no Rich Header present
        """
        try:
            self.log.debug(inspect.currentframe().f_code.co_name)
            # Attempt to parse Rich header
            rich_header = self.pe.parse_rich_header()
            if rich_header is None:
                return None

            # Get list of @Comp.IDs and counts from Rich header
            # Elements in rich_fields at even indices are @Comp.IDs
            # Elements in rich_fields at odd indices are counts
            rich_fields = rich_header.get("values", None)
            if not rich_fields or len(rich_fields) % 2 != 0:
                return None

            md5 = hashlib.md5()
            md5_sorted = hashlib.md5()
            sorted_vector = []

            # Update hash using @Comp.IDs only
            for i in range(0, len(rich_fields), 2):
                compid = rich_fields[i]
                md5.update(pack("<L", compid))
                sorted_vector.append(compid)

            sorted_vector.sort()
            for compid in sorted_vector:
                md5_sorted.update(pack("<L", compid))

            return md5.hexdigest(), md5_sorted.hexdigest()
        except Exception as e:
            self.log.error(f"Failed to compute RichPV hash: {e}")
            return None

    def _extract_elf_hashes(self):
        """Extract ELF specific similarity hashes."""
        self.log.debug(inspect.currentframe().f_code.co_name)

        try:
            with open(self.filepath, 'rb') as f:
                elf = ELFFile(f)
                if not elf:
                    return

                # Generate all similarity hashes
                import_hash = self._generate_elf_import_hash(elf)
                export_hash = self._generate_elf_export_hash(elf)
                section_hash = self._generate_elf_section_hash(elf)
                symbol_hash = self._generate_elf_symbol_hash(elf)
                dynamic_hash = self._generate_elf_dynamic_hash(elf)

                # Only set hashes if they have meaningful values
                if import_hash:
                    self.hashes.import_hash = import_hash
                if export_hash:
                    self.hashes.export_hash = export_hash
                if section_hash:
                    self.hashes.section_hash = section_hash
                if symbol_hash:
                    self.hashes.symhash = symbol_hash
                if dynamic_hash:
                    self.hashes.dynamic_hash = dynamic_hash

        except Exception as e:
            self.log.error(f"Failed to extract ELF hashes: {e}")

    def _generate_elf_import_hash(self, elf) -> str:
        """Generate MD5 hash of sorted, deduplicated imported symbol names."""
        try:
            imported_symbols = set()

            # Get dynamic symbol table
            dynsym_section = elf.get_section_by_name('.dynsym')
            if dynsym_section and hasattr(dynsym_section, 'iter_symbols'):
                for symbol in dynsym_section.iter_symbols():
                    # Look for undefined symbols (imports)
                    if (symbol.entry.get('st_shndx', 0) == 'SHN_UNDEF' and
                        symbol.name and
                        symbol.entry.get('st_info', {}).get('bind') in ['STB_GLOBAL', 'STB_WEAK']):
                        imported_symbols.add(symbol.name)

            # Also check relocations for additional imports
            for section in elf.iter_sections():
                if hasattr(section, 'iter_relocations'):
                    try:
                        for relocation in section.iter_relocations():
                            if hasattr(relocation, 'symbol') and relocation.symbol and relocation.symbol.name:
                                imported_symbols.add(relocation.symbol.name)
                    except:
                        pass

            # Sort and concatenate
            sorted_imports = sorted(list(imported_symbols))

            # Return None if no imports found
            if not sorted_imports:
                return None

            imports_string = '|'.join(sorted_imports)

            # Generate MD5 hash
            return hashlib.md5(imports_string.encode('utf-8')).hexdigest()

        except Exception as e:
            self.log.error(f"Error generating ELF import hash: {e}")
            return None

    def _generate_elf_export_hash(self, elf) -> str:
        """Generate MD5 hash of sorted, deduplicated exported symbol names."""
        try:
            exported_symbols = set()

            # Check both static and dynamic symbol tables
            symbol_sections = ['.symtab', '.dynsym']

            for section_name in symbol_sections:
                section = elf.get_section_by_name(section_name)
                if not section or not hasattr(section, 'iter_symbols'):
                    continue

                for symbol in section.iter_symbols():
                    # Check if symbol is exported (defined and globally visible)
                    if (symbol.name and
                        symbol.entry.get('st_shndx', 0) != 'SHN_UNDEF' and
                        symbol.entry.get('st_info', {}).get('bind') in ['STB_GLOBAL', 'STB_WEAK'] and
                        symbol.entry.get('st_info', {}).get('type') in ['STT_FUNC', 'STT_OBJECT']):
                        exported_symbols.add(symbol.name)

            # Sort and concatenate
            sorted_exports = sorted(list(exported_symbols))

            # Return None if no exports found
            if not sorted_exports:
                return None

            exports_string = '|'.join(sorted_exports)

            # Generate MD5 hash
            return hashlib.md5(exports_string.encode('utf-8')).hexdigest()

        except Exception as e:
            self.log.error(f"Error generating ELF export hash: {e}")
            return None

    def _generate_elf_section_hash(self, elf) -> str:
        """Generate MD5 hash of section layout (names, types, flags sequence)."""
        try:
            section_info = []

            for section in elf.iter_sections():
                header = section.header
                section_name = section.name or "<unnamed>"
                section_type = header.get('sh_type', 'SHT_NULL')
                section_flags = header.get('sh_flags', 0)

                # Create a consistent representation
                section_repr = f"{section_name}:{section_type}:{section_flags}"
                section_info.append(section_repr)

            # Return None if no meaningful sections found
            if not section_info:
                return None

            # Join all section info
            sections_string = '|'.join(section_info)

            # Generate MD5 hash
            return hashlib.md5(sections_string.encode('utf-8')).hexdigest()

        except Exception as e:
            self.log.error(f"Error generating ELF section hash: {e}")
            return None

    def _generate_elf_symbol_hash(self, elf) -> str:
        """Generate MD5 hash of sorted symbol names and types."""
        try:
            symbol_info = set()

            # Check both static and dynamic symbol tables
            symbol_sections = ['.symtab', '.dynsym']

            for section_name in symbol_sections:
                section = elf.get_section_by_name(section_name)
                if not section or not hasattr(section, 'iter_symbols'):
                    continue

                for symbol in section.iter_symbols():
                    if symbol.name:
                        symbol_type = symbol.entry.get('st_info', {}).get('type', 'STT_NOTYPE')
                        symbol_bind = symbol.entry.get('st_info', {}).get('bind', 'STB_LOCAL')

                        # Create a consistent representation
                        symbol_repr = f"{symbol.name}:{symbol_type}:{symbol_bind}"
                        symbol_info.add(symbol_repr)

            # Sort and concatenate
            sorted_symbols = sorted(list(symbol_info))

            # Return None if no symbols found
            if not sorted_symbols:
                return None

            symbols_string = '|'.join(sorted_symbols)

            # Generate MD5 hash
            return hashlib.md5(symbols_string.encode('utf-8')).hexdigest()

        except Exception as e:
            self.log.error(f"Error generating ELF symbol hash: {e}")
            return None

    def _generate_elf_dynamic_hash(self, elf) -> str:
        """Generate MD5 hash of dynamic section entries (DT_* tags)."""
        try:
            dynamic_info = []

            # Get the dynamic section
            dynamic_section = elf.get_section_by_name('.dynamic')
            if not dynamic_section:
                return None

            # Extract dynamic tags and their values
            for tag in dynamic_section.iter_tags():
                dt_tag = tag.entry.d_tag

                # Create a representation based on tag type
                if dt_tag == 'DT_NEEDED':
                    dynamic_info.append(f"DT_NEEDED:{tag.needed}")
                elif dt_tag == 'DT_SONAME':
                    dynamic_info.append(f"DT_SONAME:{tag.soname}")
                elif dt_tag == 'DT_RPATH':
                    dynamic_info.append(f"DT_RPATH:{tag.rpath}")
                elif dt_tag == 'DT_RUNPATH':
                    dynamic_info.append(f"DT_RUNPATH:{tag.runpath}")
                else:
                    # For other tags, use the tag name and value
                    dt_value = tag.entry.d_val if hasattr(tag.entry, 'd_val') else 0
                    dynamic_info.append(f"{dt_tag}:{dt_value}")

            # Sort to ensure consistent ordering
            dynamic_info.sort()
            dynamics_string = '|'.join(dynamic_info)

            # Generate MD5 hash
            return hashlib.md5(dynamics_string.encode('utf-8')).hexdigest()

        except Exception as e:
            self.log.error(f"Error generating ELF dynamic hash: {e}")
            return None

    def _extract_macho_hashes(self):
        """Extract Mach-O specific similarity hashes using machofile API.

        For FAT binaries, this extracts hashes for the current file being processed
        (either the FAT container or an individual slice). The machofile library
        handles the architecture-specific extraction when an arch parameter is provided.

        For slices: We parse the slice file directly since it's a standalone Mach-O.
        For FAT container: We use the provided macho object with combined/fat hashes.
        """
        self.log.debug(inspect.currentframe().f_code.co_name)

        try:
            # If we have a pre-parsed macho object (FAT container or single-arch with passed object)
            if self.macho:
                architectures = self.macho.get_architectures()
                is_fat = len(architectures) > 1

                if is_fat:
                    # For FAT container, get combined hashes (key may be 'fat' or 'combined')
                    all_hashes = self.macho.get_similarity_hashes()
                    if all_hashes:
                        # Try 'fat' first, then 'combined' for backwards compatibility
                        similarity_hashes = all_hashes.get('fat', all_hashes.get('combined', {}))
                    else:
                        similarity_hashes = {}
                else:
                    # Single-arch with pre-parsed object
                    similarity_hashes = self.macho.get_similarity_hashes(arch=architectures[0]) if architectures else {}
            else:
                # No pre-parsed object - parse the file (slice case)
                import machofile
                macho = machofile.UniversalMachO(self.filepath)
                macho.parse()

                architectures = macho.get_architectures()
                if architectures:
                    # For a slice, there's only one architecture
                    similarity_hashes = macho.get_similarity_hashes(arch=architectures[0]) or {}
                else:
                    similarity_hashes = {}

            # Set the hash values on the Hashes object
            if similarity_hashes:
                if similarity_hashes.get('dylib_hash'):
                    self.hashes.macho_dylib_hash = similarity_hashes['dylib_hash']
                if similarity_hashes.get('import_hash'):
                    self.hashes.macho_import_hash = similarity_hashes['import_hash']
                if similarity_hashes.get('export_hash'):
                    self.hashes.macho_export_hash = similarity_hashes['export_hash']
                if similarity_hashes.get('entitlement_hash'):
                    self.hashes.macho_entitlement_hash = similarity_hashes['entitlement_hash']
                if similarity_hashes.get('symhash'):
                    self.hashes.macho_symhash = similarity_hashes['symhash']

        except Exception as e:
            self.log.error(f"Failed to extract Mach-O hashes: {e}")

    def _extract_apk_hashes(self):
        """Extract APK specific similarity hashes (permhash)."""
        self.log.debug(inspect.currentframe().f_code.co_name)
        try:
            from permhash.functions import permhash_apk
            ph = permhash_apk(self.filepath)
            if ph:
                self.hashes.permhash = ph
        except Exception as e:
            self.log.error(f"Failed to extract APK hashes: {e}")

    def extract(self):
        try:
            self.log.debug(inspect.currentframe().f_code.co_name)
            self._extract_hashes()
            return self.hashes
        except Exception as e:
            self.log.error(f"Error extracting hashes: {e}")
            return None

    def prepare_export_data(self, exporter_type: str) -> Any:
        if exporter_type == "ElasticsearchExporter":
            return self.hashes
        elif exporter_type == "ClickHouseExporter":
            # Safely get hash values with fallbacks for None values
            sha256 = getattr(self.hashes, 'sha256', None) or ""
            md5 = getattr(self.hashes, 'md5', None) or ""
            sha1 = getattr(self.hashes, 'sha1', None) or ""
            ssdeep_hash = getattr(self.hashes, 'ssdeep_hash', None) or ""
            tlsh_hash = getattr(self.hashes, 'tlsh_hash', None) or ""
            authentihash = getattr(self.hashes, 'authentihash', None)
            imphash = getattr(self.hashes, 'imphash', None)
            impfuzzy = getattr(self.hashes, 'impfuzzy', None)
            typerefhash = getattr(self.hashes, 'typerefhash', None)
            richhash = getattr(self.hashes, 'richhash', None)
            richpe_hash = getattr(self.hashes, 'richpe_hash', None)
            richpv_hash = getattr(self.hashes, 'richpv_hash', None)
            richpv_hash_sorted = getattr(self.hashes, 'richpv_hash_sorted', None)
            # ELF hashes
            import_hash = getattr(self.hashes, 'import_hash', None)
            export_hash = getattr(self.hashes, 'export_hash', None)
            section_hash = getattr(self.hashes, 'section_hash', None)
            symbol_hash = getattr(self.hashes, 'symhash', None)
            dynamic_hash = getattr(self.hashes, 'dynamic_hash', None)
            # Mach-O hashes
            macho_dylib_hash = getattr(self.hashes, 'macho_dylib_hash', None)
            macho_import_hash = getattr(self.hashes, 'macho_import_hash', None)
            macho_export_hash = getattr(self.hashes, 'macho_export_hash', None)
            macho_entitlement_hash = getattr(self.hashes, 'macho_entitlement_hash', None)
            macho_symhash = getattr(self.hashes, 'macho_symhash', None)
            # APK hashes
            permhash = getattr(self.hashes, 'permhash', None)

            data = [[
                sha256,
                md5,
                sha1,
                ssdeep_hash,
                tlsh_hash,
                authentihash,
                imphash,
                impfuzzy,
                typerefhash,
                richhash,
                richpe_hash,
                richpv_hash,
                richpv_hash_sorted,
                import_hash,
                export_hash,
                section_hash,
                symbol_hash,
                dynamic_hash,
                macho_dylib_hash,
                macho_import_hash,
                macho_export_hash,
                macho_entitlement_hash,
                macho_symhash,
                permhash,
                datetime.now(timezone.utc)
            ]]

            column_names = [
                'sha256', 'md5', 'sha1', 'ssdeep_hash', 'tlsh_hash',
                'authentihash', 'imphash', 'impfuzzy', 'typerefhash',
                'richhash', 'richpe_hash', 'richpv_hash', 'richpv_hash_sorted',
                'import_hash', 'export_hash', 'section_hash', 'symbol_hash', 'dynamic_hash',
                'macho_dylib_hash', 'macho_import_hash', 'macho_export_hash',
                'macho_entitlement_hash', 'macho_symhash',
                'permhash',
                'analysis_date'
            ]

            column_type_names = [
                # sha256, md5, sha1
                'String', 'String', 'String',
                # ssdeep_hash, tlsh_hash
                'Nullable(String)', 'Nullable(String)',
                # authentihash, imphash, impfuzzy, typerefhash
                'Nullable(String)', 'Nullable(String)',
                'Nullable(String)', 'Nullable(String)',
                # richhash, richpe_hash, richpv_hash, richpv_hash_sorted
                'Nullable(String)', 'Nullable(String)',
                'Nullable(String)', 'Nullable(String)',
                # import_hash, export_hash, section_hash, symbol_hash, dynamic_hash
                'Nullable(FixedString(32))', 'Nullable(FixedString(32))',
                'Nullable(FixedString(32))', 'Nullable(FixedString(32))',
                'Nullable(FixedString(32))',
                # macho_dylib_hash, macho_import_hash, macho_export_hash, macho_entitlement_hash, macho_symhash
                'Nullable(FixedString(32))', 'Nullable(FixedString(32))',
                'Nullable(FixedString(32))', 'Nullable(FixedString(32))',
                'Nullable(FixedString(32))',
                # permhash (APK)
                'Nullable(FixedString(64))',
                # analysis_date
                'DateTime64(3, \'UTC\')'
            ]

            return (data, column_names, column_type_names)

    def get_clickhouse_table(self) -> str:
        return "redb_hashes"

    def tag(self):
        return Tag.HASH.value