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

DeviceIoControl 實戰系列(四):取得硬碟詳細資訊 (序號、韌體、型號、支援模式)

     編輯:組合語言

標準 IOCTL_DISK_GET_DRIVE_GEOMETRY 僅能取得基本幾何參數。若需硬碟出廠序號、韌體版本、型號、支援的傳輸模式 (DMA/PIO/UltraDMA) 等詳細資訊,必須透過 ATA/ATAPI IDENTIFY DEVICE 命令,配合 DeviceIoControl 送至埠驅動程式。本文提供完整 C++ 結構定義、核心邏輯與兩種存取路徑 (IDE 直通 / SCSI Miniport)。

核心常數與結構

// IOCTL 控制碼
#define DFP_SEND_DRIVE_COMMAND   CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS)
#define DFP_RECEIVE_DRIVE_DATA   CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS|FILE_WRITE_ACCESS)
#define IOCTL_SCSI_MINIPORT      0x0004D008
#define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI<<16)+0x0501)

// ATA 命令
#define IDE_ATA_IDENTIFY 0xEC

// IDE 任務檔暫存器
typedef struct _IDEREGS {
  BYTE bFeaturesReg, bSectorCountReg, bSectorNumberReg;
  BYTE bCylLowReg, bCylHighReg, bDriveHeadReg, bCommandReg, bReserved;
} IDEREGS;

// 驅動程式回傳狀態
typedef struct _DRIVERSTATUS {
  BYTE bDriverError, bIDEStatus, bReserved[2];
  DWORD dwReserved[2];
} DRIVERSTATUS;

// 輸入緩衝區
typedef struct _SENDCMDINPARAMS {
  DWORD cBufferSize; IDEREGS irDriveRegs; BYTE bDriveNumber; BYTE bReserved[3];
  DWORD dwReserved[4]; BYTE bBuffer[1];
} SENDCMDINPARAMS;

// 輸出緩衝區
typedef struct _SENDCMDOUTPARAMS {
  DWORD cBufferSize; DRIVERSTATUS DriverStatus; BYTE bBuffer[1];
} SENDCMDOUTPARAMS;

// IDENTIFY DEVICE 回傳資料 (512 bytes, 256 WORDs) - 節錄關鍵欄位
typedef struct _IDINFO {
  USHORT wGenConfig, wNumCyls, wReserved2, wNumHeads, wReserved4, wReserved5, wNumSectorsPerTrack;
  USHORT wVendorUnique[3];
  CHAR   sSerialNumber[20];      // WORD 10-19
  USHORT wBufferType, wBufferSize, wECCSize;
  CHAR   sFirmwareRev[8];        // WORD 23-26
  CHAR   sModelNumber[40];       // WORD 27-46
  // ... 能力位元組、時序、版本、UltraDMA 支援位元組 ...
} IDINFO;

關鍵對齊提醒:WORD 57-58 (CHS 可尋址扇區數) 不滿足 32-bit 對齊,不可定義為單一 ULONG,需拆為 USHORT wCurSectorsLow, wCurSectorsHigh,否則結構偏移錯誤導致資料解析失敗 (Lynn McGuire 原版即犯此錯)。

核心函式實作

1. 開啟裝置

HANDLE OpenDevice(LPCTSTR filename) {
  return CreateFile(filename, GENERIC_READ|GENERIC_WRITE,
    FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
}

2. IDE 直通路徑 (需管理員權限)


BOOL IdentifyDevice(HANDLE hDevice, PIDINFO pIdInfo) {
  PSENDCMDINPARAMS  pSCIP = (PSENDCMDINPARAMS)GlobalAlloc(LMEM_ZEROINIT, sizeof(SENDCMDINPARAMS)-1);
  PSENDCMDOUTPARAMS pSCOP = (PSENDCMDOUTPARAMS)GlobalAlloc(LMEM_ZEROINIT, sizeof(SENDCMDOUTPARAMS)+sizeof(IDINFO)-1);
  pSCIP->irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY;
  pSCIP->cBufferSize = 0;
  pSCOP->cBufferSize = sizeof(IDINFO);
  DWORD dwOut;
  BOOL b = DeviceIoControl(hDevice, DFP_RECEIVE_DRIVE_DATA,
    pSCIP, sizeof(SENDCMDINPARAMS)-1,
    pSCOP, sizeof(SENDCMDOUTPARAMS)+sizeof(IDINFO)-1,
    &dwOut, NULL);
  memcpy(pIdInfo, pSCOP->bBuffer, sizeof(IDINFO));
  GlobalFree(pSCOP); GlobalFree(pSCIP);
  return b;
}

BOOL GetPhysicalDriveInfoInNT(int nDrive, PIDINFO pIdInfo) {
  char szFile[20]; sprintf(szFile, "\\\\.\\PhysicalDrive%d", nDrive);
  HANDLE h = OpenDevice(szFile); if (h==INVALID_HANDLE_VALUE) return FALSE;
  BOOL b = IdentifyDevice(h, pIdInfo);
  if (b) { AdjustString(pIdInfo->sSerialNumber,20); AdjustString(pIdInfo->sModelNumber,40); AdjustString(pIdInfo->sFirmwareRev,8); }
  CloseHandle(h); return b;
}

3. SCSI Miniport 路徑 (不受使用者權限限制)


BOOL IdentifyDeviceAsScsi(HANDLE hDevice, int nDrive, PIDINFO pIdInfo) {
  PSRB_IO_CONTROL pSRBIO = (PSRB_IO_CONTROL)GlobalAlloc(LMEM_ZEROINIT,
    sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDOUTPARAMS)+sizeof(IDINFO)-1);
  PSENDCMDINPARAMS pSCIP = (PSENDCMDINPARAMS)((BYTE*)pSRBIO+sizeof(SRB_IO_CONTROL));
  PSENDCMDOUTPARAMS pSCOP = (PSENDCMDOUTPARAMS)((BYTE*)pSRBIO+sizeof(SRB_IO_CONTROL));
  pSRBIO->HeaderLength = sizeof(SRB_IO_CONTROL);
  pSRBIO->Timeout = 10000;
  pSRBIO->Length = sizeof(SENDCMDOUTPARAMS)+sizeof(IDINFO)-1;
  pSRBIO->ControlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;
  strncpy((char*)pSRBIO->Signature, "SCSIDISK", 8);
  pSCIP->irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY;
  pSCIP->bDriveNumber = nDrive;
  DWORD dwOut;
  BOOL b = DeviceIoControl(hDevice, IOCTL_SCSI_MINIPORT,
    pSRBIO, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1,
    pSRBIO, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDOUTPARAMS)+sizeof(IDINFO)-1,
    &dwOut, NULL);
  memcpy(pIdInfo, pSCOP->bBuffer, sizeof(IDINFO));
  GlobalFree(pSRBIO);
  return b;
}

BOOL GetIdeDriveAsScsiInfoInNT(int nDrive, PIDINFO pIdInfo) {
  // nDrive: 0=PriMaster, 1=PriSlave, 2=SecMaster, 3=SecSlave
  char szFile[20]; sprintf(szFile, "\\\\.\\Scsi%d:", nDrive/2);
  HANDLE h = OpenDevice(szFile); if (h==INVALID_HANDLE_VALUE) return FALSE;
  BOOL b = IdentifyDeviceAsScsi(h, nDrive%2, pIdInfo);
  if (b && pIdInfo->sModelNumber[0]!='\0') {
    AdjustString(pIdInfo->sSerialNumber,20); AdjustString(pIdInfo->sModelNumber,40); AdjustString(pIdInfo->sFirmwareRev,8);
  }
  CloseHandle(h); return b;
}

4. 字串位元組序修正


void AdjustString(char* str, int len) {
  for (int i=0; i=0 && str[i]==' ') str[i--]='\0'; // 右修剪
}

權限與適用性說明

路徑需求權限適用對象備註
PhysicalDrive (IDE 直通)Administrator / SYSTEMIDE/ATA/SATA 硬碟最直接,資訊最全
ScsiX: (SCSI Miniport)一般使用者即可IDE/ATA/SATA (經由 SCSI 層轉譯)繞過 ACL,但 Win 未來版本可能封鎖
  • IDENTIFY PACKET DEVICE (0xA1) 用於 ATAPI 裝置 (光碟機、ZIP、磁帶機),但驅動程式常不支援透傳,實務上難以取得資訊。
  • SCSI、USB、NVMe 介面硬碟不適用此法,需各自專屬 IOCTL (如 IOCTL_SCSI_PASS_THROUGH、IOCTL_STORAGE_QUERY_PROPERTY)。
  • Windows 98/Me 請參考 DiskID32 範例 (使用 VXD/APM)。

安全性警示

ATA 指令集包含 READ SECTORS、WRITE SECTORS、SECURITY ERASE、SLEEP 等具破壞性命令。若擴充此框架發送寫入/安全命令,務必三思,錯誤操作可能導致資料永久遺失或硬碟損壞。

掌握 DeviceIoControl + IDENTIFY DEVICE 模式,即可在應用層建立硬碟資訊檢測、健康監控、資產管理等實用工具。

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