Modifier le firmware par le OpenWrt

From Deimos.fr / Bloc Notes Informatique
Jump to: navigation, search

1 Introduction

Comme vous le savez, il y a quelque temps, j’ai tenté d’installer OpenWrt sur une Fonera+ (la grosse avec deux connecteur Ethernet). A force d’insister, j’ai fini par obtenir quelque chose de partiellement fonctionnel : recompilation du firmware Fon et mise en marche des deux ports Ethernet. Cependant, le Wifi reste toujours un problème.

Après une periode de travail sur mes Fonera Frankensteinisés, j’ai donc décidé de m’y remettre et de tenter une autre approche. Utiliser le firmware binaire Fon et activer une console série.Première étape, récupérer une mise à jour binaire pour la Fonera+ sous la forme du fichier foneraplus_1.1.1.1.fon.

Il s’agit d’une archive tar compressée gzip à laquelle Fon à ajouté une entête spécifique comme l’explique Stefan Tomanek sur son site. Ses explications pour le firmware de la Fonera classique semble parfaitement valables pour la Fonera+.

2 Modification du firmware

On joue donc de cat et de tail pour supprimer l’entête et obtenir une véritable archive :

cat foneraplus_1.1.1.1.fon  | tail -c +520 - > arch.tar.gz

Le fichier obtenu contient trois fichiers qui sont utilisés par le routeur pour se mettre lui-même à jour :

hotfix
image_full
upgrade

Le fichier image_full est construit avec l’image du kernel, le système de fichier racine et un cheksum pour le loader stage2 lancé par RedBoot. Un coup d’oeil au contenu du Makefile du BuildRoot Fon (les sources du firmware) et on comprend la procédure de construction :

  • le l’image du kernel compressé lzma est construite classiquement
  • le script Perl fonimage.pl ajoute une entête de 12 octets contenant un CRC via le module Digest::CRC
  • dd est utilisé pour obtenir un fichier multiple de 64Ko avec padding de zéro si nécessaire (bs=64k conv=sync)
  • le fichier image du kernel ne semble pas utilisée
  • fonimage.pl est également utilisé pour regouper le kernel et le système de fichiers racine en squashfs. Le script Perl et lancé pour construire l’image du système complet avec le CRC.

On en conclu donc que le fichier image_full est un entête/CRC de 12 octets suivi du kernel lzma puis de l’image squashfs du système de fichiers racine. Un coup d’oeil au script Perl nous apprend également la composition de l’entête de 12 octets :

  • 4 octets pour la taille
  • 4 octets pour le CRC
  • 4 octets pour l’emplacement du système de fichier racine

Vérifier est un jeu d’enfant, il suffit d’afficher les valeurs en utilisant fonimage.pl à la main :

./fonimage.pl daimage vmlinux.lzma root.squashfs
size   : 2295397
offset : 715783
ls -l vmlinux.lzma daimage
2295409 2007-11-18 12:20 daimage
715783 2007-09-29 20:31 vmlinux.lzma

L’offset correspond à la taille du kernel et la taille (size) correspond à celle de l’image finale moins celle de l’entête. Les valeurs numériques sont enregistrées dans l’entête dans un format spécifique appelé unsigned long in “network” (big-endian) order. Effectivement dans l’entête du fichier daimage on retrouve :

hexdump daimage | head -n 1
0000000 2300 6506 3616 ac9f 0a00 07ec 006d 8000

23006506 c’est en réalité 00230665, soit 2295397, la taille de daimage moins 12. Le fichier image_full a comme entête :

hexdump image_full | head -n 1
0000000 2100 debf 14a2 9bd3 0a00 3450 006d 8000

2100debf c’est 0021bfde, soit, 2211806. 2211806+12 égale 2211818. Mais la taille du fichier est 2293764. Ça ne correspond pas ! Retour au Makefile.

Après avoir utilisé le script fonimage.pl, le Makefile appel prepare_generic_squashfs déclaré dans image.mk, un fichier d’OpenWrt. La fonction utilise dd :

dd if=$(1) of=$(KDIR)/tmpfile.1 bs=64k conv=sync
$(call add_jffs2_mark,$(KDIR)/tmpfile.1)
dd of=$(1) if=$(KDIR)/tmpfile.1 bs=64k conv=sync
$(call add_jffs2_mark,$(1))

On retrouve le padding en blocs de 64k et un appel à add_jffs2_mark qui fait :

echo -ne '\xde\xad\xc0\xde' >> $(1)

Bref :

  • on resize l’image en blocs de 64k
  • on ajout 0xdeadc0de (Dead Code ???)
  • on resize encore
  • on ajoute encore 0xdeadc0de

Vérifions avec un BuildRoot de chez Fon compilé par nos soins :

cd bin
find .. -name *.lzma
../build_mips/linux-2.6-fonera/vmlinux.lzma
find .. -name root.squashfs
../build_mips/linux-2.6-fonera/root.squashfs
../target/linux/fonera-2.6/image/fonimage.pl \
daimage \
../build_mips/linux-2.6-fonera/vmlinux.lzma \
../build_mips/linux-2.6-fonera/root.squashfs
dd if=daimage of=temp1 bs=64k conv=sync
35+1 enregistrements lus
36+0 enregistrements écrits
2359296 octets (2.4 MB) copiés, 0.00566277 seconde, 417 MB/s
echo -ne '\xde\xad\xc0\xde' >> temp1
dd if=temp1 of=daimage1 bs=64k conv=sync
36+1 enregistrements lus
37+0 enregistrements écrits
2424832 octets (2.4 MB) copiés, 0.00597735 seconde, 406 MB/s
echo -ne '\xde\xad\xc0\xde' >> daimage1
ls -l daimage1 openwrt-fonera-2.6.image
2424836 2007-11-18 13:37 daimage1
2424836 2007-09-29 20:31 openwrt-fonera-2.6.image
md5sum daimage1 openwrt-fonera-2.6.image
b57f8b2279bdb9a6483e094c58fc3381  daimage1
b57f8b2279bdb9a6483e094c58fc3381  openwrt-fonera-2.6.image

Bingo ! Nous sommes en mesure de recréer manuellement une image. Nous avons donc parfaitement analysé le processus de construction. Reste à inverser ce processus pour obtenir un fichier du noyau et une image du rootfs que nous pourrons modifier.

Fon mem map.png

Le plus simple pour être sûr est de démonter notre propre essai. On commence donc par regarder l’entête de daimage1 :

hexdump daimage1 |  head -n 1
0000000 2300 6506 3616 ac9f 0a00 07ec 006d 8000

Les valeurs qui nous intéressent ici sont :

  • la taille de l’image : 23006506 soit 00230665 soit 2295397 octets
  • la taille du kernel (l’offset pour trouver le rootfs) : 0a0007ec soit 000aec07 soit 715783

On retire l’entête :

cat daimage1 | tail -c +13 > nohead

Puis le padding en utilisant la taille de l’image spécifiée dans l’entête :

cat nohead | head -c +2295397 > nopad

Notre fichier est maintenant la concaténation du kernel et du rootfs. On utilise l’offset précisé dans l’entête pour récupérer le rootfs. taille image - offset = taille rootfs (2295397-715783=1579614):

cat nopad | tail -c 1579614 > squash

Tant qu’à faire on extrait également le kernel pour avoir tous les éléments :

cat nopad | head -c +715783 > dakern

Vérifions :

md5sum dakern ../build_mips/linux-2.6-fonera/vmlinux.lzma
0c50b77f12c3e18a91db1d027fe0ecc6  dakern
0c50b77f12c3e18a91db1d027fe0ecc6  ../build_mips/linux-2.6-fonera/vmlinux.lzma
md5sum squash ../build_mips/linux-2.6-fonera/root.squashfs
19576d7b96f07ba7694d615e2afe78d1  squash
19576d7b96f07ba7694d615e2afe78d1  ../build_mips/linux-2.6-fonera/root.squashfs

Ca marche, nous sommes en mesure de démonter une mise à jour officielle Fon. Appliquons cela à image_full :

hexdump image_full | head -n 1
0000000 2100 debf 14a2 9bd3 0a00 3450 006d 8000

Un peu de calcul :

  • taille de l’image : 2100debf soit 0021bfde soit 2211806
  • taille du kernel : 0a003450 soit 000a5034 soit 675892
  • taille du rootfs : 2211806-675892=1535914

Démontage :

cat image_full | tail -c +13 > nohead
cat nohead | head -c +2211806 > nopad
cat nopad | tail -c 1535914 > squash
cat nopad | head -c +675892 > dakern

Ridicule vérification :

hexdump dakern | head -n 1
0000000 006d 8000 ff00 ffff ffff ffff 00ff 0204

C’est bien une entête de kernel lzma.

Il nous faut maintenant démonter le squashfs. Nous nous heurtons à plusieurs problèmes dont la compression lzma et l’endian. L’image est construite pour un système big endian (MIPS) mais un PC x86 est little endian. Oubliez simplement l’idée de monter le système de fichiers en loopback. Il faut regarder du côté de l’outil unsquashfs mais là encore, beaucoup de problèmes. Après une grosse période de recherche, je suis finalement tombé sur un kit de modification de firmware : firmware_mod_tools_prebuilt.tar.gz.

Cette archive contient un unsquashfs-lzma qui fonctionne parfaitement avec les images pour la Fonera (la nouvelle comme l’ancienne) :

/tmp/unsquashfs-lzma squash
Reading a different endian SQUASHFS filesystem on squash
created 407 files
created 67 directories
created 165 symlinks
created 0 devices
created 0 fifos

Et nous retrouvons un répertoire squashfs-root contenant l’arborescence utile. Un petit coup d’oeil dans /etc/inittab et effectivement, là, il manque quelque chose que j’aurai aimé :

ttyS0::askfirst:/bin/ash --login

On l’ajoute et on reconstruit un squashfs tout neuf en utilisant les outils du BuildRoot :

/chemin/vers/openwrt/staging_dir_mips/bin/mksquashfs-lzma \
squashfs-root root.squashfs -nopad -noappend -root-owned -be
Creating big endian 3.0 filesystem on root.squashfs, block size 65536.
Big endian filesystem, data block size 65536, compressed data,
compressed metadata, compressed fragments
Filesystem size 1499.77 Kbytes (1.46 Mbytes)
31.47% of uncompressed filesystem size (4766.19 Kbytes)
Inode table size 4801 bytes (4.69 Kbytes)
23.44% of uncompressed inode table size (20479 bytes)
Directory table size 5572 bytes (5.44 Kbytes)
57.00% of uncompressed directory table size (9776 bytes)
Number of duplicate files found 4
Number of inodes 639
Number of files 407
Number of fragments 28
Number of symbolic links  165
Number of device nodes 0
Number of fifo nodes 0
Number of socket nodes 0
Number of directories 67
Number of uids 1
root (0)
Number of gids 0

3 Upload du Firmware

Nous avons un kernel et un nouveau rootfs. Nous réutilisons les commandes permettant de créer une image : fonimage.pl, dd, echo, dd, echo. Nous obtenons une nouvelle image qu’il ne reste plus qu’à flasher sur la Fonera+ via RedBoot :

fis delete image
load -r -b 0x80041000 monimage
fis create -b 0x80041000 -f 0xA8040000 -l 0x00230004 -e 0x80040400 image

Après cette opération la Fonera+ est redémarrée par la commande reset. Aucun message du noyau n’apparaît, c’est normal, mais après la phase de démarrage complète on a bien un shell utilisable. Les messages du DHCP ne cessent de pourrir la console mais ça marche !

4 Ressources

http://www.lefinnois.net/wp/index.php/2007/11/18/modification-du-firmware-fon-pour-une-fonera/
http://www.dd-wrt.com/wiki/index.php/LaFonera_Software_Flashing
http://www.dd-wrt.com/wiki/index.php/LaFonera_(fr)
http://www.cure.nom.fr/blog/archives/141-Fonera-et-le-firmware-alternatif-DD-WRT.html
http://www.dd-wrt.com/wiki/index.php/Wireless_Access_Point