Hanchao Li

35 papers A* 2A 2C 2Journal 12Unranked 17
YearRankTypeTitle / Venue / Authors
2024 conf
APCCAS
Zhongzhiguang Lu, Hanchao Li, Yihao Zhuang, Hanlin Xie, Geok Ing Ng, Yuanjin Zheng
2024 C conf
TENCON
Geok Ing Ng, Hanlin Xie, Hanchao Li
2024 J jnl
Vis. Comput.
Hanchao Li, Xinguo Liu
2023 conf
ICEBE
Hanchao Li, Xiang Li
2023 A conf
CIKM
Chaobo He, Junwei Cheng, Quanlong Guan, Xiang Fei, Hanchao Li, Yong Tang
2022 J jnl
IEEE Trans. Comput. Soc. Syst.
Chaobo He, Xiang Fei, Qiwei Cheng, Hanchao Li, Zeng Hu, Yong Tang
2022 J jnl
IEEE Trans. Big Data
Chaobo He, Yulong Zheng, Xiang Fei, Hanchao Li, Zeng Hu, Yong Tang
2022 conf
ICEBE
Hanchao Li, Qian Wang, Dongli Fang, Xinbei Qian
2022 conf
PRCV (3)
Hanchao Li, Yizhu Lin, Xinguo Liu
2021 conf
ICEBE
Hanchao Li, Xiang Fei, Ming Yang, Kuo-Ming Chao, Chaobo He
2021 C conf
CGI
Penglei Ji, Hanchao Li, Luyan Jiang, Xinguo Liu
2021 J jnl
J. Sensors
Yating Yu, Fei Yuan, Hanchao Li, Cristian Ulianov, Guiyun Tian
2021 J jnl
Future Gener. Comput. Syst.
Chaobo He, Hai Liu, Yong Tang, Shuangyin Liu, Xiang Fei, Qiwei Cheng, Hanchao Li
2021 J jnl
J. Vis. Commun. Image Represent.
Penglei Ji, Jie Li, Hanchao Li, Xinguo Liu
2020 J jnl
IEEE Access
Chaobo He, Hai Liu, Yong Tang, Xiang Fei, Hanchao Li, Qiong Zhang
2019 conf
ICEBE
Hongyu You, Ming Yang, Xiang Fei, Kuo-Ming Chao, Hanchao Li
2019 A* conf
CVPR
Hanchao Li, Pengfei Xiong, Haoqiang Fan, Jian Sun
2019 J jnl
CoRR
Hanchao Li, Pengfei Xiong, Haoqiang Fan, Jian Sun
2019 conf
ICEBE
Hanchao Li, Xiang Fei, Ming Yang, Kuo-Ming Chao, Chaobo He
2018 A* conf
VR
Juan Liu, Hanchao Li, Lu Zhao, Siwei Zhao, Guowen Qi, Yulong Bian, Xiangxu Meng, Chenglei Yang
2018 conf
ICEBE
Hanchao Li, Hongyu You, Xiang Fei, Ming Yang, Kuo-Ming Chao, Chaobo He
2018 J jnl
J. Supercomput.
Chaobo He, Xiang Fei, Hanchao Li, Yong Tang, Hai Liu, Shuangyin Liu
2018 A conf
BMVC
Hanchao Li, Pengfei Xiong, Jie An, Lingxue Wang
2018 J jnl
CoRR
Hanchao Li, Pengfei Xiong, Jie An, Lingxue Wang
2017 conf
ICEBE
Chaobo He, Xiang Fei, Hanchao Li, Yong Tang, Hai Liu, Qimai Chen
2017 conf
ICEBE
Hanchao Li, Zhouhemu Tang, Xiang Fei, Kuo-Ming Chao, Ming Yang, Chaobo He
2017 J jnl
Concurr. Comput. Pract. Exp.
Chaobo He, Hanchao Li, Xiang Fei, Atiao Yang, Yong Tang, Jia Zhu
2017 conf
CBD
Chaobo He, Xiang Fei, Hanchao Li, Hai Liu, Yong Tang, Qimai Chen
2017 conf
ICSAI
Ali Haider Fakhrulddin, Xiang Fei, Hanchao Li
2017 conf
ICEBE
Hanchao Li, David Yee Fan Zuo, Xiang Fei, Kuo-Ming Chao, Ming Yang, Chaobo He
2016 J jnl
Pers. Ubiquitous Comput.
Yulong Bian, Chenglei Yang, Fengqiang Gao, Huiyu Li, Shisheng Zhou, Hanchao Li, Xiaowen Sun, Xiangxu Meng
2016 conf
SII
Hanchao Li, Daisuke Harada, Naohiko Hanajima, Hidekazu Kajiwara, Kentarou Kurashige, Yoshinori Fujihira, Masato Mizukami
2016 conf
ICEBE
Hanchao Li, Xiang Fei, Kuo-Ming Chao, Ming Yang, Chaobo He
2015 conf
IIKI
Yulong Bian, Chenglei Yang, Fengqiang Gao, Hanchao Li, Xiaowen Sun, Xiangxu Meng, Yu Wang
2015 conf
CBD
Chaobo He, Hanchao Li, Xiang Fei, Yong Tang, Jia Zhu
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