Nathalie Henry Riche

89 papers A* 26A 3B 8Misc 1Journal 38Unranked 9
YearRankTypeTitle / Venue / Authors
2024 J jnl
IEEE Trans. Vis. Comput. Graph.
Christophe Hurter, Bernice E. Rogowitz, Guillaume Truong, Tiffany Andry, Hugo Romat, Ludovic Gardy, Fereshteh Amini, Nathalie Henry Riche
2024 conf
CHI Extended Abstracts
Nikhita Joshi, Richard Li, Jiannan Li, Leonardo Pavanatto, Michel Pahud, Jatin Sharma, Bongshin Lee, Hugo Romat, William Buxton, Nicolai Marquardt, Ken Hinckley, Nathalie Henry Riche
2023 ed.
UIST (Adjunct Volume)
Sean Follmer, Jeff Han, Jürgen Steimle, Nathalie Henry Riche
2023 J jnl
CoRR
Adrian Kristanto, Maxime Cordeil, Benjamin Tag, Nathalie Henry Riche, Tim Dwyer
2023 A* ed.
UIST
Sean Follmer, Jeff Han, Jürgen Steimle, Nathalie Henry Riche
2022 J jnl
IEEE Trans. Vis. Comput. Graph.
Zezhong Wang, Hugo Romat, Fanny Chevalier, Nathalie Henry Riche, Dave Murray-Rust, Benjamin Bach
2022 A* conf
CHI
Hugo Romat, Nicolai Marquardt, Ken Hinckley, Nathalie Henry Riche
2021 A* conf
UIST
Nicolai Marquardt, Nathalie Henry Riche, Christian Holz, Hugo Romat, Michel Pahud, Frederik Brudy, David Ledo, Chunjong Park, Molly Jane Nicholas, Teddy Seyed, Eyal Ofek, Bongshin Lee, William A. S. Buxton, Ken Hinckley
2021 conf
VISIGRAPP
Nathalie Henry Riche
2021 A* conf
CHI
Rebecca Zheng, Marina Fernández Camporro, Hugo Romat, Nathalie Henry Riche, Benjamin Bach, Fanny Chevalier, Ken Hinckley, Nicolai Marquardt
2020 A* conf
CHI
Hugo Romat, Nathalie Henry Riche, Christophe Hurter, Steven Mark Drucker, Fereshteh Amini, Ken Hinckley
2020 A* conf
CHI
Arjun Srinivasan, Bongshin Lee, Nathalie Henry Riche, Steven Mark Drucker, Ken Hinckley
2020 J jnl
CoRR
Arjun Srinivasan, Bongshin Lee, Nathalie Henry Riche, Steven Mark Drucker, Ken Hinckley
2020 A* conf
UIST
Frederik Brudy, David Ledo, Michel Pahud, Nathalie Henry Riche, Christian Holz, Anand Waghmare, Hemant Bhaskar Surale, Marcus Peinado, Xiaokuan Zhang, Shannon Joyner, Badrish Chandramouli, Umar Farooq Minhas, Jonathan Goldstein, William Buxton, Ken Hinckley
2020 A* conf
UIST
Hugo Romat, Christopher Collins, Nathalie Henry Riche, Michel Pahud, Christian Holz, Adam Riddle, Bill Buxton, Ken Hinckley
2019 A* conf
CHI
Hugo Romat, Nathalie Henry Riche, Ken Hinckley, Bongshin Lee, Caroline Appert, Emmanuel Pietriga, Christopher Collins
2019 A* conf
CHI
Zezhong Wang, Shunming Wang, Matteo Farinella, Dave Murray-Rust, Nathalie Henry Riche, Benjamin Bach
2019 A* conf
CHI
Nam Wook Kim, Hyejin Im, Nathalie Henry Riche, Alicia Wang, Krzysztof Gajos, Hanspeter Pfister
2019 A* conf
CHI
Nam Wook Kim, Nathalie Henry Riche, Benjamin Bach, Guanpeng Xu, Matthew Brehmer, Ken Hinckley, Michel Pahud, Haijun Xia, Michael J. McGuffin, Hanspeter Pfister
2019 J jnl
IEEE Trans. Vis. Comput. Graph.
Christophe Hurter, Nathalie Henry Riche, Steven Mark Drucker, Maxime Cordeil, Richard Alligier, Romain Vuillemot
2019 Misc conf
ISS
Yea-Seul Kim, Nathalie Henry Riche, Bongshin Lee, Matthew Brehmer, Michel Pahud, Ken Hinckley, Jessica Hullman
2019 A* conf
UIST
Hugo Romat, Emmanuel Pietriga, Nathalie Henry Riche, Ken Hinckley, Caroline Appert
2019 J jnl
Dagstuhl Reports
Mikko Kivelä, Fintan McGee, Guy Melançon, Nathalie Henry Riche, Tatiana von Landesberger
2018 A* conf
CHI
Hugo Romat, Caroline Appert, Benjamin Bach, Nathalie Henry Riche, Emmanuel Pietriga
2018 J jnl
IEEE Trans. Vis. Comput. Graph.
Darren Edge, Nathalie Henry Riche, Jonathan Larson, Christopher M. White
2018 A* conf
CHI
Sungsoo (Ray) Hong, Minhyang (Mia) Suh, Nathalie Henry Riche, Jooyoung Lee, Juho Kim, Mark Zachry
2018 A* conf
CHI
Haijun Xia, Nathalie Henry Riche, Fanny Chevalier, Bruno Rodrigues De Araújo, Daniel Wigdor
2018 conf
CHI Extended Abstracts
Haijun Xia, Nathalie Henry Riche, Fanny Chevalier, Bruno Rodrigues De Araújo, Daniel Wigdor
2018 A* conf
CHI
Benjamin Bach, Zezhong Wang, Matteo Farinella, Dave Murray-Rust, Nathalie Henry Riche
2018 B conf
AVI
Fereshteh Amini, Nathalie Henry Riche, Bongshin Lee, Jason Leboe-McGowan, Pourang Irani
2018 book
Kim Marriott, Falk Schreiber, Tim Dwyer, Karsten Klein, Nathalie Henry Riche, Takayuki Itoh, Wolfgang Stuerzlinger, Bruce H. Thomas
2018 ch.
Immersive Analytics
Tim Dwyer, Kim Marriott, Tobias Isenberg, Karsten Klein, Nathalie Henry Riche, Falk Schreiber, Wolfgang Stuerzlinger, Bruce H. Thomas
2018 J jnl
CoRR
Michel Pahud, Eyal Ofek, Nathalie Henry Riche, Christophe Hurter, Jens Grubert
2018 J jnl
IEEE Computer Graphics and Applications
Fanny Chevalier, Nathalie Henry Riche, Basak Alper, Catherine Plaisant, Jeremy Boy, Niklas Elmqvist
2018 conf
BELIV
Donghao Ren, Bongshin Lee, Matthew Brehmer, Nathalie Henry Riche
2017 A* conf
CHI
Yann Riche, Nathalie Henry Riche, Ken Hinckley, Sheri Panabaker, Sarah Fuelling, Sarah Williams
2017 J jnl
IEEE Trans. Vis. Comput. Graph.
Fereshteh Amini, Nathalie Henry Riche, Bongshin Lee, Andrés Monroy-Hernández, Pourang Irani
2017 A conf
CSCW
Kiley Sobel, Geza Kovacs, Galen McQuillen, Andrew Cross, Nirupama Chandrasekaran, Nathalie Henry Riche, Ed Cutrell, Meredith Ringel Morris
2017 J jnl
IEEE Computer Graphics and Applications
Benjamin Bach, Nathalie Henry Riche, Sheelagh Carpendale, Hanspeter Pfister
2017 J jnl
IEEE Trans. Vis. Comput. Graph.
Matthew Brehmer, Bongshin Lee, Benjamin Bach, Nathalie Henry Riche, Tamara Munzner
2017 J jnl
IEEE Trans. Vis. Comput. Graph.
Benjamin Bach, Nathalie Henry Riche, Christophe Hurter, Kim Marriott, Tim Dwyer
2017 conf
PervasiveHealth
Eun Kyoung Choe, Bongshin Lee, Haining Zhu, Nathalie Henry Riche, Dominikus Baur
2017 J jnl
Comput. Graph. Forum
Sean McKenna, Nathalie Henry Riche, Bongshin Lee, Jeremy Boy, Miriah Meyer
2017 A* conf
CHI
Basak Alper, Nathalie Henry Riche, Fanny Chevalier, Jeremy Boy, T. Metin Sezgin
2016 B conf
AVI
Fanny Chevalier, Nathalie Henry Riche, Catherine Plaisant, Amira Chalbi, Christophe Hurter
2016 J jnl
Dagstuhl Reports
Sheelagh Carpendale, Nicholas Diakopoulos, Nathalie Henry Riche, Christophe Hurter
2016 J jnl
Dagstuhl Reports
Tim Dwyer, Nathalie Henry Riche, Karsten Klein, Wolfgang Stuerzlinger, Bruce H. Thomas
2016 J jnl
Comput. Graph. Forum
Michael Behrisch, Benjamin Bach, Nathalie Henry Riche, Tobias Schreck, Jean-Daniel Fekete
2016 A* conf
CHI
Benjamin Bach, Natalie Kerracher, Kyle Wm. Hall, Sheelagh Carpendale, Jessie Kennedy, Nathalie Henry Riche
2016 J jnl
IEEE Trans. Vis. Comput. Graph.
Quirijn W. Bouts, Tim Dwyer, Jason Dykes, Bettina Speckmann, Sarah Goodwin, Nathalie Henry Riche, Sheelagh Carpendale, Ariel Liebman
2015 J jnl
IEEE Computer Graphics and Applications
Nathalie Henry Riche
2015 J jnl
Comput. Graph. Forum
Kasper Dinkla, Nathalie Henry Riche, Michel A. Westenberg
2015 A conf
GD
Lev Nachmanson, Roman Prutkin, Bongshin Lee, Nathalie Henry Riche, Alexander E. Holroyd, Xiaoji Chen
2015 J jnl
CoRR
Lev Nachmanson, Roman Prutkin, Bongshin Lee, Nathalie Henry Riche, Alexander E. Holroyd, Xiaoji Chen
2015 J jnl
IEEE Computer Graphics and Applications
Bongshin Lee, Nathalie Henry Riche, Petra Isenberg, Sheelagh Carpendale
2015 J jnl
Comput. Graph. Forum
Sanjay Kairam, Nathalie Henry Riche, Steven Mark Drucker, Roland Fernandez, Jeffrey Heer
2015 B conf
PacificVis
Bongshin Lee, Greg Smith, Nathalie Henry Riche, Amy K. Karlson, Sheelagh Carpendale
2015 J jnl
Comput. Graph. Forum
Benjamin Bach, Nathalie Henry Riche, Tim Dwyer, Tara M. Madhyastha, Jean-Daniel Fekete, Thomas J. Grabowski
2015 A* conf
CHI
Fereshteh Amini, Nathalie Henry Riche, Bongshin Lee, Christophe Hurter, Pourang Irani
2014 B conf
PacificVis
Weiwei Cui, Xiting Wang, Shixia Liu, Nathalie Henry Riche, Tara M. Madhyastha, Kwan-Liu Ma, Baining Guo
2014 conf
IHM
Nathalie Henry Riche, Yann Riche, Nicolas Roussel, Sheelagh Carpendale, Tara M. Madhyastha, Thomas J. Grabowski
2014 A* conf
CHI
Basak Alper, Nathalie Henry Riche, Tobias Höllerer
2013 J jnl
IEEE Trans. Vis. Comput. Graph.
Jessica Hullman, Steven Mark Drucker, Nathalie Henry Riche, Bongshin Lee, Danyel Fisher, Eytan Adar
2013 J jnl
IEEE Trans. Vis. Comput. Graph.
Tim Dwyer, Nathalie Henry Riche, Kim Marriott, Christopher Mears
2013 J jnl
IEEE Trans. Vis. Comput. Graph.
Wouter Meulemans, Nathalie Henry Riche, Bettina Speckmann, Basak Alper, Tim Dwyer
2013 A* conf
CHI
Basak Alper, Benjamin Bach, Nathalie Henry Riche, Tobias Isenberg, Jean-Daniel Fekete
2012 conf
BCS HCI
Christophe Hurter, Benjamin R. Cowan, Audrey Girouard, Nathalie Henry Riche
2012 J jnl
IEEE Trans. Vis. Comput. Graph.
Bongshin Lee, Petra Isenberg, Nathalie Henry Riche, Sheelagh Carpendale
2012 B conf
AVI
Nathalie Henry Riche, Tim Dwyer, Bongshin Lee, Sheelagh Carpendale
2012 A* conf
CHI
Cody Dunne, Nathalie Henry Riche, Bongshin Lee, Ronald A. Metoyer, George G. Robertson
2012 J jnl
IEEE Trans. Vis. Comput. Graph.
Marian Dörk, Nathalie Henry Riche, Gonzalo A. Ramos, Susan T. Dumais
2012 B conf
AVI
Nathalie Henry Riche, Kori Inkpen, John T. Stasko, Tom Gross, Mary Czerwinski
2012 J jnl
IEEE Trans. Vis. Comput. Graph.
Jagoda Walny, Bongshin Lee, Paul Johns, Nathalie Henry Riche, Sheelagh Carpendale
2012 A* conf
CHI
Ronald A. Metoyer, Bongshin Lee, Nathalie Henry Riche, Mary Czerwinski
2011 conf
CHI Extended Abstracts
Christophe Hurter, Audrey Girouard, Nathalie Henry Riche, Catherine Plaisant
2011 A* conf
CHI
Neema Moraveji, Meredith Ringel Morris, Daniel Morris, Mary Czerwinski, Nathalie Henry Riche
2011 B conf
ITS
Jeffrey Browne, Bongshin Lee, Sheelagh Carpendale, Nathalie Henry Riche, Timothy Sherwood
2011 J jnl
IEEE Trans. Vis. Comput. Graph.
Basak Alper, Nathalie Henry Riche, Gonzalo A. Ramos, Mary Czerwinski
2011 J jnl
Comput. Graph. Forum
Sohaib Ghani, Nathalie Henry Riche, Niklas Elmqvist
2011 J jnl
Inf. Vis.
Sean Kandel, Jeffrey Heer, Catherine Plaisant, Jessie Kennedy, Frank van Ham, Nathalie Henry Riche, Chris E. Weaver, Bongshin Lee, Dominique Brodbeck, Paolo Buono
2011 J jnl
IEEE Trans. Vis. Comput. Graph.
Jagoda Walny, Sheelagh Carpendale, Nathalie Henry Riche, Gina Venolia, Philip Fawcett
2010 conf
CHI Extended Abstracts
Yann Riche, Nathalie Henry Riche, Petra Isenberg, Anastasia Bezerianos
2010 J jnl
IEEE Trans. Vis. Comput. Graph.
Niklas Elmqvist, Yann Riche, Nathalie Henry Riche, Jean-Daniel Fekete
2010 ch.
Handbook of Social Network Technologies
Nathalie Henry Riche, Jean-Daniel Fekete
2010 A conf
Conference on Designing Interactive Systems
Uta Hinrichs, Danyel Fisher, Nathalie Henry Riche
2010 J jnl
IEEE Trans. Vis. Comput. Graph.
Bongshin Lee, Nathalie Henry Riche, Amy K. Karlson, Sheelagh Carpendale
2010 J jnl
Comput. Graph. Forum
Nathalie Henry Riche, Bongshin Lee, Catherine Plaisant
2010 J jnl
IEEE Trans. Vis. Comput. Graph.
Nathalie Henry Riche, Tim Dwyer
2010 B conf
AVI
Nathalie Henry Riche, Bongshin Lee, Fanny Chevalier
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"