Engenharia Reversa no Modo Desenvolvedor Animal Crossing

Usando o código em um GameCube real

No verão passado, comecei a engenharia reversa Animal Crossing para o GameCube. Eu queria explorar a possibilidade de criar mods para este jogo. Além disso, queria documentar o processo para criar tutoriais para pessoas interessadas em invadir ROMs e engenharia reversa. Neste post, falarei sobre os recursos de depuração para desenvolvedores que permaneceram no jogo e também compartilharei como descobri combos de truques que podem ser usados ​​para desbloqueá-los.

new_Debug_mode


Estudando os símbolos de depuração restantes, notei os nomes das funções e variáveis ​​que contêm a palavra "debug" e decidi que seria interessante ver se alguma funcionalidade de depuração permanecia no jogo. Se eu conseguir ativar as funções de depuração ou desenvolvimento, isso me ajudará no processo de criação de mods.

A primeira função que notei foi new_Debug_mode . É chamado pela função de entry , que inicia imediatamente após a tela do logotipo da Nintendo terminar. Tudo o que ela faz é colocar a estrutura de byte 0x1C94 e salvar um ponteiro nela.

Depois de ser chamado na entry na estrutura hospedada no deslocamento 0xD4 imediatamente antes de chamar o mainproc valor 0 é definido.


Para ver o que acontece quando o valor não é zero, 80407C8C a instrução li r0, 0 em 80407C8C , substituindo-a por li r0, 1 . Os bytes brutos da instrução li r0, 0 são 38 00 00 00 onde o valor atribuído está no final da instrução, então eu poderia substituir os bytes por 38 00 00 01 e obter li r0, 1 . Como uma maneira mais confiável de criar instruções, você pode usar algo como o kstool :

$ kstool ppc32be "li 0, 1"
li 0, 1 = [ 38 00 00 01 ]


No emulador Dolphin, esse patch pode ser aplicado acessando a guia "Patches" nas propriedades do jogo e inserindo-o da seguinte maneira:


Depois de atribuir o valor 1, um gráfico interessante apareceu na parte inferior da tela:



Parecia um indicador de desempenho: as pequenas barras na parte inferior da tela aumentavam ou diminuíam. (Mais tarde, quando examinei os nomes das funções que desenham esse gráfico, descobri que elas realmente exibem métricas de uso de CPU e memória.)

Foi ótimo, mas não particularmente útil. Depois de atribuir o valor 1, minha cidade parou de carregar, para que nada mais pudesse ser feito aqui.

Modo Zuru


Novamente, comecei a procurar outras referências a funções de depuração e várias vezes me deparei com algo chamado "modo zuru". As ramificações dos blocos de código com funcionalidade de depuração costumavam verificar a variável zurumode_flag .

função game_move_first

zzz_LotsOfDebug (o nome que eu mesmo game_move_first ) na função game_move_first mostrada acima é chamado apenas quando zurumode_flag não zurumode_flag igual a zero.

Procurando funções associadas a esse valor, encontrei estas:

  • zurumode_init
  • zurumode_callback
  • zurumode_update
  • zurumode_cleanup

À primeira vista, seu objetivo é misterioso, eles manipulam bits nos desvios de uma variável chamada osAppNMIBuffer .

Aqui está como o trabalho dessas funções olhou à primeira vista:

zurumode_init


  • Define zurumode_flag como 0
  • Verifica vários bits no osAppNMIBuffer
  • Salva um ponteiro para a função zurumode_callback na estrutura padmgr
  • Chamadas zurumode_update

zurumode_update


  • Verifica vários bits no osAppNMIBuffer
  • Dependendo do valor desses bits, o zurumode_flag atualiza
  • Imprime uma string de formato no console do SO.

Isso geralmente é útil para contextualizar o código, mas havia muitos caracteres não imprimíveis na linha. O único texto reconhecível foi "zurumode_flag" e "% d".

sequência de formato do modo zuru

Supondo que poderia ser texto em japonês com codificação de caracteres multibyte, passei a string pela ferramenta de reconhecimento de codificação e descobri que a string foi codificada com Shift-JIS. Na tradução, a linha simplesmente significava "O valor de zurumode_flag mudou de% d para% d". Isso não nos fornece muitas informações novas, mas agora sabemos que o Shift-JIS é usado: em arquivos binários e tabelas de linhas, há muito mais linhas nessa codificação.

zurumode_callback


  • Chamadas zerumode_check_keycheck
  • Verifica vários bits no osAppNMIBuffer
  • O valor zurumode_flag está sendo zurumode_flag
  • Chamadas zurumode_update

zerumode_check_keycheck até nos conhecermos por causa de uma ortografia diferente ... o que é?

zerumode_check_keycheck

Uma função enorme e complexa que trabalha muito mais em bits com valores sem nome.

Nesse ponto, decidi dar um passo atrás e estudar outras funções e variáveis ​​de depuração, porque não tinha certeza da importância do modo zuru. Além disso, não entendi o que “verificação de chave” significa aqui. É possível que essa seja uma chave criptográfica?

Voltar à depuração


Nessa época, notei um problema com minha maneira de carregar símbolos de depuração na IDA. O arquivo foresta.map no disco do jogo contém muitos endereços e nomes de funções e variáveis. No começo, não vi que os endereços de cada seção recomeçam do zero, então escrevi um script simples que adiciona uma entrada de nome para cada linha do arquivo.

Escrevi novos scripts da IDA para corrigir o carregamento de tabelas de símbolos para diferentes seções do programa: .text , .rodata , .data e .bss . A seção .text contém todas as funções, então eu fiz isso para que, desta vez, quando eu definir o nome, o script reconheceria automaticamente as funções em cada endereço.

Nas seções de dados, ele agora criou um segmento para cada objeto binário (por exemplo, m_debug.o , que deveria ser o código compilado para algo chamado m_debug ) e definiu o espaço e os nomes de cada parte dos dados.

Isso me deu muito mais informações, mas tive que definir manualmente o tipo de dados para cada parte dos dados, porque defini cada objeto de dados como uma matriz de bytes simples. (Olhando para trás, entendo que seria melhor supor que fragmentos de 4 bytes continha números inteiros de 32 bits, porque havia muitos e muitos endereços contidos de funções e dados importantes para a criação de referências cruzadas.)

Estudando o novo segmento .bss para m_debug_mode.o , encontrei várias variáveis ​​no formato quest_draw_status e event_status . Isso é interessante porque eu queria que informações úteis, não apenas um gráfico de desempenho, fossem exibidas no modo de depuração. Felizmente, nesses registros de dados, havia referências cruzadas para um grande pedaço de código verificando debug_print_flg .

Usando um depurador no emulador Dolphin, defino um ponto de interrupção no local da função em que debug_print_flg verificado (no 8039816C ) para entender como essa verificação funciona. Mas o programa nunca passou para esse ponto de interrupção.

Vamos game_debug_draw_last por que isso acontece: essa função é chamada por game_debug_draw_last . Adivinha qual valor é verificado antes de sua chamada condicional? zurumode_flag ! Que diabos está acontecendo?

verificação zurumode_flag

Defina um ponto de interrupção nessa verificação ( 80404E18 ) e funcionou imediatamente. O valor de zurumode_flag era zero, portanto, na execução normal, o programa teria perdido a chamada para esta função. Em vez disso, inseri uma instrução de ramificação NOP (substituí-a por uma instrução que não faz nada) para verificar o que acontece quando a função é chamada.

No depurador Dolphin, isso pode ser feito pausando o jogo, clicando com o botão direito do mouse nas instruções e selecionando “Insert nop”:

Depuração do depurador Dolphin

Nada aconteceu. Depois, verifiquei o que estava acontecendo dentro da função e descobri outra construção ramificada que contornava tudo de interessante que acontecia em 803981a8 . Também inseri o NOP e a letra “D” apareceu no canto superior direito da tela.

Letra do modo de depuração D

Nesta função no 8039816C (eu chamei de zzz_DebugDrawPrint ), ainda há um monte de código interessante, mas não é chamado. Se você olhar para esta função na forma de um gráfico, poderá ver que há uma série de operadores de ramificação que ignoram os blocos de código em toda a função:

Ramificações em zzz_DebugDrawPrint

Depois de inserir o NOP em vez de várias outras construções ramificadas, comecei a ver várias coisas interessantes na tela:

Mais material de depuração sendo impresso

A próxima pergunta era como ativar essa funcionalidade de depuração sem alterar o código.

Além disso, em algumas construções de ramificação, zurumode_flag ocorre novamente nessa função de desenho de depuração. zurumode_update outro patch para que em zurumode_update sinalizador zurumode_update sempre zurumode_flag atribuído ao valor 2, porque quando não se compara com 0, é comparado especificamente com o valor 2.

Depois de reiniciar o jogo, vi no canto superior direito da tela uma mensagem "msg. não "

exibição do número da mensagem

O número 687 é o identificador de registro da mensagem exibida mais recentemente. Eu o verifiquei usando o programa visualizador de tabelas que escrevi no início da análise, mas você também pode verificá-lo usando o editor de tabelas de strings com uma GUI completa , que escrevi para invadir ROMs. Aqui está a aparência da postagem no editor:

Mensagem 687 no editor de tabela de cadeias

Nesse ponto, ficou claro que o estudo do modo zuru não era mais retirado - está diretamente relacionado às funções de depuração do jogo.

Voltar ao modo Zuru novamente


zurumode_init inicializa várias coisas:

  • 0xC(padmgr_class) recebe o valor do endereço zurumode_callback
  • 0x10(padmgr_class) recebe o valor do endereço da própria padmgr_class
  • 0x4(zuruKeyCheck) recebe o valor do último bit na palavra carregada de 0x3C(osAppNMIBuffer) .

Eu descobri o que padmgr o padmgr , uma abreviação de "gamepad manager". Isso significa que pode haver uma combinação especial de teclas (botões) que podem ser inseridas no gamepad para ativar o modo zuru, ou algum tipo de dispositivo de depuração ou função do console do desenvolvedor que pode ser usado para enviar um sinal para ativá-lo.

zurumode_init é executado apenas na primeira inicialização do jogo (quando o botão de reset é pressionado, ele não funciona).

Depois de definir um ponto de interrupção no endereço 8040efa4 , no qual o valor 0x4(zuruKeyCheck) é atribuído 0x4(zuruKeyCheck) , podemos ver que, ao carregar sem pressionar as teclas, o valor é definido como 0. Se você substituí-lo por 1, algo interessante acontece:

Tela de título com o modo zuru

A letra “D” aparece novamente no canto superior direito (desta vez é verde, não amarelo), e algumas informações de montagem também são exibidas:

[CopyDate: 02/08/01 00:16:48 ]
[Date: 02-07-31 12:52:00]
[Creator:SRD@SRD036J]


Um patch que sempre define 0x4(zuruKeyCheck) como 1 no início é assim:

8040ef9c 38c00001

Esta parece ser a maneira correta de inicializar o modo zuru. Após isso, várias ações podem ser necessárias para obter a exibição de certas informações de depuração. Iniciando o jogo, dando uma volta nele e conversando com um morador, não veremos nenhuma das mensagens mencionadas acima (exceto a letra "D" no canto).

Os suspeitos mais prováveis ​​são zurumode_update e zurumode_callback .

zurumode_update


zurumode_update chamado primeiro em zurumode_init e depois constantemente chamado por zurumode_callback .

Ele verifica novamente o último bit 0x3C(osAppNMIBuffer) e, com base nesse valor, atualiza zurumode_flag .

Se o bit for zero, o sinalizador será definido como zero.

Caso contrário, a seguinte instrução é executada, com o valor completo 0x3c(osAppNMIBuffer) sendo r5 :

extrwi r3, r5, 1, 28

Extrai o 28º bit de r5 e o armazena em r3 .

Em seguida, 1 é adicionado ao resultado, ou seja, o resultado final é sempre 1 ou 2.

Em seguida, zurumode_flag comparado com o resultado anterior, dependendo de quantos dos 28º e últimos bits estão configurados para 0x3c(osAppNMIBuffer) : 0, 1 ou 2.

Este valor é gravado em zurumode_flag . Se não mudar nada, a função sai e retorna o valor atual do sinalizador. Se ele mudar o valor, será executada uma cadeia muito mais complexa de blocos de código.

Uma mensagem é exibida em japonês: o mesmo "valor zurumode_flag alterado de% d para% d", sobre o qual falamos acima.

Em seguida, uma série de funções é chamada com argumentos diferentes, dependendo se o sinalizador se tornou igual a zero ou não. O código assembler desta parte é monótono, então mostrarei seu 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) } 

Observe que, se o sinalizador for zero, o argumento 0 será passado para JC_JUTDbPrint_setVisible.

Se o sinalizador não for igual a 0x3C(osAppNMIBuffer) E o bit 25 ou 31 são definidos como 0x3C(osAppNMIBuffer) , então setVisible passado o argumento 1.

Esta é a primeira chave para ativar o modo zuru: o último bit 0x3C(osAppNMIBuffer) deve ser definido como 1 para exibir informações de depuração e definir zurumode_flag valor diferente de zero.

zurumode_callback


zurumode_callback está localizado em 8040ee74 e provavelmente é chamado por uma função relacionada ao gamepad. Após inserir um ponto de interrupção no depurador Dolphin, a pilha de chamadas nos mostra que na verdade está sendo chamada de padmgr_HandleRetraceMsg .

Uma de suas primeiras ações foi executar zerucheck_key_check . Essa função é complexa, mas parece que em geral foi projetada para ler e atualizar o valor de zuruKeyCheck . Antes de passar para a função de verificação de tecla, decidi verificar como esse valor é usado no restante da função de retorno de chamada.

Em seguida, ele novamente verifica alguns bits em 0x3c(osAppNMIBuffer) . Se o bit 26 estiver definido ou se o bit 25 estiver definido e padmgr_isConnectedController(1) retornar um valor diferente de zero, o último bit em 0x3c(osAppNMIBuffer) como 1!

Se nenhum desses bits estiver definido ou o bit 25 estiver definido, mas padmgr_isConnectedController(1) retornar 0, a função verificará se o byte no endereço 0x4(zuruKeyCheck) é igual a zero. Se igual, ele redefine o último bit no valor original e o grava de volta em 0x3c(osAppNMIBuffer) . Caso contrário, ele ainda define o último bit como 1.

No pseudo-código, fica assim:

 x = osAppNMIBuffer[0x3c] if (BIT(x, 26) || (BIT(x, 25) && isConnectedController(1)) || zuruKeyCheck[4] != 0) { osAppNMIBuffer[0x3c] = x | 1 // set last bit } else { osAppNMIBuffer[0x3c] = x & ~1 // clear last bit } 

Depois disso, se o bit 26 não estiver definido, a função continuará chamando zurumode_update e será encerrada.

Se o bit estiver definido, se 0x4(zuruKeyCheck) não 0x4(zuruKeyCheck) igual a zero, ele carregará uma string de formato na qual exibirá o seguinte: “ZURU% d /% d”.

Para resumir o subtotal


Aqui está o que acontece:

padmgr_HandleRetraceMsg chama zurumode_callback . Eu suponho que essa "mensagem de retração de identificador" significa que simplesmente verifica as teclas digitadas no controlador. Com cada varredura, isso pode causar uma série de retornos de chamada diferentes.

Quando zurumode_callback executado zurumode_callback ele verifica os pressionamentos de tecla atuais (botões). Parece que ela está verificando um botão específico ou uma combinação de botões.

O último bit no NMI Buffer é atualizado dependendo dos bits específicos em seu valor atual, bem como do valor de um dos bytes zuruKeyCheck ( 0x4(zuruKeyCheck) ).

Então zurumode_update é zurumode_update e verifica esse bit. Se for 0, o sinalizador do modo zuru será definido como 0. Se for 1, o sinalizador de modo mudará para 1 ou 2, dependendo se o bit 28 estiver definido.

Existem três maneiras de ativar o modo zuru:

  1. O bit 26 está definido como 0x3C(osAppNMIBuffer)
  2. O bit 25 está definido como 0x3C(osAppNMIBuffer) e o controlador está conectado à porta 2
  3. 0x4(zuruKeyCheck) não 0x4(zuruKeyCheck) zero

osAppNMIBuffer


Interessado no que significa osAppNMIBuffer , comecei a procurar por "NMI" e encontrei links no contexto da Nintendo como "interrupção não mascarável". Acontece que o nome dessa variável é totalmente mencionado na documentação do desenvolvedor do Nintendo 64:

o osAppNMIBuffer é um buffer de 64 bytes que é limpo após uma reinicialização a frio. Se o sistema reiniciar devido à NMI, o estado desse buffer não será alterado.

De fato, esse é um pequeno pedaço de memória salva durante uma reinicialização "suave" (com o botão de reinicialização). O jogo pode usar esse buffer para armazenar quaisquer dados enquanto o console estiver na rede. O Animal Crossing original foi lançado no Nintendo 64, por isso é lógico que algo semelhante deveria aparecer no código.

Se formos para o arquivo binário boot.dol (tudo mostrado acima estava em foresta.rel ), sua função main possui muitos links para osAppNMIBuffer . Uma rápida olhada mostra que há uma série de verificações que podem levar à 0x3c(osAppNMIBuffer) valores de diferentes bits 0x3c(osAppNMIBuffer) usando operações OR.

Os seguintes valores do operando OR podem ser interessantes:

  • Bit 31: 0x01
  • Bit 30: 0x02
  • Bit 29: 0x04
  • Bit 28: 0x08
  • Bit 27: 0x10
  • Bit 26: 0x20

Lembramos que os bits 25, 26 e 28 são especialmente interessantes: 25 e 26 determinam se o modo zuru está ativado e o bit 28 determina o nível da flag (1 ou 2).
O bit 31 também é interessante, mas parece que muda dependendo dos valores dos outros.

Bit 26

Primeiro: no endereço 800062e0 há uma instrução ori r0, r0, 0x20 com um valor de buffer de 0x3c . Ele define o bit 26, que sempre ativa o modo zuru.

Bit de configuração 26

Para que o bit seja definido, o oitavo byte retornado de DVDGetCurrentDiskID deve ser 0x99 . Este identificador está localizado no início da imagem de disco do jogo e é carregado na memória em 80000000 . Em um lançamento de varejo regular do jogo, o ID fica assim:

47 41 46 45 30 31 00 00 GAFE01..

Substituindo o último byte do identificador por um 0x99 para 0x99 , obtemos a seguinte imagem ao iniciar o jogo:

ID da versão do jogo 0x99

E o seguinte é exibido no console do SO:

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 os outros patches podem ser removidos, após o que a letra D aparece novamente no canto superior direito da tela, mas não há mais mensagens de depuração ativadas.

Bit 25

O bit 25 é usado em conjunto com a verificação da porta do controlador 2. O que faz com que ele seja ligado?

Bit 25 e 28

Acontece que ele deve usar a mesma verificação do bit 28: a versão deve ser maior ou igual a 0x90 . Se o bit 26 estiver definido (o ID é 0x99 ), os dois bits também serão definidos e o modo zuru ainda será ativado.

No entanto, se a versão estiver no intervalo de 0x90 a 0x98 , o modo zuru não será ativado instantaneamente. Lembre-se da verificação realizada em zurumode_callback - o modo será ativado apenas se o bit 25 estiver definido e padmgr_isConnectedController(1) retornar um valor diferente de zero.

Após o controlador ser conectado à porta 2 (o argumento isConnectedController possui zero indexação), o modo zuru é ativado. A letra D e as informações sobre a montagem aparecem na tela inicial, e nós ... podemos controlar a exibição da depuração usando os botões do segundo controlador!

Alguns botões executam ações que não apenas alteram a exibição, mas também, por exemplo, aumentam a velocidade do jogo.

zerucheck_key_check


O último mistério permanece 0x4(zuruKeyCheck) . Acontece que esse valor é atualizado com uma enorme função complexa que mostrei acima:

zerumode_check_keycheck

Usando o depurador do emulador Dolphin, consegui determinar que o valor verificado por esta função é um conjunto de bits correspondente ao pressionamento do botão no segundo controlador.

O rastreamento de cliques no botão é armazenado em um valor de 16 bits em 0x2(zuruKeyCheck) . Quando o controlador não está conectado, o valor é 0x7638 .

2 bytes contendo sinalizadores das teclas pressionadas do controlador são baixados e atualizados no início de zerucheck_key_check . O novo valor é passado com o registro r4 função padmgr_HandleRetraceMsg quando chama a função de retorno de chamada.

fim de verificação de chave

Perto do final de zerucheck_key_check há outro local em que 0x4(zuruKeyCheck) atualizado 0x4(zuruKeyCheck) .Ele não apareceu na lista de referências cruzadas porque é usado como o endereço base r3, e podemos descobrir o valor r3apenas olhando para qual valor é atribuído a ele antes de chamar esta função.

No endereço, o 8040ed88valor é r4gravado em 0x4(zuruKeyCheck). Logo antes disso, mas ele é gravado no mesmo local e, em seguida, XORs a partir de 1. A tarefa desta operação é alternar o valor do byte (e de fato o último bit) entre 0 e 1. (Se o valor for 0, o resultado de
XOR de 1 será 1). Se o valor for 1, o resultado será 0. Consulte a tabela verdade para XOR.)

fim de verificação de chave

Anteriormente, quando estudei os valores na memória, não percebi esse comportamento, mas tentarei quebrar essa instrução no depurador para entender o que está acontecendo. O valor original é carregado em 8040ed7c.

Sem tocar nos botões do controlador, não chegarei a esse ponto de interrupção na tela inicial. Para entrar nesse bloco de código, o valor r5deve se tornar igual 0xbantes da instrução de ramificação anterior ao ponto de interrupção ( 8040ed74). Dos muitos caminhos diferentes que levam a esse bloco, apenas um atribui um r5valor 0xbà sua frente no endereço 8040ed68.

Configurando r5 para 0xb

Observe que, para alcançar o bloco que atribui o r5valor 0xB, o valor r0deve ser igual logo antes dele 0x1000. Após os blocos da cadeia até o início da função, podemos ver todas as restrições necessárias para atingir esse bloco:

  • 8040ed74: o valor r5deve ser igual0xB
  • 8040ed60: o valor r0deve ser igual0x1000
  • 8040ebe8: o valor r5deve ser igual0xA
  • 8040ebe4: o valor r5deve ser menor0x5B
  • 8040eba4: o valor r5deve ser maior0x7
  • 8040eb94: o valor r6deve ser 1
  • 8040eb5c: o valor r0não deve ser 0
  • 8040eb74: os valores do botão da porta 2 devem mudar

Rastreando o caminho do código

Aqui chegamos ao ponto em que os valores dos botões antigos são carregados e os novos valores são salvos. Em seguida, vêm algumas operações aplicadas aos valores novos e antigos: A operação XOR marca todos os bits que foram alterados entre os dois valores. Em seguida, a operação AND mascara a nova entrada para definir todos os bits que não estão configurados no momento para o estado 0. O resultado é um conjunto de novos bits (pressionar o botão) no novo valor. Se não estiver vazio, estamos no caminho certo. Para fazer a diferença , o quarto dos botões de rastreamento de 16 bits deve mudar. Depois de inserir um ponto de interrupção após a operação XOR / AND, descobri que o botão START aciona esse estado. A próxima pergunta é como fazê- lo ser inicialmente igual

old_vals = old_vals XOR new_vals
old_vals = old_vals AND new_vals


r0

r00x1000

r50xA. r5e r6são carregados desde 0x0(zuruKeyCheck)o início da função de teste principal e atualizados mais perto do fim, quando não entramos no bloco de código que o inclui 0x4(zuruKeyCheck).

Existem vários lugares antes disso onde o r5valor é atribuído 0xA:

  • 8040ed50
  • 8040ed00
  • 8040ed38

8040ed38

  • 8040ed34: o valor r0deve ser igual 0x4000(o botão B é pressionado)
  • 8040ebe0: o valor r5deve ser igual0x5b
  • 8040eba4: o valor r5deve ser maior0x7
  • então tudo corre como antes ...

r5 deve começar com 0x5b

8040ed00

  • 8040ecfc: o valor r0deve ser igual 0xC000(A e B são pressionados)
  • 8040ebf8: o valor r5deve ser> = 9
  • 8040ebf0: o valor r5deve ser menor que 10
  • 8040ebe4: o valor r5deve ser menor0x5b
  • 8040eba4: r5deve ser mais0x7
  • então tudo corre como antes ...

r5 deve começar às 9

8040ed50

  • 8040ed4c: o valor r0deve ser igual 0x8000(botão A pressionado)
  • 8040ec04: o valor r5deve ser menor0x5d
  • 8040ebe4: o valor r5deve ser maior0x5b
  • 8040eba4: o valor r5deve ser maior0x7
  • então tudo corre como antes ...

r5deve começar com 0x5c

Parece que existe algum tipo de estado entre as teclas, após o qual você precisa inserir uma certa sequência de combos a partir dos botões, terminando com a tecla START. Parece que A e / ou B devem ir logo antes de INICIAR.

Se você rastrear o caminho do código que define o r5valor como 9, surge um padrão: r5- é um valor crescente que pode aumentar quando um r0valor adequado é encontrado ou zero. Os casos mais estranhos, onde não é um valor na faixa de 0x0até0xB, ocorre ao processar etapas com vários botões, por exemplo, quando A e B. são pressionados simultaneamente.Uma pessoa que tenta entrar nesse combo geralmente não pode pressionar os dois botões exatamente ao mesmo tempo enquanto acompanha o gamepad, portanto, é necessário processar o botão pressionado primeiro.

Continuamos a explorar diferentes caminhos de código:

  • r5assume o valor 9 quando RIGHT é pressionado no endereço 8040ece8.
  • r5assume o valor 8 quando o botão direito C no endereço é pressionado 8040eccc.
  • r5assume o valor 7 quando o botão esquerdo C é pressionado no endereço 8040ecb0.
  • r5assume o valor 6 quando ESQUERDA é pressionado no endereço 8040ec98.
  • r5assume o valor 5 (e r6 assume o valor 1) quando DOWN é pressionado no endereço 8040ec7c.
  • r5assume o valor 4 quando o botão superior C no endereço é pressionado 8040ec64.
  • r5assume o valor 3 quando o botão inferior C no endereço é pressionado 8040ec48.
  • r5assume o valor 2 quando UP é pressionado no endereço 8040ec30.
  • r5assume o valor 1 (e r6assume o valor 1) quando Z é pressionado no endereço 8040ec1c.

A sequência atual é:

Z, CIMA, C-BAIXO, C-UP, BAIXO, ESQUERDA, C-ESQUERDA, C-DIREITA, DIREITA, A + B, INÍCIO

Antes de verificar Z, mais uma condição é verificada: embora um novo botão deva ser pressionado Z, os sinalizadores atuais devem ser iguais 0x2030: os pára-choques esquerdo e direito também devem ser pressionados (eles têm valores de 0x10e 0x20). Além disso, ACIMA / ABAIXO / ESQUERDA / DIREITA são botões D-pad, não um stick analógico.

Batota código


A combinação completa fica assim:

  1. Segure os para-choques L + R e pressione Z
  2. D-up
  3. C-DOWN
  4. C-up
  5. D-down
  6. D-esquerda
  7. C-esquerda
  8. C-DIREITA
  9. D-DIREITA
  10. A + B
  11. INICIAR

Isso funciona! Conecte o controlador à segunda porta e insira o código, após o qual as informações de depuração serão exibidas. Depois disso, você pode começar a pressionar os botões no segundo (ou até terceiro) controlador para executar várias ações.

Essa combinação funcionará sem corrigir o número da versão do jogo. Pode até ser usado em uma cópia regular do jogo, sem nenhuma ferramenta de trapaça ou mods de console. Voltar a entrar em combos desativa o modo zuru.

Usando o código em um GameCube real

A mensagem "ZURU% d /% d" é zurumode_callbackusada para exibir o status dessa combinação se você a inserir quando o ID do disco já for igual 0x99(provavelmente para depurar o próprio código de fraude). O primeiro número é sua posição atual na sequência, correspondente r5. O segundo pega o valor 1, quando certos botões da sequência são mantidos, eles podem corresponder a quando o r6valor é atribuído 1.

A maioria das mensagens não explica o que está fazendo na tela; portanto, para entender sua finalidade, é necessário encontrar funções que as processem. Por exemplo, uma longa linha de estrelas azuis e vermelhas na parte superior da tela são espaços reservados para exibir o status de várias missões. Quando a missão está ativa, alguns números aparecem lá, informando o status da missão.

A tela preta exibida ao pressionar Z é um console para exibir mensagens de depuração, especificamente para aspectos de baixo nível, como alocação de memória, erros de heap e outras exceções ruins. Por comportamento, fault_callback_scrollpode-se supor que ele seja usado para exibir esses erros antes que o sistema reinicie. Ele não inicia nenhum desses erros, mas pode causar a impressão de alguns caracteres ilegíveis com vários NOPs. Eu acho que no futuro será muito útil para exibir suas próprias mensagens de depuração:

Caracteres de lixo da console

Depois de fazer tudo isso, descobri que entrar no modo de depuração corrigindo o ID da
versão 0x99é conhecido por outras pessoas: https://tcrf.net/Animal_Crossing#Debug_Mode . (Também existem boas notas no link que indicam várias mensagens e conversam sobre outras coisas que podem ser feitas com o controlador na porta 3.) No entanto, até onde eu sei, ninguém publicou a combinação de truques ainda.

Isso é tudo. Há outros recursos de desenvolvedor que eu gostaria de explorar, como a tela de depuração do cartão e a tela de seleção do emulador NES e como ativá-los sem usar patches.

Tela de seleção de mapa


Além disso, publicarei artigos sobre engenharia reversa de sistemas de diálogo, eventos e missões com o objetivo de criar mods.

Source: https://habr.com/ru/post/pt413967/


All Articles