Bootstrapper 2: Partitietabellenblues

Hoewel het in theorie niet voor zou mogen komen, is het in de praktijk soms zo dat we met een verminkte partitietabel zitten. Dat klinkt desastreus, want het is immers het initiƫle aanknopingspunt voor de hele diskopbouw. Toch blijkt het mee te vallen; want hoewel kennisintensief, blijkt heel veel in zo'n geval nog te redden.
| posted on Thu, 01 Apr 2004, 00:00 | weblog | rss | spin-off | comments |
Translate to English Translate to English

Onlangs installeerde ik Windows Server 2003 op een multi-boot systeem, naast twee al aanwezige Linux systemen. Of daarmee de oorlog werd verklaard aan Linux weet ik niet, maar na afloop van de installatie miste ik drie partities aan het eind van mijn schijf. Het leek wel alsof een virus te keer was gegaan! Dat zijn de momenten dat de angst je om het hart slaat...

Strikt genomen is er geen probleem natuurlijk -- alle data staat in zo'n geval nog op de schijf, er is 'alleen' een truc nodig om de partitietabellen er weer heen te laten wijzen. Maar dat is nog een hele klus!

Deze klus bleek het beste te klaren onder Linux, omdat daaronder meer filesystemen worden herkend dan onder Windows. Bovendien is het onder Linux mogelijk om zo diep te graven als nodig, terwijl dat onder Windows vaak moeilijker gaat.

De gebarsten Linux systemen bootten nog half dankzij een bewust klein gehouden root partitie, namelijk zonder /usr en /home erop, maar als zelfs dat niet was gestart hadden we gewoon Knoppix kunnen gebruiken, een methode om Linux vanaf CD te draaien. De handigste manier om Knoppix te starten is dan op de commandline, met als bootcommando

knoppix single

Opbouw van de partitietabel

We gaan in dit verhaal uit van de gemiddelde PC. Partitietabellen op zulke systemen zijn een typisch allegaartje van de aannames die men in de loop der jaren heeft gemaakt over de maximale afmetingen van harde schijven.

Zo is er oorspronkelijk aangenomen dat 4 partities voldoende waren voor iedereen. Overigens was dit met goede redenen -- een partitietabel met 4 entries heeft voorspelbare afmetingen en kan daardoor met simpele bootstrapcode worden ingelezen. De tabel staat in de master boot record (MBR) ofwel het eerste diskblok van 512 bytes.

In de opbouw van de MBR zijn de eerste 446 bytes bedoeld voor een bootstrap-programma, daarna volgt een tabel van 4 partities in 16 bytes elk, en tenslotte volgt een statische 'signature' van het bootblok, namelijk de bytes 0x55 en 0xaa.

Tegenwoordig zijn we niet tevreden met een maximum van 4 partities, bijvoorbeeld omdat systemen als Linux het belang van scheiding van partities duidelijk maken -- het is onder Linux handig om een swap partitie te hebben, een door weinig verandering stabiele root, een groot softwarebrok /usr, een tussen distributies deelbare /home en een BIOS-bereikbare /boot. En dan wil je misschien ook nog wel dual-booten!

Er zijn daarom 'extended partities' bedacht, waarvan je er zo veel kunt hebben als je wilt. Ter onderscheid heten de partities in de MBR nu 'primaire' partities. Extended partities voegen een klein stukje partitietabel toe vooraan elk van de partities. Dat stukje tabel kan een partitie bevatten, in hetzelfde formaat van 16 bytes als in de MBR, en tevens kan er een verwijzing in staan naar de volgende extended partitie op de schijf. De eerste extended partitie wordt aangewezen vanuit de laatste van de vier partities in de tabel in de MBR.

Primair herstel

Wie een backup van de MBR maakt, redt daarmee dus alleen de primaire partitietabel maar niet de extended partities. Maar gezien de centrale lokatie van de MBR, in tegenstelling tot de gedistribueerde opzet van extended partities, is het van behoorlijk veel belang om de MBR te kunnen herstellen.

Een commando om de 446 bytes aan bootcode in de MBR te overschrijven onder DOS of Windows is

fdisk /mbr

Dit commando overschrijft de partitietabel niet, en is dus vooral geschikt om bootloaders en virussen de nek om te draaien. Na dit commando staat in de MBR weer de gewone bootcode. Onder Linux is iets soortgelijks te bewerkstelligen met

dd if=/dev/zero of=/dev/hda bs=446 count=1

Hierin staat /dev/hda voor de eerste IDE schijf, waarin 1 blok ter grootte van 446 bytes wordt overschreven met nulbytes. Door 446 te veranderen in 512 wordt de partitietabel ook overschreven, maar doorgaans is dat niet wenselijk.

Wie LILO gebruikt als bootloader, kan een backup vinden van de 512 bytes lange MBR, in de file /boot/boot.0300 -- en die is dus al aanwezig zonder dat je bewust een backup had gemaakt. Hij kan dienen om de partitietabel te herstellen:

dd if=/boot/boot.0300 of=/dev/hda bs=512 count=1

Het omgekeerde, dus een backup maken, kunnen we handmatig doen door de argumenten if en of te verwisselen. Vaak is het daarbij handig om /boot/boot.0300 te vervangen door iets als /root/partities. Door parameters seek en/of skip is het met het dd-commando ook mogelijk om extended partitietabeldelen veilig te stellen, maar het berekenen van de offsets voor die blokken kan eigenlijk beter aan programmatuur worden overgelaten.

Beschrijving van een partitie

Zoals gezegd wordt een partitie volledig beschreven in 16 bytes. Die kunnen we uit een naar file weggeschreven versie van de partitietabel bekijken met een Linux commando als

hexdump -s 446 -C /root/partities

We gebruiken hier overigens -C om een byte-voor-byte dump te krijgen; het gewone hexdump levert op een PC verwarrende dumps doordat de bytes per twee worden verwisseld wanneer ze als 16-bit words worden gehexdumpt. De uitkomst van dit commando is een dump van alle bytes van 446 tot 512. De laatste twee bytes zijn de signature, dus 0x55 en 0xaa, en als dat zo is dan bevatten de voorgaande vier regels de vier primaire partities.

De 16 bytes betekenen het volgende, met een concreet voorbeeld erbij:

0x00 Niet aktief (aktief is 0x80)
0x00 0x01 0x65 Start head/sector/cylinder
0x0b Type inhoud van partitie
0xfe 0xbf 0xde Eind head/sector/cylinder
0x0018c225 LBA-nummer startsector
0x009b69fa Aantal sectoren

Het type inhoud van de partitie (bijvoorbeeld vfat of ntfs voor een Windows filesysteem, of swap of ext2 voor Linux) is onder Linux fdisk in te stellen met het T commando, en een tabel met de mogelijke waarden is op te vragen met het L commando. Speciaal is de waarde 0x00 die aangeeft dat dit geen partitie is.

De notatie met head/sector/cylinder heeft als nadeel dat het wat snel tegen hedendaagse grenzen aanloopt -- er zit al snel een getal bij dat de 255 overschrijdt. De LBA-methode werkt met 32-bits getallen waarin sectoren gewoon opeenvolgend genummerd worden, en waarmee dus tot 2^32 sectoren van 512 bytes aan te spreken zijn -- in principe goed voor harde schijven tot 2 TB ofwel 2048 GB. Merk op dat de laatste twee getallen in omgekeerde bytevolgorde genoemd staan in de hexdump van de tabel. Dit is namelijk de volgorde waarin een 386 processor longwords opslaat.

Het formaat voor extended partities, dus eigenlijk verwijzingen naar verdere brokjes partitietabel, wijkt enigszins af van bovenstaande. We geven wederom een concreet voorbeeld:

0x00 Niet aktief (aktief is 0x80)
0x00 0x81 0xdf Start head/sector/cylinder
0x0f Type van extended partitie
0xfe 0xff 0xff Eind head/sector/cylinder
0x00b42c1f LBA-nummer startsector
0x03f4e324 Aantal sectoren

Het herkennen van dit partitierecord als extended gaat aan de hand van het type van de partitie -- wanneer daar 0x05, 0x0f of 0x85 staat beschrijft de rest een extended partitie in plaats van een daadwerkelijke datapartitie. Voor normale datapartities wijst de startsector de bootsector van die partitie aan; bij extended partities wijst dit naar de sector die de extended partitie beschrijft, plus de eventuele verwijzing naar de volgende extended partitie.

De extended partitie-informatie staat ook weer aan het eind van de startsector van de extended partitie. Deze gehele sector kunnen we ophalen door eerst de omrekening van hex naar decimaal te doen met bijvoorbeeld

python -c 'print 0x00b42c1f'
11807775

Daarna is deze sector op te halen met

dd if=/dev/hda skip=11807775 of=/root/extpart bs=512 count=1

De hexdump van dit blok toont wederom de signature bytes 0x55 en 0xaa aan het eind, en daarvoor een of twee records uit een partitietabel -- doorgaans een datablok en een link naar de volgende sector uit de extended partitietabel, overigens wel relatief ten opzichte van de eerste extended partitietabel.

Er zijn programma's die dit soort gegevens veilig stellen en terugplaatsen. Een Google-search naar "partition table backup" levert een aardige waslijst van onmiddelijk downloadbare programma's. Let altijd even op of zulke programma's in staat zijn om ook extended partities veilig te stellen, anders ben je nog maar halverwege. Denk eraan de backup altijd ook op een andere schijf (bijvoorbeeld een floppy) op te slaan. En geef de voorkeur aan utilities voor DOS en Linux boven die voor Windows. Het bootstrappen van Windows is knap lastig, terwijl DOS van floppy boot en Linux van floppy of CD.

Vaak kunnen dit soort programma's meer doen dan alleen backup en recovery van partitietabellen. Verdergaande functionaliteit heeft vaak kennis van de filesystemen in de partities nodig, en dan wordt het belangrijk in de gaten te houden wat er ondersteund wordt. Voor Windows is het van belang dat niet alleen fat, maar ook ntfs ondersteund wordt, voor Linux wil je naast ext2 ook ondersteuning voor de journaliing filesystemen ext3 en/of reiser -- in Linux swap heb je doorgaans minder interesse omdat het daarbij gaat om vergankelijke data.

Redden wat er te redden valt

In het ideale geval bestaat er al een backup van de partitietabel op het moment dat een partitietabelverminking optreedt, maar vaker zal dat niet het geval zijn. Maar zelfs in zo'n geval is er nog redding mogelijk!

Herstel altijd eerst zo veel mogelijk -- bijvoorbeeld de MBR uit een kopie die LILO nog heeft liggen. Natuurlijk maak je eerst een backup van de 512 bytes lange MBR alvorens hem te overschrijven -- want als het resultaat niet bevalt wil je terug kunnen. Desnoods kun je ook met een hex-editor gaan zitten editen in de opgeslagen MBR, maar dat is alleen nuttig wanneer een combinatie van LILO- en MBR-data nodig is. Voorkom dit riskante werk liever als het even kan!

Het programma gpart is in staat om, door een scan van alle blokken op de harde schijf, na te gaan of een bepaald filesysteem aanwezig is. Doordat zo'n filesysteem doorgaans ook weet waar z'n grenzen liggen is daarmee af te leiden in welke partitities hij origineel vervat was. Voor het hier geschetste probleem van een deels verloren gegane partitietabel dus de ideale oplossing?

Helaas -- gpart is populair en wordt warm aanbevolen, maar het herkent niet alle filesystemen. Het herkent fat en ntfs filesystemen, en voor Linux ook ext2, swap en reiser, maar ext3 wordt niet herkend, en laten populaire distributies zoals RedHat daar nu net gebruik van maken. Wanneer dat echter geen probleem is, voldoet gpart uitstekend. Het herkende bovenstaande partities bijvoorbeeld als volgt in de verbose mode:

Primary partition(2)
   type: 011(0x0B)(DOS or Windows 95 with 32 bit FAT) (BOOT)
   size: 4973mb #s(10185210) s(1622565-11807774)
   chs:  (101/0/1)-(734/254/63)d (101/0/1)-(734/254/63)r
   hex:  80 00 01 65 0B FE BF DE 25 C2 18 00 FA 69 9B 00

Primary partition(3)
   type: 015(0x0F)(Extended DOS, LBA)
   size: 32412mb #s(66380580) s(11807775-78188354)
   chs:  (735/0/1)-(1023/254/63)d (735/0/1)-(4866/254/63)r
   hex:  00 00 81 DF 0F FE FF FF 1F 2C B4 00 24 E3 F4 03

Kort en goed, de herkenning van deze partities was perfect, met de kleine uitzondering dat gpart niet kon afleiden welke partities bootbaar ofwel aktief zijn. Dat is gelukkig met fdisk snel hersteld na afloop. Heel handig is ook dat gpart zo op te starten is dat de uitvoer in een bestand wordt weggeschreven, zodat eerdergenoemde handmatige controle en verwerking mogelijk wordt, voor als de nood het hoogst is. Normaliter run je gpart echter een keer op de schijf, en als dat lijkt te kloppen run je hem nogmaals en schrijf je het resultaat op dezelfde schijf weg met de -W optie:

gpart -v /dev/hda # ...en mits okay...
gpart -v -W /dev/hda /dev/hda

Bovendien heeft gpart een optie -b om een backup van de oude MBR te maken (en dus niet van extended partities) alvorens die te overschrijven. Er is een -i optie om bevestiging te vragen bij elke gevonden partitie. Al met al een zeer praktisch programma, als je er geen ext3 partities op na houdt.

Gissen naar mogelijke partities

Als voorgaande fase niet werkt, zoals in ons geval, dan wordt het tijd voor nog een beetje meer giswerk. Dat is mogelijk door de Linux tool rescuept te draaien. Die maakt onderdeel uit van de util-linux set. Bovenstaande partities worden hierdoor bijvoorbeeld herkend als

#  4973 MB  fat32 partition (type  b): sectors   1622565- 11807774
#     0 MB candidate ext pt (type  5): sectors  11807775- 11807775
#  3373 MB     found in ept (type 83): sectors  11807838- 18715724
...
# 32412 MB extended part ok (type  5): sectors  11807775- 78188354

Er wordt op de gebruikelijke plaatsen gescand naar mogelijke extended partitietabellen, en als de inhoud en de afronding daarvan consistent lijken wordt de laatste regel gerappporteerd en zijn de "found in ept" regels dus op te vatten als succesvol gevonden extended partities. Als je dit draait op een harde schijf zonder problemen dan zie je de primaire partities (zoals de fat32 partitie in het voorbeeld) gevolgd door zo'n lijst van extended partities eindigend met een ok-conclusie.

Veel interesssanter wellicht, is dat ook zinnige data wordt uitgevoerd als een extended partitie niet klopt. Dat ziet er bijvoorbeeld zo uit:

#     0 MB candidate ext pt (type  5): sectors  34555815- 34555815
# retracted
# 21305 MB   ext2 partition (type 83): sectors  34555878- 78188349

Wat hier gebeurt is dat het begin van een extended partitie wordt gevonden (die beslaat 1 sector, dus begin- en eindblok zijn hetzelfde en de afmeting rondt af op 0 MB) maar om ongedocumenteerde redenen wordt die afgewezen. Wat wel wordt herkend is dat daarna een ext2 partitie (met daarin een ext3 filesysteem, dat wordt in de partitietabel niet onderscheiden) op de schijf staat, en de begin- en eindsector daarvan worden hier gereproduceerd. Nu komen we ergens!

Er wordt een lijst gegenereerd die als input kan dienen voor een standaardcommando, sfdisk, maar voordien zul je de uitvoer willen editen omdat alle drives initieel een fout nummer krijgen:

/dev/hda0 : start=  1622565, size=10185210, Id= b
/dev/hda0 : start= 11807775, size=66380580, Id= 5
/dev/hda0 : start= 34555878, size=43632472, Id=83

GNU partition editor

Als laatste mogelijkheid bekeken we GNU parted, dat in staat bleek filesystemen te herkennen, op soortgelijke wijze als gpart, alleen kan deze tool wel overweg met ext3 filesystemen. Het print commando geeft partities weer in het volgende formaat:

Minor    Start       End     Type      Filesystem  Flags
2        792.268   5765.515  primary   fat32       
4       5765.515  38177.907  extended              lba

De start en end kolommen geven de sectoren aan het begin en einde van de partitie weer, als kommagetal in megabytes. Dat is wat raar, maar aangezien er toch altijd nauwelijks verklaarbare kleine afwijkingen zijn in de precieze getallen, is het zo gek nog niet. De hele tool werkt verder met deze benaderde getallen. Ten opzichte van de uitvoer van rescuept moet dus wat omgerekend worden; omdat het daar gaat om sectoren van 512 bytes elk, moeten de getallen daar door 2048 worden gedeeld.

Om de niet herkende voorbeeldpartitie te herstellen volstond het ingeven van een enkel commando,

rescue 16872.987 38177.907

Of iets met getallen in die buurt. Hierna wordt een paar minuten gescand naar het filesysteem, en zodra dat herkend is (als ext3) kan dat worden toegelaten door de gebruiker, waarna de extended partietabel onmiddelijk wordt aangepast voor verwerking na de volgende reboot. Pfew, we zijn gered!

Het allerlaagste niveau

We hebben diep moeten duiken voor deze reddingsoperatie, maar hopelijk dient dit artikel als voorbeeld voor hoeveel nog mogelijk is als de nood hoog is. We gaan in de komende afleveringen van NetOpus in op een paar even fundamentele reddingsoperaties. In ieder geval komen nog aan bod het booten vanaf een netwerk en het afdwingen van rootrechten op een Unix systeem waarvan het rootpassword zoekgeraakt is.

Experimenteren met floppy

Hieronder volgt een transcript van een experiment met een partitietabel, veilig en wel op een floppy. Jawel, dat kan dus werkelijk! Het maakt gebruik van de scripts partitionlist_get.py en partitionlist_set.py; deze staan tevens onder versiebeheer met Darcs.

dd if=/dev/zero of=/dev/fd0 bs=512 count=1
fdisk /dev/fd0

En hoppa!

[root@aida /]# fdisk -l /dev/fd0

Disk /dev/fd0: 1 MB, 1474560 bytes
2 heads, 18 sectors/track, 80 cylinders
Units = cylinders of 36 * 512 = 18432 bytes

    Device Boot    Start       End    Blocks   Id  System
/dev/fd0p1             1         3        45   83  Linux
/dev/fd0p2             4         4        18    b  Win95 FAT32
/dev/fd0p4             5        80      1368    5  Extended
/dev/fd0p5             5        12       135   82  Linux swap
/dev/fd0p6            13        50       675   83  Linux
/dev/fd0p7            51        77       477   83  Linux
/dev/fd0p8            78        80        45   83  Linux


Then, download the partition table from the floppy:

[root@aida /]# ./partitionlist_get.py >testfloppy.partitions

Then, wipe the test floppy -- of course a rather tough test, and
not useful in practice since it also wipes the data sectors that
you are probably trying to recover by reconstructing the partitions.

[root@aida /]# dd if=/dev/zero of=/dev/fd0 bs=512 count=2880

Then, recover the partition table on the floppy:

[root@aida /]# ./partitionlist_set.py <testfloppy.partitions

And finally, re-retrieve the partition table:

[root@aida /]# fdisk -l /dev/fd0

Disk /dev/fd0: 1 MB, 1474560 bytes
2 heads, 18 sectors/track, 80 cylinders
Units = cylinders of 36 * 512 = 18432 bytes

    Device Boot    Start       End    Blocks   Id  System
/dev/fd0p2             1         3        45   83  Linux
/dev/fd0p3             4         4        18    b  Win95 FAT32
/dev/fd0p4             5        80      1368    5  Extended
/dev/fd0p5             5        12       135   82  Linux swap
/dev/fd0p6            13        50       675   83  Linux
/dev/fd0p7            51        77       477   83  Linux
/dev/fd0p8            78        80        45   83  Linux


Looks familiar, does it not?!?

Verantwoording

Deze reeks is gedurende 2004 verschenen in NetOpus. De reeks staat als geheel onder http://rick.vanrein.org/blog/netopus/bootstrapper

Translate to English Translate to English

Comments on this article