Grave ships with two injectors per platform: a friendly one for everyday use, and a raw one for users who want stealth.
Windows
On Windows, Grave is distributed as two binaries that load the same payload in different ways. No administrator permissions are required.
The user-friendly path. Run the executable and it reflectively loads the client into a target Minecraft process, with no extra files and no setup. This is what almost everyone should use.
For users who want to drive injection themselves. The .bin file is
position-independent - raw bytes that execute correctly at any
address. There is no PE header and no entry point in the usual sense.
You read it, allocate executable memory in a host process,
copy it in, and jump to its first byte.
The minimal example below shows the technique. It spawns a suspended copy of itself as a sacrificial host, writes the shellcode in, flips the page to RX, and starts a remote thread at the base of the allocation:
#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <cstdlib> int main() { // 1. Read the position-independent shellcode. HANDLE f = CreateFileA("Grave.bin", GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); DWORD size = GetFileSize(f, nullptr); auto* bin = (BYTE*)malloc(size); DWORD read = 0; ReadFile(f, bin, size, &read, nullptr); CloseHandle(f); // 2. Spawn a suspended process to host the payload. char self[MAX_PATH]; GetModuleFileNameA(nullptr, self, sizeof(self)); STARTUPINFOA si{ sizeof(si) }; PROCESS_INFORMATION pi{}; CreateProcessA(self, nullptr, nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi); // 3. Allocate, write, and flip to executable in the host. void* base = VirtualAllocEx(pi.hProcess, nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); WriteProcessMemory(pi.hProcess, base, bin, size, nullptr); DWORD old; VirtualProtectEx(pi.hProcess, base, size, PAGE_EXECUTE_READWRITE, &old); free(bin); // 4. Run from the first byte and wait. The shellcode cleans up after itself. HANDLE th = CreateRemoteThread(pi.hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)base, nullptr, 0, nullptr); WaitForSingleObject(th, INFINITE); CloseHandle(th); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; }
The host process is just somewhere for the bytes to run, it does not need to be
Minecraft. Once executing, the shellcode locates the Minecraft process on its own
and performs injection from there. By the time WaitForSingleObject
returns, the shellcode has zeroed and freed every region it used, including the
allocation it ran from.
Once you're injected, browse modules in the sidebar to see what's available.