Speedran Legend of Zelda manipulando a memória do jogo



A primeira parte de Legend of Zelda é um clássico imortal. Um jogador comum precisa de alguns dias para completá-lo, mas para os corredores de velocidade mais experientes, essa é uma questão de meia hora. No entanto, o bug muito confuso e complexo aberto pelo Sockfolder permite ao usuário executar código arbitrário diretamente do jogo para concluir o jogo em menos de três minutos.

Resumidamente, isso acontece da seguinte maneira:

  1. Digite o código na tela de entrada de nome.
  2. Nós entramos na segunda masmorra, apito.
  3. Passamos para o cemitério, chamamos de dez fantasmas.
  4. Esperamos pelas condições necessárias, colocamos o jogo em pausa quando as criaturas estão em determinados lugares.
  5. Faça uma pausa, pressione A e B ao mesmo tempo, e é isso!

Sim, isso é incrível. Agora, vamos dar uma olhada no que está acontecendo no jogo e como esse incrível bug de Legend of Zelda é executado.



Como causar um erro


Então, primeiro inserimos o código na tela de entrada de nome. Os nomes dos arquivos são armazenados na memória e cada caractere corresponde a um byte específico na memória. Damos nomes estranhos aos arquivos, porque, na verdade, escrevemos os bytes necessários na memória, que se tornarão o código do assembler. Existem apenas duas limitações:

  • podemos trabalhar com apenas três arquivos, ou seja, o programa pode conter apenas 3 * 8 caracteres = 24 bytes;
  • precisamos inserir como os cinco primeiros caracteres de um dos arquivos ZELDA se queremos começar com a segunda missão.



Precisamos começar com a segunda busca por um motivo que explicarei mais adiante. Chamamos o primeiro arquivo ZELDA porque esse nome é mais adequado. Agora vamos começar o jogo. Antes de chegarmos ao cemitério, nosso comportamento é praticamente diferente do jogo de sempre. A principal coisa que precisamos fazer é tentar não morrer duas vezes. Por "morrer", significa a própria morte e a transição frequente para o menu. Você entenderá por que isso acontece, a partir de explicações adicionais.



Depois de ouvirmos o apito, vamos à tela do cemitério para concluir o bug. O erro é acionado aqui, porque ao usar o apito, um objeto é criado a partir da lápide, que abre uma escada oculta. Isso é necessário para a ocorrência de um bug. E tudo isso é devido às limitações dos sprites. Em Legend of Zelda, a tela pode ter até 11 sprites por vez. Se você tentar criar um 12º sprite, o jogo não permitirá que você faça isso. Para reproduzir o erro, quebramos o limite de estouro e criamos o 12º sprite.



Quando você cria o objeto apito, o jogo esquece de verificar quantos sprites havia na tela antes. Portanto, quando criamos o número máximo de sprites, o sprite é criado fora da tabela de sprites, a memória é substituída e ocorre um estado inesperado.

Há um pequeno fragmento na memória começando no deslocamento 350 que armazena os identificadores de onze sprites. Um sprite é criado quando a tela é carregada, começando em uma posição com o menor deslocamento possível do sprite. Isso significa que os sprites estão procurando uma posição com um deslocamento mínimo, começando no deslocamento 350 e além. Quando um sprite deve ser criado, o jogo procura um valor vazio na tabela de sprites para substituí-lo pelo identificador de sprite, criando-o. Ao contrário de criar sprites ao carregar a tela, ao tentar criar um sprite, o jogo procura uma posição com um deslocamento máximo para o sprite. Isso significa que primeiro ela verifica se é possível criar um sprite na posição 10 (0A). Caso contrário, ela verifica a posição 9 (09), ou seja, deslocamento 359 e assim por diante. Se todas as posições do sprite estiverem ocupadas, o jogo se renderá e não criará um sprite.



No entanto, os desenvolvedores esqueceram de executar verificações de borda para que o jogo pudesse “desistir” ao tentar criar um objeto “apito”, e o jogo continua procurando por bytes com o valor 00 em um local fora da tabela de sprites para anotar o identificador de objeto. Mas onde ela procura esse significado primeiro? De fato, esta tabela faz parte de uma matriz maior que armazena informações sobre sprites. Quando o apito encontra um lugar para criar no início da tabela, ele começa a procurar posições com o valor 00 a partir do final da matriz.

Na tela do cemitério, esse fragmento da matriz contém informações sobre o estado atual das ações fantasmas. Vamos agora falar sobre os estados de ação dos fantasmas. Os fantasmas criados no cemitério se movem aleatoriamente. Isso ocorre porque suas ações são controladas pelo comportamento determinado pelo seu estado de ação. Esses estados de ação podem ter qualquer valor de 0 (00) a 5 (05), dependendo das ações do fantasma. Eles são escritos no final de uma matriz que contém informações sobre sprites.

Em geral, todos esses estados de ação correspondem a uma posição na tabela de funções que define as ações dos fantasmas. Como os estados podem ter apenas 6 valores, a tabela possui o tamanho necessário para armazenar todas as seis ações. Aqui, o estado de ação 0 (00) corresponde à aceleração do fantasma. Isso é importante, porque quando o apito começar a procurar um lugar para criar seu sprite, ele preencherá a primeira posição encontrada na memória com o valor 00. A aceleração fantasma corresponde à ação 0, registrada no código com o valor 00, então o jogo considera uma posição vazia e escreve o identificador nele apito 5E.



Em seguida, o jogo tenta executar o código na posição 5E da tabela, mas como a tabela contém apenas valores até 05, o jogo executa dados "indesejados" como um código muito além da tabela. Se você fizer tudo certo, os dados de "lixo" nos levarão ao código que gravamos com os símbolos do arquivo, o jogo será direcionado a Zelda e o jogo será realizado.

No entanto, para que o bug funcione, precisamos danificar o terceiro estado da ação na tabela. Portanto, o terceiro fantasma criado deve ser acelerado e todos os fantasmas subsequentes devem executar outras ações (cujo código não é igual a 00). Esta é uma informação importante. Agora vamos ver o que acontece quando executamos um bug.

Execução de bug


Se fizermos tudo certo, o jogo tenta executar os dados na posição 5E da tabela. Começa a ler como dados de código começando no deslocamento 602. As informações associadas ao estado da ação Link são armazenadas na memória nos desvios 602 e 603, para que possamos controlá-lo. Quando Link está de pé, os valores das compensações 602 e 603 serão 00. Mas quando pressionamos o botão B para usar o apito, o valor em 602 será 10 e, quando pressionamos A para usar a espada, o valor em 603 será 01.



Portanto, quando pressionarmos esses dois botões simultaneamente, os dados vizinhos serão 10 01. O jogo interpreta esses dados como um comando de ramificação BPL para compensar 605 e executar os dados nele como código. Os valores de 605 e 606 serão 00 se nossa saúde não estiver muito baixa e 40 se nossa saúde estiver baixa. Para passar rapidamente o jogo, esses valores devem ser iguais a 00, portanto, você deve tentar manter a saúde até que o bug seja concluído. O valor 00 corresponde à instrução BRK (interrupção). Como não faz nada aqui, precisamos que os valores sejam 00 e o jogo continue executando o código.



Desde que usamos a espada, o jogo continuará executando instruções no próximo byte ímpar. A próxima instrução, que não é BRK, está no deslocamento 08, mas como usamos a espada, o jogo pula sobre ela e executa o código em 09 e 0A. O valor no byte 09 é sempre 10, o que significa que estamos lidando novamente com a instrução de ramificação BPL. Os bytes no deslocamento 623 (62E) estão relacionados à música. Ela tenta aumentar o processo de tocar música, mas às vezes salta para valores mais baixos.

Isso significa que, para concluir o bug, precisamos aplicá-lo a uma peça musical específica. Se o valor for pequeno, temos a chance de pular na memória para uma área com valores significativamente variáveis. Eles mudam tão aleatoriamente que não podemos controlá-los em tempo real para receber instruções que o jogo deve seguir. Portanto, a transição aqui provavelmente levará a um travamento do jogo.



No entanto, após esses dados caóticos, há uma área de dados seguros. Portanto, tentaremos pular lá. Precisamos de mais dados no 60A para pular com precisão a área insegura e obter dados permanentes e seguros. Se nós fomos lá, tudo está em ordem. Mas na compensação 630, há um contador para as mortes de Link. Se Link morrer duas vezes, esse valor será 02, que forma uma instrução e interrompe o jogo. É por isso que é importante que você não morra duas vezes. Portanto, como resultado, passamos para o deslocamento 638, que é o início da tabela que armazena os nomes dos arquivos. Se chegarmos aqui, o jogo executará o código que inserimos na tela do arquivo. E aqui começa ...

Código


Os nomes dos arquivos são armazenados na memória em ordem. Isso significa que os primeiros bytes que executamos são um arquivo chamado ZELDA. Felizmente, o código executado por esses bytes é seguro, para que possamos seguir adiante. Como o nome ZELDA não é importante, vejamos o que o restante do código faz. Primeiro, executamos três comandos PLP (pull da pilha, pull da pilha). Quando chamamos uma função, ela grava dois valores na pilha. Esses valores correspondem a onde você estava no código quando executou a função. Assim, o jogo pode descobrir para onde voltar depois de executar a função.

Começamos extraindo três valores da pilha e, em seguida, executamos a função de retorno. Fazemos isso para retornar ao local em que estávamos antes da execução do bug. Caso contrário, o jogo não concluirá a execução dos dados como um código e congelará como resultado. Mas antes de chegarmos ao código que danifica a memória e termina o jogo, precisamos falar sobre mais alguns valores.

Os valores no deslocamento de memória 10 correspondem ao número do mundo em que estamos. Se estamos no mundo acima do solo, o valor é 00. Para o primeiro calabouço, o valor é 01, para o segundo - 02 e assim por diante. Como Zelda está na nona masmorra, precisamos que esse valor seja 09. No entanto, estamos executando um bug no mundo acima do solo e o valor é 00. Portanto, precisamos encontrar uma maneira de igualá-lo a 09. O valor no deslocamento 11 corresponde a alguns dados sobre o estado do jogo. Quando o jogo carrega, o valor é 00, quando o jogo não carrega - 01. Precisamos carregar o nono calabouço, portanto esse valor deve ser 00.

O valor no deslocamento 12 corresponde a alguma parte do estado. Quando a tela carrega, o valor geralmente é 02, quando não carrega - geralmente 05. Como queremos carregar a masmorra, esse valor deve ser 02.

O último valor que queremos alterar corresponde à missão em que estamos. O valor no deslocamento 62D corresponde à primeira ou segunda missão e tem o valor 00 ou 01. Começamos a partir da segunda, mas queremos terminar na primeira, porque é assim que confundimos o jogo. Portanto, precisamos alterar esse valor para 00. Isso nos colocará em um híbrido estranho da primeira e da segunda missões. Precisamos desse estado híbrido para executar o código para uma finalidade específica. Veremos isso quando explicarmos o que o código faz.



Bem, estabelecemos metas, vamos para o código. Primeiro, temos a função LSR (deslocamento lógico para a direita) do deslocamento 11. Essa função divide o valor pela metade e grava o restante no carry. 11 é um lugar na memória que armazena o valor correspondente ao estado do jogo e, antes dessa instrução, seu valor é 01. Quando dividimos por dois, obtemos 00 com o restante 01, pois a divisão é inteira. No deslocamento 11, o valor 00 é gravado e, na transferência, 01.

Então temos a função ROX para mudar 0D (girar para a esquerda, esquerda) com o índice X. Como substituímos o estado de ação do terceiro fantasma que criamos, X é igual a três. 0D + X é 0D + 03, que é hexadecimal 10. Portanto, essa função gira para a esquerda no deslocamento 10. Essa função multiplica o valor no deslocamento 10 por 2 e adiciona à hifenização. O valor começa em 0. Zero vezes 2 é igual a 0 e 0 + 1 é igual a 1, então o valor é gravado para compensar 10.

Agora a operação ROX é repetida novamente para compensar 0D com o índice X. O valor de X não mudou, ainda é três, portanto, essa é outra curva à esquerda no deslocamento 10. 1 * 2 = 2 e, como já usamos a transferência, seu valor é 0. 2 + 0 = 2, portanto o valor 2 é gravado no deslocamento 2.

Em seguida, temos a terceira operação de ROX para o deslocamento 0D. 2 * 2 = 4 e 4 + 0 = 4, então o valor 4 é escrito no deslocamento 10.

A próxima instrução é LSR no deslocamento 12. Começamos com o valor 5, então a divisão por dois nos dá o restante 1. 2 é escrito no deslocamento 12 e 1 - na transferência.

Em seguida, o último ROX vira para a esquerda no deslocamento 10. 4 * 2 = 8. O valor de transferência é 1, o adicionamos ao produto, obtendo 9.

A última instrução antes de retornar é um deslocamento lógico para a direita no deslocamento 62D. O valor aqui é 1, porque estamos na segunda missão. E como dividimos por 2, o valor será igual a 0. Então, entramos no modo de busca híbrida.

Agora, executamos a função de retorno e o jogo atualiza os valores de seu estado com os valores registrados em nosso código. Isso completa o bug e entramos no quarto de Zelda. Chegamos lá porque a busca híbrida confunde o jogo. Ela não sabe exatamente onde colocar Link. E assim nos encontramos no quarto de Zelda, tendo passado pelo jogo.



Espero que isso ajude você a entender o que está acontecendo no jogo Legend of Zelda ao executar código arbitrário e executar um bug rápido no jogo.

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


All Articles