Hack Age of Empires III para cambiar la configuración de calidad del sombreador

El comienzo de mayo de 2020: si eres como yo, entonces la cuarentena te hizo rehacer juegos que no se han lanzado desde hace muchos años.

Y si eres aún más como yo, entonces en algún lugar podrías tener un disco de Age of Empires 3. Tal vez estás jugando en una Mac, tal vez aún no te has actualizado a Catalina y te gustaría comandar a Morgan Black.

Entonces, comienzas el juego, entras en el menú principal e inmediatamente notas que algo está mal ... El menú parece asqueroso .


Si se pregunta qué es exactamente "desagradable", preste atención al agua. Todo lo demás también es terrible, pero es menos obvio.


Entonces, entras en las opciones, elevas todos los parámetros al máximo ... Pero el juego sigue siendo feo.

Observa que las opciones de " Calidad del sombreador " están sospechosamente bloqueadas en " Baja ".

Intento No. 1 - parámetros de pirateo


En este punto, comienzas a buscar la carpeta del juego, que supuestamente se creó en algún lugar de la carpeta Documentos, porque el juego fue en 2005, y luego todos lo hicieron.

¿Qué estamos buscando? Por supuesto, el archivo en el que se almacenan los parámetros. La interfaz del menú no nos permite cambiar la configuración, pero somos complicados, ¿verdad?

Encontramos el XML requerido, porque el juego es 2005 y es, por supuesto, XML, donde encontramos la opción " optiongrfxshaderquality ", que está establecida en 0. Parece que lo estábamos buscando, por lo que aumentamos el valor a 100, porque no hay mucha calidad. Aquí estoy de acuerdo contigo.


También puede notar que este es un uso terrible de XML. Afortunadamente, no tiene buenos usos.

Satisfechos con nosotros mismos, lanzamos el juego. Por desgracia, nada ha cambiado. Al mirar nuevamente el archivo XML, vemos que el parámetro se establece nuevamente en 0. Ensemble Studios (que descanse en paz) mira con desprecio todo su ingenio.

Después de eso, podríamos rendirnos . Esta es una Mac, por lo tanto, es poco probable que se encuentren los parches escritos por los usuarios (de hecho, lo verifiqué, hay todo tipo de soluciones dudosas que pueden funcionar). Se trata de los gráficos. Parece que solucionar el problema será difícil.

Pero no nos rendiremos. Porque recordamos cómo debería ser la edad 3. Recordamos cómo admiramos esta hermosa agua ondulada. Estos efectos de partículas. Estas enormes naves que simplemente no encajan en el marco. Y, es solo que la cámara fue ampliada, ¿en qué estaba pensando?

Intento No. 1 - piratería de datos


Al principio de la carpeta en Documentos hay un archivo de registro. El juego escribió una tarjeta gráfica allí e informó que había seleccionado la configuración "genérico dx7". Dice que ha instalado Intel GMA 950, que realmente estaba en la computadora anterior.

Pero en esto tienes Intel Iris Graphics 6100. Algo está realmente mal. Supone: el juego determina las capacidades gráficas de la computadora, comparándolas con una base de datos de tarjetas gráficas en lugar de verificar las capacidades de la tarjeta en sí. Porque eso es exactamente lo que hacen los desarrolladores de AAA, y si trabajaste en RTS de código abierto , entonces sabes cómo sucede esto.


El contenido del archivo de registro. Si le parece curioso que Intel esté designado como 0x8086, ha elegido el artículo apropiado.

Por casualidad, encontrará notas antiguas de parches que confirman sus inquietudes. Buscas en la carpeta GameData, pero solo hay archivos .bar y .xmb que en su mayoría son ilegibles. Está buscando grep " Intel " y " dx7 ". No pasa nada. Eliminar varios archivos ... No pasa nada. Pero sabes que los juegos AAA pueden almacenar algunos recursos en lugares extraños, y este es el puerto de los juegos de Windows en Mac, por lo que todo puede ser muy extraño aquí.

En última instancia, encontrará el archivo "render.bar", si lo mueve, el juego se bloquea cuando se inicia. No está muy claro, pero no está mal. Abrimos el archivo en Hex Fiend , porque este programa generalmente le permite aprender algo sobre archivos binarios. Y así resulta. Parte de estos datos es texto legible. Algunos de ellos son sombreadores. Pero la mayoría de los datos están extrañamente divididos por un espacio vacío ...


Interfaz Hex Fiend Es minimalista, pero funciona. Los primeros bytes son las letras "ESPN", que probablemente sean el encabezado .bar.

Usted exclama: "¡Obviamente, esto es UTF-16!" El juego fue creado para Windows, por lo que es lógico que almacene texto en UTF-16, desperdiciando casi la mitad de un archivo de datos de 30 megabytes. Después de todo, a Windows le encantaba UTF-16 (aunque no debería ), así que ¿por qué no usar esta codificación y sombreadores ASCII en un solo archivo? ¡Es una idea lógica!

(En realidad, me tomó alrededor de un día resolverlo)

Hex Fiend tiene la opción de analizar bytes como UTF-16, por lo que nuevamente estamos buscando la cadena grep dx7. Hay varias entradas. Los reemplazamos con la línea dx9, que también se encuentra en los datos, guardamos el archivo y lanzamos el juego.

Si Los registros nuevamente no muestran "dx9". Esto probablemente se establece en otro lugar.

Probemos algo más. Supongamos que el juego reconoce tarjetas gráficas y sus identificadores hexadecimales se almacenan en los registros (en mi caso, 0x8086 es Intel y también 0x2773). Estamos buscando "8086" en Hex Fiend, encontramos algo y parte del texto parece prometedor. La serie Intel 9 es la serie GMA 950, y Age 3 probablemente no funcionó muy bien en ella.


Algunos números son como identificadores de tarjeta (2582, 2592); quizás si los reemplaza, entonces algo cambiará. Hicimos una copia del archivo de todos modos, así que puedes intentarlo.

Por desgracia, esto también es un callejón sin salida. De hecho, estamos estudiando el archivo incorrecto. Afortunadamente, puedo decirte esto porque pasé mucho tiempo e intenté demasiado . Esta maldita cosa tomó alrededor de 20 horas. Y esto no cuenta el tiempo para todas las capturas de pantalla ... El

archivo deseado se llama "DataP.bar", y si reemplazamos sabiamente la ID, veremos algo completamente nuevo en los registros:


No entiendo bien por qué este "dx7 genérico" no es el mismo que en la captura de pantalla anterior.

Por supuesto, en el juego todavía estamos limitados por la configuración "Baja", es decir, los registros no mienten. Esperábamos un medio, pero por desgracia .

Parece que no se puede lograr más de la piratería de datos. Es hora de tomar las herramientas en serio.

Intento No. 2 - piratería


Entonces, si nada tiene éxito, nos queda una última cosa: cambiar el archivo ejecutable en sí (es decir, como dijeron en 2005, "descifrarlo"). Parece que hoy simplemente se llama piratería, pero este término se ha conservado en el campo de la piratería de juegos (por supuesto, personalmente nunca lo he hecho).

Pasamos a la parte más interesante del artículo (sí, ya has leído mil palabras, pero ¿no era curioso?).

Tolva de desmontaje


En esta etapa, nuestra herramienta será un desensamblador: una aplicación que recibe el código binario del archivo ejecutable y lo convierte en un código de ensamblador legible (ja, ja). El más popular de estos es IDA Pro, pero es increíblemente caro y no estoy seguro de si funciona en una Mac, así que usaré Hopper . No es gratuito (cuesta $ 90), pero se puede usar libremente durante 30 minutos a la vez con casi todas las funciones necesarias.

No explicaré la interfaz en detalle, porque todo está bien presentado en el tutorial de Hopper , pero le diré lo que haré e intentaré tomar suficientes capturas de pantalla para que pueda aprender algo.

Primero debe decir: me tomó cinco días resolverlo, y se hicieron muchos intentos fallidos. Básicamente, hablaré sobre pasos exitosos, por lo que parecerá mágico. Pero fue dificil . No te preocupes si tienes algún problema.

Y una nota más: el "craqueo" suele ser ilegal y / o realizado con fines ilegales. Demuestro un ejemplo bastante raro de un uso bastante legítimo, que al mismo tiempo es completamente "sombrero blanco", por lo que todo está en orden aquí. Asumiré que, estrictamente hablando, esto es ilegal / contrario al acuerdo del usuario final, pero, obviamente, es un caso de fuerza mayor . Sea como fuere, paga por lo que te gusta, y solo por eso, porque el anti-consumismo es genial.




Arrastre la aplicación Age 3 para abrirla. Simplemente seleccione "x86" en lugar de powerPC, porque el juego es algo 2005.

Etapa 1: busca el fragmento deseado


Sorprendentemente, esto puede ser lo más difícil. Tenemos un código desmontado, pero no tenemos idea de dónde buscar. Sí, algunos juegos tienen símbolos de depuración, por lo que puedes leer los nombres de las funciones, pero no en nuestro caso. Hopper nos da nombres como "sub_a8cbb6", con los que tenemos que lidiar solos.

Afortunadamente, tenemos una ventaja: nuestro archivo de registro . Es muy probable que se usen literales de cadena al escribir en el registro, es decir, los ASCII se muestran en el archivo ejecutable. Podemos buscarlos con grep, porque no los eliminaremos mediante ninguna compilación (sin embargo, pueden ocultarse por la ofuscación del código. Afortunadamente, esto no sucedió). Encontrar los literales de cadena grep suele ser lo primero que hacen al desmontar un programa. Entonces, ingresemos "proveedor encontrado" en el cuadro de búsqueda de Hopper y encontrará algo:


Oh sí, literales de cadena, los adoro.

Esto en sí mismo no nos hizo avanzar tanto. Pero como la dirección de esta "cadena de literales" está codificada, podemos encontrar enlaces a ella. Hopper los resaltó en verde a la derecha: "DATA XREF = sub_1d5908 + 1252". Al hacer doble clic en la línea, pasaremos a nuestro héroe: procedimiento sub_1d5908 .


Aquí encontramos el código del ensamblador. Se cree que es difícil de leer, pero no lo es. Lo más difícil es entenderlo.

De manera predeterminada, Hopper usa la "sintaxis Intel", es decir, el primer operando es el receptor y el segundo es la fuente. En la línea resaltada que vemos mov dword [esp+0x1DC8+var_1DC4], aXmlRenderConfi. Vamos a hacerlo.

Nuestra primera línea en ensamblador


movEs un equipo MOV conocido por estar en x86 Turing-complete . Mueve (en realidad copia) los datos de A a B, lo que esencialmente significa leer A y escribirlos en B. De acuerdo con lo anterior, aXmlRenderConfiesto es A, y dword [esp+0x1DC8+var_1DC4]esto es B, el destinatario. Echemos un vistazo más de cerca a esto.

aXmlRenderConfi- Este es el valor que Hopper nos ayuda. Esto es en realidad un alias para la dirección de memoria del literal de cadena en el que hicimos clic hace unos segundos. Si divide la ventana y mira el código en modo hexadecimal (que es el modo preferido para los piratas informáticos), veremos allí 88 18 83 00.


Hopper resalta convenientemente los mismos fragmentos seleccionados en ambas ventanas.

Si el valor de "voltear" es correcto, obtendremos 0x00831888, la dirección de memoria del literal de cadena (en una de las capturas de pantalla que se muestran arriba, incluso se resalta en amarillo). Entonces, se resuelve un enigma: el código ejecuta MOV, es decir, escribe la dirección 0x00831888.

¿Por qué se escribe como 88 18 83 00, no cómo 00 83 18 88? Esto sucedió debido al orden de los bytes , un tema bastante confuso que generalmente tiene poco impacto. Esta es una de esas cosas que hace que la gente diga que "las computadoras no funcionan". Para nosotros, esto significa que este problema ocurrirá en todos los números con los que trabajaremos hoy.

Es posible que haya notado que dije "escriba la dirección", y eso es cierto: realmente no nos importan los contenidos, que resultaron ser una cadena literal con un byte cero al final. Anotamos la dirección porque el código luego se referirá a esta dirección. Este es un puntero.

¿Qué hay de dword [esp+0x1DC8+var_1DC4]? Aquí todo es más complicado. dwordsignifica que estamos trabajando con "palabras dobles (palabras dobles)", es decir, con 16 * 2 bits. Es decir, copiamos 32 bits. Recordemos: nuestra dirección es0x00831888, es decir, 8 caracteres hexadecimales, y cada carácter hexadecimal puede tener 16 valores, es decir, 4 bits de datos. Entonces obtenemos 8 * 4 = 32. Por cierto, la notación "32 bits" para sistemas informáticos vino de aquí. Si utilizáramos 64 bits, la dirección de memoria se escribiría como 0x001122334455667788, tendría el doble de longitud y 2 ^ 32 veces más grande, y tendríamos que copiar 16 bytes. Entonces tendríamos mucha más memoria, pero copiar el puntero requeriría el doble de trabajo y, por lo tanto, 64 bits en realidad no son el doble de rápido que 32 bits. Por cierto, una explicación más detallada del término "palabra" se puede encontrar aquí . Porque, por supuesto, es más complicado que mi explicación.

Entonces, ¿qué pasa con la parte entre corchetes? Los corchetes significan que calculamos lo que hay dentro y lo consideramos un puntero. Por lo tanto, el comando MOV escribirá en la dirección de memoria obtenida de los cálculos. Echemos un vistazo de nuevo con más detalle (y no se preocupe, cuando haya terminado con esto, esencialmente sabrá cómo leer el código del ensamblador con la sintaxis de Intel).

espEs un registro que en ensamblador es el equivalente más cercano a una variable. Hay muchos registros en la arquitectura x86, y los diferentes registros tienen diferentes formas de aplicación, pero realmente no nos importará. Obtenga más información sobre esto aquí.. Solo tenga en cuenta que existen registros especiales para números de punto flotante, así como "banderas" que se utilizan para comparaciones y operaciones similares. Todo lo demás son solo números hexadecimales que Hopper modificó un poco para ayudarnos. var_1DC4- esto -0x1DC4, podemos verlo al comienzo del procedimiento, o en el panel derecho. Esto significa que el resultado del cálculo [esp+0x1DC8+var_1DC4]será [esp + 0x1DC8 — 0x1DC4]igual [esp + 4]. En esencia, esto significa "tomar el valor de 32 bits del registro ESP, agregar 4 e interpretar el resultado como una dirección de memoria".

Entonces, repetimos: escribimos la dirección de la cadena literal en "esp + 4". Esta es información correcta, pero es completamente inútil. ¿Qué significa esto?

Esta es la dificultad para leer el código de ensamblaje: para analizar lo que debe hacer. Aquí tenemos una ventaja: sabemos que lo más probable es que escriba algo en el registro. Por lo tanto, podemos esperar una llamada a una función que escribe en el registro. Vamos a buscarla.


En la captura de pantalla anterior, hay varias llamadas similares, así como el comando callque llama al procedimiento. Hopper generalmente habla de esto (no sé por qué no lo hizo aquí), pero en esencia, damos argumentos para llamar a la función. Si hace clic en diferentes llamadas de subrutina, encontramos algo llamado imp___jump_table___Z22StringVPrintfExWorkerAPcmmPS_PmmPKcS_. Este es el nombre decodificado de la función, y la parte "Printf" es suficiente para entender que realmente genera algo. No nos importa cómo lo hace.

Da un paso atrás


En esta etapa, nos apartamos completamente de lo que hicimos: tratamos de convencer al juego de que nuestra tarjeta gráfica de 2015 era lo suficientemente potente como para lanzar el juego de 2005. Resumamos Encontramos el lugar donde se registra el registro. Si tenemos suerte, entonces aquí está el código que verifica los parámetros y capacidades de las tarjetas gráficas. Afortunadamente, tuvimos mucha suerte (existe una alta probabilidad de que, de lo contrario, este artículo no hubiera sido así).

Para obtener una visión general, utilizaremos la función de Gráfico de flujo de control de la tolva, que divide el código en pequeños bloques y dibuja flechas que indican diferentes transiciones. Esto es mucho más fácil de entender que en un ensamblador ordinario, en el que a menudo todo está en un desastre.


La llamada a la entrada del registro está en medio de una función terriblemente larga, porque esto, nuevamente, generalmente ocurre en los juegos AAA. Aquí deberíamos buscar otros literales de cadena Hopper y "pistas", con la esperanza de encontrar algo. Al comienzo del procedimiento se encuentra el resto del registro, y debajo hay partes interesantes sobre "forzar dx7" y los parámetros "Muy altos", así como un problema con la versión de sombreadores de píxeles ... que tenemos.

El registro de nuestros datos "pirateados" informó que teníamos sombreadores de píxeles de la versión 0.0, y que son incompatibles con los parámetros "Altos". Este código se encuentra en la esquina inferior izquierda de nuestra función, y si observa de cerca, puede ver algo curioso (resaltado por su humilde servidor): este es el mismo código repetido cuatro veces para los parámetros "Muy alto", "Alto", "Medio" y "Bajo". Y sí, me llevó varias horas encontrar esto.


Destaqué en azul el bloque en el que se muestra "pixel shader version 0.0 High" en el registro. Destaqué partes similares con otros hermosos colores.

Las únicas diferencias son que escribimos "0x0", "0x1", "0x2" y "0x3" en diferentes lugares. Esto es sospechosamente similar al archivo de parámetros que examinamos anteriormente y al parámetro optiongrfxshaderquality (es posible que aún no lo entienda. Pero lo es, créame).

En esta etapa, podemos intentar ir de diferentes maneras, lo cual hice. Pero seré breve: haremos lo más necesario: descubrir por qué falla la verificación del sombreador de píxeles. Busquemos una rama. El bloque de salida al registro es lineal, lo que significa que debería estar en algún lugar arriba.


Fanáticos de SEMVER, vean un formato de versión más avanzado: números de coma flotante.

Y, de hecho, justo encima de él hay jbun comando de transición (piense en "goto"). Este es un " salto condicional ", y la condición es " si es menor ". Los "ifs" del ensamblador funcionan a través de banderas de registro , de las que hablé brevemente anteriormente. Es suficiente saber que necesitamos mirar el comando anterior: lo más probable es que establezca la bandera. A menudo es un comando cmpo test, pero se usa aquí ucomiss. Esto es barbarie. Pero ella fácilmente busca en Google : ucomiss . Este es un comando de comparación de coma flotante, y esto explica por qué el código tienexmm0: Este es un registro de números de coma flotante. Esto es lógico: los registros informan que nuestra versión de los sombreadores de píxeles es "0.0", que es un valor de punto flotante. Entonces, ¿qué se encuentra en la dirección de memoria 0x89034c? Los datos hexadecimales tienen un aspecto 00 00 00 40y pueden ser confusos. Pero sabemos que debemos esperar números de coma flotante, así que interpretemos los datos así: eso significa 2.0. Cuál es un valor lógico de coma flotante para la versión de sombreadores de píxeles que Microsoft realmente usó en su lenguaje de sombreado de alto nivelen el que se escriben los sombreadores DirectX. Y Age 3 es un juego de DirectX, a pesar de que el puerto en Mac usa OpenGL. También es lógico que en 2005 se requieran sombreadores de píxeles de la versión 2.0 para habilitar el parámetro Alto, por lo que nos estamos moviendo en la dirección correcta.

¿Cómo arreglamos esto? Esta instrucción de comparación realiza una comparación con el valor al xmm0que se da en la línea de arriba: movss xmm0, dword [eax+0xA2A8]. MOVSS es un comando especial de "movimiento" para registros de coma flotante, y escribimos 32 bits de la dirección, que es el resultado de evaluar eax + 0xA2A8.

Presumiblemente, este valor no es 2.0, sino más bien 0.0. Vamos a arreglarlo

Hackear de verdad


Finalmente, estamos listos para llegar a los parámetros del juego High. Queremos seguir el camino "rojo" y omitir toda la lógica con la "versión incorrecta de sombreadores de píxeles". Esto puede hacerse pasando con éxito la comparación, es decir, invirtiendo la condición de transición, pero tenemos un truco más: el código está compuesto de tal manera que, al eliminar la transición, iremos al camino "rojo". Simplemente reemplacemos la transición con nopuna operación que no hace nada (es imposible simplemente eliminar o agregar datos al archivo ejecutable porque se producirá un desplazamiento y todo se romperá).

Recomiendo salir de CFG para esto, porque de lo contrario Hopper comienza a volverse loco. Si todo se hace correctamente, en la ventana Hex veremos líneas rojas.



El rojo muestra los cambios que hemos realizado. En este punto, si pagó por Hopper, simplemente puede guardar el archivo. Pero no pagué, así que abriré el archivo ejecutable en Hex Fiend, encontraré el área deseada (copiando el área de Hopper a la pestaña Buscar dentro de Hex Fiend) y la cambiaré manualmente. Tenga cuidado aquí, puede romper todo, por lo que le recomiendo copiar el archivo ejecutable de origen en alguna parte.

Una vez hecho esto, inicie el juego, vaya a las opciones ... Y aclamaciones: podemos seleccionar "Alto". Pero no podemos elegir "medio". Y no podemos usar los altos parámetros de las sombras. ¡Sin embargo, estamos progresando!


¡Solo eche un vistazo a estos magníficos reflejos en el agua!

Mejoras


De hecho, puedes arreglar esto de la mejor manera. Nuestra solución funcionó, pero podemos lograr que la condición se cumpla correctamente: hacer que el juego piense que nuestra tarjeta gráfica tiene sombreadores de la versión 2.0.

Como recordamos, el juego carga el valor en la dirección eax+0xA2A8. Puede usar Hopper para averiguar qué se grabó originalmente allí. Necesitamos encontrar un comando en el que 0xA2A8 se use como el operando del destinatario (elegí 0xA2A8 porque es un valor bastante específico y no podemos estar seguros de que el mismo registro no se use en otro lugar). Aquí la luz de fondo de la tolva es muy útil para nosotros, porque podemos hacer clic en 0xA2A8 y buscar las partes amarillas en CFG.


Nuestra victoria es un poco más alta: como era de esperar, escribimos el valor de algún tipo de registro de coma flotante. Admito que realmente no entiendo lo que está sucediendo antes de esto (mi mejor suposición: el juego verifica la posibilidad de compresión de textura), pero esto no es particularmente importante. Escribamos "2.0" y continuemos el trabajo.

Para hacer esto, usaremos la "instrucción de ensamblaje" de la aplicación Hopper, y luego haremos las mismas manipulaciones en Hex Fiend.




Usamos MOV en lugar de MOVSS porque escribimos el valor habitual, y no el valor especial del registro de coma flotante. Además, está en orden de bytes directo, es decir, invertido.

Notarás que algo extraño está sucediendo: "comimos" al equipo jb. Sucedió porque el nuevo comando toma más bytes que el anterior y, como dije, no podemos agregar o eliminar datos del archivo ejecutable para guardar las compensaciones. Por lo tanto, Hopper no tiene más remedio que destruir el equipo jb. Esto puede convertirse en un problema, y ​​podemos eliminarlo moviendo el comando hacia arriba (después de todo, ya no necesitamos escribir el valor xmm3). Esto funcionará aquí, porque en cualquier caso, estamos haciendo la ramificación en el camino correcto. Suerte.

Si comienzas el juego ... nada cambiará mucho. Sugerí que la serie Intel 9 no es lo suficientemente rápida, por lo que el juego se queja. Esto no es un problema, porque en realidad estamos luchando por "Muy Alto".

¿Cuál fue la carta más poderosa alrededor de 2004? NVIDIA GeforceFX 6800. Vamos a engañarnos para convencer al juego de que lo tenemos instalado.

Por la madriguera del conejo


Sí, esta es una sección completamente nueva.

Sabemos que el juego usa códigos hexadecimales para referirse a tarjetas gráficas, y que recibe información de archivos de datos. Sabemos que esto debería suceder en la función que estamos estudiando (al menos sería extraño si no fuera así). Entonces podemos encontrar el código que hace esto. Pero aún puede ser difícil, porque no sabemos qué buscar exactamente.

Tenemos una pista: las opciones se comportan de manera extraña; hay Bajo / Alto, pero no Medio. Entonces, tal vez hay otro código que maneja todos estos "Muy alto", "Alto", "Medio" y "Bajo". Y en realidad lo está, en la misma función. Él es mucho antes en CFG, y parece interesante.


Podemos suponer que los datos sin procesar se almacenan en algún tipo de XML, porque es muy probable, porque hay varios otros archivos de datos que son XML. Y XML es esencialmente un grupo de punteros y atributos. Por lo tanto, es probable que el código aquí verifique algunos atributos XML (y de hecho, a continuación se muestra "se espera uno de ID, Muy alto ...", que es muy similar a verificar la corrección del archivo). Podemos imaginar una estructura XML similar en la que los valores booleanos determinan la calidad de los sombreadores:


Vale la pena considerar que esta es una suposición muy arbitraria. Todo puede verse un poco diferente.

Se muestra una parte muy interesante en el lado izquierdo del CFG (para usted puede estar a la derecha, pero se muestra a la izquierda en la captura de pantalla): vemos el mismo var_CCque se usa en el código que escribe la ID del dispositivo en el registro. Es decir ebp+var_CC, probablemente se refiere a la ID de nuestro dispositivo, 0x2773. Lo extraño es que no hay una cadena literal en la primera "verificación", pero este es un error de Hopper: la dirección 0x0089da8e contiene datos hexadecimales 69006400, que en UTF-16 significa "id" (de hecho, probablemente Hopper no pudo entender esto debido a UTF16). Y solo estamos buscando una identificación.

¿Sabes que? Ejecutemos el depurador y verifiquemos todo en realidad.

Depuración de juegos


Abra una ventana de terminal y ejecute lldb (simplemente escriba lldb y presione Intro). Primero tenemos que decirle a LLDB que siga a Age 3, y luego podemos comenzar el juego llamando run. El juego comienza y no obtenemos nada útil.


Ahora necesitamos agregar un punto de interrupción: para confirmar nuestras suposiciones, queremos detener la ejecución cuando lleguemos al código que se muestra arriba. No tenemos código fuente, pero no importa: podemos establecer un punto de interrupción en la dirección de memoria. Y tenemos la dirección en el código. Agreguemos una parada a una llamada MOV que recibe un "id", su dirección es 0x001d5e68. Para agregar un punto de interrupción, solo ingrese br set -a 001D5E68. Después de comenzar, el juego se detiene y LLDB muestra un código desmontado (en la sintaxis de AT&T, no Intel, por lo que todos los operandos están invertidos, pero vemos que este es el mismo código). Puede notar que LLDB en este caso es realmente más inteligente que Hopper; nos dice que estamos realizando operaciones relacionadas con XML y la salida de printf.



¿Te sientes como un hacker?

Para seguir adelante, tomemos una "instrucción de pasos". Un comando corto para esto
es ni. Repitiendo varias veces, nos damos cuenta de que estamos de vuelta a donde comenzamos. Este es un ciclo! Y esto es completamente lógico: probablemente estamos iterando alrededor de algún tipo de XML. De hecho, Hopper nos mostró esto: si seguimos el CFG, veremos un ciclo (posible).


Esto significa:if (*(ebp+var_CC) == *(ebp+var_28)) { *(ebp+var_1D84)=edi }

su condición de salida es que el valor ebp+var_1D84sea ​​distinto de cero. Y vemos un parámetro de código interesante en el resaltado por mí var_CC.

Agreguemos un punto de interrupción en este cmpy lo resolveremos.



Izquierda: establezca un punto de interrupción en el CMP y continúe la ejecución. A la derecha, monitoreamos los registros y la memoria.

Observamos los registros con reg ry el valor de la memoria con mem read $ebp-0x28. eaxy en realidad contiene 0x2773, y el valor en memoria ahora es 0x00A0. Si lo ejecuta varias veces thread c, veremos que se produce una iteración en diferentes ID en busca de coincidencias. En algún momento, los valores coincidirán, el ciclo saldrá y comenzará el juego. Ahora sabemos cómo gestionarlo.

Recupere el archivo de registro: identificó tanto el dispositivo como el fabricante. Probablemente en algún lugar hay un ciclo similar para los nombres de los fabricantes. Y, de hecho, es muy similar y se encuentra en el CFG directamente encima de nuestro ciclo. Este bucle es un poco más simple, y buscamos la cadena "vendedor".

Esta vez la comparación se hace convar_D0. Esta variable se usa realmente como ID del fabricante. Si ponemos un punto de interrupción aquí y verificamos todo, veremos el familiar 0x8086 en eax, y al mismo tiempo, está ocurriendo una comparación con 0x10DE. Escríbalo eaxy veamos qué sucede.



Guau.

Es decir, 0x10DE es NVIDIA. De hecho, uno podría entender esto al estudiar un archivo de datos, pero no es muy interesante. Ahora la pregunta es: ¿cuál es el identificador de GeforceFX 6800? Podemos usar LLDB y simplemente verificar cada identificador, pero llevará tiempo (hay muchos de ellos, intenté encontrar el correcto). Así que echemos un vistazo a render.bar esta vez.


Supongamos que esto es "XMB", la representación binaria de XML utilizada en Age 3.

Este archivo es bastante caótico. Pero después de las etiquetas Geforce FX 6800, vemos varios identificadores. Lo más probable es que necesitemos uno de ellos. Probemos con 00F1.

La forma más fácil de probar esto es usar LLDB y establecer los puntos de interrupción deseados. Omitiré este paso, pero diré que surgió 00F1 (afortunadamente).

En esta etapa, debemos responder la pregunta: "¿Cómo hacer que este cambio sea permanente?" Parece que será más fácil cambiar los valores var_CCy var_D0en 0X00F1 y 0X10DE. Para hacer esto, solo necesitamos obtener espacio para el código.

Una solución simple sería reemplazar una de las llamadas de registro de registro con un NOP, por ejemplo, una llamada al subsistema. Esto nos liberará varias decenas de bytes, y esto es incluso más de lo necesario. Seleccionemos todo esto y reemplácelo con NOP. Entonces solo necesitamos escribir información usando las variables MOV: ensamblador mov dword [ebp+var_CC], 0xF1y mov dword [ebp+var_D0], 0x10DE.



Antes y después

Vamos a ejecutar el juego. Finalmente, hemos logrado algo: puede elegir entre Bajo, Medio o Alto, así como habilitar Parámetros altos para sombras.


Pero todavía no podemos habilitar Very High, y esto es triste. ¿Qué esta pasando?


Ah, lo tengo.

Al final resultó que, la Geforce FX 6800 debería admitir sombreadores de píxeles de la versión 3.0, y solo configuramos la versión 2.0. Ahora es fácil para nosotros solucionarlo: solo escriba 3.0 punto flotante ebp+0xA2A8. Este valor es 0x40400000.

Finalmente, el juego ya no es caprichoso y podemos disfrutar de configuraciones muy altas.


Extraño, pero se ve completamente diferente. Los colores son mucho menos vibrantes y ha aparecido niebla dependiente del alcance. También hay un pequeño problema de conflicto de sombras (también se encuentra en las capturas de pantalla anteriores, esto se debe a los parámetros de sombra alta, no a la calidad de los sombreadores), que aún no he podido resolver.

Y este es el final de nuestro viaje, gracias por leer.

Además, daré ejemplos de cómo se ve cada uno de los parámetros del juego, porque todas las capturas de pantalla que se encuentran en línea son terribles o están incompletas.

Los gráficos en Medium son bastante aceptables, pero adolecen de falta de sombras. Por lo que puedo decir, Very High en su mayor parte agrega una LUT completamente diferente (una buena explicación de este término se puede encontrar en la sección Clasificación de color de este artículo.), niebla en la distancia, y tal vez incluso un modelo de iluminación completamente diferente? La imagen es muy diferente y es bastante extraña.





De arriba a abajo: muy alto, alto (con opciones de sombra alta), medio (solo con sombra habilitada), bajo (con sombra deshabilitada).

Otros enlaces utiles


El blog que he vinculado anteriormente contiene un maravilloso análisis de las líneas de renderización modernas: http://www.adriancourreges.com/blog/

Rate 0 AD es un juego RTS de código abierto del mismo género: https://play0ad.com . Ella necesita desarrolladores.

Si desea saber más sobre el formato XMB, puede leer su código en 0 AD . No lo comprobé, pero estoy bastante seguro de que este es el mismo formato, porque la persona que escribió el decodificador XMB para Age 3 en Age of Empires Heaven también implementó estos archivos en el código fuente 0 AD en 2004. Así que esta será una lección divertida en paleontología de código. Gracias por todo el trabajo, Ykkrosh.

Recuerdo exactamente que leí un excelente tutorial sobre el uso de Hopper, pero ahora no puedo encontrar el enlace. Si alguien sabe de lo que estoy hablando, suelte el enlace.

Al final, me gustaría mencionar el proyecto Cosmic Frontier: un remake de la serie clásica Escape Velocity (más de Override, pero también es compatible con Nova). Este es un juego increíble, y es muy triste que pocas personas lo sepan. Ahora los creadores han lanzado una campaña en Kickstarter, y el desarrollador principal tiene un blog de desarrollo muy interesante , que habla sobre el uso de Hex Fiend para realizar ingeniería inversa de los formatos de datos del juego original. Esta es una gran lectura. Si crees que mi artículo fue una locura. luego en una de las publicaciones ellos:

  • Lanzamos el clásico emulador de Mac para usar las API obsoletas para leer un archivo de datos cifrados.
  • Cifrado de ingeniería inversa del código del juego.
  • Utilizaron un truco del sistema de archivos para acceder a los mismos datos en una Mac moderna.

Sí, estas son personas realmente apasionadas, y espero que su proyecto se complete con éxito, porque EV Nova es mi juego favorito.

All Articles