mercredi 19 mai 2010

od et table de partitions

Nous désirons afficher le contenu de la table de partitions qui se trouve à l'adresse 446 du secteur 0 (MBR) d'un disque
Pour ce faire, utilisons la commande od avec les options
- j 446 pour sauter (jump) 446 octets
- N 64 pour limiter l'ouput à 64 octets
puisque nous savons que cette table contient 4 lignes de 16 octets chacune:
 # od -j 446 -N 64 /dev/sdb
0000676 000400 000001 177007 177777 000077 000000 007503 002152
0000716 177000 177777 177005 177777 111016 002154 023762 002436
0000736 177200 177777 177203 177777 007602 002152 032311 000002
0000756 177000 177777 177203 177777 042113 002154 037301 000000
0000776
L'output n'est pas celui souhaité. Les lignes de la table de partitions se présentent sous forme de 8 chiffres codés sur 2 octets, chaque ligne étant précédée de son adresse. On voit que les adresses varient par incrément de 20. Or 20 en octal vaut 16 en décimal. Les adresses seraient elles en octal?
Vérifions:
# echo $((6*64+7*8+6))
446
  
Bingo! Et bien oui: od signifie octal display. Mais pas de panique, si l'affichage s'effectue en octal par défaut, on peut changer cela grâce à de multiples options:
-Ad pour avoir les adresses en décimal
-tx1 pour afficher le contenu sous forme de nombre hexadécimal codé sur un octet.
# od -j 446 -N 66 -Ad -tx1 /dev/sdb
0000446 00 01 01 00 07 fe ff ff 3f 00 00 00 43 0f 6a 04
0000462 00 fe ff ff 05 fe ff ff 0e 92 6c 04 f2 27 1e 05
0000478 80 fe ff ff 83 fe ff ff 82 0f 6a 04 c9 34 02 00
0000494 00 fe ff ff 83 fe ff ff 4b 44 6c 04 c1 3e 00 00
0000510 55 aa
0000512

Nous avons cette fois demandé la lecture de 66 octets afin que s'affiche le nombre magique 55aa (à l'adresse 510), sorte de signature qui indique la fin du secteur d'amorçage.
La colonne  1 de la table (en vert) contient uniquement des 00 sauf si la partition est marquée comme
étant amorçable: dans ce cas elle contient 80
La colonne 5 (en jaune) donne le type de partition:

0b = FAT 32
07 = NTFS
05 = Étendue
83 = Linux
Les 4 colonnes en turquoise correspondent à l'adresse (en secteurs) de la partition, les 4 colonnes en saumon sa taille (en secteurs toujours).
Comme la première partition commence normalement au secteur 63 (3f en hexadécimal), on constate que les adresses des partitions se présentent sous forme d'un nombre hexadécimal codé sur 4 octets certes,
mais au format little-endian, c'est-à-dire avec l'octet de poids le plus faible à gauche. Il en est logiquement de même pour les tailles.
Calculons la taille de la première partition, c'est-à-dire le nombre décimal correspondant à 43 0f 6a 04:
$ echo $(((4*16+3)+(0*16+15)*256+(6*16+10)*65536+(0*16+4)*16777216))
74059587
L'opération est assez fastidieuse, mais il suffit de réécrire le nombre hexadécimal avec en premier l'octet de poids le plus fort pour arriver aisément au résultat:
$ echo $((0x046a0f43))
74059587
Cependant la table de partition comprend encore 6 nombres de ce type pouvant être calculés.
Pourquoi ne pas demander à od d'effectuer cela pour nous!
Il suffit de remplacer l'option -tx1 par l'option -tu4 qui affiche le contenu sous forme d'un nombre décimal non s
igné, codé sur 4 octets:
# od -j 446 -N 64 -Ad -tu4 /dev/sdb
0000446      65792 4294966791       63 74059587
0000462 4294966784 4294966789 74224142 85862386
0000478 4294966912 4294966915 74059650   144585
0000494 4294966784 4294966915 74204235    16065
Oui, od est vraiment une petite merveille.

Par curiosité regardons ce qui se trouve à l'adresse 446 du VBR (Volume Boot Record), secteur 0 de la partition 1 et ajoutons z au type x1 de manière à afficher à la suite de chaque ligne générée les caractères imprimables qui y figurent:
# od -j 446 -N 66 -Ad -tx1z /dev/sdb1
0000446 0d 0a 45 6e 74 72 65 7a 20 43 74 72 6c 2b 41 6c >..Entrez Ctrl+Al<
0000462 74 2b 53 75 70 70 72 20 70 6f 75 72 20 72 65 64 >t+Suppr pour red<
0000478 82 6d 61 72 72 65 72 0d 0a 00 0d 0a 00 00 00 00 >.marrer.........<
0000494 00 00 00 00 00 00 00 00 00 00 83 99 a8 be 00 00 >................<
0000510 55 aa >U.<
0000512
Il n'y a rien à cette adresse qui ressemble de près ou de loin à une table de partitions.
Remarquons que l'on pouvait afficher le contenu de cette région du disque en utilisant son adresse absolue, puisque la table de partitions nous donne l'adresse en secteur de la partition 1, c'est-à-dire 63.
# od -j $((63*512+446)) -N 66 -Ad -tx1z /dev/sdb
0032702 0d 0a 45 6e 74 72 65 7a 20 43 74 72 6c 2b 41 6c >..Entrez Ctrl+Al<
0032718 74 2b 53 75 70 70 72 20 70 6f 75 72 20 72 65 64 >t+Suppr pour red<
0032734 82 6d 61 72 72 65 72 0d 0a 00 0d 0a 00 00 00 00 >.marrer.........<
0032750 00 00 00 00 00 00 00 00 00 00 83 99 a8 be 00 00 >................<
0032766 55 aa >U.<
0032768
Donc les VBR ne contiennent pas de table de partitions.
Essayons ceci:
# od -j 446 -N 66 -Ad -tx1 /dev/sdb2
0000446 00 fe ff ff 83 fe ff ff 01 00 00 00 f1 fe 82 02
0000462 00 fe ff ff 05 fe ff ff f2 fe 82 02 40 9c 8a 02
0000478 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0000510 55 aa 0000512
(Le * représente une ligne identique à la précédente)
 Oops, nous venons d'écrire que les VBR ne contiennent pas de table de partitions et pourtant nous voyons ici ce qui ressemble fort à  une table de partitions avec deux lignes non triviales!
Oui mais sdb2 n'est pas une partition primaire, ma
is une partition étendue et son secteur 0 n'est pas un VBR mais un EBR (Extended Boot Record).
La première ligne de cette pseudo-table de partitions correspond à une partition de type linux  (83) qui n'apparaît pas dans la table de partitions du MBR: il s'agit d'une partition logique notée sdb5 (les partitions décrites dans le MBR sont numérotées de 1 à 4). Son adresse est relative non pas au disque mais à la partition étendue sdb2. Nous voyons, puisque l'adresse est 1, que son VBR (et oui les partitions logiques ont aussi un VBR) vient juste après le EBR de sdb2. Notons que ceci n'est pas un standard, mais correspond à la réalité du disque examiné.
Quant à la deuxième ligne, elle correspond à une pseudo-partition étendue (type 05) qui est le container de sdb6, la partition logique suivante. Nous voyons que ce container commence juste après sdb5. Le secteur 0 de ce container est aussi un EBR. Nous voudrions afficher la pseudo-table de partition qui y figure.
L'adresse relative (donc par rapport à sdb2) du container de sdb6 est:
$ echo $((0x0282fef2))
42139378
Le nombre à communiquer à od via l'option -j, est celui-là  multiplié par 512.
Alors od est peut-être une petite merveille, mais il ne fonctionnera pas pour une valeur de j aussi élevée.
C'est ici qu'on a
ppelle dd à la rescousse:
# dd skip=$((0x046c920e+0x0282fef2)) count=1 if=/dev/sdb | od -Ad -tx1 -j 446
1+0 enregistrements lus
1+0 enregistrements écrits
512 octets (512 B) copiés, 5,7541e-05 s, 8,9 MB/s
000446 00 fe ff ff 83 fe ff ff 3f 00 00 00 01 9c 8a 02
000462 00 fe ff ff 05 fe ff ff 32 9b 0d 05 c0 8c 10 00
000478 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
000510 55 aa
000512
On part de sdb et non pas de sdb2: il faut travailler avec les adresses absolues. De ce fait, dd doit sauter un nombre de blocs (taille par défaut 512 octets) donné par l'adresse de la partition étendue sdb2 à laquelle s'ajoute l'adresse relative du container de sdb6. Il lit ensuite un seul bloc (count=1). C'est ce bloc unique qui est transmis à od. L'option -N y devient donc inutile.
La première ligne de la pseudo-table de partitions du container de sdb6, décrit la partition logique sdb6 elle-même. Elle est de nouveau de type linux et son adresse par rapport à son propre container (qui est aussi l'adresse de son VBR) est de 63 secteurs. La deuxième ligne donne l'adresse relative à sdb2 (et la taille) d'un nouveau container , celui qui contient sdb7. Pour afficher la pseudo-table suivante, il suffit de remplacer dans la commande précédente l'adresse relative du container sdb6 par celle du container sdb7.
# dd skip=$((0x046c920e+0x050d9b32)) count=1 if=/dev/sdb | od -Ad -tx1 -j 446
1+0 enregistrements lus
1+0 enregistrements écrits
512 octets (512 B) copiés, 0,000309911 s, 1,7 MB/s
0000446 00 fe ff ff 82 fe ff ff 3f 00 00 00 81 8c 10 00
0000462 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0000510 55 aa
0000512
La partition décrite en ligne 1 (sdb7) est une partition de type swap (type 82), qui se trouve à l'adresse 63 de son container. Il n'y a plus de nouveau container: donc cette partition logique est la dernière de la partition étendue sdb2.
En résumé, le secteur 0 de chaque container de partition logique est un EBR qui contient une pseudo-table de partitions.
La première ligne de cette pseudo-table décrit la partition logique contenue: type, adresse par rapport à l'origine du container (le plus souvent 63) et taille. Le secteur 0 de cette partition
logique est un VBR.
La deuxième ligne si elle existe décrit le container suivant: adresse par rapport à l'origine de la partition étendue et taille.
Toutes ces données doivent être cohérentes: ainsi la taille du container vaut l'adresse relative de la part
ition logique contenue + la taille de cette partition.
Vérifions le pour sdb7:

$ echo $((0x108cc0-(0x3f+0x108c81)))
0
Les différents containers sdb5, sdb6, sdb7 ne constituent pas un système de poupées russes: il sont situés côte à côte.
Ainsi l'adresse du container de sdb7 s'obt
ient à partir de l'adresse + la taille du container de sdb6:
# echo $((0x050d9b32-(0x0282fef2+0x028a9c40)))
0
La somme de la taille de ces containers donnent la taille du super container, la partition étendue sdb2:

$ echo $((0x0282fef2+0x028a9c40+0x108cc0-0x051e27f2))
0
Il ne faut pas confondre les containers sdb5, sdb6, sdb7 et les partitions logiques proprement dites dont les tailles additionnées ne redonnent d'ailleurs pas la taille de la partition étendue.

$ echo $((0x0282fef1+0x028a9c01+0x108c81-0x051e27f2))
-127
127 correspond aux 3 EBR et à l'espace vide entre ces EBR et les VBR.
On peut représenter schématiquement la situation comme ceci (proportions non respectées):

(Les VBR font partie des partitions logiques: ils en occupent le secteur 0)