Гайд [Гайд] Разработка читов для Minecraft на C++ с использованием JNI

Jadddxxx

Участник
Автор темы
47
26
Здравствуйте, уважаемые пользователи форума.

Данное руководство создано с целью восполнить пробел в русскоязычной информации по разработке читов для Minecraft с использованием C++ и JNI (Java Native Interface). Большинство существующих решений основаны на Java, однако они не всегда применимы.

Основная цель этого метода — создание читов для кастомных лаунчеров (например, на кастомных серверах которые используют свою JVM такие как laby mode, vime world, rust в майне (noad) ), которые запрещают вам делать просто моды, или стандартные длл на джаве . В таких условиях инжект нативной C++ DLL библиотеки в процесс лаунчера и последующее взаимодействие с JVM через JNI становится одним из немногих рабочих подходов.

Это не сравнение "что лучше, Java или C++", а инструкция для решения конкретной задачи, где стандартные методы не работают.

**Java Native Interface (JNI)** — это стандартный механизм в платформе Java, который позволяет Java-коду взаимодействовать с приложениями и библиотеками, написанными на других языках, таких как C и C++.

В нашем случае JNI выступает в роли "моста". Процесс Minecraft работает в виртуальной машине Java (JVM). Наш чит, написанный на C++, будет представлять собой внешнюю DLL-библиотеку. JNI позволит этой библиотеке после инжекта в процесс "достучаться" до JVM, находить Java-классы игры, читать значения их полей (координаты, здоровье) и вызывать их методы (атака, прыжок).



**Шаг 1: Подготовка рабочего окружения**

Для работы потребуется следующее программное обеспечение:

1. **IDE для C++:** **Visual Studio 2019/2022** с установленным набором инструментов "Разработка классических приложений на C++".
2. **JDK (Java Development Kit):** Критически важный компонент. Версия JDK **должна соответствовать версии Java, с которой запускается Minecraft**. Неправильный выбор приведет к ошибкам.

* Для Minecraft **1.8 - 1.16.5**: Используйте **JDK 8**.
* Для Minecraft **1.17.x**: Используйте **JDK 16**.
* Для Minecraft **1.18 и новее**: Используйте **JDK 17**.

Найти и скачать нужную версию JDK можно в любом поисковике.

**Настройка проекта в Visual Studio:**
1. Создайте новый проект типа **"Библиотека динамической компоновки (DLL)"**.
2. Откройте **Свойства проекта**.
3. Перейдите в `C/C++ -> Общие -> Дополнительные каталоги включаемых файлов`.
4. Добавьте два пути к установленному JDK:
* `C:\Program Files\Java\jdk-ВЕРСИЯ\include`
* `C:\Program Files\Java\jdk-ВЕРСИЯ\include\win32`
*(Замените `jdk-ВЕРСИЯ` на вашу реальную папку, например, `jdk1.8.0_202`)*.

После этого вы сможете использовать `#include <jni.h>` в вашем коде.


### **Шаг 2: Инициализация JNI и подключение к JVM**

После инжекта нашей DLL в процесс игры, первым делом необходимо получить доступ к окружению JNI. Это делается через поиск активной JVM и присоединение к ней текущего потока.

Базовый код для `dllmain.cpp`:

Базовый код для `dllmain.cpp`::
#include <Windows.h>
#include <jni.h>

JavaVM* g_JavaVM = nullptr;
JNIEnv* g_JniEnv = nullptr;

DWORD WINAPI MainCheatThread(HMODULE hModule) {
    // 1. Получаем указатель на созданную виртуальную машину Java
    jsize vmCount;
    if (JNI_GetCreatedJavaVMs(&g_JavaVM, 1, &vmCount) != JNI_OK || vmCount == 0) {
        FreeLibraryAndExitThread(hModule, 0);
        return 0;
    }

    // 2. Присоединяем текущий поток к JVM для получения рабочего окружения JNIEnv
    if (g_JavaVM->AttachCurrentThread((void**)&g_JniEnv, nullptr) != JNI_OK) {
        FreeLibraryAndExitThread(hModule, 0);
        return 0;
    }

    // --- Начиная с этого момента, g_JniEnv является валидным указателем ---
    // --- Здесь будет размещена основная логика чита ---

    while (!(GetAsyncKeyState(VK_DELETE) & 1)) {
        // Главный цикл работы чита
        // Здесь будут вызываться функции 
        Sleep(15);
    }

    // 3. Отсоединяем поток от JVM перед выгрузкой библиотеки
    g_JavaVM->DetachCurrentThread();
    FreeLibraryAndExitThread(hModule, 0);
    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
        DisableThreadLibraryCalls(hModule);
        CloseHandle(CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)MainCheatThread, hModule, 0, nullptr));
    }
    return TRUE;
}


### **Шаг 3: Обфускация (Vanilla) и ее отсутствие (Forge/Fabric)**

Это ключевой аспект, который определяет сложность разработки.

* **Vanilla Minecraft (чистая версия):** Исходный код игры **обфусцирован**. Это значит, что имена классов, методов и полей заменены на короткие и бессмысленные (например, класс `net.minecraft.entity.player.EntityPlayer` может называться `azy`). Работа напрямую с такими именами крайне затруднительна и требует реверс-инжиниринга.
* **Forge / Fabric:** Данные загрузчики модов в процессе своей работы **деобфусцируют** код игры, возвращая ему осмысленные имена. Это значительно упрощает разработку, так как можно обращаться к классам и полям по их логичным названиям (`thePlayer`, `theWorld`, `posX` и т.д.).

**Рекомендация:**
Для обучения и отладки настоятельно рекомендуется использовать клиент с установленным Forge или Fabric.

**Шаг 4: Поиск ключевых классов и полей (fields)**


Имея `g_JniEnv`, мы можем начать взаимодействовать с игрой. Основная задача — получить доступ к объекту игрока и мира.

**Алгоритм получения объекта игрока:**
1. Найти класс `net/minecraft/client/Minecraft` с помощью `recaf, jadx (вам нужно открыть дамп и там искать классы, а уже в классах flied`.
2. Найти его статический метод `getMinecraft()`.
3. Вызвать этот метод, чтобы получить экземпляр класса `Minecraft`.
4. Из этого экземпляра получить поле `thePlayer`.

C++:
// 1. Находим класс Minecraft (точки заменяются на слеши)
jclass minecraftClass = g_JniEnv->FindClass("net/minecraft/client/Minecraft");

// 2. Находим статический метод getMinecraft().
// "func_71410_x" — это MCP-имя для версии 1.8.9. Для других версий оно может отличаться.
// "()Lnet/minecraft/client/Minecraft;" — это JNI-сигнатура метода.
jmethodID getMinecraftMethod = g_JniEnv->GetStaticMethodID(minecraftClass, "func_71410_x", "()Lnet/minecraft/client/Minecraft;");

// 3. Вызываем метод
jobject minecraftInstance = g_JniEnv->CallStaticObjectMethod(minecraftClass, getMinecraftMethod);

// 4. Находим и получаем поле thePlayer
// "field_71439_g" — MCP-имя для thePlayer
jfieldID playerField = g_JniEnv->GetFieldID(minecraftClass, "field_71439_g", "Lnet/minecraft/client/entity/EntityPlayerSP;");
jobject playerInstance = g_JniEnv->GetObjectField(minecraftInstance, playerField);

*Имена `func_...` и `field_...` являются частью MCP (Mod Coder Pack). Для поиска актуальных имен для вашей версии Minecraft используйте онлайн-базы данных MCP-маппингов.*

Получив `playerInstance`, вы можете получить доступ к его полям.

* `posX`, `posY`, `posZ` (тип `double`) - Координаты.
* `motionX`, `motionY`, `motionZ` (тип `double`) - Вектор движения.
* `onGround` (тип `boolean`) - Находится ли игрок на земле.
* `hurtTime` (тип `int`) - Таймер с момента получения урона.
* `inventory` (тип `InventoryPlayer`) - Инвентарь игрока.

Для доступа к ним используется `g_JniEnv->GetFieldID()` для получения ID, а затем `g_JniEnv->GetDoubleField()`, `g_JniEnv->GetBooleanField()` и т.д. для чтения значения.

**Шаг 5: Работа с обфусцированным кодом: Дампинг**

Если ваша цель — ванильный клиент или сервер с собственными модами без деобфускации, вам потребуется провести анализ.
Основной метод — **дампинг классов**. С помощью спецаильных утилит (например, Java Class Dumper...) можно выгрузить все загруженные в JVM классы в виде `.class` файлов. Далее эти файлы открываются в декомпиляторе (JD-GUI, Luyten, Recaf) для анализа их логики и поиска нужных полей и методов по косвенным признакам (например, по используемым строковым константам или числовым значениям).


### **Пример реализации: функция FullBright (максимальная яркость)**

Простая функция, отключающая тени и делающая мир полностью освещенным.

C++:
// ... (после получения minecraftInstance)

// Получаем объект настроек gameSettings
jfieldID settingsField = g_JniEnv->GetFieldID(minecraftClass, "field_71474_y", "Lnet/minecraft/client/settings/GameSettings;");
jobject settingsInstance = g_JniEnv->GetObjectField(minecraftInstance, settingsField);

// Получаем класс GameSettings
jclass settingsClass = g_JniEnv->GetObjectClass(settingsInstance);

// Получаем поле gammaSetting (яркость)
jfieldID gammaField = g_JniEnv->GetFieldID(settingsClass, "field_74333_g", "F"); // "F" - сигнатура для типа float

// Устанавливаем новое значение
g_JniEnv->SetFloatField(settingsInstance, gammaField, 100.0f);

// При деактивации функции следует восстановить оригинальное значение.

**Заключение**

Данное руководство описывает лишь базовые шаги для начала работы. Дальнейшее развитие включает в себя изучение рендеринга (ESP) через хукинг OpenGL/DirectX, перехват Java-методов для создания более сложных функций (Killaura, Aimbot) и работу со списком сущностей в мире.

Надеюсь, эта информация будет полезной для ваших исследований.

**Отказ от ответственности:** Вся информация предоставлена исключительно в образовательных и исследовательских целях. Автор не несет ответственности за ее использование, которое может привести к блокировке игровых аккаунтов.
 

Jadddxxx

Участник
Автор темы
47
26
Для разработчиков читов для кастомных серверов (Vime World, Rust Ex Remake, Rust me (не получиться так ведь ВМП стоит).)
Тот-же вайм блокирует любые подключения к jvm.dll - java.dll
Как обойти ? Легко.
Пример -
пример:
bool CheckJavaSignature() {
    HANDLE hProcess = GetCurrentProcess();
    HMODULE hModules[1024];
    DWORD cbNeeded;
   
    if (EnumProcessModules(hProcess, hModules, sizeof(hModules), &cbNeeded)) {
        for (DWORD i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
            BYTE buffer[8];
            SIZE_T bytesRead;
           
            if (ReadProcessMemory(hProcess, hModules[i], buffer, sizeof(buffer), &bytesRead)) {
                if (bytesRead >= 8 &&
                    buffer[0] == 0xCA && buffer[1] == 0xFE &&
                    buffer[2] == 0xBA && buffer[3] == 0xBE &&
                    buffer[4] == 0x00 && buffer[5] == 0x00) {
                    return true;
                }
               
                if (bytesRead >= 4 &&
                    buffer[0] == 0xCA && buffer[1] == 0xFE &&
                    buffer[2] == 0xBA && buffer[3] == 0xBE) {
                    return true;
                }
            }
        }
    }
    return false;
}
говорю сразу, для всех проектов может не подойти ведь это стандартные сигнатуры jvm .
Пояснения для чайников -
Так как вайм чекает ДЛЛ , мы берем и подключаемься к джаве через главную сигнатуру :)
Почему 2 сигны ?
Ведь что-бы вам не искать на разных версиях официальных стоит 2 разных сигны. Тоесть если одна не ворк будет 2 юзаться

Здравствуйте, уважаемые пользователи форума.

Данное руководство создано с целью восполнить пробел в русскоязычной информации по разработке читов для Minecraft с использованием C++ и JNI (Java Native Interface). Большинство существующих решений основаны на Java, однако они не всегда применимы.

Основная цель этого метода — создание читов для кастомных лаунчеров (например, на кастомных серверах которые используют свою JVM такие как laby mode, vime world, rust в майне (noad) ), которые запрещают вам делать просто моды, или стандартные длл на джаве . В таких условиях инжект нативной C++ DLL библиотеки в процесс лаунчера и последующее взаимодействие с JVM через JNI становится одним из немногих рабочих подходов.

Это не сравнение "что лучше, Java или C++", а инструкция для решения конкретной задачи, где стандартные методы не работают.

**Java Native Interface (JNI)** — это стандартный механизм в платформе Java, который позволяет Java-коду взаимодействовать с приложениями и библиотеками, написанными на других языках, таких как C и C++.

В нашем случае JNI выступает в роли "моста". Процесс Minecraft работает в виртуальной машине Java (JVM). Наш чит, написанный на C++, будет представлять собой внешнюю DLL-библиотеку. JNI позволит этой библиотеке после инжекта в процесс "достучаться" до JVM, находить Java-классы игры, читать значения их полей (координаты, здоровье) и вызывать их методы (атака, прыжок).



**Шаг 1: Подготовка рабочего окружения**

Для работы потребуется следующее программное обеспечение:

1. **IDE для C++:** **Visual Studio 2019/2022** с установленным набором инструментов "Разработка классических приложений на C++".
2. **JDK (Java Development Kit):** Критически важный компонент. Версия JDK **должна соответствовать версии Java, с которой запускается Minecraft**. Неправильный выбор приведет к ошибкам.

* Для Minecraft **1.8 - 1.16.5**: Используйте **JDK 8**.
* Для Minecraft **1.17.x**: Используйте **JDK 16**.
* Для Minecraft **1.18 и новее**: Используйте **JDK 17**.

Найти и скачать нужную версию JDK можно в любом поисковике.

**Настройка проекта в Visual Studio:**
1. Создайте новый проект типа **"Библиотека динамической компоновки (DLL)"**.
2. Откройте **Свойства проекта**.
3. Перейдите в `C/C++ -> Общие -> Дополнительные каталоги включаемых файлов`.
4. Добавьте два пути к установленному JDK:
* `C:\Program Files\Java\jdk-ВЕРСИЯ\include`
* `C:\Program Files\Java\jdk-ВЕРСИЯ\include\win32`
*(Замените `jdk-ВЕРСИЯ` на вашу реальную папку, например, `jdk1.8.0_202`)*.

После этого вы сможете использовать `#include <jni.h>` в вашем коде.


### **Шаг 2: Инициализация JNI и подключение к JVM**

После инжекта нашей DLL в процесс игры, первым делом необходимо получить доступ к окружению JNI. Это делается через поиск активной JVM и присоединение к ней текущего потока.

Базовый код для `dllmain.cpp`:

Базовый код для `dllmain.cpp`::
#include <Windows.h>
#include <jni.h>

JavaVM* g_JavaVM = nullptr;
JNIEnv* g_JniEnv = nullptr;

DWORD WINAPI MainCheatThread(HMODULE hModule) {
    // 1. Получаем указатель на созданную виртуальную машину Java
    jsize vmCount;
    if (JNI_GetCreatedJavaVMs(&g_JavaVM, 1, &vmCount) != JNI_OK || vmCount == 0) {
        FreeLibraryAndExitThread(hModule, 0);
        return 0;
    }

    // 2. Присоединяем текущий поток к JVM для получения рабочего окружения JNIEnv
    if (g_JavaVM->AttachCurrentThread((void**)&g_JniEnv, nullptr) != JNI_OK) {
        FreeLibraryAndExitThread(hModule, 0);
        return 0;
    }

    // --- Начиная с этого момента, g_JniEnv является валидным указателем ---
    // --- Здесь будет размещена основная логика чита ---

    while (!(GetAsyncKeyState(VK_DELETE) & 1)) {
        // Главный цикл работы чита
        // Здесь будут вызываться функции
        Sleep(15);
    }

    // 3. Отсоединяем поток от JVM перед выгрузкой библиотеки
    g_JavaVM->DetachCurrentThread();
    FreeLibraryAndExitThread(hModule, 0);
    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
        DisableThreadLibraryCalls(hModule);
        CloseHandle(CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)MainCheatThread, hModule, 0, nullptr));
    }
    return TRUE;
}


### **Шаг 3: Обфускация (Vanilla) и ее отсутствие (Forge/Fabric)**

Это ключевой аспект, который определяет сложность разработки.

* **Vanilla Minecraft (чистая версия):** Исходный код игры **обфусцирован**. Это значит, что имена классов, методов и полей заменены на короткие и бессмысленные (например, класс `net.minecraft.entity.player.EntityPlayer` может называться `azy`). Работа напрямую с такими именами крайне затруднительна и требует реверс-инжиниринга.
* **Forge / Fabric:** Данные загрузчики модов в процессе своей работы **деобфусцируют** код игры, возвращая ему осмысленные имена. Это значительно упрощает разработку, так как можно обращаться к классам и полям по их логичным названиям (`thePlayer`, `theWorld`, `posX` и т.д.).

**Рекомендация:**
Для обучения и отладки настоятельно рекомендуется использовать клиент с установленным Forge или Fabric.

**Шаг 4: Поиск ключевых классов и полей (fields)**

Имея `g_JniEnv`, мы можем начать взаимодействовать с игрой. Основная задача — получить доступ к объекту игрока и мира.

**Алгоритм получения объекта игрока:**
1. Найти класс `net/minecraft/client/Minecraft` с помощью `recaf, jadx (вам нужно открыть дамп и там искать классы, а уже в классах flied`.
2. Найти его статический метод `getMinecraft()`.
3. Вызвать этот метод, чтобы получить экземпляр класса `Minecraft`.
4. Из этого экземпляра получить поле `thePlayer`.

C++:
// 1. Находим класс Minecraft (точки заменяются на слеши)
jclass minecraftClass = g_JniEnv->FindClass("net/minecraft/client/Minecraft");

// 2. Находим статический метод getMinecraft().
// "func_71410_x" — это MCP-имя для версии 1.8.9. Для других версий оно может отличаться.
// "()Lnet/minecraft/client/Minecraft;" — это JNI-сигнатура метода.
jmethodID getMinecraftMethod = g_JniEnv->GetStaticMethodID(minecraftClass, "func_71410_x", "()Lnet/minecraft/client/Minecraft;");

// 3. Вызываем метод
jobject minecraftInstance = g_JniEnv->CallStaticObjectMethod(minecraftClass, getMinecraftMethod);

// 4. Находим и получаем поле thePlayer
// "field_71439_g" — MCP-имя для thePlayer
jfieldID playerField = g_JniEnv->GetFieldID(minecraftClass, "field_71439_g", "Lnet/minecraft/client/entity/EntityPlayerSP;");
jobject playerInstance = g_JniEnv->GetObjectField(minecraftInstance, playerField);

*Имена `func_...` и `field_...` являются частью MCP (Mod Coder Pack). Для поиска актуальных имен для вашей версии Minecraft используйте онлайн-базы данных MCP-маппингов.*

Получив `playerInstance`, вы можете получить доступ к его полям.

* `posX`, `posY`, `posZ` (тип `double`) - Координаты.
* `motionX`, `motionY`, `motionZ` (тип `double`) - Вектор движения.
* `onGround` (тип `boolean`) - Находится ли игрок на земле.
* `hurtTime` (тип `int`) - Таймер с момента получения урона.
* `inventory` (тип `InventoryPlayer`) - Инвентарь игрока.

Для доступа к ним используется `g_JniEnv->GetFieldID()` для получения ID, а затем `g_JniEnv->GetDoubleField()`, `g_JniEnv->GetBooleanField()` и т.д. для чтения значения.

**Шаг 5: Работа с обфусцированным кодом: Дампинг**

Если ваша цель — ванильный клиент или сервер с собственными модами без деобфускации, вам потребуется провести анализ.
Основной метод — **дампинг классов**. С помощью спецаильных утилит (например, Java Class Dumper...) можно выгрузить все загруженные в JVM классы в виде `.class` файлов. Далее эти файлы открываются в декомпиляторе (JD-GUI, Luyten, Recaf) для анализа их логики и поиска нужных полей и методов по косвенным признакам (например, по используемым строковым константам или числовым значениям).


### **Пример реализации: функция FullBright (максимальная яркость)**

Простая функция, отключающая тени и делающая мир полностью освещенным.

C++:
// ... (после получения minecraftInstance)

// Получаем объект настроек gameSettings
jfieldID settingsField = g_JniEnv->GetFieldID(minecraftClass, "field_71474_y", "Lnet/minecraft/client/settings/GameSettings;");
jobject settingsInstance = g_JniEnv->GetObjectField(minecraftInstance, settingsField);

// Получаем класс GameSettings
jclass settingsClass = g_JniEnv->GetObjectClass(settingsInstance);

// Получаем поле gammaSetting (яркость)
jfieldID gammaField = g_JniEnv->GetFieldID(settingsClass, "field_74333_g", "F"); // "F" - сигнатура для типа float

// Устанавливаем новое значение
g_JniEnv->SetFloatField(settingsInstance, gammaField, 100.0f);

// При деактивации функции следует восстановить оригинальное значение.

**Заключение**

Данное руководство описывает лишь базовые шаги для начала работы. Дальнейшее развитие включает в себя изучение рендеринга (ESP) через хукинг OpenGL/DirectX, перехват Java-методов для создания более сложных функций (Killaura, Aimbot) и работу со списком сущностей в мире.

Надеюсь, эта информация будет полезной для ваших исследований.

**Отказ от ответственности:** Вся информация предоставлена исключительно в образовательных и исследовательских целях. Автор не несет ответственности за ее использование, которое может привести к блокировке игровых аккаунтов.
Скоро выйдет полноценный видео урок по созданию читов.
на 40 минут либо же 1 час.
Обговорим все тонкости создания читов, дампинга, реверс инженеринга, поиск классов и так далее.
 
Последнее редактирование:
  • Нравится
Реакции: Shelok_Kholmes и etereon