首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

透過 I/O 端口讀取 IDE 硬碟識別資訊的實作指南

     編輯:關於C++

前言

在早期的 DOS 與 Windows 9x 時代,開發者常需直接操作硬體以獲取硬碟詳細資訊。本文介紹如何透過 I/O 端口指令,直接向 IDE 控制器發送 IDENTIFY DEVICE (0xEC) 命令,並解析返回的 256 字資料區塊,以取得型號、序號、韌體版本及幾何參數等關鍵資訊。雖然現代作業系統已限制應用層直接存取硬體,但此技術對理解底層儲存架構、編寫啟動磁區工具或進行嵌入式開發仍具參考價值。

核心流程解析

1. 環境偵測與控制器就緒等待

程式首先從 BIOS 資料區 (0x40:0x75) 讀取硬碟數量,隨後針對每顆磁碟執行識別流程。關鍵在於輪詢狀態埠 (0x1F7) 的 BSY 位 (Bit 7),確認控制器閒置後,才能安全發送命令。

2. 磁碟選擇與命令發送

透過裝置/磁頭暫存器 (0x1F6) 選擇 Master (0xA0) 或 Slave (0xB0) 裝置,隨後向命令埠 (0x1F7) 寫入 0xEC。此時控制器開始準備識別資料,程式需再次等待 DRQ 位 (Bit 3) 置位,代表資料已就緒可讀。

3. 資料讀取與解析

資料埠 (0x1F0) 以 16 位字元模式連續輸出 256 個字 (512 Bytes)。原始碼中的 GetAscii 函式負責將特定位移範圍的高低位元組轉換為可讀字串,對應 ATA 規格定義的欄位:

  • 位移 10-19:序號 (Serial Number)
  • 位移 23-26:韌體版本 (Firmware Revision)
  • 位移 27-46:產品型號 (Model Number)

其餘位移則包含柱面/磁頭/扇區數、緩衝區大小、ECC 位元組數等幾何與效能參數,程式並行呼叫 BIOS INT 13h (AH=08h) 取得 BIOS 回報的幾何資訊,兩者對比可發現邏輯/物理參數差異。

完整程式碼重構 (Borland C++ 3.1 風格)

#include <dos.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>

char *GetAscii(unsigned int inData[], int offStart, int offEnd);

int main(void) {
    unsigned int diskData[256];
    unsigned int offset;
    int loop, numDrv;
    union REGS regs;
    unsigned int biosCyl[2], biosHead[2], biosSec[2];

    numDrv = peekb(0x40, 0x75);  // BIOS 硬碟計數

    for (loop = 0; loop < numDrv; loop++) {
        // 等待控制器就緒
        while (inp(0x1F7) != 0x50);

        // 選擇裝置:Master=0xA0, Slave=0xB0
        outp(0x1F6, (loop == 0 ? 0xA0 : 0xB0));
        outp(0x1F7, 0xEC);  // IDENTIFY DEVICE

        // 等待資料就緒 (DRQ=1)
        while (inp(0x1F7) != 0x58);

        // 讀取 256 字 (512 Bytes)
        for (offset = 0; offset != 256; offset++)
            diskData[offset] = inpw(0x1F0);

        // 取得 BIOS 幾何參數
        regs.h.ah = 0x08;
        regs.h.dl = 0x80 + loop;
        int86(0x13, ®s, ®s);

        if (!regs.x.cflag) {
            biosHead[loop] = regs.h.dh + 1;
            biosSec[loop]  = regs.h.cl & 0x3F;
            biosCyl[loop]  = ((regs.h.cl & 0xC0) << 2) + regs.h.ch + 2;
        }

        // 輸出結果
        printf("DRIVE %d:\n", loop);
        printf("Model Number______________________: %s\n", GetAscii(diskData, 27, 46));
        printf("Serial Number_____________________: %s\n", GetAscii(diskData, 10, 19));
        printf("Controller Revision Number________: %s\n\n", GetAscii(diskData, 23, 26));
        printf("Able to do Double Word Transfer___: %6s\n", (diskData[48] == 0 ? "No" : "Yes"));
        printf("Controller type___________________: %04X\n", diskData[20]);
        printf("Controller buffer size (bytes)____: %6u\n", diskData[21] * 512);
        printf("Number of ECC bytes transferred___: %6u\n", diskData[22]);
        printf("Number of sectors per interrupt___: %6u\n\n", diskData[47]);

        printf("Hard Disk Reports\n");
        printf("Number of Cylinders (Fixed)_______: %6u\n", diskData[1]);
        printf("Number of Heads___________________: %6u\n", diskData[3]);
        printf("Number of Sectors per Track_______: %6u\n\n", diskData[6]);

        printf("BIOS Reports\n");
        printf("Number of Cylinders_______________: %6u\n", biosCyl[loop]);
        printf("Number of Heads___________________: %6u\n", biosHead[loop]);
        printf("Number of Sectors per Track_______: %6u\n\n", biosSec[loop]);

        printf("Press any key to continue...\n\n");
        getch();
    }
    return 0;
}

char *GetAscii(unsigned int inData[], int offStart, int offEnd) {
    static char retVal[255];
    int loop, loop1;
    for (loop = offStart, loop1 = 0; loop <= offEnd; loop++) {
        retVal[loop1++] = (char)(inData[loop] >> 8);   // High byte
        retVal[loop1++] = (char)(inData[loop] & 0xFF); // Low byte
    }
    retVal[loop1] = '\0';
    return retVal;
}

現代環境下的替代方案

在 Windows NT 核心系列 (含 2000/XP/Vista/7/10/11) 中,應用程式無法直接使用 inp/outp 存取 I/O 端口。開發者應改用:

  • SMART 監控:透過 DeviceIoControl 配合 IOCTL_STORAGE_QUERY_PROPERTY 或 SMART_RCV_DRIVE_DATA 取得識別資料。
  • WMI/CIM:查詢 Win32_DiskDrive 類別的 Model、SerialNumber、FirmwareRevision 等屬性。
  • ATA PASS-THROUGH:以 IOCTL_ATA_PASS_THROUGH_DIRECT 直接下達 ATA 指令,最接近本文做法且受作業系統保護。

總結

本範例展示了經典的「軟體直接驅動硬體」思維,雖已不適用於現代受保護模式作業系統,但其遵循的 ATA/ATAPI 命令協定至今仍是 SATA/SSD 控制器的基礎。理解此流程有助於深入掌握儲存子系統運作、開發韌體更新工具或進行數位鑑識分析。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved