Wei Le

67 papers A* 15A 4Journal 33Unranked 15
YearRankTypeTitle / Venue / Authors
2026 J jnl
CoRR
Md Mahbubur Rahman, Hengbo Tong, Wei Le
2025 J jnl
CoRR
Shaila Sharmin, Anwar Hossain Zahid, Subhankar Bhattacharjee, Chiamaka Igwilo, Miryung Kim, Wei Le
2025 J jnl
Proc. ACM Softw. Eng.
Shaila Sharmin, Anwar Hossain Zahid, Subhankar Bhattacharjee, Chiamaka Igwilo, Miryung Kim, Wei Le
2025 A* conf
ICSE
Benjamin Steenhoek, Kalpathy Sivaraman, Renata Saldivar Gonzalez, Yevhen Mohylevskyy, Roshanak Zilouchian Moghaddam, Wei Le
2025 J jnl
CoRR
Monoshi Kumar Roy, Simin Chen, Benjamin Steenhoek, Jinjun Peng, Gail E. Kaiser, Baishakhi Ray, Wei Le
2024 J jnl
CoRR
Benjamin Steenhoek, Md Mahbubur Rahman, Monoshi Kumar Roy, Mirza Sanjida Alam, Earl T. Barr, Wei Le
2024 J jnl
CoRR
Benjamin Steenhoek, Kalpathy Sivaraman, Renata Saldivar Gonzalez, Yevhen Mohylevskyy, Roshanak Zilouchian Moghaddam, Wei Le
2024 A* conf
ICSE
Benjamin Steenhoek, Hongyang Gao, Wei Le
2024 conf
ICBAR
Hengxiang Ye, Wei Le, Wanqing Liu
2024 J jnl
Ann. Oper. Res.
Xiaoyang Yao, Enmeng Liu, Xiaolei Sun, Wei Le, Jianping Li
2024 J jnl
CoRR
Samrajya Thapa, Koushik Howlader, Subhankar Bhattacharjee, Wei Le
2024 A* conf
ICSE
Yangruibo Ding, Benjamin Steenhoek, Kexin Pei, Gail E. Kaiser, Wei Le, Baishakhi Ray
2024 conf
SC Workshops
Anwar Hossain Zahid, Ignacio Laguna, Wei Le
2024 J jnl
CoRR
Anwar Hossain Zahid, Ignacio Laguna, Wei Le
2024 A* conf
ICSE
Md Mahbubur Rahman, Ira Ceka, Chengzhi Mao, Saikat Chakraborty, Baishakhi Ray, Wei Le
2024 conf
SBCCI
Zhenghan Fang, Lirida Naviner, Wen Wang, Wei Le, Hao Cai
2023 J jnl
CoRR
Xiuyuan Guo, Ashwin Kallingal Joshy, Benjamin Steenhoek, Wei Le, Lori Flynn
2023 J jnl
CoRR
Ashwin Kallingal Joshy, Mirza Sanjida Alam, Shaila Sharmin, Qi Li, Wei Le
2023 J jnl
CoRR
Mohammad Wardat, Breno Dantas Cruz, Wei Le, Hridesh Rajan
2023 A* conf
ICSE
Benjamin Steenhoek, Md Mahbubur Rahman, Richard Jiles, Wei Le
2023 J jnl
CoRR
Adithya Kulkarni, Mohna Chakraborty, Yonas Sium, Sai Charishma Valluri, Wei Le, Qi Li
2023 conf
ICANN (10)
Eliska Kloberdanz, Wei Le
2023 J jnl
CoRR
Benjamin Steenhoek, Md Mahbubur Rahman, Shaila Sharmin, Wei Le
2023 conf
ICBAR
Wanqing Liu, Wei Le
2023 A* conf
ICSE
Jiongchi Yu, Yuechen Wu, Xiaofei Xie, Wei Le, Lei Ma, Yingfeng Chen, Jingyu Hu, Fan Zhang
2023 J jnl
CoRR
Eliska Kloberdanz, Wei Le
2023 J jnl
J. Supercomput.
Taghreed Bagies, Wei Le, Jeremy Sheaffer, Ali Jannesari
2023 J jnl
CoRR
Ashwin Kallingal Joshy, Benjamin Steenhoek, Xiuyuan Guo, Wei Le
2023 conf
ICANN (9)
Eliska Kloberdanz, Wei Le
2023 J jnl
CoRR
Yangruibo Ding, Benjamin Steenhoek, Kexin Pei, Gail E. Kaiser, Wei Le, Baishakhi Ray
2023 J jnl
CoRR
Md Mahbubur Rahman, Ira Ceka, Chengzhi Mao, Saikat Chakraborty, Baishakhi Ray, Wei Le
2023 A* conf
KDD
Wei Qian, Chenxu Zhao, Wei Le, Meiyi Ma, Mengdi Huai
2022 J jnl
CoRR
Benjamin Steenhoek, Md Mahbubur Rahman, Richard Jiles, Wei Le
2022 J jnl
CoRR
Benjamin Steenhoek, Wei Le, Hongyang Gao
2022 A* conf
ICSE
Mohammad Wardat, Breno Dantas Cruz, Wei Le, Hridesh Rajan
2022 A* conf
ICSE
Eliska Kloberdanz, Kyle G. Kloberdanz, Wei Le
2022 J jnl
CoRR
Eliska Kloberdanz, Kyle G. Kloberdanz, Wei Le
2022 A* conf
ASE
Ashwin Kallingal Joshy, Wei Le
2022 J jnl
CoRR
Ashwin Kallingal Joshy, Wei Le
2021 conf
ICANN (1)
Eliska Kloberdanz, Jin Tian, Wei Le
2021 J jnl
CoRR
Mohammad Wardat, Breno Dantas Cruz, Wei Le, Hridesh Rajan
2021 A* conf
ICSE
Mohammad Wardat, Wei Le, Hridesh Rajan
2021 J jnl
CoRR
Mohammad Wardat, Wei Le, Hridesh Rajan
2021 A* conf
ICML
Xiaofei Xie, Wenbo Guo, Lei Ma, Wei Le, Jian Wang, Lingjun Zhou, Yang Liu, Xinyu Xing
2021 J jnl
IEEE Trans. Software Eng.
Danilo Dominguez Perez, Wei Le
2021 J jnl
CoRR
Ashwin Kallingal Joshy, Xueyuan Chen, Benjamin Steenhoek, Wei Le
2021 A conf
ISSTA
Ashwin Kallingal Joshy, Xueyuan Chen, Benjamin Steenhoek, Wei Le
2019 J jnl
IEEE Trans. Software Eng.
Xiaofei Xie, Bihuan Chen, Liang Zou, Yang Liu, Wei Le, Xiaohong Li
2019 J jnl
CoRR
Ashwin Kallingal Joshy, Wei Le
2019 conf
ICMSS
Donghui He, Wei Le, Benhai Guo, Hongjuan Yin
2019 J jnl
CoRR
Danilo Dominguez Perez, Wei Le
2017 conf
MOBILESoft@ICSE
Danilo Dominguez Perez, Wei Le
2017 J jnl
CoRR
Danilo Dominguez Perez, Wei Le
2017 conf
ICSE (Companion Volume)
Danilo Dominguez Perez, Wei Le
2016 A* conf
ICSE
Bihuan Chen, Yang Liu, Wei Le
2016 conf
SIGSOFT FSE
Xiaofei Xie, Bihuan Chen, Yang Liu, Wei Le, Xiaohong Li
2015 A conf
ISSTA
Xiaofei Xie, Yang Liu, Wei Le, Xiaohong Li, Hongxu Chen
2014 A conf
MSR
Daniel E. Krutz, Wei Le
2014 A* conf
ICSE
Wei Le, Shannon D. Pattison
2013 J jnl
ACM Trans. Softw. Eng. Methodol.
Wei Le, Mary Lou Soffa
2013 A* conf
ICSE
Wei Le
2011 A conf
ISSTA
Wei Le, Mary Lou Soffa
2011 conf
SESENA@ICSE
Wei Le, Jing Yang, Mary Lou Soffa, Kamin Whitehouse
2010 conf
SIGSOFT FSE
Wei Le, Mary Lou Soffa
2008 conf
SIGSOFT FSE
Wei Le, Mary Lou Soffa
2007 conf
PASTE
Wei Le, Mary Lou Soffa
2005 J jnl
IEEE Trans. Software Eng.
David Coppit, Jinlin Yang, Sarfraz Khurshid, Wei Le, Kevin J. Sullivan
redb/extractors/apk_extractors/apk_resources.py
← Index redb/extractors/apk_extractors/apk_resources.py python
import hashlib
import inspect
import os
from datetime import datetime, timezone
from typing import Any

from redb.extractors.enum import Tag
from redb.extractors.apk_extractor import APKExtractor
from redb.models.dataclasses import APKResource


# ─── Suspicious file types ──────────────────────────────────────────────
# File types that are suspicious when found inside res/ or assets/.
# Excludes javascript/html (extremely common in legitimate hybrid apps)
# and common media/font types that are normal APK content.
SUSPICIOUS_TYPES = {
    # Executables — no legitimate reason in assets/res
    "elf", "pebin", "macho", "dex", "apk",
    # Java containers — DexClassLoader target
    "jar",
    # Archives — rare in legitimate assets (~135:1 malware-to-benign ratio)
    "zip", "gzip", "7z", "xz", "tar", "bzip2", "rar", "7zip", "lzma",
    # Scripts with system execution capability
    "shell", "python", "powershell", "batch",
}

# ─── Entropy thresholds ─────────────────────────────────────────────────
# For unrecognized/unknown types: encrypted payloads typically land > 7.0
ENTROPY_HIGH_UNKNOWN = 7.0
# For recognized-but-non-image types: stricter threshold
ENTROPY_EXTREME = 7.85

# ─── Android-specific binary format magic bytes ─────────────────────────
# These formats are common in legitimate APKs but unknown to Magika,
# causing misclassification (e.g., AXML → "gzip", profm → "unknown").
AXML_MAGIC = b'\x03\x00\x08\x00'       # Android Binary XML (compiled res/*.xml)
ARSC_MAGIC = b'\x02\x00\x0c\x00'       # Android compiled resource table
ART_PROF_MAGIC = b'pro\x00'            # ART baseline profile
ART_PROFM_MAGIC = b'prm\x00'           # ART baseline profile metadata

# ─── Allowlisted paths ──────────────────────────────────────────────────
# Fixed, hardcoded paths in the Android build system that are always benign.
# ART profiles at these exact paths are shipped by Jetpack ProfileInstaller.
ALLOWLISTED_PATHS = {
    "assets/dexopt/baseline.prof",
    "assets/dexopt/baseline.profm",
}

# ─── Image handling ─────────────────────────────────────────────────────
# Magika-confirmed image types: high entropy is expected (lossy codecs
# like VP8/JPEG arithmetic-code toward entropy ~7.95-8.0 by design).
IMAGE_MAGIKA_TYPES = {"png", "webp", "jpeg", "gif", "bmp", "tiff", "ico"}
IMAGE_EXTENSIONS = {".png", ".webp", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico"}

# ─── Types Magika assigns when it can't identify the content ────────────
UNRECOGNIZED_MAGIKA_TYPES = {"unknown", "empty"}

# ─── Resource scan limits ───────────────────────────────────────────────
MAX_RESOURCE_FILES = 5000


class APKResourceExtractor(APKExtractor):

    def __init__(
        self, filepath, log, exporters=None, index_prefix=None,
        known_benign=False, known_malicious=False,
        apk=None,
    ):
        super().__init__(
            filepath, log, exporters, index_prefix,
            known_benign, known_malicious, apk,
        )
        self.resources = []
        self.suspicious_files = []
        self.log.debug(inspect.currentframe().f_code.co_name)

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

    # ─── Core classification logic ──────────────────────────────────────

    def _identify_android_format(self, header: bytes) -> str | None:
        """
        Identify Android-specific binary formats that Magika doesn't know.
        Returns a corrected type label, or None to fall through to Magika.
        """
        if len(header) < 4:
            return None

        magic4 = header[:4]

        # Android Binary XML — all res/*.xml in a compiled APK.
        # Magika often misclassifies this as "gzip".
        if magic4 == AXML_MAGIC:
            return "android_binary_xml"

        # Android compiled resource table (resources.arsc chunks)
        if magic4 == ARSC_MAGIC:
            return "android_resource_table"

        # ART baseline profiles — high entropy (zlib inside) but benign.
        # The format is inert (method reference bitmaps/metadata, not
        # executable code) and some build configs place them at varying paths.
        if magic4 == ART_PROF_MAGIC:
            return "android_art_profile"
        if magic4 == ART_PROFM_MAGIC:
            return "android_art_profile_metadata"

        return None

    def _is_suspicious_resource(
        self, path: str, magika_type: str, entropy: float,
        android_type: str | None,
    ) -> bool:
        """
        Determine if a resource file is suspicious.

        Detection layers:
        1. Allowlisted paths → always benign
        2. Android-specific format override → reclassify Magika mislabels
        3. Image extension vs Magika type mismatch → encrypted blob detection
        4. Magika-confirmed images → benign regardless of entropy
        5. Suspicious type match → flag known-dangerous types
        6. High-entropy unknown blobs → likely encrypted payloads
        """

        # ── Layer 1: Allowlisted paths (hardcoded Android build artifacts) ──
        if path in ALLOWLISTED_PATHS:
            return False

        # ── Layer 2: Android-specific format detection ──────────────────────
        # Override Magika's label for formats it doesn't recognize.
        # All Android-specific formats (AXML, ARSC, ART profiles) are
        # legitimate build artifacts — never suspicious.
        if android_type is not None:
            return False

        # ── Layer 3: Image extension / Magika type mismatch ─────────────────
        # If the file extension claims "image" but Magika's content analysis
        # disagrees, this is a strong signal for an encrypted payload with
        # a fake image extension (e.g., ErrorFather's "rbyypivsnw.png").
        ext = os.path.splitext(path)[1].lower()
        if ext in IMAGE_EXTENSIONS and magika_type not in IMAGE_MAGIKA_TYPES:
            # Exception: Magika might label a valid image as "unknown" if
            # the file is very small (< ~16 bytes). Don't flag tiny files.
            if entropy > 5.0:
                return True

        # ── Layer 4: Magika-confirmed images → benign ───────────────────────
        # Lossy codecs (VP8, JPEG) produce entropy up to ~8.0 by design.
        # If Magika confirms image structure, high entropy is expected.
        if magika_type in IMAGE_MAGIKA_TYPES:
            return False

        # ── Layer 5: Known suspicious file types ────────────────────────────
        if magika_type in SUSPICIOUS_TYPES:
            return True

        # ── Layer 6: High-entropy unrecognized blobs ────────────────────────
        # Files Magika can't identify with high entropy are likely encrypted
        # payloads. Most Android malware packers store encrypted DEX/SO
        # payloads as opaque blobs with random names and no valid magic.
        if magika_type in UNRECOGNIZED_MAGIKA_TYPES and entropy > ENTROPY_HIGH_UNKNOWN:
            return True

        # ── Layer 7: Extreme entropy on any non-image recognized type ───────
        # Catches edge cases where Magika assigns a benign label (e.g.,
        # "xml", "txt") but the entropy is impossibly high for that format.
        if magika_type not in IMAGE_MAGIKA_TYPES and entropy > ENTROPY_EXTREME:
            return True

        return False

    # ─── Extraction pipeline ────────────────────────────────────────────

    def extract(self):
        if not self._is_valid_apk():
            self.log.error(f"Invalid APK for {self.hash.sha256}")
            return None

        try:
            from magika import Magika
            magika = Magika()
        except Exception as e:
            self.log.error(f"Failed to initialize Magika for {self.hash.sha256}: {e}")
            magika = None

        self.resources = []
        self.suspicious_files = []
        scanned = 0

        zf = self._get_zip_file()
        if not zf:
            return None

        with zf:
            for info in zf.infolist():
                if info.is_dir():
                    continue
                if not (info.filename.startswith("res/") or
                        info.filename.startswith("assets/")):
                    continue

                if scanned >= MAX_RESOURCE_FILES:
                    self.log.warning(
                        f"Resource scan limit reached ({MAX_RESOURCE_FILES}), "
                        f"stopping resource enumeration"
                    )
                    break
                scanned += 1

                try:
                    data = zf.read(info.filename)
                except Exception as e:
                    self.log.warning(
                        f"Error reading resource {info.filename}: {e}"
                    )
                    continue

                try:
                    file_sha256 = hashlib.sha256(data).hexdigest()
                    file_entropy = round(self.calculate_entropy(data), 3)

                    # Read first bytes for Android-specific format detection
                    header = data[:16] if len(data) >= 16 else data

                    if magika:
                        try:
                            filetype = magika.identify_bytes(data).output.label
                        except Exception:
                            filetype = "unknown"
                    else:
                        filetype = "unknown"

                    # Identify Android-specific formats once, reuse for
                    # both stored type and suspicion classification
                    android_type = self._identify_android_format(header)
                    stored_type = android_type if android_type else filetype

                    suspicious = self._is_suspicious_resource(
                        path=info.filename,
                        magika_type=filetype,
                        entropy=file_entropy,
                        android_type=android_type,
                    )

                    resource = APKResource(
                        path=info.filename,
                        size=info.file_size,
                        sha256=file_sha256,
                        filetype_magika=stored_type,
                        entropy=file_entropy,
                    )

                    if suspicious:
                        resource.is_suspicious = True
                        self.suspicious_files.append(resource)

                    self.resources.append(resource)
                except Exception as e:
                    self.log.warning(
                        f"Error processing resource {info.filename}: {e}"
                    )
                    continue

        if not self.resources:
            return None

        return {
            "total_resource_count": len(self.resources),
            "total_resource_size": sum(r.size for r in self.resources),
            "suspicious_file_count": len(self.suspicious_files),
            "resources": self.resources,
            "suspicious_files": self.suspicious_files,
        }

    # ─── Export ──────────────────────────────────────────────────────────

    def prepare_export_data(self, exporter_type: str) -> Any:
        if exporter_type == "ClickHouseExporter":
            if not self.resources:
                return None

            current_time = datetime.now(timezone.utc)
            data = []
            for res in self.resources:
                data.append([
                    self.sha256,
                    res.path,
                    res.size,
                    res.sha256,
                    res.filetype_magika,
                    res.entropy,
                    int(res.is_suspicious),
                    current_time,
                ])

            column_names = [
                'sha256', 'resource_path', 'resource_size',
                'resource_sha256', 'resource_magika', 'resource_entropy',
                'is_suspicious', 'analysis_date',
            ]

            column_type_names = [
                'FixedString(64)', 'String', 'UInt64',
                'FixedString(64)', 'LowCardinality(String)', 'Float32',
                'UInt8', "DateTime64(3, 'UTC')",
            ]

            return (data, column_names, column_type_names)

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