🎮 Trainer en C++ con Soporte para Punteros y Teclas de Activación

 

En esta entrada te comparto un proyecto práctico: un trainer en C++ que permite activar y desactivar trucos dentro de un videojuego en ejecución. Este trainer está orientado a modificar valores como vida, munición o velocidad usando teclas rápidas (hotkeys) y se integra con estructuras de memoria complejas mediante el uso de punteros multinivel, al estilo de Cheat Engine.


🧠 ¿Qué es un Trainer?

Un trainer es una aplicación externa que interactúa con un videojuego en tiempo real, permitiendo alterar ciertos valores internos como salud infinita, munición ilimitada, etc. Lo hace accediendo directamente a la memoria del proceso del juego.


🛠️ ¿Qué hace este Trainer?

Este proyecto en C++ permite:

  • Buscar el proceso del juego por su nombre (por ejemplo, target.exe).

  • Acceder a direcciones de memoria simples o punteros con múltiples niveles de offsets.

  • Escribir valores personalizados como 999 para vida o 100 para desactivar el truco.

  • Activar/desactivar trucos presionando teclas como F1, F2, F3.

  • Mostrar una interfaz en consola clara, donde el estado ON/OFF cambia de color (verde para ON, rojo para OFF).

  • Todo sin parpadeos molestos ni mensajes innecesarios.


💻 Estructura del Código

El código se organiza en las siguientes funciones clave:

  • GetPID(): Obtiene el ID del proceso del juego.

  • ResolverDireccion(): Navega punteros para llegar a la dirección final de memoria.

  • EscribirValor(): Escribe en memoria el valor correspondiente según el estado del truco.

  • Dibujar(): Muestra el menú interactivo con los nombres de los trucos, su tecla asociada y el estado (ON/OFF) con colores.


🔐 Requisitos

  • Sistema operativo: Windows

  • Lenguaje: C++

  • Compilador: MinGW o MSVC

  • Permisos de administrador (para escribir en la memoria de otro proceso)

  • Cheat Engine (opcional, para encontrar direcciones y offsets)


📦 Aplicaciones prácticas

Este tipo de trainer es ideal para:

  • Modders que desean probar comportamientos del juego.

  • Jugadores que quieren crear sus propias herramientas tipo Cheat Engine.

  • Aprendizaje avanzado de manipulación de memoria en Windows.


🧪 Próximos pasos

Si deseas mejorar este trainer, podrías agregar:

  • Soporte para lectura de valores (ReadProcessMemory).

  • Interfaz gráfica (GUI) con librerías como ImGui o Qt.

  • Detección automática del módulo base para punteros más avanzados.

  • Soporte para tipos de datos float, byte, o incluso estructuras personalizadas.


¿Te interesa el código fuente completo y funcional? ¡Ya lo publiqué en esta entrada!
También puedes ver una imagen del trainer en acción y un video explicativo próximamente en mi canal de YouTube.



#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

// Configuración
const wchar_t* procesoObjetivo = L"csgo.exe"; // Cambia esto por el nombre real del juego

												// Cheat estructurado con soporte de punteros
struct Cheat {
	string nombre;
	int tecla;
	bool activado;
	uintptr_t base;             // Dirección base (o dirección directa)
	vector offsets; // Lista de offsets si hay punteros
};

// Cheats
vector cheats = {
	{ "Vida",      VK_F1, false, 0x01234567,{} },                             // Dirección directa
	{ "Municion",  VK_F2, false, 0x564A7D34,{ 0x100, 0x50, 0x2E0, 0xEC, 0x18, 0xC, 0x8CC } },             // Ejemplo con punteros
	{ "Velocidad", VK_F3, false, 0x00FFBEEF,{ 0x14, 0x28 } }                    // Otro puntero
};

const int valorON = 999;
const int valorOFF = 13;

// Obtener PID del proceso
DWORD GetPID(const wchar_t* proceso) {
	DWORD pid = 0;
	HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32W entry;
	entry.dwSize = sizeof(entry);
	if (Process32FirstW(snap, &entry)) {
		do {
			if (wcscmp(entry.szExeFile, proceso) == 0) {
				pid = entry.th32ProcessID;
				break;
			}
		} while (Process32NextW(snap, &entry));
	}
	CloseHandle(snap);
	return pid;
}

// Obtener dirección final si hay punteros
uintptr_t ResolverDireccion(HANDLE hProc, uintptr_t base, const vector& offsets) {
	uintptr_t direccion = base;
	SIZE_T bytesRead;
	for (size_t i = 0; i < offsets.size(); ++i) {
		ReadProcessMemory(hProc, (LPCVOID)direccion, &direccion, sizeof(direccion), &bytesRead);
		direccion += offsets[i];
	}
	return direccion;
}

// Escribir valor ON/OFF
void EscribirValor(const Cheat& cheat) {
	DWORD pid = GetPID(procesoObjetivo);
	HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
	if (hProc) {
		uintptr_t direccion = cheat.base;

		if (!cheat.offsets.empty()) {
			direccion = ResolverDireccion(hProc, cheat.base, cheat.offsets);
		}

		int valor = cheat.activado ? valorON : valorOFF;
		WriteProcessMemory(hProc, (LPVOID)direccion, &valor, sizeof(valor), nullptr);

		CloseHandle(hProc);
	}
}

// Dibujar estado
void Dibujar(const vector& lista) {
	COORD cursorPos = { 0, 0 };
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cursorPos);

	cout << "======= TRAINER CON PUNTEROS =======\n\n";

	for (const auto& cheat : lista) {
		cout << left << setw(12) << cheat.nombre;

		if (cheat.tecla >= VK_F1 && cheat.tecla <= VK_F12) {
			int num = cheat.tecla - VK_F1 + 1;
			cout << "[F" << num << "] ";
		}
		else {
			cout << "[?] ";
		}

		HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
		SetConsoleTextAttribute(h, cheat.activado ? FOREGROUND_GREEN | FOREGROUND_INTENSITY
			: FOREGROUND_RED | FOREGROUND_INTENSITY);

		cout << (cheat.activado ? "ON " : "OFF");

		SetConsoleTextAttribute(h, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
		cout << "\n";
	}

	cout << "\nESC para salir\n";
}

int main() {
	SetConsoleTitleA("Trainer con Punteros");

	system("cls");
	for (int i = 0; i < 20; ++i) cout << "\n";

	while (true) {
		for (auto& cheat : cheats) {
			if (GetAsyncKeyState(cheat.tecla) & 1) {
				cheat.activado = !cheat.activado;
				EscribirValor(cheat);
			}
		}

		Dibujar(cheats);

		if (GetAsyncKeyState(VK_ESCAPE) & 1)
			break;

		Sleep(80);
	}

	return 0;
}