libmwemu 0.24.5

x86 32/64bits and system internals emulator, for securely emulating malware and other stuff.
Documentation
use std::cell::RefCell;

use crate::emu::Emu;

thread_local! {
    static CURRENT_EMU: RefCell<Option<*const Emu>> = RefCell::new(None);
}

pub fn with_current_emu<F, R>(f: F) -> Option<R>
where
    F: FnOnce(&Emu) -> R,
{
    CURRENT_EMU.with(|current| {
        current
            .borrow()
            .and_then(|ptr| unsafe { ptr.as_ref().map(|emu| f(emu)) })
    })
}

pub fn with_current_emu_mut<F, R>(f: F) -> Option<R>
where
    F: FnOnce(&mut Emu) -> R,
{
    CURRENT_EMU.with(|current| {
        current
            .borrow()
            .and_then(|ptr| unsafe { (ptr as *mut Emu).as_mut().map(|emu| f(emu)) })
    })
}

pub fn set_current_emu(emu: &Emu) {
    CURRENT_EMU.with(|current| {
        *current.borrow_mut() = Some(emu as *const _);
    });
}

pub fn clear_current_emu() {
    CURRENT_EMU.with(|current| {
        *current.borrow_mut() = None;
    });
}

pub fn is_emu_set() -> bool {
    CURRENT_EMU.with(|current| current.borrow().is_some())
}

pub fn log_emu_state(emu: &mut Emu) {
    log::error!("=== EMULATOR STATE ===");
    log::error!("Current position: {}", emu.pos);

    let color = "\x1b[0;31m";
    if let Some(decoded) = emu.last_decoded {
        let out = emu.format_instruction(&decoded);
        // Use `last_decoded_addr` (where the instruction lived) instead of
        // `emu.pc()` (which already reflects the *next* instruction after
        // a `ret`/branch) so the dump shows e.g.
        //   `2 0x10000036c: ret`
        // instead of `2 0x0: ret`.
        log::trace!(
            "{}{} 0x{:x}: {}{}",
            color,
            emu.pos,
            emu.last_decoded_addr,
            out,
            emu.colors.nc
        );
    }

    // Log registers
    log::error!("Registers:");
    if emu.cfg.arch.is_aarch64() {
        let regs = emu.regs_aarch64();
        for i in 0..31 {
            if i % 2 == 0 && i + 1 < 31 {
                log::error!("  X{:<2}: 0x{:016x}  X{:<2}: 0x{:016x}", i, regs.x[i], i + 1, regs.x[i + 1]);
            } else if i % 2 == 0 {
                log::error!("  X{:<2}: 0x{:016x}", i, regs.x[i]);
            }
        }
        log::error!("  SP:  0x{:016x}", regs.sp);
        log::error!("  PC:  0x{:016x}", regs.pc);
        log::error!("  NZCV: N={} Z={} C={} V={}", regs.nzcv.n, regs.nzcv.z, regs.nzcv.c, regs.nzcv.v);
    } else {
        log::error!(
            "  RAX: 0x{:016x}  RBX: 0x{:016x}",
            emu.regs().rax,
            emu.regs().rbx
        );
        log::error!(
            "  RCX: 0x{:016x}  RDX: 0x{:016x}",
            emu.regs().rcx,
            emu.regs().rdx
        );
        log::error!(
            "  RSI: 0x{:016x}  RDI: 0x{:016x}",
            emu.regs().rsi,
            emu.regs().rdi
        );
        log::error!(
            "  RBP: 0x{:016x}  RSP: 0x{:016x}",
            emu.regs().rbp,
            emu.regs().rsp
        );
        log::error!(
            "  R8:  0x{:016x}  R9:  0x{:016x}",
            emu.regs().r8,
            emu.regs().r9
        );
        log::error!(
            "  R10: 0x{:016x}  R11: 0x{:016x}",
            emu.regs().r10,
            emu.regs().r11
        );
        log::error!(
            "  R12: 0x{:016x}  R13: 0x{:016x}",
            emu.regs().r12,
            emu.regs().r13
        );
        log::error!(
            "  R14: 0x{:016x}  R15: 0x{:016x}",
            emu.regs().r14,
            emu.regs().r15
        );
        log::error!("  RIP: 0x{:016x}", emu.regs().rip);

        // Log flags
        log::error!("EFLAGS: 0x{:08x}", emu.flags().dump());
    }

    // Log last instruction if available
    if let Some(decoded) = emu.last_decoded {
        let out = emu.format_instruction(&decoded);
        log::error!("Last instruction: {}", out);
        log::error!("Instruction size: {}", emu.last_instruction_size);
    }

    // Log call stack — only x86 has a tracked call stack; aarch64 emu would
    // panic on `call_stack()` (see `crates/libmwemu/src/emu/call_stack.rs`).
    if !emu.cfg.arch.is_aarch64() && !emu.call_stack().is_empty() {
        log::error!(
            "Call stack (last {} entries):",
            emu.call_stack().len().min(10)
        );
        for (i, entry) in emu.call_stack().iter().rev().take(10).enumerate() {
            log::error!("  {}: {:x}:call:{:x}", i, entry.0, entry.1);
        }
    }

    // Log execution info
    log::error!("Tick count: {}", emu.tick);
    log::error!("Base address: 0x{:x}", emu.base);
    log::error!("Filename: {}", emu.filename);

    log::error!("==============================");
}