在Windows中直接发送ATA命令到设备?

我正在尝试将ATA命令发送到Windows中的物理磁盘,并获得设备的响应。

注意:在这种情况下,我想发送IDENTIFY DEVICE (0xEC)命令。 该设备将响应一个512字节的数据块。 (特别是我对119字的第0位感兴趣 – 设备支持TRIM命令 )。

我知道我需要使用CreateFile来打开设备:

 handle = CreateFile( "\\.\PhysicalDrive0", GENERIC_READ, FILE_SHARE_READ, nil, // no security attributes OPEN_EXISTING, 0, // flags and attributes nil // no template file ); 

但在此之后,我不知道该怎么做。

我想用[DeviceIoControl][4]发送0xEC

 // const ATACommand_IdentifyDevice = 0xEC; uint bytesReturned = 0; DeviceIoControl(handle, 0xEC, // IO Control Code nil, // input buffer not needed 0, // input buffer is zero bytes @buffer, // output buffer to store the returned 512-bytes 512, // output buffer is 512 bytes long out bytesReturned, nil // not an overlapped operation ); 

但这是完全错误的。 发送给DeviceIoControl的IoControlCode必须是一个有效的IO_CTL,它们是使用macros构build的 :

 #define CTL_CODE(DeviceType, Function, Method, Access) ( ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) ) 

在SDK中,有许多有效的磁盘pipe理控制代码 ,例如:

  • IOCTL_DISK_CREATE_DISK
  • IOCTL_DISK_GET_DRIVE_GEOMETRY
  • IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
  • IOCTL_DISK_GET_PARTITION_INFO
  • IOCTL_STORAGE_QUERY_PROPERTY

但是他们都不是IDENTIFY DEVICE命令,或者返回任何返回的内容。

所以我相信我必须使用一些“原始”的发送命令的方法。


四处搜寻,我偶然发现IOCTL没有logging

 #define DFP_RECEIVE_DRIVE_DATA 0x0007c088 

当你分解IOCTL碎片时,意味着:

 Custom: (0) Device Type: (7) FILE_DEVICE_DISK Required Access: (3) METHOD_NEITHER Custom: (0) Function Code: (34) Transfer Type: (0) 

但是在inputBuffer必须包含什么,它的大小, outputBuffer将包含什么,或者它需要什么地方没有任何文档。 我也不知道functionCode 34(0x22)是什么。


我的问题:如何将原始ATA命令(例如0xEC)发送到ATA设备,并读取其响应?

也可以看看

  • IOCTL_ATA_PASS_THROUGH控制代码
  • IOCTL_ATA_PASS_THROUGH_DIRECT控制代码
  • ATA_PASS_THROUGH_EX结构

回答件

使用ReadWrite访问权限打开驱动器:

 handle = CreateFile( "\\.\PhysicalDrive0", GENERIC_READ or GENERIC_WRITE, // IOCTL_ATA_PASS_THROUGH requires read-write FILE_SHARE_READ, nil, // no security attributes OPEN_EXISTING, 0, // flags and attributes nil // no template file ); 

ATA_PASS_THROUGH_EX结构设置为我们的input缓冲区,以便与IOCTL_ATA_PASS_THROUGH IO控制代码一起使用:

 ATA_PASS_THROUGH_EX inputBuffer; inputBuffer.Length = sizeof(ATA_PASS_THROUGH_EX); inputBuffer.AtaFlags = ATA_FLAGS_DATA_IN; inputBuffer.DataTransferLength = 0; inputBuffer.DataBufferOffset = 0; // todo: put the ATA command (eg 0xEC) somewhere uint inputBufferSize = sizeof(ATA_PASS_THROUGH_EX); 

设置一个输出缓冲区来保存驱动器预期的512字节响应:

 Byte[] outputBuffer = new Byte[512]; uint outputBufferSize = 512; 

调用DeviceIoControl

 int ioControlCode = IOCTL_ATA_PASS_THROUGH; // or maybe IOCTL_ATA_PASS_THROUGH_DIRECT uint bytesReturned = 0; DeviceIoControl(handle, ioControlCode, inputBuffer, inputBufferSize, outputBuffer, outputBufferSize, out bytesReturned, nil // not an overlapped operation ); 

closures文件句柄:

 handle.Close(); 

Solutions Collecting From Web of "在Windows中直接发送ATA命令到设备?"

你需要使用IOCTL_ATA_PASS_THROUGH / IOCTL_ATA_PASS_THROUGH_DIRECT,这些都有很好的记录。 另外,您还需要CreateFile的GENERIC_READ | GENERIC_WRITE访问权限。

请注意,XP SP2以前版本不支持这些功能。 另外,如果你有一个nForce的nvidia驱动程序的MB,你的SATA驱动器将显示为SCSI,你不能使用这个通道。

在某些情况下,SMART IOCTL(例如SMART_RCV_DRIVE_DATA)将在nForce驱动程序上运行。 您可以使用这些来获得IDENTIFY和SMART数据,但没有其他的。

开源smartmontools是开始寻找示例代码的好地方。

编辑:从应用程序交谈到ATA设备的示例。

 EResult DeviceOperationManagerWin::executeATACommandIndirect(ATACommand & Cmd) { const uint32 FillerSize = 0; Utils::ByteBuffer B; B.reserve(sizeof(ATA_PASS_THROUGH_EX) + 4 + Cmd.bufferSize()); ATA_PASS_THROUGH_EX & PTE = * (ATA_PASS_THROUGH_EX *) B.appendPointer(sizeof(ATA_PASS_THROUGH_EX) + FillerSize + Cmd.bufferSize()); uint8 * DataPtr = ((uint8 *) &PTE) + sizeof(ATA_PASS_THROUGH_EX) + FillerSize; memset(&PTE, 0, sizeof(ATA_PASS_THROUGH_EX) + FillerSize); PTE.Length = sizeof(PTE); PTE.AtaFlags = 0; PTE.AtaFlags |= Cmd.requiresDRDY() ? ATA_FLAGS_DRDY_REQUIRED : 0; switch (Cmd.dataDirection()) { case ddFromDevice: PTE.AtaFlags |= ATA_FLAGS_DATA_IN; break; case ddToDevice: PTE.AtaFlags |= ATA_FLAGS_DATA_OUT; memcpy(DataPtr, Cmd.buffer(), Cmd.bufferSize()); break; default: break; } PTE.AtaFlags |= Cmd.is48Bit() ? ATA_FLAGS_48BIT_COMMAND : 0; PTE.AtaFlags |= Cmd.isDMA() ? ATA_FLAGS_USE_DMA : 0; PTE.DataTransferLength = Cmd.bufferSize(); PTE.TimeOutValue = Cmd.timeout(); PTE.DataBufferOffset = sizeof(PTE) + FillerSize; PTE.DataTransferLength = Cmd.bufferSize(); PTE.CurrentTaskFile[0] = Cmd.taskFileIn0().Features; PTE.CurrentTaskFile[1] = Cmd.taskFileIn0().Count; PTE.CurrentTaskFile[2] = Cmd.taskFileIn0().LBALow; PTE.CurrentTaskFile[3] = Cmd.taskFileIn0().LBAMid; PTE.CurrentTaskFile[4] = Cmd.taskFileIn0().LBAHigh; PTE.CurrentTaskFile[5] = Cmd.taskFileIn0().Device; PTE.CurrentTaskFile[6] = Cmd.taskFileIn0().Command; PTE.CurrentTaskFile[7] = 0; if (Cmd.is48Bit()) { PTE.PreviousTaskFile[0] = Cmd.taskFileIn1().Features; PTE.PreviousTaskFile[1] = Cmd.taskFileIn1().Count; PTE.PreviousTaskFile[2] = Cmd.taskFileIn1().LBALow; PTE.PreviousTaskFile[3] = Cmd.taskFileIn1().LBAMid; PTE.PreviousTaskFile[4] = Cmd.taskFileIn1().LBAHigh; PTE.PreviousTaskFile[5] = Cmd.taskFileIn1().Device; PTE.PreviousTaskFile[6] = 0; PTE.PreviousTaskFile[7] = 0; } DWORD BR; if (!DeviceIoControl(FHandle, IOCTL_ATA_PASS_THROUGH, &PTE, B.size(), &PTE, B.size(), &BR, 0)) { FLastOSError = GetLastError(); LOG_W << "ioctl ATA_PT failed for " << Cmd << ": " << FLastOSError << " (" << Utils::describeOSError(FLastOSError) << ")"; return Utils::mapOSError(FLastOSError); } Cmd.taskFileOut0().Error = PTE.CurrentTaskFile[0]; Cmd.taskFileOut0().Count = PTE.CurrentTaskFile[1]; Cmd.taskFileOut0().LBALow = PTE.CurrentTaskFile[2]; Cmd.taskFileOut0().LBAMid = PTE.CurrentTaskFile[3]; Cmd.taskFileOut0().LBAHigh = PTE.CurrentTaskFile[4]; Cmd.taskFileOut0().Device = PTE.CurrentTaskFile[5]; Cmd.taskFileOut0().Status = PTE.CurrentTaskFile[6]; Cmd.taskFileOut1().Error = PTE.PreviousTaskFile[0]; Cmd.taskFileOut1().Count = PTE.PreviousTaskFile[1]; Cmd.taskFileOut1().LBALow = PTE.PreviousTaskFile[2]; Cmd.taskFileOut1().LBAMid = PTE.PreviousTaskFile[3]; Cmd.taskFileOut1().LBAHigh = PTE.PreviousTaskFile[4]; Cmd.taskFileOut1().Device = PTE.PreviousTaskFile[5]; Cmd.taskFileOut1().Status = PTE.PreviousTaskFile[6]; if (Cmd.dataDirection() == ddFromDevice) { memcpy(Cmd.buffer(), DataPtr, Cmd.bufferSize()); } return resOK; } 

编辑:示例没有外部依赖。

IDENTIFY需要一个512字节的数据缓冲区:

 unsigned char Buffer[512 + sizeof(ATA_PASS_THROUGH_EX)] = { 0 }; ATA_PASS_THROUGH_EX & PTE = *(ATA_PASS_THROUGH_EX *) Buffer; PTE.Length = sizeof(PTE); PTE.TimeOutValue = 10; PTE.DataTransferLength = 512; PTE.DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX); 

按照ATA规范设置IDE寄存器。

 IDEREGS * ir = (IDEREGS *) PTE.CurrentTaskFile; ir->bCommandReg = 0xEC; ir->bSectorCountReg = 1; 

IDENTIFY既不是48位也不是DMA,它从设备读取:

 PTE.AtaFlags = ATA_FLAGS_DATA_IN | ATA_FLAGS_DRDY_REQUIRED; 

做的ioctl:

 DeviceIOControl(Handle, IOCTL_ATA_PASS_THROUGH, &PTE, sizeof(Buffer), &PTE, sizeof(Buffer), &BR, 0); 

在这里,你应该插入错误检查,从DeviceIOControl和通过查看IDEREGS设备报告的错误。

获取IDENTIFY数据,假设您已经定义了一个结构IdentifyData

 IdentifyData * IDData = (IdentifyData *) (Buffer + sizeof(ATA_PASS_THROUGH_EX)); 

根据Erik的回答https://stackoverflow.com/a/5071027/15485我写了以下自包含的代码&#x3002; 我在戴尔SSD笔记本电脑上测试了它,并运行了Windows 7。

 // Sending ATA commands directly to device in Windows? // https://stackoverflow.com/questions/5070987/sending-ata-commands-directly-to-device-in-windows #include <Windows.h> #include <ntddscsi.h> // for ATA_PASS_THROUGH_EX #include <iostream> // I have copied the struct declaration from // "IDENTIFY_DEVICE_DATA structure" http://msdn.microsoft.com/en-us/library/windows/hardware/ff559006(v=vs.85).aspx // I think it is better to include the suitable header (MSDN says the header is Ata.h and suggests to include Irb.h) typedef struct _IDENTIFY_DEVICE_DATA { struct { USHORT Reserved1 :1; USHORT Retired3 :1; USHORT ResponseIncomplete :1; USHORT Retired2 :3; USHORT FixedDevice :1; USHORT RemovableMedia :1; USHORT Retired1 :7; USHORT DeviceType :1; } GeneralConfiguration; USHORT NumCylinders; USHORT ReservedWord2; USHORT NumHeads; USHORT Retired1[2]; USHORT NumSectorsPerTrack; USHORT VendorUnique1[3]; UCHAR SerialNumber[20]; USHORT Retired2[2]; USHORT Obsolete1; UCHAR FirmwareRevision[8]; UCHAR ModelNumber[40]; UCHAR MaximumBlockTransfer; UCHAR VendorUnique2; USHORT ReservedWord48; struct { UCHAR ReservedByte49; UCHAR DmaSupported :1; UCHAR LbaSupported :1; UCHAR IordyDisable :1; UCHAR IordySupported :1; UCHAR Reserved1 :1; UCHAR StandybyTimerSupport :1; UCHAR Reserved2 :2; USHORT ReservedWord50; } Capabilities; USHORT ObsoleteWords51[2]; USHORT TranslationFieldsValid :3; USHORT Reserved3 :13; USHORT NumberOfCurrentCylinders; USHORT NumberOfCurrentHeads; USHORT CurrentSectorsPerTrack; ULONG CurrentSectorCapacity; UCHAR CurrentMultiSectorSetting; UCHAR MultiSectorSettingValid :1; UCHAR ReservedByte59 :7; ULONG UserAddressableSectors; USHORT ObsoleteWord62; USHORT MultiWordDMASupport :8; USHORT MultiWordDMAActive :8; USHORT AdvancedPIOModes :8; USHORT ReservedByte64 :8; USHORT MinimumMWXferCycleTime; USHORT RecommendedMWXferCycleTime; USHORT MinimumPIOCycleTime; USHORT MinimumPIOCycleTimeIORDY; USHORT ReservedWords69[6]; USHORT QueueDepth :5; USHORT ReservedWord75 :11; USHORT ReservedWords76[4]; USHORT MajorRevision; USHORT MinorRevision; struct { USHORT SmartCommands :1; USHORT SecurityMode :1; USHORT RemovableMediaFeature :1; USHORT PowerManagement :1; USHORT Reserved1 :1; USHORT WriteCache :1; USHORT LookAhead :1; USHORT ReleaseInterrupt :1; USHORT ServiceInterrupt :1; USHORT DeviceReset :1; USHORT HostProtectedArea :1; USHORT Obsolete1 :1; USHORT WriteBuffer :1; USHORT ReadBuffer :1; USHORT Nop :1; USHORT Obsolete2 :1; USHORT DownloadMicrocode :1; USHORT DmaQueued :1; USHORT Cfa :1; USHORT AdvancedPm :1; USHORT Msn :1; USHORT PowerUpInStandby :1; USHORT ManualPowerUp :1; USHORT Reserved2 :1; USHORT SetMax :1; USHORT Acoustics :1; USHORT BigLba :1; USHORT DeviceConfigOverlay :1; USHORT FlushCache :1; USHORT FlushCacheExt :1; USHORT Resrved3 :2; USHORT SmartErrorLog :1; USHORT SmartSelfTest :1; USHORT MediaSerialNumber :1; USHORT MediaCardPassThrough :1; USHORT StreamingFeature :1; USHORT GpLogging :1; USHORT WriteFua :1; USHORT WriteQueuedFua :1; USHORT WWN64Bit :1; USHORT URGReadStream :1; USHORT URGWriteStream :1; USHORT ReservedForTechReport :2; USHORT IdleWithUnloadFeature :1; USHORT Reserved4 :2; } CommandSetSupport; struct { USHORT SmartCommands :1; USHORT SecurityMode :1; USHORT RemovableMediaFeature :1; USHORT PowerManagement :1; USHORT Reserved1 :1; USHORT WriteCache :1; USHORT LookAhead :1; USHORT ReleaseInterrupt :1; USHORT ServiceInterrupt :1; USHORT DeviceReset :1; USHORT HostProtectedArea :1; USHORT Obsolete1 :1; USHORT WriteBuffer :1; USHORT ReadBuffer :1; USHORT Nop :1; USHORT Obsolete2 :1; USHORT DownloadMicrocode :1; USHORT DmaQueued :1; USHORT Cfa :1; USHORT AdvancedPm :1; USHORT Msn :1; USHORT PowerUpInStandby :1; USHORT ManualPowerUp :1; USHORT Reserved2 :1; USHORT SetMax :1; USHORT Acoustics :1; USHORT BigLba :1; USHORT DeviceConfigOverlay :1; USHORT FlushCache :1; USHORT FlushCacheExt :1; USHORT Resrved3 :2; USHORT SmartErrorLog :1; USHORT SmartSelfTest :1; USHORT MediaSerialNumber :1; USHORT MediaCardPassThrough :1; USHORT StreamingFeature :1; USHORT GpLogging :1; USHORT WriteFua :1; USHORT WriteQueuedFua :1; USHORT WWN64Bit :1; USHORT URGReadStream :1; USHORT URGWriteStream :1; USHORT ReservedForTechReport :2; USHORT IdleWithUnloadFeature :1; USHORT Reserved4 :2; } CommandSetActive; USHORT UltraDMASupport :8; USHORT UltraDMAActive :8; USHORT ReservedWord89[4]; USHORT HardwareResetResult; USHORT CurrentAcousticValue :8; USHORT RecommendedAcousticValue :8; USHORT ReservedWord95[5]; ULONG Max48BitLBA[2]; USHORT StreamingTransferTime; USHORT ReservedWord105; struct { USHORT LogicalSectorsPerPhysicalSector :4; USHORT Reserved0 :8; USHORT LogicalSectorLongerThan256Words :1; USHORT MultipleLogicalSectorsPerPhysicalSector :1; USHORT Reserved1 :2; } PhysicalLogicalSectorSize; USHORT InterSeekDelay; USHORT WorldWideName[4]; USHORT ReservedForWorldWideName128[4]; USHORT ReservedForTlcTechnicalReport; USHORT WordsPerLogicalSector[2]; struct { USHORT ReservedForDrqTechnicalReport :1; USHORT WriteReadVerifySupported :1; USHORT Reserved01 :11; USHORT Reserved1 :2; } CommandSetSupportExt; struct { USHORT ReservedForDrqTechnicalReport :1; USHORT WriteReadVerifyEnabled :1; USHORT Reserved01 :11; USHORT Reserved1 :2; } CommandSetActiveExt; USHORT ReservedForExpandedSupportandActive[6]; USHORT MsnSupport :2; USHORT ReservedWord1274 :14; struct { USHORT SecuritySupported :1; USHORT SecurityEnabled :1; USHORT SecurityLocked :1; USHORT SecurityFrozen :1; USHORT SecurityCountExpired :1; USHORT EnhancedSecurityEraseSupported :1; USHORT Reserved0 :2; USHORT SecurityLevel :1; USHORT Reserved1 :7; } SecurityStatus; USHORT ReservedWord129[31]; struct { USHORT MaximumCurrentInMA2 :12; USHORT CfaPowerMode1Disabled :1; USHORT CfaPowerMode1Required :1; USHORT Reserved0 :1; USHORT Word160Supported :1; } CfaPowerModel; USHORT ReservedForCfaWord161[8]; struct { USHORT SupportsTrim :1; USHORT Reserved0 :15; } DataSetManagementFeature; USHORT ReservedForCfaWord170[6]; USHORT CurrentMediaSerialNumber[30]; USHORT ReservedWord206; USHORT ReservedWord207[2]; struct { USHORT AlignmentOfLogicalWithinPhysical :14; USHORT Word209Supported :1; USHORT Reserved0 :1; } BlockAlignment; USHORT WriteReadVerifySectorCountMode3Only[2]; USHORT WriteReadVerifySectorCountMode2Only[2]; struct { USHORT NVCachePowerModeEnabled :1; USHORT Reserved0 :3; USHORT NVCacheFeatureSetEnabled :1; USHORT Reserved1 :3; USHORT NVCachePowerModeVersion :4; USHORT NVCacheFeatureSetVersion :4; } NVCacheCapabilities; USHORT NVCacheSizeLSW; USHORT NVCacheSizeMSW; USHORT NominalMediaRotationRate; USHORT ReservedWord218; struct { UCHAR NVCacheEstimatedTimeToSpinUpInSeconds; UCHAR Reserved; } NVCacheOptions; USHORT ReservedWord220[35]; USHORT Signature :8; USHORT CheckSum :8; } IDENTIFY_DEVICE_DATA, *PIDENTIFY_DEVICE_DATA; // Taken from smartmontools // Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents // bytes. static void swapbytes(char * out, const char * in, size_t n) { for (size_t i = 0; i < n; i += 2) { out[i] = in[i+1]; out[i+1] = in[i]; } } // Taken from smartmontools // Copies in to out, but removes leading and trailing whitespace. static void trim(char * out, const char * in) { // Find the first non-space character (maybe none). int first = -1; int i; for (i = 0; in[i]; i++) if (!isspace((int)in[i])) { first = i; break; } if (first == -1) { // There are no non-space characters. out[0] = '\0'; return; } // Find the last non-space character. for (i = strlen(in)-1; i >= first && isspace((int)in[i]); i--) ; int last = i; strncpy(out, in+first, last-first+1); out[last-first+1] = '\0'; } // Taken from smartmontools // Convenience function for formatting strings from ata_identify_device void ata_format_id_string(char * out, const unsigned char * in, int n) { bool must_swap = true; #ifdef __NetBSD__ /* NetBSD kernel delivers IDENTIFY data in host byte order (but all else is LE) */ // TODO: Handle NetBSD case in os_netbsd.cpp if (isbigendian()) must_swap = !must_swap; #endif char tmp[65]; n = n > 64 ? 64 : n; if (!must_swap) strncpy(tmp, (const char *)in, n); else swapbytes(tmp, (const char *)in, n); tmp[n] = '\0'; trim(out, tmp); } int main(int argc, char* argv[]) { HANDLE handle = ::CreateFileA( "\\\\.\\PhysicalDrive0", GENERIC_READ | GENERIC_WRITE, //IOCTL_ATA_PASS_THROUGH requires read-write FILE_SHARE_READ, 0, //no security attributes OPEN_EXISTING, 0, //flags and attributes 0 //no template file ); if ( handle == INVALID_HANDLE_VALUE ) { std::cout << "Invalid handle\n"; } // IDENTIFY command requires a 512 byte buffer for data: const unsigned int IDENTIFY_buffer_size = 512; const BYTE IDENTIFY_command_ID = 0xEC; unsigned char Buffer[IDENTIFY_buffer_size + sizeof(ATA_PASS_THROUGH_EX)] = { 0 }; ATA_PASS_THROUGH_EX & PTE = *(ATA_PASS_THROUGH_EX *) Buffer; PTE.Length = sizeof(PTE); PTE.TimeOutValue = 10; PTE.DataTransferLength = 512; PTE.DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX); // Set up the IDE registers as specified in ATA spec. IDEREGS * ir = (IDEREGS *) PTE.CurrentTaskFile; ir->bCommandReg = IDENTIFY_command_ID; ir->bSectorCountReg = 1; // IDENTIFY is neither 48-bit nor DMA, it reads from the device: PTE.AtaFlags = ATA_FLAGS_DATA_IN | ATA_FLAGS_DRDY_REQUIRED; DWORD BR = 0; BOOL b = ::DeviceIoControl(handle, IOCTL_ATA_PASS_THROUGH, &PTE, sizeof(Buffer), &PTE, sizeof(Buffer), &BR, 0); if ( b == 0 ) { std::cout << "Invalid call\n"; } IDENTIFY_DEVICE_DATA * data = (IDENTIFY_DEVICE_DATA *) (Buffer + sizeof(ATA_PASS_THROUGH_EX)); // Nota Bene: I think some endianness control is needed char model[40+1]; ata_format_id_string(model, data->ModelNumber, sizeof(model)-1); char serial[20+1]; ata_format_id_string(serial, data->SerialNumber, sizeof(serial)-1); char firmware[8+1]; ata_format_id_string(firmware, data->FirmwareRevision, sizeof(firmware)-1); std::cout << "ModelNumber: " << model << "\n"; std::cout << "SerialNumber: " << serial << "\n"; std::cout << "FirmwareRevision: " << firmware << "\n"; return 0; } 

您需要IOCTL_ATA_PASS_THROUGH控制代码