ECSTASY

Technical Reference

System Architecture

A complete breakdown of how the runtime operates — from the moment you click Launch to clean shutdown. The system is split into native C++ components that handle process-level operations and a Java payload that implements application logic through bytecode instrumentation.

Execution Pipeline

01

Launcher GUI

Process scan

02

DLL Injection

LoadLibrary

03

JVM Attach

JNI_GetCreatedJavaVMs

04

Agent Load

Agent_OnAttach

05

JAR Deploy

Instrumentation API

06

Java Init

Bootstrap.init()

07

Bytecode Transform

ClassFileTransformer

08

Runtime Hooks

onRender / onPacket

09

Shutdown

Revert + Cleanup

Full Lifecycle

Every phase in detail — what happens, where it happens, and exactly how.

01
Launch & Target AcquisitionInjector

The launcher executable starts with a native GUI and begins scanning for Minecraft's GLFW window handle. Once found, it resolves the process ID and waits for the JVM to fully initialize before proceeding.

  • Scans for GLFW30 window class via FindWindow
  • Resolves target PID from the window handle
  • Extracts the embedded loader DLL to a uniquely-named temp file using GetTickCount() to avoid file-lock collisions on re-injection
02
DLL InjectionInjector

A remote thread is created inside the target process to call LoadLibrary on the extracted DLL. This causes Windows to map our loader into Minecraft's address space and execute DllMain.

  • Opens the target process with PROCESS_ALL_ACCESS
  • Allocates memory in the remote process via VirtualAllocEx
  • Writes the DLL path string into remote memory
  • Creates a remote thread targeting LoadLibraryA
03
JVM Discovery & AttachNative Loader

Once inside the game process, the loader DLL spawns a background thread. It enumerates all loaded modules to locate the JVM's exported JNI_GetCreatedJavaVMs function, then attaches the current thread to the Java runtime.

  • EnumProcessModules scans all loaded DLLs in the process
  • GetProcAddress searches each module for JNI_GetCreatedJavaVMs
  • The discovered function returns the active JavaVM pointer
  • AttachCurrentThread binds the native thread to JNI, yielding a JNIEnv
04
Agent Loading via Instrumentation APINative Loader

The loader locates the JVM's instrument.dll and calls its Agent_OnAttach function with the path to the embedded JAR. This loads the JAR as a Java agent, triggering agentmain() which captures the Instrumentation instance for later bytecode transformation.

  • Locates instrument.dll in the JVM's bin directory via GetModuleHandle
  • Resolves the Agent_OnAttach export from instrument.dll
  • Calls Agent_OnAttach with the temp JAR path as argument
  • agentmain() stores the Instrumentation instance and registers it via System.getProperties()
  • No JVMTI needed — uses the standard Java Instrumentation API
05
Payload DeliveryNative Loader

The Java agent JAR is embedded inside the loader DLL as a compiled byte array. It is extracted to a temp file before agent loading. After load, the temp JAR is immediately deleted from disk as an anti-detection measure.

  • JAR binary is compiled into the DLL at build time via jar_to_array.py
  • Written to %TEMP% with a tick-count-based unique filename
  • Agent loading makes classes available through the app classloader
  • Temp JAR is erased from disk after successful load
  • DLL PE headers are zeroed in memory to reduce forensic footprint
06
Bootstrap InitializationJava Payload

The native loader finds and invokes Bootstrap.init() via JNI. This initializes the module system, registers classloader-safe callbacks via System.getProperties(), installs bytecode transformers, and starts the IPC pipe server.

  • Class.forName loads Bootstrap through the app classloader
  • Callbacks registered in System.getProperties() as Consumer/Function objects
  • ModuleManager.init() registers all combat, movement, and utility modules
  • TransformerRegistry receives RenderTransformer and KeepAliveTransformer
  • PipeServer opens a named pipe for communication with the injector GUI
07
Bytecode TransformationJava Payload

Bootstrap registers a ClassFileTransformer with the Instrumentation API, then calls retransformClasses on the target Minecraft classes. The transformer intercepts each class and uses ASM to inject callback hooks that route through System.getProperties() — avoiding classloader conflicts entirely.

  • Instrumentation.addTransformer() registers the ClassFileTransformer
  • retransformClasses() is called on MinecraftClient, InGameHud, and ClientConnection
  • ASM ClassWriter + MethodVisitor inject hooks into render, HUD, and packet methods
  • Hooks call System.getProperties().get(key) to retrieve Consumer/Function callbacks
  • This bridge design avoids LinkageError from classloader mismatches
  • Transformer is retained for later cleanup/retransformation
08
Runtime Hooks ActiveJava Payload

With bytecode transformed, the injected callbacks fire every game tick. The render hook drives the module system and key handler. The packet hook enables network interception. All module logic executes within these entry points.

  • Bootstrap.onRender() — called every render frame, ticks all enabled modules
  • Bootstrap.onHudDraw() — called during HUD rendering for overlay drawing
  • Bootstrap.onPacketReceive() — intercepts inbound packets, can cancel processing
  • MCReflect initializes on first render call, caching Minecraft field/method handles
09
Clean Shutdown & EjectionNative Loader + Java Payload

Shutdown is triggered via an IPC UNINJECT command from the launcher. Bootstrap.shutdown() disables all modules, clears transformer state, and calls Instrumentation.retransformClasses() with an empty transformer — restoring original bytecode. System properties are cleaned and all traces are removed.

  • UNINJECT command received via named pipe IPC
  • Bootstrap.shutdown() disables all active modules
  • TransformerRegistry cleared, retransformClasses() restores original bytecode
  • System.getProperties() entries for callbacks are removed
  • DLL PE headers already zeroed; temp JAR already deleted
  • Launcher GUI shows uninject confirmation popup

Components

The four subsystems that compose the project.

Injector

C++ Executable

The main launcher with a native GUI. Handles process discovery, DLL extraction from its embedded binary payload, remote thread injection into the target process, and an IPC named pipe for bidirectional communication with the running payload.

Win32 APICreateRemoteThreadNamed Pipes

Native Loader

C++ DLL

The core runtime that bridges Windows and the JVM. Discovers the running Java VM, loads the Java agent via the Instrumentation API's Agent_OnAttach, calls Bootstrap.init() via JNI, and handles anti-detection measures like PE header erasure and temp file cleanup.

JNIInstrumentation APIAgent_OnAttachDllMain

Java Payload

Java Agent JAR

The application logic layer. Contains the module framework, bytecode transformers using ASM, a classloader-safe callback bridge via System.getProperties(), packet interception hooks, a named-pipe IPC server, and the full shutdown/revert pathway that restores original class bytecode.

ASM BytecodeClassFileTransformerReflection

Website

Next.js Portal

The client-facing web portal built with Next.js and Supabase. Manages user authentication, license key generation and validation, secure gated downloads of the compiled binaries, and a client suite for purchase history.

Next.jsSupabase AuthServer Actions

Bytecode Transformation Flow

The core hook mechanism — how the Java Instrumentation API and ASM cooperate to modify Minecraft classes at runtime using a classloader-safe properties bridge.

Bootstrap.init()Register callbacks in System.getProperties()
InstrumentationaddTransformer()
retransformClasses()Target Minecraft classes
ClassFileTransformertransform(name, bytes)
ASM VisitorsClassWriter + MethodVisitor
Inject HooksProperties bridge callbacks
Hooked Method RunsMinecraft render/packet tick
System.getProperties().get()Retrieve Consumer/Function
Bootstrap CallbacksonRender / onPacket

Build Pipeline

The one-click build system compiles Java and C++ components, then embeds each artifact into the next layer — producing a single self-contained executable.

Step 1

Compile JAR

Ant builds the Java payload into a single JAR with all transformers and modules.

Step 2

Embed JAR

jar_to_array.py converts the JAR binary into a C++ header as a const byte array.

Step 3

Build DLL

CMake compiles the native loader with the embedded JAR into loader.dll.

Step 4

Embed DLL

dll_to_array.py converts the DLL binary into a C++ header for the injector.

Step 5

Build Launcher

CMake compiles the injector, producing the final ecstasy_v4.exe.

Nesting Architecture

Each artifact is embedded inside the next, like Russian nesting dolls. The final executable contains everything needed for a complete injection cycle.

ecstasy_v4.exeLauncher
loader.dllNative Loader
payload.jarJava Agent
Transformers + Modules + IPC