我正在尝试将 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
);
但在此之后,我不知道该怎么做。
我考虑过0xEC
使用发送[DeviceIoControl][4]
:
// 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,它是使用宏构建的:
#define CTL_CODE(DeviceType, Function, Method, Access) (
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
)
查看 SDK,有许多有效的磁盘管理控制代码,例如:
- 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
#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 设备发送原始 ATA 命令(例如 0xEC)并读取其响应?
也可以看看
答案片段
打开具有读写访问权限的驱动器:
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
结构作为我们的输入缓冲区以与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 (e.g. 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
);
关闭文件句柄:
handle.Close();