Journal An unexpected Linux : reverse engineering

Posté par  . Licence CC By‑SA.
Étiquettes :
115
28
mai
2017

Sommaire

Bonjour bonjour

Depuis quelques semaines maintenant, je suis l'heureux propriétaire (et pas privateur) d'un flipper Stern Ghostbusters, une bien belle machine bourrée de mécanique et d'électronique…
Lors de l'installation, le vendeur m'a expliqué les rudiments de la maintenance de la bête, et m'a surtout fait télécharger et installer une mise à jour du firmware, en m'expliquant qu'il avait déjà eu des gros soucis sur des machines suite à une mise à jour mal appliquée.
Le firmware pèse un petit 967MB. PARDON ? 967MB pour un flipper ?
Le poids associé à la crainte sur un éventuel soucis lors des futures mises à jour m'a donc incité à regarder le contenu de cette archive… Ceux qui souhaitent jouer également peuvent la télécharger sur le site de Stern Pinball.

0) Bonjour archive, qui es-tu ?

Le classique, quand on rencontre un nouveau fichier sans connaître son véritable format (le fichier a pour extension spk), c'est de demander à la libmagic et son utilitaire file…

$ file ghostbusters-1_13.spk 
ghostbusters-1_13.spk: data

Ha. Allons bon…

$ strings ghostbusters-1_13.spk | head -n 10
SPKS
SPK0|e
SIDX
spike
STRS
etc/init.d/game
etc/init.d/game_monitor
etc/init.d/update
etc/fstab
usr/local/spike/kernel.sha1

Mais qu'est-ce-donc que ceci ? Je ne connais aucun format d'archive répondant à ce format, et mes recherches sur internet n'ont rien trouvé. On va continuer avec strings quelques instants… tentons la chance.

$ strings ghostbusters-1_13.spk | grep vermagic
vermagic=2.6.30 mod_unload ARMv5 

Ha ben voilà, on a un noyau Linux dans le flipper ! Ça c'est une bonne surprise, j'ai maintenant un Linux de plus à la maison… Et autre bonne surprise, ce n'est pas un format compressé vu que la chaîne est encore en clair.

Mais ça ne m'explique pas ce qu'est ce format d'archive, car si le noyau est bien un Linux, alors ce n'est pas une archive disque qui elle serait reconnue par file. Étrange, étrange.

1) Éditeur hexadécimal, mon ami !

Pour la suite des opérations, j'utilise l'excellent okteta et sa très pratique table de conversion. Le but sera de comprendre le format du fichier pour pouvoir ensuite l'extraire.
À l'ouverture, on remarque donc en premier élément SPKS, suivi d'un ensemble de 64 bits avant la chaîne SPK0.
Que peuvent contenir ces 64 bits, qui sont : 0xEFBB693C 0x02000000… Après un peu de creusage de méninges, en little endian, les 32 premiers bits correspondent à l'offset pour atteindre la fin du fichier… donc la taille de la section qui suit ? Et 2 pourrait être un nombre d'éléments ou un flag quelconque ?
Laissons ce 2 pour plus tard, et continuons.

SPK0 est suivi à son tour d'un entier de 32 bits puis d'un mot «SIDX»… Il semblerait que ce format utilise des en-têtes ASCII de 4 lettres pour chacune des sections, ce qui va beaucoup nous aider.
L'entier de 32 bits est à son tour… un offset, qui nous mène à un nouveau «SPK0» dans le fichier. Et ce SPK0 est suivi d'un offset qui nous mène à la fin du fichier…

Donc le 2 dans SPKS indiquait un nombre d'éléments, des «sous-archives» probablement… Tout de suite après se trouve un SIDX, suivi par un entier de 32 bits indiquant sa taille…

Et on peut itérer ainsi… Le bloc SPKS contient N SPK0 qui contiennent successivement un SIDX, puis des STRS contenant la liste des noms de fichier, puis une liste de FINF, autant qu'il y a de strings, puis une section SDAT qui ne contient par contre pas d'information de taille (elle commence par un 0 sur 32 bits) mais juste les fichiers concaténés les uns aux autres… zut, la structure change sur cette partie, probablement pour simplifier la construction du fichier.

Vu les noms, on peut se douter que FINF contient les informations sur les fichiers. Le premier fichier est etc/init.d/game, et le second etc/init.d/game_monitor… Lisons donc les données, ces fichiers commenceront probablement par un shebang.

SDAT est suivi, comme je l'ai dit, de 4 octets à 0, puis… d'un #!/bin/sh, commençant à l'adresse 0x6E0. Bingo. Il n'y a plus qu'à localiser le suivant… Il se situe à l'adresse 0x1F83. Donc le fichier pèse 6307 octets. Et quand on cherche 6307 dans le premier bloc FINF, on le trouve en troisième position. Donc un FINF contient 32 bits pour indiquer la taille du FINF, 32 bits de on sait pas quoi (ils sont à 0), puis la taille du fichier.

Nous voilà désormais équipés pour écrire le script python qui décompressera l'archive.

2) Le script Python

Quick and Dirty devrait être une variante officielle de Python.

#!/usr/bin/env python3

import os
import os.path
import struct
import sys

fh = open(sys.argv[1], "rb")

hdr_magic = fh.read(4)
assert(hdr_magic == b'SPKS')


whole_size, part_count = (struct.unpack('<II', fh.read(8)))
print("File size is %s, for %s parts" % (whole_size, part_count))


for part in range(part_count):
        files = []
        print("Working on part %s" % part)
        hdr = fh.read(4)
        assert(hdr == b'SPK0')
        # Following the header is the part size
        (whole_size,) = struct.unpack('<I', fh.read(4))
        header_start = fh.tell()
        assert(fh.read(4) == b'SIDX')
        (idx_size,) = struct.unpack('<I', fh.read(4))
        fh.read(48)
        assert(fh.read(4) == b'STRS')
        (strs_size,) = struct.unpack('<I', fh.read(4))
        strings = fh.read(strs_size).split(b'\0')

        # Now the file infos
        hdr = b''
        i = 0
        while hdr != b'FEND':
                hdr = fh.read(4)
                (size,) = struct.unpack('<I', fh.read(4))
                if size > 0:
                        hdr_data = fh.read(size)
                        (file_offset, file_size) = struct.unpack('<II', hdr_data[:8])
                        files.append((strings[i], file_size))
                        i += 1

        # Now we should have reached SDAT
        assert(fh.read(4) == b'SDAT')
        fh.read(4)      # Skip 4 \0

        header_end = fh.tell()
        i = 0
        for (filename, file_size) in files:
                print("Extracting %s..." % (filename))
                os.makedirs(os.path.dirname(filename), exist_ok=True)
                tgt = open(filename, "wb")
                tgt.write(fh.read(file_size))
                tgt.close()
                i += 1


assert(fh.read(4) == b'SEND')

Et on peut lancer le script, dans toute sa splendeur…

$ python3 spk.py ghostbusters-1_13.spk 
File size is 1013562351, for 2 parts
Working on part 0
Extracting b'etc/init.d/game'...
Extracting b'etc/init.d/game_monitor'...
Extracting b'etc/init.d/update'...

C'est-y pas beau ?

3) Récursion infinie…

Dans les fichiers extraits, nous avons un fichier binaire ARM usr/local/bin/spk… SPK ? C'est trop tentant…
Mais ma machine est hélas en amd64, pas en ARM.
QEMU à la rescousse les amis, dans son mode le moins connu, le mode «user», qui permet de lancer un binaire Linux d'une architecture X sur un Linux d'une architecture Y…

$ qemu-arm ./usr/local/bin/spk 
Usage: spk command [ command_opts ]
       spk install [-s] [-f] PACKAGE_FILENAME
       spk list PACKAGE_FILENAME

Hoooo que c'est beau… Nous avons là le programme pour extraire l'archive, c'est magique ! Il suffisait donc d'extraire l'archive pour pouvoir l'extraire…

Et la commande list fonctionne bien pour lister le contenu de l'archive. Et excellente nouvelle, des sha1sum et des md5 (on sait jamais, pour les collisions ?) sont donnés pour chaque fichier, ce qui devrait un peu protéger les mises à jour…

Par contre, le contenu est beaucoup moins intéressant pour de futurs dépannages du flipper en cas d'incident plus poussé : tout est dans un fichier image.bin de 973638KB, à un format inconnu… jusqu'à un prochain journal, si j'en ai le temps…

PS: Alors, j'ai fait cette recherche…

$ strings ghostbusters/game | grep -i openssl
SHA1 part of OpenSSL 1.0.2 22 Jan 2015

Je veux pas critiquer, mais il y a des outils de communication, propriétaires, qui utilisent encore aujourd'hui des OpenSSL plus vieux qu'un flipper… la honte…

  • # Noms ou Listes >?>

    Posté par  . Évalué à 5.

    Je veux pas critiquer, mais il y a des outils de communication, propriétaires, qui utilisent encore aujourd'hui des OpenSSL plus vieux qu'un flipper… la honte…

    Tu en connais ou tu sais ou on peut trouver une liste fiable , histoire de savoir quoi et pourquoi eviter certain de ces outils

    En tous cas merci pour ce journal très rafraichissant - Du Linux dans du flipper - j'imaginais bien que Tux est partout mais là ca me surprends en bien .:)

    • [^] # Re: Noms ou Listes >?>

      Posté par  . Évalué à 10.

      J'ai oublié de le préciser : le flipper n'est pas connecté à internet, y'a pas de prise réseau… Donc même si ils avaient utilisé OpenSSL 0.9 j'aurais pas critiqué.
      Et donc l'outil de communication, imposé dans ma boite hélas, c'est Zoom…

  • # Super interéssant

    Posté par  . Évalué à 8.

    Très interréssant et gg pour l'extraction

    Au cas où et parceque j'aime bien extraire aussi des firmwares je te conseillerai binwalk et photorec (quand c'est ni chiffré ni compressé).

    Ceci dit je comprends mal la raison qui pourrait pousser un flip à se mettre à jour (?). C'est pas connecté au net quand même ?

    • [^] # Re: Super interéssant

      Posté par  . Évalué à 10.

      Alors, dans les changelogs, y'a :
      1) correction de bugs ou de fautes sur les messages (et il en reste… ils doivent pas avoir de correcteur sur leur éditeur)
      2) ajout de nouvelles fonctionnalités…

      Cf. http://f0727d06e1d39b514478-a613b39d02a32124478c749a23d19af0.r44.cf5.rackcdn.com/ghostbusters-1_13-README.txt

      On n'est pas sur un flipper mécanique, y'a un gros écran de DEL avec au moins un endroit où tu déclenches des jeux interactifs, sans bille, à contrôler au flipper

      • [^] # Re: Super interéssant

        Posté par  . Évalué à 10.

        Je suppose que c'est un de ces engins :
        Titre de l'image

        Belle acquisition, c'est marrant de voir du Nux sur ce genre de machines, j'aurai jamais cru (je pensais même pas qu'il éxistait des mises à jour de firmware pour les flippers, c'est dire…).

        J'ai hâte de voir si tu arrives à décompiler le image.bin, on pourrait imaginer changer les règles du flipper !

        La majeure partie des morts l'était déjà de son vivant et le jour venu, ils n'ont pas senti la différence.

        • [^] # Re: Super interéssant

          Posté par  . Évalué à 6.

          J'ai hâte de voir si tu arrives à décompiler le image.bin, on pourrait imaginer changer les règles du flipper !

          Je pense pas que les règles seront dans le image.bin hélas, à mon avis ça sera plutôt dans l'exécutable game à côté… Ça m'étonnerait qu'ils utilisent un système de script pour le jeu… Mais c'est à suivre…

          Et sinon c'est le modèle de gauche sur la photo, le «pro».

    • [^] # Re: Super interéssant

      Posté par  (site web personnel) . Évalué à 6.

      • [^] # Re: Super interéssant

        Posté par  . Évalué à -2. Dernière modification le 29 mai 2017 à 03:02.

        # aptitude

        Sélection du paquet python-hachoir-core précédemment désélectionné.
        (Lecture de la base de données… 341307 fichiers et répertoires déjà installés.)
        Préparation du dépaquetage de …/python-hachoir-core_1.3.3-4_all.deb …
        Dépaquetage de python-hachoir-core (1.3.3-4) …
        Sélection du paquet python-hachoir-parser précédemment désélectionné.
        Préparation du dépaquetage de …/python-hachoir-parser_1.3.4-2_all.deb …
        Dépaquetage de python-hachoir-parser (1.3.4-2) …
        Sélection du paquet python-hachoir-metadata précédemment désélectionné.
        Préparation du dépaquetage de …/python-hachoir-metadata_1.3.3-2_all.deb …
        Dépaquetage de python-hachoir-metadata (1.3.3-2) …
        Sélection du paquet python-hachoir-regex précédemment désélectionné.
        Préparation du dépaquetage de …/python-hachoir-regex_1.0.5-2_all.deb …
        Dépaquetage de python-hachoir-regex (1.0.5-2) …
        Sélection du paquet python-hachoir-subfile précédemment désélectionné.
        Préparation du dépaquetage de …/python-hachoir-subfile_0.5.3-3_all.deb …
        Dépaquetage de python-hachoir-subfile (0.5.3-3) …
        Sélection du paquet python-urwid précédemment désélectionné.
        Préparation du dépaquetage de …/python-urwid_1.2.1-2+b1_amd64.deb …
        Dépaquetage de python-urwid (1.2.1-2+b1) …
        Sélection du paquet python-hachoir-urwid précédemment désélectionné.
        Préparation du dépaquetage de …/python-hachoir-urwid_1.1-3_all.deb …
        Dépaquetage de python-hachoir-urwid (1.1-3) …
        Sélection du paquet python-hachoir-wx précédemment désélectionné.
        Préparation du dépaquetage de …/python-hachoir-wx_0.3-3_all.deb …
        Dépaquetage de python-hachoir-wx (0.3-3) …
        Traitement des actions différées (« triggers ») pour man-db (2.7.0.2-5) …
        Traitement des actions différées (« triggers ») pour menu (2.1.47) …
        Paramétrage de python-hachoir-core (1.3.3-4) …
        Paramétrage de python-hachoir-parser (1.3.4-2) …
        Paramétrage de python-hachoir-metadata (1.3.3-2) …
        Paramétrage de python-hachoir-regex (1.0.5-2) …
        Paramétrage de python-hachoir-subfile (0.5.3-3) …
        Paramétrage de python-urwid (1.2.1-2+b1) …
        Paramétrage de python-hachoir-urwid (1.1-3) …
        Paramétrage de python-hachoir-wx (0.3-3) …
        Traitement des actions différées (« triggers ») pour menu (2.1.47) …
        Appuyez sur Entrée pour continuer.

        hachoir-metadata VirtualBox\ VMs/Nouveau\ groupe/unstable20161204/unstable20161204.webm

        Common:
        - Duration: 1 hour 2 min 6 sec
        - Producer: vpxencv1.3.0
        - MIME type: video/webm
        - Endianness: Big endian
        Video stream:
        - Image width: 1024 pixels
        - Image height: 768 pixels
        - Compression: V_VP8

        hachoir-urwid --debug VirtualBox\ VMs/Nouveau\ groupe/unstable20161204/unstable20161204.vdi

        [warn] [/file[0]/padding] padding contents doesn't look normal (invalid pattern at byte 1)!

        Traceback (most recent call last):
        File "/usr/lib/python2.7/dist-packages/hachoir_parser/guess.py", line 87, in parse
        parser_obj = parser(stream, validate=self.validate)
        File "/usr/lib/python2.7/dist-packages/hachoir_parser/parser.py", line 153, in init
        HachoirParser.init(self, stream, **args)
        File "/usr/lib/python2.7/dist-packages/hachoir_parser/parser.py", line 43, in init
        raise ValidateError(res or _("no reason given"))
        ValidateError: Invalid signature

        Unable to parse file: VirtualBox VMs/Nouveau groupe/unstable20161204/unstable20161204.vdi

        Mais au deuxième fichier pris au hasard, il semble qu'il me faille creuser un peu plus avant d'utiliser de tels outils…

        Bon, rien à voir avec cet article d'anthologie.

  • # Ca fait plaisir

    Posté par  . Évalué à 10.

    Entre les X journaux reprenant avec quelques modifications, ce qu'on trouve déjà ailleurs (but in English), j'ai pris grand plaisir à lire ton journal.
    Retour aux sources, Pas de blabla inutile, Un PB, une solution …..on met les mains dans le cambouis et on avance pas à pas …..

    A force d'entendre que DLFP c'était mieux avant …. …lire les péripéties d'un bidouilleur qui plutôt que d'attendre qu'on lui trouve une réponse à sa question se casse la tête pour trouver lui même une solution…. rempli mon cœur de joie

    Merci Pinaraf

    • [^] # Re: Ca fait plaisir

      Posté par  . Évalué à 8.

      Je me joins aux louanges. Merci.
      Sinon, près d'un giga, flipper électronique ou pas ça fait un peu beaucoup non ? Ça ne sentirait pas une intégration d'une distrib' Linux à la va vite ? Ça m'étonnerait que tout le giga soit entièrement exploité… simple interrogation.

      • [^] # Re: Ca fait plaisir

        Posté par  . Évalué à 10. Dernière modification le 28 mai 2017 à 17:42.

        Alors dans l'archive d'un giga, il n'y a pas tout le système (!!). Il n'y a pas le shell par exemple, donc les scripts ne risquent pas de marcher.

        Tout est sur une carte SD, mais où était le fun de prendre la carte SD pour la brancher sur un PC plutôt que d'analyser le contenu de l'archive ? :) (plus sérieusement, j'ai préféré jouer avec le flipper que l'ouvrir pour le moment, et en plus comme ça vous pouvez «reproduire»)

        Sinon quelques chaines intéressantes extraites de la mise à jour :
        - GCC: (Sourcery CodeBench Lite 2014.05-29) 4.8.3 20140320 (prerelease)
        - /scratch/maciej/arm-linux-2014.05-rel/obj/glibc-2014.05-29-arm-none-linux-gnueabi-i686-pc-linux-gnu/default/csu/abi-note.o

        Ça semble correspondre à un usage de ça : https://sourcery.mentor.com/GNUToolchain/release2795
        Ils auraient sûrement à gagner à utiliser buildroot, j'enverrai peut-être mon CV pour leur proposer de l'aide :)

        • [^] # Re: Ca fait plaisir

          Posté par  . Évalué à 10.

          Et j'oublie le principal dans la question… dans le giga, 99% des données c'est vraiment les données du jeu (image.bin). Je n'ai pas encore analysé, mais il y a probablement :
          - toutes les vidéos en 4/16 couleurs sans aucune forme de compression
          - tous les sons, sans aucune compression
          - toutes les polices, sans aucune compression (je me répète ?)

          J'ai parcouru un peu le fichier à l'éditeur hexadécimal et ça ressemble vraiment à du stockage sous cette forme (on voit des formes se dessiner en de-zoomant)
          Mais sans aucune forme de compression, une vidéo de 128x32 pixels, en disons 4 couleurs, 30 FPS… déjà 240 kB par seconde. Juste quand tu gagnes une extra-ball, t'as une vidéo de ~5 secondes, donc l'extra coûte 1200 kB. Et des animations sur cet écran, il y en a beaucoup beaucoup.
          Et si on ajoute à ça les fichiers sonores, on part sur du volumineux, nécessairement.

          Après si ça se trouve, les sons sont en MP3 dégueu, les images compressées à mort, les polices dans d'autres fichiers… mais j'y crois pas :)

          • [^] # Re: Ca fait plaisir

            Posté par  . Évalué à 1.

            Merci pour tes réponses :)
            Tu dois avoir raison, il doit avoir des données "multimédias" non compressées, je n'y avais pas pensé, l'explication est plus raisonnable, ce sont quand même de beaux bijoux.

    • [^] # Re: Ca fait plaisir

      Posté par  (site web personnel) . Évalué à 2.

      A force d'entendre que DLFP c'était mieux avant

      Moui sur DLFP on entend surtout "c'était mieux à vent" ;-)

      Merci Pinaraf

      +1

      kentoc'h mervel eget bezan saotred

      • [^] # Re: Ca fait plaisir

        Posté par  . Évalué à 2.

        L'une des formulations est probablement due à un bug du logiciel de synthèse bouchotte. Il faudrait faire un rapport de bug je pense.

  • # emulateur de flipper

    Posté par  . Évalué à 9.

    Peut etre pourrais-tu glaner quelques info sur le format BIN du coté de PinMame : un émulateur de flipper, qui permet d'executer les ROM des différents fabricants.

    • [^] # Re: emulateur de flipper

      Posté par  . Évalué à 9.

      Merci, je regarderai.

      Pour le moment, je prévoyais de m'amuser avec mon éditeur hexadécimal…

      DLFP

      J'ai joué avec les contrastes pour que ça soit lisible… Sur la toute droite c'est l'éditeur hexadécimal qui affiche les offset, au milieu les valeurs en hexa et à droite décodées en ascii…

      • [^] # Re: emulateur de flipper

        Posté par  (site web personnel) . Évalué à 1.

        Ben, je pense que to fichier data, c'est juste les images/animations!
        On arrive a lire le texte en plissant les yeux.

        • [^] # Re: emulateur de flipper

          Posté par  . Évalué à 2.

          Tout à fait, la capture d'écran me paraissait assez claire : on y voit en hexadecimal le logo de Stern Pinball.
          Cf un futur journal pour les détails sur ce fichier :)

  • # C'est pas la taille qui compte mais comment on s'en sert...

    Posté par  (Mastodon) . Évalué à 3.

    967MB ça fait quand même beaucoup quand on compare aux roms de Devil Crush / Devil Crash qui font respectivement 205kB (pcengine) et 385kB (megadrive).

  • # Porté en logiciel?

    Posté par  . Évalué à 4.

    Il n'y a plus qu'à faire une table avec ces données pour le seul flipper libre que je connaisse sous Linux : pinball.

    Sinon, ça utilise peut-être ce moteur libre?

    ⚓ À g'Auch TOUTE! http://afdgauch.online.fr

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.