Karol Miller

50 papers A* 1C 3Journal 42Unranked 4
YearRankTypeTitle / Venue / Authors
2025 J jnl
CoRR
Mostafa Jamshidian, Adam Wittek, Saeideh Sekhavat, Farah Alkhatib, Jens Carsten Ritter, Paul M. Parizel, Donatien Le Liepvre, Florian Bernard, Ludovic Minvielle, Antoine Fondanèche, Jane Polce, Christopher Wood, Karol Miller
2025 J jnl
CoRR
Saeideh Sekhavat, Mostafa Jamshidian, Adam Wittek, Karol Miller
2025 J jnl
CoRR
Sajjad Arzemanzadeh, Karol Miller, Tim Rosenow, Sjoerd B. Vos, Adam Wittek
2025 J jnl
CoRR
Alastair Catlin, Mostafa Jamshidian, Adam Wittek, Karol Miller
2025 J jnl
CoRR
Andy Trung Huynh, Benjamin Zwick, Michael Halle, Adam Wittek, Karol Miller
2025 J jnl
CoRR
Mostafa Jamshidian, Adam Wittek, Hozan Mufty, Geert Maleux, Inge Fourneau, Elke Ruth Gizewski, Eva Gassner, Alexander Loizides, Maximilian Lutz, Florian K. Enzmann, Donatien Le Liepvre, Florian Bernard, Ludovic Minvielle, Antoine Fondanèche, Karol Miller
2024 J jnl
J. Comput. Phys.
George C. Bourantas, Benjamin F. Zwick, Dimitrios S. Lampropoulos, Vassilios C. Loukopoulos, K. Katsanos, A. A. Dimas, Vasilis N. Burganos, Adam Wittek, Karol Miller
2024 J jnl
CoRR
Mostafa Jamshidian, Saeideh Sekhavat, Adam Wittek, Karol Miller
2024 J jnl
CoRR
Mostafa Jamshidian, Benjamin Zwick, Arosha S. Dissanayake, Adam Wittek, Timothy J. Phillips, Stephen Honeybul, Graeme J. Hankey, Karol Miller
2024 J jnl
CoRR
Andy Trung Huynh, Benjamin Zwick, Mostafa Jamshidian, Michael Halle, Adam Wittek, Karol Miller
2024 J jnl
CoRR
Mostafa Jamshidian, Adam Wittek, Saeideh Sekhavat, Karol Miller
2024 J jnl
CoRR
Andy Trung Huynh, Benjamin Zwick, Michael Halle, Adam Wittek, Karol Miller
2024 J jnl
CoRR
Sajjad Arzemanzadeh, Benjamin Zwick, Karol Miller, Tim Rosenow, Stuart I. Hodgetts, Adam Wittek
2024 J jnl
CoRR
Farah Alkhatib, Mostafa Jamshidian, Donatien Le Liepvre, Florian Bernard, Ludovic Minvielle, Adam Wittek, Karol Miller
2023 J jnl
Appl. Math. Comput.
Benjamin F. Zwick, George C. Bourantas, Farah Alkhatib, Adam Wittek, Karol Miller
2023 J jnl
Appl. Math. Comput.
Benjamin F. Zwick, George C. Bourantas, Farah Alkhatib, Adam Wittek, Karol Miller
2023 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Saima Safdar, Benjamin F. Zwick, Yue Yu, George C. Bourantas, Grand R. Joldes, Simon K. Warfield, Damon E. Hyde, Sarah F. Frisken, Tina Kapur, Ron Kikinis, Alexandra J. Golby, Arya Nabavi, Adam Wittek, Karol Miller
2022 J jnl
CoRR
Saima Safdar, Benjamin Zwick, George C. Bourantas, Grand R. Joldes, Damon Hyde, Simon K. Warfield, Adam Wittek, Karol Miller
2022 J jnl
Comput. Biol. Medicine
Yue Yu, Saima Safdar, George C. Bourantas, Benjamin Zwick, Grand R. Joldes, Tina Kapur, Sarah F. Frisken, Ron Kikinis, Arya Nabavi, Alexandra J. Golby, Adam Wittek, Karol Miller
2022 J jnl
CoRR
Farah Alkhatib, George C. Bourantas, Adam Wittek, Karol Miller
2022 J jnl
NeuroImage
Benjamin F. Zwick, George C. Bourantas, Saima Safdar, Grand R. Joldes, Damon E. Hyde, Simon K. Warfield, Adam Wittek, Karol Miller
2022 J jnl
CoRR
Benjamin F. Zwick, George C. Bourantas, Farah Alkhatib, Adam Wittek, Karol Miller
2021 J jnl
Robotica
Sudharsan Thiruvengadam, Jei Shian Tan, Karol Miller
2020 J jnl
Neurocomputing
Sudharsan Thiruvengadam, Jei Shian Tan, Karol Miller
2020 J jnl
CoRR
George C. Bourantas, D. L. Lampropoulos, Benjamin F. Zwick, Vassilios C. Loukopoulos, Adam Wittek, Karol Miller
2020 J jnl
CoRR
Dimitrios S. Lampropoulos, George C. Bourantas, Benjamin F. Zwick, G. C. Kagadis, Adam Wittek, Karol Miller, Vassilios C. Loukopoulos
2020 J jnl
CoRR
George C. Bourantas, Benjamin F. Zwick, Grand R. Joldes, Adam Wittek, Karol Miller
2020 J jnl
Symmetry
Ioannis D. Boutopoulos, Dimitrios S. Lampropoulos, George C. Bourantas, Karol Miller, Vassilios C. Loukopoulos
2019 J jnl
Appl. Math. Comput.
George C. Bourantas, Vassilios C. Loukopoulos, Grand Roman Joldes, Adam Wittek, Karol Miller
2019 J jnl
CoRR
Karol Miller, Grand Roman Joldes, George C. Bourantas, Simon K. Warfield, Damon E. Hyde, Ron Kikinis, Adam Wittek
2019 J jnl
CoRR
Konstantinos A. Mountris, George C. Bourantas, Daniel Millan, Grand R. Joldes, Karol Miller, Esther Pueyo, Adam Wittek
2019 J jnl
CoRR
Grand Roman Joldes, George C. Bourantas, Benjamin Zwick, Habibullah Amin Chowdhury, Adam Wittek, Sudip Agrawal, Konstantinos A. Mountris, Damon Hyde, Simon K. Warfield, Karol Miller
2019 J jnl
Medical Image Anal.
Grand Roman Joldes, George C. Bourantas, Benjamin Zwick, Habibullah Amin Chowdhury, Adam Wittek, Sudip Agrawal, Konstantinos A. Mountris, Damon Hyde, Simon K. Warfield, Karol Miller
2017 J jnl
J. Sci. Comput.
Habibullah Amin Chowdhury, Adam Wittek, Karol Miller, Grand Roman Joldes
2015 J jnl
Appl. Math. Comput.
Grand Roman Joldes, Habibullah Amin Chowdhury, Adam Wittek, Barry J. Doyle, Karol Miller
2015 J jnl
Medical Image Anal.
Mao Li, Karol Miller, Grand Roman Joldes, Barry J. Doyle, Revanth Reddy Garlapati, Ron Kikinis, Adam Wittek
2015 J jnl
Comput. Biol. Medicine
Revanth Reddy Garlapati, Ahmed Mostayed, Grand Roman Joldes, Adam Wittek, Barry J. Doyle, Karol Miller
2014 conf
ISBMS
Mao Li, Karol Miller, Grand Roman Joldes, Ron Kikinis, Adam Wittek
2011 J jnl
Briefings Bioinform.
Adam Wittek, Karol Miller
2009 J jnl
Medical Image Anal.
Karol Miller
2009 conf
MICCAI (1)
Grand Roman Joldes, Adam Wittek, Mathieu Couton, Simon K. Warfield, Karol Miller
2009 J jnl
Medical Image Anal.
Grand Roman Joldes, Adam Wittek, Karol Miller
2007 conf
MICCAI (1)
Ashley Horton, Adam Wittek, Karol Miller
2005 conf
MICCAI (2)
Adam Wittek, Ron Kikinis, Simon K. Warfield, Karol Miller
2004 J jnl
Int. J. Robotics Res.
Karol Miller
2003 C conf
ISRR
Karol Miller, Wieslaw Lucjan Nowinski
2002 C conf
ICARCV
Zeikc Taylor, Karol Miller
2001 C conf
ISRR
Karol Miller
1995 A* conf
ICRA
Karol Miller
1995 J jnl
J. Robotics Mechatronics
Karol Miller, Boris S. Stevens
redb/extractors/pe_extractors/pe_dotnet.py
← Index redb/extractors/pe_extractors/pe_dotnet.py python
import inspect
import pefile
from dotnetfile import DotNetPE

import base64
from hashlib import md5
from pprint import pprint

import magic
import pefile

from redb.extractors.enum import Tag
from redb.extractors.pe_extractor import PEExtractor
from redb.models.dataclasses import PEDotNet
from datetime import datetime, timezone
import json
from typing import Any


class PEDotNetExtractor(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_dotnet"
        self.dotnet, self.error = self._generate_dotnetfile_object()
        self.log.debug(inspect.currentframe().f_code.co_name)

    def _decode_assembly_flags(self, flag_value):
        self.log.debug(inspect.currentframe().f_code.co_name)
        ASSEMBLY_FLAGS = {
            0x00000001: "COMIMAGE_FLAGS_ILONLY",
            0x00000002: "COMIMAGE_FLAGS_32BITREQUIRED",
            0x00000004: "COMIMAGE_FLAGS_IL_LIBRARY",
            0x00000008: "COMIMAGE_FLAGS_STRONGNAMESIGNED",
            0x00000010: "COMIMAGE_FLAGS_NATIVE_ENTRYPOINT",
            0x00010000: "COMIMAGE_FLAGS_TRACKDEBUGDATA",
            0x00020000: "COMIMAGE_FLAGS_32BITPREFERRED",
        }
        flags = []
        for bit, name in ASSEMBLY_FLAGS.items():
            if flag_value & bit:
                flags.append(name)
        return flags

    def _normalize_data(self, data):
        self.log.debug(inspect.currentframe().f_code.co_name)
        if isinstance(data, str):
            return {"value": data}
        return data

    def _compute_md5(self, data):
        self.log.debug(inspect.currentframe().f_code.co_name)
        if isinstance(data, str):
            # If it's a string, encode it to first
            return md5(data.encode("raw_unicode_escape")).hexdigest()
        elif not isinstance(data, bytes):
            # If it's neither string nor bytes, convert it to string and then to bytes
            return md5(str(data).encode("raw_unicode_escape")).hexdigest()
        else:
            return md5(data).hexdigest()

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

    def extract(self):
        self.log.debug(inspect.currentframe().f_code.co_name)
        metadata = {}

        if self.error:
            metadata["Corrupted"] = self.error.args[0]
            dotnet = PEDotNet(metadata)
            self.dotnet = dotnet  # Store for later use in prepare_export_data
            return dotnet  # Return the extracted data instead of exporting directly

        metadata["info"] = {}
        try:
            available_tables = self.dotnet.existent_metadata_tables()
        except Exception as e:
            self.log.error(f"Error getting metadata tables for {self.hash.sha256}: {e}")
            available_tables = []
            metadata["info"]["PotentiallyCorrupted"] = True

        # GUID extraction
        try:
            input_string = self.dotnet.guid_stream_guids[0].string_representation
            
            formatted_uuid = (
                f"{input_string[6:8]}{input_string[4:6]}{input_string[2:4]}{input_string[0:2]}"
                f"-{input_string[10:12]}{input_string[8:10]}"
                f"-{input_string[14:16]}{input_string[12:14]}"
                f"-{input_string[16:20]}"
                f"-{input_string[20:]}"
            )
            metadata["info"]["ModuleVersionID"] = formatted_uuid
        except Exception as e:
            self.log.error(f"Error extracting GUID for {self.hash.sha256}: {e}")

        # CLR Version
        try:
            metadata["info"]["CLRversion"] = self.dotnet.get_runtime_target_version()
        except Exception as e:
            self.log.error(f"Error getting CLR version for {self.hash.sha256}: {e}")

        # Module information
        try:
            if self.dotnet.metadata_table_exists("Module"):
                metadata["info"]["AssemblyModuleName"] = self.dotnet.Module.get_module_name()
        except Exception as e:
            self.log.error(f"Error getting module info for {self.hash.sha256}: {e}")

        # Basic properties
        try:
            metadata["info"].update({
                "IsNativeNgenImage": self.dotnet.is_native_image(),
                "IsMixedAssembly": self.dotnet.is_mixed_assembly(),
                "IsWindowsFormsApp": self.dotnet.is_windows_forms_app(),
                "EntryPointToken": self.dotnet.clr_header.EntryPointToken.value,
                "HasNativeEntryPoint": self.dotnet.has_native_entry_point(),
            })
        except Exception as e:
            self.log.error(f"Error getting basic properties for {self.hash.sha256}: {e}")

        # Assembly flags
        try:
            metadata["info"]["AssemblyFlags"] = self._decode_assembly_flags(
                self.dotnet.clr_header.Flags.value
            )
        except Exception as e:
            self.log.error(f"Error getting assembly flags for {self.hash.sha256}: {e}")

        # More Metadata/Info CLR Header
        try:
            metadata["info"]["NumOfStreams"] = self.dotnet.get_number_of_streams()
            # [TODO] The following two on VT are "RVA entry point"
            #  and "Resources va" respectively. To verify.
            metadata["info"][
                "ResourcesDirectoryAddress"
            ] = self.dotnet.clr_header.ResourcesDirectoryAddress.value
            metadata["info"][
                "ResourcesDirectorySize"
            ] = self.dotnet.clr_header.ResourcesDirectorySize.value
            metadata["info"][
                "StrongNameSignatureAddress"
            ] = self.dotnet.clr_header.StrongNameSignatureAddress.value
            metadata["info"][
                "StrongNameSignatureSize"
            ] = self.dotnet.clr_header.StrongNameSignatureSize.value
            metadata["info"][
                "CodeManagerTableAddress"
            ] = self.dotnet.clr_header.CodeManagerTableAddress.value
            metadata["info"][
                "CodeManagerTableSize"
            ] = self.dotnet.clr_header.CodeManagerTableSize.value
            metadata["info"][
                "VTableFixupsAddress"
            ] = self.dotnet.clr_header.VTableFixupsAddress.value
            metadata["info"][
                "VTableFixupsSize"
            ] = self.dotnet.clr_header.VTableFixupsSize.value
            metadata["info"][
                "ExportAddressTableJumpsAddress"
            ] = self.dotnet.clr_header.ExportAddressTableJumpsAddress.value
            metadata["info"][
                "ExportAddressTableJumpsSize"
            ] = self.dotnet.clr_header.ExportAddressTableJumpsSize.value
            metadata["info"][
                "ManagedNativeHeaderAddress"
            ] = self.dotnet.clr_header.ManagedNativeHeaderAddress.value
            metadata["info"][
                "ManagedNativeHeaderSize"
            ] = self.dotnet.clr_header.ManagedNativeHeaderSize.value
        except Exception as e:
            self.log.error(f"Error getting more metadata/info clr header for {self.hash.sha256}: {e}")

        # Streams section
        try:
            metadata["Streams"] = {}
            stream_names = self.dotnet.get_stream_names()
            for name in stream_names:
                indx = stream_names.index(name)
                stream_header = self.dotnet.dotnet_stream_headers[indx]
                stream_address = self.dotnet.dotnet_streams[indx].address
                metadata["Streams"][name] = {}
                metadata["Streams"][name]["raw_name"] = base64.b64encode(
                    stream_header.Name.value
                ).decode()
                # metadata["Streams"][name]["raw_name"] = name
                metadata["Streams"][name]["size"] = stream_header.Size.value
                # Retrieve the stream data directly using the given address and size
                stream_data = self.pe.get_data(stream_address, stream_header.Size.value)
                metadata["Streams"][name]["entropy"] = (
                    "%.2f" % pefile.SectionStructure.entropy_H(self.pe, stream_data)
                )
                metadata["Streams"][name]["md5"] = md5(stream_data).hexdigest()
        except Exception as e:
            self.log.error(f"Error getting streams for {self.hash.sha256}: {e}")

        # ManifestResource
        try:
            metadata["ManifestResource"] = ()
            if "ManifestResource" in available_tables:
                metadata["info"]["HasManifestResource"] = True
                metadata["ManifestResource"] = (
                    self.dotnet.ManifestResource.get_resource_names()
                )
        except Exception as e:
            self.log.error(f"Error getting manifest resource for {self.hash.sha256}: {e}")

        # ExternalAssemblyRef
        try:
            metadata["ExternalAssemblyRef"] = {}
            if "AssemblyRef" in available_tables:
                metadata["ExternalAssemblyRef"][
                    "names"
                ] = self.dotnet.AssemblyRef.get_assemblyref_names()
                metadata["ExternalAssemblyRef"][
                    "cultures"
                ] = self.dotnet.AssemblyRef.get_assemblyref_cultures()
        except Exception as e:
            self.log.error(f"Error getting external assembly ref for {self.hash.sha256}: {e}")

        # AssemblyData
        try:
            metadata["AssemblyData"] = {}
            if "Assembly" in available_tables:
                metadata["AssemblyData"][
                    "name"
                ] = self.dotnet.Assembly.get_assembly_name()
                # It's officially a one-row table, but there are ITW files with more than one row.
                # It stores information about the current assembly. It is documented in ECMA-335.
                assembly_table = self.dotnet.metadata_tables_lookup[
                    "Assembly"
                ].table_rows[0]
                metadata["AssemblyData"][
                    "MajorVersion"
                ] = assembly_table.MajorVersion.value
                metadata["AssemblyData"][
                    "MinorVersion"
                ] = assembly_table.MinorVersion.value
                metadata["AssemblyData"]["hashalgid"] = assembly_table.HashAlgId.value
                metadata["AssemblyData"][
                    "BuildNumber"
                ] = assembly_table.BuildNumber.value
                metadata["AssemblyData"][
                    "RevisionNumber"
                ] = assembly_table.RevisionNumber.value
                metadata["AssemblyData"][
                    "culture"
                ] = self.dotnet.Assembly.get_assembly_culture()
        except Exception as e:
            self.log.error(f"Error getting assembly data for {self.hash.sha256}: {e}")

        # TypeDef handling
        try:
            metadata["TypeDef"] = {}
            if "TypeDef" in available_tables:
                metadata["info"]["HasTypeDef"] = True
                # Get basic type definitions
                try:
                    # Get all types (ANY visibility)
                    type_defs = self.dotnet.TypeDef.get_type_names()
                    metadata["TypeDef"]["names"] = type_defs if type_defs else []
                    
                    # Get detailed type information with methods
                    types_with_methods = self.dotnet.TypeDef.get_type_names_with_methods()
                    if types_with_methods:
                        metadata["TypeDef"]["detailed"] = []
                        for type_info in types_with_methods:
                            type_data = {
                                "type": type_info.Type,
                                "namespace": type_info.Namespace,
                                "flags": type_info.Flags,
                                "methods": []
                            }
                            for method in type_info.Methods:
                                method_data = {
                                    "name": method.Name,
                                    "flags": method.Flags
                                }
                                if method.Signature:
                                    method_data["signature"] = {
                                        "parameters": method.Signature.get("parameter", []),
                                        "return_type": method.Signature.get("return", ""),
                                        "has_this": method.Signature.get("hasthis", False)
                                    }
                                type_data["methods"].append(method_data)
                            metadata["TypeDef"]["detailed"].append(type_data)
                except AttributeError as e:
                    self.log.warning(f"TypeDef table exists but attributes missing for {self.hash.sha256}: {e}")
                    # metadata["info"]["TypeDefParsingError"] = f"Missing attributes: {str(e)}"
                except Exception as e:
                    self.log.error(f"Error parsing TypeDef table for {self.hash.sha256}: {e}")
                    # metadata["info"]["TypeDefParsingError"] = str(e)
        except Exception as e:
            self.log.error(f"Error handling TypeDef section for {self.hash.sha256}: {e}")
            metadata["TypeDef"] = {"names": [], "detailed": []}
            # metadata["info"]["TypeDefParsingError"] = str(e)

        # ExternalUnmanagedModules
        try:
            metadata["ExternalUnmanagedModules"] = ()
            if "ModuleRef" in available_tables:
                metadata["info"]["HasModuleRef"] = True
                metadata["ExternalUnmanagedModules"] = (
                    self.dotnet.ModuleRef.get_unmanaged_module_names(
                        self.dotnet.Type.UnmanagedModules.RAW
                    )
                )
        except Exception as e:
            self.log.error(f"Error getting external unmanaged modules for {self.hash.sha256}: {e}")

        # ExternalUnmanagedMethods
        try:
            metadata["ExternalUnmanagedMethods"] = ()
            if "ImplMap" in available_tables:
                metadata["info"]["HasImplMap"] = True
                metadata["ExternalUnmanagedMethods"] = (
                    self.dotnet.ImplMap.get_unmanaged_functions()
                )
        except Exception as e:
            self.log.error(f"Error getting external unmanaged methods for {self.hash.sha256}: {e}")

        # Event
        try:
            metadata["Event"] = ()
            if "Event" in available_tables:
                metadata["info"]["HasEvent"] = True
                metadata["Event"] = self.dotnet.Event.get_event_names()
        except Exception as e:
            self.log.error(f"Error getting event for {self.hash.sha256}: {e}")

        # Resources
        try:
            metadata["Resources"] = []
            if self.dotnet.has_resources():
                tmp_resources_list = []
                resource_data = self.dotnet.get_resources()
                for data in resource_data:
                    tmp_dict = {}
                    for resource_item in data.items():
                        if resource_item[0] == "SubResources":
                            if resource_item[1]:
                                tmp_dict["SubResources"] = {}
                                for sub_resource in resource_item[1]:
                                    for sub_resource_item in sub_resource.items():
                                        if sub_resource_item[0] == "Data":
                                            # Check if data is a sequence type that supports len()
                                            if hasattr(sub_resource_item[1], '__len__') and len(sub_resource_item[1]) > 100:
                                                tmp_dict["SubResources"]["Data"] = {}
                                                try:
                                                    # tmp_dict["SubResources"]["Data"][
                                                    #     "md5"
                                                    # ] = md5(
                                                    #     sub_resource_item[1].encode()
                                                    # ).hexdigest()
                                                    tmp_dict["SubResources"]["Data"][
                                                        "md5"
                                                    ] = self._compute_md5(
                                                        sub_resource_item[1]
                                                    )
                                                except Exception as e:
                                                    self.log.error(
                                                        f"Error in dotnet resources"
                                                        f" extraction {self.hash.sha256}: {e}"
                                                    )
                                                guess = magic.from_buffer(
                                                    sub_resource_item[1], mime=True
                                                )
                                                tmp_dict["SubResources"]["Data"][
                                                    "MimeType"
                                                ] = (guess if guess else None)
                                            else:
                                                normalized_data = self._normalize_data(
                                                    sub_resource_item[1].decode()
                                                    if isinstance(
                                                        sub_resource_item[1], bytes
                                                    )
                                                    else sub_resource_item[1]
                                                )
                                                tmp_dict["SubResources"][
                                                    sub_resource_item[0]
                                                ] = normalized_data
                                        else:
                                            tmp_dict["SubResources"][
                                                sub_resource_item[0]
                                            ] = (
                                                sub_resource_item[1].decode()
                                                if isinstance(
                                                    sub_resource_item[1], bytes
                                                )
                                                else sub_resource_item[1]
                                            )
                        elif resource_item[0] == "Data":
                            # Check if data is a sequence type that supports len()
                            if hasattr(resource_item[1], '__len__') and len(resource_item[1]) > 100:
                                tmp_dict["Data"] = {}
                                tmp_dict["Data"]["md5"] = self._compute_md5(
                                    resource_item[1]
                                )
                                guess = magic.from_buffer(resource_item[1], mime=True)
                                tmp_dict["Data"]["MimeType"] = guess if guess else None
                            else:
                                normalized_data = self._normalize_data(
                                    resource_item[1].decode()
                                    if isinstance(resource_item[1], bytes)
                                    else resource_item[1]
                                )
                                tmp_dict["Data"] = normalized_data
                        else:
                            tmp_dict[resource_item[0]] = (
                                resource_item[1].decode()
                                if isinstance(resource_item[1], bytes)
                                else resource_item[1]
                            )
                    tmp_resources_list.append(tmp_dict)
                metadata["Resources"] = tmp_resources_list
        except Exception as e:
            self.log.error(f"Error getting resources for {self.hash.sha256}: {e}")
            metadata["Resources"] = []  # Ensure we always have a valid list even on error

        dotnet = PEDotNet(metadata)
        self.dotnet = dotnet  # Store for later use in prepare_export_data
        return dotnet  # Return the extracted data instead of exporting directly


    def prepare_export_data(self, exporter_type: str) -> Any:
        if exporter_type == "ElasticsearchExporter":
            return self.dotnet
        elif exporter_type == "ClickHouseExporter":
            # Convert metadata to JSON string
            metadata_json = json.dumps(self.dotnet.dotnet)
            
            data = [[
                self.sha256,
                self.md5,
                self.sha1,
                metadata_json,
                datetime.now(timezone.utc)
            ]]
            
            column_names = [
                'sha256', 'md5', 'sha1', 'dotnet', 'analysis_date'
            ]
            
            if not data:
                return None

            column_type_names = [
                'String', 'String', 'String', 'JSON', 'DateTime64(3, \'UTC\')'
            ]

            return (data, column_names, column_type_names)

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