Ioannis Gkioulekas

76 papers A* 15A 1B 5Misc 1Journal 46Unranked 8
YearRankTypeTitle / Venue / Authors
2025 A* conf
SIGGRAPH Asia
Arjun Teh, Delio Vicini, Bernd Bickel, Ioannis Gkioulekas, Matthew O'Toole
2025 J jnl
CoRR
Arjun Teh, Delio Vicini, Bernd Bickel, Ioannis Gkioulekas, Matthew O'Toole
2025 J jnl
CoRR
Neham Jain, Andrew Jong, Sebastian A. Scherer, Ioannis Gkioulekas
2025 J jnl
CoRR
Bailey Miller, Rohan Sawhney, Keenan Crane, Ioannis Gkioulekas
2025 J jnl
ACM Trans. Graph.
Bailey Miller, Rohan Sawhney, Keenan Crane, Ioannis Gkioulekas
2025 conf
SIGGRAPH Courses
Rohan Sawhney, Bailey Miller, Ioannis Gkioulekas, Keenan Crane
2024 J jnl
CoRR
Hanyu Chen, Bailey Miller, Ioannis Gkioulekas
2024 J jnl
ACM Trans. Graph.
Hanyu Chen, Bailey Miller, Ioannis Gkioulekas
2024 conf
SIGGRAPH (Conference Paper Track)
Arjun Teh, Ioannis Gkioulekas, Matthew O'Toole
2024 J jnl
CoRR
Bailey Miller, Rohan Sawhney, Keenan Crane, Ioannis Gkioulekas
2024 J jnl
ACM Trans. Graph.
Bailey Miller, Rohan Sawhney, Keenan Crane, Ioannis Gkioulekas
2024 A* conf
CVPR
Bailey Miller, Hanyu Chen, Alice Lai, Ioannis Gkioulekas
2024 conf
EGSR (ST)
Tanli Su, Ioannis Gkioulekas
2024 J jnl
CoRR
Dhawal Sirikonda, Praneeth Chakravarthula, Ioannis Gkioulekas, Adithya Pediredla
2024 J jnl
Discret. Comput. Geom.
Ioannis Gkioulekas, Steven J. Gortler, Louis Theran, Todd E. Zickler
2024 J jnl
ACM Trans. Graph.
Bailey Miller, Rohan Sawhney, Keenan Crane, Ioannis Gkioulekas
2023 J jnl
CoRR
Bailey Miller, Rohan Sawhney, Keenan Crane, Ioannis Gkioulekas
2023 J jnl
ACM Trans. Graph.
Bailey Miller, Rohan Sawhney, Keenan Crane, Ioannis Gkioulekas
2023 conf
SIGGRAPH Courses
Alankar Kotwal, Florian Willomitzer, Ioannis Gkioulekas
2023 J jnl
CoRR
Juhyeon Kim, Wojciech Jarosz, Ioannis Gkioulekas, Adithya Pediredla
2023 J jnl
ACM Trans. Graph.
Juhyeon Kim, Wojciech Jarosz, Ioannis Gkioulekas, Adithya Pediredla
2023 A* conf
CVPR
Adithya Pediredla, Srinivasa G. Narasimhan, Maysamreza Chamanzar, Ioannis Gkioulekas
2023 conf
ICCV (Workshops)
Dakshit Agrawal, Jiajie Xu, Siva Karthik Mustikovela, Ioannis Gkioulekas, Ashish Shrivastava, Yuning Chai
2023 J jnl
CoRR
Dakshit Agrawal, Jiajie Xu, Siva Karthik Mustikovela, Ioannis Gkioulekas, Ashish Shrivastava, Yuning Chai
2023 A* conf
ICRA
Mohamad Qadri, Michael Kaess, Ioannis Gkioulekas
2023 A* conf
CVPR
Byeongjoo Ahn, Michael DeZeeuw, Ioannis Gkioulekas, Aswin C. Sankaranarayanan
2023 J jnl
CoRR
Bailey Miller, Hanyu Chen, Alice Lai, Ioannis Gkioulekas
2023 A* conf
CVPR
Alankar Kotwal, Anat Levin, Ioannis Gkioulekas
2023 A* conf
CVPR
Alankar Kotwal, Anat Levin, Ioannis Gkioulekas
2023 J jnl
CoRR
Rohan Sawhney, Bailey Miller, Ioannis Gkioulekas, Keenan Crane
2023 J jnl
ACM Trans. Graph.
Rohan Sawhney, Bailey Miller, Ioannis Gkioulekas, Keenan Crane
2022 A* conf
CVPR
Ryan Po, Adithya Pediredla, Ioannis Gkioulekas
2022 J jnl
ACM Trans. Graph.
Arjun Teh, Matthew O'Toole, Ioannis Gkioulekas
2022 J jnl
CoRR
Mohamad Qadri, Michael Kaess, Ioannis Gkioulekas
2022 J jnl
CoRR
Alankar Kotwal, Anat Levin, Ioannis Gkioulekas
2022 J jnl
Comput. Graph. Forum
Guangyan Cai, Kai Yan, Zhao Dong, Ioannis Gkioulekas, Shuang Zhao
2022 J jnl
CoRR
Guangyan Cai, Kai Yan, Zhao Dong, Ioannis Gkioulekas, Shuang Zhao
2022 J jnl
CoRR
Alankar Kotwal, Anat Levin, Ioannis Gkioulekas
2021 J jnl
CoRR
Ryan Po, Adithya Pediredla, Ioannis Gkioulekas
2021 A* conf
ICCV
Shumian Xin, Neal Wadhwa, Tianfan Xue, Jonathan T. Barron, Pratul P. Srinivasan, Jiawen Chen, Ioannis Gkioulekas, Rahul Garg
2021 J jnl
CoRR
Shumian Xin, Neal Wadhwa, Tianfan Xue, Jonathan T. Barron, Pratul P. Srinivasan, Jiawen Chen, Ioannis Gkioulekas, Rahul Garg
2021 J jnl
ACM Trans. Graph.
Marina Alterman, Chen Bar, Ioannis Gkioulekas, Anat Levin
2021 J jnl
ACM Trans. Graph.
Byeongjoo Ahn, Ioannis Gkioulekas, Aswin C. Sankaranarayanan
2021 B conf
ICCP
Chen Bar, Marina Alterman, Ioannis Gkioulekas, Anat Levin
2021 J jnl
Comput. Ind. Eng.
Ioannis Gkioulekas, Lazaros G. Papageorgiou
2020 A conf
IROS
Eric Westman, Ioannis Gkioulekas, Michael Kaess
2020 A* conf
ICRA
Eric Westman, Ioannis Gkioulekas, Michael Kaess
2020 J jnl
Comput. Chem. Eng.
Sergio Medina-González, Ioannis Gkioulekas, Vivek Dua, Lazaros G. Papageorgiou
2020 J jnl
ACM Trans. Graph.
Alankar Kotwal, Anat Levin, Ioannis Gkioulekas
2020 J jnl
ACM Trans. Graph.
Fujun Luan, Shuang Zhao, Kavita Bala, Ioannis Gkioulekas
2020 J jnl
ACM Trans. Graph.
Adithya Pediredla, Yasin Karimi Chalmiani, Matteo Giuseppe Scopelliti, Maysamreza Chamanzar, Srinivasa G. Narasimhan, Ioannis Gkioulekas
2020 J jnl
ACM Trans. Graph.
Cheng Zhang, Bailey Miller, Kai Yan, Ioannis Gkioulekas, Shuang Zhao
2020 J jnl
ACM Trans. Graph.
Chen Bar, Ioannis Gkioulekas, Anat Levin
2020 Misc conf
ICASSP
Alankar Kowtal, Avilash K. Cramer, Dufan Wu, Kai Yang, Wolfgang Krull, Ioannis Gkioulekas, Rajiv Gupta
2020 B conf
ICCP
Chengqian Che, Fujun Luan, Shuang Zhao, Kavita Bala, Ioannis Gkioulekas
2020 B conf
ICCP
Kfir Shem-Tov, Sai Praveen Bangaru, Anat Levin, Ioannis Gkioulekas
2019 J jnl
CoRR
Chen Bar, Marina Alterman, Ioannis Gkioulekas, Anat Levin
2019 J jnl
ACM Trans. Graph.
Chen Bar, Marina Alterman, Ioannis Gkioulekas, Anat Levin
2019 A* conf
CVPR
Shumian Xin, Sotiris Nousias, Kiriakos N. Kutulakos, Aswin C. Sankaranarayanan, Srinivasa G. Narasimhan, Ioannis Gkioulekas
2019 J jnl
ACM Trans. Graph.
Cheng Zhang, Lifan Wu, Changxi Zheng, Ioannis Gkioulekas, Ravi Ramamoorthi, Shuang Zhao
2019 A* conf
CVPR
Chia-Yin Tsai, Aswin C. Sankaranarayanan, Ioannis Gkioulekas
2019 A* conf
ICCV
Byeongjoo Ahn, Akshat Dave, Ashok Veeraraghavan, Ioannis Gkioulekas, Aswin C. Sankaranarayanan
2019 J jnl
ACM Trans. Graph.
Adithya Pediredla, Ashok Veeraraghavan, Ioannis Gkioulekas
2019 J jnl
Expert Syst. Appl.
Ioannis Gkioulekas, Lazaros G. Papageorgiou
2019 B conf
ICCP
Ankit Raghuram, Adithya Pediredla, Srinivasa G. Narasimhan, Ioannis Gkioulekas, Ashok Veeraraghavan
2018 J jnl
CoRR
Chengqian Che, Fujun Luan, Shuang Zhao, Kavita Bala, Ioannis Gkioulekas
2016 conf
ECCV (3)
Ioannis Gkioulekas, Anat Levin, Todd E. Zickler
2016 conf
Measuring, Modeling, and Reproducing Material Appearance
Ioannis Gkioulekas, Kavita Bala, Frédo Durand, Anat Levin, Shuang Zhao, Todd E. Zickler
2015 J jnl
ACM Trans. Graph.
Ioannis Gkioulekas, Anat Levin, Frédo Durand, Todd E. Zickler
2015 A* conf
CVPR
Ioannis Gkioulekas, Bruce Walter, Edward H. Adelson, Kavita Bala, Todd E. Zickler
2013 J jnl
ACM Trans. Graph.
Ioannis Gkioulekas, Shuang Zhao, Kavita Bala, Todd E. Zickler, Anat Levin
2013 J jnl
IEEE Trans. Pattern Anal. Mach. Intell.
Sanjeev J. Koppal, Ioannis Gkioulekas, Travis Young, Hyunsung Park, Kenneth B. Crozier, Geoffrey L. Barrows, Todd E. Zickler
2013 J jnl
ACM Trans. Graph.
Ioannis Gkioulekas, Bei Xiao, Shuang Zhao, Edward H. Adelson, Todd E. Zickler, Kavita Bala
2011 conf
NIPS
Ioannis Gkioulekas, Todd E. Zickler
2011 A* conf
CVPR
Sanjeev J. Koppal, Ioannis Gkioulekas, Todd E. Zickler, Geoffrey L. Barrows
2010 B conf
ICIP
Ioannis Gkioulekas, Georgios Evangelopoulos, Petros Maragos
redb/extractors/decompiler/DecompileAPK.py
← Index redb/extractors/decompiler/DecompileAPK.py python
"""APK Code Analysis Extractor.

Decompiles and disassembles APK DEX bytecode at the method level,
producing per-method content and reference records analogous to
the Binary Ninja code_binja_* tables.

Uses androguard + JADX + apktool to replicate Binary Ninja analysis
depth for Android applications.
"""

import gc
import hashlib
import inspect
import logging
import os
import threading
import time
from datetime import datetime, timezone
from typing import Any, Dict, Optional

from redb.extractors.decompiler.apk.analyzer import APKCodeAnalyzer
from redb.extractors.enum import Tag
from redb.extractors.extractor import Extractor


class DecompileAPK(Extractor):
    """APK code analysis extractor — produces multi-table ClickHouse export.

    Follows the same pattern as DecompileBinja for consistency.
    """

    def __init__(
        self,
        filepath,
        log,
        exporters=None,
        index_prefix=None,
        known_benign=False,
        known_malicious=False,
        filetype=None,
        decompile_modules=None,
    ):
        super().__init__(
            filepath,
            log,
            exporters,
            index_prefix,
            known_benign=known_benign,
            known_malicious=known_malicious,
        )
        self.log.debug(inspect.currentframe().f_code.co_name)
        self.analysis_results = None
        self.analyzer = None
        self.filetype = filetype or "apk"
        self.decompile_modules = decompile_modules or {"all"}

        try:
            self.APK_DECOMPILE_TIMEOUT = int(
                os.getenv("APK_DECOMPILE_TIMEOUT", "600")
            )
        except ValueError:
            self.log.warning(
                "Invalid APK_DECOMPILE_TIMEOUT value, using default of 600 seconds"
            )
            self.APK_DECOMPILE_TIMEOUT = 600

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cleanup_run()

    def calculate_md5(self, input_str):
        """Calculate MD5 hash of a string."""
        return hashlib.md5(input_str.encode("utf-8")).hexdigest()

    def cleanup_run(self):
        """Clean up after analysis."""
        try:
            if self.analyzer:
                self.analyzer.cleanup()
                self.analyzer = None
            gc.collect()
        except Exception as e:
            self.log.error(f"Error in cleanup: {e}")

    def analyze_apk(self) -> Optional[Dict[str, Any]]:
        """Run APK code analysis and return results."""
        self.log.debug("Starting APK code analysis")
        try:
            self.analyzer = APKCodeAnalyzer(
                filepath=self.filepath,
                timeout=self.APK_DECOMPILE_TIMEOUT,
                log=self.log,
                decompile_modules=self.decompile_modules,
            )
            results = self.analyzer.extract()
            return results
        except Exception as e:
            self.log.error(f"Error in APK code analysis: {e}")
            import traceback
            self.log.error(f"Traceback: {traceback.format_exc()}")
            return None
        finally:
            self.cleanup_run()

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

        Uses daemon thread with timeout, same pattern as DecompileBinja.
        """
        self.log.debug(inspect.currentframe().f_code.co_name)

        extraction_completed = False
        extraction_result = False
        extraction_error = None

        def do_extraction():
            nonlocal extraction_completed, extraction_result, extraction_error
            try:
                results = self.analyze_apk()
                if not results:
                    extraction_result = False
                else:
                    self.analysis_results = results
                    self.analysis_results["sha256"] = self.sha256
                    self.analysis_results["sha1"] = self.sha1
                    self.analysis_results["md5"] = self.md5
                    extraction_result = True
            except Exception as e:
                extraction_error = e
                extraction_result = False
            finally:
                extraction_completed = True

        extraction_thread = threading.Thread(target=do_extraction)
        extraction_thread.daemon = True
        extraction_thread.start()

        start_time = time.time()
        while (
            not extraction_completed
            and (time.time() - start_time) < self.APK_DECOMPILE_TIMEOUT
        ):
            time.sleep(1)

        if not extraction_completed:
            self.log.error(
                f"APK extraction timed out after {self.APK_DECOMPILE_TIMEOUT} seconds"
            )
            self.cleanup_run()
            return None

        if extraction_error:
            self.log.error(f"Error in APK extraction: {extraction_error}")
            return None

        return self.analysis_results if extraction_result else None

    def prepare_export_data(self, exporter_type: str) -> Any:
        """Prepare data for database export."""
        self.log.debug(inspect.currentframe().f_code.co_name)
        if not self.analysis_results:
            return None

        if exporter_type == "ClickHouseExporter":
            now = datetime.now(timezone.utc)
            export = {"multi_table": True}

            # Table 1: Decompiled method content
            if self.analysis_results.get("decompiled_content"):
                export["decompiled_content"] = {
                    "table": "code_apk_decompiled_methods_content",
                    "data": [
                        [
                            f["decompiled_method_hash"],
                            f["decompiled_method"],
                            f.get("decompiled_method_type", "UNKNOWN"),
                            1 if f.get("decompiled_has_string_encryption") else 0,
                            1 if f.get("decompiled_has_reflection_calls") else 0,
                            1 if f.get("decompiled_excessive_goto_count") else 0,
                            now,
                        ]
                        for f in self.analysis_results["decompiled_content"]
                    ],
                    "column_names": [
                        "decompiled_method_hash",
                        "decompiled_method",
                        "decompiled_method_type",
                        "decompiled_has_string_encryption",
                        "decompiled_has_reflection_calls",
                        "decompiled_excessive_goto_count",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "String",
                        "Enum8('USER'=1, 'LIBRARY'=2, 'UNKNOWN'=5)",
                        "UInt8",
                        "UInt8",
                        "UInt8",
                        "DateTime64(3, 'UTC')",
                    ],
                }

            # Table 2: Decompiled method references
            if self.analysis_results.get("decompiled_refs"):
                export["decompiled_refs"] = {
                    "table": "code_apk_decompiled_methods_references",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            f["decompiled_method_hash"],
                            f.get("smali_method_hash"),
                            f.get("decompiled_class_name", ""),
                            f.get("decompiled_method_name", ""),
                            f.get("decompiled_method_signature", ""),
                            f.get("decompiled_method_prototype", ""),
                            f.get("functions_caller", []),
                            f.get("functions_call", []),
                            now,
                        ]
                        for f in self.analysis_results["decompiled_refs"]
                    ],
                    "column_names": [
                        "sha256",
                        "decompiled_method_hash",
                        "smali_method_hash",
                        "decompiled_class_name",
                        "decompiled_method_name",
                        "decompiled_method_signature",
                        "decompiled_method_prototype",
                        "functions_caller",
                        "functions_call",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(64)",
                        "Nullable(FixedString(64))",
                        "LowCardinality(String)",
                        "LowCardinality(String)",
                        "String",
                        "String",
                        "Array(String)",
                        "Array(String)",
                        "DateTime64(3, 'UTC')",
                    ],
                }

            # Table 3: Smali method content
            if self.analysis_results.get("smali_content"):
                export["smali_content"] = {
                    "table": "code_apk_smali_methods_content",
                    "data": [
                        [
                            f["smali_method_hash"],
                            f["smali_method"],
                            f.get("smali_method_type", "UNKNOWN"),
                            f.get("smali_instructions_count", 0),
                            f.get("smali_register_count", 0),
                            1 if f.get("smali_has_string_encryption") else 0,
                            1 if f.get("smali_has_reflection_calls") else 0,
                            1 if f.get("smali_excessive_goto_count") else 0,
                            f.get("smali_flattened_score", 0.0),
                            f.get("smali_mba_score", 0.0),
                            now,
                        ]
                        for f in self.analysis_results["smali_content"]
                    ],
                    "column_names": [
                        "smali_method_hash",
                        "smali_method",
                        "smali_method_type",
                        "smali_instructions_count",
                        "smali_register_count",
                        "smali_has_string_encryption",
                        "smali_has_reflection_calls",
                        "smali_excessive_goto_count",
                        "smali_flattened_score",
                        "smali_mba_score",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "String",
                        "Enum8('USER'=1, 'LIBRARY'=2, 'UNKNOWN'=5)",
                        "UInt32",
                        "UInt16",
                        "UInt8",
                        "UInt8",
                        "UInt8",
                        "Float64",
                        "Float64",
                        "DateTime64(3, 'UTC')",
                    ],
                }

            # Table 4: Smali method references
            if self.analysis_results.get("smali_refs"):
                export["smali_refs"] = {
                    "table": "code_apk_smali_methods_references",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            f["smali_method_hash"],
                            f.get("decompiled_method_hash"),
                            f.get("smali_class_name", ""),
                            f.get("smali_method_name", ""),
                            f.get("smali_method_signature", ""),
                            now,
                        ]
                        for f in self.analysis_results["smali_refs"]
                    ],
                    "column_names": [
                        "sha256",
                        "smali_method_hash",
                        "decompiled_method_hash",
                        "smali_class_name",
                        "smali_method_name",
                        "smali_method_signature",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(64)",
                        "Nullable(FixedString(64))",
                        "LowCardinality(String)",
                        "LowCardinality(String)",
                        "String",
                        "DateTime64(3, 'UTC')",
                    ],
                }

            # Table 5: Method similarity metrics (content-based fuzzy matching)
            if self.analysis_results.get("similarity_metrics"):
                export["method_similarity_metrics"] = {
                    "table": "code_apk_method_similarity_metrics",
                    "data": [
                        [
                            f["smali_method_hash"],
                            f.get("ssdeep_smali"),
                            f.get("tlsh_smali"),
                            f.get("ssdeep_smali_normalized"),
                            f.get("tlsh_smali_normalized"),
                            f.get("minhash", []),
                            now,
                        ]
                        for f in self.analysis_results["similarity_metrics"]
                    ],
                    "column_names": [
                        "smali_method_hash",
                        "ssdeep_smali",
                        "tlsh_smali",
                        "ssdeep_smali_normalized",
                        "tlsh_smali_normalized",
                        "minhash",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "Nullable(String)",
                        "Nullable(FixedString(72))",
                        "Nullable(String)",
                        "Nullable(FixedString(72))",
                        "Array(UInt8)",
                        "DateTime64(3, 'UTC')",
                    ],
                }

            # Table 5b: CFG method features (structural/topological)
            if self.analysis_results.get("cfg"):
                export["cfg_methods"] = {
                    "table": "code_apk_cfg_methods",
                    "data": [
                        [
                            cfg["smali_method_hash"],
                            cfg["cfg_topology_hash"],
                            cfg["block_count"],
                            cfg["edge_count"],
                            cfg.get("cfg_instructions_count", 0),
                            cfg.get("call_count", 0),
                            cfg["cyclomatic_complexity"],
                            cfg.get("loop_count", 0),
                            cfg.get("max_depth", 0),
                            cfg.get("max_fan_out", 0),
                            cfg.get("md_index_topdown", 0),
                            cfg.get("md_index_bottomup", 0),
                            cfg.get("prime_product_smali", 0),
                            cfg.get("cfg_feature_tlsh"),
                            cfg.get("wl_minhash", []),
                            cfg.get("bb_features", []),
                            cfg.get("cfg_adjacency", []),
                            now,
                        ]
                        for cfg in self.analysis_results["cfg"]
                        if cfg is not None
                    ],
                    "column_names": [
                        "smali_method_hash",
                        "cfg_topology_hash",
                        "block_count",
                        "edge_count",
                        "cfg_instructions_count",
                        "call_count",
                        "cyclomatic_complexity",
                        "loop_count",
                        "max_depth",
                        "max_fan_out",
                        "md_index_topdown",
                        "md_index_bottomup",
                        "prime_product_smali",
                        "cfg_feature_tlsh",
                        "wl_minhash",
                        "bb_features",
                        "cfg_adjacency",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(16)",
                        "UInt16",
                        "UInt16",
                        "UInt32",
                        "UInt16",
                        "UInt16",
                        "UInt16",
                        "UInt16",
                        "UInt16",
                        "UInt64",
                        "UInt64",
                        "UInt64",
                        "Nullable(FixedString(72))",
                        "Array(UInt8)",
                        "Array(Array(UInt16))",
                        "Array(UInt32)",
                        "DateTime64(3, 'UTC')",
                    ],
                }

            # Table 6: Strings — reuse code_binja_strings_raw for cross-format correlation
            # DEX strings are MUTF-8; string_raw = string since no encoding difference
            if self.analysis_results.get("strings"):
                export["strings_raw"] = {
                    "table": "code_binja_strings_raw",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            s["string"],
                            s["string"],  # string_raw = string (MUTF-8 decoded to UTF-8)
                            s.get("string_encoding", "UTF8"),
                            s.get("string_offset", 0),
                            s.get("string_length", len(s["string"])),
                            s.get("string_length", len(s["string"])),  # string_raw_length = string_length
                            s.get("string_entropy", 0.0),
                        ]
                        for s in self.analysis_results["strings"]
                    ],
                    "column_names": [
                        "sha256",
                        "string",
                        "string_raw",
                        "string_encoding",
                        "string_offset",
                        "string_length",
                        "string_raw_length",
                        "string_entropy",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "String",
                        "String",
                        "LowCardinality(String)",
                        "UInt64",
                        "UInt32",
                        "UInt32",
                        "Float32",
                    ],
                }

            # Table 7: Analysis errors
            if self.analysis_results.get("analysis_errors"):
                export["analysis_errors"] = {
                    "table": "code_apk_analysis_errors",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            f.get("class_name"),
                            f.get("method_name"),
                            f.get("error_location", "unknown"),
                            f.get("error_message", ""),
                            f.get("error_type", "unknown"),
                            self.calculate_md5(
                                f"{f.get('error_message', '')}"
                                f"{f.get('class_name', '')}"
                                f"{f.get('method_name', '')}"
                                f"{f.get('error_location', 'unknown')}"
                            ),
                            "new",
                            now,
                        ]
                        for f in self.analysis_results["analysis_errors"]
                    ],
                    "column_names": [
                        "sha256",
                        "class_name",
                        "method_name",
                        "error_location",
                        "error_message",
                        "error_type",
                        "error_hash",
                        "status",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "Nullable(String)",
                        "Nullable(String)",
                        "LowCardinality(String)",
                        "Nullable(String)",
                        "Nullable(String)",
                        "FixedString(32)",
                        "Enum8('new'=1, 'investigating'=2, 'fixed'=3, 'wontfix'=4)",
                        "DateTime64(3, 'UTC')",
                    ],
                }

            return export

        return None

    def tag(self) -> str:
        """Return the tag for this extractor."""
        return Tag.APK_DECOMPILED.value

    def get_clickhouse_table(self) -> str:
        """Not used directly as we're handling multiple tables."""
        pass