我使用Lazarus 1.2.2和Freepascal 2.6.4。
我有一个名为QuickHash的程序可以散列文件,当我在Linux上运行它时,它也可以用来散列物理磁盘(/ dev / sdXX)。但是,我想添加Windows版本的功能。
我认为要访问物理设备,如磁盘,必须使用CreateFile。具体来说,CreateFileW。
因此,用户单击一个按钮,该按钮扫描计算机中的磁盘并将其列在列表框中。然后解析用户双击的那个(ListBox.GetSelectedText)为字符串' \。\ PhyscialDiskX'并将其分配给字符串变量
strDiskID := getDiskID(Listbox.GetSelectedText);
工作正常。
然后我尝试创建该磁盘的句柄: hSelectedDisk := Windows.CreateFileW(PWideChar(strDiskID), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
基于this article,具体为"您必须同时使用CreateFile()FILE_SHARE_READ和FILE_SHARE_WRITE标志才能访问驱动器"我还尝试了以下其他两种组合:
hSelectedDisk := CreateFileW(PWideChar(strDiskID), GENERIC_READ, FILE_SHARE_READ AND FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
hSelectedDisk := CreateFileW(PWideChar(strDiskID), GENERIC_READ, FILE_SHARE_READ OR FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
所有三个成功分配了句柄。但是,顶部语法和底部语法最终会产生错误(如下所述)。中间选项立即返回零字节文件的默认初始化哈希值,即SHA1的DA39 ...。
我的问题是我无法将该句柄(这是一个整数)传递给Freepascal md5和SHA1单元的SHA1File和MD5FILE函数。他们期望一个文件名,必须是一个字符串。
所以,如果我将strDiskID(' \。\ PhyscialDiskX')传递给它(它完全取消了分配句柄的对象),我确实得到了磁盘活动,程序似乎正在运行。
strDiskHashValue := SHA1Print(SHA1File(strDiskID));
ShowMessage(strDiskHashValue);
CloseHandle(hSelectedDisk);
但即使在像500Mb USB驱动器这样的小型磁盘上运行,也需要很长时间才能返回"运行错误1117"根据{{3}}表示
"ERROR_IO_DEVICE
1117 (0x45D)
The request could not be performed because of an I/O device error."
但是,我已经在几个工作磁盘上尝试过,错误仍在继续。
所以,我的问题,最终,我是如何将成功分配的THandle传递给散列函数的呢? (UI也在this提出了问题,但有时我会从那些看不到帖子的成员那里得到答案。
答案 0 :(得分:4)
您无法将卷句柄传递给不期望卷句柄的函数。这些都是非常特殊的手柄,对使用要求非常严格。其中最重要的是您必须读取扇区对齐的块,其大小是扇区大小的倍数。
因此解决方案是让您负责读取数据。将其读入缓冲区,然后将该缓冲区传递给散列库。这意味着您将需要一个可以重复调用以处理新数据的散列库。所有全面的散列库都将提供此类功能。
共享模式标志与按位或:
组合FILE_SHARE_READ or FILE_SHARE_WRITE
我会像这样创建句柄:
hSelectedDisk := CreateFileW(PWideChar(strDiskID), FILE_READ_DATA,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
首先,我将专注于阅读卷的内容。一旦你能做到这一点,哈希将是常规的。
从评论中可以看出,您在编写散列代码时遇到了一些麻烦。首先,您需要分配一个扇区大小倍数的缓冲区:
var
Buffer: Pointer;
....
GetMem(Buffer, BufferSize);
使用IOCTL_DISK_GET_DRIVE_GEOMETRY
查找扇区大小。并记下文档中的这篇文章:
要读取或写入卷的最后几个扇区,必须调用DeviceIoControl并指定FSCTL_ALLOW_EXTENDED_DASD_IO。这表示文件系统驱动程序不对分区读取或写入调用执行任何I / O边界检查。相反,边界检查由设备驱动程序执行。
现在你有了一个缓冲区,你可以阅读并散列内容。
var
ctx: TSHA1Context;
Digest: TSHA1Digest;
BytesRead: DWORD;
....
SHA1Init(ctx);
repeat
if not ReadFile(hSelectedDisk, Buffer^, BufferSize, BytesRead, nil) then
// handle error, raise exception
SHA1Update(ctx, Buffer^, BytesRead);
until BytesRead < BufferSize;
SHA1Final(ctx, Digest);
我没有尝试编译或测试此代码。它并不意味着完整或全面。它只是为了向您展示如何解决这个问题。