如何使用 ATA 命令读取磁盘的特定扇区?
Posted
技术标签:
【中文标题】如何使用 ATA 命令读取磁盘的特定扇区?【英文标题】:How to Read particular sector of a disk using ATA command? 【发布时间】:2015-07-18 21:54:37 【问题描述】:我想在 vc++ 中使用 ATA 命令读取磁盘的特定扇区(MBR 扇区)。我是 VC++ 新手,所以在使用 DeviceIoControl 向磁盘发送命令时遇到问题。我提供了一个代码,用于使用命令读取扇区(0x20)读取扇区。
BOOL status = FALSE;
PATA_PASS_THROUGH_EX pATAData;
DWORD dataSize = sizeof(ATA_PASS_THROUGH_EX) + 512;
BYTE Buffer[sizeof(ATA_PASS_THROUGH_EX) + 512];
DWORD bytescopied = 0;
pATAData = (ATA_PASS_THROUGH_EX*)Buffer;
ZeroMemory(pATAData,dataSize); // clears the buffer
pATAData->Length = sizeof(ATA_PASS_THROUGH_EX);
pATAData->DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX);
pATAData->DataTransferLength = 512;
pATAData->TimeOutValue = 2;
pATAData->CurrentTaskFile[1] = 0x01;
pATAData->CurrentTaskFile[2] = 0x00;
pATAData->CurrentTaskFile[3] = 0x00;
pATAData->CurrentTaskFile[4] = 0x00;
pATAData->AtaFlags =ATA_FLAGS_DATA_IN;
pATAData->CurrentTaskFile[6] = 0x20; // command Read Sector(s)(0x20)
/* sends the command to the device, **hDevice** is device handle*/
status = DeviceIoControl(hDevice, IOCTL_ATA_PASS_THROUGH, pATAData, dataSize,Buffer, dataSize, &bytescopied, NULL );
我无法理解这段代码有什么问题以及我在这里缺少什么,但它不起作用。 我在这里缺少什么?如果 PATA_PASS_THROUGH_EX 结构的参数有问题,请告诉如何读取第一个扇区(MBR)。
【问题讨论】:
你有没有调用 GetLastError 来查看它失败的原因? 不确定这是否适用,因为您没有显示打开设备的代码,但您还是可以看看。 ***.com/questions/3362037/… 为什么会有qt
标签?使用其他标签,您可能会得到更好的帮助,例如:device
、hard-drive
或 sata
。另外,你得到的错误是什么?
是的,我调用 GetLastError。它给出错误 ERROR_INVALID_PARAMETER (0x57)。但我不明白哪个参数是错误的。实际上我是 Windows 编程新手。
固定标签以匹配代码。
【参考方案1】:
感谢大家的帮助。我得到了解决方案。一点我没有注意到的事情。那是Ata Flags。我必须发送多个标志。例如。
pATAData->ataFlags = ATA_FLAGS_48BIT_COMMAND | ATA_FLAGS_DRDY_REQUIRED | ATA_FLAGS_DATA_IN
同时在 PreviousTaskFile[6] 中发送 ATA 命令 Opcode。 需要注意的几件事是 硬盘的块大小是多少?它可能超过 512 字节,特别是如果它是一个大磁盘(如(1TB...4TB))。因此,您需要相应地调整尺寸。您可以通过执行 EC identify ATA 命令查看大小,然后查看生成的数据结构。
【讨论】:
【参考方案2】:感谢你们的帮助。我得到了解决方案。我没有在 CurrentTaskFile 中分配设备句柄。
pATAData->CurrentTaskFile[5] = (UCHAR)hDevice;
但是 IDENTIFY_DEVICE(ECh) 命令在没有这个的情况下成功发送。我不知道这是对还是错,但这是有效的。
【讨论】:
现在我可以读取扇区,但现在我必须读取大型存储磁盘(最大 4 TB)。谁能帮助我如何在 CurrentTaskFile[] 中发送详细信息以读取大空间磁盘。因为 CurrentTaskFile[] 是 UCHAR 类型的数组。如果我想从 1 TB 磁盘读取最后一个扇区。 您需要使用扩展命令 (EXT) 以及 PreviousTaskFile[] 关于 CurrentTaskFile[5] 又名“设备”寄存器。旧的 ATA 规范为“LBA 模式”保留了该寄存器的第 6 位。这意味着您应该设置该位以启用“LBA 模式”,并清除它以使用“CHS 模式”。为简单起见,只需将此寄存器设置为 0x40 即可始终使用 LBA 模式。 IDENTIFY_DEVICE 命令之所以起作用,是因为它不访问任何扇区,所以它不关心 LBA 或 CHS 模式。 谢谢@tchau.dev。您的最后两个 cmets 对我很有帮助。是的,我必须将扩展命令 (EXT) 与 PreviousTaskFile[] 一起用于大磁盘。【参考方案3】:感谢大家的帮助。经过长时间的讨论,我发现我必须为大磁盘发送扩展命令。但现在我正在发送 Read Sector(s) Ext(0x24 oppcode) 命令。
BOOL status = FALSE;
PATA_PASS_THROUGH_EX pATAData;
DWORD dataSize = sizeof(ATA_PASS_THROUGH_EX) + 512;
BYTE Buffer[sizeof(ATA_PASS_THROUGH_EX) + 512];
DWORD bytescopied = 0;
pATAData = (ATA_PASS_THROUGH_EX*)Buffer;
ZeroMemory(pATAData,dataSize); // clears the buffer
pATAData->Length = sizeof(ATA_PASS_THROUGH_EX);
pATAData->DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX);
pATAData->DataTransferLength = 512;
pATAData->TimeOutValue = 2;
pATAData->CurrentTaskFile[0] = 0x00;
pATAData->CurrentTaskFile[1] = 0x01;
pATAData->CurrentTaskFile[2] = 0x01;
pATAData->CurrentTaskFile[3] = 0x00;
pATAData->CurrentTaskFile[4] = 0x00;
pATAData->CurrentTaskFile[5] = 0x40;
pATAData->CurrentTaskFile[7]= 0x00;
pATAData->AtaFlags =ATA_FLAGS_48BIT_COMMAND;
pATAData->PreviousTaskFile[0] = 0x00;
pATAData->PreviousTaskFile[1] = 0x00;
pATAData->PreviousTaskFile[2] = 0x00;
pATAData->PreviousTaskFile[3] = 0x00;
pATAData->PreviousTaskFile[4] = 0x00;
pATAData->PreviousTaskFile[5] = 0x04;
pATAData->PreviousTaskFile[7]= 0x00;
pATAData->CurrentTaskFile[6] = 0x24; // command Read Sector(s) Ext(0x24)
/* sends the command to the device, **hDevice** is device handle*/
status = DeviceIoControl(hDevice, IOCTL_ATA_PASS_THROUGH, pATAData, dataSize,Buffer, dataSize, &bytescopied, NULL );
但是这里有同样的问题。命令成功执行,但它没有读取任何扇区。我找不到任何错误。
【讨论】:
在使用 DeviceIoControl 时需要考虑两种类型的错误。 (1) 第一个是来自 DeviceIoControl 告诉你命令是否成功发送到设备。 (2) 第二个来自设备本身。在这种情况下,SATA 或 PATA 驱动器。每个命令都会返回一个状态,指示该命令是否成功。这将在“状态”寄存器 (CurrentTaskFile[7]) 和“错误”寄存器 (CurrentTaskFile[0]) 中。 “状态”寄存器告诉你是否有错误,“错误”寄存器会告诉你错误的类型。 我没有遇到上述错误。一切看起来都不错,但没有从驱动器中读取缓冲区。 “状态”寄存器 (CurrentTaskFile[7] 和“错误”寄存器 (CurrentTaskFile[0]) 均为零。GetLastError() 重新运行代码 (0x57)(ERROR_INVALID_PARAMETER)。DeviceIoControl 返回 TRUE,但输出缓冲区为空。lpBytesReturned 参数的值是 40(正好是 PATA_PASS_THROUGH_EX 结构的大小)这意味着没有从驱动器中读取数据。 错误寄存器可以为零,但状态寄存器至少应将“设备就绪”位设置为 1(位 6)。不要相信 DeviceIoControl() 返回状态。看起来 GetLastError() 说的是实话。 更正一下,“状态”寄存器应该和“命令”寄存器一样,即 CurrentTaskFile[6]。 没有错误没有结果,我该怎么办。 “状态”寄存器(CurrentTaskFile[7] 和“错误”寄存器(CurrentTaskFile[0])都为零。只有两个寄存器“命令”寄存器为 0x58,“设备”寄存器为 0xE0 有任何返回值无济于事. 我该怎么办。以上是关于如何使用 ATA 命令读取磁盘的特定扇区?的主要内容,如果未能解决你的问题,请参考以下文章