---
title: "Ce firmware n’a pas été dumpé. Il a été reconstruit à partir de ce que le processeur a lu"
locale: "fr"
url: "https://irz.fr/fr/articles/firmware-spi-read-reconstruction-fr"
markdown_url: "https://irz.fr/fr/articles/firmware-spi-read-reconstruction-fr.md"
category: "tech"
tags: ["firmware", "SPI", "Quad-SPI", "reverse engineering", "Scapy", "logic analyzer"]
published_at: "2026-09-24T19:45:00.000Z"
author: "Manon Girard"
translation: "https://irz.fr/en/articles/firmware-spi-read-reconstruction-en.md"
---

# Ce firmware n’a pas été dumpé. Il a été reconstruit à partir de ce que le processeur a lu

Matthew Alt reconstruit une image firmware en observant les lectures SPI au boot. Chaque transaction révèle une adresse et des octets, mais toute zone jamais demandée reste inconnue.

Un dump de flash a une promesse simple : lire toutes les adresses et repartir avec une copie du composant.

Matthew « wrongbaud » Alt n’avait pas cette option. Pour une analyse black-box, son équipe ne disposait que d’un exemplaire du matériel. Les interfaces de debug utiles étaient désactivées, la lecture de la flash en circuit échouait, et dessouder la puce risquait d’endommager une cible irremplaçable.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

Faute de pouvoir interroger proprement la mémoire, il écoute **le processeur le faire à sa place**.

Un analyseur logique posé sur le bus SPI capture les commandes émises au démarrage et les réponses de la flash Winbond W25Q128JV de 16 Mio. Chaque transaction de lecture contient déjà les deux pièces nécessaires à une reconstruction : une adresse demandée par le processeur et les octets renvoyés depuis cette adresse.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

L’image obtenue peut ensuite être ouverte dans Binwalk comme un firmware ordinaire.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)[5](https://binwalk.readthedocs.io/en/latest/)

Le résultat ressemble à un dump sans en avoir la garantie d’exhaustivité. Toute la méthode tient dans cet écart.

## Une transaction vaut une petite preuve

Sur le bus, il n’existe aucun gros objet « firmware.bin ». Le contrôleur sélectionne la flash, envoie une commande et une adresse, puis laisse la puce répondre.

Sur la première capture d’Alt, la ligne d’horloge tourne autour de **16 MHz**. Une autre ligne descend pour marquer le `Chip Select`. Puis apparaît `0x03`, opcode standard `READ` de la W25Q128JV.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

La transaction observée lit l’adresse zéro. Les octets qui reviennent commencent par `03 00 00 08`. Pris comme mot 32 bits little-endian, cela donne `0x08000003`.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

À cet instant, la trace électrique retrouve une adresse dans la mémoire.

Un `READ` à `0x001000` suivi de 256 octets fournit donc un fragment localisable. Ces octets peuvent reprendre leur place à `0x001000` dans l’image de 16 Mio.

Alt initialise donc son image reconstruite entièrement à `0xFF`, valeur d’un NOR flash effacé, puis remplit uniquement les offsets pour lesquels il possède une vraie lecture. Une deuxième structure marque en parallèle les octets effectivement couverts.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

> **La capture ne copie pas la flash : elle accumule des preuves adressées**
> Schéma montrant des transactions SPI adresse plus données replacées dans une image de 16 Mio avec des trous inconnus
> - READ(adresse) + réponse → fragment placé au bon offset
> - SPI CAPTURÉ
0x03 · adresse
→ octets lus
> - jaune = observé · noir = inconnu
> - Une région jamais lue par le CPU reste inconnue, même si le fichier reconstruit fait bien 16 Mio.
> Le fichier final conserve les adresses de la flash, mais sa couverture dépend strictement des transactions observées.

## Scapy sert ici à autre chose qu’au réseau

La capture brute vient de Saleae Logic. L’analyseur SPI sait déjà exporter les octets décodés en CSV, avec le début des transactions, les valeurs MOSI et MISO et leur timing.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

Il reste à transformer ces colonnes en transactions exploitables.

Alt utilise **Scapy**, outil Python connu pour construire, décoder et manipuler des paquets réseau. Mais Scapy est plus général que son usage le plus visible : sa documentation explique comment définir de nouveaux protocoles à partir de champs et de couches.[3](https://scapy.readthedocs.io/en/latest/build_dissect.html)[4](https://scapy.readthedocs.io/en/latest/)

Il crée donc une représentation d’une transaction SPI et une couche `SPIFlashCmd`. L’opcode devient un champ énuméré ; les commandes `0x03`, `0x0B` ou `0x6B` peuvent porter une adresse 24 bits ; les variantes rapides ajoutent leurs cycles dummy. La réponse de lecture devient simplement un blob de longueur connue.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

Une fois les transactions structurées, reconstruire l’image devient très mécanique :

1. lire une transaction ;
2. vérifier qu’il s’agit d’une réponse de lecture ;
3. récupérer son adresse ;
4. copier les octets reçus à cet offset ;
5. marquer cette zone comme couverte.

C’est là que Scapy paie son loyer : le code suivant manipule des **transactions logiques**, plus des colonnes CSV qui imitent encore les fils.

C’est ce qui permettra ensuite de réutiliser la même reconstruction quand le bus change physiquement de forme.

## Le premier firmware est troué

Le fichier produit par la capture SPI standard est assez bon pour que Binwalk y retrouve des structures familières : données gzip, bloc LZMA, chaînes de démarrage et initialisation DDR. Alt identifie ainsi le bootloader et l’image du noyau Linux.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

Mais le root filesystem manque.

C’est là qu’un fichier `.bin` devient trompeur. Les zones non observées ont été remplies avec `0xFF`; visuellement, elles sont identiques à de la mémoire réellement effacée.

Or « aucun octet observé ici » ne veut pas dire « la flash contient réellement 0xFF ici ».

La carte de couverture empêche de confondre les deux.

Alt envisage d’abord deux possibilités : la capture s’est arrêtée trop tôt, ou le contrôleur a changé de mode d’accès à la flash.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

La deuxième est la bonne.

## Le bus change au milieu du boot

Au début du boot, IO0 et IO1 transportent les données en SPI classique. IO2 et IO3 restent encore dans leur rôle de broches de contrôle.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

Puis elles commencent elles aussi à commuter.

En parallèle, l’horloge passe d’environ **16 MHz à 50 MHz**. Le noyau a reconfiguré le contrôleur pour utiliser le **Quad-SPI**.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

Les lectures suivantes utilisent notamment l’opcode `0x6B`, `FAST_READ_QUAD_OUT`. C’est une commande dite **1-1-4** : opcode et adresse partent encore sur une seule ligne, huit cycles dummy suivent, puis les données reviennent en parallèle sur quatre lignes.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

À fréquence plus élevée et avec quatre voies de données, Alt estime le débit de lecture environ douze fois supérieur à celui du début du boot.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

Le décodeur SPI standard de Saleae ne sait pas suivre ce mode. Si IO2 et IO3 restent ignorées, la capture perd justement les données lues lorsque le système charge son filesystem.

> **Le boot change de largeur de bus**
> Schéma comparant SPI 16 MHz sur une voie de données et Quad-SPI 50 MHz avec quatre voies lors du chargement du filesystem
> - MÊME FLASH · DEUX FAÇONS DE LA LIRE PENDANT LE BOOT
> - SPI STANDARD · ~16 MHz
> - READ 0x03
IO0 / IO1
bootloader + noyau
> - QUAD-SPI · ~50 MHz
> - FAST_READ 0x6B
IO0–IO3
rootfs + autres régions
> - Un seul décodeur ne suffit pas si le protocole physique change après le chargement du noyau.
> Le trou du premier fichier n’était pas nécessairement de la flash vide : la cible avait simplement commencé à lire sur quatre lignes.

## 67,3 % est déjà énormément

Alt refait donc la capture avec un analyseur Quad-SPI et adapte l’import à son format.

Cette fois, l’outil compte **36 839 transactions `FAST_READ_QUAD_OUT`**. Sur les 16 777 216 octets de la W25Q128JV, il replace **11 288 528 octets**, soit **67,3 %** de couverture.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

Binwalk retrouve alors un SquashFS à `0x2D0000`, d’environ 3,16 Mio, puis un JFFS2 à `0xDE0000`.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

Mais le début du fichier Quad-SPI est rempli de `0xFF`.

Encore une fois, ce n’est pas la preuve que le début de la flash est vide. La capture Quad a commencé après le changement de mode. Bootloader et noyau avaient déjà été lus en SPI simple.

Les deux captures se complètent, mais pas aux mêmes endroits :

- capture SPI simple : bonnes données du début, bootloader et noyau ;
- capture Quad-SPI : filesystem et grandes régions lues plus tard ;
- image finale : superposition des fragments à leurs offsets réels.

Alt montre un `dd` qui copie tout depuis `0x2D0000` de l’image quad vers l’image initiale ; son outil `spidump` propose aussi un mode `--merge` pour superposer plusieurs captures.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

On n’est plus vraiment dans la copie de fichier. Chaque capture documente une autre portion de l’espace d’adresses, puis les morceaux se superposent.

## Ce que le processeur ne lit pas reste invisible

La limite n’est pas un détail de l’implémentation. Elle découle directement de la méthode.

Un programmateur externe peut demander arbitrairement chaque adresse de la flash. Un analyseur logique passif ne commande rien. Il voit uniquement ce que le système choisit de lire pendant la fenêtre observée.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)[2](https://hackaday.com/2026/09/24/reconstructing-device-firmware-from-spi-reads)

Une partition de secours jamais montée peut donc rester absente. Une clé stockée dans une région que le boot ne touche pas restera inconnue. Une configuration liée à une autre fonction du produit restera absente si cette fonction n’est jamais déclenchée pendant la capture.

On peut toutefois provoquer davantage de lectures : plusieurs boots, recovery, configuration, réseau, mises à jour. Chaque scénario peut ajouter des régions à la carte sans dessouder la puce.

C’est aussi pourquoi la **carte de couverture** est aussi importante que le fichier `.bin`. Sans elle, `0xFF` mélange deux états très différents : valeur réellement observée et donnée absente remplacée par une valeur par défaut.

## Observer avant d’extraire

La technique ne bat pas un vrai dump dans tous les cas.

Si la puce peut être lue proprement avec un programmateur, un dump exhaustif est plus direct. Le sniffing ajoute un analyseur logique, le décodage du protocole, des questions de signal integrity, plusieurs captures et éventuellement plusieurs modes de bus.[1](https://voidstarsec.com/blog/scapy-spi-reconstruction)

Mais il possède une propriété précieuse : **il est passif du point de vue du protocole**. Il n’a pas besoin de convaincre la flash de parler à un second maître pendant qu’elle reste soudée sur la carte. Il observe la conversation qui fonctionne déjà entre le processeur et sa mémoire.

Avec un exemplaire unique ou une carte illisible en circuit, ce caractère passif devient une raison sérieuse de choisir cette voie.

Elle donne en revanche une bonne règle de travail en reverse engineering matériel.

Quand l’objet ne vous laisse pas lire directement son état, observez **les décisions qu’il prend à partir de cet état**.

Ici, chaque adresse demandée au boot révèle quelques octets de plus. Pas toute la flash. Juste ce que le processeur a eu besoin de savoir.

Cela suffit déjà à reconstruire un firmware exploitable.

## References

1. [Matthew Alt — Extending Scapy for Hardware Reverse Engineering, VoidStar Security, 23 septembre 2026](https://voidstarsec.com/blog/scapy-spi-reconstruction)
2. [Hackaday — Reconstructing Device Firmware From SPI Reads, 24 septembre 2026](https://hackaday.com/2026/09/24/reconstructing-device-firmware-from-spi-reads)
3. [Scapy — Adding new protocols](https://scapy.readthedocs.io/en/latest/build_dissect.html)
4. [Scapy 2.7.1 documentation](https://scapy.readthedocs.io/en/latest/)
5. [Binwalk documentation](https://binwalk.readthedocs.io/en/latest/)
