從扇區看USB-FDD和USB-HDD在引導時的區別

田宇發表於2015-03-06

這個問題其實大家在平時是不會注意到的,我也是因為最近在PC機上寫系統載入程式的時候,才注意到的。

起因是這樣的,我在bochs虛擬機器裡寫了個載入程式,想移植到物理機上,大家都懂的,虛擬機器是支援軟碟機的,操作簡單方便,而且還可以建立虛擬軟盤,在系統啟動的時候,可以通過BIOS中斷呼叫,寫入映象也很方便。但是,問題來了!現在已經很難搞到軟碟機了和軟盤了,所以,最好的解決辦法就是用U盤代替軟盤,而且,大家現在也都這麼幹。於是乎,我就去網上找了找關於U盤啟動相關的資料。本來以為沒什麼,就沒把它當回事,可是,百度查出來的結果很不樂觀,第一頁一堆U盤引導助手什麼的,顯然不是我們們要的東西,不過倒是可以強讀MBR,把引導扇區的資料讀出來,然後反彙編,就能看出來了,但是,貌似比較耗時、費力,而且結果也不一定讓人滿意。那麼,下一步該怎麼辦呢?

還是從原點出發吧!因為在BIOS的配置選項裡面有啟動項的設定,這個大家都是知道的。等我進入到BIOS啟動項配置介面的時候,困惑就出來了,到底該用那麼模式?什麼是USB-FDD?什麼是USB-HDD?

經過一番簡單的調查以後,原來USB-FDD是模擬軟盤模式,一般的軟盤都可以用這個模式來啟動;USB-HDD是硬碟模式,像行動硬碟什麼的都可以用這種方式啟動;

知道了這些,下面就該為U盤選取轉化模式了。我用的磁碟管理軟體是DiskGenius,這個小巧易用,如果大家手頭有別的軟體也可以。然後找了一個16MB大小的U盤,這個淘寶都可以買到,也就幾塊錢。下面先把U盤裡扇區的資料全部清空。大家一定要注意,不要光格式化U盤,因為這樣的話U盤裡的檔案系統是清空的,但是扇區裡還是會殘留髒資料的,這個原因如果你看過檔案系統是如何操作的,那麼你就會理解其中的緣由了,所以,這就是為什麼誤刪除的檔案還可以找回來的原因。

清理完U盤裡的資料後,該選擇啟動模式了,先選USB-FDD模式吧,感覺軟盤模式會簡單一些。
enter image description here

要注意把更改驅動器號、掃描壞扇區、建立DOS系統幾個選項勾選掉,這樣才能保證扇區裡是一個乾淨的檔案系統。

至於為什麼要格式化成FAT12呢?是因為它的結構比較簡單,大致可以一目瞭然。點選轉換,完成對U盤的設定及檔案系統的格式化。

接下來我們看看U盤MBR(引導扇區)裡的資料:

enter image description here

如果你瞭解引導扇區的結構,那麼只要仔細觀察可以看出,這個引導扇區記錄和軟盤的沒有什麼區別。如果不瞭解的話,那麼也沒關係,其實裡面並不複雜,它由兩部分構成:主開機記錄、55AA結束標誌符。讀者可以很清楚的看到在0x36地址處的字串“FAT12”和附近的一些亂碼是這個檔案系統的結構,如果有興趣的讀者可以到網上查一查FAT12檔案系統的各項欄位。值得注意的是:FAT12檔案系統的開始位置一般不是從MBR 的0x36或者0x00處開始的,而是從0x03開始的,這點在讀者自己學習的時候要注意,不然得到的結果就會和你已經格式化了的檔案系統不一致。

對於瞭解磁碟結構的同學們,大家都知道軟盤和硬碟都是有扇區、磁頭、磁軌的,但是在U盤裡沒有這個概念,不過呢,我在DiskGenius軟體裡還是獲得了相關的資訊,詳見下圖所示:

enter image description here

大家可以清楚的看到,這個U盤容量16MB,2個柱面,255個磁頭,每磁軌63個扇區,總扇區數:32768。但是大家千萬要注意,這裡的資料應該是DiskGenius這個軟體自己模擬的,我在寫載入程式的時候,用int13 AH=2 以CHS(cylinders-heads-sectors,磁柱-磁頭-扇區)定址模式讀取U盤扇區的資料是無效的,或者讀出來的資料和扇區對應不上;對於出現這種問題,將在本文的後面介紹,請大家稍安勿躁。

再來看看USB-HDD模式,這個是硬碟的啟動模式。同樣,為了看清扇區內容,我們還是要先清空扇區裡的資料。然後選擇製作USB-HDD啟動盤,並且建立可引導分割槽。使用預設的配置就可以了,作者這裡的預設配置如下:

enter image description here

我們還是使用的FAT12檔案系統。在完成格式化以後,讓我們還是來看看MBR(引導扇區)裡面的資料吧,詳見下圖所示:

enter image description here

如果瞭解引導扇區結構的朋友肯定能明白,其實硬碟的引導扇區和軟盤的引導扇區還是有一定區別的;如前面提到的,軟盤的引導扇區包括:引導程式碼和55AA結束符兩部分,加在一起是512B位元組;而硬碟的引導扇區包括:引導程式碼、硬碟分割槽表、55AA結束符三部分組成;把引導扇區分成三段:0~0x1BD是引導程式碼,0x1BE~0x1FD是硬碟分割槽表,一共四項每項16個位元組,最後兩個位元組是55AA結束符。

在這裡是80h,01h,01h,00h,01h,feh,3fh,01h,3fh,00h,00h,00h,43h,7dh,00h,00h看來只有一個硬碟分割槽,其他的硬碟分割槽表內容全部都是0x00,正好我們剛才只建立了一個硬碟分割槽,那麼這16個位元組都是代表什麼意思呢?請參見下表:

位元組位移 欄位長度 欄位名和定義
0 1Byte 80h 引導指示符,制定該分割槽是否是活動分割槽
1 1Byte 01h 開始磁頭
2 2Byte 0001h 開始扇區和柱面
4 1Byte 01h 系統ID,定義了分割槽的型別
5 1Byte FEh 結束磁頭
6 2Byte 013Fh 結束扇區和柱面
8 4Byte 0000,003Fh 相對扇區數,從該磁碟的開始到該分割槽的開始的位移量,以扇區來計算
12 4Byte 0000,7D43h 總扇區數,該分割槽中的扇區總數

可以與下圖U盤的引數做一下簡單的對照,

enter image description here

0000,003Fh的十進位制表示就是63,0000,7D43h的十進位制表示就是32067,與起始扇區號63、總扇區數32067是一致的,至於系統ID嘛,這個算是固定的,01代表的是FAT12,05代表的是擴充套件分割槽表,linux的檔案系統是83等等,這裡就不多介紹了。

再讓我們跳轉到3fh扇區去看看,哪裡的內容是什麼吧!

enter image description here

我想聰明的你已經發現了,在0x7E36處也是FAT12檔案系統標示符,看來我們格式化的檔案系統在這裡,而不是在MBR裡,這就是硬碟分割槽表的功能,如果我們把一塊硬碟只給一個系統或者分割槽使用,對於今天的我們,未免容量有些大,使用硬碟分割槽表可以把硬碟分解成4個主邏輯分割槽。在主邏輯分割槽內可以在定義擴充套件分割槽表,主分割槽再細分為若干個邏輯擴充套件分割槽(就是系統ID:05),這回看來跟硬碟上的結構沒有什麼區別了。

介紹了這麼多,下面該解決前文留下的那個問題了,對於那些假的扇區、磁頭、磁軌引數我們該怎麼辦呢?

其實BIOS早就為我們解決了這個問題,對於正常的讀扇區操作,使用BIOS的INT 13h AH = 02h中斷即可,其中AL=要讀扇區數,CH=柱面(磁軌)號,CL=起始扇區號,DH=磁頭號,DL=驅動器號(軟碟機從0開始,0:軟碟機A,1:軟碟機B;硬碟從80h開始,80h:硬碟C,81h:硬碟D)。這裡在多講一句,BIOS通過設定實現軟碟機引導,當我們的載入程式被執行的時候,那麼我們的程式碼所在的軟碟機就是軟碟機A,所以程式碼的DL暫存器賦值0就可以,不必糾結那個軟碟機是A,那個軟碟機是B。硬碟也是一樣,設定成80h即可,等到系統核心執行起來以後,磁碟的驅動程式會重新為磁碟分配磁碟裝置序列號。這種模式就是傳統的CHS(cylinders-heads-sectors,磁柱-磁頭-扇區)定址模式,根據相應的設定定位到具體的讀取扇區上。對於傳統的分扇區、磁頭、磁軌的磁碟裝置這種操作方式沒有問題,但是像U盤或者固態硬碟等沒有扇區、磁頭、磁軌的裝置我們該如何讀取扇區呢?

對於這些不分扇區、磁頭、磁軌的裝置,BIOS使用了一種新型的讀寫方式——LBA(Logical Block Address) ,中文名稱:邏輯區塊地址。這種方式可以直接使用順序的扇區號,而不用再進行磁頭,磁軌,扇區的轉換了。因此BIOS的INT 13h AH = 42h中斷實現了讀扇區的擴充套件,其中DL=驅動器號,這個和INT 13h AH = 02h中斷的DL暫存器一樣,而DS:SI指向的是一個叫做disk address packet(硬碟地址包)的結構,它的格式如下表:

Offset Size Description
00h Byte Size of packet(10h or 18h)
01h Byte Reserved(0)
02h Word Number of blocks to transfor(max 007fh for phoenix EDD)
04h DWord Transfer buffer
08h QWord Starting absolute block number
10h QWord 64-bit flat address of transfer buffer(used if DWord at 04h is FFFF:FFFFh)

這個結構有16位元組和24位元組兩種,根據使用者需要讀取的地址長度而決定,作者目前使用的是在真實模式下定址4GB空間並且使用16位元組的LBA定址結構,通過BIOS的INT 13h AH = 42h中斷讀取扇區內容,可以使程式正常執行,並且獲得正確的結果。

希望這篇文章能給你在編寫載入程式方面提供一些幫助。

相關文章