從 DOS 時代的 BIOS 中斷 INT 13h 到 Windows NT 核心的 DeviceIoControl,磁碟底層存取方式經歷了從實模式到保護模式、從特權指令直接執行到受管核心物件的根本變革。本文對比兩種時代的程式碼實作,解析 Windows 如何封裝硬體操作並提供安全的介面。
INT 13h在實模式下,程式擁有完整硬體控制權。讀取開機磁區 (Cylinder 0, Head 0, Sector 1) 典型程式碼:
char buffer[512];
regs.x.dx = 0; // 驅動器 A: (0=軟碟, 0x80=硬碟)
regs.x.cx = 0x0001; // 柱面 0, 扇區 1 (CH=0, CL=1)
regs.x.bx = FP_OFF(buffer); // 緩衝區偏移
regs.es = FP_SEG(buffer); // 緩衝區段地址
regs.x.ax = 0x0201; // AH=02h 讀取, AL=01h 扇區數
int86x(0x13, ®s, ®s, &sregs); // 呼叫 BIOS 中斷
關鍵點: 直接設定暫存器、分段地址轉換 (FP_OFF/FP_SEG)、攜帶旗標暫存器 (CF=進位旗標表示錯誤)。程式碼簡潔但極度危險,任意存取可破壞檔案系統。
DeviceIoControl 模型Windows 禁止使用者模式直接執行特權指令或存取 I/O 埠。所有硬體操作必須透過核心模式驅動程式,使用者模式以 DeviceIoControl 發送控制碼 (IOCTL) 請求核心代為執行。
DIOC_REGISTERS模擬 x86 暫存器組,用於傳遞 INT 13h 參數給 VWIN32 虛擬裝置驅動程式 (VxD):
#pragma pack(push, 1)
typedef struct _DIOC_REGISTERS {
DWORD reg_ebx;
DWORD reg_edx;
DWORD reg_ecx;
DWORD reg_eax;
DWORD reg_edi;
DWORD reg_esi;
DWORD reg_flags; // 包含 CF (Carry Flag)
} DIOC_REGISTERS;
#pragma pack(pop)
// 1. 開啟 VWIN32 虛擬裝置
HANDLE hDevice = CreateFile("\\\\.\\VWIN32", 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (hDevice == INVALID_HANDLE_VALUE) return FALSE;
// 2. 重置磁碟控制器 (INT 13h AH=00h)
DIOC_REGISTERS reg = {0};
reg.reg_eax = 0x0000; // 功能碼: 重置
reg.reg_edx = driveNumber; // 驅動器號
reg.reg_flags = 0x0001; // 預設 CF=1 (假設錯誤)
DWORD cbReturned;
DeviceIoControl(hDevice, VWIN32_DIOC_DOS_INT13, ®, sizeof(reg), ®, sizeof(reg), &cbReturned, NULL);
// 3. 磁頭定位 (INT 13h AH=0Ch)
reg.reg_eax = 0x0C00; // 功能碼: 尋軌
reg.reg_ecx = (cylinder << 8) | sector; // CH=柱面, CL=扇區
reg.reg_edx = (head << 8) | driveNumber; // DH=磁頭, DL=驅動器
DeviceIoControl(hDevice, VWIN32_DIOC_DOS_INT13, ®, sizeof(reg), ®, sizeof(reg), &cbReturned, NULL);
// 4. 讀取扇區 (INT 13h AH=02h)
reg.reg_eax = 0x0200 | sectorCount; // AH=02h 讀取, AL=扇區數
reg.reg_ecx = (cylinder << 8) | sector;
reg.reg_edx = (head << 8) | driveNumber;
reg.reg_ebx = (DWORD)buffer; // 緩衝區線性地址 (保護模式下為平坦地址)
DeviceIoControl(hDevice, VWIN32_DIOC_DOS_INT13, ®, sizeof(reg), ®, sizeof(reg), &cbReturned, NULL);
if (!cbReturned || (reg.reg_flags & 0x0001)) {
// CF=1 表示錯誤
// 錯誤碼在 reg.reg_eax 低位元組 (AH)
}
| 維度 | DOS INT 13h | Windows DeviceIoControl |
|---|---|---|
| 執行模式 | 實模式 (Ring 0),直接特權指令 | 使用者模式 (Ring 3) → 核心模式 (Ring 0) 切換 |
| 記憶體定址 | 分段地址 (Segment:Offset),需 FP_OFF/FP_SEG 轉換 | 32/64 位元平坦線性地址,直接使用緩衝區指標 |
| 參數傳遞 | CPU 暫存器 (AH/AL/CH/CL/DH/DL/ES:BX) | 結構體 DIOC_REGISTERS 模擬暫存器 |
| 錯誤處理 | 旗標暫存器 CF (Carry Flag),AH=錯誤碼 | 回傳值 FALSE + GetLastError(),或結構體 reg_flags CF 位 |
| 安全性 | 無保護,可任意讀寫實體磁碟 | 需管理員權限,受物件管理器 DACL 控制,可被防毒軟體攔截 |
| 適用場景 | 開機載入器、磁碟工具、病毒 (MBR 感染) | 磁碟備份軟體、分割區管理、取證工具、惡意軟體分析 |
CreateFile 開啟 \\.\PhysicalDriveN 或 \\.\X:,配合 ReadFile/WriteFile + SetFilePointerEx 進行邏輯扇區存取,自動處理檔案系統鎖定與快取一致性。IOCTL_DISK_*、IOCTL_STORAGE_* 等標準控制碼。requireAdministrator,避免全域提權。DWORD vs ULONG_PTR 需嚴格區分,使用 #pragma pack 確保跨編譯器一致。從 INT 13h 到 DeviceIoControl,不僅是 API 風格變更,更反映了作業系統從「信任程式」到「不信任程式、核心仲裁一切」的安全哲學演進。理解這段歷史,有助於在維護舊系統、開發安全工具、分析惡意程式碼時做出正確技術判斷。