从物理硬盘读取数据
Posted
技术标签:
【中文标题】从物理硬盘读取数据【英文标题】:Reading Data from a Physical Hard Drive 【发布时间】:2017-08-17 13:49:35 【问题描述】:我正在尝试开发一个程序来查找 2 个连接的未格式化物理驱动器并读取字节。该程序当前以管理员模式运行,因为这是我猜该程序可以看到未格式化的硬盘驱动器的唯一方法。我正在使用 Visual Studio 2015,它在 Windows 7 机器上运行。
问题是它只能读取 512 的倍数(512 是扇区大小)。目前未格式化的硬盘位于磁盘 2 和 3 插槽中(它们都是 SSD)。它首先读取 512 字节(正常工作),如果它是格式化的硬盘驱动器,则不再进行读取。如果它是未格式化的硬盘,它会继续读取更多字节。如果它是硬盘驱动器 A,它会读取接下来的 1024 个字节并且它可以工作(read_amount = 1024)。如果它是硬盘驱动器 B,它会读取接下来的 1025 个字节并且它不起作用(read_amount = 0)。我不确定为什么它不能读取 512/扇区大小的倍数。我的理解是,当您使用 dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL 调用“CreateFile()”函数时,我应该能够读取不是扇区大小倍数的大小(如果您使用 FILE_FLAG_NO_BUFFERING 那么您只能读取 512 的倍数而我没有使用那面旗帜)。请参阅下面的代码。
// Hard_Drive_Read.cpp : 定义控制台应用程序的入口点。
// 该程序假定您有两个未格式化的硬盘驱动器连接到您的计算机。
#include <Windows.h>
#include <io.h>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <iomanip>
using namespace std;
int main(int argc, char *argv[])
if (argc != 3)
cout << "Need to enter 2 arguments" << endl;
exit(0);
int frames_to_process = atoi(argv[2]);
if (frames_to_process < 1)
cout << "invalid argument 2" << endl;
exit(0);
//HANDLE hDisk_A;
//HANDLE hDisk_B;
LPCTSTR dsksrc = L"\\\\.\\PhysicalDrive";
wchar_t dsk[512] = L"";
bool channel_A_found = false;
bool channel_B_found = false;
char frame_header_A[1024];
char frame_header_B[1025];
HANDLE hDisk;
char buff_read[512];
DWORD read_amount = 0;
for (int i = 0; i < 4; i++)
swprintf(dsk, 511, L"%s%d", dsksrc, i);
hDisk = CreateFile(dsk, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDisk == INVALID_HANDLE_VALUE)
printf("%s%d%s", "couldn't open the drive ", i, "\n");
CloseHandle(hDisk);
else
printf("%s%d%s", "successfully open the drive ", i, "\n");
BOOL read_success_1 = ReadFile(hDisk, buff_read, 512, &read_amount, NULL);
cout << "read amount 1 - " << read_amount << endl;
if ((read_success_1 == TRUE) && (read_amount == 512))
if ((buff_read[510] == (char)0x55) && (buff_read[511] == (char)0xAA)) // test for a formatted drive; is there other identifiers?
cout << i << " is a formatted drive" << endl;
else
cout << "Not a formatted drive, trying to find sync " << endl;
ofstream writeBinary_Test;
if (i == 2)
writeBinary_Test.open("file_A_test.bin", ofstream::out | ofstream::binary);
ReadFile(hDisk, frame_header_A, 1024, &read_amount, NULL);
cout << "read amount " << read_amount << endl;
writeBinary_Test.write(frame_header_A, 1024);
writeBinary_Test.close();
else if(i == 3)
writeBinary_Test.open("file_B_test.bin", ofstream::out | ofstream::binary);
ReadFile(hDisk, frame_header_B, 1025, &read_amount, NULL);
cout << "read amount " << read_amount << endl;
writeBinary_Test.write(frame_header_B, 1025);
writeBinary_Test.close();
LARGE_INTEGER distanceToMove;
SetFilePointerEx(hDisk, distanceToMove, NULL, FILE_BEGIN);
else
if (channel_A_found && channel_B_found)
cout << "both drives found" << endl;
break;
if ((channel_A_found == false) || (channel_B_found == false))
cout << "Couldn't Find Hard Drive A or Drive B or Both" << endl;
cout << "Exiting the program" << endl;
exit(0);
CloseHandle(hDisk);
return 0;
最后,我想使用 SetFilePointerEx() 在硬盘驱动器上移动,我的程序必须使用和数据大小(不是 512 的倍数)。因此,我必须能够读取不是 512 倍数的大小。关于如何修复这个程序的任何想法?我是否正确使用了我的标志?
非常感谢任何帮助!
【问题讨论】:
ReadFile
检查FO_NO_INTERMEDIATE_BUFFERING
中的FILE_OBJECT
标志,如果它将检查缓冲区大小和偏移量设置为多个DeviceObject->SectorSize
。 FO_NO_INTERMEDIATE_BUFFERING
标志何时设置? I/O 子系统在CreateFile
中使用FILE_NO_INTERMEDIATE_BUFFERING
选项时设置它但 设备本身也可以设置它。 FileSystem\Raw
设备(当你打开它并处理你的文件对象时它会挂载物理磁盘)总是设置这个标志。
【参考方案1】:
documentation for CreateFile 说:
卷句柄可以根据特定文件系统的判断以非缓存方式打开,即使在 CreateFile 中未指定非缓存选项也是如此。您应该假设所有 Microsoft 文件系统都以非缓存方式打开卷句柄。对文件的非缓存 I/O 的限制也适用于卷。
虽然没有明确说明,但这适用于驱动器和卷。
实际上,这不是问题。编写一个从任意偏移量返回任意数量数据的辅助函数很简单,同时只执行对齐读取。
【讨论】:
谢谢,我想我还得为 SetFilePointerEx() 编写一个辅助函数。理想情况下,我希望一切都表现出 fseek() 和 fread() 的表现。几个问题,1)我不太了解卷和磁盘之间的区别。我假设卷是磁盘的逻辑形式,磁盘访问比卷访问快吗? 2)我还读到了一些人,与 ReadFile() 相比,fread 的性能更好。只要用 ReadFile() 开启缓存还能比 fread 慢吗?fread
是一个 C 运行时库函数,最终调用 ReadFile
来完成实际工作。因为它在用户模式下缓冲数据,所以如果您进行大量小读取,它可能比ReadFile
执行得更好。卷(例如\\.\C:
)和驱动器\\.\PhysicalDrive0
之间的主要区别在于,驱动器代表整个硬盘驱动器,而卷代表单个分区。您还与不同的驱动程序级别交谈,因此语义上可能存在一些差异,我不记得我脑海中的细节。
您当然可以实现自己的 fread
和 fseek
等效项。如果您使用的是 Visual Studio,您甚至可以查看 C 运行时库源代码,了解 Microsoft 是如何做到的。【参考方案2】:
我必须能够读取不是 512 倍数的大小。
这是不可能的。对于磁盘的直接访问,您只能读取和写入扇区大小的倍数。此外,您必须对齐您的读取和写入操作。那就是文件指针必须是扇区大小的倍数。
如果您想提供一个允许任意查找、读取和写入的接口,那么您需要在对齐的原始磁盘访问之上实现自己的缓冲。
【讨论】:
msdn.microsoft.com/en-us/library/cc644950(v=vs.85).aspx 应用程序在处理使用 FILE_FLAG_NO_BUFFERING 打开的文件时必须满足某些要求。以下具体适用: • 文件访问大小,包括 OVERLAPPED 结构中的可选文件偏移量(如果指定),必须是卷扇区大小的整数倍的字节数。例如,如果扇区大小为 512 字节,则应用程序可以请求读取和写入 512、1,024、1,536 或 2,048 字节,但不能请求 335、981 或 7,171 字节。如果你使用 FILE_ATTRIBUTE_NORMAL 会怎样? 缓冲是一个更高级别的功能,您可以在使用卷时获得。直接访问磁盘时,没有缓冲,必须使用对齐访问。 好吧,我最初尝试使用 _open_osfhandle() 函数和 _fdopen() 将 HANDLE 转换为文件指针。它大部分都在工作,但后来我意识到 fseek() 和 fread() 并不总是有效。例如,当我执行的 fseek 不是 512 的倍数(正)时,fread 无法在扇区内开始读取(它总是从扇区的开头开始)。但是,如果它有一个负偏移量,它工作得很好。然后我决定使用这种方法。您是否有从未格式化的硬盘驱动器读取不是扇区大小倍数的字节的想法?我很感激你的cmets 我不确定如何才能使我的答案更清楚。我很抱歉成为坏消息的承担者。但是您将不得不接受对齐访问的需要。如果您需要呈现未对齐的界面,请添加自己的缓冲层。我已经在答案中说了所有这些。我现在又说了一遍。我没有什么要补充的了。以上是关于从物理硬盘读取数据的主要内容,如果未能解决你的问题,请参考以下文章