Nadine Couture

64 papers A* 1B 12C 4Journal 11Unranked 32
YearRankTypeTitle / Venue / Authors
2026 A* conf
CHI
Stéphanie Rey, Anke M. Brock, Nadine Couture
2025 conf
IHM
Elie Roussarie, Sophie Lepreux, Nadine Couture, Christophe Kolski
2025 B conf
TEI
Leonardo Angelini, Goda Klumbyte, Maxime Daniel, Nadine Couture, Elena Mugellini, Claude Draude
2024 B conf
ICMI
Zakariae Belmekki, David Antonio Gómez Jáuregui, Patrick Reuter, Jun Li, Jean-Claude Martin, Karl Jenkins, Nadine Couture
2023 B conf
ACII
Samory Houzangbe, Sylvain Fleury, Dimitri Masson, David Antonio Gómez Jáuregui, Jérémy Legardeur, Nadine Couture, Simon Richir
2023 conf
ISMAR-Adjunct
Maxime Cordeil, Thomas Billy, Nicolas Mellado, Loïc Barthe, Nadine Couture, Patrick Reuter
2022 conf
ETIS
Stéphanie Rey, Anke M. Brock, Brygg Ullmer, Nadine Couture
2022 J jnl
Frontiers Virtual Real.
Samory Houzangbe, Dimitri Masson, Sylvain Fleury, David Antonio Gómez Jáuregui, Jérémy Legardeur, Simon Richir, Nadine Couture
2022 conf
ETIS
Vincent Ferrari, Valentin Braud, Laurent Bovet, Nadine Couture
2022 conf
ETIS
Leonardo Angelini, Nadine Couture, Mira El Kamali, Quentin Meteier, Elena Mugellini
2021 J jnl
J. Data Intell.
Paulo Pérez, Philippe Roose, Yudith Cardinale, Mark Dalmau, Dominique Masson, Nadine Couture
2020 B conf
TEI
Stéphanie Rey, Célia Picard, Yanis Fatmi, Fanny Franco, Sarah Guilbert, Jérémie Manéré, Christophe Bortolaso, Mustapha Derras, Nadine Couture, Anke M. Brock
2020 B conf
TEI
Stéphanie Rey, Nadine Couture, Célia Picard, Christophe Bortolaso, Mustapha Derras, Anke M. Brock
2020 conf
ETIS
Stéphanie Rey, Anke M. Brock, Christophe Bortolaso, Mustapha Derras, Nadine Couture
2020 C conf
MoMM
Paulo Pérez, Philippe Roose, Yudith Cardinale, Marc Dalmau, Dominique Masson, Nadine Couture
2020 conf
INFORSID
Paulo Pérez, Philippe Roose, Marc Dalmau, Yudith Cardinale, Nadine Couture, Dominique Masson
2020 C conf
AICCSA
Paulo Pérez, Philippe Roose, Yudith Cardinale, Marc Dalmau, Dominique Masson, Nadine Couture
2020 C conf
FedCSIS
Paulo Pérez, Philippe Roose, Nadine Couture, Yudith Cardinale, Marc Dalmau, Dominique Masson
2019 conf
IHM
Irvin Dongo, David Antonio Gómez Jáuregui, Nadine Couture
2019 conf
NIME
David Antonio Gómez Jáuregui, Irvin Dongo, Nadine Couture
2019 B conf
TEI
Maxime Daniel, Guillaume Rivière, Nadine Couture
2018 conf
IHM
Stéphanie Fleck, Guillaume Rivière, Regina P. Ticona-Herrera, Nadine Couture
2018 B conf
TEI
Maxime Daniel, Guillaume Rivière, Nadine Couture
2018 B conf
TEI
Leonardo Angelini, Elena Mugellini, Nadine Couture, Omar Abou Khaled
2018 J jnl
Informatics
Leonardo Angelini, Elena Mugellini, Omar Abou Khaled, Nadine Couture
2018 conf
CHI Extended Abstracts
Leonardo Angelini, Zuzanna Lechelt, Eva Hornecker, Paul Marshall, Can Liu, Margot Brereton, Alessandro Soro, Nadine Couture, Omar Abou Khaled, Elena Mugellini
2018 B conf
TEI
Leonardo Angelini, Elena Mugellini, Omar Abou Khaled, Nadine Couture, Elise van den Hoven, Saskia Bakker
2018 conf
IHM
Paulo Pérez, Philippe Roose, Marc Dalmau, Nadine Couture, Yudith Coromoto, Dominique Masson
2018 conf
Eurographics (Education Papers)
Brett Ridel, Patrick Reuter, Nadine Couture
2017 conf
IHM
Maxime Daniel, Nadine Couture, Guillaume Rivière
2017 conf
ETIS
Leonardo Angelini, Nadine Couture, Omar Abou Khaled, Elena Mugellini
2017 J jnl
CoRR
Leonardo Angelini, Nadine Couture, Omar Abou Khaled, Elena Mugellini
2017 conf
ETIS
Regina P. Ticona-Herrera, Guillaume Rivière, Nadine Couture, Stéphanie Fleck
2016 ed.
Ubimob
Gérôme Canals, Nadine Couture, Laurence Nigay, Philippe Roose, Chantal Taconet
2016 conf
IHM
Maxime Daniel, Guillaume Rivière, Nadine Couture, Stéphane Kreckelbergh
2016 conf
IHM
Maxime Daniel, Guillaume Rivière, Nadine Couture
2016 conf
HCI-Aero
Juan Ángel Lorenzo del Castillo, Nadine Couture
2015 conf
UbiComp/ISWC Adjunct
Leonardo Angelini, Maurizio Caon, Nadine Couture, Omar Abou Khaled, Elena Mugellini
2014 conf
ISMAR-MASH'D
Alexis Clay, Gaël Domenger, Julien Conan, Axel Domenger, Nadine Couture
2014 ed.
Ergo'IA
Nadine Couture, J. M. Christian Bastien, Tomás Dorta
2014 J jnl
ACM Journal on Computing and Cultural Heritage
Brett Ridel, Patrick Reuter, Jérémy Laviole, Nicolas Mellado, Nadine Couture, Xavier Granier
2013 conf
IHM
Brett Ridel, Patrick Reuter, Jérémy Laviole, Nicolas Mellado, Xavier Granier, Nadine Couture
2013 conf
SUI
Alexis Clay, Jean-Christophe Lombardo, Julien Conan, Nadine Couture
2012 J jnl
i-com
Dietrich Kammer, Marius Brade, Ingmar S. Franke, Jens R. Helmert, Jens Geelhaar, Nadine Couture
2012 conf
NIME
Alexis Clay, Nadine Couture, Myriam Desainte-Catherine, Pierre-Henri Vulliard, Joseph Larralde, Elodie Decarsin
2012 J jnl
Vis. Comput.
Nawel Takouachet, Nadine Couture, Patrick Reuter, Pierre Joyot, Guillaume Rivière, Nicolas Verdon
2011 J jnl
ERCIM News
Patrick Reuter, Nicolas Mellado, Xavier Granier, Isabelle Hairy, Robert Vergnieux, Nadine Couture
2010 conf
DSM@SPLASH
Youssef Ridene, Nicolas Belloir, Franck Barbier, Nadine Couture
2010 J jnl
ACM Journal on Computing and Cultural Heritage
Patrick Reuter, Guillaume Rivière, Nadine Couture, Stéphanie Mahut, Loïc Espinasse
2010 conf
Erog'IA
Nadine Couture, Marion Wolff, Régis Mollard, Laurent Todeschini
2010 book
Nadine Couture
2010 conf
IHM
Alexis Clay, Nadine Couture, Laurence Nigay
2010 ch.
The Engineering of Mixed Reality Systems
Nadine Couture, Guillaume Rivière, Patrick Reuter
2010 J jnl
J. Multimodal User Interfaces
Guillaume Rivière, Nadine Couture, Patrick Reuter
2010 conf
Erog'IA
Alexis Clay, Nadine Couture, Laurence Nigay
2009 C conf
ArtsIT
Alexis Clay, Elric Delord, Nadine Couture, Gaël Domenger
2009 B conf
ACII
Alexis Clay, Nadine Couture, Laurence Nigay
2009 conf
AFFINE@ICMI
Alexis Clay, Mathieu Courgeon, Nadine Couture, Elric Delord, Céline Clavel, Jean-Claude Martin
2008 B conf
VRST
Patrick Reuter, Guillaume Rivière, Nadine Couture, Stéphanie Mahut, Nicolas Sorraing, Loïc Espinasse
2008 conf
IHM
Patrick Reuter, Nadine Couture, Guillaume Rivière
2008 B conf
TEI
Nadine Couture, Guillaume Rivière, Patrick Reuter
2007 conf
VAST
Patrick Reuter, Guillaume Rivière, Nadine Couture, Nicolas Sorraing, Loïc Espinasse, Robert Vergnieux
2007 J jnl
CoRR
Alexis Clay, Nadine Couture, Laurence Nigay
2007 conf
IHM
Nadine Couture, Guillaume Rivière
redb/extractors/decompiler/bninja/analysis/disassembly.py
← Index redb/extractors/decompiler/bninja/analysis/disassembly.py python
import re
import time

import binaryninja
from binaryninja.enums import (
    InstructionTextTokenType,
)

# Support both package and standalone imports
try:
    from ..function_type import FunctionTypeAnalysis
    from ..utils.hashes import calculate_sha256
except ImportError:
    # Fallback to absolute imports (for multiprocessing spawned processes)
    from redb.extractors.decompiler.bninja.function_type import FunctionTypeAnalysis
    from redb.extractors.decompiler.bninja.utils.hashes import calculate_sha256


class DisassemblyAnalysis:
    INVALID_STACK_SIZE = -1

    def __init__(self, arch, function, bv, logger):
        self.arch = arch
        self.function = function
        self.bv = bv
        self.logger = logger
        if self.function is not None and hasattr(self.function, "instructions"):
            self.instructions = self.function.instructions
        else:
            self.instructions = []
        self.errors = []
        return

    def log_error(
        self, message, function_name, address, exception=None, error_location="unknown"
    ):
        """Log an error during processing."""
        error_msg = f"Error in function {function_name} at {address}: {message}"
        if exception:
            error_msg += f" - {str(exception)}"
        self.logger.error(error_msg)

        # Add to errors list
        error = {
            "function_name": function_name,
            "function_address": str(address),
            "error_location": error_location,
            "error_message": message,
            "error_details": str(exception) if exception else "",
            "error_type": type(exception).__name__ if exception else "Unknown",
            "timestamp": int(time.time() * 1000),
        }
        self.errors.append(error)

    def get_json(self):
        try:
            # Build disassembly string and normalized versions
            disassembly_builder = [[], []]  # Address and instruction text

            # Create a dictionary mapping addresses to instruction tokens
            instr_tokens_by_addr = {}
            for instr_tokens, addr in self.instructions:
                instr_tokens_by_addr[addr] = instr_tokens

            addresses = sorted(instr_tokens_by_addr.keys())
            for address in addresses:
                # Original disassembly with addresses
                # instr_tokens, address = instruction
                instr_tokens = instr_tokens_by_addr[address]
                disassembly_builder[0].append(address)
                disassembly_builder[1].append("".join(map(str, instr_tokens)))

            # Join with newlines
            disassembly_str = "\n".join(disassembly_builder[1])
            disassembly_with_addresses = "\n".join(
                f"{hex(address)}: {instr_text}"
                for address, instr_text in zip(
                    disassembly_builder[0], disassembly_builder[1], strict=False
                )
            )

            disassembly_json = {
                "disassembled_function_hash": calculate_sha256(disassembly_str),
                "disassembled_function": disassembly_with_addresses,
                "disassembled_function_no_addresses": disassembly_str,
                "disassembled_function_name": self.function.name,
                "disassembled_function_address": self.function.start,
                "instructions_count": len(instr_tokens_by_addr.keys()),
                "function_type": FunctionTypeAnalysis(self.function)
                .get_function_type()
                .name,
            }

            # Add additional metrics
            type_frequencies = self.collect_instruction_types()
            disassembly_json["instructions_types"] = list(type_frequencies.keys())
            disassembly_json["control_flow_count"] = (
                self.count_control_flow_instructions()
            )
            disassembly_json["memory_access_pattern"] = self.collect_memory_patterns()
            disassembly_json["register_usage"] = self.collect_register_usage()
            disassembly_json["data_references_count"] = self.count_data_references()
            disassembly_json["max_block_size"] = self.compute_max_block_size()
            disassembly_json["num_calls"] = self.compute_num_calls()
            disassembly_json["stack_size"] = self.estimate_stack_size()

            return disassembly_json, self.errors

        except Exception as e:
            self.log_error(
                "Failed to collect instruction types",
                self.function.name,
                self.function.start,
                e,
                "collect_instruction_types",
            )
            raise ValueError(e) from e

    def collect_instruction_types(self):
        """Collect instruction type frequencies from a function."""
        type_frequencies = {}

        try:
            # Iterate through all instructions in the function
            for instruction in self.instructions:
                instr_tokens = instruction[0]  # Get the instruction tokens

                # Extract the mnemonic from the instruction tokens
                mnemonic = None
                for token in instr_tokens:
                    if token.type == InstructionTextTokenType.InstructionToken:
                        mnemonic = token.text
                        break

                if not mnemonic:
                    continue

                # Use normalize_opcode to get standardized opcode
                normalized = self.normalize_opcode(mnemonic)

                # Get category from opcode_categories or use the instruction type directly
                category = self.arch.opcode_categories.get(normalized)
                if category:
                    self._increment_frequency(type_frequencies, category)

        except Exception as e:
            self.log_error(
                "Failed to collect instruction types",
                self.function.name,
                self.function.start,
                e,
                "collect_instruction_types",
            )

        return type_frequencies

    def normalize_opcode(self, opcode):
        return opcode.upper()

    def collect_memory_patterns(self):
        """Collect memory access patterns from a function."""
        patterns = []
        try:
            for instruction in self.instructions:
                instr_tokens = instruction[0]

                # We need to capture memory operands between BeginMemoryOperandToken and EndMemoryOperandToken
                in_memory_operand = False
                memory_operand_text = ""

                for token in instr_tokens:
                    if token.type == InstructionTextTokenType.BeginMemoryOperandToken:
                        in_memory_operand = True
                        memory_operand_text = ""
                    elif token.type == InstructionTextTokenType.EndMemoryOperandToken:
                        in_memory_operand = False

                        # Process the captured memory operand text
                        if memory_operand_text:
                            # Categorize memory access pattern
                            if (
                                "+" in memory_operand_text
                                and "*" in memory_operand_text
                            ):
                                if "MEM_SCALED_INDEX" not in patterns:
                                    patterns.append("MEM_SCALED_INDEX")
                            elif (
                                "+" in memory_operand_text or "-" in memory_operand_text
                            ):
                                if "MEM_BASE_OFFSET" not in patterns:
                                    patterns.append("MEM_BASE_OFFSET")
                            else:
                                if "MEM_DIRECT" not in patterns:
                                    patterns.append("MEM_DIRECT")

                            # Check for stack accesses
                            if any(
                                reg in memory_operand_text
                                for reg in ["SP", "BP", "ESP", "EBP", "RSP", "RBP"]
                            ):
                                if "MEM_STACK" not in patterns:
                                    patterns.append("MEM_STACK")
                            # Check for string operations
                            elif (
                                any(
                                    reg in memory_operand_text
                                    for reg in ["SI", "DI", "ESI", "EDI", "RSI", "RDI"]
                                )
                                and "MEM_STRING" not in patterns
                            ):
                                patterns.append("MEM_STRING")
                    elif in_memory_operand:
                        # Accumulate token text while inside a memory operand
                        memory_operand_text += token.text
        except Exception as e:
            self.log_error(
                "Failed to collect memory patterns",
                self.function.name,
                self.function.start,
                e,
                "collect_memory_patterns",
            )
        return patterns

    def collect_register_usage(self):
        """Collect register usage from a function."""
        registers = []
        try:
            # Define register groups we're interested in tracking
            register_groups = {
                "GPR": [
                    "RAX",
                    "RBX",
                    "RCX",
                    "RDX",
                    "R9",
                    "R10",
                    "R11",
                    "R12",
                    "R13",
                    "R14",
                    "R15",
                    "EAX",
                    "EBX",
                    "ECX",
                    "EDX",
                    "R9D",
                    "R10D",
                    "R11D",
                    "R12D",
                    "R13D",
                    "R14D",
                    "AX",
                    "BX",
                    "CX",
                    "DX",
                ],
                "GPR_INDEX": ["RSI", "RDI", "ESI", "EDI", "SI", "DI"],
                "GPR_STACK": ["RSP", "RBP", "ESP", "EBP", "SP", "BP"],
                "SIMD": ["XMM", "YMM", "ZMM"],
                "FPU": ["ST", "ST0", "ST1", "ST2", "ST3", "ST4", "ST5", "ST6", "ST7"],
                "FLAGS": ["FLAGS", "EFLAGS", "RFLAGS"],
                "CONTROL_REGISTER": ["CR0", "CR2", "CR3", "CR4", "CR8"],
                "DEBUG_REGISTER": ["DR0", "DR1", "DR2", "DR3", "DR6", "DR7"],
            }

            # Extract registers from instructions
            for instruction in self.instructions:
                instr_tokens = instruction[0]
                for token in instr_tokens:
                    if token.type == InstructionTextTokenType.RegisterToken:
                        reg = token.text.upper()
                        # Check which group this register belongs to
                        for group, regs in register_groups.items():
                            # if any(r in reg for r in regs) or any(reg.startswith(r) for r in regs):
                            if any(reg == r or reg.startswith(r) for r in regs):
                                if group not in registers:
                                    registers.append(group)
                                break
        except Exception as e:
            self.log_error(
                "Failed to collect register usage",
                self.function.name,
                self.function.start,
                e,
                "collect_register_usage",
            )
        return registers

    def count_data_references(self):
        """Count the number of data references in a function."""
        count = 0
        try:

            if self.function.mlil is None:
                return 0

            for block in self.function.mlil:
                for instr in block:
                    instr_str = str(instr)
                    logged = False
                    src = None

                    # Check for constant dereferencing or symbolic refs
                    if hasattr(instr, "src"):
                        src = instr.src
                        if isinstance(
                            src,
                            (
                                binaryninja.mediumlevelil.MediumLevelILConstPtr,
                                binaryninja.mediumlevelil.MediumLevelILConst,
                            ),
                        ):
                            count += 1
                            logged = True

                    # Check full string for hardcoded addresses or symbol-like tokens
                    if re.search(r"\b0x[0-9A-Fa-f]{3,}\b", instr_str) and not logged:
                        count += 1
                        logged = True

                    if "_" in instr_str and not logged:
                        count += 1
                        logged = True

                    # Only check for MediumLevelILConstPtr if src exists
                    if src is not None and isinstance(
                        src, binaryninja.mediumlevelil.MediumLevelILConstPtr
                    ):
                        addr = src.constant
                        # Check if address is in data sections
                        segment = self.bv.get_segment_at(addr)
                        if segment and segment.writable:
                            # print(f"[{function.name}] Matched data section reference in: {instr_str}")
                            count += 1
                            logged = True
        except Exception as e:
            self.logger.warning(
                f"Failed to use MLIL for counting data references in {self.function.name} at {self.function.start}: {e}"
            )
        return count

    def compute_max_block_size(self):
        """Compute the maximum basic block size in a function."""
        max_size = 0
        if self.function is None:
            return 0

        for block in self.function.basic_blocks:
            try:
                # Count instructions in this block using the direct length approach
                # This avoids UTF-8 decoding issues entirely
                block_size = block.instruction_count
                max_size = max(max_size, block_size)
            except Exception as e:
                self.log_error(
                    f"[HandledError] computing max block size: {e}",
                    self.function.name,
                    self.function.start,
                    e,
                    "compute_max_block_size",
                )
        return max_size

    def count_control_flow_instructions(self):
        """Count the number of control flow instructions in a function."""
        count = 0
        try:
            for instruction in self.instructions:
                instr_tokens = instruction[0]
                if self.arch.is_control_flow_instruction(instr_tokens):
                    count += 1
        except Exception as e:
            self.log_error(
                "Failed to count control flow instructions",
                self.function.name,
                self.function.start,
                e,
                "count_control_flow_instructions",
            )
        return count

    def compute_num_calls(self) -> int:
        """Compute the number of call instructions in a function."""
        num_calls = 0
        try:
            for instruction in self.instructions:
                instr_tokens = instruction[0]
                # Extract the mnemonic
                for token in instr_tokens:
                    if token.type == InstructionTextTokenType.InstructionToken:
                        if token.text.upper() == "CALL":
                            num_calls += 1
                        break
        except Exception as e:
            self.log_error(
                "Failed to compute number of calls",
                self.function.name,
                self.function.start,
                e,
                "compute_num_calls",
            )
        return num_calls

    def _increment_frequency(self, frequencies, type_name):
        """Increment the frequency count for an instruction type."""
        if type_name in frequencies:
            frequencies[type_name] += 1
        else:
            frequencies[type_name] = 1

    def estimate_stack_size(self):
        """Estimate the stack size used by a function."""
        try:
            # Binary Ninja provides a stack adjustment value for functions
            # Need to convert OffsetWithConfidence to a plain integer
            stack_adjust = self.function.stack_adjustment
            if hasattr(stack_adjust, "value"):  # Handle OffsetWithConfidence objects
                return stack_adjust.value
            return stack_adjust
        except Exception as e:
            self.log_error(
                "Failed to estimate stack size",
                self.function.name,
                self.function.start,
                e,
                "estimate_stack_size",
            )
            return self.INVALID_STACK_SIZE