Ce post est le résultat de mes recherches et de ma démarche sur l’analyse d’un routeur domestique.
TL-WR841N
Je me suis enfin décidé à acheter un routeur Wifi et de le décortiquer. J’ai opté pour un routeur sans fil TP-LINK (TL-WR841N)
C’est un routeur pas très cher (18€ sur Amazon) et il supporte OpenWRT/LEDE des OS alternatifs pour ajouter de nouvelles fonctionnalités.
Firmware
En attendant la livraison de mon nouveau jouet je me suis renseigné sur la partie logicielle de ce routeur.
J’ai donc téléchargé le firmware disponible sur le site de TP-Link. Le fichier compressé au format ZIP contient 3 fichiers :
Celui qui nous intéresse est bien évidement le fichier TL-WR841Nv14_EU_0.9.1_4.16_up_boot[180319-rel57291].bin
qu’on renommera en firmware.bin
.
Pour voir ce que l’on peut identifier dans le firmware, on peut utiliser binwalk.
Le firmware est divisé en 3 parties :
- U-Boot : le bootloader qui va démarrer en premier et charger le kernel
- Le noyau Linux : compressé au format LZMA
- SquashFS : le système de fichiers compressé sous Linux
C’est à peu près tout ce qu’il faut pour faire tourner un OS embarqué sur un routeur.
U-Boot
Pour commencer je vais extraire U-Boot du firmware grâce à l’utilitaire dd
.
Pour vérifier que ce n’est pas corrompu on va regarder les chaines de caractères avec l’utilitaire strings
:
La première chaine correspond à la version de U-Boot et sa date de compilation. Donc le fichier n’est pas corrompu. Pour ensuite le charger dans IDA Pro, il va nous falloir l’adresse de chargement du bootloader (Load address). Sur l’image ci-dessus on a deux adresses : 0x80200000
et 0x80100000
. Si on Google l’une des deux on tombe sur la doc de U-Boot :
1
2
3
4
5
6
7
8
9
10
4. The default load and start addresses of the applications are as
follows:
Load address Start address
x86 0x00040000 0x00040000
PowerPC 0x00040000 0x00040004
ARM 0x0c100000 0x0c100000
MIPS 0x80200000 0x80200000
Blackfin 0x00001000 0x00001000
NDS32 0x00300000 0x00300000
Nios II 0x02000000 0x02000000
Nous avons notre adresse : 0x80200000
. Plus qu’a rebaser le programme et le tour est joué.
Il ne reste plus qu’à le reverser. Mais comme le code de U-Boot est sous license GPL, pas besoin puisque TP-Link fourni déjà le code source.
Linux kernel
Place au kernel, tout comme U-Boot on va l’extraire avec dd
. Comme on le voit avec l’image ci-dessous le fichier LZMA est corrompu lors de l’extraction, on ne peut donc pas y extraire le kernel :(.
Heureusement, comme le bootloader, le noyau Linux est sous licence GPL et il est donc obligatoire pour TP-Link de publier les sources du noyau Linux qu’il utilisent si il y a eu modification. Ceci étant le cas on peut directement accéder aux sources du noyau Linux et tous les autres logiciels utilisé avec ce routeur.
SquashFS
Comme dit plus haut, SquashFS est un système de fichier compressé pour le noyau Linux. Il est en lecture seule et ne permet pas d’écrire dessus. Ce système est principalement utilisé pour les OS embarqués comme OpenWRT. De plus il est aussi installé sur les version Live de la plupart des distributions Linux.
On va pouvoir l’extraire du firmware comme le bootloader et le kernel.
Ci-dessus je l’utilise unsquashfs
pour décompresser le fichier. On retrouve l’arbre de fichiers du routeur.
Un coup de ls
et on peut voir que /bin/ls
(comme tous les autres exécutables des FS) est un lien symbolique vers /bin/busybox
1
2
λ ~/Desktop/fw/squashfs-root » ls -la bin/ls
lrwxrwxrwx 1 mathieu mathieu 7 May 12 22:21 bin/ls -> busybox
Busybox est considéré comme le couteau suisse des systèmes Linux embarqué car un seul binaire contient la plupart des utilitaires GNU généralement disponibles sur les distributions Linux.
De plus avec la commande file
on peut déterminer l’architecture du processeur utilisé par le routeur :
1
2
λ » file bin/busybox
bin/busybox: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
Hardware
J’ai reçu mon routeur. La première chose que j’ai faite était d’ouvrir le boitier pour voir ce qu’il y a dedans.
Connexion série
Comme attendu il y a les PINs UART pour ouvrir une connexion série sur le routeur. Il est temps de souder. J’ai soudé 3 fils sur les PINs à disposition :
- bleu : GND (j’avais pas de fil noir)
- blanc : TX
- vert : RX
A l’aide d’un convertisseur USB vers TTL on peut donc ouvrir une sortie série sur le routeur et obtenir un shell.
Voici le setup :
On peut lancer PuTTY et avec le baudrate à 115200, puis démarrer le routeur et on voit le bootlog défiler. Comme prévu U-Boot démarre puis il va charger le noyau Linux.
D’après les logs on peut voir que c’est un noyau Linux 2.6.36. On aurait aussi pu voir ça dans /proc/sys/kernel
:
1
2
/proc/sys/kernel # cat osrelease
2.6.36
Dès que le routeur a entièrement démarré, il suffit d’appuyer sur la touche entrée, et nous avons un shell. Contrairement à d’autres routeurs, celui-ci n’a pas d’identifiant ni de mot de passe.
LEDs
Ce routeur comporte 3 LEDs. Mais d’après le code source du kernel, certains routeurs TP-Links peuvent en avoir plus que 3 en fonction des fonctionnalité de ceux-ci.
Dans /proc/tplink
on peut voir à quoi ces LEDs correspondent à l’aide des fichiers suivants :
led_internet
: statut de la connexion internetled_wlan_24G
: statut de la connexion sans filled_sys
: statut du système du routeur
Il y a aussi le fichier let_status
qui permet d’activer ou désactiver les LEDs. Pour changer le comportement d’une LED il suffit d’éditer le fichier en question. Exemple : echo 0 > led_internet
.
Voici le code dans le noyau Linux qui va lire et écrire dans le fichier /proc/tplink/led_internet
pour changer le statut de la LED en question :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
static int led_internet_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
return count;
}
static int led_internet_write_proc(struct file *file, const char *buffer,
unsigned long count, void *data)
{
char val_string[16];
int val, val1;
if (count > sizeof(val_string) - 1)
return -EINVAL;
if (copy_from_user(val_string, buffer, count))
return -EFAULT;
if (sscanf(val_string, "%d %d", &val, &val1) != 2)
{
printk("usage: <action> <unused>\n");
return count;
}
wan_status = val;
if (val && ((NULL != fp_ethTestWan) && (1 == fp_ethTestWan()))) {
led_setWanOn(1);
} else {
if ((NULL != fp_ethTestWan) && (1 == fp_ethTestWan())) {
led_setWanOff(1);
} else {
led_setWanOff(0);
}
}
return count;
}
C’est à peu près le même code pour les autres LEDs.
C’est tout pour le moment. Pour la suite j’aimerais mettre en place l’environnement de développement pour créer un nouveau firmware pour cet appareil et tester dans QEMU et pourquoi pas trouver et exploiter quelques bugs à distance. Les routeurs domestiques sont bourrés de bugs et les applicatifs ne sont pas maintenu très longtemps, il est donc probable qu’on puisse y trouver quelque chose d’interessant.
Si vous avez besoin d’infos ou souhaitez corriger des erreurs, vous pouvez me ping sur Twitter : @matteyeux
Github : matteyeux
Sources :