问题: 即使在 Win 7 Embedded 上启用 EWF 并使用 Win32 API 的 CreateFile/ReadFile 方法,我也无法获得可重现的文件、卷甚至物理驱动器哈希(例如 SHA1)。看起来(对我来说)ReadFile 正在从 EWF 的 RAM 覆盖中读取,但我需要它来从磁盘中获取字节。
背景: 我们正在将受监管的硬件+软件产品从 Windows XP Embedded (FAT32) 更新到 Windows 7 Embedded (NTFS)。应用程序代码是用 C#.Net 和 VC++ 编写的。
监管要求是能够验证硬盘上存在的所有文件(包括操作系统文件)是提供给监管机构的参考文件的精确副本。监管机构需要能够在硬件设备正常运行(生产中)时执行此验证,而无需停止设备上运行的应用程序。
在 XP Embedded 时代,通过解析 FAT32 文件系统并通过 ReadFile(Win 32 API,kernel32.dll)读取磁盘簇,我们能够在启用 EWF 并在设备运行的情况下获得一致的文件哈希。如果我们将文件作为 FileStream 读取,则哈希值不一致。
我们的操作系统和应用程序部署为不可变的主磁盘映像。我们使用逐字节驱动器克隆硬件从主设备克隆硬盘驱动器。我们还在 Linux 上通过 OSFClone (dd) 进行了克隆。
我们正在尝试在 Windows 7 Embedded (x64) 中重现此(即可验证的文件哈希),这需要操作系统的 NTFS 分区。
测试环境:
- 操作系统是 Windows Embedded Standard 7 EWF 在我们的卷(C:, D:) 上以 RAM 模式启用
- NTFSLastAccessUpdate 已在 SYSTEM\Control\ 下的注册表中设置为“1”
- 在启用 EWF 和通过 ewfmgr 提交的更改之前,从驱动器中删除了所有 Bootstat.dat 文件
以前,为了测试驱动器是否不可变,我做了以下操作:
- 使用 Win 7e 驱动器启动设备并在进行更改后关机(启用 EWF)
- 将 Win7e 硬盘插入 Kali Linux 系统并使用 dd | sha1sum 获取整个驱动器的哈希值
- 插回设备中的 Win7e 驱动器并启动,进行更改(启用 EWF)并重复 dd | sha1sum 再次进入 Kali Linux(例如 dd if=/dev/sda1 | sha1sum),其中 /dev/sda1 是受 EWF 保护的 Windows 分区。
该测试中不同靴子之间的签名匹配。我再次运行上面的测试,但需要一段时间。[编辑:我现在再次运行测试:EWF 正在工作,Linux 在 Win7e 驱动器重新启动之间为 /dev/sda1 返回完全相同的哈希]。NTFSLib 和下面粘贴的测试代码不会重现受 EWF 保护的驱动器的相同签名。
问题: 我们已经尝试使用 CreateFile / ReadFile 方法以及“NtfsLib”(https://github.com/LordMike/NtfsLib)来读取单个文件、卷和 \.\PhysicalDriveN 但我们没有得到可重现的哈希值重启设备。也就是说,我们每次都会为 Windows 文件 (C:\windows\windowsupdate.log) 获得不同的 SHA1 哈希值;我们每次都会为 \.\C: 获得不同的哈希值,并且每次都会为 \.\PhysicalDrive0 获得不同的哈希值。
我正在粘贴下面用于计算签名的 C# 代码。我不介意用另一种语言重写,比如 C 或 C++,只要我能从硬盘中获取原始字节。如果我在读取原始字节时做错了什么,请告诉我。我不需要读取和散列单个文件本身。只要哈希在设备重新启动之间匹配,我就可以读取整个磁盘或整个卷并对其进行哈希处理。散列整个驱动器或卷将满足监管要求。对我来说,我从 ReadFile 获得的字节似乎显示了 EWF 对文件系统的看法。
我将不胜感激任何人可以给我的任何提示。我一直在阅读有关 ReadFile 的各种帖子,但没有找到任何有关 EWF 行为的线索。
我在 Windows 上以管理员身份运行下面的代码,并且我的 app.manifest 设置了 requireAdmin 东西。
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using Microsoft.Win32.SafeHandles;
namespace CSharpReadDisk
{
class DiskReader
{
public const uint GenericRead = 0x80000000;
public const uint FileShareRead = 1;
public const uint FileShareWrite = 2;
public const uint OpenExisting = 3;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern unsafe IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
static extern unsafe bool ReadFile(IntPtr hFile, void* lpBuffer,
uint nNumberOfBytesToRead, uint* lpNumberOfBytesRead, IntPtr lpOverlapped);
[DllImport("kernel32", SetLastError = true)]
static extern unsafe bool CloseHandle(IntPtr hObject);
public unsafe IntPtr Open(string filename)
{
// open the existing file for reading
IntPtr handle = CreateFile(filename, GenericRead, FileShareRead | FileShareWrite, IntPtr.Zero, OpenExisting, 0, IntPtr.Zero);
return handle;
}
public unsafe uint Read(IntPtr handle, byte[] buffer, uint count)
{
uint n = 0;
fixed (byte* p = buffer)
{
if (!(ReadFile(handle, p, count, &n, IntPtr.Zero)))
{
return 0;
}
}
return n;
}
public unsafe bool Close(IntPtr handle)
{
return CloseHandle(handle);
}
}
class Test
{
static void Main(string[] args)
{
DiskReader dr = new DiskReader();
Console.Write("Enter path to drive, volume or file: ");
string path = Console.ReadLine();
IntPtr fh = dr.Open(path);
try
{
SafeFileHandle sfh = new SafeFileHandle(fh, true);
if (sfh.IsInvalid)
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
Console.WriteLine("Enter read buffer size (MB): ");
int bs = Console.Read();
byte[] lpBuffer = new byte[bs * 1024 * 1024];
uint bytesRead = 0;
SHA1Managed sha1 = new SHA1Managed();
while ((bytesRead = dr.Read(fh, lpBuffer, (uint)lpBuffer.Length)) > 0)
{
sha1.TransformBlock(lpBuffer, 0, (int)bytesRead, null, 0);
Console.Write(".");
}
sha1.TransformFinalBlock(lpBuffer, 0, (int)bytesRead);
Console.WriteLine("\nSHA1: {0}", BitConverter.ToString(sha1.Hash));
}
catch (Exception e)
{
Console.WriteLine("An exception occurred:\n HResult: {0}\n Message: {1}\n InnerException: {2}\n Source: {3}\n TargetSite: {4}\n StackTrace: {5}",
e.HResult, e.Message, e.InnerException, e.Source, e.TargetSite, e.StackTrace);
}
dr.Close(fh); // close filehandle
Console.WriteLine("\nPress any key to exit...");
Console.ReadKey();
}
}
}