Xianzhong Tian

46 papers A 2B 3Journal 30Unranked 11
YearRankTypeTitle / Venue / Authors
2025 J jnl
J. Supercomput.
Xianzhong Tian, Yucheng Li, Xuhua Mao, Luoming Zhang, Ziyi Lin
2024 J jnl
Peer Peer Netw. Appl.
Xianzhong Tian, Yuheng Shao, Yujia Zou, Junxian Zhang
2024 J jnl
IEEE Internet Things J.
Xianzhong Tian, Huixiao Meng, Yifan Shen, Junxian Zhang, Yuzhe Chen, Yanjun Li
2024 J jnl
Comput. Networks
Yue Li, Yanjun Li, Yuzhe Chen, Jiahui Tong, Xianzhong Tian, Kaikai Chi
2023 J jnl
IEEE Trans. Geosci. Remote. Sens.
Xianzhong Tian, Tianying Chang, Yongxin Guo, Hong-Liang Cui
2023 J jnl
IEEE Trans. Veh. Technol.
Xiaoying Liu, Bin Xu, Xiong Wang, Kechen Zheng, Kaikai Chi, Xianzhong Tian
2023 J jnl
Peer Peer Netw. Appl.
Xianzhong Tian, Pengcheng Xu, Yifan Shen, Yuheng Shao
2023 J jnl
Telecommun. Syst.
Yuzhe Chen, Yanjun Li, Meihui Gao, Xianzhong Tian, Kaikai Chi
2022 J jnl
IEEE Trans. Geosci. Remote. Sens.
Xianzhong Tian, Zhongmin Wang, Tianying Chang, Hong-Liang Cui
2022 A conf
IPDPS
Xianzhong Tian, Huixiao Meng, Yanjun Li, Pingting Miao, Pengcheng Xu
2021 conf
WASA (1)
Xianzhong Tian, Jialun Chen, Zheng Zhao, Huixiao Meng
2021 A conf
MSWiM
Xianzhong Tian, Lu Zhou, Ting Xu
2021 J jnl
Sensors
Xianzhong Tian, Juan Zhu, Ting Xu, Yanjun Li
2021 J jnl
IEEE Trans. Instrum. Meas.
Xianzhong Tian, Qijia Guo, Zhongmin Wang, Tianying Chang, Hong-Liang Cui
2020 J jnl
IET Commun.
Shuwei Qiu, Yi-hua Zhu, Xianzhong Tian, Kaikai Chi
2020 J jnl
IET Commun.
Siliang Gong, Xiaoying Liu, Kechen Zheng, Xianzhong Tian, Yi-hua Zhu
2019 J jnl
IEEE Access
Xianzhong Tian, Min Guo, Yuzhe Chen, Yanjun Li
2019 J jnl
IEEE Wirel. Commun. Lett.
Yufan Zhang, Ertao Li, Yi-hua Zhu, Kaikai Chi, Xianzhong Tian
2019 J jnl
KSII Trans. Internet Inf. Syst.
Xianzhong Tian, Jiacun He, Yuzhe Chen, Yanjun Li
2019 B conf
GLOBECOM
Yinan Zhu, Xianzhong Tian, Kaikai Chi, Chenyiming Wen, Yi-hua Zhu
2019 B conf
ICPADS
Yinan Zhu, Kaikai Chi, Xianzhong Tian
2018 J jnl
IET Commun.
Yi-hua Zhu, Ertao Li, Kaikai Chi, Xianzhong Tian
2018 J jnl
Int. J. Commun. Syst.
Kaikai Chi, Xinchen Wei, Yanjun Li, Xianzhong Tian
2017 J jnl
IEEE Netw.
Kaikai Chi, Liang Huang, Yanjun Li, Yi-hua Zhu, Xianzhong Tian, Ming Xia
2017 J jnl
IEEE Syst. J.
Xianzhong Tian, Yi-hua Zhu, Kaikai Chi, Jiajia Liu, Daqiang Zhang
2017 J jnl
计算机科学
Xianzhong Tian, Chuchao Lin
2017 J jnl
计算机科学
Xianzhong Tian, Jie Shen
2017 J jnl
计算机科学
Kaikai Chi, Liushuan Zhu, Zhen Cheng, Xianzhong Tian
2016 J jnl
KSII Trans. Internet Inf. Syst.
Yanjun Li, Jian Pan, Xianzhong Tian
2016 conf
MSN
Lijing Li, Yi-hua Zhu, Xianzhong Tian, Kaikai Chi, Lin Xu
2016 J jnl
IEEE Trans. Veh. Technol.
Yi-hua Zhu, Kaikai Chi, Xianzhong Tian, Victor C. M. Leung
2016 J jnl
计算机科学
Xianzhong Tian, Yun Xiao
2015 conf
BigCom
Yi-hua Zhu, Chaoran Zhu, Xianzhong Tian
2015 J jnl
Int. J. Distributed Sens. Networks
Deqing Zhu, Lin Xu, Yi-hua Zhu, Xianzhong Tian
2015 J jnl
计算机科学
Xianzhong Tian, Sheng Yang, Wei Xu
2014 conf
IIKI
Deqing Zhu, Lin Zu, Yi-hua Zhu, Xianzhong Tian
2014 J jnl
Comput. Networks
Kaikai Chi, Yi-hua Zhu, Xiaohong Jiang, Xianzhong Tian
2013 B conf
WCNC
Kaikai Chi, Yi-hua Zhu, Xiaohong Jiang, Xianzhong Tian
2013 J jnl
Nano Commun. Networks
Kaikai Chi, Yi-hua Zhu, Xiaohong Jiang, Xianzhong Tian
2012 conf
CCIS
Xianzhong Tian, Qianwei Zhou, Zhen Cheng
2012 conf
CCIS
Zhen Cheng, Kaikai Chi, Xianzhong Tian, Yanjun Li
2011 conf
CMC
Xianzhong Tian, Aijiao Chen
2011 conf
ICC
Yi-hua Zhu, Xianzhong Tian, Jun Zheng
2010 conf
APWCS
Xianzhong Tian, Yong-Gang Miao, Wei Xu, Bo-Jie Fan, Jian Pan
2006 conf
IMSCCS (1)
Xianzhong Tian, Tongsen Hu
2006 conf
IMSCCS (2)
Xianzhong Tian, Tongsen Hu
tests/unit/test_decompile_decompiler.py
← Index tests/unit/test_decompile_decompiler.py python
"""Unit tests (mocked BN) for bninja/decompiler.py — BinaryNinjaDecompiler."""
import sys
import json
import time
import pytest
from unittest.mock import MagicMock, patch, mock_open

from tests.unit.conftest_binja_stubs import (
    install_binja_stubs,
    MockFunction,
    MockBasicBlock,
    MockDisassemblyLine,
    MockToken,
    MockSymbol,
    MockBinaryView,
    MockEdge,
    SymbolType,
    InstructionTextTokenType,
    BranchType,
)

install_binja_stubs()

from redb.extractors.decompiler.bninja.decompiler import (
    BinaryNinjaDecompiler,
    is_lib_or_thunk,
)
from redb.extractors.decompiler.bninja.function_type import FunctionType


# ============================================================================
# 10a. BinaryNinjaDecompiler
# ============================================================================


class TestBinaryNinjaDecompilerLogError:
    def _make_decompiler(self):
        with patch.object(BinaryNinjaDecompiler, "__init__", lambda self, *a, **kw: None):
            d = BinaryNinjaDecompiler.__new__(BinaryNinjaDecompiler)
            d.log = MagicMock()
            d.errors = []
            d.filepath = "/fake/binary"
            d.bv = None
            d.analysis_results = None
            d.exporters = []
            d.index_prefix = None
            d.filetype = None
            d.goresym = None
            d.BNINJA_TIMEOUT = 60
            return d

    def test_log_error_appends(self):
        d = self._make_decompiler()
        d.log_error("test error", "func1", 0x1000, Exception("boom"), "extract")
        assert len(d.errors) == 1

    def test_log_error_schema(self):
        d = self._make_decompiler()
        d.log_error("test error", "func1", 0x1000, Exception("boom"), "extract")
        error = d.errors[0]
        expected_keys = [
            "function_name",
            "function_address",
            "error_location",
            "error_message",
            "error_details",
            "error_type",
            "timestamp",
        ]
        for key in expected_keys:
            assert key in error, f"Missing key: {key}"


class TestBinaryNinjaDecompilerIsTooFewBlocks:
    def _make_decompiler(self):
        with patch.object(BinaryNinjaDecompiler, "__init__", lambda self, *a, **kw: None):
            d = BinaryNinjaDecompiler.__new__(BinaryNinjaDecompiler)
            d.log = MagicMock()
            d.errors = []
            d.MIN_FUNCTION_SIZE = 10
            return d

    def test_is_too_few_blocks_none(self):
        d = self._make_decompiler()
        func = MagicMock()
        func.basic_blocks = None
        assert d.is_too_few_blocks(func) is False

    def test_is_too_few_blocks_empty(self):
        # Empty basic_blocks now returns True because Binja creates function
        # stubs with empty blocks before analysis completes — we no longer
        # skip these so they get filtered downstream instead.
        d = self._make_decompiler()
        func = MagicMock()
        func.basic_blocks = []
        assert d.is_too_few_blocks(func) is True

    def test_is_too_few_blocks_small_single(self):
        d = self._make_decompiler()
        block = MagicMock()
        block.disassembly_text = [MagicMock() for _ in range(5)]  # < 10
        func = MagicMock()
        func.basic_blocks = [block]
        assert d.is_too_few_blocks(func) is False

    def test_is_too_few_blocks_large_single(self):
        d = self._make_decompiler()
        block = MagicMock()
        block.disassembly_text = [MagicMock() for _ in range(15)]  # >= 10
        func = MagicMock()
        func.basic_blocks = [block]
        assert d.is_too_few_blocks(func) is True

    def test_is_too_few_blocks_multiple(self):
        d = self._make_decompiler()
        func = MagicMock()
        func.basic_blocks = [MagicMock(), MagicMock()]
        assert d.is_too_few_blocks(func) is True


class TestIsLibOrThunk:
    def test_is_lib_or_thunk_user(self):
        func = MagicMock()
        func.symbol.type = SymbolType.FunctionSymbol
        func.is_thunk = False
        assert is_lib_or_thunk(func) is True  # NOT filtered

    def test_is_lib_or_thunk_thunk(self):
        func = MagicMock()
        func.symbol.type = SymbolType.FunctionSymbol
        func.is_thunk = True
        assert is_lib_or_thunk(func) is False  # Filtered out

    def test_is_lib_or_thunk_external(self):
        func = MagicMock()
        func.symbol.type = SymbolType.ImportedFunctionSymbol
        func.is_thunk = False
        assert is_lib_or_thunk(func) is False  # Filtered out


class TestBinaryNinjaDecompilerExtract:
    def _make_decompiler(self):
        with patch.object(BinaryNinjaDecompiler, "__init__", lambda self, *a, **kw: None):
            d = BinaryNinjaDecompiler.__new__(BinaryNinjaDecompiler)
            d.log = MagicMock()
            d.errors = []
            d.filepath = "/fake/binary"
            d.bv = MagicMock()
            d.analysis_results = None
            d.exporters = []
            d.index_prefix = None
            d.filetype = None
            d.goresym = None
            d.BNINJA_TIMEOUT = 60
            d.arch = MagicMock()
            return d

    def test_extract_success(self):
        d = self._make_decompiler()
        mock_results = {
            "decompiled": [{"test": True}],
            "disassembled": [],
            "cfg": [],
            "llil": [],
            "errors": [],
            "strings": [],
            "mlil": [],
        }
        with patch.object(d, "analyze_binary", return_value=mock_results):
            result = d.extract()
            assert result is True
            assert d.analysis_results == mock_results

    def test_extract_failure(self):
        d = self._make_decompiler()
        with patch.object(d, "analyze_binary", return_value=None):
            result = d.extract()
            assert result is False

    def test_tag(self):
        d = self._make_decompiler()
        assert d.tag() == "DECOMPILED"


class TestBinaryNinjaDecompilerAnalyzeBinary:
    def _make_decompiler(self):
        with patch.object(BinaryNinjaDecompiler, "__init__", lambda self, *a, **kw: None):
            d = BinaryNinjaDecompiler.__new__(BinaryNinjaDecompiler)
            d.log = MagicMock()
            d.errors = []
            d.filepath = "/fake/binary"
            d.analysis_results = None
            d.exporters = []
            d.index_prefix = None
            d.filetype = None
            d.goresym = None
            d.BNINJA_TIMEOUT = 60
            d.arch = MagicMock()
            d.MIN_FUNCTION_SIZE = 10
            return d

    def test_analyze_binary_links_hlil_disasm(self):
        """Bi-directional hash linkage between decompiled and disassembled."""
        d = self._make_decompiler()
        d.bv = MagicMock()
        d.bv.functions = []
        # Mock methods to return controlled data
        with patch.object(d, "extract_hlil") as mock_hlil, \
             patch.object(d, "extract_disasm") as mock_disasm, \
             patch.object(d, "extract_cfg", return_value=None), \
             patch.object(d, "extract_lowlevel", return_value=None):

            mock_hlil.return_value = {
                "decompiled_function_hash": "HLIL_HASH",
                "decompiled_function_name": "test",
                "decompiled_function": "code",
            }
            mock_disasm.return_value = {
                "disassembled_function_hash": "DISASM_HASH",
                "disassembled_function_no_addresses": "asm",
            }

            # Manually feed one function
            mock_func = MagicMock()
            mock_func.symbol.type = SymbolType.FunctionSymbol
            mock_func.is_thunk = False
            mock_func.basic_blocks = [MagicMock(), MagicMock()]
            d.bv.functions = [mock_func]

            results = d.analyze_binary()
            if results and results["decompiled"]:
                hlil_r = results["decompiled"][0]
                disasm_r = results["disassembled"][0]
                assert hlil_r["disassembled_function_hash"] == "DISASM_HASH"
                assert disasm_r["decompiled_function_hash"] == "HLIL_HASH"

    def test_analyze_binary_links_llil_disasm(self):
        """Bi-directional linkage between LLIL and disassembly."""
        d = self._make_decompiler()
        d.bv = MagicMock()

        with patch.object(d, "extract_hlil", return_value=None), \
             patch.object(d, "extract_disasm") as mock_disasm, \
             patch.object(d, "extract_cfg", return_value=None), \
             patch.object(d, "extract_lowlevel") as mock_llil:

            mock_disasm.return_value = {
                "disassembled_function_hash": "DISASM_HASH",
                "disassembled_function_no_addresses": "asm",
            }
            mock_llil.return_value = {
                "sha256_llil": "LLIL_HASH",
                "tlsh_llil": "tlsh_val",
            }

            mock_func = MagicMock()
            mock_func.symbol.type = SymbolType.FunctionSymbol
            mock_func.is_thunk = False
            mock_func.basic_blocks = [MagicMock(), MagicMock()]
            d.bv.functions = [mock_func]

            results = d.analyze_binary()
            if results and results["llil"]:
                llil_r = results["llil"][0]
                assert llil_r["disassembled_function_hash"] == "DISASM_HASH"

    def test_analyze_binary_links_cfg_disasm(self):
        """CFG gets disassembled_function_hash."""
        d = self._make_decompiler()
        d.bv = MagicMock()

        with patch.object(d, "extract_hlil", return_value=None), \
             patch.object(d, "extract_disasm") as mock_disasm, \
             patch.object(d, "extract_cfg") as mock_cfg, \
             patch.object(d, "extract_lowlevel", return_value=None):

            mock_disasm.return_value = {
                "disassembled_function_hash": "DISASM_HASH",
                "disassembled_function_no_addresses": "asm",
            }
            mock_cfg.return_value = {
                "function_address": 0x1000,
                "cyclomatic_complexity": 3,
            }

            mock_func = MagicMock()
            mock_func.symbol.type = SymbolType.FunctionSymbol
            mock_func.is_thunk = False
            mock_func.basic_blocks = [MagicMock(), MagicMock()]
            d.bv.functions = [mock_func]

            results = d.analyze_binary()
            if results and results["cfg"]:
                cfg_r = results["cfg"][0]
                assert cfg_r["disassembled_function_hash"] == "DISASM_HASH"

    def test_analyze_binary_sets_cyclomatic(self):
        """cyclomatic_complexity set on disasm from CFG."""
        d = self._make_decompiler()
        d.bv = MagicMock()

        with patch.object(d, "extract_hlil", return_value=None), \
             patch.object(d, "extract_disasm") as mock_disasm, \
             patch.object(d, "extract_cfg") as mock_cfg, \
             patch.object(d, "extract_lowlevel", return_value=None):

            mock_disasm.return_value = {
                "disassembled_function_hash": "HASH",
                "disassembled_function_no_addresses": "asm",
            }
            mock_cfg.return_value = {
                "function_address": 0x1000,
                "cyclomatic_complexity": 7,
            }

            mock_func = MagicMock()
            mock_func.symbol.type = SymbolType.FunctionSymbol
            mock_func.is_thunk = False
            mock_func.basic_blocks = [MagicMock(), MagicMock()]
            d.bv.functions = [mock_func]

            results = d.analyze_binary()
            if results and results["disassembled"]:
                disasm_r = results["disassembled"][0]
                assert disasm_r.get("cyclomatic_complexity") == 7


class TestApplyGoReSym:
    def _make_decompiler(self):
        with patch.object(BinaryNinjaDecompiler, "__init__", lambda self, *a, **kw: None):
            d = BinaryNinjaDecompiler.__new__(BinaryNinjaDecompiler)
            d.log = MagicMock()
            d.errors = []
            d.bv = MagicMock()
            return d

    def test_apply_goresym_user_functions(self):
        d = self._make_decompiler()
        d.goresym = "/tmp/goresym.json"
        data = {
            "UserFunctions": [{"Start": 0x1000, "FullName": "main.main"}],
        }
        with patch("builtins.open", mock_open(read_data=json.dumps(data))):
            d._BinaryNinjaDecompiler__apply_goresym()
            d.bv.define_user_symbol.assert_called()

    def test_apply_goresym_std_functions(self):
        d = self._make_decompiler()
        d.goresym = "/tmp/goresym.json"
        data = {
            "StdFunctions": [{"Start": 0x2000, "FullName": "runtime.main"}],
        }
        with patch("builtins.open", mock_open(read_data=json.dumps(data))):
            d._BinaryNinjaDecompiler__apply_goresym()
            d.bv.define_user_symbol.assert_called()

    def test_apply_goresym_invalid_json(self):
        d = self._make_decompiler()
        d.goresym = "/tmp/goresym.json"
        with patch("builtins.open", mock_open(read_data="not json {")):
            d._BinaryNinjaDecompiler__apply_goresym()
            # Should log error but not crash
            d.log.log_error if hasattr(d.log, 'log_error') else None
            # Just verify no exception raised