Methods

Egg Hunter

How the egg hunter method works in Sysplant — compile-time egg placement, automatic binary patching before main().

Egg Hunter Method


Principle

The egg_hunter method is a two-phase technique:

  1. At compile time, the inline syscall opcode is replaced by a unique 8-byte marker (the "egg"). The compiled binary contains no syscall instruction.
  2. At runtime, before main() runs, an automatic initialisation function scans the binary's .text section for the egg bytes and patches them in-place with real syscall; ret bytes.

This means the binary that is written to disk never contains syscall opcodes. Static analysis of the file on disk does not reveal syscall stubs. The stubs become functional only in memory at process startup.


Generated stub (x64, from source)

The ASM is identical to direct, except the syscall opcode is replaced by the egg placeholder:

SPT_Syscall:
    pop  rax              ; discard return address
    pop  rax              ; load function hash
    mov  [rsp+ 8], rcx    ; save arg1
    mov  [rsp+16], rdx    ; save arg2
    mov  [rsp+24], r8     ; save arg3
    mov  [rsp+32], r9     ; save arg4
    sub  rsp, 0x28
    mov  rcx, rax
    call SPT_GetSyscallNumber    ; returns SSN in EAX
    add  rsp, 0x28
    mov  rcx, [rsp+ 8]
    mov  rdx, [rsp+16]
    mov  r8,  [rsp+24]
    mov  r9,  [rsp+32]
    mov  r10, rcx
    ##__EGG_MARKER__##    ; 8-byte placeholder — no syscall opcode in the object file
    ret

The ##__EGG_MARKER__## placeholder is expanded at code-generation time into an 8-byte sequence that serves as the egg.


The sanitizer (from stubs/sanitizer.c)

The sanitizer provides two elements.

Replacement bytes

BYTE SPT_EGG_REPLACE[] = {
    0x0f, 0x05,   // syscall
    0x90,         // nop
    0x90,         // nop
    0xC3,         // ret
    0x90,         // nop
    0xCC,         // int3 (debug break)
    0xCC          // int3
};

The egg is replaced with syscall; nop; nop; ret; nop; int3; int3. The extra bytes after ret pad the 8-byte egg slot.

Automatic initialisation

__attribute__((used))
static void __SPT_EggHunterInit(void) {
    SPT_SanitizeSyscalls();
}

This function is registered in the .CRT$XCU section, which the C runtime processes before calling main(). No explicit initialisation call is needed in application code.

You can also call SPT_SanitizeSyscalls() manually if your runtime does not honour .CRT$XCU (e.g., some cross-compilation setups).


How SPT_SanitizeSyscalls works

  1. Locates the current module's PE header.
  2. Finds the .text section.
  3. Calls VirtualProtect to make the .text section read-write-execute (RWX).
  4. Scans the section byte-by-byte for the egg pattern.
  5. Replaces each egg occurrence with SPT_EGG_REPLACE.
  6. Calls VirtualProtect again to restore the original memory protection.

After this function returns, every egg in the .text section has become a functional syscall; nop; nop; ret; nop; int3; int3 sequence.


Characteristics

PropertyValue
syscall opcode on diskNo
syscall opcode in memoryYes — after .CRT$XCU init
Requires manual initNo (automatic via .CRT$XCU)
Manual call availableYes — SPT_SanitizeSyscalls()
Return address during syscallInside your module (same as direct)

Limitations

  • The .CRT$XCU mechanism is a MinGW/GCC feature. Ensure your build toolchain supports CRT init table sections for the auto-init to trigger.
  • VirtualProtect on the .text section is a detectable memory operation. EDR products that monitor RWX page transitions may flag the sanitisation step itself.
Copyright © 2026