磁盤操作資料

MBR Date
  自磁芯大戰以來,病毒從DOS時代的病毒發展到WINDOS系統的病毒,從變形、加密到智能化現在的病毒是讓人防不勝防,現在網絡上病毒大肆氾濫,給人們帶來的很大的危害,本人在此僅做拋磚引玉,介紹病毒的原理,希望大家共同研究交流。(本文參考了網絡上的部分文章,並引用了部分內容。)
要學DOS下的病毒,首先你必須要學會或掌握彙編語言。DOS下病毒一般分爲引導型病毒、文件型病毒、混合型病毒等。大部分病毒是感染COM和EXE文件,因此你必須瞭解COM文件和EXE文件結構。
一 .COM文件結構及原理
.COM 文件比較簡單,.COM文件包含程序的一個絕對映象―――就是說,爲了運行程序準確的處理器指令和內存中的數據,MS-DOS通過直接把該映象從文件拷貝到內存而加載.COM程序,它不作任何改變。爲加載一個.COM程序,MS-DOS首先試圖分配內存,因爲.COM程序必須位於一個64K的段中,所以.COM文件的大小不能超過65,024(64K減去用於PSP的256字節和用於一個起始堆棧的至少256字節)。如果MS-DOS不能爲程序、一個PSP、一個起始堆棧分配足夠內存,QQ:9750406則分配嘗試失敗。否則,MS-DOS分配儘可能多的內存(直至所有保留內存),即使.COM程序本身不能大於64K。在試圖運行另一個程序或分配另外的內存之前,大部分.COM程序釋放任何不需要的內存。
分配內存後,MS-DOS在該內存的頭256字節建立一個PSP,如果PSP中的第一個FCB含有一個有效驅動器標識符,則置AL爲00h,否則爲0FFh。MS-DOS還置AH爲00h或0FFh,這依賴於第二個FCB是否含有一個有效驅動器標識符。建造PSP後,MS-DOS在PSP後立即開始(偏移100h)加載.COM文件,它置SS,DS和ES爲PSP的段地址,接着創建一個堆棧.爲創建一個堆棧,MS-DOS置SP爲0000h,若已分配了至少64K內存;否則,它置寄存器爲比所分配的字節總數大2的值.最後,它把0000h推進棧(這是爲了保證與在早期MS-DOS版本上設計的程序的兼容性)。MS-DOS通過把控制傳遞偏移100h處的指令而啓動程序.程序設計者必須保證.COM文件的第一條指令是程序的入口點。注意,因爲程序是在偏移100h處加載,因此所有代碼和數據偏移也必須相對於100h.彙編語言程序設計者可通過置程序的初值爲100h而保證這
一點(例如通過在原程序的開始使用語句org 100h).
二 EXE文件結構
 EXE 文件比較複雜,每個EXE文件都有一個文件頭,結構如下:
       EXE文件頭信息    
    ―――――――――――――――――――
     ├ 偏移量 ┤   意義         ┤
     ├00h-01h ┤MZ‘EXE文件標記     ┤
     ├2h-03h ┤文件長度除512的餘數  ┤
     ├04h-05h ┤...............商  ┤
     ├06h-07h ┤重定位項的個數      ┤
     ├08h-09h ┤文件頭除16的商     ┤
     ├0ah-0bh ┤程序運行所需最小段數 ┤
     ├0ch-0dh ┤..............大.... ┤
     ├oeh-0fh ┤堆棧段的段值 (SS)   ┤
     ├10h-11h ┤........sp      ┤
     ├12h-13h ┤文件校驗和       ┤
     ├14h-15h ┤IP           ┤
     ├16h-17h ┤CS           ┤
     ├18h-19h ┤............  ┤
     ├1ah-1bh ┤............     ┤
     ├1ch   ┤............      ┤ 
 ―――――――――――――――――――――――――
.EXE文件包含一個文件頭和一個可重定位程序映象。文件頭包含MS-DOS用於加載程序的信息,例如程序的大小和寄存器的初始值。文件頭還指向一個重定位表,該表包含指向程序映象中可重定位段地址的指針鏈表。文件頭的形式與EXEHEADER結構對應:
EXEHEADER STRUC
exSignature dw 5A4Dh ;.EXE標誌
exExraBytes dw ? ;最後(部分)頁中的字節數
exPages dw ? ;文件中的全部和部分頁數
exRelocItems dw ? ;重定位表中的指針數
exHeaderSize dw ? ;以字節爲單位的文件頭大小
exMinAlloc dw ? ;最小分配大小
exMaxAlloc dw ? ;最大分配大小
exInitSS dw ? ;初始SS值
exInitSP dw ? ;初始SP值
exChechSum dw ? ;補碼校驗值
exInitIP dw ? ;初始IP值
exInitCS dw ? ;初始CS值
exRelocTable dw ? ;重定位表的字節偏移量
exOverlay dw ? ;覆蓋號
EXEHEADER ENDS程序映象,包含處理器代碼和程序的初始數據,緊接在文件頭之後。它的大小以字節爲單位,等於.EXE文件的大小減去文件頭的大小,也等於exHeaderSize的域的值乘以16。MS-DOS通過把該映象直接從文件拷貝到內存加載.EXE程序然後調整定位表中說明的可重定位段地址。
定位表是一個重定位指針數組,每個指向程序映象中的可重定位段地址。文件頭中的exRelocItems域說明了數組中指針的個數,exRelocTable域說明了分配表的起始文件偏移量。每個重定位指針由兩個16位值組成:偏移量和段值。 爲加載.EXE程序,MS-DOS首先讀文件頭以確定.EXE標誌並計算程序映象的大小。然後它試圖申請內存。首先,它計算程序映象文件的大小加上PSP的大小再加上EXEHEADER結構中的exMinAlloc域說明的內存大小這三者之和,如果總和超過最大可用內存塊的大小。則MS-DOS停止加載程序並返回一個出錯值。否則面,它計算程序映象的大小加上PSP的大小再加上EXEHEADER結構中exMaxAlloc域說明的內存大小之和,如果第二個總和小於最大可用內存塊的大小,則MS-DOS 分配計算得到的內存量。否則,它分配最大可用內存塊。分配完內存後,MS-DOS確定段地址,也稱爲起始段地址,MS-DOS從此處加載程序映象。如果exMinAlloc域和exMaxAlloc域中的值都爲零,則MS-DOS把映象儘可能地加載到內存最高端。否則,它把映象加載到緊挨着PSP域之上。接下來,MS-DOS讀取重定位表中的項目調整所有由可重定位指針說明的段地址。對於重定位表中的每個指針,MS-DOS尋找程序映象中相應的可重定位段地址,並把起始段地址加到它之上。一旦調整完畢,段地址便指向了內存中被加載程序的代碼和數據段。 MS-DOS在所分配內存的最低部分建造256字節的PSP,把AL和AH設置爲加載 .COM程序時所設置的值。MS-DOS使用文件頭中的值設置SP與SS,調整SS初始值,把起始地址加到它之上。MS-DOS還把ES和DS設置爲PSP的段地址.最後,MS-DOS從程序文件頭讀取CS和IP的初始值,把起始段地址加到CS之 上,把控制轉移到位於調整後地址處的程序。

三、引導型病毒原理
瞭解引導型病毒的原理,首先要了解引導區的結構。軟盤只有一個引導區,稱爲DOS BOOT SECTER ,只要軟盤做了格式化,就會存在。其作用爲查找盤上有無IO.SYS DOS.SYS,若有則引導,若無則顯示‘NO SYSTEM DISK...’等信息。硬盤有兩個引導區,在0面0道1扇區的稱爲主引導區,內有主引導程序和分區表,主引導程序查找激活分區,該分區的第一個扇區即爲DOS BOOT SECTER。絕大多數病毒感染硬盤主引導扇區和軟盤DOS引導扇區。

***3.5”軟盤格式***
3.5”軟盤是雙面的,所以零磁道有正反兩面,正面爲0-17扇區,
反面是18-35扇區。
0 扇區: Boot area (引導扇區);
1 - 9 扇區: 1st FAT area (第一張文件分配表);
10 - 18 扇區: 2st FAT area (第二張文件分配表);
19 - 32 扇區: Root dir area(也叫 File Directory Table,FDT)
文件目錄表(根目錄)
33-2879 扇區: Data area (數據區)

***硬盤的主引導記錄結構***
硬盤的主引導記錄結構
偏移 機器碼 符號指令 說明
0000 FA CLI ;屏蔽中斷
0001 33C0 XOR AX,AX
0003 8ED0 MOV SS,AX ;(SS)=0000H
0005 BC007C MOV SP,7C00 ;(SP)=7C00H
0008 8BF4 MOV SI,SP ;(SI)=7C00H
000A 50 PUSH AX
000B 07 POP ES ;(ES)=0000H
000C 50 PUSH AX
000D 1F POP DS ;(DS)=0000H
000E FB STI
000F FC CLD
0010 BF0006 MOV DI,0600
0013 B90001 MOV CX,0100 ;共512字節
0016 F2 REPNZ
0017 A5 MOVSW ;主引導程序把自己從0000:7C00處搬到
;0000:0600處,爲Dos分區的引導程序騰
;出空間
0018 EA1D060000 JMP 0000:061D ;跳到0000:061D處繼續執行,實際上就是
;執行下面的MOV指令(001D偏移處)
001D BEBE07 MOV SI,07BE ;07BE-0600=01BE,01BE是分區表的首址
0020 B304 MOV BL,04 ;分區表最多4項,即最多4個分區
0022 803C80 CMP BYTE PTR [SI],80 ;80H表示活動分區
0025 740E JZ 0035 ;找到活動分區則跳走
0027 803C00 CMP BYTE PTR [SI],00 ;00H爲有效分區的標誌
002A 751C JNZ 0048 ;既非80H亦非00H則分區表無效
002C 83C610 ADD SI,+10 ;下一個分區表項,每項16字節
002F FECB DEC BL ;循環計數減一
0031 75EF JNZ 0022 ;檢查下一個分區表項
0033 CD18 INT 18 ;4個都不能引導則進入ROM Basic
0035 8B14 MOV DX,[SI]
0037 8B4C02 MOV CX,[SI+02] ;取活動分區的引導扇區的面,柱面,扇區
003A 8BEE MOV BP,SI ;然後繼續檢查後面的分區表項
003C 83C610 ADD SI,+10
003F FECB DEC BL
0041 741A JZ 005D ;4個都查完則去引導活動分區
0043 803C00 CMP BYTE PTR [SI],00 ;00H爲分區有效標誌
0046 74F4 JZ 003C ;此分區表項有效則繼續查下一個
0048 BE8B06 MOV SI,068B ;068B-0600=018B,取"無效分區"字符串
004B AC LODSB ;從字符串中取一字符
004C 3C00 CMP AL,00 ;00H表示串尾
004E 740B JZ 005B ;串顯示完了則進入死循環
0050 56 PUSH SI
0051 BB0700 MOV BX,0007
0054 B40E MOV AH,0E
0056 CD10 INT 10 ;顯示一個字符
0058 5E POP SI
0059 EBF0 JMP 004B ;循環顯示下一個字符
005B EBFE JMP 005B ;此處爲死循環
005D BF0500 MOV DI,0005 ;讀入活動分區的引導扇,最多試讀5次
0060 BB007C MOV BX,7C00
0063 B80102 MOV AX,0201
0066 57 PUSH DI
0067 CD13 INT 13 ;讀
0069 5F POP DI
006A 730C JNB 0078 ;讀盤成功則跳走
006C 33C0 XOR AX,AX
006E CD13 INT 13 ;讀失敗則復位磁盤
0070 4F DEC DI
0071 75ED JNZ 0060 ;不到5次則再試讀
0073 BEA306 MOV SI,06A3 ;06A3-0600=00A3,即"Error loading"串
0076 EBD3 JMP 004B ;去顯示字符串,然後進入死循環
0078 BEC206 MOV SI,06C2 ;06C2-0600=00C2,即"Missing.."串
0076 EBD3 JMP 004B ;去顯示字符串,然後進入死循環
0078 BEC206 MOV SI,06C2 ;06C2-0600=00C2,即"Missing.."串
007B BFFE7D MOV DI,7DFE ;7DFE-7C00=01FE,即活動分區的引導扇
;區的最後兩字節的首址
007E 813D55AA CMP WORD PTR [DI],AA55;最後兩字節爲AA55H則有效
0082 75C7 JNZ 004B ;無效則顯示字符串並進入死循環
0084 8BF5 MOV SI,BP
0086 EA007C0000 JMP 0000:7C00 ;有效則跳去引導該分區
0080 49 6E 76 61 6C Inval
0090 69 64 20 70 61 72 74 69-74 69 6F 6E 20 74 61 62 id partition tab
00A0 6C 65 00 45 72 72 6F 72-20 6C 6F 61 64 69 6E 67 le.Error loading
00B0 20 6F 70 65 72 61 74 69-6E 67 20 73 79 73 74 65 operating syste
00C0 6D 00 4D 69 73 73 69 6E-67 20 6F 70 65 72 61 74 m.Missing operat
00D0 69 6E 67 20 73 79 73 74-65 6D 00 00 FB 4C 38 1D ing system...L8.
00E0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00F0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0100 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0180 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0190 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
01A0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
01B0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 80 01 ................;分區表
01C0 01 00 06 0F 7F 9C 3F 00-00 00 F1 59 06 00 00 00 ......?....Y....
01D0 41 9D 05 0F FF 38 30 5A-06 00 40 56 06 00 00 00 
A....80Z..@V....
01E0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
01F0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 55 AA ..............U.


使用INT 13H的02功能調用把位於硬盤保留扇區中0道0頭1扇區處的硬盤主引導記錄讀到內存的ES:BX處。現在把讀出程序代碼進行如下分析:

1、移動主引導記錄程序
0E74:7C00 33C0 XOR AX,AX ;AX清零
0E74:7C02 8ED0 MOV SS,AX ;SS清零
0E74:7C04 BC007C MOV SP,7C00 ;SP=7C00,堆棧設在0:7C00H
0E74:7C07 FB STI ;開中斷
0E74:7C08 50 PUSH AX
0E74:7C09 07 POP ES ;ES=0
0E74:7C0A 50 PUSH AX
0E74:7C0B 1F POP DS ;DS=0
0E74:7C0C FC CLD
0E74:7C0D BE1B7C MOV SI,7C1B ;源地址爲0:7C1BH
0E74:7C10 BF1B06 MOV DI,061B ;目的地址爲0:061BH
0E74:7C13 50 PUSH AX
0E74:7C14 57 PUSH DI
0E74:7C15 B9E501 MOV CX,01E5 ;移動01E5字節
0E74:7C18 F3 REPZ ;將主引導記錄從0:7C1B-0:7DFF
0E74:7C19 A4 MOVSB ;移至0:061B-0:07FF
0E74:7C1A CB RETF ;轉移到0:061B,繼續執行程序

2、順序查找四個硬盤分區表,尋找自舉標誌
0E74:061B BEBE07 MOV SI,07BE ;SI指向硬盤分區表1的自舉標誌

0E74:061E B104 MOV CL,04 ;查找四個分區
0E74:0620 382C CMP [SI],CH
0E74:0622 7C09 JL 062D ;如果[SI]的第7位爲1,即爲自
;舉標誌,轉062DH
0E74:0624 7515 JNZ 063B ;如果[SI]不爲0,出錯,轉063BH
0E74:0626 83C610 ADD SI,+10 ;依次檢驗四個分區表,直至找到

0E74:0629 E2F5 LOOP 0620 ;自舉標誌
0E74:062B CD18 INT 18 ;找不到自舉標誌,進入BOOT異
;常處理程序。
0E74:062D 8B14 MOV DX,[SI] ;保存自舉驅動器號於DL中
0E74:062F 8BEE MOV BP,SI ;保存自舉分區地址指針於BP
0E74:0631 83C610 ADD SI,+10 ;繼續檢驗自舉分區後的分區
0E74:0634 49 DEC CX ;自舉標誌,直至四個分區都
0E74:0635 7416 JZ 064D ;檢查完
0E74:0637 382C CMP [SI],CH ;若其餘的自舉標誌不爲0,出錯

0E74:0639 74F6 JZ 0631 

3、出錯,寫屏幕程序段
0E74:063B BE1007 MOV SI,0710 ;錯誤信息輸出,死循環
0E74:063E 4E DEC SI
0E74:063F AC LODSB
0E74:0640 3C00 CMP AL,00
0E74:0642 74FA JZ 063E
0E74:0644 BB0700 MOV BX,0007
0E74:0647 B40E MOV AH,0E
0E74:0649 CD10 INT 10
0E74:064B EBF2 JMP 063F

硬盤主引導記錄程序的功能是讀出自舉分區的BOOT程序,並把控制轉移到分區BOOT程序。整個程序流程如下:
1 將本來讀入到0:7C00H處的硬盤主引導記錄程序移至0:61BH處;
⑵ 順序讀入四個分區表的自舉標誌,以找出自舉分區,若找不到,轉而執行INT18H的BOOT異常執行中斷程序;
⑶ 找到自舉分區後,檢測該分區的系統標誌,若爲32位FAT表或16位FAT表但支持13號中斷的擴展功能,就轉到執行13號中斷的41號功能調用進行安裝檢驗,檢驗成功,就執行42號擴展讀功能調用把BOOT區程序讀入到內存0:7C00H處,成功,跳到第⑸步,若讀失敗或系統標誌爲其它,就調用13號中斷的讀扇區功能調用把BOOT讀到0:7C00H;
⑷ 用13號中斷的讀扇區功能時,用兩種方式分別進行5次試讀。第一種方式是直接從自舉分區的頭扇區讀入BOOT程序,若讀成功,但結束標誌不是55AA,則改用第二種方式,又如果用第一種方式試讀五次均不成功,就改用第二種方式。若兩種方式試讀均失敗,就轉到出錯處理程序; ⑸ 讀入BOOT區程序成功,轉至0:7C00H處執行BOOT程序。 

NT下對I/O地址的訪問
Windows NT 操作系統設置的進程模式會使運行在其中的應用程序訪問I/O地址的指令引起保護性的失敗。這使得應用程序需要附以一個設備驅動程序進行I/O操作。設備驅動程序運行在內核模式,這使得在這種狀態的中運行的進程可以執行I/O操作。

---- Windows 95/98 是僅爲 Intel 類型機器設計的,沒有額外複雜的I/O需求,而Windows NT 被設計成可以在不同機器機構上進行移植。這使得Windows NT 的系統模式要求驅動程序的編寫者要考慮一臺機器可能有多種類型的總線,這可能需要在總線之間傳遞地址。這種模式還要區別I/O空間和內存空間。在多總線的機器中每一總線可以既支持內存又支持I/O循環。

---- 根據定義,I/O寄存器或者端口訪問是通過I/O循環實現的。然而,在一些系統中外部總線的I/O空間可以被映像到進程內存空間。硬件抽象層(Hardware Abstract Layer)決定這些。要訪問I/O寄存器,驅動程序編寫者必須知道寄存器在那一總線,它的I/O空間地址在那條總線。一條總線是由其接口類行 (如 ISA 、PCI 等)和編號(從零開始)決定的。

---- 下面是一個假象設備訪問I/O的例子,接口類型:ISA 編號 0 地址 0xE700。設備描述如下: Offset Size Usage 0 1 Command register 1 1 Status register 2 2 Word data register 4 4 Dword data register

---- 用開發NT 設備驅動程序的工具包DriverDorks 可以用以下 步驟訪問設備:
---- 建立一個KIoRange的對象映像設備寄存器。
   KIoRange DeviceIos; Status = DevceIos.Initialize(
     Isa, // 總線類型
     0, // 總線號
     0xE700, // 總線地址
     8, // 設備數
     TRUE // 映像到系統空間(如果端口是內存映像的)
     );
   if(NT_SUCCESS(status)) //建立成功

---- 可以用KIoRange 的成員函數訪問寄存器:
     //寄存器偏移量
     #define COMMAND 0
     #define STATUS 1
     #define WDATA 2
     #define DDATA 3

     //讀狀態寄存器
     UCHAR DeviceStatus = DeviceIos.inb(STATUS);

     //寫命令寄存器
     DeviceIos.outb(COMMAND,CMD_RESET);

     //寫20個字到端口
     DeviceIos.outw(WDATA,buffer,20);

---- 另外也可以建立KIoRegister 的對象來訪問設備:
     KIoRegister CommandReg = DeviceIos[COMMAND];
     KIoRegister StatusReg = DeviceIos[STATUS];
     CommandRge=(UCHAR)RESET;  //寫 RESET命令
     UCHAR status=StatusReg; //讀狀態寄存器
     如果在同一函數中頻繁訪問寄存器用KioRegiser 比用KIoRange 的成員函數的性能好一些。無論如何,數據類型必須正確(UCHAR,USHORT,ULONG),這些決定了到總線上數據的實際大小.

硬盤的 dos 管理結構
  1. 磁道,扇區,柱面和磁頭數
   硬盤最基本的組成部分是由堅硬金屬材料製成的塗以磁性介質的盤片,不同容量硬盤的盤片數不等。每個盤片有兩面,都可記錄信息。盤片被分成許多扇形的區域,每個區域叫一個扇區,每個扇區可存儲 128 × 2 的 n 次方( n = 0.1.2 .3 )字節信息。在 dos 中每扇區是 128 × 2 的 2 次方= 512 字節,盤片表面上以盤片中心爲圓心,不同半徑的同心圓稱爲磁道。硬盤中,不同盤片相同半徑 的磁道所組成的圓柱稱爲柱面。磁道與柱面都是表示不同半徑的圓,在許多場合,磁道和柱面可以互換使用,我們知道,每個磁 盤有兩個面,每個面都有一個磁頭,習慣用磁頭號來區分。扇區,磁道(或柱面)和磁頭數構成了硬盤結構的基本參數,幫這些 參數可以得到硬盤的容量,基計算公式爲:
  存儲容量=磁頭數×磁道(柱面)數×每道扇區數×每扇區字節數
  要點:( 1 )硬盤有數個盤片,每盤片兩個面,每個面一個磁頭
     ( 2 )盤片被劃分爲多個扇形區域即扇區
     ( 3 )同一盤片不同半徑的同心圓爲磁道
     ( 4 )不同盤片相同半徑構成的圓柱面即柱面
     ( 5 )公式: 存儲容量=磁頭數×磁道(柱面)數×每道扇區數×每扇區字節數
     ( 6 )信息記錄可表示爲:××磁道(柱面),××磁頭,××扇區


  2. 簇
   “簇”是 dos 進行分配的最小單位。當創建一個很小的文件時,如是一個字節,則它在磁盤上並不是只佔一個字節的空間, 而是佔有整個一簇。 dos 視不同的存儲介質(如軟盤,硬盤),不同容量的硬盤,簇的大小也不一樣。簇的大小可在稱爲磁盤 參數塊( bpb )中獲取。簇的概念僅適用於數據區。
   本點:( 1 )“簇”是 dos 進行分配的最小單位。
      ( 2 )不同的存儲介質,不同容量的硬盤,不同的 dos 版本,簇的大小也不一樣。
      ( 3 )簇的概念僅適用於數據區。

  3. 扇區編號定義:絕對扇區與 dos 扇區
   由前面介紹可知,我們可以用柱面 / 磁頭 / 扇區來唯一定位磁盤上每一個區域,或是說柱面 / 磁頭 / 扇區與磁盤上每一個扇區有 一一對應關係,通常 dos 將“柱面 / 磁頭 / 扇區”這樣表示法稱爲“絕對扇區”表示法。但 dos 不能直接使用絕對扇區進行磁盤上的 信息管理,而是用所謂“相對扇區”或“ dos 扇區”。“相對扇區”只是一個數字,如柱面 140 ,磁頭 3 ,扇區 4 對應的相對扇區號 爲 2757 。該數字與絕對扇區“柱面 / 磁頭 / 扇區”具有一一對應關係。當使用相對扇區編號時, dos 是從柱面 0 ,磁頭 1 ,扇區 1 開始 (注:柱面 0 ,磁頭 0 ,扇區 1 沒有 dos 扇區編號, dos 下不能訪問,只能調用 bios 訪問),第一個 dos 扇區編號爲 0 ,該磁道上剩餘 的扇區編號爲 1 到 16 (設每磁道 17 個扇區),然後是磁頭號爲 2 ,柱面爲 0 的 17 個扇區,形成的 dos 扇區號從 17 到 33 。直到該柱面的 所有磁頭。然後再移到柱面 1 ,磁頭 1 ,扇區 1 繼續進行 dos 扇區的編號,即按扇區號,磁頭號,柱面號(磁道號)增長的順序連續 地分配 dos 扇區號。
    公式:記 dh --第一個 dos 扇區的磁頭號
             dc --第一個 dos 扇區的柱面號
             ds --第一個 dos 扇區的扇區號
             ns --每磁道扇區數
             nh --磁盤總的磁頭數
  則某扇區(柱面 c ,磁頭 h ,扇區 s )的相對扇區號 rs 爲:
    rs = nh × ns ×( c - dc )+ ns ×( h - dh )+( s - ds )
  若已知 rs , dc , dh , ds , ns 和 nh 則
    s =( rs mod ns )+ ds
    h =(( rs div ns ) mod nh )+ dh
    c =(( rs div ns ) div nh )+ dc
    要點:( 1 )以柱面 / 磁頭 / 扇區表示的爲絕對扇區又稱物理磁盤地址
       ( 2 )單一數字表示的爲相對扇區或 dos 扇區,又稱邏輯扇區號
       ( 3 )相對扇區與絕對扇區的轉換公式

 4.dos 磁盤區域的劃分
   格式化好的硬盤,整個磁盤按所記錄數據的作用不同可分爲主引導記錄( mbr:main boot record ), dos 引導記錄( dbros boot record ),文件分配表( fat:file assign table ),根目錄( bd:boot directory )和數據區。前 5 個重要信息在磁盤的外 磁道上,原因是外圈周長總大於內圈周長,也即外圈存儲密度要小些,可傷心性高些。
    要點:( 1 )整個硬盤可分爲 mbr , dbr , fat , bd 和數據區。
       ( 2 ) mbr , dbr , fat ,和 bd 位於磁盤外道。

 5.mbr
    mbr 位於硬盤第一個物理扇區(絕對扇區)柱面 0 ,磁頭 0 ,扇區 1 處。由於 dos 是由柱面 0 ,磁頭 1 ,扇區 1 開始,故 mbr 不屬於 dos 扇區, dos 不能直接訪問。 mbr 中包含硬盤的主引導程序和硬盤分區表。分區表有 4 個分區記錄區。記錄區就是記錄有關分區信
息的一張表。它從主引導記錄偏移地址 01beh 處連續存放,每個分區記錄區佔 16 個字節。
分區表的格式 分區表項的偏移 意義   佔用字節數
   00 引導指示符 1b
   01 分區引導記錄的磁頭號 1b
   02 分區引導記錄的扇區和柱面號 2b
   04 系統指示符 1b
   05 分區結束磁頭號 1b
   06 分區結束扇區和柱面號 2b
   08 分區前面的扇區數 4b
   0c 分區中總的扇區數 4b
   4 個分區中只能有 1 個活躍分區,即 c 盤。標誌符是 80h 在分區表的第一個字節處。若是 00h 則表示非活躍分區。例如:
   80 01 01 00 0b fe 3f 81 3f 00 00 00 c 3 dd 1f 00
   00 00 01 82 05 fe bf 0c 02 de 1f 00 0e 90 61 00
   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
   要點:( 1 ) mbr 位於硬盤第一個物理扇區柱面 0 ,磁頭 0 ,扇區 1 處。不屬於 dos 扇區,
      ( 2 )主引導記錄分爲硬盤的主引導程序和硬盤分區表。

  6.dbr
  dbr 位於柱面 0 ,磁頭 1 ,扇區 1 ,即邏輯扇區 0 。 dbr 分爲兩部分: dos 引導程序和 bpb ( bios 參數塊)。其中 dos 引導程序完成 dos 系統文件( io.sys , msdos.sys )的定位與裝載,而 bpb 用來描述本 dos 分區的磁盤信息, bpb 位於 dbr 偏移 0bh 處,共 13 字節。 它包含邏輯格式化時使用的參數,可供 dos 計算磁盤上的文件分配表,目錄區和數據區的起始地址, bpb 之後三個字提供物理格 式化(低格)時採用的一些參數。引導程序或設備驅動程序根據這些信息將磁盤邏輯地址( dos 扇區號)轉換成物理地址(絕對 扇區號)。
   bpb 格式  序號 偏移地址 意義
            1 03h - 0ah oem 號
            2 0bh - 0ch 每扇區字節數
            3 0dh 每簇扇區數
            4 0eh - 0fh 保留扇區數
            5 10h fat 備份數
            6 11h - 12h 根目錄項數
            7 13h - 14h 磁盤總扇區數
            8 15h 描述介質
            9 16h - 17h 每 fat 扇區數
            10 18h - 19h 每磁道扇區數
            11 1ah - 1bh 磁頭數
            12 1ch - 1fh 特殊隱含扇區數
            13 20h - 23h 總扇區數
            14 24h - 25h 物理驅動器數
            15 26h 擴展引導簽證
            16 27h - 2ah 卷系列號
            17 2bh - 35h 卷標號
            18 36h - 3dh 文件系統號
            dos 引導記錄公式:
   文件分配表≡保留扇區數
   根目錄≡保留扇區數+ fat 的個數×每個 fat 的扇區數
   數據區≡根目錄邏輯扇區號+( 32 ×根目錄中目錄項數+(每扇區字節數- 1 )) div 每扇區字節數
   絕對扇區號≡邏輯扇區號+隱含扇區數
   扇區號≡(絕對扇區號 mod 每磁道扇區數)+ 1
   磁頭號≡(絕對扇區號 div 每磁道扇區數) mod 磁頭數
   磁道號≡(絕對扇區號 div 每磁道扇區數) div 磁頭數
   要點:( 1 ) dbr 位於柱面 0 ,磁頭 1 ,扇區 1 ,其邏輯扇區號爲 0
      ( 2 ) dbr 包含 dos 引導程序和 bpb 。
      ( 3 ) bpb 十分重要,由此可算出邏輯地址與物理地址。

  7. 文件分配表
   文件分配表是 dos 文件組織結構的主要組成部分。我們知道 dos 進行分配的最基本單位是簇。文件分配表是反映硬盤上所 有簇的使用情況,通過查文件分配表可以得知任一簇的使用情況。 dos 在給一個文件分配空間時總先掃描 fat ,找到第一個可 用簇,將該空間分配給文件,並將該簇的簇號填到目錄的相應段內。即形成了“簇號鏈”。 fat 就是記錄文件簇號的一張表。 fat 的頭兩個域爲保留域,對 fat12 來說是 3 個字節, fat 來說是 4 個字節。其中頭一個字節是用來描述介質的,其餘字節爲 ffh 。介質格式與 bpb 相同。
    第一個字節的 8 位意義:
    7 6 5 4 3  2 1 0
    └───── - ┘ │ │ │┌ 0 非雙面
    置 1 │ │ └┤
    │ │ └ 1 雙面
    │ │┌ 0 不是 8 扇區
    │ └┤
    │ └ 1 是 8 扇區
    │┌ 0 不是可換的
    └┤
    └ 1 是可換的
    fat 結構含義
    fat12 fat16 意義
    000h 0000h 可用
    ff0h - ff6h fff0h - fff6h 保留
    ff7h fff7h 壞
    ff8h - fffh fff8h - ffffh 文件最後一個簇
    ××× h ×××× h 文件下一個簇
    對於 fat16 ,簇號× 2 作偏移地址,從 fat 中取出一字即爲 fat 中的域。
    邏輯扇區號=數據區起始邏輯扇區號+(簇號- 2 )×每簇扇區數
    簇號=(邏輯扇區號-數據區起始邏輯扇區號) div 每簇扇區數+ 2
要點:( 1 ) fat 反映硬盤上所有簇的使用情況,它記錄了文件在硬盤中具體位置(簇)。
   ( 2 )文件第一個簇號(在目錄表中)和 fat 的該文件的簇號串起來形成文件的“簇號鏈”,恢復被破壞的文件就是根 據這條鏈。
   ( 3 )由簇號可算邏輯扇區號,反之,由邏輯扇區號也可以算出簇號,公式如上。
   ( 4 ) fat 位於 dbr 之後,其 dos 扇區號從 1 開始。

 8. 文件目錄
   文件目錄是 dos 文件組織結構的又一重要組成部分。文件目錄分爲兩類:根目錄,子目錄。根目錄有一個,子目錄可以有 多個。子目錄下還可以有子目錄,從而形成“樹狀”的文件目錄結構。子目錄其實是一種特殊的文件, dos 爲目錄項分配 32 字 節。目錄項分爲三類:文件,子目錄(其內容是許多目錄項),卷標(只能在根目錄,只有一個。目錄項中有文件(或子目 錄,或卷標)的名字,擴展名,屬性,生成或最後修改日期,時間,開始簇號,及文件大小。
   目錄項的格式   字節偏移 意義 佔字節數
                  00h 文件名 8b
                  08h 擴展名 3b
                  0bh 文件屬性 1b
                  0ch 保留 10b
                  16h 時間 2b
                  18h 日期 2b
                  1ah 開始簇號 2b
                  1ch 文件長度 4b
   目錄項文件名區域中第一個字節還有特殊的意義: 00h 代表未使用  05h 代表實際名爲 e5h ebh 代表此文件已被刪除
目錄項屬性區域的這個字節各個位的意義如下: 7 6 5 4 3 2 1 0
                      未 修 修 子 卷 系 隱 只
                      用 改 改 目 標 統 藏 讀
                         標 標 錄   屬 屬 屬
                         志 志     性 性 性
  注意: windows 的長文件名使用了上表中所說的“保留”這片區域。
  要點:( 1 )文件目錄是記錄所有文件,子目錄名,擴展名屬性,建立或刪除最後修改日期。文件開始簇號及文件長度的一張 登記表 .
    ( 2 ) dos 中 dir 列出的內容訓是根據文件目錄表得到的。
    ( 3 )文件起始簇號填在文件目錄中,其餘簇都填在 fat 中上一簇的位置上。

9. 物理驅動器與邏輯驅動器
   物理驅動器指實際安裝的驅動器。 邏輯驅動器是對物理驅動器格式化後產生的 硬盤邏輯鎖巧解在談論具體的解決方法前,先講述一下被 " 邏輯鎖 " 鎖住的硬盤爲什麼不能用普通辦法啓動的原因:計算機在引導 dos 系統時將會搜索所有邏輯盤的順序,當 dos 被引導時,首先要去找主引 導扇區的分區表信息,位於硬盤的零頭零柱面的第一個扇區的 obeh 地址開始的地方,當 分區信息開始的地方爲 80h 時表示是主引導分區,其他的爲擴展分區,主引導分區被定義 爲邏輯盤 c 盤,然後查找擴展分區的邏輯盤,被定義爲 d 盤,以此類推找到 e , f , g..... " 邏輯鎖 " 就是在此下手,修改了正常的主引導分區記錄將擴展分區的第一個邏輯盤指向 自己, dos 在啓動時查找到第一個邏輯盤後,查找下個邏輯盤總是找到是自己,這樣一來 就形成了死循環,這就是使用軟驅 , 光驅,雙硬盤都不能正常啓動的原因。實際上這 " 邏輯鎖 " 只是利用了 dos 在啓動時的一個小小缺陷,便令不少高手都束手無策。知道了 " 邏輯 鎖 " 的 " 上鎖 " 原理,要解鎖也就比較容易了。以前我看到有位朋友採用 " 熱拔插 " 硬盤電源的方法來處理:就是在當系統啓動時,先不給被鎖的硬盤插上電源線,等待啓動完成後再給硬盤 " 熱插 " 上電源線,這時如果硬盤沒有燒壞的話,系統就可以控制硬盤了。當然這是一種非常危險的方法,大家不要輕易嘗試,下面介紹兩種比較簡單和安全的處理方法。

   方法一:修改 dos 啓動文件 首先準備一張 dos6.22 的系統盤,帶上 debug 、 pctools5.0 、 fdisk 等工具。然後在一臺正常的機器上,使用你熟悉的二進制編輯工具( debug 、 pctools5.0 ,或者 windows 下的 ultraedit 都行)修改軟盤上的 io.sys 文件(修改前記住改該文件的屬性爲正常),具體是在這個文件裏面搜索第一個 "55aa" 字符串,找到以後修改爲任何其他數值即可。用這張修改過的系統軟盤你就可以順利地帶着被鎖的硬盤啓動了。不過這時由於該硬盤正常的分區表已經被黑客程序給惡意修改了,你無法用 fdisk 來刪除和修改分區,而且仍無法用正常的啓動盤啓動系統,這時你可以用 debug 來手工恢復。使用 debug 手工修復硬盤步驟如下:

a:/>debug
-a
-xxxx:100 mov ax,0201 讀一個扇區的內容
-xxxx:103 mov bx,500 設置一個緩存地址
-xxxx:106 mov cx,0001 設置第一個硬盤的硬盤指針
-xxxx:109 mov dx,0080 讀零磁頭
-xxxx: 10c int 13 硬盤中斷
-xxxx:10e int 20
-xxxx:0110 退出程序返回到指示符
-g 運行
-d500 查看運行後 500 地址的內容

這時候會發現地址 6be 開始的內容是硬盤分區的信息,發現此硬盤的擴展分區指向自己,這就使 dos 或 windows 啓動時查找硬盤邏輯盤進去死循環,在 debug 指示符下用 e 命令修改內存數據 具體如下:

e6be
xx.0 xx.0 xx.0...............
.............................
.......................55 aa
55 aa 表示硬盤有效的標記,不要修改, xx0 表示把以前的數據 "xx" 改成 0

再用硬盤中斷 13 把修改好的數據寫入硬盤就可以了,具體如下:
a:/>debug
a 100 表示修改 100 地址的彙編指令
-xxxx:100 mov ax,0301 寫硬盤一個扇區
-xxxx: 這裏直接按回車
-g 運行
-q 退出

   然後運行 fdisk/mbr (重置硬盤引導扇區的引導程序 ) ,再重新啓動電腦就行了。 怎麼樣?用這種方法處理夠簡單的吧?而且這種方法還有一個好處就是可以保住盤上的 數據!如果你不需要保數據的話,還有更加簡單的處理方法:

   方法二:巧設 bios ,用 dm 解鎖大家知道 dm 軟件是不依賴於主板 bios 的硬盤識別安裝軟件,(所以在不能識別大硬盤的老主板上也可用 dm 來安裝使用大容量硬盤)。就算在 bios 中將硬盤設爲 "none" , dm 也可識別並處理硬盤。首先你要找到和硬盤配套的 dm 軟件(找 js 要或去網上蕩),然後把 dm 拷到一張系統盤上。接上被鎖硬盤,開機,按住 del 鍵,進 cmos 設置,將所有 ide 硬盤設爲 none (這是關鍵所在 ! ),保存設置,重啓動,這時系統即可 " 帶鎖 " 啓動。啓動後運行 dm ,你會發現 dm 可以繞過 bios ,識別出硬盤,選中該硬盤,分區格式化,就 ok 了。這麼簡單?不過這種 方法的弱點是硬盤上的數據將全部丟失。

如何開發自己的操作系統的引導程序?
當你打開計算機時發生了什麼?
  1.電源打開;2.BIOS開始執行;3.引導程序開始執行。
  引導程序的規定:你要有一個普通的二進制文件(COM 格式);大小是512個字節;最後兩個字節一定是0AA55h;它能被載入到內存地址0x7C00。
  工具:
  NASM――是一個免費的彙編工具(有DOS/windows/Linux三種版本)
  PARTCOPY2.0――DOS下可自由往磁盤拷貝數據的軟件
  舉例:
  1.Just hang……
  這個簡單的引導程序只能掛起:
  hang:
   jmp hang
  times 512-($-$$)-2 db 0
  dw 0AA55h
  連接這個引導程序:
  nasm -f bin -o hang.bin hang.asm
  現在你需要一張格式化磁盤,傳送hang.bin到磁盤的引導扇區
  partcopy hang.bin 0 200 -f0
  “0”的意思是指從hang.bin文件的頂端開始傳送
  “200”的意思是指拷貝200個字節
  插入磁盤和重新啓動機器,測試這個引導程序。
  2.一個實模式下的引導程序
  上面的程序非常簡單,下面介紹一個稍微複雜一點的程序。
  bits 16
  org 0x7C00
  start:
  cli;關中斷
  mov ax,0x9000;設置堆棧址:0x90000
  mov ss,ax
  mov sp,0
  sti;開中斷
  l1:push ds
  mov dl,0;
  重新設置磁盤控制器
  mov ax,0
  int 13h
  pop ds
   jc fail
  push es
  mov ax,0x1000;ES:BX=10000
  mov es,ax
  mov bx,0
  mov ah,2;讀磁盤扇區
  mov al,5;讀入5個扇區
  mov cx,2;柱面號=0,扇區號=2
  mov dx, 0;磁頭號=0,驅動器號=0
  int 13h;ES:BX=來自磁盤上的數據
  pop es
  jc l1
  mov ax,0x10000;設置段寄器
  mov es,ax
  mov ds,ax
  push ax
  mov ax,0
  push ax
  retf
  fail:
  jmp fail
  times 512-($-$$)-2 db 0
  dw 0AA55h
  連接這個引導程序:
  nasm -f bin -o boot.bin boot.asm
  傳送boot.bin到磁盤的引導扇區
  partcopy boot.bin 0 200 -f0
  爲了使程序可以看到,在編譯下面程序
  mov ax,1000h;修改段寄存器
  mov ds,ax
  mov es,ax
  mov si,msg;打印 "JIPPIKAYE!"
  call putstr
  hang:;掛起
  jmp hang
  putstr:
  lodsb
  or al,al
  jz short putstrd
  mov ah,0x0E
  mov bx,0x0007
  int 0x10
  jmp putstr
  putstrd:
  retn
  msg db 'JIPPIKAYE!',13,10,0
  連接和傳送:
  nasm -f bin -o boot.bin boot.asm
  partcopy boot.bin 0 200 -f0 200
  在partcopy中最後一個參數“200”意思是指磁盤的偏移地址插入磁盤和重新啓動機器,你會看到“JIPPIKAYE”然後掛起。 


Windows 2000的引導過程

事實上,Windows 2000的引導過程是從安裝時候就已經開始的。

那我們首先從windows 2000的安裝說起。
當windows 2000 setup運行時,它向硬盤上寫入MBR(主引導記錄),同時在這個磁盤驅動器的第一個可引導分區(就是我們在fdisk後激活的分區)寫入引導扇區,引導扇區的內容根據不同的文件系統格式而變化(FAT或者是NTFS)。如果你的機器上曾裝有MS操作系統並建立了引導扇區的話,windows 2000 setup將檢測它要覆蓋的引導扇區是否有效,如果有效的話,windows 2000 setup安裝程序將把引導扇區的內容複製到這個分區的根目錄中的文件bootsect.dos中。Setup程序在寫完引導扇區後,將把windows 2000所用的文件拷貝到硬盤,包括兩個引導文件Ntldr和Ntdetect.com。另外,setup還會在引導分區的根目錄中建立引導菜單文件boot.ini。
例:
[boot loader]
timeout=3
default=multi(0)disk(0)rdisk(0)partition(1)/WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)/WINDOWS="Microsoft Windows "
multi(0)disk(0)rdisk(0)partition(2)/WINDOWS="Windows Server 2000" /fastdetect

這是我的機器上的boot.ini文件,該內容顯示裝了兩個操作系統,win98和win2000,後面的那個參數/fastdetect最常見,是安裝系統時默認的,它的作用是使ntdetect忽略秉性和串行設備的枚舉。Boot.ini文件中的相關參數還有很多,各有不同的功能,因爲與本文沒太大關係,所以不作具體介紹,有興趣的朋友可以到網上找找有關資料。

Windows 2000的啓動:
當你按下機器上的power鍵,計算機就開始啓動了,首先是上電自檢,通過後bios引導計算機去讀取硬盤上的MBR,根據MBR中的信息,找到引導分區,將引導分區內的引導扇區的代碼讀入內存並把控制權交給該代碼。引導扇區代碼的作用是向Windows 2000提供磁盤驅動器(硬盤)的結構和格式信息並且從磁盤根目錄中讀取Ntldr文件,在引導扇區代碼將Ntldr加載到內存後,它把控制權交給Ntldr的入口點。如果引導扇區代碼在根目錄中沒有找到Ntldr文件的話,若文件系統爲FAT格式,則顯示:“Boot:無法找到Ntldr”,若引導文件系統是NTFS格式,則顯示:“NTLDR丟失”。然後,Ntldr使用內建的文件系統代碼從根目錄讀取boot.ini文件(Ntldr內建代碼與引導扇區文件系統代碼不同的是,Ntldr文件系統代碼可以讀取子目錄)。此時,Ntldr清除屏幕,如果boot.ini中存在不止一種引導選項,則顯示引導選擇菜單,如果在boot.ini制定的超時範圍內未有任何動作的話,Ntldr會選擇默認的選項。引導選項確定後,Ntldr加載和執行Ntdetect.com(這是一個使用系統bios進行查詢計算機基本設備和設置信息的16位實模式程序)。然後,Ntldr開始清除屏幕並顯示:“Starting Windows……”進度欄。這個進度欄保持空白,直到Ntldr開始加載引導驅動程序(假如有100個引導驅動程序,則每加載一個文件,進度條增加1%)。在進度條的下面是信息:“For troubleshooting and advanced startup options for windows 2000 , press F8 .”如果此時按下F8鍵,會出現高級啓動菜單,包括:已知的最近正確模式(last known good),安全模式(safe mode),調試模式(debug mode)等等等等。
此後,Ntldr加載合適的內核和HAL映像文件(缺省爲Ntoskrnl.exe和HAL.dll),讀入SYSTEM註冊表hive文件(hive文件是一種包含註冊表子樹的文件)以確定該加載哪些引導驅動程序,加載引導驅動程序,爲Ntoskrnl.exe的執行準備CPU寄存器。之後,Ntldr調用Ntoskrnl.exe並由它開始初始化執行程序子系統並引導系統-啓動(system-start)設備驅動程序,在一系列的初始化工作完成後Ntoskrnl.exe爲系統本機應用程序作準備並運行smss.exe。
smss的主要任務是:初始化註冊表,創建系統環境變量,加載Win32子系統(Win32k.sys)的內核模式部分,啓動子系統進程Crss,啓動登陸進程winlogon。然後,winlogon開始執行其啓動步驟,如創建初始的窗口和桌面對象等等。然後它創建服務控制管理器(SCM)進程(Winnt/System32/Services.exe),它加載所有的標記爲自動啓動(auto-start)的服務程序和設備驅動程序和本機安全驗證子系統(Lsass)進程(Winnt/system32/Lsass.exe)。當一切加載成功且用戶在控制檯成功登陸後,SCM則認爲系統引導成功,註冊表中 已知最近正確配置(HKLM/SYSTEM/select/LastKnownGood)由/CurrentControlSet替代。反之,如果用戶在引導的時候選擇高級菜單中的已知最近正確模式(LastKnownGood)或者加載時驅動程序返回一個嚴重的或者關鍵的錯誤,系統會以LastKnownGood的值作爲CurrentControlSet 的值。
之後,我們便看到了熟悉的桌面。至此,windows 2000的引導過程結束。
限於篇幅,本文只簡單的講述一下windows 2000操作系統引導的大體過程,一些細節方面的東東請看我整理的其他windows 2000操作系統方面的文章。


不同WINDOWS平臺下磁盤邏輯扇區的直接讀寫
一、概述
    在DOS操作系統下,通過BIOS的INT13、DOS的INT25(絕對讀)、INT26(絕對寫)等功能調用實現對磁盤邏輯扇區或物理扇區的讀寫是很方便的,C語言中還有對應上述功能調用的函數:biosdisk、absread和abswrite等。但在WINDOWS操作系統下編寫WIN32應用程序時卻再也不能直接使用上述的中斷調用或函數了。那麼,在WINDOWS操作系統下能不能實現磁盤扇區的直接讀寫呢?如何實現磁盤扇區的讀寫呢?爲了解決這些問題,筆者查閱了一些相關資料後發現,WINDOWS操作系統也提供了讀寫磁盤扇區的方法,只是在不同的版本中有着不同的方式和使用限制。最後,筆者編寫了一個磁盤扇區直接讀寫類,不敢獨專,特提供出來,希望能對大家有所幫助。
    注:這裏INT13表示INT 13H,其它類同。
二、一個讀取軟盤扇區的例子
    WINDOWS操作系統對所有的存儲設備實行了統一管理,而且爲了安全起見,操作系統還不允許在WIN32應用程序(工作在Ring3級)中直接調用中斷功能,如INT13、INT21、INT25、INT26等。但它同時也提供了一些服務來彌補這種缺憾,在WIN95/98中,VWIN32服務就是其中一種。VWIN32服務是通過一個VXD來實現的,它提供了設備IO功能,通過它,使用API函數DeviceIoControl便可以實現WIN32應用程序和磁盤設備驅動程序間的通信,從而實現對磁盤的存取。VWIN32提供的服務是一系列的控制命令字,它們實現諸如DOS操作系統下的INT13、INT25、INT26和INT21等功能調用。下面是它定義的一些控制命令字:
   VWIN32_DIOC_DOS_IOCTL     (1)  實現INT21 功能
   VWIN32_DIOC_DOS_INT25     (2)  實現INT25 功能
   VWIN32_DIOC_DOS_INT26     (3)  實現INT26 功能  
   VWIN32_DIOC_DOS_INT13     (4)  實現INT13 功能
   VWIN32_DIOC_DOS_DRIVEINFO (6)  實現INT21 730x 功能  

如果要對磁盤進行讀寫,只要使用DeviceIoControl執行相應命令即可,下面的例子用來讀取軟盤的一個扇區(使用INT13):
    第一步:打開VWIN32服務,HANDLE hDev=CreateFile("
////.//VWIN32",0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,NULL);
    第二步:填充中斷所用到的相關寄存器。這裏將寄存器放在一個結構中,結構定義如下(有關INT13使用的寄存器情況,請參閱相關資料):
      typedef struct INT13Regs{
  PVOID buffer;   // ebx 寄存器

        BYTE Drive;    // 磁盤號 dl
 BYTE Head;      //磁頭號 dh
 WORD EDX_High;  // edx 寄存器 

 BYTE Sector;    //起始扇區 cl
 BYTE Track;     //磁道號   ch
 WORD ECX_High;  //ecx 寄存器

 BYTE Number;    //要讀寫的扇取數 al
 BYTE CMD;       //命令:2--讀,3--寫,5--格式化 ah
 WORD EAX_High;  //eax 寄存器

 DWORD EDI;       // edi 寄存器
        DWORD ESI;       // esi
        DWORD EFLAG;     // flags
      }INT13_REGISTERS;

      unsigned char Buffer[512];//定義緩衝區,放置讀取扇區數據
      INT13_REGISTERS reg={0};//定義寄存器結構變量
 
      reg.buffer =(void *)Buffer;
      reg.Drive =0;//0-軟盤A  1-軟盤B 0x80-硬盤c
      reg.Head =0;
      reg.Track=0;
      reg.Sector=1;
      reg.Number=1;
      reg.CMD=2;  //讀取
    第三步:調用設備IO API函數DeviceIoControl執行4號命令(即VWIN32_DIOC_DOS_INT13), BOOL b_ret=DeviceIoControl(hDev,4,&reg,sizeof(INT13_REGISTERS),&reg,sizeof(INT13_REGISTERS),&lpRet,0);
如果其返回值不等於零,調用成功,進一步處理....否則調用失敗。
    第四步:關閉服務,CloseHandle(hDev);

三、限制或侷限
    上面是使用INT13讀取軟盤扇區的完整步驟,在WIN95/98下它是可以工作的。那麼,是否將上面的寄存器結構中的Drive置爲0x80就可以讀取邏輯硬盤C盤的扇區了呢?回答是否定的。INT13用來存取硬盤的功能在WINDOWS中被忽略了。另外,INT25、INT26雖然可以存取硬盤,但是它們不能工作在FAT32格式的硬盤上。下面的列表將詳細列舉與磁盤操作相關的中斷調用的限制情況(不特殊說明,指的是在WIN95/98操作系統下):

     中斷功能             限制及使用情況

      INT13             不可以讀寫硬盤,僅支持軟盤
    INT25/INT26         不可以讀/寫FAT32硬盤,支持FAT12、FAT16
   INT21(440DH-41H/61H) 不可用(文檔資料中說支持FAT12、FAT16、FAT32,實際上沒有實現)
   INT21(7305H)         可以讀寫軟盤、硬盤,支持FAT12、FAT16、FAT32,但要求WIN95OSR2及以後版本

    值得一提的是上表中的INT21--7305H功能是專門提供用來支持FAT32的,並且用來替換INT25/INT26,對應的控制命令字是6(即VWIN32_DIOC_DOS_DRIVEINFO),它和INT13、INT25、INT26等中斷功能的一個顯著區別是:它不使用寄存器來傳遞參數(INT21--440DH-41H/61H類同),而是使用一個稱爲DISKIO的結構,寄存器EBX用來保存指向該結構的地址。DISKIO的定義如下:
     typedef struct _DISKIO {
      DWORD  dwStartSector;   // 要讀寫的起始扇區號
      WORD   wSectors;        // 要讀寫的扇區數
      DWORD  dwBuffer;        // 用來保存讀/寫數據的緩衝區
     }DISKIO, * PDISKIO;

另外,在使用該功能時還需要特別設置一些寄存器,如ECX必須爲-1,用ESI來表示讀寫。下面的例子是使用該功能來實現上面的例子功能,即讀軟盤A的一個扇區。首先定義一個新的寄存器結構供本例使用:
     typedef struct _DIOC_REGISTERS{
       DWORD EBX;
       DWORD EDX;
       DWORD ECX;
       DWORD EAX;
       DWORD EDI;
       DWORD ESI;
       DWORD Flags;
      }DIOC_REGISTERS; 

其實該結構和上面的INT13_REGISTERS是一樣的,只不過INT13_REGISTERS將寄存器細分開了,可讀性更強些。本例從步驟上說和上面的例子相同,只有寄存器設置一步在內容上有差異。
    第一步:打開VWIN32服務。
    第二步:設置寄存器。
     DIOC_REGISTERS reg = {0};
     DISKIO         dio;
     unsigned char Buffer[512];
     //設置參數結構
     dio.dwStartSector = 0;//注意:和上例不同,不是1,從0開始編號
     dio.wSectors      = 1;
     dio.dwBuffer      = (DWORD)Buffer;
      //設置寄存器
     reg.EAX = 0x7305;    //功能上類似於INT25,絕對讀
     reg.EBX = (DWORD)&dio;//參數結構的地址
     reg.ECX = -1;//必須是-1     
     reg.EDX = 1;  //注意:和上例不同,驅動器編號變了,0--缺省 1--A、2--B、3--C 
     reg.ESI = 0;  //ESI的bit0表示讀寫,0--讀、1--寫

    在寫狀態時SI的bit1--bit12,bit15必須是0,bit13、bit14、bit15共同來表示所寫數據的類型,具體見下表:
           15 14 13 類型描述
           0  0  0  其它或不知道. 
           0  0  1  FAT數據 
           0  1  0  目錄數據 
           0  1  1  一般數據 
           1  x  x  保留。bit15必須是0 

     第三步:調用API。BOOL b_ret=DeviceIoControl(hDev,6,&reg, sizeof(DIOC_REGISTERS),&reg,sizeof(DIOC_REGISTERS),&cb,0);
     第四步:關閉服務。

可以發現,兩種方法讀到的數據完全一致。

四、WIN2000中的磁盤扇區讀寫
    在WINNT和WIN2000中磁盤被看做一種標準設備,可以使用CreateFile象打開文件一樣打開並存取。CreateFile支持兩種方式的磁盤設備--邏輯磁盤(格式爲"
//./C:")和物理磁盤(格式爲"//./PHYSICALDRIVEx",其中x爲數字),例如打開A:盤進行讀取操作,只要這樣:
     HANDLE hDev=CreateFile("
////.//A:",GENERIC_READ,FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
如果得到的句柄有效,就可以使用ReadFile來讀取了,
     ReadFile(hDev,Buffer,512,&dwRet,0);
讀取結束要關閉該句柄,
     CloseHandle(hDev);
這比WIN95/98下的磁盤扇區讀取方便多了。
    另外,上面的例子是操作邏輯磁盤的,它包括軟驅、硬盤分區等;物理磁盤指的是實際的硬盤,它不關心該硬盤被分成幾個區,硬盤的編號是從0開始的,"
//./PHYSICALDRIVE0"表示第一塊硬盤,其它依此類推。大家可能馬上會想起,利用這種機制可以對硬盤的分區表進行存取了。確實如此,此時便可以對硬盤的主引導扇區(獨立存在的一個扇區,包含分區表信息,不同於磁盤分區的BOOT區)進行操作了。
     unsigned char Buffer[512]={0};
     HANDLE hDev=CreateFile("
////.//PHYSICALDRIVE0",GENERIC_WRITE,FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
     WriteFile(hDev,Buffer,512,&dwRet,0);
     CloseHandle(hDev);

危險!!!千萬別這麼做!!!

五、一個自適應的磁盤讀寫類
   由上面的例子可以看出,不同的操作系統下對磁盤扇區的讀寫有不同的方式,爲了能夠在各類操作系統下能夠使用統一的方法讀寫磁盤扇區,特設計了一個通用類。該類的設計思想如下:首先編寫各類操作系統下的磁盤扇區存取函數,然後通過GetVersionEx來判斷操作系統,進而選取對應的函數來實現磁盤扇區的讀寫。由上面的分析可知,WINDOWS操作系統對INT13的支持是最差的,所以在這裏只使用INT25、INT26、INT21--7305等中斷調用來實現。類的定義如下:
class CDiskInfo{
public:
 CDiskInfo();
 ~CDiskInfo();
private:
 HANDLE hDev;
        DWORD dwCurrentPlatform;
        void GetPlatform();  //取得操作系統,並存入變量dwCurrentPlatform
   
 BOOL Win2000_AccessSectors(WORD CMD,BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);//用於WIN2000、WINNT等操作系統,
 BOOL Int25_ReadSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);
        BOOL Int26_WriteSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);//用於WIN95以前的操作系統
        BOOL Int21_AccessSectors(WORD CMD,BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);//7305功能實現,用於WIN95OSR2、WIN98等操作系統

public:
  //對外統一提供Read和Write操作,類內部根據平臺選用適合的函數調用
 BOOL ReadSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);
 BOOL WriteSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);
};

該類對外提供了兩個接口,即ReadSectors和WriteSectors,其參數是一樣的,分別是要讀寫的磁盤編號bDrive,要存取磁盤的開始扇區號dwStartSector,要讀取的扇區數wSectors和讀寫扇區數據的緩衝區lpSectBuff。這裏磁盤編號是從1開始的,即1代表A:,2代表B:,3代表C:,依此類推。扇區的編號從0開始。使用時也很簡單,只要作如下聲明即可:
        BYTE Buffer[1024];      
  CDiskInfo A;
        BOOL bRet=A.ReadSectors(1,0,2,Buffer);
    詳細情況見附帶的類文件及測試程序。
六、補充說明
    嚴格來說,在對磁盤進行讀寫時,應該遵循以下順序:打開設備(WIN95/98下爲VWIN32服務,WIN2000下爲磁盤設備)、鎖卷、驗證卷的有效性、讀/寫、開鎖卷、關閉設備。這裏爲了描述上的簡潔,忽略了鎖卷/開鎖卷及驗證有效性等操作。有興趣的朋友可以自行添加。
    另外,該類僅實現了邏輯驅動器的讀寫,要想實現諸如對物理硬盤的主引導扇區的讀寫,還需要其它技術,如thunk技術,即編寫兩個動態庫,一個是WIN32動態庫,一個是WIN16動態庫(thunk技術只可以用動態庫實現),其中WIN16動態庫轉到DPMI模式,調用INT13(或者擴展INT13)來實現物理磁盤扇區的讀寫。有關thunk技術請參閱相關文檔資料。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章