Novedades

Guia Guía de Hacking de Juegos en Español

Estado
Cerrado para nuevas respuestas.


c0de

Administrador
Desde
19 Abr 2020
Mensajes
329

Guía de Hacking de Juegos en Español​

Una guía para principiantes para comprender las técnicas del hacking de juegos

1651699872383.png

Creado a partir de materiales publicados originalmente entre 2019 y 2021 en https://
gamehacking.academy y Game Hacking Academy. No me atribuyo los créditos por grandioso trabajo, lo único que hare es traducirlo al español y quizás en alguno aumentar o mejorarlo de acuerdo a mi experiencia


Tabla de contenido
  1. Introducción
  2. Recursos Externos
  3. Básicos
    1. Fundamentos de la computadora
    2. Fundamentos de los juegos
    3. Fundamentos de la piratearía
    4. Configuración de un laboratorio en una maquina virtual
    5. Hackear memoria
  4. Depurar y Reversear (Debugging & Reversing)
    1. Fundamentos de la Depuración
    2. Fundamentos del Reversear
    3. Cambiando el Código del Juego
    4. Reverseando Código
    5. Cuevas de Código (Code Cave)
    6. Usando Cuevas de Código
    7. Asignación Dinámica de Memoria (Dynamic Memory Allocation)
    8. Derrotando al DMA
  5. Programación
    1. Fundamentos de la Programación
    2. Hack de Memoria Externa
    3. Hack de Memoria DLL
    4. Cuevas de Código y DLL
    5. Imprimiendo Texto
  6. RTS Hacks
    1. StatHack
    2. Map Hack
    3. Macro Bot
  7. FPS Hacks
    1. Fundamentos 3D
    2. Wallhack (Memoria)
    3. Wallhack (OpenGL)
    4. Chams (OpenGL)
    5. Triggerbot
    6. Aimbot
    7. No Recoil
    8. Radar Hack
    9. ESP
    10. Multihack
  8. Multijugador (Multiplayer)
    1. Fundamentos del Multijugador
    2. Análisis de paquetes
    3. Reverseando paquetes
    4. Creando un cliente externo
    5. Proxiando trafico TCP
  9. Herramientas de desarrollo
    1. Inyector DLL
    2. Escaneo de patrones (Pattern Scanner)
    3. Escaneo de memoria (Memory Scanner)
    4. Desesambladores
    5. Depuradores
    6. Registro de Llamadas (Call Logger)

Continuara en unas horas

Saludos
 
Última edición:

1. INTRODUCCION

Hackear juegos requiere una combinación única de revertir(reversing), administración de memoria, redes y habilidades de seguridad. A pesar de que la piratería ética se ha disparado en popularidad, la piratería de juegos todavía ocupa un nicho muy pequeño en la comunidad de seguridad más amplia. Mientras que puede no tener el mismo atractivo para los titulares que Chrome 0day o una fuga masiva de datos, la sensación única de crear un aimbot que funcione para un juego y luego destruir un servidor con él es difícil de replicar en cualquier otro medio. Cuando comencé a aprender a hackear juegos hace años, los recursos estaban distribuidos en varios sitios y eran muy escasos. Por lo general, encontrará una sección de código vinculado a un sitio roto. A continuación, buscaría algún foro que tuviera alguna parte del sitio roto en una publicación y juntar la información. Mientras esto sera recompensada con una búsqueda minuciosa, fue una enorme inversión de tiempo. En estos días, hay varios lugares donde puede encontrar una variedad de información sobre la piratería de juegos. Puedes encontrar código repetitivo para casi cualquier motor, junto con las compensaciones(offset) de memoria para cualquier estructura que le puede interesar. Sin embargo, un área aún desatendida por toda la información disponible hoy en día son los conceptos. Y fundamentos detrás de las compensaciones.

Mi esperanza es que este tutorial ayude a llenar esos vacíos.

2. RECURSOS EXTERNOS

Esta es una lista de todos los recursos externos, como herramientas y juegos, utilizados en este libro. Están ordenados por su apariencia. Esto tiene la intención de ayudar si planea leer esto reservar en un lugar sin acceso a Internet.

3. BASICOS

3.1 COMPONENTES DE UNA COMPUTADORA

Una computadora típica tiene varios componentes conectados. Entre los más importantes son:
  • Disco Duro
  • RAM
  • Tarjeta de Video
  • La Placa Madre
  • CPU
Si tuviera que quitar el costado de una computadora de escritorio, las partes podrían colocarse en un configuración así:

1654321644303.png

Para nuestros propósitos, solo abordaremos brevemente los primeros cuatro componentes, y luego se centrará en la CPU en la siguiente sección:
  • Los discos duros son responsables de almacenar archivos grandes, como fotos, ejecutables, o archivos del sistema.
  • La RAM contiene datos a los que se debe acceder rápidamente. Los datos se cargan en la RAM desde el disco duro.
  • Las tarjetas de video son las encargadas de mostrar los gráficos al monitor.
  • Las placas base(placa madre o motherboard) unen todos estos componentes y les permiten comunicar.

3.1.1 EL CPU

La CPU es el cerebro de la computadora. Es responsable de ejecutar instrucciones. Estas instrucciones son simples y varían según la arquitectura. Por ejemplo, una instrucción podría sumar dos números. Para acelerar el tiempo de ejecución, la CPU tiene varias áreas especiales donde puede almacenar y modificar datos. Estos se llaman registros.

1654322122984.png

3.1.2 INSTRUCCIONES

Todos los programas de ordenador están formados por una serie de instrucciones. Como discutimos anteriormente, una instrucción es simplista y generalmente solo hace una cosa. Por ejemplo, las siguientes son algunas de las instrucciones que se encuentran en la mayoría de las arquitecturas:
  • Añadir dos números
  • Restar dos números
  • Compara dos números
  • Mover un número a una sección de memoria (RAM)
  • Ir a otra sección del código
Los programas de computadora se desarrollan a partir de estas sencillas instrucciones combinadas. Por ejemplo, una calculadora simple podría verse así:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

La primera instrucción (mov) mueve el valor de 5 al registro eax . El segundo mueve el valor de 4 al registro ebx. La instrucción add luego agrega eax y ebx y coloca el resultado nuevamente en eax .

3.1.3 PROGRAMAS DE COMPUTADOR

Los programas de computadora son colecciones de instrucciones. Los programas son responsables de recibir un valor (la entrada) y luego producir un valor (la salida) basado en el valor recibido.

Por ejemplo, un programa simple podría tomar un número como entrada, aumentar el número en 1 y luego moverlo a una salida. Podría verse como:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Un programa más complejo tendría muchos de estos programas simples "dentro" de él. En este contexto, estos programas internos simples se denominan funciones. Las funciones, al igual que los programas, toman una entrada y producen una salida. Por ejemplo, podríamos convertir nuestro programa anterior en una función que haga lo mismo. Podría verse como:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

También podríamos hacer otra función que haga una operación similar. Por ejemplo, podríamos escribir una función para disminuir un número en 1:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Estas dos funciones ( sumar y restar ) se pueden usar para crear un programa más complejo. Este nuevo programa tomará un número y lo incrementará o disminuirá. Tomará dos entradas:
  1. Un número
  2. Una operación matemática, en este caso, sumar (+) o restar (-)
Este nuevo programa será más largo y tendrá dos formas diferentes de ejecutarse. Estos se explicarán después del código:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Este código tiene dos funciones en la parte superior. Como discutimos, estos toman una entrada y luego suman o restan 1 de la entrada para producir la salida. La instrucción cmp compara dos valores. En este caso, está comparando el tipo de operación recibido como entrada y un valor codificado en el programa, - . Si estos valores son iguales, vamos a (o saltamos a) otra sección del código ( je = saltar si es igual).

Si la operación es igual a - , vamos al código que resta 1 al número. De lo contrario, continuamos el programa y sumamos 1 al número antes de salir.

Comparar números y luego saltar a diferentes códigos dependiendo de su valor se conoce como bifurcación. La bifurcación es un componente clave del diseño de programas complejos que pueden reaccionar a diferentes entradas. Por ejemplo, un juego a menudo tendrá una rama para cada dirección en la que un jugador puede moverse.

3.1.4 BINARIO, DECIMAL Y HEXADECIMAL

Fundamentalmente, las CPU son circuitos. Los circuitos tienen electricidad fluyendo a través de ellos (encendidos) o no (apagados). Estos dos estados se pueden representar mediante un sistema numérico binario (o de base 2). En un sistema de base 2, tiene dos valores posibles: 0 y 1. Un número binario de ejemplo es 1101.

Estamos familiarizados con un sistema numérico decimal (o base 10), que tiene 10 valores posibles: 0, 1, 2, 3, 4, 5, 6, 7, 8 y 9. Un número decimal de ejemplo es 126. Este El número se puede representar en un formato más explícito como:

(1 * 10 2 ) + (2 * 10 1 ) + (6 * 10 0 )

Podemos representar el número binario anterior (1101) en el mismo formato. Sin embargo, reemplazaremos los 10 con 2, ya que estamos cambiando de un sistema de base 10 a base 2:

(1 * 2 3 ) + (1 * 2 2 ) + (0 * 2 1 ) + (1 * 2 0 )

Los números binarios pueden volverse difíciles de manejar rápidamente cuando necesitan representar valores más grandes. Por ejemplo, la representación binaria del número decimal 250 es 11111010.

Para representar estos números binarios más grandes, los números hexadecimales (base 16) se usan comúnmente en computación. Los números hexadecimales suelen tener el prefijo del identificador 0x y tienen dieciséis valores posibles: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E y F. Un ejemplo hexadecimal número es 0xA1D.

3.1.5 LENGUAJES DE PROGRAMACION

Las instrucciones se representan como números, como todos los demás datos en una computadora. Estos números se conocen como códigos de operación, a menudo abreviados como códigos de operación. Los códigos de operación varían según la arquitectura. La CPU conoce cada código de operación y lo que debe hacer cuando se encuentra con cada uno. Los códigos de operación se representan comúnmente en formato hexadecimal. Por ejemplo, si una CPU Intel encuentra un 0xE9, sabe que necesita ejecutar una instrucción jmp (salto).

Las primeras computadoras requerían que los programas se escribieran en códigos de operación. Obviamente, esto es difícil de hacer, especialmente para programas más complejos. Luego se adoptaron variantes de un lenguaje ensamblador, lo que permitió la escritura de instrucciones. Se ven similares a los ejemplos que escribimos anteriormente. El lenguaje ensamblador es más fácil de leer que solo los códigos de operación, pero aún es difícil desarrollar programas complejos.

Para mejorar la experiencia de desarrollo, se desarrollaron varios lenguajes de alto nivel, como FORTRAN, C y C++. Estos lenguajes son fáciles de leer e introdujeron operaciones de control de flujo como los condicionales if y else . Por ejemplo, el siguiente es nuestro programa de incremento/decremento en C. En C, un int se refiere a un número entero o entero (-1, 0, 1 o 2 son ejemplos).

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Todos estos lenguajes de nivel superior se compilan hasta ensamblar. Luego, un ensamblador tradicional convierte ese ensamblaje en códigos de operación que la CPU puede entender.

3.1.6 LENGUAJES DE PROGRAMACION

Escribir programas para comunicarse con el hardware es un proceso difícil y que requiere mucho tiempo. Para ejecutar nuestro programa de incremento/decremento, también tendríamos que escribir código para manejar las pulsaciones de teclas desde un teclado, mostrar gráficos en el monitor, crear juegos de caracteres para poder representar letras y números, y comunicarnos con la memoria RAM y el disco duro. . Para facilitar el desarrollo de programas se crearon los sistemas operativos. Estos contienen código que ya puede manejar estas funciones de hardware. También tienen varias funciones estándar que se usan comúnmente, como copiar datos de una ubicación a otra.

Los tres sistemas operativos principales que aún se utilizan en la actualidad son Windows, Linux y MacOS. Todos estos tienen diferentes bibliotecas y métodos para comunicarse con el hardware. Esta es la razón por la que los programas escritos para Windows no funcionan en Linux.

3.1.7 APLICACIONES

Los sistemas operativos necesitan una forma de determinar cómo manejar los datos cuando un usuario los selecciona. Si los datos son una foto, el sistema operativo quiere abrir una aplicación específica (como Paint) para ver la foto. Del mismo modo, si los datos son una aplicación en sí, el sistema operativo debe pasarlos a la CPU para que se ejecuten.

Cada sistema operativo maneja la ejecución de forma única. En Linux, se establece un permiso ejecutable especial en un archivo normal. En Windows, las aplicaciones tienen un formato especial que Windows sabe cómo analizar. Esto se conoce como formato PE, o ejecutable portátil. El formato PE tiene varias secciones, como la sección .text para almacenar el código del programa y la sección .data para almacenar variables.

3.1.8 JUEGOS

Con todo eso fuera del camino, finalmente podemos hablar de juegos. Los juegos son simplemente aplicaciones. En Windows, están formateados en formato PE, idéntico a cualquier otra aplicación. Contienen una sección .text que contiene el código del programa, compuesto por códigos de operación. Luego, la CPU ejecuta estos códigos de operación, y el sistema operativo muestra los gráficos resultantes y maneja la entrada, como si se presionaran teclas.

3.2 FUNDAMENTO DE LOS JUEGOS

3.2.1 JUEGOS

Si bien los juegos son aplicaciones, son complejos y se componen de varias partes. Algunos de estos incluyen:
  • Gráficos
  • Sonidos
  • Entrada
  • Físico
  • Lógica del juego
Debido a la complejidad de cada parte, la mayoría de los juegos usan funciones externas para estas partes. Estas funciones externas se combinan en lo que se llama una biblioteca. Luego, otros programas utilizan las bibliotecas para reducir la cantidad de código escrito. Por ejemplo, para dibujar imágenes y formas en una pantalla, la mayoría de los juegos usan la biblioteca DirectX u OpenGL.

Para algunos tipos de hacks, es importante identificar las bibliotecas que se utilizan. Un wallhack es un tipo de hackeo que le permite al hacker ver a otros jugadores a través de paredes sólidas. Un método para programar un wallhack es modificar la biblioteca de gráficos del juego. Tanto OpenGL como DirectX son vulnerables a este tipo de ataques, pero cada uno requiere un enfoque diferente.

Para la mayoría de los trucos, modificaremos la lógica del juego. Esta es la sección de instrucciones responsable de cómo se desarrolla el juego. Por ejemplo, la lógica del juego controlará qué tan alto salta un personaje o cuánto dinero recibe el jugador. Al cambiar este código, potencialmente podemos saltar tan alto como queramos o ganar una cantidad infinita de dinero.

3.2.2 ESTRUCTURA DEL JUEGO

La lógica del juego se compone de instrucciones, como todo código de computadora. Debido a la complejidad de los juegos, a menudo se escriben en un lenguaje de alto nivel y se compilan. A menudo se requiere comprender la estructura general del código original para hacks más complejos.

La mayoría de los juegos tienen dos funciones principales:
  • Configuración (Setup)
  • Bucle principal (Main Loop)
La función de configuración se ejecuta cuando se inicia el juego por primera vez. Es responsable de cargar imágenes, sonidos y otros archivos grandes desde el disco duro y colocarlos en la memoria RAM. El bucle principal es un tipo especial de función que se ejecuta indefinidamente hasta que el reproductor se cierra. Es responsable de manejar la entrada, reproducir sonidos y actualizar la pantalla, entre otras cosas. Un bucle principal de ejemplo podría verse así:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Todas estas funciones a su vez llaman a otras funciones. Por ejemplo, la función handle_input podría verse así:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Cada juego está programado de manera diferente. Algunos juegos pueden priorizar la actualización de gráficos antes de manejar la entrada. Sin embargo, todos los juegos tienen algún tipo de bucle principal.

3.2.3 DATOS Y CLASES

Todos los datos que se pueden actualizar en un juego se almacenan en una variable. Esto incluye cosas como la puntuación, la posición o el dinero de un jugador. Estas variables se declaran en el código. Un ejemplo de definición de variable en C podría verse así:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Este código declararía la variable dinero como un número entero. Como aprendimos en la última lección, los valores enteros en C son números enteros (como 1, 2 o 3). Imagina si tuviéramos que rastrear el dinero de varios jugadores. Una forma de hacer esto sería tener varias declaraciones, así:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Una desventaja de este enfoque es que es difícil de mantener a medida que el juego se vuelve más grande y más complejo. Por ejemplo, para escribir un código que aumente el dinero de cada jugador en 1, tendríamos que actualizar manualmente cada variable:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Si agregáramos a otro jugador, tendríamos que ir y actualizar cada sección del código que alteró el dinero de los jugadores. Un mejor enfoque es declarar estos valores en una lista. Luego podemos usar una instrucción conocida como bucle para recorrer cada elemento de la lista. Esto se conoce como iteración. En C, las listas se implementan comúnmente usando lo que se conoce como matriz. Para nuestros propósitos, puede suponer que las listas y las matrices son sinónimos. Un tipo de bucle en C se conoce como bucle for. Los bucles for se dividen en tres segmentos: el valor inicial, el valor final y cómo actualizar el valor después de cada iteración. Un ejemplo del código anterior podría escribirse así:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Ahora solo tendríamos que actualizar la variable current_players para agregar soporte para otro jugador.

Para facilitar el desarrollo de aplicaciones complejas, los desarrolladores suelen utilizar un modelo de programación conocido como programación orientada a objetos u POO. En POO, las variables y funciones se agrupan en colecciones llamadas clases. Las clases suelen ser independientes. Por ejemplo, muchos juegos tendrán una clase Player. Esta clase contendrá varias variables como la posición, el nombre o el dinero del jugador. Estas variables dentro de la clase se conocen como miembros. Las clases también contendrán funciones para modificar estos miembros. Un ejemplo de una clase de jugador podría verse así:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Los juegos a menudo contendrán listas de clases. Por ejemplo, el juego Quake 3 tiene una matriz de todos los jugadores actualmente conectados a un servidor. Cada jugador tiene su propia clase de jugador en el juego. Para calcular la pantalla de puntaje, el juego revisará a cada jugador en la lista y verá la cantidad de muertes que tienen.

3.2.4 MEMORIA

Los juegos tienen muchos recursos grandes, como imágenes y sonidos. Estos deben cargarse desde el disco duro, generalmente en la fase de configuración del juego. Una vez cargados, se colocan en la memoria RAM, junto con el código y los datos del juego. Debido a que los juegos son tan grandes, deben cargar constantemente diferentes datos de la RAM en los registros para operar. Esta carga generalmente se realiza mediante un comando mov . Este comando moverá una sección de la memoria a un registro. Nuestra función de ejemplo de increase_money ejecutada por la CPU podría verse así:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

En este ejemplo, usamos 0x12345678 como la ubicación en RAM del dinero del jugador. La mayoría de los juegos tendrán esta estructura pero una ubicación diferente. Para juegos más complejos, estas ubicaciones se basarán en otras ubicaciones. Si nuestro juego tuviera una clase de jugador, el código de aumento de dinero ejecutado por la CPU necesitaría usar la ubicación de la clase del jugador para recuperar el dinero.

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

En este caso, la CPU tuvo que compensar(offset) la ubicación del dinero en función de la ubicación de la clase Player.

3.2.5 CLIENTE MULTIJUGADOR

Los juegos multijugador permiten que varios jugadores interactúen entre sí. Para permitir esto, los juegos multijugador utilizan clientes y servidores. A continuación se muestra un ejemplo del modelo cliente-servidor:

1654323389213.png

Los clientes representan la copia del juego de cada jugador y contienen toda la información sobre el juego local. Por ejemplo, cada cliente contendrá el dinero de ese jugador. Cuando un jugador provoca una acción para cambiar su dinero, el cliente es responsable de enviar esta actualización al servidor.

La información también se puede enviar en ambas direcciones. Un ejemplo de esto es el movimiento del jugador. Un cliente le dirá al servidor que el jugador ha movido su posición. Luego, el servidor les indicará a todos los demás clientes que actualicen sus posiciones asociadas para el cliente movido.

3.2.6 SERVIDORES MULTIJUGADORES

Mientras que el cliente representa la copia del juego de cada jugador, el servidor se asegura de que todos los clientes conectados estén jugando la misma copia del juego. Los servidores a menudo restringirán los cambios que aceptan de los clientes. Por ejemplo, imagina que escribimos un truco para cambiar nuestro dinero en un juego. Si es un juego multijugador, el servidor rechazará nuestros cambios. Esta es la razón por la que los trucos para un solo jugador a menudo no funcionan en el modo multijugador.

Hablaremos más sobre los fundamentos del modo multijugador en una lección futura.

3.3 FUNDAMENTO DEL HACKING

3.3.1 PASOS DEL HACKING

Todo hacking consiste en modificar la memoria de un juego. Escribir cualquier truco implica cuatro pasos principales:
  1. Identifica lo que quieres cambiar.
  2. Entiende qué memoria necesitas localizar.
  3. Localiza esa memoria en el juego.
  4. Cambia esa memoria.
Estos pasos aplican para cualquier hack, sin importar la complejidad.

3.3.1 IDENTIFICAR

El primer paso para cualquier truco es identificar lo que quieres hacer. Diferentes hacks requerirán diferentes enfoques. Por ejemplo, modificar el dinero de un jugador requerirá una modificación de memoria de una variable, mientras que ver a otros jugadores a través de las paredes requerirá una modificación de memoria del código del juego.

3.3.1 ENTENDER

Para modificar la memoria, necesitamos localizarla. Antes de intentar localizarlo, necesitamos entender qué memoria necesitamos localizar. En algunos casos, la memoria que deseas modificar será una variable. En otros casos, querrá modificar grandes secciones de código. Hay tres tipos principales de modificaciones:
  • Variables, como modificar el valor del dinero de un jugador
  • Código, como modificar cómo se dibujan las paredes
  • Archivos, como modificar los artículos guardados en su inventario

3.3.2 LOCALIZAR

Una vez que sepa lo que quiere cambiar y comprendas dónde buscar, puedes comenzar a buscarlo. Para algunos hacks, esto puede implicar buscar en la memoria con una herramienta llamada escáner de memoria. Para otros, puede implicar revisar el código del juego usando una herramienta llamada depurador. Dependiendo del juego y el enfoque, este puede ser un proceso que requiere mucho tiempo.

3.3.3 CAMBIOS

Después de haber localizado la memoria, el último paso es cambiarla. Inicialmente, esto significará usar un escáner de memoria o un depurador para modificar manualmente la memoria. Una vez que hayas verificado que esto funciona, puede escribir un programa para cambiarlo automáticamente por usted.

3.4 CONFIGURACION DE UNA MAQUINA VIRTUAL DE LABORATORIO (VM)

3.4.1 VISION GENERAL

Al realizar cualquier tipo de piratería, es una buena práctica separar sus máquinas personales y las de piratería. Una manera fácil de lograr esto es usar una máquina virtual o VM. En este laboratorio, configuraremos una VM de piratería de juegos que ejecuta Windows 10. Este será el entorno que usaremos para todos los demás laboratorios también.

Debido a los requisitos de hardware de los juegos, a menudo encontrará que será imposible ejecutar un determinado juego en una máquina virtual. En estos casos, una opción es crear otra sección en su disco duro conocida como partición. Luego puede instalar Windows en esta partición separada y reservarla para piratear juegos.

3.4.2 VIRTUALBOX

Para estos laboratorios, usaremos un hipervisor de máquina virtual conocido como VirtualBox. Este software le permite ejecutar y administrar máquinas virtuales. Puedes descargarlo AQUI.

3.4.3 MAQUINA VIRTUAL

A continuación, necesitamos una máquina virtual. Dado que la mayoría de los juegos se lanzan para Windows, usaremos una máquina virtual con Windows 10. Microsoft lanza máquinas virtuales de Windows 10 gratuitas para probar versiones antiguas de Internet Explorer. Estos son completamente legales pero tienen un límite de activación de 90 días. Para nuestros propósitos, descargaremos una imagen de VirtualBox. Puedes descargar la imagen AQUI. Tenga en cuenta que estas máquinas virtuales tienen un tamaño aproximado de 7 GB. Una vez descargada la imagen, extraiga el archivo OVF del archivo. VirtualBox usa extensiones de OVA y OVF para imágenes. Estas imágenes contienen una copia guardada de una máquina preconfigurada.

Una vez que configuremos nuestra máquina, crearemos una instantánea de ella. Esto nos permitirá tirar la máquina vieja y crear una nueva copia cuando caduque. Nunca guarde notas ni nada personal en su máquina virtual.

Abra VirtualBox e importe el OVF descargado. Mantenga todas las opciones por defecto.

1660495770012.png

Una vez importada la máquina virtual, iníciela. Windows 10 se iniciará y lo enviará a una pantalla de inicio de sesión. La contraseña del usuario es “Passw0rd!”, sin las comillas.

3.4.4 INSTRUMENTOS

Usaremos un script de Boxstarter para instalar algunas herramientas de pirateo de juegos. Boxstarter es un conjunto de utilidades que le permite crear secuencias de comandos y recrear instalaciones. En este caso, lo usaremos para instalar un buscador y depurador de memoria. Dentro de su VM, abra Powershell y ejecute los siguientes dos comandos:

Código:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Código:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

El primer comando instala BoxStarter y se toma de su sitio web. El segundo comando es un script de configuración que habilita algunas opciones de carpeta e instala tres programas:
  1. Cheat Engine, un escáner de memoria
  2. x64dbg, un depurador
  3. Chocolatey, un administrador de paquetes
A medida que descubra más herramientas, debe crear su propia secuencia de comandos y agregarlas.

3.4.5 CLONACION DE MAQUINAS VIRTUALES

Ahora que tenemos nuestro entorno configurado, crearemos un clon. Esto nos permitirá recrear nuestra VM con todas las herramientas ya instaladas. Apague la máquina virtual por completo y luego seleccione Exportar a OCI . Mantenga todos los valores predeterminados y comience la exportación.

1660496362867.png

Esto creará un OVA, similar al OVF que descargamos inicialmente. Si alguna vez corrompemos nuestro entorno, simplemente podemos eliminarlo e importar este nuevo OVA. Ya tendrá todas nuestras herramientas instaladas, por lo que no necesitamos perder tiempo reinstalándolas.

3.5 HACKEAR MEMORIA

3.5.1 OBJETIVO

Para nuestro primer truco, apuntaremos a un juego llamado "The Battle for Wesnoth", abreviado como Wesnoth. Este es un juego gratuito de código abierto que no tiene mecanismos anti-trampas. La mayoría de los laboratorios en este sitio apunta específicamente a la versión 1.14.9 Para instalarlo en nuestra máquina virtual, abra Powershell como administrador y ejecute el siguiente comando:

Código:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Esto usará Chocolatey para instalar The Battle for Wesnoth. Una vez finalizada la instalación, abre el juego y comprueba que funciona. Luego, ve al menú de Preferencias y cambia el modo de video del juego a Ventana . Finalmente, juega la misión del tutorial para aprender cómo funciona el juego.

3.5.2 IDENTIFICAR

Nuestro objetivo para este truco es cambiar nuestro oro. Los jugadores usan oro para comprar tropas en el juego, así que cuanto más oro tengamos, más tropas podremos comprar. Esto lo lograremos mediante el uso de una herramienta llamada Cheat Engine, que nos permite escanear y modificar la memoria del juego. Para ello, utilizaremos los pasos que aprendimos en la lección FUNDAMENTOS DE LA PIRATERIA.

Los jugadores solo reciben oro mientras juegan un escenario. Para crear un escenario, seleccione Multijugador y luego Juego local.

1660496929743.png

Mantenga los valores predeterminados para el resto de la configuración e inicie el juego.

3.5.3 ENTENDER

En este juego, el oro de un jugador se almacena en una variable. Esta variable está referenciada por el código del juego.

1660497001689.png

Para completar con éxito nuestro truco, necesitaremos encontrar dónde se almacena esta variable en la memoria y cambiar su valor. Como estamos tratando con una variable, usaremos un escáner de memoria para encontrarla.

3.5.4 LOCALIZAR

Nuestro siguiente paso es localizar la memoria que contiene nuestro valor en oro. Comenzaremos abriendo Cheat Engine y adjuntándolo a Wesnoth. En esta herramienta, haga clic en el ícono en la parte superior izquierda que parece una computadora con una lupa.

1660497069338.png

En la ventana que aparece, elija el proceso de Wesnoth.

1660497084741.png

Los escáneres de memoria te permiten buscar un valor en la memoria del juego. En el juego de ejemplo, el jugador tiene 75 de oro, así que eso es lo que buscaremos. Coloque su valor de oro en el cuadro Valor y seleccione Nuevo escaneo . Volverán varios miles de resultados.

1660497116841.png

Los escaneos iniciales en cualquier escáner de memoria devolverán miles de resultados. Esto se debe a que los juegos son complejos y miles de secciones de memoria tienen el mismo valor que nuestro oro. Estas otras secciones podrían ser variables como cronómetros, el oro del oponente o la salud del personaje. Nuestro objetivo cuando usamos un escáner de memoria es filtrar estos resultados a uno o dos valores que luego podemos probar manualmente. Para ello, modificaremos nuestro valor de oro en el juego y luego realizaremos otro escaneo usando el botón Siguiente escaneo . La próxima operación de escaneo solo traerá resultados que anteriormente eran nuestro valor inicial, en este caso, 75 .

Recluta una unidad de tropas en el juego y mira tu nuevo valor en oro. Coloque este nuevo valor en el cuadro Valor y seleccione Siguiente análisis . En el siguiente ejemplo, reclutamos una unidad por 17 de oro, dejándonos con 58 de oro.

1660497201408.png

El resultado de este segundo escaneo es solo un valor. Es muy probable que este sea nuestro oro, pero lo confirmaremos en el siguiente paso.

1660497226913.png

3.5.5 CAMBIO

En Cheat Engine, haces doble clic en una ubicación de memoria para moverla al cuadro inferior de la pantalla. Este bloque inferior le permite editar el valor almacenado en la ubicación de la memoria. Haga doble clic en su resultado para llevarlo al cuadro inferior. A continuación, haga doble clic en el valor para que aparezca un cuadro que le pedirá el nuevo valor. Escriba algo grande allí, como 200.

1660497298977.png

Con el valor cambiado, vuelve al juego. Debería ver su oro actualizarse a 200. Reclute una tonelada de unidades para confirmar que su cambio fue exitoso.

1660497315571.png
 
Última edición:

4. DEPURAR Y REVERSEAR (DEBUGGING AND REVERSING)

4.1 FUNDAMENTOS DE DEPURACION

4.1.1 METAS

En nuestro laboratorio anterior, hackeamos nuestro oro modificando una variable en la memoria. La modificación de memoria como esta es poderosa, pero también tiene limitaciones. Los hacks más complejos a menudo requerirán que modifiques el código del juego. Por ejemplo, imagina si quisiéramos crear un truco que nos permitiera reclutar unidades sin oro. Una forma de hacer esto sería monitorear constantemente nuestro oro y aumentarlo manualmente cada vez que reclutamos una unidad. Esto también requeriría que busques constantemente nuevas unidades agregadas al juego y el costo de esa unidad. Un enfoque más fácil sería modificar el código del juego para que nunca disminuya el dinero de un jugador al reclutar.

Ver el código de un juego mientras se ejecuta se conoce como depuración. Comprender y modificar ese código para hacer lo que desea se conoce como inversión. No tienes que depurar un juego para revertirlo, pero es muy útil si puedes.

4.1.2 HERRAMIENTAS INVOLUCRADAS

Para depurar un juego, utiliza una herramienta conocida como depurador. El primer paso en la depuración es "adjuntar" el depurador al juego que desea depurar. Una vez que se adjunta, puede ver el código del juego en la memoria. También puede pausar la ejecución del juego, cambiar el código del juego y modificar los registros. Veremos ejemplos de estas acciones en laboratorios futuros.

Los depuradores pueden causar efectos secundarios no deseados. Por ejemplo, si cambia el código del juego incorrectamente, el juego puede fallar. Dependiendo del juego, esto puede congelar la pantalla de tu computadora. Esta es otra razón para separar siempre su máquina de piratería de su máquina personal.

Hay muchos depuradores, pero algunos conocidos incluyen IDA y gdb. Otros depuradores, como WinDbg y OllyDbg, a menudo se mencionan pero ya no se mantienen. En esta serie, usaremos un depurador de código abierto llamado x64dbg. Como cualquier otra herramienta, es más importante conocer los fundamentos que la herramienta. El mismo enfoque que aprenderemos mientras usamos x64dbg se puede aplicar a cualquier depurador.

4.1.3 DESMONTAJE Y DEPURACION

Después de adjuntar un depurador a un juego, el depurador mostrará el código del juego. Sin embargo, este no es el código del juego original. Como comentamos en la primera lección, los juegos suelen programarse en un lenguaje de alto nivel, como C++. Sin embargo, el ejecutable que se ejecuta en nuestra computadora solo contiene los códigos de operación para que la CPU los ejecute. Esta falta del código original es lo que dificulta la marcha atrás. A menudo, los juegos contendrán miles de estos códigos de operación.

Al ensamblar un programa, cada línea de código ensamblador se convierte en un código de operación. El desmontaje es el proceso en el que los códigos de operación se vuelven a convertir en montaje. Normalmente, el desmontaje y la depuración se usan indistintamente, especialmente cuando se invierte un juego. Sin embargo, se pueden hacer por separado. Es posible desmontar un programa sin depurarlo. Esto se conoce como análisis estático y se realiza comúnmente cuando se invierte el malware. También es posible depurar un programa sin desmontarlo. Un ejemplo común de esto sería depurar un programa que haya escrito. En este caso, tiene el código original y no es necesario desmontarlo.

Es posible recrear parcialmente el código de alto nivel original del desensamblado. Esto se conoce como descompilación. Sin embargo, el desensamblado siempre es representativo de la ejecución del código, mientras que la descompilación no lo es. Los descompiladores a menudo se ven obligados a adivinar la estructura original del código. Si bien estas herramientas pueden ser útiles, también pueden llevarte por caminos falsos. En estas lecciones, no cubriremos la descompilación.

4.1.4 ENSAMBLADOR

Al depurar y revertir un juego, se ocupará principalmente del ensamblaje. El ensamblado es similar al primer lenguaje que cubrimos en la lección FUNDAMENTOS DE LA COMPUTADORA Cada instrucción en ensamblaje hace una cosa, como sumar o restar. No es necesario conocer todas las instrucciones de montaje para invertir un juego.

Si bien puede parecer desalentador, cualquier código ensamblador se puede entender al leerlo una instrucción a la vez. Al depurar, esto se conoce como recorrer paso a paso el desensamblaje. A menudo, hay muchas instrucciones en un juego que no son críticas de entender mientras se da marcha atrás. Por ejemplo, la CPU tiene que hacer varias instrucciones al sumar números que tienen valores decimales. Si solo nos interesa el resultado de esta suma, podemos saltarnos muchas de estas instrucciones. Comprender qué instrucciones se pueden omitir viene con la experiencia.

4.2 FUNDAMENTOS DE ENSAMBLAJE

4.2.1 VISION GENERAL

En la lección anterior, discutimos brevemente cómo al depurar un juego, se ocupará principalmente de las instrucciones de ensamblaje. Hay una gran cantidad de instrucciones de montaje; sin embargo, para nuestros propósitos, solo necesitaremos entender un puñado. El propósito de esta lección es presentar y explicar algunas de las instrucciones de ensamblaje comunes que encontrará al depurar un juego.

4.2.2 GESTION DE DATOS

Como hemos discutido anteriormente, los datos comúnmente se mueven de la memoria a un registro. Luego, se realizan operaciones en estos datos antes de que se vuelvan a mover a la memoria. Este movimiento se hace comúnmente usando la instrucción mov . La instrucción mov puede tener múltiples formas. Por ejemplo, las siguientes instrucciones mueven el valor de ecx a eax y mueven el valor 1 a eax , respectivamente:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Además, la instrucción mov se puede usar para mover datos de la memoria a un registro. Para hacer esto, podemos usar una directiva especial, como dword ptr ds . Esto le dice a la instrucción mov que mueva los datos almacenados en una dirección de memoria a un registro. El siguiente ejemplo mueve el valor almacenado en 0x12345678 a eax:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Cuando se trata de clases, los juegos a menudo querrán cargar una dirección en lugar de un valor. Un juego a menudo almacenará una dirección en un registro y luego cargará esa dirección en otro registro para recuperar ciertas variables dentro de una clase. Para hacer esto, los juegos comúnmente usan la instrucción lea:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

4.2.3 CAMBIO DE DATOS

Una vez que los datos se cargan en un registro, los juegos usarán varias instrucciones para modificar los datos. Las instrucciones inc y dec se pueden usar para aumentar o disminuir el valor en el registro en uno:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

De manera similar, las instrucciones de suma y sub pueden usarse para sumar y restar de un valor en un registro:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Otras operaciones, como multiplicar y dividir, también se admiten a través de las instrucciones mul y div . La instrucción mul toma un solo valor y lo multiplica por el valor almacenado en eax:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

La operación div funciona de la misma manera:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

A menudo, los juegos necesitarán manipular los bits individuales de un valor almacenado en un registro. Para ello, utilizarán varias instrucciones bit a bit. Estos incluyen las operaciones shift left y shift right ( shl y shr ), y las operaciones and , or y xor:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

4.2.4 CONTROL DE FLUJO

Los juegos se componen de cientos de funciones diferentes. Para navegar a estas funciones desde el ciclo principal, los juegos usarán una variedad de instrucciones de control de flujo. Por ejemplo, la instrucción de salto ( jmp ) salta a otra sección de código para comenzar a ejecutar:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Los juegos también usarán la instrucción de call para ejecutar una función:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Las diferencias entre estas instrucciones se discutirán más en futuros laboratorios. Por ahora, la principal diferencia es que una instrucción jmp cambia permanentemente el lugar donde se ejecuta un juego, mientras que una instrucción call lo cambia temporalmente.

Los juegos a menudo necesitarán comparar un valor de un registro con otro valor para determinar si es necesario realizar una acción. Por ejemplo, si un jugador llega a 0 vidas, el juego querrá presentar una pantalla de "Game Over" al jugador. Para hacer esto, los juegos usarán las operaciones de comparación ( cmp ) y test:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Probar un registro contra sí mismo, como en el ejemplo anterior, tiene el efecto de comparar un registro con el valor 0.

Las instrucciones cmp y test establecen varios indicadores especiales en la CPU. Estas banderas se pueden usar en combinación con operaciones de salto condicional para navegar a secciones de código. Por ejemplo, la instrucción saltar si cero ( jz ) saltará a una sección de código si los valores comparados son iguales. El siguiente ejemplo saltará 0x12345678 si ebx y eax son iguales:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Los saltos condicionales más comunes son saltar si es cero ( jz ), saltar si no es cero ( jnz ), saltar si es igual ( je ) y saltar si no es igual ( jne ). En aras de invertir los juegos, jz y je pueden considerarse idénticos.

4.2.5 LA PILA (STACK)

Además de los registros, los programas pueden almacenar información en un lugar conocido como la pila. La información se almacena (o empuja) en la pila mediante el uso de la instrucción de push. La información se recibe (o extrae) de la pila a través de la instrucción pop . El siguiente ejemplo empuja el valor de 5 en la pila y luego coloca ese valor en el registro eax.

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Las funciones a menudo toman varias entradas o parámetros. Para lograr esto en ensamblaje, los juegos a menudo empujarán varios valores en la pila antes de una llamada, así:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

La mayoría de las CALLS comenzarán y terminarán con las siguientes instrucciones:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Las primeras tres instrucciones se conocen como el prólogo de la función y configuran el marco de la pila para la función. Cada función tendrá su propio marco de pila. Las últimas dos instrucciones restauran el marco de pila de la función anterior y regresan a la función que llamó a esta función actual.

4.2.6 UN EJEMPLO

El siguiente ejemplo contiene varias instrucciones combinadas. En este ejemplo, 0x12345678 representa la dirección de memoria que contiene el número de vidas del jugador actual y 0x45923821 es una función que imprime texto en la pantalla:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Mirando las instrucciones, el juego primero carga el número de vidas del jugador actual en eax. Luego disminuye este valor en 1. Luego vuelve a mover ese valor al número de vidas del jugador actual. Luego, comprueba si el jugador tiene 0 vidas. Si no, salta de nuevo a algún código por encima de esta sección. Si es así, salta a una sección de un código a continuación que llama a una función con dos parámetros colocados en la pila. A partir de estas instrucciones, podemos adivinar que este ejemplo es responsable de verificar si el jugador ha perdido todas sus vidas y necesita que se le presente una pantalla de fin de juego.

4.3 UTILIZANDO BREAKPOINTS

4.3.1 CONTEXTO

El proceso de revertir un juego puede parecer abrumador la primera vez que conectas un depurador a un juego. La mejor manera de comenzar a invertir un juego es descubrir qué quieres mirar y luego encontrar dónde está. Una vez que establezca este contexto, puede recorrer solo las instrucciones que realmente le importan.

Hay muchas maneras de establecer un contexto. En algunos casos, es posible que desee buscar el texto que se muestra cuando el juego realiza una determinada acción. Cualquier ubicación que cargue este texto debe estar relacionada eventualmente con la acción que le interesa. En otros casos, puede usar las direcciones de memoria que se encuentran en el editor de memoria para encontrar el código que le interesa. Independientemente del enfoque que tome, utilizará un punto de interrupción(breakpoint).

4.3.2 PUNTOS DE INTERRUPCION (BREAKPOINTS)

Los puntos de interrupción permiten que el depurador pause la ejecución del juego en una instrucción específica. Con el juego en pausa, puede recorrer las instrucciones individuales y ver la memoria del juego. Puede establecer puntos de interrupción en cualquier tipo de memoria. Esto incluye la memoria encontrada usando un escáner de memoria.

Los puntos de interrupción se pueden configurar para que se disparen tanto condicional como no condicionalmente. Los puntos de interrupción condicionales solo se activarán si se cumplen sus condiciones. Estas condiciones pueden ser cosas como registros que tienen un cierto valor o la memoria (en la que se establece el punto de interrupción) cambiando. Cuando se activa un punto de interrupción, también se conoce como aparición.

4.3.3 PUNTOS DE INTERRUPCION DE LA MEMORIA

La mejor manera de ilustrar el uso de puntos de interrupción es a través de un ejemplo. En esta sección, examinaremos cómo se puede usar un punto de interrupción de la memoria para establecer un contexto.

De vuelta en el laboratorio del punto 3.5 HACKEAR MEMORIA, encontramos la ubicación de memoria de nuestro oro. Podemos usar esta ubicación de memoria para encontrar la lógica del juego responsable de bajar nuestro oro. Hacemos esto estableciendo un punto de interrupción condicional en la ubicación de la memoria de nuestro oro y luego ingresando al juego para reclutar una unidad. Cuando reclutemos a la unidad, aparecerá el punto de interrupción y la ejecución se detendrá en la función responsable de reducir el oro del jugador. Esta función puede parecerse a la siguiente, con la línea resaltada que representa nuestra ubicación en pausa.

1669510457295.png

La primera instrucción mueve el valor almacenado en 0x05500ABC al registro eax. Este valor fue la ubicación que encontramos en nuestro laboratorio anterior para el oro. La siguiente instrucción mueve un valor hipotético para el costo de la unidad al registro ebx. Luego, el juego resta el costo de la unidad de nuestro valor en oro. Nuestra ubicación en pausa es responsable de mover el nuevo valor del oro nuevamente a la ubicación de la memoria que almacena nuestro valor en oro.

Puede notar que el juego no se detuvo en la operación de resta. Esto se debe a que esta operación solo modifica el valor en el registro y no el valor real de la memoria en la que configuramos el punto de interrupción. Los puntos de interrupción siempre se detendrán en la instrucción inmediatamente después de la memoria afectada.

4.3.4 PUNTOS DE INTERRUPCION DEL CODIGO

A veces puede ser difícil o imposible encontrar un valor de memoria para establecer un punto de interrupción. En estos casos, puede establecer un punto de interrupción en una sección de código. Un ejemplo común de esto es establecer un punto de interrupción en una referencia de texto y luego usarlo para encontrar la función de nivel superior que nos interesa.

Considere un juego para el que queremos escribir un wallhack. El bucle principal del juego puede parecerse a:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Y la función draw_wall del juego puede parecerse a:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Finalmente, la función print_error puede parecerse a:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Un método para escribir un wallhack es eliminar la función draw_wall de este juego. Dado que no hay una variable para usar como punto de interrupción de memoria, en su lugar usaremos un punto de interrupción de código.

Los depuradores te permiten ver todo el texto de un juego y todas las ubicaciones que usan ese texto. Por ejemplo, con un depurador, podríamos encontrar el texto de textura de pared No se pudo encontrar y dónde se hace referencia. Puede verse algo como:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Esta sección de código es responsable de cargar la cadena en un registro y luego llamar a la función print_to_log. Al establecer un punto de interrupción en este código y luego encontrar una textura faltante en el juego, nuestro punto de interrupción aparecería. Luego podríamos continuar paso a paso por el código hasta que nos devolviera a la función que llamó a este código. Esto se conoce como salir de una función. Después de salir, estaríamos en la función draw_wall y luego podríamos eliminar la función.

4.3.5 PUNTOS DE INTERRUPCION DEL CODIGO

Un nop (opcode 0x90) significa que no hay operación. Al encontrar esta instrucción, una CPU no hará nada y continuará con la siguiente instrucción. Este comportamiento se puede utilizar para modificar la lógica del juego.

Por ejemplo, en la sección anterior Puntos de interrupción de la memoria, encontramos la parte del código responsable de restar nuestro oro. El código se parecía a:

C++:
Por favor, Acceder o Regístrate para ver el contenido de los códigos!

Al reemplazar la operación sub(restar) con un nop, el juego ya no restará nuestro oro.
 
Última edición:
Estado
Cerrado para nuevas respuestas.
Atrás
Arriba