I'm trying to read raw disk. I've successfully opened the drive and obtained valid handle (CreateFile), set offset for that handle to zero (SetFilePointerEx) and read the data to a buffer Byte[]
(ReadFile). So far so good. But for some unknown reason, when I compare the buffer (took my a while to figure it out actually) to what 3rd party utilities (Disk Investigator) shows. It doesn't contain a boot information (jump instruction), but correct disk data, but starting at offset 85 = 0x55. So somewhere in the middle of volume boot information.
Why is that? Is there something, I'm missing (obviously I am)?
System: Windows 8 (in VmWare Workstation)
Code:
// 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 (elsewhere)
driveHandle = CreateFile("\\.\PhysicalDrive1", WinFileAccessMode.GenericRead,
WinFileSharedAccess.All, IntPtr.Zero, WinFileMode.OpenExisting,
WinFileAttribute.None, IntPtr.Zero);
PInvoke methods:
[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);
Enumerations:
public enum WinMoveMethod : uint
{
Begin = 0,
Current = 1,
End = 2
}
Can't find anymore a library using a namespace "DiskLib". I can't test that or completely upload now, even I don't know anymore who wrote that piece of code, but the lines you may find interesting are
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);
}
}
The DeviceIO class is a p/invoke boilerplate for Win32 API.
The DiskInfo class is a wrapper to WMI Win32_DiskDrive
and Win32_DiskPartition
classes.
I used that library once to clone a disk (on Win7). Hope that helps to find the solution.
For completeness and because of the details, the DeviceIO class is throughout using unsigned integers:
/// <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
}