Vadim Ermolayev

83 papers A 1B 1Misc 25Journal 7Unranked 35
YearRankTypeTitle / Venue / Authors
2023 Misc ed.
ICTERI
Grigoris Antoniou, Vadim Ermolayev, Vitaliy Kobets, Vira Liubchenko, Heinrich C. Mayr, Aleksander Spivakovsky, Vitaliy Yakovyna, Grygoriy Zholtkevych
2022 ed.
ICTERI (Revised Selected Papers)
Vadim Ermolayev, David Esteban, Vitaliy Yakovyna, Heinrich C. Mayr, Grygoriy Zholtkevych, Mykola Nikitchenko, Aleksander Spivakovsky
2022 ed.
CoSinE@ICTERI
Vadim Ermolayev, Arnold E. Kiv, Serhiy O. Semerikov, Vladimir N. Soloviev, Andrii M. Striuk
2022 book
Victoria Kosa, Vadim Ermolayev
2021 conf
CoSinE@ICTERI
Vadim Ermolayev, Arnold E. Kiv, Serhiy O. Semerikov, Vladimir N. Soloviev, Andrii M. Striuk
2021 ed.
ICTERI (Revised Selected Papers)
Andreas Bollin, Vadim Ermolayev, Heinrich C. Mayr, Mykola Nikitchenko, Aleksander Spivakovsky, Mykola V. Tkachuk, Vitaliy Yakovyna, Grygoriy Zholtkevych
2021 Misc ed.
ICTERI
Vadim Ermolayev, David Esteban, Heinrich C. Mayr, Mykola Nikitchenko, Sergiy Bogomolov, Grygoriy Zholtkevych, Vitaliy Yakovyna, Aleksander Spivakovsky
2020 ed.
ICTERI (Revised Selected Papers)
Vadim Ermolayev, Frédéric Mallet, Vitaliy Yakovyna, Heinrich C. Mayr, Aleksander Spivakovsky
2019 ed.
ICTERI (Revised Selected Papers)
Vadim Ermolayev, Mari Carmen Suárez-Figueroa, Vitaliy Yakovyna, Heinrich C. Mayr, Mykola S. Nikitchenko, Aleksander Spivakovsky
2019 conf
ICTERI (Revised Selected Papers)
Victoria Kosa, David Chaves-Fraga, Hennadii Dobrovolskyi, Vadim Ermolayev
2019 Misc conf
ICTERI
Victoria Kosa, David Chaves-Fraga, Hennadii Dobrovolskyi, Egor Fedorenko, Vadim Ermolayev
2019 Misc ed.
ICTERI
Vadim Ermolayev, Frédéric Mallet, Vitaliy Yakovyna, Heinrich C. Mayr, Aleksander Spivakovsky
2019 ed.
ICTERI Workshops
Vadim Ermolayev, Frédéric Mallet, Vitaliy Yakovyna, Vyacheslav S. Kharchenko, Vitaliy Kobets, Artur Kornilowicz, Hennadiy Kravtsov, Mykola S. Nikitchenko, Serhiy Semerikov, Aleksander Spivakovsky
2018 conf
ICTERI Workshops
Vadim Ermolayev, Mari Carmen Suárez-Figueroa, Oleksii Molchanovskyi
2018 conf
PhD@ICTERI
Svitlana Moiseyenko, Vadim Ermolayev
2018 conf
PhD@ICTERI
Alexander Vasileyko, Vadim Ermolayev
2018 ed.
ICTERI (Revised Selected Papers)
Nick Bassiliades, Vadim Ermolayev, Hans-Georg Fill, Vitaliy Yakovyna, Heinrich C. Mayr, Mykola S. Nikitchenko, Grygoriy Zholtkevych, Aleksander Spivakovsky
2018 J jnl
Enterp. Model. Inf. Syst. Archit.
Vadim Ermolayev
2018 Misc ed.
ICTERI
Vadim Ermolayev, Mari Carmen Suárez-Figueroa, Agnieszka Lawrynowicz, Raúl Palma, Vitaliy Yakovyna, Heinrich C. Mayr, Mykola S. Nikitchenko, Aleksander Spivakovsky
2018 Misc conf
ICTERI
Alyona Chugunenko, Victoria Kosa, Rodion Popov, David Chaves-Fraga, Vadim Ermolayev
2017 conf
ICTERI (Revised Selected Papers)
Victoria Kosa, David Chaves-Fraga, Dmitriy Naumenko, Eugene Yuschenko, Carlos Badenes-Olmedo, Vadim Ermolayev, Aliaksandr Birukou
2017 Misc ed.
ICTERI (Revised Selected Papers)
Athula Ginige, Heinrich C. Mayr, Dimitris Plexousakis, Vadim Ermolayev, Mykola S. Nikitchenko, Grygoriy Zholtkevych, Aleksander Spivakovskiy
2017 J jnl
Trans. Comput. Collect. Intell.
Michael Cochez, Vagan Y. Terziyan, Vadim Ermolayev
2017 Misc ed.
ICTERI
Vadim Ermolayev, Nick Bassiliades, Hans-Georg Fill, Vitaliy Yakovyna, Heinrich C. Mayr, Vyacheslav S. Kharchenko, Vladimir S. Peschanenko, Mariya Shyshkina, Mykola S. Nikitchenko, Aleksander Spivakovsky
2017 ed.
ICTERI Workshops
Vadim Ermolayev, Mari Carmen Suárez-Figueroa, Vitaliy Yakovyna, Vyacheslav S. Kharchenko, Vitaliy Kobets, Hennadiy Kravtsov, Vladimir S. Peschanenko, Yaroslav Prytula, Mykola S. Nikitchenko, Aleksander Spivakovsky
2016 Misc ed.
ICTERI
Vadim Ermolayev, Aleksander Spivakovsky, Mykola S. Nikitchenko, Athula Ginige, Heinrich C. Mayr, Dimitris Plexousakis, Grygoriy Zholtkevych, Oleksandr Burov, Vyacheslav S. Kharchenko, Vitaliy Kobets
2015 conf
International KEYSTONE Conference
Michael Cochez, Vagan Y. Terziyan, Vadim Ermolayev
2015 J jnl
Int. J. Artif. Intell. Tools
Rajendra Akerkar, Vadim Ermolayev, Nick Bassiliades
2015 Misc ed.
ICTERI
Sotiris Batsakis, Heinrich C. Mayr, Vitaliy Yakovyna, Mykola S. Nikitchenko, Grygoriy Zholtkevych, Vyacheslav S. Kharchenko, Hennadiy Kravtsov, Vitaliy Kobets, Vladimir S. Peschanenko, Vadim Ermolayev, Yuriy Bobalo, Aleksander Spivakovsky
2015 J jnl
Comput. Sci. J. Moldova
Vadim Ermolayev
2014 ed.
WIMS
Rajendra Akerkar, Nick Bassiliades, John Davies, Vadim Ermolayev
2014 Misc ed.
ICTERI
Vadim Ermolayev, Heinrich C. Mayr, Mykola S. Nikitchenko, Aleksander Spivakovsky, Grygoriy Zholtkevych
2014 J jnl
Int. J. Comput. Sci. Appl.
Vadim Ermolayev, Sotiris Batsakis, Natalya Keberle, Olga Tatarintseva, Grigoris Antoniou
2013 Misc conf
ICTERI
Maxim Davidovsky, Vadim Ermolayev, Vyacheslav Tolok
2013 conf
ICTERI (Revised Selected Papers)
Maxim Davidovsky, Vadim Ermolayev, Vyacheslav Tolok
2013 Misc conf
ICTERI
Eugene Alferov, Vadim Ermolayev
2013 ed.
ICTERI (Revised Selected Papers)
Vadim Ermolayev, Heinrich C. Mayr, Mykola S. Nikitchenko, Aleksander Spivakovsky, Grygoriy Zholtkevych
2013 ed.
ICTERI (Revised Selected Papers)
Vadim Ermolayev, Heinrich C. Mayr, Mykola S. Nikitchenko, Aleksander Spivakovsky, Grygoriy Zholtkevych
2013 Misc conf
ICTERI
Vadim Ermolayev, Maxim Davidovsky
2013 Misc ed.
ICTERI
Vadim Ermolayev, Heinrich C. Mayr, Mykola S. Nikitchenko, Aleksander Spivakovsky, Grygoriy Zholtkevych, Mikhail Zavileysky, Hennadiy Kravtsov, Vitaliy Kobets, Vladimir S. Peschanenko
2013 conf
ICTERI (Revised Selected Papers)
Olga Tatarintseva, Vadim Ermolayev, Brita Keller, Wolf-Ekkehard Matzke
2013 Misc conf
ICTERI
Olga Tatarintseva, Vadim Ermolayev
2012 conf
ICAART (2)
Maxim Davidovsky, Vadim Ermolayev, Vyacheslav Tolok
2012 conf
UNISCON
Maxim Davidovsky, Vadim Ermolayev, Vyacheslav Tolok
2012 conf
WIMS
Vadim Ermolayev, Maxim Davidovsky
2012 Misc conf
ICTERI
Maxim Davidovsky, Vadim Ermolayev, Vyacheslav Tolok
2012 conf
ICTERI (Revised Selected Papers)
Vadim Ermolayev, Natalya Keberle, Sergey Borue
2012 Misc conf
ICTERI
Vadim Ermolayev, Natalya Keberle, Sergey Borue
2012 Misc conf
ICTERI
Olga Tatarintseva, Yuriy Borue, Vadim Ermolayev
2012 Misc ed.
ICTERI
Vadim Ermolayev, Heinrich C. Mayr, Mykola S. Nikitchenko, Aleksander Spivakovsky, Grygoriy Zholtkevych, Mikhail Zavileysky, Vitaliy Kobets
2012 conf
UNISCON
Olga Tatarintseva, Yuriy Borue, Vadim Ermolayev
2011 ch.
Context and Semantics for Knowledge Management
Vadim Ermolayev, Frank Dengler, Carolina Fortuna, Tadej Stajner, Tom Bösser, Elke-Maria Melchior
2011 J jnl
Int. J. Artif. Intell. Tools
Maxim Davidovsky, Vadim Ermolayev, Vyacheslav Tolok
2011 Misc conf
ICTERI
Olga Tatarintseva, Vadim Ermolayev, Anna Fensel
2011 Misc ed.
ICTERI
Vadim Ermolayev, Heinrich C. Mayr, Mykola S. Nikitchenko, Aleksander Spivakovsky, Mikhail Zavileysky, Grygoriy Zholtkevych
2010 conf
CIAO@EKAW
Vadim Ermolayev, Carlos Ruiz, Marcel Tilly, Eyck Jentzsch, José Manuél Gómez-Pérez, Wolf-Ekkehard Matzke
2010 Misc conf
IDC
Vadim Ermolayev, Frank Dengler, Eyck Jentzsch, Wolf-Ekkehard Matzke
2010 Misc conf
IDC
Maxim Davidovsky, Vadim Ermolayev, Wolf-Ekkehard Matzke, Vyacheslav Tolok
2010 ed.
CIAO@EKAW
Vadim Ermolayev, José Manuél Gómez-Pérez, Peter Haase, Paul Warren
2010 conf
CIAO@EKAW
Vadim Ermolayev, Anton Copylov, Nataliya Keberle, Eyck Jentzsch, Wolf-Ekkehard Matzke
2009 conf
UNISCON
Vadim Ermolayev, Natalya Keberle, Eyck Jentzsch, Richard Sohnius, Wolf-Ekkehard Matzke
2008 B conf
COMPSAC
Vadim Ermolayev, Natalya Keberle, Wolf-Ekkehard Matzke
2008 A conf
ER
Vadim Ermolayev, Natalya Keberle, Wolf-Ekkehard Matzke
2008 conf
UNISCON
Vadim Ermolayev, Wolf-Ekkehard Matzke, Richard Sohnius
2008 conf
UNISCON
Vadim Ermolayev, Natalya Keberle, Wolf-Ekkehard Matzke, Richard Sohnius
2007 conf
ICEIS (2)
Richard Sohnius, Eyck Jentzsch, Wolf-Ekkehard Matzke, Vadim Ermolayev
2007 conf
ISTA
Natalya Keberle, Vadim Ermolayev, Wolf-Ekkehard Matzke
2007 conf
HoloMAS
Vadim Ermolayev, Wolf-Ekkehard Matzke
2007 conf
HoloMAS
Vadim Ermolayev, Wolf-Ekkehard Matzke
2006 conf
ISTA
Vadim Ermolayev, Natalya Keberle
2006 conf
CE
Richard Sohnius, Vadim Ermolayev, Eyck Jentzsch, Natalya Keberle, Wolf-Ekkehard Matzke, Vladimir Samoilov
2006 conf
IAT
Vladimir Gorodetsky, Oleg Karsaev, Victor Konushy, Wolf-Ekkehard Matzke, Eyck Jentzsch, Vadim Ermolayev
2005 Misc conf
ISWC
Vadim Ermolayev, Natalya Keberle, Wolf-Ekkehard Matzke, Vladimir Vladimirov
2005 conf
CEEMAS
Vladimir Gorodetsky, Vadim Ermolayev, Wolf-Ekkehard Matzke, Eyck Jentzsch, Oleg Karsaev, Natalya Keberle, Vladimir Samoilov
2005 conf
AOIS
Vadim Ermolayev, Eyck Jentzsch, Oleg Karsayev, Natalya Keberle, Wolf-Ekkehard Matzke, Vladimir Samoilov, Richard Sohnius
2005 conf
ER (Workshops)
Vadim Ermolayev, Eyck Jentzsch, Oleg Karsaev, Natalya Keberle, Wolf-Ekkehard Matzke, Vladimir Samoilov
2005 conf
ER (Workshops)
Vadim Ermolayev, Vladimir I. Gorodetski, Eyck Jentzsch, Wolf-Ekkehard Matzke
2004 conf
ISTA
Vadim Ermolayev, Natalya Keberle, Vladimir Vladimirov
2004 J jnl
Int. J. Web Serv. Res.
Vadim Ermolayev, Natalya Keberle, Sergey Plaksin, Oleksandr Kononenko, Vagan Y. Terziyan
2003 conf
ISTA
Vadim Ermolayev, Natalya Keberle, Sergey Plaksin, Vladimir Vladimirov
2003 conf
ICWS-Europe
Vadim Ermolayev, Natalya Keberle, Sergey Plaksin
2001 conf
ISTA
Natalya Keberle, Vadim Ermolayev
2001 conf
ER (Workshops)
Vadim Ermolayev, Natalya Keberle, Vyachyslav Tolok
redb/extractors/pe_extractors/pe_features.py
← Index redb/extractors/pe_extractors/pe_features.py python
import base64
import inspect
import json
from pprint import pprint
import re

import pefile

from redb.ext.rich_header import get_rich_idVersion
from redb.extractors.enum import Tag
from redb.extractors.pe_extractor import PEExtractor
from redb.extractors.pe_extractors.pe_dotnet import PEDotNetExtractor
from redb.models.dataclasses import PE
from datetime import datetime, timezone
from typing import Any


class PEFeaturesExtractor(PEExtractor):

    def __init__(
        self,
        filepath,
        log,
        exporters=None,
        index_prefix=None,
        elastic_index=None,
        known_benign=False,
        known_malicious=False,
        pe=None,
    ):
        super().__init__(
            filepath,
            log,
            exporters,
            index_prefix,
            elastic_index,
            known_benign,
            known_malicious,
            pe,
        )
        self.pe_features = None
        self.elastic_index = self.index_prefix + "-pe_features"
        self.log.debug(inspect.currentframe().f_code.co_name)

    def _extract_type(self):
        self.log.debug(inspect.currentframe().f_code.co_name)
        if self.pe.is_dll():
            return "DLL"
        elif self.pe.is_driver():
            return "DRIVER"
        elif self.pe.is_exe():
            return "EXE"

    def _extract_architecture(self):
        self.log.debug(inspect.currentframe().f_code.co_name)
        mt = {"0x14c": "x86", "0x0200": "Itanium", "0x8664": "x64"}
        machine_value = self.pe.FILE_HEADER.Machine
        if isinstance(machine_value, int):
            return mt.get(str(hex(machine_value)), "")
        return str(machine_value) + " => Not x86/64 or Itanium"

    def _extract_rich_header(self):
        """Extract the Rich header from the PE file

        to decode it back from base64, use:
        rh_tmp = json.loads(rh_b64_string)
        rh_decoded = {}
        for k in rh_tmp:
            if isinstance(rh_tmp[k], str):
                rh_decoded[k] = base64.b64decode(rh_tmp[k])
            else:
                rh_decoded[k] = rh_tmp[k]
        """
        self.log.debug(inspect.currentframe().f_code.co_name)
        rh = self.pe.parse_rich_header()
        rich_header_b64 = {}
        rich_header_infos = []
        if rh:
            for key in rh:
                if isinstance(rh[key], bytes):
                    rich_header_b64[key] = base64.b64encode(rh[key]).decode("utf-8")
                else:
                    rich_header_b64[key] = rh[key]
            rich_header_infos = self._get_rich_header_infos(rh)
        return json.dumps(rich_header_b64) if rich_header_b64 else None, "\n".join(
            item for item in rich_header_infos
        ) if rich_header_infos else None

    def _get_rich_header_infos(self, rh):
        """Parse Rich header information from the PE Rich Header dump"""
        self.log.debug(inspect.currentframe().f_code.co_name)

        rich_header_infos = []
        try:
            # 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
            # example:                  'values': [8681481,
            #                             1,
            #                             9795593,
            #                             1]
            rich_fields = rh.get("values", [])
            if len(rich_fields) % 2 != 0:
                self.log.info(f"rich header extraction stopped for {self.hash.sha256}")
                return None

            comp_id = None
            for i in rich_fields:
                if rich_fields.index(i) % 2 == 0:
                    # even -> save value
                    comp_id = get_rich_idVersion(i)
                else:
                    # odd -> add to list
                    if comp_id:
                        rich_header_infos.append(f"{comp_id} count={i}")
                        comp_id = None
        except Exception as e:
            self.log.error(
                f"Extract rich header error {self.hash.sha256} Exception: {e}"
            )
        return rich_header_infos

    def _extract_version_info(self):
        """Extract the VS_VERSIONINFO field in a PE file

        Returns:
        vsinfo: a list of "key:value" strings from VS_INFORMATION content.
               None if no VS_INFORMATION content is present.
        """
        self.log.debug(inspect.currentframe().f_code.co_name)
        version_info = []
        try:
            if hasattr(self.pe, "VS_VERSIONINFO") and hasattr(self.pe, "FileInfo"):
                for finfo in self.pe.FileInfo:
                    for entry in finfo:
                        if hasattr(entry, "StringTable"):
                            for st_entry in entry.StringTable:
                                for key, str_entry in list(st_entry.entries.items()):
                                    version_info.append(f"{key.decode()}:{str_entry.decode()}")
        except Exception as e:
            self.log.error(
                f"Extract VersionInfo error {self.hash.sha256}  Exception: {e}"
            )
        return version_info if version_info else None

    def _extract_exports(self):
        """
        Returns:
        export_library_name: a str representing the name of the export library.
                            An empty string if no export library is present.
        exp_symbols_list: a list of function names exported as per DIRECTORY_ENTRY_EXPORT.symbols
                            An empty list if no export library is present.
        export_timestamp: a timestamp representing the time of the export.
        """
        self.log.debug(inspect.currentframe().f_code.co_name)
        export_symbols_list = []
        export_library_name = ""
        export_library_name_raw = ""
        export_timestamp = None
        try:
            if hasattr(self.pe, "DIRECTORY_ENTRY_EXPORT"):
                export_timestamp = self.pe.DIRECTORY_ENTRY_EXPORT.struct.TimeDateStamp
                export_directory = self.pe.DIRECTORY_ENTRY_EXPORT
                export_library_name_rva = export_directory.struct.Name
                try:
                    export_library_name = self.remove_non_utf8(
                        self.pe.get_string_at_rva(export_library_name_rva)
                    ).decode()
                    export_library_name_raw = self.pe.get_string_at_rva(
                        export_library_name_rva
                    ).__str__()
                except Exception as e:
                    self.log.error(
                        f"Error while getting export library name {self.pe.get_string_at_rva(export_library_name_rva)} for file: {self.hash.sha256} {e}"
                    )
                    export_library_name = "REDB_ERROR"
                if hasattr(self.pe.DIRECTORY_ENTRY_EXPORT, "symbols"):
                    for exp in self.pe.DIRECTORY_ENTRY_EXPORT.symbols:
                        export_symbols_list.append(
                            exp.name.decode() if exp.name else None
                        )
        except Exception as e:
            self.log.error(f"Extract exports error {self.hash.sha256} Exception: {e}")

        return (
            export_library_name if export_library_name else None,
            export_library_name_raw if export_library_name_raw else None,
            export_symbols_list if export_symbols_list else None,
            export_timestamp if export_timestamp else None,
        )

    def _extract_dbg_info(self):
        """Extract debug information from PE if present

        At the moment this function parses only the DEBUG_ENTRY Structure where Type
        field == 1, i.e. IMAGE_DEBUG_TYPE_CODEVIEW, where the pdb is eventually present.

        Returns:
        debug_entry: the dump of pe.DIRECTORY_ENTRY_DEBUG.struct only for the type 1
                    "IMAGE_DEBUG_TYPE_CODEVIEW". "None" if not present.
        debug_time: a int representing the epoch timestamp as present in the
                    DIRECTORY_ENTRY_DEBUG. "None" if not present.
        pdb_info: a str with the pdb path. "None" if not present.
        """
        self.log.debug(inspect.currentframe().f_code.co_name)
        dbg_timestamp = None
        dbg_timestamp_utc = ""
        dbg_pdb_info = ""
        dbg_pdb_info_raw = ""
        dbg_struct = ""
        try:
            if hasattr(self.pe, "DIRECTORY_ENTRY_DEBUG"):
                for debug_entry in self.pe.DIRECTORY_ENTRY_DEBUG:
                    if (
                        debug_entry.struct.Type
                        == pefile.DEBUG_TYPE["IMAGE_DEBUG_TYPE_CODEVIEW"]
                    ):
                        dbg_timestamp = debug_entry.struct.TimeDateStamp
                        dbg_timestamp_utc = datetime.fromtimestamp(
                            dbg_timestamp, timezone.utc
                        ).strftime("%Y-%m-%d %H:%M:%S")
                        # dbg_struct = base64.b64encode(debug_entry.entry).decode()
                        dbg_struct = debug_entry.entry.__str__()
                        if hasattr(debug_entry.entry, "PdbFileName"):
                            dbg_pdb_info = self.remove_non_utf8(
                                debug_entry.entry.PdbFileName.rstrip(b"\x00")
                            ).decode()
                            dbg_pdb_info_raw = debug_entry.entry.PdbFileName.rstrip(
                                b"\x00"
                            ).__str__()
                            # if dbg_pdb_info:
                            #     dbg_pdb_info = dbg_pdb_info.rstrip(b"\x00")
                            #     dbg_pdb_info = dbg_pdb_info.decode()

        except Exception as e:
            self.log.error(f"Extract DBG info error {self.hash.sha256} Exception: {e}")
        return (
            dbg_timestamp if dbg_timestamp else None,
            dbg_timestamp_utc if dbg_timestamp_utc else None,
            dbg_pdb_info if dbg_pdb_info else None,
            dbg_pdb_info_raw if dbg_pdb_info_raw else None,
            dbg_struct if dbg_struct else None,
        )

    def _extract_tls_info(self):
        """Check for the presence of Thread Local Storage and related extract
        callback addresses.

        Taken from the original version of PEScanner, as the python3 porting
        "ext_pescanner" does not have it.

        Returns:
        pe.DIRECTORY_ENTRY_TLS.struct: a pefile.Structure type containing the dump
                    of the TLS structure and content. "None" is no TLS is present.
        callbacks: a list containing the TLS callbacks addresse. Empty list if no
                    address is found, "None" is no TLS is present.
        """
        self.log.debug(inspect.currentframe().f_code.co_name)
        callbacks = []
        tls_dir_struct = ""
        try:
            if (
                hasattr(self.pe, "DIRECTORY_ENTRY_TLS")
                and self.pe.DIRECTORY_ENTRY_TLS
                and self.pe.DIRECTORY_ENTRY_TLS.struct
                and self.pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks
            ):
                # tls_dir_struct = base64.b64encode(self.pe.DIRECTORY_ENTRY_TLS.struct).decode()
                tls_dir_struct = self.pe.DIRECTORY_ENTRY_TLS.struct.__str__()
                callback_array_rva = (
                    self.pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks
                    - self.pe.OPTIONAL_HEADER.ImageBase
                )
                # Originally it was while True
                # todo while can't be used cause risky
                # what maximum range makes sense to add here?
                for idx in range(10000):
                    func = self.pe.get_dword_from_data(
                        self.pe.get_data(callback_array_rva + 4 * idx, 4), 0
                    )
                    if func == 0:
                        break
                    callbacks.append(func)
        except Exception as e:
            self.log.error(f"Extract TLS error {self.hash.sha256} Exception: {e}")
        return callbacks if callbacks else None, tls_dir_struct if tls_dir_struct else None

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

    def extract(self):
        try:
            self.log.debug(inspect.currentframe().f_code.co_name)
            (
                export_library_name,
                export_library_name_raw,
                export_symbols_list,
                export_timestamp,
            ) = self._extract_exports()
            (
                dbg_timestamp,
                dbg_timestamp_utc,
                dbg_pdb_info,
                dbg_pdb_info_raw,
                dbg_struct,
            ) = self._extract_dbg_info()
            tls_callbacks, tls_struct = self._extract_tls_info()
            rich_header_dump, rich_header_parsed = self._extract_rich_header()

            is_dotnet = self._check_dotnet()

            number_of_resources = 0
            number_of_imports = 0
            if hasattr(self.pe, "DIRECTORY_ENTRY_RESOURCE"):
                number_of_resources = len(self.pe.DIRECTORY_ENTRY_RESOURCE.entries)
            if hasattr(self.pe, "DIRECTORY_ENTRY_IMPORT"):
                number_of_imports = len(self.pe.DIRECTORY_ENTRY_IMPORT)

            self.pe_features = PE(
                type=self._extract_type(),
                magic=hex(self.pe.OPTIONAL_HEADER.Magic),
                entry_point=hex(self.pe.OPTIONAL_HEADER.AddressOfEntryPoint),
                major_linker_version=self.pe.OPTIONAL_HEADER.MajorLinkerVersion,
                minor_linker_version=self.pe.OPTIONAL_HEADER.MinorLinkerVersion,
                target_machine=self.pe.FILE_HEADER.Machine,
                architecture=self._extract_architecture(),
                compilation_time=self.pe.FILE_HEADER.TimeDateStamp,
                compilation_time_utc=datetime.fromtimestamp(
                    self.pe.FILE_HEADER.TimeDateStamp, timezone.utc
                ).strftime("%Y-%m-%d %H:%M:%S"),
                rich_header_dump=rich_header_dump,
                rich_header_parsed=rich_header_parsed,
                dos_header=self.pe.DOS_HEADER.__str__(),
                nt_header=self.pe.NT_HEADERS.__str__(),
                optional_header=self.pe.OPTIONAL_HEADER.__str__(),
                file_header=self.pe.FILE_HEADER.__str__(),
                version_info=self._extract_version_info(),
                export_library_name=export_library_name,
                export_library_name_raw=export_library_name_raw,
                export_symbols_list=export_symbols_list,
                export_timestamp=export_timestamp,
                is_dotnet=is_dotnet,
                dbg_timestamp=dbg_timestamp,
                dbg_timestamp_utc=dbg_timestamp_utc,
                dbg_pdb_info=dbg_pdb_info,
                dbg_pdb_info_raw=dbg_pdb_info_raw,
                dbg_struct=dbg_struct,
                tls_callbacks=tls_callbacks,
                tls_struct=tls_struct,
                is_signed=self._is_signed(),
                has_overlay=self._has_overlay(),
                number_of_sections=len(self.pe.sections),
                number_of_imports=number_of_imports,
                number_of_exports=(
                    len(export_symbols_list) if export_symbols_list else 0
                ),
                number_of_resources=number_of_resources,
            )
            return self.pe_features
        except Exception as e:
            self.log.error(f"Error extracting PE features {self.hash.sha256}: {e}")
            return None

    def prepare_export_data(self, exporter_type: str) -> Any:
        self.log.debug(inspect.currentframe().f_code.co_name)
        if exporter_type == "ElasticsearchExporter":
            return self.pe_features
        elif exporter_type == "ClickHouseExporter":
            try:
                pe_dump = self.pe.dump_dict()   
                # Convert PE headers directly to JSON
                dos_header_raw = json.dumps(pe_dump['DOS_HEADER'])
                nt_header_raw = json.dumps(pe_dump['NT_HEADERS'])
                optional_header_raw = json.dumps(pe_dump['OPTIONAL_HEADER'])
                file_header_raw = json.dumps(pe_dump['FILE_HEADER'])

                # Convert magic and entry_point from hex strings to integers
                magic_raw = int(self.pe_features.magic, 16) if isinstance(self.pe_features.magic, str) else self.pe_features.magic
                entry_point = int(self.pe_features.entry_point, 16) if isinstance(self.pe_features.entry_point, str) else self.pe_features.entry_point

                # Convert target_machine to string if it's an integer
                target_machine = str(self.pe_features.target_machine) if isinstance(self.pe_features.target_machine, int) else self.pe_features.target_machine

                # debug struct
                dbg_struct_raw = {}
                if self.pe_features.dbg_struct:
                    if hasattr(self.pe, "DIRECTORY_ENTRY_DEBUG"):
                        for debug_entry in self.pe.DIRECTORY_ENTRY_DEBUG:
                            if (
                                debug_entry.struct.Type
                                == pefile.DEBUG_TYPE["IMAGE_DEBUG_TYPE_CODEVIEW"]
                            ):
                                dbg_struct_raw = json.dumps(debug_entry.struct.__dict__)

                # tls struct
                tls_struct_raw = {}
                if self.pe_features.tls_struct:
                    if (
                        hasattr(self.pe, "DIRECTORY_ENTRY_TLS")
                        and self.pe.DIRECTORY_ENTRY_TLS
                        and self.pe.DIRECTORY_ENTRY_TLS.struct
                        and self.pe.DIRECTORY_ENTRY_TLS.struct.AddressOfCallBacks
                    ):
                        tls_struct_raw = json.dumps(self.pe.DIRECTORY_ENTRY_TLS.struct.__dict__)

                # Ensure arrays are properly initialized
                rich_header_parsed = []
                if self.pe_features.rich_header_parsed:
                    if isinstance(self.pe_features.rich_header_parsed, str):
                        rich_header_parsed = [x for x in self.pe_features.rich_header_parsed.split("\n") if x]
                    elif isinstance(self.pe_features.rich_header_parsed, list):
                        rich_header_parsed = self.pe_features.rich_header_parsed

                version_info = self.pe_features.version_info if self.pe_features.version_info else []
                version_info_raw = {}
                if version_info:
                    for finfo in self.pe.FileInfo:
                        for entry in finfo:
                            if hasattr(entry, "StringTable"):
                                for st_entry in entry.StringTable:
                                    for key, str_entry in list(st_entry.entries.items()):
                                        version_info_raw[key.decode()] = str_entry.decode()
                version_info_raw = json.dumps(version_info_raw) if version_info_raw else "{}"

                export_symbols_list = self.pe_features.export_symbols_list if self.pe_features.export_symbols_list else []
                tls_callbacks = self.pe_features.tls_callbacks if self.pe_features.tls_callbacks else []

                # Prepare data array
                data = [[
                    self.sha256,
                    self.md5,
                    self.sha1,
                    self.pe_features.dos_header,
                    dos_header_raw,
                    self.pe_features.nt_header,
                    nt_header_raw,
                    self.pe_features.optional_header,
                    optional_header_raw,
                    self.pe_features.file_header,
                    file_header_raw,
                    magic_raw,
                    entry_point,
                    self.pe_features.major_linker_version,
                    self.pe_features.minor_linker_version,
                    target_machine,
                    self.pe_features.architecture,
                    self.pe_features.compilation_time,
                    1 if self.pe_features.is_dotnet else 0,
                    1 if self.pe_features.is_signed else 0,
                    1 if self.pe_features.has_overlay else 0,
                    self.pe_features.number_of_sections,
                    self.pe_features.number_of_imports,
                    self.pe_features.number_of_exports,
                    self.pe_features.number_of_resources,
                    self.pe_features.type,
                    self.pe_features.dbg_struct,
                    dbg_struct_raw,
                    self.pe_features.dbg_timestamp,
                    self.pe_features.dbg_pdb_info,
                    self.pe_features.dbg_pdb_info_raw,
                    self.pe_features.tls_struct,
                    tls_struct_raw,
                    self.pe_features.export_timestamp,
                    self.pe_features.export_library_name,
                    self.pe_features.export_library_name_raw,
                    self.pe_features.rich_header_dump if self.pe_features.rich_header_dump else "{}",
                    rich_header_parsed,
                    version_info,
                    version_info_raw,
                    export_symbols_list,
                    tls_callbacks,
                    datetime.now(timezone.utc)
                ]]

                column_names = [
                    'sha256', 'md5', 'sha1',
                    'dos_header', 'dos_header_raw', 'nt_header', 'nt_header_raw', 'optional_header', 'optional_header_raw', 'file_header', 'file_header_raw',
                    'magic_raw', 'entry_point',
                    'major_linker_version', 'minor_linker_version',
                    'target_machine', 'architecture',
                    'compilation_time',
                    'is_dotnet', 'is_signed', 'has_overlay',
                    'number_of_sections', 'number_of_imports', 'number_of_exports', 'number_of_resources',
                    'type',
                    'dbg_struct', 'dbg_struct_raw', 'dbg_timestamp', 'dbg_pdb_info', 'dbg_pdb_info_raw',
                    'tls_struct', 'tls_struct_raw', 'export_timestamp', 'export_library_name', 'export_library_name_raw',
                    'rich_header_dump', 'rich_header_parsed', 'version_info', 'version_info_raw',
                    'export_symbols_list', 'tls_callbacks',
                    'analysis_date'
                ]

                column_type_names = [
                    'FixedString(64)', 'FixedString(32)', 'FixedString(40)',
                    'String', 'JSON', 'String', 'JSON', 'String', 'JSON', 'String', 'JSON',
                    'UInt16', 'UInt32',
                    'UInt8', 'UInt8',
                    'LowCardinality(String)', 'Enum8(\'x86\' = 1, \'Itanium\' = 2, \'x64\' = 3)',
                    'UInt64',
                    'UInt8', 'UInt8', 'UInt8',
                    'UInt16', 'UInt16', 'UInt16', 'UInt16',
                    'Enum8(\'DLL\' = 1, \'EXE\' = 2, \'DRIVER\' = 3)',
                    'Nullable(String)', 'JSON', 'Nullable(UInt64)', 'Nullable(String)', 'Nullable(String)',
                    'Nullable(String)', 'JSON', 'Nullable(UInt64)', 'Nullable(String)', 'Nullable(String)',
                    'JSON', 'Array(Nullable(String))', 'Array(Nullable(String))', 'JSON',
                    'Array(Nullable(String))', 'Array(Nullable(UInt64))',
                    'DateTime64(3, \'UTC\')'
                ]

                if not data:
                    return None

                return (data, column_names, column_type_names)
            except Exception as e:
                self.log.error(f"Error preparing export data: {e}")
                raise

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