Raffaele A. Calogero

30 papers C 1Misc 3Journal 23Unranked 3
YearRankTypeTitle / Venue / Authors
2025 J jnl
Bioinform.
Riccardo Aucello, Simone Pernice, Dora Tortarolo, Raffaele A. Calogero, Celia Herrera-Rincon, Giulia Ronchi, Stefano Geuna, Francesca Cordero, Pietro Lió, Marco Beccuti
2024 J jnl
BMC Bioinform.
Simone Alessandri, Maria L. Ratto, Sergio Rabellino, Gabriele Piacenti, Sandro Gepiro Contaldo, Simone Pernice, Marco Beccuti, Raffaele A. Calogero, Luca Alessandrì
2023 J jnl
Bioinform.
Simone Pernice, Roberta Sirovich, Elena Grassi, Marco Viviani, Martina Ferri, Francesco Sassi, Luca Alessandrì, Dora Tortarolo, Raffaele A. Calogero, Livio Trusolino, Andrea Bertotti, Marco Beccuti, Martina Olivero, Francesca Cordero
2021 J jnl
BMC Bioinform.
Marco Antonio Tangaro, Pietro Mandreoli, Matteo Chiara, Giacinto Donvito, Marica Antonacci, Antonio Parisi, Angelica Bianco, Angelo Romano, Daniela Manila Bianchi, Davide Cangelosi, Paolo Uva, Ivan Molineris, Vladimir Nosi, Raffaele A. Calogero, Luca Alessandrì, Elena Pedrini, Marina Mordenti, Emanuele Bonetti, Luca Sangiorgi, Graziano Pesole, Federico Zambelli
2020 J jnl
CoRR
Simone Pernice, Marco Beccuti, Greta Romano, Marzio Pennisi, Alessandro Maglione, Santina Cutrupi, Francesco Pappalardo, Lorenzo Capra, Giuliana Franceschinis, Massimiliano De Pierro, Gianfranco Balbo, Francesca Cordero, Raffaele A. Calogero
2019 J jnl
BMC Bioinform.
Simone Pernice, Marzio Pennisi, Greta Romano, Alessandro Maglione, Santina Cutrupi, Francesco Pappalardo, Gianfranco Balbo, Marco Beccuti, Francesca Cordero, Raffaele A. Calogero
2019 J jnl
BMC Bioinform.
Francesca Cordero, Raffaele A. Calogero, Michele Caselle
2019 conf
CIBB
Simone Pernice, Marco Beccuti, Greta Romano, Marzio Pennisi, Alessandro Maglione, Santina Cutrupi, Francesco Pappalardo, Lorenzo Capra, Giuliana Franceschinis, Massimiliano De Pierro, Gianfranco Balbo, Francesca Cordero, Raffaele A. Calogero
2018 C conf
PDP
Greta Romano, Elisa Genuardi, Raffaele A. Calogero, Simone Ferrero
2018 J jnl
BMC Bioinform.
Neha Kulkarni, Luca Alessandrì, Riccardo Panero, Maddalena Arigoni, Martina Olivero, Giulio Ferrero, Francesca Cordero, Marco Beccuti, Raffaele A. Calogero
2018 J jnl
Bioinform.
Marco Beccuti, Francesca Cordero, Maddalena Arigoni, Riccardo Panero, Elvio Gilberto Amparore, Susanna Donatelli, Raffaele A. Calogero
2017 J jnl
BMC Bioinform.
Marco Beccuti, Elisa Genuardi, Greta Romano, Luigia Monitillo, Daniela Barbero, Mario Boccadoro, Marco Ladetto, Raffaele A. Calogero, Simone Ferrero, Francesca Cordero
2015 J jnl
BMC Syst. Biol.
Chiara Fornari, Gianfranco Balbo, Sami M. Halawani, Omar M. Ba-Rukab, Ab Rahman Ahmad, Raffaele A. Calogero, Francesca Cordero, Marco Beccuti
2015 J jnl
BMC Bioinform.
Matteo Carrara, Josephine Lum, Francesca Cordero, Marco Beccuti, Michael Poidinger, Susanna Donatelli, Raffaele A. Calogero, Francesca Zolezzi
2014 J jnl
Bioinform.
Marco Beccuti, Matteo Carrara, Francesca Cordero, Fulvio Lazzarato, Susanna Donatelli, Francesca Nadalin, Alberto Policriti, Raffaele A. Calogero
2013 J jnl
BMC Bioinform.
Francesca Cordero, Marco Beccuti, Chiara Fornari, Stefania Lanzardo, Laura Conti, Federica Cavallo, Gianfranco Balbo, Raffaele A. Calogero
2013 J jnl
BMC Bioinform.
Matteo Carrara, Marco Beccuti, Federica Cavallo, Susanna Donatelli, Fulvio Lazzarato, Francesca Cordero, Raffaele A. Calogero
2012 Misc conf
CISIS
Matteo Carrara, Federica Cavallo, Maddalena Arigoni, Raffaele A. Calogero
2011 conf
BCB
Francesca Cordero, Stefania Lanzardo, Maddalena Arigoni, Chiara Fornari, Laura Conti, Gianfranco Balbo, Federica Cavallo, Daniele Manini, Raffaele A. Calogero
2011 conf
CS2Bio@DisCoTec
Chiara Fornari, Francesca Cordero, Daniele Manini, Gianfranco Balbo, Raffaele A. Calogero
2010 Misc conf
CISIS
Alessia Visconti, Francesca Cordero, Marco Botta, Raffaele A. Calogero
2009 Misc conf
CISIS
Silvia C. Lenzken, Silvia Vivarelli, Francesca Zolezzi, Francesca Cordero, Cristina Della Beffa, Raffaele A. Calogero, Silvia Barabino
2007 J jnl
BMC Bioinform.
Roberta Bosotti, Giuseppe Locatelli, Sandra Healy, Emanuela Scacheri, Luca Sartori, Ciro Mercurio, Raffaele A. Calogero, Antonella Isacchi
2007 J jnl
Math. Comput. Model.
Daniela Bianchi, Raffaele A. Calogero, Brunello Tirozzi
2007 J jnl
Bioinform.
Remo Sanges, Francesca Cordero, Raffaele A. Calogero
2005 J jnl
BMC Bioinform.
Federica Cavallo, Annalisa Astolfi, Manuela Iezzi, Francesca Cordero, Pierluigi Lollini, Guido Forni, Raffaele A. Calogero
2004 J jnl
Bioinform.
Maria Carmela Accardo, Ennio Giordano, Sara Riccardo, Filomena Anna Digilio, Giovanni Iazzetti, Raffaele A. Calogero, Maria Furia
2004 J jnl
Bioinform.
Fulvio Lazzarato, Giuliana Franceschinis, Marco Botta, Francesca Cordero, Raffaele A. Calogero
1998 J jnl
Bioinform.
Giovanni Iazzetti, Maria Luisa Chiusano, Raffaele A. Calogero
1998 J jnl
Bioinform.
Giovanni Iazzetti, G. Santini, M. Rau, E. Bucci, Raffaele A. Calogero
redb/extractors/decompiler/bninja/analysis/cfg_features.py
← Index redb/extractors/decompiler/bninja/analysis/cfg_features.py python
import struct
from collections import deque
from typing import Optional

import blake3
import mmh3


# ---------------------------------------------------------------------------
# Task 1.1: Core Graph Utilities
# ---------------------------------------------------------------------------

def bfs_order(successors: list[list[int]], n: int) -> list[int]:
    """
    BFS traversal from node 0 (entry block), returns node indices in visit order.
    Unreachable nodes appended at the end.
    """
    if n == 0:
        return []

    visited = set()
    order = []
    queue = deque([0])
    visited.add(0)

    while queue:
        idx = queue.popleft()
        order.append(idx)
        for target in successors[idx]:
            if target not in visited:
                visited.add(target)
                queue.append(target)

    # Append unreachable blocks (dead code)
    for i in range(n):
        if i not in visited:
            order.append(i)

    return order


def bfs_max_depth(successors: list[list[int]], n: int) -> int:
    """
    Maximum BFS depth from entry block (node 0).
    Replaces the per-block depth column with a single scalar.
    """
    if n == 0:
        return 0

    depth = {0: 0}
    max_d = 0
    queue = deque([0])

    while queue:
        node = queue.popleft()
        for s in successors[node]:
            if s not in depth:
                depth[s] = depth[node] + 1
                if depth[s] > max_d:
                    max_d = depth[s]
                queue.append(s)

    return max_d


# ---------------------------------------------------------------------------
# Task 1.2: Back-Edge Detection (Iterative DFS)
# ---------------------------------------------------------------------------

def count_back_edges(successors: list[list[int]], n: int) -> int:
    """
    Count natural loops via iterative DFS back-edge detection.
    A back edge is an edge to a GRAY (in-stack) node.

    Iterative to avoid stack overflow on functions with 1000+ blocks
    (common in obfuscated malware, VM dispatchers, unrolled loops).
    """
    if n == 0:
        return 0

    WHITE, GRAY, BLACK = 0, 1, 2
    color = [WHITE] * n
    back_edges = 0

    stack = [(0, iter(successors[0]))]
    color[0] = GRAY

    while stack:
        u, children = stack[-1]
        try:
            v = next(children)
            if color[v] == GRAY:
                back_edges += 1
            elif color[v] == WHITE:
                color[v] = GRAY
                stack.append((v, iter(successors[v])))
        except StopIteration:
            color[u] = BLACK
            stack.pop()

    return back_edges


# ---------------------------------------------------------------------------
# Task 1.3: Topology Hash
# ---------------------------------------------------------------------------

def compute_topology_hash(
    successors: list[list[int]],
    bfs: list[int],
    n: int,
) -> bytes:
    """
    BLAKE3 hash of BFS-ordered canonical adjacency.
    Pure graph shape — ignores all block content.
    Two functions with identical control flow structure produce identical hashes.

    Returns 16 bytes (128-bit).
    """
    if n == 0:
        return b'\x00' * 16

    # Remap: original index -> BFS position
    remap = {original: position for position, original in enumerate(bfs)}

    canonical = bytearray()
    for position in range(n):
        original_idx = bfs[position]
        remapped_succs = sorted(
            remap[s] for s in successors[original_idx] if s in remap
        )
        # Pack: node_index (2 bytes) + num_successors (1 byte) + successor indices (2 bytes each)
        canonical.extend(struct.pack('<HB', position, len(remapped_succs)))
        for s in remapped_succs:
            canonical.extend(struct.pack('<H', s))

    return blake3.blake3(bytes(canonical)).digest(length=16)


# ---------------------------------------------------------------------------
# Task 1.4: MD-Index (Top-Down and Bottom-Up)
# ---------------------------------------------------------------------------

def compute_md_index_topdown(
    successors: list[list[int]],
    predecessors: list[list[int]],
    bfs: list[int],
) -> int:
    """
    BinDiff-style top-down MD-index.
    Hash of (in_degree, out_degree) sequence in BFS order from entry.
    Returns UInt64.
    """
    if not bfs:
        return 0

    degree_bytes = bytearray()
    for idx in bfs:
        in_deg = min(len(predecessors[idx]), 255)
        out_deg = min(len(successors[idx]), 255)
        degree_bytes.extend(struct.pack('<BB', in_deg, out_deg))

    h = blake3.blake3(bytes(degree_bytes)).digest(length=8)
    return struct.unpack('<Q', h)[0]


def compute_md_index_bottomup(
    successors: list[list[int]],
    predecessors: list[list[int]],
    n: int,
) -> int:
    """
    Bottom-up MD-index: BFS from exit blocks (no successors),
    traversing edges in reverse.
    Returns UInt64.
    """
    if n == 0:
        return 0

    exits = [i for i in range(n) if len(successors[i]) == 0]
    if not exits:
        exits = [n - 1]  # Fallback: use last block

    visited = set(exits)
    order = []
    queue = deque(exits)

    while queue:
        idx = queue.popleft()
        order.append(idx)
        for pred in predecessors[idx]:
            if pred not in visited:
                visited.add(pred)
                queue.append(pred)

    # Append unreachable blocks
    for i in range(n):
        if i not in visited:
            order.append(i)

    degree_bytes = bytearray()
    for idx in order:
        in_deg = min(len(predecessors[idx]), 255)
        out_deg = min(len(successors[idx]), 255)
        degree_bytes.extend(struct.pack('<BB', in_deg, out_deg))

    h = blake3.blake3(bytes(degree_bytes)).digest(length=8)
    return struct.unpack('<Q', h)[0]


# ---------------------------------------------------------------------------
# Task 1.5: Prime Product
# ---------------------------------------------------------------------------

# Small primes assigned to LLIL opcode categories.
# Keys are the integer values of binaryninja.LowLevelILOperation enum members.
# We use integer keys so this module doesn't import binaryninja.
#
# Mapping rationale: same operation class -> same prime.
# Using LLIL (not native asm) makes this architecture-independent.
#
# Populated at import time by cfg.py using the real LowLevelILOperation enum values.
# Unknown ops map to prime 1 (identity element) in compute_prime_product().
LLIL_OP_PRIMES: dict[int, int] = {}


def compute_prime_product(llil_operations: list[int]) -> int:
    """
    Product of small primes assigned to each LLIL opcode.
    Position-independent: block reordering doesn't change the result.
    Mod 2^64 for fixed-size storage.

    Args:
        llil_operations: flat list of LLIL operation enum integer values
                         for all instructions in the function.
    Returns:
        UInt64 prime product, or 0 if no instructions.
    """
    if not llil_operations:
        return 0

    product = 1
    for op in llil_operations:
        prime = LLIL_OP_PRIMES.get(op, 1)
        product = (product * prime) % (2**64)

    return product


# ---------------------------------------------------------------------------
# Task 1.6: ACFG Block Features
# ---------------------------------------------------------------------------

# Instruction category indices for ACFG feature vectors
CAT_ARITHMETIC = 0
CAT_LOGIC = 1
CAT_TRANSFER = 2
CAT_CALL = 3
CAT_COMPARISON = 4
CAT_MEMORY = 5
CAT_OTHER = 6

# Maps LLIL operation integer values to category indices.
# Populated at import time by cfg.py using the real LowLevelILOperation enum.
LLIL_OP_CATEGORIES: dict[int, int] = {}


def build_block_features(
    block_llil_ops: list[list[int]],
    successors: list[list[int]],
    n: int,
) -> list[list[int]]:
    """
    Extract Gemini-style ACFG features per block.

    Args:
        block_llil_ops: per-block list of LLIL operation integer values.
                        block_llil_ops[i] is the list of ops for block i.
                        Empty list if LLIL unavailable for that block.
        successors: index-based adjacency list.
        n: number of blocks.

    Returns:
        List of [instr_count, arithmetic, logic, transfer, call, comparison,
                 memory, successor_count] per block. All values capped at 65535.
    """
    features = []
    for i in range(n):
        cats = [0, 0, 0, 0, 0, 0, 0]
        ops = block_llil_ops[i] if i < len(block_llil_ops) else []
        for op in ops:
            cat = LLIL_OP_CATEGORIES.get(op, CAT_OTHER)
            cats[cat] += 1

        instr_count = len(ops)
        features.append([
            min(instr_count, 65535),
            min(cats[CAT_ARITHMETIC], 65535),
            min(cats[CAT_LOGIC], 65535),
            min(cats[CAT_TRANSFER], 65535),
            min(cats[CAT_CALL], 65535),
            min(cats[CAT_COMPARISON], 65535),
            min(cats[CAT_MEMORY], 65535),
            min(len(successors[i]), 65535),
        ])

    return features


# ---------------------------------------------------------------------------
# Task 1.7: CFG Feature TLSH
# ---------------------------------------------------------------------------

def compute_cfg_feature_tlsh(
    bb_features: list[list[int]],
    bfs: list[int],
) -> Optional[str]:
    """
    TLSH hash of BFS-ordered per-block feature vectors.
    Captures both structure (BFS ordering) and instruction distribution.

    Returns TLSH hex string or None if too few bytes for TLSH (< 50).
    """
    import tlsh as _tlsh

    feature_bytes = bytearray()
    for idx in bfs:
        feats = bb_features[idx]
        feature_bytes.extend(struct.pack(
            '<HBBBBBBB',
            min(feats[0], 65535),
            min(feats[1], 255),
            min(feats[2], 255),
            min(feats[3], 255),
            min(feats[4], 255),
            min(feats[5], 255),
            min(feats[6], 255),
            min(feats[7], 255),
        ))

    if len(feature_bytes) < 50:
        return None

    try:
        h = _tlsh.hash(bytes(feature_bytes))
        return h if h and h != 'TNULL' else None
    except Exception:
        return None


# ---------------------------------------------------------------------------
# Task 1.8: WL-MinHash
# ---------------------------------------------------------------------------

# Pre-computed seeds for MinHash permutations.
NUM_WL_MINHASH_PERMS = 128
_WL_MINHASH_SEEDS = list(range(NUM_WL_MINHASH_PERMS))  # Seeds 0..127


def compute_wl_minhash(
    successors: list[list[int]],
    predecessors: list[list[int]],
    bb_features: list[list[int]],
    n: int,
    iterations: int = 3,
) -> list[int]:
    """
    Weisfeiler-Leman MinHash for fuzzy topology similarity.

    Initial labels: mmh3 hash of per-block ACFG feature tuple (content-aware).
    WL refinement: incorporate sorted neighbor labels at each iteration.
    MinHash: 128-permutation signature over shingle set.

    Returns list of 128 uint8 values, or [255]*128 sentinel for empty functions.
    """
    if n == 0:
        return [255] * NUM_WL_MINHASH_PERMS

    # Initial labels: hash of instruction category tuple per block
    labels = []
    for i in range(n):
        feats = bb_features[i] if i < len(bb_features) else [0] * 8
        # mmh3 with seed=0 for initial labels
        label = mmh3.hash(str(tuple(feats)), 0) & 0xFFFFFFFF
        labels.append(label)

    # Collect shingles: (iteration, label) pairs as strings for mmh3
    shingles: set[str] = set()

    # Iteration 0: individual block labels
    for label in labels:
        shingles.add(f"0:{label}")

    # WL iterations: refine labels by neighborhood aggregation
    for iteration in range(1, iterations + 1):
        new_labels = []
        for i in range(n):
            succ_labels = tuple(sorted(labels[s] for s in successors[i]))
            pred_labels = tuple(sorted(labels[p] for p in predecessors[i]))
            composite = f"{labels[i]}|{succ_labels}|{pred_labels}"
            new_label = mmh3.hash(composite, 0) & 0xFFFFFFFF
            new_labels.append(new_label)
            shingles.add(f"{iteration}:{new_label}")
        labels = new_labels

    if not shingles:
        return [255] * NUM_WL_MINHASH_PERMS

    # Compute MinHash signature using mmh3 with different seeds
    shingle_list = list(shingles)
    signature = []
    for seed in _WL_MINHASH_SEEDS:
        min_val = 0xFFFFFFFF
        for s in shingle_list:
            h = mmh3.hash(s, seed) & 0xFFFFFFFF
            if h < min_val:
                min_val = h
        # Compress to uint8 for storage
        signature.append(min_val & 0xFF)

    return signature


# ---------------------------------------------------------------------------
# Task 1.9: Packed Adjacency
# ---------------------------------------------------------------------------

def pack_adjacency(successors: list[list[int]]) -> list[int]:
    """
    Pack CFG edges as Array(UInt32).
    Each UInt32 = (source_index << 16) | target_index.
    Supports up to 65,535 blocks per function.
    """
    edges = []
    for src, targets in enumerate(successors):
        for tgt in targets:
            if src < 65536 and tgt < 65536:
                edges.append((src << 16) | tgt)
    return edges