0

问题: 即使在 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();
        }
    }
}
4

1 回答 1

0

任何有兴趣的人都可以更新,您实际上可以通过 Win32 API 中的 CreateFile/ReadFile 进入 EWF 之下。您必须使用 \.\PhysicalDriveN 句柄,然后读取与您感兴趣的卷相对应的特定字节。您可以使用 Win32 API 也提供的 SetFilePointer 函数来做到这一点。当然,如果您想对整个驱动器进行哈希处理,则不需要读取与卷对应的特定字节。关于这个主题的 MSDN 文档不是很清楚,并且这些文档没有考虑特定的 EWF 用例。

于 2016-07-15T23:38:18.360 回答