为啥在 C# 中读取的原始磁盘从稍微偏移的偏移中读取?
Posted
技术标签:
【中文标题】为啥在 C# 中读取的原始磁盘从稍微偏移的偏移中读取?【英文标题】:Why raw disk read in C# reads from slightly shifted offset?为什么在 C# 中读取的原始磁盘从稍微偏移的偏移中读取? 【发布时间】:2013-03-03 11:43:32 【问题描述】:我正在尝试读取原始磁盘。我已成功打开驱动器并获得有效句柄 (CreateFile),将该句柄的偏移量设置为零 (SetFilePointerEx) 并将数据读取到缓冲区 Byte[]
( 读取文件)。到现在为止还挺好。但是由于某些未知原因,当我将缓冲区(实际上花了我一段时间才弄清楚)与 3rd 方实用程序(Disk Investigator)显示的内容进行比较时。它不包含引导信息(跳转指令),但正确的磁盘数据,但从偏移量 85 = 0x55 开始。所以在卷启动信息中间的某个地方。
这是为什么呢?有什么东西,我失踪了(显然我是)?
系统:Windows 8(在 VmWare 工作站中)
代码:
// moves the pointer to a given offset
Int64 offset = 0;
Int64 newOffset;
Int32 bufferSize = (Int32) clusterSizeInBytes;
SetFilePointerEx(driveHandle.Handle, offset, out newOffset, WinMoveMethod.Begin);
Int32 error = Marshal.GetLastWin32Error();
// reads the raw buffer
Int32 numberOfBytesRead;
rawData = new Byte[bufferSize];
Boolean result = ReadFile(driveHandle.Handle, rawData, bufferSize,
out numberOfBytesRead, IntPtr.Zero);
CreateFile(其他地方)
driveHandle = CreateFile("\\.\PhysicalDrive1", WinFileAccessMode.GenericRead,
WinFileSharedAccess.All, IntPtr.Zero, WinFileMode.OpenExisting,
WinFileAttribute.None, IntPtr.Zero);
PInvoke 方法:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Boolean SetFilePointerEx(
[In] SafeFileHandle fileHandle,
[In] Int64 distanceToMove,
[Out] out Int64 newOffset,
[In] WinMoveMethod moveMethod);
[DllImport("kernel32", SetLastError = true)]
public extern static Boolean ReadFile(
[In] SafeFileHandle handle,
[Out] Byte[] buffer,
[In] Int32 numBytesToRead,
[Out] out Int32 numBytesRead,
[In] IntPtr overlapped);
枚举:
public enum WinMoveMethod : uint
Begin = 0,
Current = 1,
End = 2
【问题讨论】:
我没有得到关于启动信息的部分。你试图达到什么目的?您的读取是否显示与磁盘工具不同的数据在相同的偏移量?也许您在一个中打开了一个分区,在另一个中打开了一个卷? 我只是想读取原始磁盘。它应该以 NTFS 引导扇区的跳转指令(偏移 0 处的 0xEB5290)开始。但是当我从 \\.\PhysicalDrive2 读取句柄时,它只是从偏移量 85 读取。所以我无法读取前 84 个字节。即使 SetFilePointerEx 设置为 0。 我很难相信它是从不均匀的偏移量中读取的,因为这会移动整个磁盘。由于偏移量,您将无法读取完整的磁盘扇区(例如 512 字节)。那不可能是真的。您的错误在其他地方。可能你误解了数据。你能展示你得到什么和你期望什么吗? 好的。我和你一样困惑。当我使用非对齐读取时,它会像预期的那样失败,当我从偏移量 我添加了更多代码。抱歉,它是大型项目的一部分,因此很难将位(尤其是枚举)分开。 【参考方案1】:找不到使用命名空间“DiskLib”的库。我现在无法测试或完全上传,即使我不再知道是谁编写了那段代码,但你可能会觉得有趣的行是
public class DiskStream : Stream
public const int DEFAULT_SECTOR_SIZE = 512;
private const int BUFFER_SIZE = 4096;
private string diskID;
private DiskInfo diskInfo;
private FileAccess desiredAccess;
private SafeFileHandle fileHandle;
public DiskInfo DiskInfo
get return this.diskInfo;
public uint SectorSize
get return this.diskInfo.BytesPerSector;
public DiskStream(string diskID, FileAccess desiredAccess)
this.diskID = diskID;
this.diskInfo = new DiskInfo(diskID);
this.desiredAccess = desiredAccess;
// if desiredAccess is Write or Read/Write
// find volumes on this disk
// lock the volumes using FSCTL_LOCK_VOLUME
// unlock the volumes on Close() or in destructor
this.fileHandle = this.openFile(diskID, desiredAccess);
private SafeFileHandle openFile(string id, FileAccess desiredAccess)
uint access;
switch (desiredAccess)
case FileAccess.Read:
access = DeviceIO.GENERIC_READ;
break;
case FileAccess.Write:
access = DeviceIO.GENERIC_WRITE;
break;
case FileAccess.ReadWrite:
access = DeviceIO.GENERIC_READ | DeviceIO.GENERIC_WRITE;
break;
default:
access = DeviceIO.GENERIC_READ;
break;
SafeFileHandle ptr = DeviceIO.CreateFile(
id,
access,
DeviceIO.FILE_SHARE_READ,
IntPtr.Zero,
DeviceIO.OPEN_EXISTING,
DeviceIO.FILE_FLAG_NO_BUFFERING | DeviceIO.FILE_FLAG_WRITE_THROUGH,
IntPtr.Zero);
if (ptr.IsInvalid)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
return ptr;
public override bool CanRead
get
return (this.desiredAccess == FileAccess.Read || this.desiredAccess == FileAccess.ReadWrite) ? true : false;
public override bool CanWrite
get
return (this.desiredAccess == FileAccess.Write || this.desiredAccess == FileAccess.ReadWrite) ? true : false;
public override bool CanSeek
get
return true;
public override long Length
get return (long)this.Length;
public ulong LengthU
get return this.diskInfo.Size;
public override long Position
get
return (long)PositionU;
set
PositionU = (ulong)value;
public ulong PositionU
get
ulong n = 0;
if (!DeviceIO.SetFilePointerEx(this.fileHandle, 0, out n, (uint)SeekOrigin.Current))
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
return n;
set
if (value > (this.LengthU - 1))
throw new EndOfStreamException("Cannot set position beyond the end of the disk.");
ulong n = 0;
if (!DeviceIO.SetFilePointerEx(this.fileHandle, value, out n, (uint)SeekOrigin.Begin))
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
public override void Flush()
// not required, since FILE_FLAG_WRITE_THROUGH and FILE_FLAG_NO_BUFFERING are used
//if (!Unmanaged.FlushFileBuffers(this.fileHandle))
// Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
public override void Close()
if (this.fileHandle != null)
DeviceIO.CloseHandle(this.fileHandle);
this.fileHandle.SetHandleAsInvalid();
this.fileHandle = null;
base.Close();
public override void SetLength(long value)
throw new NotSupportedException("Setting the length is not supported with DiskStream objects.");
public override int Read(byte[] buffer, int offset, int count)
return (int)Read(buffer, (uint)offset, (uint)count);
public unsafe uint Read(byte[] buffer, uint offset, uint count)
uint n = 0;
fixed (byte* p = buffer)
if (!DeviceIO.ReadFile(this.fileHandle, p + offset, count, &n, IntPtr.Zero))
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
return n;
public override void Write(byte[] buffer, int offset, int count)
Write(buffer, (uint)offset, (uint)count);
public unsafe void Write(byte[] buffer, uint offset, uint count)
uint n = 0;
fixed (byte* p = buffer)
if (!DeviceIO.WriteFile(this.fileHandle, p + offset, count, &n, IntPtr.Zero))
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
public override long Seek(long offset, SeekOrigin origin)
return (long)SeekU((ulong)offset, origin);
public ulong SeekU(ulong offset, SeekOrigin origin)
ulong n = 0;
if (!DeviceIO.SetFilePointerEx(this.fileHandle, offset, out n, (uint)origin))
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
return n;
public uint ReadSector(DiskSector sector)
return this.Read(sector.Data, 0, sector.SectorSize);
public void WriteSector(DiskSector sector)
this.Write(sector.Data, 0, sector.SectorSize);
public void SeekSector(DiskSector sector)
this.Seek(sector.Offset, SeekOrigin.Begin);
DeviceIO 类是 Win32 API 的 p/invoke 样板。
DiskInfo 类是 WMI Win32_DiskDrive
和 Win32_DiskPartition
类的包装器。
我曾经使用该库克隆磁盘(在 Win7 上)。希望这有助于找到解决方案。
出于完整性和细节考虑,DeviceIO 类始终使用无符号整数:
/// <summary>
/// P/Invoke wrappers around Win32 functions and constants.
/// </summary>
internal partial class DeviceIO
#region Constants used in unmanaged functions
public const uint FILE_SHARE_READ = 0x00000001;
public const uint FILE_SHARE_WRITE = 0x00000002;
public const uint FILE_SHARE_DELETE = 0x00000004;
public const uint OPEN_EXISTING = 3;
public const uint GENERIC_READ = (0x80000000);
public const uint GENERIC_WRITE = (0x40000000);
public const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
public const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
public const uint FILE_READ_ATTRIBUTES = (0x0080);
public const uint FILE_WRITE_ATTRIBUTES = 0x0100;
public const uint ERROR_INSUFFICIENT_BUFFER = 122;
#endregion
#region Unamanged function declarations
[DllImport("kernel32.dll", SetLastError = true)]
public static unsafe extern SafeFileHandle CreateFile(
string FileName,
uint DesiredAccess,
uint ShareMode,
IntPtr SecurityAttributes,
uint CreationDisposition,
uint FlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(SafeFileHandle hHandle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool DeviceIoControl(
SafeFileHandle hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
[Out] IntPtr lpOutBuffer,
uint nOutBufferSize,
ref uint lpBytesReturned,
IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern unsafe bool WriteFile(
SafeFileHandle hFile,
byte* pBuffer,
uint NumberOfBytesToWrite,
uint* pNumberOfBytesWritten,
IntPtr Overlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern unsafe bool ReadFile(
SafeFileHandle hFile,
byte* pBuffer,
uint NumberOfBytesToRead,
uint* pNumberOfBytesRead,
IntPtr Overlapped);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetFilePointerEx(
SafeFileHandle hFile,
ulong liDistanceToMove,
out ulong lpNewFilePointer,
uint dwMoveMethod);
[DllImport("kernel32.dll")]
public static extern bool FlushFileBuffers(
SafeFileHandle hFile);
#endregion
【讨论】:
谢谢。我已经知道该怎么做,但是它从偏移量 85 而不是 0 读取。所以我缺少 84 个字节。这是问题的核心。 @SmartK8 听起来很详细。你使用的偏移量和长度是 BytesPerSector 的倍数吗? 是的,我愿意。但是数据很奇怪,所以我尝试将偏移量设置为 0(可以肯定)。并期望看到引导扇区的跳转指令。我得到的是 33C0BE.. 代替。所以我使用了 Disk Inspector 来搜索它。它是在原始磁盘偏移量 85 处找到的。即使我将偏移量设置为 0,它也成功了。 @SmartK8 注意你读(MBR)向前但你需要向后解析它(它是小端)... 就是这样..(非)问题已解决。我会接受你的回答,因为它基本上是正确的。问题是输入句柄的类型。 :)以上是关于为啥在 C# 中读取的原始磁盘从稍微偏移的偏移中读取?的主要内容,如果未能解决你的问题,请参考以下文章