//! Dhaedalida `RabbitStart` / `RabbitEnd` virtualization markers for Rust on Windows.
//! Covers both x86-64 (PE32+) and 32-bit x86 (PE32).
//!
//! Drop this file into your crate as a module and bracket the code you want
//! protected:
//!
//! ```ignore
//! mod dhae_marker;
//! fn validate_license(key: &str) -> bool {
//!     unsafe { dhae_marker::RabbitStart(); }
//!     // ... sensitive code ...
//!     unsafe { dhae_marker::RabbitEnd(); }
//!     true
//! }
//! ```
//!
//! The two markers are defined in assembly via `global_asm!` (stable Rust - no
//! nightly, no `#[naked]`), as raw `.byte` encodings. Raw bytes are
//! syntax-neutral, so they are exact regardless of the assembler dialect LLVM is
//! in, and the 8 magic bytes land verbatim in `.text` where the Dhaedalida GUI
//! finds them. The values below MUST equal the six DHAE_SIG_* constants in
//! `sdk/dhae_marker_sig.h`, in that file's order.
//!
//! Build an ordinary release binary (`cargo build --release` / `rustc -O`); for
//! 32-bit add `--target i686-pc-windows-msvc` (install it once with
//! `rustup target add i686-pc-windows-msvc`). Detection only needs a direct
//! `call` (Rust emits `E8` for an intra-crate call); the incremental-link `jmp`
//! thunk is tolerated by the scanner.
//!
//! Both an underscore-prefixed and a bare label are defined on 32-bit, because
//! i386 COFF decorates a cdecl symbol as `_RabbitStart` while the raw asm label is
//! literal - defining both means the binding links whichever name the toolchain
//! asks for.

#![allow(non_snake_case)]

use core::arch::global_asm;

// x86-64: 48 B8 <imm64> = movabs rax, imm ; C3 = ret.
#[cfg(target_arch = "x86_64")]
global_asm!(
    ".globl RabbitStart",
    ".p2align 4",
    "RabbitStart:",
    ".byte 0x48, 0xB8, 0x01, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1", // movabs rax, 0xD11AE5C0DE765401
    ".byte 0xC3",                                                        // ret
    ".globl RabbitEnd",
    ".p2align 4",
    "RabbitEnd:",
    ".byte 0x48, 0xB8, 0x02, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1", // movabs rax, 0xD11AE5C0DE765402
    ".byte 0xC3",
    ".globl BearStart",
    ".p2align 4",
    "BearStart:",
    ".byte 0x48, 0xB8, 0x05, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1", // movabs rax, 0xD11AE5C0DE765405
    ".byte 0xC3",
    ".globl BearEnd",
    ".p2align 4",
    "BearEnd:",
    ".byte 0x48, 0xB8, 0x06, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1", // movabs rax, 0xD11AE5C0DE765406
    ".byte 0xC3",
);

// x86-32: no 64-bit immediate exists, so the magic is inline DATA jumped over.
// EB 08 = jmp short $+10, landing exactly on the C3.
#[cfg(target_arch = "x86")]
global_asm!(
    ".globl _RabbitStart",
    ".globl RabbitStart",
    ".p2align 4",
    "_RabbitStart:",
    "RabbitStart:",
    ".byte 0xEB, 0x08",                                                  // jmp short $+10
    ".byte 0x01, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1",              // DHAE_SIG_RABBITSTART
    ".byte 0xC3",                                                        // ret
    ".globl _RabbitEnd",
    ".globl RabbitEnd",
    ".p2align 4",
    "_RabbitEnd:",
    "RabbitEnd:",
    ".byte 0xEB, 0x08",
    ".byte 0x02, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1",              // DHAE_SIG_RABBITEND
    ".byte 0xC3",
    ".globl _BearStart",
    ".globl BearStart",
    ".p2align 4",
    "_BearStart:",
    "BearStart:",
    ".byte 0xEB, 0x08",
    ".byte 0x05, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1",              // DHAE_SIG_BEARSTART
    ".byte 0xC3",
    ".globl _BearEnd",
    ".globl BearEnd",
    ".p2align 4",
    "_BearEnd:",
    "BearEnd:",
    ".byte 0xEB, 0x08",
    ".byte 0x06, 0x54, 0x76, 0xDE, 0xC0, 0xE5, 0x1A, 0xD1",              // DHAE_SIG_BEAREND
    ".byte 0xC3",
);

extern "C" {
    /// Marks the start of a region to virtualize. The enclosing function is
    /// what Dhaedalida protects (method granularity). `unsafe` because it is an
    /// FFI call into the raw asm marker.
    pub fn RabbitStart();
    /// Marks the end of a region to virtualize.
    pub fn RabbitEnd();
    /// Marks the start of a region to protect with the STACK VM: the strongest
    /// of the three modes and by far the slowest (a bytecode interpreter, budget
    /// roughly 80x native). 64-bit only in this version -- a 32-bit
    /// target virtualizes such a function instead. A function carrying two pairs
    /// is REFUSED, not resolved.
    pub fn BearStart();
    /// Marks the end of a region to protect with Bear.
    pub fn BearEnd();
}
