El verano pasado, comencé la ingeniería inversa Animal Crossing para GameCube. Quería explorar la posibilidad de crear mods para este juego. Además, quería documentar el proceso para crear tutoriales para personas interesadas en piratear ROM e ingeniería inversa. En esta publicación, hablaré sobre las características de depuración del desarrollador que permanecieron en el juego, y también compartiré cómo descubrí combinaciones de trucos que se pueden usar para desbloquearlas.
new_Debug_mode
Al estudiar los símbolos de depuración restantes, noté los nombres de las funciones y variables que contenían la palabra "depuración", y decidí que sería interesante ver si quedaba alguna funcionalidad de depuración en el juego. Si logro activar las funciones de depuración o desarrollo, esto me ayudará en el proceso de creación de modificaciones.
La primera función que noté fue
new_Debug_mode
. Se llama mediante la función de
entry
, que comienza inmediatamente después de que finaliza la pantalla del logotipo de Nintendo. Todo lo que hace es colocar la estructura de bytes
0x1C94
y guardar un puntero en ella.
Después de que se llama en la
entry
en la estructura alojada en el desplazamiento
0xD4
inmediatamente antes de llamar a
mainproc
valor 0.
Para ver qué sucede cuando el valor no es cero,
80407C8C
instrucción
li r0, 0
en
80407C8C
, reemplazándola por
li r0, 1
. Los bytes sin procesar de la instrucción
li r0, 0
son
38 00 00 00
donde el valor asignado se encuentra al final de la instrucción, por lo que podría reemplazar los bytes con
38 00 00 01
y obtener
li r0, 1
. Como una forma más confiable de crear instrucciones, puede usar algo como
kstool
:
$ kstool ppc32be "li 0, 1"
li 0, 1 = [ 38 00 00 01 ]
En el emulador Dolphin, este parche se puede aplicar yendo a la pestaña "Parches" en las propiedades del juego e ingresándolo de la siguiente manera:
Después de asignar el valor 1, apareció un gráfico interesante en la parte inferior de la pantalla:
Parecía un indicador de rendimiento: las pequeñas barras en la parte inferior de la pantalla aumentaron o disminuyeron. (Más tarde, cuando miré los nombres de las funciones que dibujan este gráfico, descubrí que en realidad muestran las métricas de uso de CPU y memoria).
Fue genial, pero no particularmente útil. Después de asignar el valor 1, mi ciudad dejó de cargarse, por lo que no se pudo hacer nada más aquí.
Modo Zuru
Nuevamente comencé a buscar otras referencias a las funciones de depuración, y varias veces me encontré con algo llamado "modo zuru". Las ramas de bloques de código con funcionalidad de depuración a menudo verificaban la variable
zurumode_flag
.
zzz_LotsOfDebug
(el nombre que se me ocurrió) en la función
game_move_first
que se muestra arriba solo se llama cuando
zurumode_flag
no
zurumode_flag
igual a cero.
Buscando funciones asociadas con este valor, encontré estas:
zurumode_init
zurumode_callback
zurumode_update
zurumode_cleanup
A primera vista, su propósito es misterioso, hacen malabarismos con los bits de una variable llamada
osAppNMIBuffer
.
Así es como se veía el trabajo de estas funciones a primera vista:
zurumode_init
- Establece
zurumode_flag
en 0 - Comprueba varios bits en
osAppNMIBuffer
- Guarda un puntero a la función
zurumode_callback
en la estructura padmgr
- Llamadas
zurumode_update
zurumode_update
- Comprueba varios bits en
osAppNMIBuffer
- Dependiendo del valor de estos bits,
zurumode_flag
actualizaciones de zurumode_flag
- Imprime una cadena de formato en la consola del sistema operativo.
Esto suele ser útil para dar contexto al código, pero había muchos caracteres no imprimibles en la línea. El único texto reconocible fue "zurumode_flag" y "% d".
Suponiendo que podría ser texto japonés con codificación de caracteres multibyte, pasé la cadena a través de la herramienta de reconocimiento de codificación y descubrí que la cadena estaba codificada con Shift-JIS. En la traducción, la línea simplemente significaba "El valor de zurumode_flag ha cambiado de% d a% d". Esto no nos da mucha información nueva, pero ahora sabemos que se usa Shift-JIS: en archivos binarios y tablas de filas hay muchas más líneas en esta codificación.
zurumode_callback
- Llamadas
zerumode_check_keycheck
- Comprueba varios bits en
osAppNMIBuffer
- El valor
zurumode_flag
se zurumode_flag
- Llamadas
zurumode_update
zerumode_check_keycheck
hasta que nos conocimos debido a una ortografía diferente ... ¿qué es?
Una gran función compleja que hace mucho más trabajo en bits con valores sin nombre.
En este punto, decidí dar un paso atrás y estudiar otras funciones y variables de depuración, porque no estaba seguro de la importancia del modo zuru. Además, no entendí qué significa "verificación de clave" aquí. ¿Es posible que esta sea una clave criptográfica?
Volver a la depuración
Alrededor de este tiempo, noté un problema con mi forma de cargar símbolos de depuración en la IDA. El archivo
foresta.map
en el disco del juego contiene muchas direcciones y nombres de funciones y variables. Al principio, no vi que las direcciones para cada sección comenzaran nuevamente desde cero, así que escribí un script simple que agrega una entrada de nombre para cada línea del archivo.
Escribí nuevos scripts de IDA para arreglar la carga de tablas de símbolos para diferentes secciones del programa:
.text
,
.rodata
,
.data
y
.bss
. La sección
.text
contiene todas las funciones, por lo que esta vez, cuando configuré el nombre, el script reconocería automáticamente las funciones en cada dirección.
En las secciones de datos, ahora creó un segmento para cada objeto binario (por ejemplo,
m_debug.o
, que se suponía que era un código compilado para algo llamado
m_debug
), y estableció el espacio y los nombres para cada pieza de datos.
Esto me dio mucha más información, pero tuve que configurar manualmente el tipo de datos para cada pieza de datos, porque definí cada objeto de datos como una simple matriz de bytes. (Mirando hacia atrás, entiendo que sería mejor suponer que los fragmentos de 4 bytes contenían enteros de 32 bits, porque había muchas, y muchas, contenían direcciones de funciones y datos importantes para construir referencias cruzadas).
Al estudiar el nuevo segmento
.bss
para
m_debug_mode.o
, encontré varias variables de la forma
quest_draw_status
y
event_status
. Esto es interesante porque quería información útil, no solo un gráfico de rendimiento, que se mostrara en modo de depuración. Afortunadamente, a partir de estos registros de datos hubo referencias cruzadas a una gran parte del código que verifica
debug_print_flg
.
Usando un depurador en el emulador Dolphin, establecí un punto de interrupción en la ubicación de la función donde
debug_print_flg
verificó
8039816C
(en
8039816C
) para comprender cómo funciona esta verificación. Pero el programa nunca pasó a este punto de quiebre.
Veamos por qué sucede esto:
game_debug_draw_last
llama a esta función. ¿Adivina qué valor se verifica antes de su llamada condicional?
zurumode_flag
! Que diablos esta pasando
Establecí un punto de interrupción en este control (
80404E18
) y funcionó de inmediato. El valor de
zurumode_flag
era cero, por lo que en la ejecución normal, el programa habría perdido la llamada a esta función. En su lugar, inserté una instrucción de bifurcación NOP (la reemplacé con una instrucción que no hace nada) para verificar qué sucede cuando se llama a la función.
En el depurador Dolphin, esto se puede hacer pausando el juego, haciendo clic derecho en las instrucciones y seleccionando "Insertar nop":
No paso nada Luego verifiqué lo que estaba sucediendo dentro de la función y descubrí otra construcción de ramificación que eludió todo lo interesante que sucedía en
803981a8
. También inserté NOP, y la letra "D" apareció en la esquina superior derecha de la pantalla.
En esta función en
8039816C
(lo llamé
zzz_DebugDrawPrint
), todavía hay un montón de código interesante, pero no se llama. Si observa esta función en forma de gráfico, puede ver que hay una serie de operadores de ramificación que omiten bloques de código en toda la función:
Después de insertar NOP en lugar de varias otras construcciones de ramificación, comencé a ver varias cosas interesantes en la pantalla:
La siguiente pregunta fue cómo activar esta funcionalidad de depuración sin cambiar el código.
Además, en algunas construcciones de rama,
zurumode_flag
ocurre nuevamente en esta función de dibujo de depuración.
zurumode_update
otro parche para que en
zurumode_update
bandera
zurumode_update
siempre
zurumode_flag
asignado el valor 2, porque cuando no se compara con 0, se compara específicamente con el valor 2.
Después de reiniciar el juego, vi en la esquina superior derecha de la pantalla un mensaje "mensaje". no ".
El número 687 es el identificador de registro del mensaje mostrado más recientemente. Lo verifiqué usando el programa de visualización de tablas que escribí al comienzo del análisis, pero también puede verificarlo usando el
editor de tablas de cadenas con una GUI completa , que escribí para piratear ROM. Así es como se ve la publicación en el editor:
En este punto, quedó claro que el estudio del modo zuru ya no se eliminaba, está directamente relacionado con las funciones de depuración del juego.
Volver al modo Zuru nuevamente
zurumode_init
inicializa varias cosas:
0xC(padmgr_class)
asigna el valor de la dirección zurumode_callback
0x10(padmgr_class)
asigna el valor de dirección de padmgr_class
0x4(zuruKeyCheck)
asigna el valor del último bit en la palabra cargada desde 0x3C(osAppNMIBuffer)
.
Descubrí qué
padmgr
, una abreviatura de "gamepad manager". Esto significa que puede haber una combinación especial de teclas (botones) que se pueden ingresar en el gamepad para activar el modo zuru, o algún tipo de dispositivo de depuración o función de la consola del desarrollador que se puede usar para enviar una señal para activarlo.
zurumode_init
se ejecuta solo en el primer arranque del juego (cuando se presiona el botón de reinicio, no funciona).
Después de establecer un punto de interrupción en la dirección
8040efa4
, en la que se asigna el valor
0x4(zuruKeyCheck)
, podemos ver que al cargar sin presionar teclas, el valor se establece en 0. Si lo reemplaza por 1, sucede algo interesante:
La letra "D" aparece nuevamente en la esquina superior derecha (esta vez es verde, no amarilla), y también se muestra información de ensamblaje:
[CopyDate: 02/08/01 00:16:48 ]
[Date: 02-07-31 12:52:00]
[Creator:SRD@SRD036J]
Un parche que siempre establece
0x4(zuruKeyCheck)
en 1 al principio tiene este aspecto:
8040ef9c 38c00001
Esta parece ser la forma correcta de inicializar el modo zuru. Después de esto, pueden ser necesarias varias acciones para lograr la visualización de cierta información de depuración. Al comenzar el juego, dar un paseo y hablar con un aldeano, no veremos ninguno de los mensajes mencionados anteriormente (excepto la letra "D" en la esquina).
Los sospechosos más probables son
zurumode_update
y
zurumode_callback
.
zurumode_update
zurumode_update
llama primero en
zurumode_init
y luego se llama constantemente por
zurumode_callback
.
Comprueba nuevamente el último bit
0x3C(osAppNMIBuffer)
y luego, en función de este valor, actualiza
zurumode_flag
.
Si el bit es cero, la bandera se pone a cero.
Si no, se ejecuta la siguiente instrucción, con el valor completo
0x3c(osAppNMIBuffer)
siendo
r5
:
extrwi r3, r5, 1, 28
Extrae el bit 28 de
r5
y lo almacena en
r3
.
Luego se agrega 1 al resultado, es decir, el resultado final es siempre 1 o 2.
Luego,
zurumode_flag
compara con el resultado anterior, dependiendo de cuántos de los bits 28 y últimos se establecen en
0x3c(osAppNMIBuffer)
: 0, 1 o 2.
Este valor se escribe en
zurumode_flag
. Si no cambia nada, la función sale y devuelve el valor del indicador actual. Si cambia el valor, se ejecuta una cadena de bloques de código mucho más compleja.
Se muestra un mensaje en japonés: el mismo "valor de zurumode_flag cambió de% d a% d", del que hablamos anteriormente.
Luego se llama a una serie de funciones con diferentes argumentos, dependiendo de si el indicador se ha convertido en cero o no. El código de ensamblador de esta parte es monótono, por lo que mostraré su pseudocódigo:
if (flag_changed_to_zero) { JC_JUTAssertion_changeDevice(2) JC_JUTDbPrint_setVisible(JC_JUTDbPrint_getManager(), 0) } else if (BIT(nmiBuffer, 25) || BIT(nmiBuffer, 31)) { JC_JUTAssertion_changeDevice(3) JC_JUTDbPrint_setVisible(JC_JUTDbPrint_getManager(), 1) }
Tenga en cuenta que si el indicador es cero, el argumento 0 se pasa a JC_JUTDbPrint_setVisible.
Si el indicador no es igual a cero, y el bit 25 o el bit 31 se establecen en
0x3C(osAppNMIBuffer)
, entonces
setVisible
pasa al argumento 1.
Esta es la primera clave para activar el modo zuru: el último bit
0x3C(osAppNMIBuffer)
debe establecerse en 1 para mostrar la información de depuración y establecer
zurumode_flag
valor distinto de cero.
zurumode_callback
zurumode_callback
se encuentra en
8040ee74
y probablemente es llamado por una función relacionada con el gamepad. Después de insertar un punto de interrupción en el depurador Dolphin, la pila de llamadas nos muestra que en realidad se está llamando desde
padmgr_HandleRetraceMsg
.
Una de sus primeras acciones fue ejecutar
zerucheck_key_check
. Esta función es compleja, pero parece que, en general, está diseñada para leer y actualizar el valor de
zuruKeyCheck
. Antes de pasar a la función de comprobación de teclas, decidí comprobar cómo se usa este valor en el resto de la función de devolución de llamada.
Luego, nuevamente comprueba algunos bits en
0x3c(osAppNMIBuffer)
. Si se establece el bit 26, o si se establece el bit 25 y
padmgr_isConnectedController(1)
devuelve un valor distinto de cero, ¡el último bit en
0x3c(osAppNMIBuffer)
en 1!
Si ninguno de estos bits está establecido, o el bit 25 está establecido, pero
padmgr_isConnectedController(1)
devuelve 0, entonces la función verifica si el byte en la dirección
0x4(zuruKeyCheck)
es igual a cero. Si es igual, restablece el último bit en el valor original y lo vuelve a
0x3c(osAppNMIBuffer)
en
0x3c(osAppNMIBuffer)
. De lo contrario, aún establece el último bit en 1.
En pseudocódigo, se ve así:
x = osAppNMIBuffer[0x3c] if (BIT(x, 26) || (BIT(x, 25) && isConnectedController(1)) || zuruKeyCheck[4] != 0) { osAppNMIBuffer[0x3c] = x | 1
Después de eso, si el bit 26 no está establecido, la función procede a llamar a
zurumode_update
y luego sale.
Si se establece el bit, entonces si
0x4(zuruKeyCheck)
no
0x4(zuruKeyCheck)
igual a cero, entonces carga una cadena de formato en la que muestra lo siguiente: "ZURU% d /% d".
Para resumir el subtotal
Esto es lo que pasa:
padmgr_HandleRetraceMsg
llama a
zurumode_callback
. Supongo que este "mensaje de seguimiento de manejo" significa que simplemente escanea las pulsaciones de teclas del controlador. Con cada escaneo, puede causar una serie de devoluciones de llamada diferentes.
Cuando
zurumode_callback
ejecuta
zurumode_callback
verifica las pulsaciones de teclado (botones) actuales. Parece que está comprobando un botón específico o una combinación de botones.
El último bit en el Buffer NMI se actualiza dependiendo de los bits específicos en su valor actual, así como del valor de uno de los bytes
0x4(zuruKeyCheck)
).
Luego se
zurumode_update
y comprueba este bit. Si es 0, el indicador de modo zuru se establece en 0. Si es 1, el indicador de modo cambia a 1 o 2, dependiendo de si el bit 28 está configurado.
Hay tres formas de activar el modo zuru:- El bit 26 se establece en
0x3C(osAppNMIBuffer)
- El bit 25 se establece en
0x3C(osAppNMIBuffer)
y el controlador está conectado al puerto 2 0x4(zuruKeyCheck)
no 0x4(zuruKeyCheck)
cero
osAppNMIBuffer
Interesado en lo que significa
osAppNMIBuffer
, comencé a buscar "NMI" y encontré enlaces en el contexto de Nintendo a "interrupción no enmascarable". Resulta que el nombre de esta variable se menciona por completo en la documentación del desarrollador para Nintendo 64:
osAppNMIBuffer es un búfer de 64 bytes que se borra al reiniciar en frío. Si el sistema se reinicia debido a NMI, el estado de este búfer no cambia.
De hecho, esta es una pequeña porción de memoria guardada durante un reinicio "suave" (con el botón de reinicio). El juego puede usar este búfer para almacenar datos mientras la consola está en la red. El Animal Crossing original se lanzó en Nintendo 64, por lo que es lógico que algo similar debería haber aparecido en el código.
Si vamos al archivo binario
boot.dol
(todo lo que se muestra arriba estaba en
foresta.rel
), entonces su función
main
tiene muchos enlaces a
osAppNMIBuffer
. Una mirada rápida muestra que hay una serie de comprobaciones que pueden conducir a
0x3c(osAppNMIBuffer)
valores de diferentes bits
0x3c(osAppNMIBuffer)
utilizando operaciones OR.
Los siguientes valores de operando OR pueden ser interesantes:
- Bit 31: 0x01
- Bit 30: 0x02
- Bit 29: 0x04
- Bit 28: 0x08
- Bit 27: 0x10
- Bit 26: 0x20
Recordamos que los bits 25, 26 y 28 son especialmente interesantes: 25 y 26 determinan si el modo zuru está activado, y el bit 28 determina el nivel de bandera (1 o 2).
El bit 31 también es interesante, pero parece que cambia según los valores de los demás.
Bit 26
En primer lugar: en la dirección
800062e0
hay una instrucción
ori r0, r0, 0x20
con un valor de búfer de
0x3c
. Establece el bit 26, que siempre activa el modo zuru.
Para establecer el bit, el octavo byte devuelto por
DVDGetCurrentDiskID
debe ser
0x99
. Este identificador se encuentra al comienzo de la imagen de disco del juego y se carga en la memoria al
80000000
. En un lanzamiento regular del juego, el ID se ve así:
47 41 46 45 30 31 00 00 GAFE01..
Reemplazando el último byte del identificador con un
0x99
para
0x99
, obtenemos la siguiente imagen al iniciar el juego:
Y lo siguiente se muestra en la consola del sistema operativo:
06:43:404 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: ZURUMODE2 ENABLE
08:00:288 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: osAppNMIBuffer[15]=0x00000078
Todos los demás parches se pueden eliminar, después de lo cual la letra D aparece nuevamente en la esquina superior derecha de la pantalla, pero ya no se activan los mensajes de depuración.
Bit 25
El bit 25 se usa junto con la verificación del puerto del controlador 2. ¿Qué hace que se encienda?
Resulta que debería usar la misma verificación que para el bit 28: la versión debe ser mayor o igual a
0x90
. Si se establece el bit 26 (ID es
0x99
), ambos bits también se establecerán y el modo zuru seguirá activado.
Sin embargo, si la versión está en el rango de
0x90
a
0x98
, entonces el modo zuru no se activa instantáneamente.
zurumode_callback
la comprobación realizada en
zurumode_callback
: el modo se habilitará solo si se establece el bit 25
y padmgr_isConnectedController(1)
devuelve un valor distinto de cero.
Después de que el controlador está conectado al puerto 2 (el argumento
isConnectedController
tiene indexación cero), se activa el modo zuru. La letra D y la información sobre el ensamblaje aparecen en la pantalla inicial, y podemos ... ¡controlar la visualización de la depuración utilizando los botones del segundo controlador!
Algunos botones realizan acciones que no solo cambian la pantalla, sino que también, por ejemplo, aumentan la velocidad del juego.
zerucheck_key_check
El último misterio sigue siendo
0x4(zuruKeyCheck)
. Resulta que este valor se actualiza con una enorme función compleja que mostré arriba:
Usando el depurador del emulador Dolphin, pude determinar que el valor verificado por esta función es un conjunto de bits correspondientes a las pulsaciones de botón en el segundo controlador.
El seguimiento de los clics de los botones se almacena en un valor de 16 bits a
0x2(zuruKeyCheck)
. Cuando el controlador no está conectado, el valor es
0x7638
.
Se descargan 2 bytes que contienen las banderas de las pulsaciones del botón del controlador y luego se actualizan al comienzo de
zerucheck_key_check
. El nuevo valor se pasa con el registro
r4
función
padmgr_HandleRetraceMsg
cuando llama a la función de devolución de llamada.
Cerca del final de
zerucheck_key_check
hay otro lugar donde
0x4(zuruKeyCheck)
actualiza
0x4(zuruKeyCheck)
. ,
r3
,
r3
, .
8040ed88
r4
0x4(zuruKeyCheck)
. XOR- 1. — ( — ) 0 1. ( 0,
XOR 1 1. 1, 0. . XOR.)
Anteriormente, cuando estudié los valores en la memoria, no noté este comportamiento, pero intentaré romper esta instrucción en el depurador para comprender lo que está sucediendo. El valor original se carga en 8040ed7c
.Sin tocar los botones del controlador, no llegaré a este punto de interrupción en la pantalla inicial. Para entrar en este bloque de código, el valor r5
debe ser igual 0xb
antes de la instrucción de bifurcación que va antes del punto de interrupción ( 8040ed74
). De los muchos caminos diferentes que conducen a este bloque, solo uno asigna un r5
valor 0xb
frente a él en la dirección 8040ed68
.Tenga en cuenta que para alcanzar el bloque que asigna el r5
valor 0xB
, el valor r0
debe ser igual justo antes 0x1000
. Siguiendo los bloques en la cadena hasta el comienzo de la función, podemos ver todas las restricciones necesarias para lograr este bloque:- 8040ed74: el valor
r5
debe ser igual0xB
- 8040ed60: el valor
r0
debe ser igual0x1000
- 8040ebe8: el valor
r5
debe ser igual0xA
- 8040ebe4: el valor
r5
debe ser menor0x5B
- 8040eba4: el valor
r5
debe ser mayor0x7
- 8040eb94: el valor
r6
debe ser 1 - 8040eb5c: el valor
r0
no debe ser 0 - 8040eb74: los valores del botón del puerto 2 deberían cambiar

Aquí hemos llegado al punto en el que se cargan los viejos valores de los botones y se guardan los nuevos valores. Luego vienen un par de operaciones aplicadas a los valores nuevos y antiguos: la operación XOR marca todos los bits que han cambiado entre los dos valores. Luego, la operación AND enmascara la nueva entrada para establecer todos los bits que no están configurados actualmente en el estado 0. El resultado es un conjunto de bits nuevos (pulsaciones de botón) en el nuevo valor. Si no está vacío, estamos en el camino correcto. Para marcar la diferencia , el cuarto de los botones de seguimiento de 16 bits debe cambiar. Después de insertar un punto de interrupción después de la operación XOR / AND, descubrí que el botón INICIO activa este estado. La siguiente pregunta es cómo lograr que sea inicialmente igualold_vals = old_vals XOR new_vals
old_vals = old_vals AND new_vals
r0
r0
0x1000
r5
0xA
. r5
y r6
se cargan desde 0x0(zuruKeyCheck)
el comienzo de la función de prueba clave y se actualizan más cerca del final, cuando no ingresamos en el bloque de código que incluye 0x4(zuruKeyCheck)
.Hay varios lugares antes de esto donde r5
se asigna el valor 0xA
:8040ed38
8040ed34
: el valor r0
debe ser igual 0x4000
( se presiona el botón B)8040ebe0
: el valor r5
debe ser igual0x5b
8040eba4
: el valor r5
debe ser mayor0x7
- entonces todo sigue como antes ...
r5
debería comenzar con 0x5b
8040ed00
8040ecfc
: el valor r0
debe ser igual 0xC000
(se presionan A y B)8040ebf8
: el valor r5
debe ser> = 98040ebf0
: el valor r5
debe ser inferior a 108040ebe4
: el valor r5
debe ser menor0x5b
8040eba4
: r5
debería ser más0x7
- entonces todo sigue como antes ...
r5
debería comenzar a las 98040ed50
8040ed4c
: el valor r0
debe ser igual 0x8000
(botón A presionado)8040ec04
: el valor r5
debe ser menor0x5d
8040ebe4
: el valor r5
debe ser mayor0x5b
8040eba4
: el valor r5
debe ser mayor0x7
- entonces todo sigue como antes ...
r5
debería comenzar con 0x5c
Parece que hay algún tipo de estado entre las pulsaciones de teclas, después de lo cual debe ingresar una cierta secuencia de combos desde los botones, que termina presionando START. Parece que A y / o B deberían ir justo antes de INICIAR.Si traza la ruta del código que establece el r5
valor en 9, entonces surge un patrón: r5
- es un valor creciente que puede aumentar cuando se encuentra un r0
valor adecuado o cero. Los casos más extraños cuando este no es un valor en el rango de 0x0
a0xB
, se produce cuando se procesan pasos con varios botones, por ejemplo, cuando se presionan simultáneamente A y B. Una persona que intenta ingresar a este combo generalmente no puede presionar ambos botones exactamente al mismo tiempo mientras rastrea el gamepad, por lo que debe procesar el botón que se presiona. el primeroContinuamos explorando diferentes rutas de código:r5
toma el valor 9 cuando se presiona DERECHA en la dirección 8040ece8
.r5
toma el valor 8 cuando se presiona el botón derecho C en la dirección 8040eccc
.r5
toma el valor 7 cuando se presiona el botón izquierdo C en la dirección 8040ecb0
.r5
toma el valor 6 cuando se presiona IZQUIERDA en la dirección 8040ec98
.r5
toma el valor 5 (y r6 toma el valor 1) cuando se presiona ABAJO en la dirección 8040ec7c
.r5
4, C 8040ec64
.r5
3, C 8040ec48
.r5
2, UP 8040ec30
.r5
1 ( r6
1), Z 8040ec1c
.
:
Z, UP, C-DOWN, C-UP, DOWN, LEFT, C-LEFT, C-RIGHT, RIGHT, A+B, START
Z : Z,
0x2030
: (
0x10
0x20
). , UP/DOWN/LEFT/RIGHT — D-pad, .
-
:
- Sostenga los parachoques L + R y presione Z
- D-up
- C-ABAJO
- C-up
- D-down
- D-izquierda
- C-izquierda
- C-DERECHA
- D-DERECHA
- A + B
- START
Funciona! Conecte el controlador al segundo puerto e ingrese el código, después de lo cual aparece la información de depuración. Después de eso, puede comenzar a presionar los botones en el segundo (o incluso tercero) controlador para realizar varias acciones.Este combo funcionará sin parchear el número de versión del juego. Incluso se puede usar en una copia minorista regular del juego sin herramientas de trampa o mods de consola. Volver a ingresar combos desactiva el modo zuru.«ZURU %d/%d»
zurumode_callback
, , ID
0x99
(, -). — ,
r5
. 1, , ,
r6
1.
, , , . , — . , - , .
La pantalla negra que se muestra al presionar Z es una consola para mostrar mensajes de depuración, específicamente para aspectos de bajo nivel, como la asignación de memoria, errores de almacenamiento dinámico y otras malas excepciones. Por comportamiento, fault_callback_scroll
se puede suponer que se usa para mostrar estos errores antes de que el sistema se reinicie. No inicia ninguno de estos errores, pero puede hacer que impriman un par de caracteres basura con varios NOP. Creo que en el futuro será muy útil para mostrar sus propios mensajes de depuración:Después de hacer todo esto, descubrí que otras personas ya conocen el modo de depuración parcheando el ID de laversión 0x99
: https://tcrf.net/Animal_Crossing#Debug_Mode . (También hay buenas notas en el enlace que indican varios mensajes, y habla sobre otras cosas que se pueden hacer con el controlador en el puerto 3.) Sin embargo, hasta donde sé, nadie ha publicado la combinación de trucos todavía.Eso es todo
Hay otras características del desarrollador que me gustaría explorar, como la pantalla de depuración de la tarjeta y la pantalla de selección del emulador NES, y cómo activarlas sin usar parches.Además, publicaré artículos sobre ingeniería inversa de sistemas de diálogo, eventos y misiones con el objetivo de crear mods.