winapi - 在 Windows 上使用 CreateFileW 在物理磁盘上运行 Freepascal 哈希函数

标签 winapi freepascal

我使用 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”,根据 this意思是

"ERROR_IO_DEVICE
1117 (0x45D)
The request could not be performed because of an I/O device error."

但是,我在几个工作磁盘上尝试过,错误仍然存​​在。

所以,我的问题最终是,我到底如何将成功分配的 THandle 传递给哈希函数? (UI 也在 Lazarus forums 上提出了这个问题,但有时我会从那些看不到那里帖子的成员那里得到答案)

最佳答案

您将无法将音量句柄传递给不需要音量句柄的函数。这些是非常特殊的 handle ,对其使用要求非常严格。最重要的是,您必须读取扇区对齐的 block ,并且其大小是扇区大小的倍数。

所以解决方案是让你负责读取数据。将其读入缓冲区,然后将该缓冲区传递给哈希库。这意味着您需要一个可以重复调用来处理新数据的哈希库。所有综合哈希库都将提供此类功能。

共享模式标志通过按位或组合:

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找出扇区大小。并记下文档中的这段文字:

To read or write to the last few sectors of the volume, you must call DeviceIoControl and specify FSCTL_ALLOW_EXTENDED_DASD_IO. This signals the file system driver not to perform any I/O boundary checks on partition read or write calls. Instead, boundary checks are performed by the device driver.

现在您有了缓冲区,您可以读取内容并对其进行哈希处理。

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);

我没有尝试编译或测试此代码。它并不意味着完整或全面。它只是为了向您展示如何解决该问题。

关于winapi - 在 Windows 上使用 CreateFileW 在物理磁盘上运行 Freepascal 哈希函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24171497/

相关文章:

C++ MessageBox (Windows.h) - 它有什么作用,我怎样才能让它不可见(或等效)?

delphi - Lazarus/Free Pascal、Synapse 将文件发送到 TCPBlock Socket

record - 参数化的 new() 需要 "identifier"

exception - 更改 Lazarus 中的异常消息

c++ - 使用重定向标准输入处理子进程中的 kbhit

vba - 在 VBA 中逐像素扫描图像

c++ - Windows 防火墙 C++ API - 如何正确清理 COM 资源?

c++ - 对 MFC 项目中的内存泄漏感到困惑,如果从未调用 _CrtDumpMemoryLeaks(),这些内存泄漏就会消失

delphi - 如何在 Lazarus 中切换 Win32/64 代码生成?

delphi - 如何使用 Indy 在 DNS 中找到 LDAP 服务器?