Jan Aerts

48 papers B 1C 1Misc 1Journal 35Unranked 10
YearRankTypeTitle / Venue / Authors
2025 J jnl
PeerJ Comput. Sci.
Daniël M. Bot, Jannes Peeters, Jori Liesenborgs, Jan Aerts
2025 J jnl
CoRR
Daniël Bot, Leland McInnes, Jan Aerts
2025 J jnl
Briefings Bioinform.
Dries Heylen, Murih Pusparum, Jurgis Kuliesius, Jim Wilson, Young-Chan Park, Jacek Jamiolkowski, Valentino D'onofrio, Dirk Valkenborg, Jan Aerts, Gökhan Ertaylan, Jef Hooyberghs
2024 J jnl
IEEE Trans. Vis. Comput. Graph.
Benjamin Bach, Mandy Keck, Fateme Rajabiyazdi, Tatiana Losev, Isabel Meirelles, Jason Dykes, Robert S. Laramee, Mashael AlKadi, Christina Stoiber, Samuel Huron, Charles Perin, Luiz Morais, Wolfgang Aigner, Doris Kosminsky, Magdalena Boucher, Søren Knudsen, Areti Manataki, Jan Aerts, Uta Hinrichs, Jonathan C. Roberts, Sheelagh Carpendale
2024 J jnl
CoRR
Daniël M. Bot, Jan Aerts
2024 conf
EuroVis (Posters)
Daniël M. Bot, Peiyang Huo, Alessio Arleo, Fernando V. Paulovich, Jan Aerts
2024 J jnl
Frontiers Bioinform.
Jannes Peeters, Daniël M. Bot, Gustavo Alberto Rovelo Ruiz, Jan Aerts
2023 J jnl
CoRR
Benjamin Bach, Mandy Keck, Fateme Rajabiyazdi, Tatiana Losev, Isabel Meirelles, Jason Dykes, Robert S. Laramee, Mashael AlKadi, Christina Stoiber, Samuel Huron, Charles Perin, Luiz Morais, Wolfgang Aigner, Doris Kosminsky, Magdalena Boucher, Søren Knudsen, Areti Manataki, Jan Aerts, Uta Hinrichs, Jonathan C. Roberts, Sheelagh Carpendale
2023 J jnl
CoRR
Daniël M. Bot, Jannes Peeters, Jori Liesenborgs, Jan Aerts
2023 conf
EuroVis (Posters)
Daniël M. Bot, Jannes Peeters, Jan Aerts
2022 J jnl
Comput. Support. Cooperative Work.
Georgia Panagiotidou, Jeroen Poblome, Jan Aerts, Andrew Vande Moere
2022 conf
EuroVis (Posters)
Dries Heylen, Jannes Peeters, Gökhan Ertaylan, Jef Hooyberghs, Jan Aerts
2021 J jnl
PeerJ Comput. Sci.
Daniel Alcaide, Jan Aerts
2021 J jnl
Frontiers Bioinform.
Jannes Peeters, Olivier Thas, Ziv Shkedy, Leyla Kodalci, Connie Musisi, Olajumoke Evangelina Owokotomo, Aleksandra Dyczko, Ibrahim Hamad, Jaco Vangronsveld, Markus Kleinewietfeld, Sofie Thijs, Jan Aerts
2021 J jnl
IEEE Computer Graphics and Applications
Jan Aerts, Jannes Peeters, Jelmer Bot, Danai Kafetzaki, Houda Lamqaddam
2021 J jnl
IEEE Trans. Vis. Comput. Graph.
Daniel Alcaide, Jan Aerts
2021 conf
IUI Companion
Robin De Croon, Artuur Leeuwenberg, Jan Aerts, Marie-Francine Moens, Vero Vanden Abeele, Katrien Verbert
2019 J jnl
Bioinform.
Thomas Moerman, Sara Aibar Santos, Carmen Bravo González-Blas, Jaak Simm, Yves Moreau, Jan Aerts, Stein Aerts
2019 J jnl
CoRR
Daniel Alcaide, Jan Aerts
2018 J jnl
PeerJ Comput. Sci.
Daniel Alcaide, Jan Aerts
2018 conf
VAST
Daniel Alcaide, Jan Aerts
2018 Misc conf
NordiCHI
Houda Lamqaddam, Jan Aerts, Koenraad Brosens, Katrien Verbert
2018 J jnl
Dagstuhl Reports
Jan Aerts, Nils Gehlenborg, Georgeta Elisabeta Marai, Kay Katja Nieselt
2017 conf
EuroVis (Posters)
Daniel Alcaide, Jan Aerts
2017 conf
EuroVis (Posters)
Thomas De Bodt, Bart Adams, Jan Aerts
2017 J jnl
PeerJ Prepr.
Daniel Alcaide, Jan Aerts
2016 J jnl
Nucleic Acids Res.
Léon-Charles Tranchevent, Amin Ardeshirdavani, Sarah ElShal, Daniel Alcaide, Jan Aerts, Didier Auboeuf, Yves Moreau
2016 conf
EuroVis (Posters)
Houda Lamqaddam, Jan Aerts
2015 conf
EuroVis (Short Papers)
Ryo Sakai, Jan Aerts
2015 J jnl
BMC Bioinform.
Jan Aerts, G. Elisabeta Marai, Kay Nieselt, Cydney B. Nielsen, Marc Streit, Daniel Weiskopf
2014 B conf
CBMS
Dusan Popovic, Charalampos N. Moschopoulos, Ryo Sakai, Alejandro Sifrim, Jan Aerts, Yves Moreau, Bart De Moor
2014 conf
IEEE VAST
Alvin Chua, Ryo Sakai, Jan Aerts, Andrew Vande Moere
2014 J jnl
J. Biomed. Semant.
Toshiaki Katayama, Mark D. Wilkinson, Kiyoko F. Aoki-Kinoshita, Shuichi Kawashima, Yasunori Yamamoto, Atsuko Yamaguchi, Shinobu Okamoto, Shin Kawano, Jin-Dong Kim, Yue Wang, Hongyan Wu, Yoshinobu Kano, Hiromasa Ono, Hidemasa Bono, Simon Kocbek, Jan Aerts, Yukie Akune, Erick Antezana, Kazuharu Arakawa, Bruno Aranda, Joachim Baran, Jerven T. Bolleman, Raoul Jean Pierre Bonnal, Pier Luigi Buttigieg, Matthew P. Campbell, Yi-An Chen, Hirokazu Chiba, Peter J. A. Cock, Kevin Bretonnel Cohen, Alexandru Constantin
2013 J jnl
Bioinform.
Ryo Sakai, Matthieu Moisse, Joke Reumers, Jan Aerts
2013 J jnl
Bioinform.
Ryo Sakai, Alejandro Sifrim, Andrew Vande Moere, Jan Aerts
2013 J jnl
BioData Min.
Georgios A. Pavlopoulos, Anastasis Oulas, Ernesto Iacucci, Alejandro Sifrim, Yves Moreau, Reinhard Schneider, Jan Aerts, Ioannis Iliopoulos
2012 J jnl
BMC Bioinform.
Christopher W. Bartlett, Soo Yeon Cheong, Liping Hou, Jesse Paquette, Pek Yee Lum, Günter Jäger, Florian Battke, Corinna Vehlow, Julian Heinrich, Kay Nieselt, Ryo Sakai, Jan Aerts, William C. Ray
2012 J jnl
BMC Bioinform.
Maria Secrier, Georgios A. Pavlopoulos, Jan Aerts, Reinhard Schneider
2012 J jnl
Bioinform.
Raoul Jean Pierre Bonnal, Jan Aerts, George Githinji, Naohisa Goto, Daniel MacLean, Chase A. Miller, Hiroyuki Mishima, Massimiliano Pagani, Ricardo Ramirez-Gonzalez, Geert Smant, Francesco Strozzi, Rob Syme, Rutger A. Vos, Trevor J. Wennblom, Ben J. Woodcroft, Toshiaki Katayama, Pjotr Prins
2012 J jnl
Source Code Biol. Medicine
Cameron Neylon, Jan Aerts, C. Titus Brown, Simon J. Coles, Les Hatton, Daniel Lemire, K. Jarrod Millman, Peter Murray-Rust, Fernando Pérez, Neil F. W. Saunders, Nigam Shah, Arfon M. Smith, Gaël Varoquaux, Egon L. Willighagen
2012 J jnl
BMC Bioinform.
Hiroyuki Mishima, Jan Aerts, Toshiaki Katayama, Raoul Jean Pierre Bonnal, Koh-ichiro Yoshiura
2012 C conf
BIBE
Thomas Boogaerts, Léon-Charles Tranchevent, Georgios A. Pavlopoulos, Jan Aerts, Joos Vandewalle
2011 J jnl
Bioinform.
Francesco Strozzi, Jan Aerts
2011 J jnl
J. Biomed. Semant.
Toshiaki Katayama, Mark D. Wilkinson, Rutger A. Vos, Takeshi Kawashima, Shuichi Kawashima, Mitsuteru Nakao, Yasunori Yamamoto, Hong-Woo Chun, Atsuko Yamaguchi, Shin Kawano, Jan Aerts, Kiyoko F. Aoki-Kinoshita, Kazuharu Arakawa, Bruno Aranda, Raoul Jean Pierre Bonnal, José María Fernández, Takatomo Fujisawa, Paul M. K. Gordon, Naohisa Goto, Syed Haider, Todd W. Harris, Takashi Hatakeyama, Isaac Ho, Masumi Itoh, Arek Kasprzyk, Nobuhiro Kido, Young-Joo Kim, Akira R. Kinjo, Fumikazu Konishi, Yulia Kovarskaya, Gregory Von Kuster, Alberto Labarga, Vachiranee Limviphuvadh, E. Luke McCarthy, Yasukazu Nakamura, Yunsun Nam, Kozo Nishida, Kunihiro Nishimura, Tatsuya Nishizawa, Soichi Ogishima, Tom Oinn, Shinobu Okamoto, Shujiro Okuda, Keiichiro Ono, Kazuki Oshita, Keun-Joon Park, Nicholas H. Putnam, Martin Senger, Jessica Severin, Yasumasa Shigemoto, Hideaki Sugawara, James Taylor, Oswaldo Trelles, Chisato Yamasaki, Riu Yamashita, Noriyuki Satoh, Toshihisa Takagi
2011 J jnl
BioData Min.
Georgios A. Pavlopoulos, Maria Secrier, Charalampos N. Moschopoulos, Theodoros G. Soldatos, Sophia Kossida, Jan Aerts, Reinhard Schneider, Pantelis G. Bagos
2010 J jnl
Bioinform.
Naohisa Goto, Pjotr Prins, Mitsuteru Nakao, Raoul Jean Pierre Bonnal, Jan Aerts, Toshiaki Katayama
2010 J jnl
J. Biomed. Semant.
Toshiaki Katayama, Kazuharu Arakawa, Mitsuteru Nakao, Keiichiro Ono, Kiyoko F. Aoki-Kinoshita, Yasunori Yamamoto, Atsuko Yamaguchi, Shuichi Kawashima, Hong-Woo Chun, Jan Aerts, Bruno Aranda, Lord H. Barboza, Raoul Jean Pierre Bonnal, Richard M. Bruskiewich, Jan Christian Bryne, José María Fernández, Akira Funahashi, Paul M. K. Gordon, Naohisa Goto, Andreas Groscurth, Alex Gutteridge, Richard C. G. Holland, Yoshinobu Kano, Edward A. Kawas, Arnaud Kerhornou, Eri Kibukawa, Akira R. Kinjo, Michael Kuhn, Hilmar Lapp, Heikki Lehväslaiho, Hiroyuki Nakamura, Yasukazu Nakamura, Tatsuya Nishizawa, Chikashi Nobata, Tamotsu Noguchi, Thomas M. Oinn, Shinobu Okamoto, Stuart Owen, Evangelos Pafilis, Matthew R. Pocock, Pjotr Prins, René Ranzinger, Florian Reisinger, Lukasz Salwínski, Mark J. Schreiber, Martin Senger, Yasumasa Shigemoto, Daron M. Standley, Hideaki Sugawara, Toshiyuki Tashiro, Oswaldo Trelles, Rutger A. Vos, Mark D. Wilkinson, William S. York, Christian M. Zmasek, Kiyoshi Asai, Toshihisa Takagi
2009 J jnl
BMC Bioinform.
Jan Aerts, Andy Law
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