Weichang Li

33 papers A* 4A 2C 1Misc 1Journal 16Unranked 9
YearRankTypeTitle / Venue / Authors
2025 conf
ROBIO
Hongjun Ma, Weichang Li, Zhihua Yang, Che Hou
2025 A conf
IROS
Hongjun Ma, Weichang Li
2025 J jnl
CoRR
Hongjun Ma, Weichang Li
2025 J jnl
IEEE Access
Haobo Kang, Hongjun Ma, Weichang Li
2025 J jnl
CoRR
Hongjun Ma, Weichang Li, Jingwei Zhang, Shenlai He, Xiaoyan Deng
2025 J jnl
CoRR
Panqi Chen, Lei Cheng, Jianlong Li, Weichang Li, Weiqing Liu, Jiang Bian, Shikai Fang
2025 J jnl
CoRR
Panqi Chen, Yifan Sun, Lei Cheng, Yang Yang, Weichang Li, Yang Liu, Weiqing Liu, Jiang Bian, Shikai Fang
2025 conf
ROBIO
Jingwei Zhang, Hongjun Ma, Weichang Li, Shenlai He, Jingyu Zhang, Che Hou
2025 conf
ROBIO
Jingyu Zhang, Hongjun Ma, Weichang Li, Jingwei Zhang, Zeqian Zhao, Che Hou
2024 J jnl
RAIRO Oper. Res.
Guoxing Huang, Yukang Yang, Weichang Li, Xianhuai Cao, Zhipeng Yang
2024 J jnl
Sensors
Nour Alzamil, Vladimir Kazei, Huawei Zhou, Weichang Li
2024 J jnl
IEEE Trans. Instrum. Meas.
Jiahong Zhang, Weichang Li, Jing Zhang, Yingna Li
2024 J jnl
Comput. Geosci.
Farhan Naseer, Vladimir Kazei, Weichang Li
2023 J jnl
Sensors
Donglin Zhu, Lei Fu, Vladimir Kazei, Weichang Li
2023 J jnl
IEEE Trans. Neural Networks Learn. Syst.
Antonio R. Paiva, Weichang Li, Chris A. Mattmann, Youzuo Lin, Maarten V. de Hoop
2023 J jnl
Comput. Geosci.
Ardiansyah Koeshidayatullah, Ivan Ferreira-Chacua, Weichang Li
2023 J jnl
IEEE Geosci. Remote. Sens. Lett.
Yong Ma, Weichang Li
2021 J jnl
Comput. Geosci.
Daniel Almeida Colombo, Ersan Turkoglu, Weichang Li, Diego Rovetta
2021 conf
ROBIO
Weichang Li, Yisheng Guan, Zixi Liang, Shangying Huang
2019 J jnl
CoRR
Michael J. Bianco, Peter Gerstoft, James Traer, Emma Ozanich, Marie A. Roch, Sharon Gannot, Charles-Alban Deledalle, Weichang Li
2019 conf
IEEE SENSORS
Robert Adams, Weichang Li, Jonathan Harrist, Tim Thiel, Muhammad Arsalan, Max Deffenbaugh
2017 Misc conf
ICASSP
Feng-Xiang Ge, Ying Chen, Weichang Li
2014 A* conf
ICML
Zhiwei Qin, Weichang Li, Firdaus Janoos
2014 conf
CDC
Weichang Li, Thomas A. Badgwell
2013 J jnl
IEEE Trans. Ind. Informatics
Mihajlo Grbovic, Weichang Li, Niranjan A. Subrahmanya, Adam K. Usadi, Slobodan Vucetic
2012 A conf
CIKM
Po Hu, Minlie Huang, Peng Xu, Weichang Li, Adam K. Usadi, Xiaoyan Zhu
2012 A* conf
ICDM
Huida Qiu, Yan Liu, Niranjan A. Subrahmanya, Weichang Li
2012 conf
NIPS
Firdaus Janoos, Weichang Li, Niranjan A. Subrahmanya, István Ákos Mórocz, William M. Wells III
2012 conf
SAM
Weichang Li, Niranjan A. Subrahmanya, Feng Xu
2012 A* conf
KDD
Jie Tang, Bo Wang, Yang Yang, Po Hu, Yanting Zhao, Xinyu Yan, Bo Gao, Minlie Huang, Peng Xu, Weichang Li, Adam K. Usadi
2011 C conf
CIS
Weichang Li, Hongning Li, Min Xie, Shupo Bu
2011 A* conf
ICDM
Po Hu, Minlie Huang, Peng Xu, Weichang Li, Adam K. Usadi, Xiaoyan Zhu
2005 conf
ICASSP (4)
Weichang Li, James C. Preisig
redb/extractors/decompiler/apk/smali_normalization.py
← Index redb/extractors/decompiler/apk/smali_normalization.py python
"""Semantic normalization of Dalvik/smali instructions.

Analogous to Binary Ninja's LLIL normalization: strips register allocation
noise and instruction encoding variants while preserving semantic operations.

Three normalization levels (most aggressive to most detailed):
  - 'category':    semantic category only (MOV, ALU, CALL, ...)
  - 'opcode':      base opcode, width-invariant (add, sub, invoke, ...)
  - 'opcode_api':  opcode category + API method/field references for
                   invoke/field/alloc instructions (default for MinHash)

References:
  - Smali+ 12-category reduction (Canfora et al.)
  - MOSDroid opcode family grouping
  - DroidSIFT/DroidSim API-sensitive similarity
"""

import re
from typing import List

# ---------------------------------------------------------------------------
# Dalvik opcode -> semantic category mapping
# ---------------------------------------------------------------------------
# Prefix-matched against instruction opcodes. Order matters for overlapping
# prefixes (longer/more-specific prefixes should come first in iteration,
# but since we use startswith and break on first match, we order by
# specificity within the list).

OPCODE_CATEGORIES = {
    # Arithmetic/logic
    "add": "ALU", "sub": "ALU", "mul": "ALU", "div": "ALU",
    "rem": "ALU", "and": "ALU", "or": "ALU", "xor": "ALU",
    "shl": "ALU", "shr": "ALU", "ushr": "ALU", "neg": "ALU",
    "not": "ALU",
    # Data movement
    "move": "MOV", "const": "CONST",
    # Memory access (field/array)
    "iget": "LOAD", "sget": "LOAD", "aget": "LOAD",
    "iput": "STORE", "sput": "STORE", "aput": "STORE",
    # Invocations
    "invoke": "CALL",
    # Control flow
    "if": "BRANCH", "goto": "JMP",
    "switch": "SWITCH",
    "return": "RET",
    # Object/type
    "new": "ALLOC", "check": "TYPE", "instance": "TYPE",
    # Array
    "fill": "ARR", "array": "ARR",
    # Comparison
    "cmpl": "CMP", "cmpg": "CMP", "cmp": "CMP",
    # Exception / synchronization
    "throw": "EXC", "monitor": "SYNC",
    # Conversion (int-to-long, float-to-int, etc.)
    "int-to": "CONV", "long-to": "CONV", "float-to": "CONV",
    "double-to": "CONV",
}

# Pre-compiled regexes for operand extraction
_METHOD_REF_RE = re.compile(r"(L[\w/$]+;->[\w<>]+\(.*?\)[\w/$;\[]*)")
_FIELD_REF_RE = re.compile(r"(L[\w/$]+;->[\w]+:[\w/$;\[]+)")
_CLASS_REF_RE = re.compile(r"(L[\w/$]+;)")
_CONST_STRING_RE = re.compile(r'^const-string(?:/jumbo)?\s')


def categorize_opcode(opcode: str) -> str:
    """Map a Dalvik opcode to its semantic category.

    Prefix-matched: 'add-int/2addr' matches 'add' -> 'ALU'.
    Returns 'OTHER' for unrecognized opcodes.
    """
    for prefix, cat in OPCODE_CATEGORIES.items():
        if opcode.startswith(prefix):
            return cat
    return "OTHER"


# Mapping from semantic categories to the ACFG feature vector indices
# used by Binary Ninja's build_block_features (cfg_features.py).
# This enables cross-platform ACFG feature comparison.
CATEGORY_TO_ACFG_INDEX = {
    "ALU": 0,       # CAT_ARITHMETIC
    "CONV": 0,      # arithmetic-adjacent
    "CMP": 4,       # CAT_COMPARISON
    "MOV": 2,       # CAT_TRANSFER
    "CONST": 2,     # transfer-adjacent (loading constants)
    "LOAD": 5,      # CAT_MEMORY
    "STORE": 5,     # CAT_MEMORY
    "CALL": 3,      # CAT_CALL
    "BRANCH": 1,    # CAT_LOGIC (conditional logic)
    "JMP": 1,       # CAT_LOGIC
    "SWITCH": 1,    # CAT_LOGIC
    "RET": 2,       # CAT_TRANSFER
    "ALLOC": 5,     # CAT_MEMORY (heap allocation)
    "TYPE": 6,      # CAT_OTHER
    "ARR": 5,       # CAT_MEMORY
    "EXC": 6,       # CAT_OTHER
    "SYNC": 6,      # CAT_OTHER
    "OTHER": 6,     # CAT_OTHER
}


def normalize_instruction(line: str, level: str = "opcode_api") -> str:
    """Normalize a single smali instruction line.

    Args:
        line: A single smali instruction (whitespace-stripped).
        level: Normalization level:
            'category'   - most aggressive: just semantic category
            'opcode'     - base opcode only, width/addressing-mode invariant
            'opcode_api' - category + API references for invoke/field/alloc
                          (default, best for MinHash similarity)

    Returns:
        Normalized instruction string, or empty string for non-instructions.
    """
    stripped = line.strip()
    if not stripped:
        return ""

    parts = stripped.split(None, 1)
    opcode = parts[0]
    operands = parts[1] if len(parts) > 1 else ""

    if level == "category":
        return categorize_opcode(opcode)

    if level == "opcode":
        # Strip type/width suffixes for invariance:
        # add-int, add-long, add-float -> 'add'
        # add-int/2addr -> 'add'
        base = re.split(r"[-/]", opcode)[0]
        return base

    if level == "opcode_api":
        # const-string: preserve string content (encrypted strings are a
        # key malware indicator)
        if _CONST_STRING_RE.match(stripped):
            # Extract the string literal
            str_match = re.search(r'"(.*)"', operands)
            if str_match:
                return f"CONST_STR \"{str_match.group(1)}\""
            return "CONST_STR"

        # invoke-*: preserve method reference
        if opcode.startswith("invoke"):
            ref = _METHOD_REF_RE.search(operands)
            if ref:
                return f"CALL {ref.group(1)}"
            return "CALL"

        # Field access: preserve field reference
        if opcode.startswith(("iget", "iput", "sget", "sput")):
            ref = _FIELD_REF_RE.search(operands)
            if ref:
                cat = "LOAD" if "get" in opcode else "STORE"
                return f"{cat} {ref.group(1)}"
            # Fallback: try space-separated format from androguard
            # e.g. "iget v0, p0, Lcom/Foo;->field Ljava/lang/String;"
            space_ref = re.search(
                r"(L[\w/$]+;->[\w]+)\s+([\w/$;\[]+)", operands
            )
            if space_ref:
                cat = "LOAD" if "get" in opcode else "STORE"
                return f"{cat} {space_ref.group(1)}:{space_ref.group(2)}"
            cat = "LOAD" if "get" in opcode else "STORE"
            return cat

        # new-instance: preserve allocated type
        if opcode.startswith("new-instance") or opcode == "new-array":
            ref = _CLASS_REF_RE.search(operands)
            if ref:
                return f"ALLOC {ref.group(1)}"
            return "ALLOC"

        # Everything else: just the category
        return categorize_opcode(opcode)

    # Unknown level: return raw opcode
    return opcode


def normalize_method_body(
    body: str, level: str = "opcode_api"
) -> List[str]:
    """Normalize all instructions in a smali method body.

    Filters out directives (.), labels (:), comments (#), and blank lines.
    Returns a list of normalized instruction strings.

    Args:
        body: Raw smali method body text.
        level: Normalization level (see normalize_instruction).

    Returns:
        List of normalized instruction strings (no empty strings).
    """
    normalized = []
    for line in body.split("\n"):
        stripped = line.strip()
        # Skip non-instructions
        if not stripped:
            continue
        if stripped.startswith((".",":", "#")):
            continue
        result = normalize_instruction(stripped, level)
        if result:
            normalized.append(result)
    return normalized