From 303a52527f08fc7eab6204cfeb790b26020b610e Mon Sep 17 00:00:00 2001 From: Gregory Lirent Date: Mon, 15 Jun 2026 09:02:55 +0300 Subject: [PATCH] Make the guest agent wait until ack; collapse the contract magics The agent no longer self-terminates on a 120s deadline: a VMI host may attach at any time, so the beacon now polls its ack flag indefinitely and exits only once the host sets it. The fixed lifetime was an artifact, not a requirement. The contract drops from three constants to one. The companion magic is derived as the byte-reversed primary (__builtin_bswap64, folded to an immediate at -O2), giving the same 16-byte beacon signature from a single source of truth; the host acknowledges by echoing that value into the ack slot instead of carrying a separate ack constant. --- src/engine/win32/contract.h | 17 +++++++++-------- src/engine/win32/guest.c | 32 +++++++++++++------------------- src/engine/win32/host.c | 4 ++-- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/engine/win32/contract.h b/src/engine/win32/contract.h index 36cb0d9..ed0210c 100644 --- a/src/engine/win32/contract.h +++ b/src/engine/win32/contract.h @@ -1,13 +1,14 @@ -#ifndef CONTRACT_MAGIC0 -#define CONTRACT_MAGIC0 0x3A7C1E94B2D6F058ull +#ifndef CONTRACT_MAGIC +#define CONTRACT_MAGIC 0x3A7C1E94B2D6F058ull #endif -#ifndef CONTRACT_MAGIC1 -#define CONTRACT_MAGIC1 0x9F41D80E6BC57A23ull -#endif - -#ifndef CONTRACT_ACK -#define CONTRACT_ACK 0xACED5EEDACED5EEDull +/* Companion magic = byte-reversed primary, folded to an immediate at -O2 (no + * runtime bswap). One source of truth; in memory magic0 ++ magic1 forms a + * 16-byte palindrome. The host echoes this value into `ack` to acknowledge, so + * there is no separate ack constant. (CONTRACT_MAGIC must not be a byte + * palindrome, or magic1 would equal magic0.) */ +#ifndef CONTRACT_MAGIC_R +#define CONTRACT_MAGIC_R (__builtin_bswap64(CONTRACT_MAGIC)) #endif #ifndef VMIE_CONTRACT_H diff --git a/src/engine/win32/guest.c b/src/engine/win32/guest.c index 7b1851f..e593f61 100644 --- a/src/engine/win32/guest.c +++ b/src/engine/win32/guest.c @@ -5,13 +5,8 @@ #define ACK_POLL_MS 5u #endif -#ifndef ACK_TIMEOUT_MS -#define ACK_TIMEOUT_MS (120*1000u) -#endif - int main(void) { contract* c = VirtualAlloc(NULL, sizeof *c, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - uint32_t timeout = ACK_TIMEOUT_MS; if (c == NULL) { return 1; } @@ -20,22 +15,21 @@ int main(void) { c->va_self = (uint64_t)(uintptr_t)c; c->ack = 0; - c->magic1 = CONTRACT_MAGIC1; + c->magic1 = CONTRACT_MAGIC_R; MemoryBarrier(); - c->magic0 = CONTRACT_MAGIC0; + c->magic0 = CONTRACT_MAGIC; - do { - if (*(volatile uint64_t*)&c->ack == CONTRACT_ACK) { - c->magic0 = 0; - c->magic1 = 0; - VirtualUnlock(c, sizeof *c); - VirtualFree(c, 0, MEM_RELEASE); - break; - } + /* Live until acknowledged. A VMI host may attach at any moment - or only + * after the guest has idled for a long time - so there is no justified + * deadline: poll the ack flag forever, exit only once the host sets it. */ + while (*(volatile uint64_t*)&c->ack != CONTRACT_MAGIC_R) { + Sleep(ACK_POLL_MS); + } - Sleep(ACK_POLL_MS); - } while (timeout -= ACK_POLL_MS); - - return timeout > 0 ? 0 : 255; + c->magic0 = 0; + c->magic1 = 0; + VirtualUnlock(c, sizeof *c); + VirtualFree(c, 0, MEM_RELEASE); + return 0; } \ No newline at end of file diff --git a/src/engine/win32/host.c b/src/engine/win32/host.c index 34db062..2f7d02a 100644 --- a/src/engine/win32/host.c +++ b/src/engine/win32/host.c @@ -62,7 +62,7 @@ static int beacon_find(vmie_mem* m, uint64_t* pa, uint64_t* va) { do { const contract* c = (void*)ptr; - if (c->magic0 == CONTRACT_MAGIC0 && c->magic1 == CONTRACT_MAGIC1) { + if (c->magic0 == CONTRACT_MAGIC && c->magic1 == CONTRACT_MAGIC_R) { *pa = offset_gpa(m, ptr - m->pa); *va = c->va_self; return 0; @@ -204,7 +204,7 @@ static uint32_t ko_export_rva(vmie_mem* m, uintptr_t cr3, uint64_t kbase, const } static void beacon_ack(vmie_mem* m, uint64_t anchor_pa) { - uint64_t ack = CONTRACT_ACK; + uint64_t ack = CONTRACT_MAGIC_R; gpa_write(m, anchor_pa + offsetof(contract, ack), &ack, 8); }