Contexte d’exécution

La description du challenge mentionne clairement un jeu "MetroCross" sur Atari ST. "small & old-school". "Small", je ne sais pas, mais "old-school", certainement. On se renseigne rapidement sur ce type de machine, fossilisée depuis quelques années : basé sur du 68000 (sans savoir encore très bien lequel), quelques centaines de KB de mémoire, une "cartouche" ROM pour démarrer des jeux… un TOS dans la ROM de base. Tiens, la description du challenge mentionne effectivement un TOS "compatible" avec la version du challenge. Faudrait pas oublier ce détail.

Appréhension des binaires mis à disposition

Le challenge est présenté initialement avec deux fichiers binaires : challenge.st et tos100fr.img. J’oublie très vite le 2ème, qui semble être générique, alors que le premier fichier porte le joli nom de challenge.st.

Il reste quand même à savoir comment est empaqueté le code dans ce fichier, pour l’instant brut, qu’est challenge.st.

Quelques outils de base sous Linux rendent un verdict très décevant :

Ça commence très mal.

Finalement, on jette un coup d’œil avec xxd :

Ça me dit rien du tout, ça : "Turbodisk", recherché sur Internet pendant un certain temps sans rien donner de concluant. Finalement, je tombe sur cette magnifique doc :
http://fr.meric.free.fr/Technique/Documentations/LeHardwareDuST.pdf
qui me sauve la vie. Je vous conseille d’y jeter un œil…

Le PDF fossilisé : un scan de doc papier, pas de recherche possible par Crtl-F, tout est à chercher au binoculaire. Merci quand même à l’auteur et à l’administrateur du site pour ce bout de doc !

Bref, en passant la roulette à 2 pages par seconde sous les yeux, je tilte sur la page 321 : elle décrit le secteur de boot d’un floppy Atari ! Je retrouve des "traces" cohérentes : 0x200 (512) octets par secteurs à l’offset 0x0b, 2 FATs à l’offset 0x10, 10 secteurs par piste à l’offset 0x18.

Ce binaire challenge.st est une image de floppy ST et ça ressemble vaguement à de la FAT, donc du FileSystem trivialement appelé "msdos" sous Linux.

Oh, un coup de losetup et de mount, on aura peut-être le contenu ?

Il y a clairement de l’eau dans le gaz, mais aussi quelques entrées qui semblent valides : c’est parti dans le sous-répertoire auto/, qui contient un fichier metro.prg. Yes. !!!

Bon, pendant que cette fichue disquette est "montée" sur le rootfs, même salement, on fait un petit snapshot de ce que Linux peut y lire, pour une éventuelle exploration future dans un répertoire de travail local. On ne sait jamais.

Extraction brute du code cible

C’est bon, on a un beau binaire d’Atari ST, ce metro.prg, qu’on donne à manger brut de fonderie à IDA, en lui précisant "juste" le type de processeur : 68000. Simple. Ya plus qu’a reverser, dans 2 heures, c’est flaggé.

Travail de reverse habituel sous IDA :

Recherche de chaîne de caractères indiquant l’évolution du programme : ça tombe bien, on trouve facilement un "Congrat..", référencé vers la fin d’une boucle, dont on trouve l’entrée, dont on découvre un truc qui ressemble à un menu principal…

Tiens, dans l’énoncé du chall, ça parle de "main menu". On désosse, on trouve une belle variable qui ressemble franchement à un compteur de niveau : initialisée à 1 avant la grande boucle, incrémentée dans un coin, comparée à 0x19 en bas de la boucle, avant d’écrire "Congrat…". Aaaaaaaah ! C’est là. Fastoche !!!

Fastoche… sauf que les accès à cette variable qui SEMBLE être le fameux "level", patché par le cheatcode, n’est modifié qu’à des endroits qui semblent tout à fait légitimes : affectation de "1", incrémentation, décrémentation, comparaison à 0x19… Me trompe-je ? Ce n’est finalement pas la variable qui contient le "level" du jeu. Remise en question. Re-reverse… relecture… Cette variable n’est pas affectée brutalement.

Toujours rien, je m’attaque au handler de clavier, vu que le cheatcode est entré pendant le mainmenu, sans doute par un système de handler appelé par interruption… on ne sait jamais, peut-être que le mécanisme d’interruption existait à l’époque…

Je me casse les dents à tout décrire dans le handler de keyboard, du registre au buffer pseudo-circulaire du mainmenu. J’en profite quand même pour observer le type de code issu du contrôleur, un scancode à l’ancienne, translaté par une simple table (trouée) en code ASCII de l’époque.

Et toujours rien qui semble catcher des entrées claviers ou joystick entre le handler et le mainmenu. Ça commence à être long…

Mais il y a quand même quelque chose un peu louche, là… rien que dans les deux captures précédentes… du code bizarre… vous n’avez pas remarqué ? Rien ne vous à choqué ? C’est pourtant assez clair : qu’est-ce que c’est que ces deux instructions "TRAP #3" et "TRAP #4" qui traînent, là, hein ? Aucune idée. Ça doit être des trucs qui vont sur le TOS, probablement. Du code légitime.

Bizarrement, il n’y a rien sur ce sujet dans la doc "tout sur atari ST", rien sur le Web, rien qui parle des traps #3 et #4, seulement des #13 et #14… bizarre, quand même.

Et puis, c’est quand même bizarre, mon programme est chargé bêtement en 0x00000000 avec IDA, et c’est là qu’il y a les vecteurs d’exceptions du 68K… il y a truc qui ne va pas là. Plus j’y pense et plus j’oublie. Grave erreur…

Sous les (très) bons conseils de mon chef, je compare le binaire du challenge avec une version disponible sur Internet, en supposant que cette dernière ne contient pas les modifs du chall. Un coup de radiff -x en couleur et je confirme le doute :

Là, c’est clair : les instructions TRAP#3 et TRAP#4 ont été sauvagement ajoutées en écrasant du code existant. On tient le bon bout là, il faut trouver les handlers. Merci chef.

En passant, ce diff montre un gros morceau de code inséré là où une routine charge des secteurs à l’arrache depuis le floppy. Plus quelques autres modifications, insignifiantes vues d’ici. Grave erreur (bis) …

Allez, même pas peur, un handler d’exception, étant donné ce qu’on a déjà vu dans IDA jusque là, ça finit toujours par l’instruction "rte". Fastoche, une recherche binaire dans un binaire, avec rafind2, c’est facile et rapide. Et là, on tombe "seulement" sur un bout de code qui ressemble à un handler, qui comparerait bien des scancode clavier…  c’est fini :

C’est beau, c’est clair, enfin à peu près. Avec la super doc en papier fossilisé, on translate chaque scancode en code ASCII, ça donne à peut près "TRUNK", ça se tient…

On essaie dans l’émulateur Hatari, et là, rien. Ça doit être les conversions "qwerty / azerty" de l’époque ? Re-essaie. Retapage. Encore, encore, encore. Rien. Il faut être honnête à ce moment-là : le cheatcode n’est PAS "TRUNK".

En y regardant de plus près, il y a un truc louche, incohérent, dans le maintien de la variable "byte_B64A". Après le "T", ça ne colle pas, pour que ça colle, il faudrait un tiret "–", ou bien la touche "8" ou bien une autre touche, ça dépend si on est en QWERTY ou AZERTY.

Oui mais toutes les touches ne sont pas mappés comme les claviers d’aujourd’hui. Aaaaaaaaah…. Essai sur Hatari, plouf, essai avec, sans, avec autre chose entre le "T" et le "U", rien, rien rien, toujours la page d’intro du jeu avec "Round 1".

C’est mort. Ce n’est pas "TRUNK".

Le mur d’incompréhension

De toute façon, ça m’étonne pas trop, finalement, parce que je n’ai toujours pas compris comment sont installés les handlers des TRAP#3 et TRAP#4. La clef doit être par là. Forcément. Traversée du désert.

Parmi les mirages rencontrés, le bootloader de cette disquette contiendrait-il du code !? Au final, c’est peut être lui qui installe ses handlers d’exception #3 et #4, exécuté discrètement par le TOS avant de booter ? Introspection des premiers sections du floppy : rien. Dans les restes de doc, rien. Rien rien.

Bien. C’est sûrement dans le TOS, compte tenu que le challenge ne fonctionne qu’avec ce TOS : début de reverse du code, puis rapidement un radiff avec le TOS original trouvé sur internet. Déception : les deux version sont totalement identiques, donc ils ne doivent pas installer les handlers des TRAP #3 et TRAP #4…

Il fait tellement chaud dans ce désert que, finalement, peut être justement que le TOS1.00 contient du code pour les TRAP #3 et TRAP #4, pas trop sec, comme depuis toujours dans les version 1.00 de tous les soft de la Terre ? Recherche des opcodes correspondant à «rte » dans tous le TOS, identification des entrées de handlers correspondant, reverse de tous les installateurs de handlers trouvés. Aucun dans le TOS ne correspond à l’installation des handlers pour les TRAP #3 et TRAP #4. Pffff. Rien, rien rien.

Et les fichiers de sons et de musiques au fait ? Mais bien sûr, voire sûrement, ils ont mis du code dedans, chargé en mémoire et détourné le flot d’exécution dessus. Oui mais non, les fichiers de sons/musiques sont identiques au jeu original, et aucun code modifié ne redirige l’exécution vers les zones de chargement de la musique/sons.

Et la drôle d’instruction "_OpenRF", au fait ?

Elle a une bonne tête d’instruction louche aussi, celle-là, finalement.

Évidemment !

Lecture de doc, changement de type de processeur sous IDA, recherche sur internet. Est-ce que le TOS1.00 aurait à voir avec cette instruction ? Rien, rien, rien. (D’ailleurs, à l’heure du write-up, je ne sais toujours pas ce que fait cette instruction… si quelqu’un peut me déniaiser à ce sujet, je suis preneur).

L’auteur du challenge à mis en œuvre des techniques de virus de l’époque avec des instructions du processeur non documentées pour exécuter du code caché quelque part, activé par des ondes magnétiques extrasolaires… Renseignement pris sur les virus de l’époque, tout avait l’air de tourner autour du bootloader du floppy. Déjà investigué, c’est pas ça. Rien, rien rien. Le désert.

Reprise du radiff entre le metro.prg du challenge et l’original, et découpe chirurgicale des différences : damned, j’avais raté un bout de code, une différence insignifiante… Grave erreur.

Alors, c’est quoi la différence avec le code ici :

Juste un beau code d’installation de handler de trap#3 et trap#4, code inséré délicatement dans du code légitime. Bien voilà, on y arrive… une fois commenté, le code du challenge ressemble à ça :

Sauf que les adresses des handlers (0x11384 et 0x12650) ne correspondent toujours pas du tout avec du code valide. Et l’affectation à l’adresse 0x113a1, pareil. Rien, rien rien.

Dépité, je joue avec le coredump d’Hatari. Pour ma culture personnelle, ça doit être du JIT, et peut-être qu’il y a quelque chose à glaner, juste pour voir un peut comment peut fonctionner cet émulateur… Peut-être que dans le process "Hatari" est mappée une zone mémoire reflétant la mémoire émulée du 68K. On ne sait jamais. Allez, c’est parti comme une brute :

Bon, vu comme ça, 700M à analyser, va falloir tailler gras.

On recherche des bouts de code de metro.prg avec notre copain rafind :

Par exemple, est-ce qu’on trouve une fois au moins, mais pas trop de fois non plus, le tout début du code 0x41f900001198 :

Rien. Mauvaise piste, on arrivera à rien avec l’image coredump de l’émulateur. Ç’aurait été trop beau, trop facile, finalement. Bah, on essaie quand même avec l’instruction suivante, on est plus à ça près :

Tiens, on retrouve UNE fois et une seule le pattern. Allez hop, envoyer moi la sauce dans IDA. Méthode de bourrin, on prend dd sur le coredump pour en sortir à peu près 77K à partir de l’offset du pattern trouvé par rafind correspondant au code à l’offset 6 dans le programme :

qui donne ceci :

Vous ne voyez rien, mais quand ça fait des heures qu’on cherche, on trouve que ça ressemble pas mal au fichier sur le disque metro.prg. Smells good !

Sortie du désert

On remet ça dans IDA, et là, c’est la sortie immédiate du désert. TOUTES LES ADRESSES correspondent à quelque chose de cohérents : les handlers des TRAP #3 et TRAP #4, les petits bouts de code modifiés par-ci par-là. Tout, tout tout !

Finalement, le programme est ÉVIDEMMENT relogé par l’OS (GEMDOS), à une adresse qui dépend de l’alignement des planètes, de la version du TOS et de l’autoboot ou pas.

Quand on démarre Hatari directement sur challenge.st, le programme metro.prg dans le répertoire spécial "auto" est chargé et exécuté automatiquement, et ce à l’adresse 0x0000a304. Le code ajouté (installation des handlers, selfmodified code) n’est PAS modifié par la table de relocation, donc ce code ne fonctionne QUE s’il est chargé en 0x0000a304, pendant l’autoboot.

En conclusion, au moins deux ÉNORMES erreurs de ma part :

  • interprétation beaucoup sommaire du radiff entre la version metro.prg du challenge et la version originale trouvée sur le Net, différence qui montrait très clairement l’installation des handlers des trap#3 et trap#4.
  • beaucoup trop de temps avant d’essayer en live le jeu au travers l’émulateur, Hatari, pour se concentrer seulement sur l’analyse statique. Non, quand on peut faire autrement, on essaie rapidement autrement. Il n’y a pas que le statique dans la vie, il faut TOUT essayer.

Enfin, une dernière autre erreur (de vieillesse), je ne me souvenais plus que les programmes MSDOS pouvaient être relogés. Les formats ELF et PE sont arrivés depuis si longtemps…

Merci Synacktiv pour cette belle prise de tête.


mushrooomguy