Почему необработанное чтение с диска в C # читается со слегка смещенного смещения?

Я пытаюсь прочитать необработанный диск. Я успешно открыл диск и получил действительный дескриптор (CreateFile), установил для этого дескриптора нулевое смещение (SetFilePointerEx) и прочитал данные в буфер Byte[] ( ReadFile). Все идет нормально. Но по какой-то неизвестной причине, когда я сравниваю буфер (мне потребовалось время, чтобы понять это на самом деле) с тем, что показывает сторонняя утилита (Disk Investigator). Он не содержит загрузочной информации (инструкции перехода), но содержит правильные данные на диске, а начинается со смещения 85 = 0x55. Так что где-то посередине тома загрузочная информация.

Это почему? Есть что-то, что я упускаю (очевидно, я)?

Система: Windows 8 (в VmWare Workstation)

Код:

// 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
}

person SmartK8    schedule 03.03.2013    source источник
comment
Я не получил информации о загрузке. Чего вы пытались добиться? Показывает ли ваше чтение данные, отличные от данных утилиты диска с тем же смещением? Может быть, вы открыли раздел в одном, а в другом том?   -  person usr    schedule 03.03.2013
comment
Я просто пытаюсь прочитать необработанный диск. Он должен начинаться с инструкции перехода загрузочного сектора NTFS (0xEB5290 по смещению 0). Но когда я читаю дескриптор из \\. \ PhysicalDrive2, он просто читает со смещения 85. Итак, я не могу прочитать первые 84 байта. Даже если SetFilePointerEx установлен в 0.   -  person SmartK8    schedule 03.03.2013
comment
Мне трудно поверить, что он читает с неравномерного смещения, потому что это сместит весь диск. Вы не сможете прочитать полные сектора диска (скажем, 512 байт) из-за смещения. Это не может быть правдой. Ваша ошибка в другом месте. Возможно, вы неверно истолковываете данные. Можете ли вы показать, что у вас есть и чего вы ожидали?   -  person usr    schedule 03.03.2013
comment
В ПОРЯДКЕ. Я озадачен, как и ты. Когда я использую невыровненное чтение, оно терпит неудачу, как и следовало ожидать, когда я читаю со смещения ‹0, оно также терпит неудачу (как и ожидалось). SetFilePointerEx правильно возвращает 0 (без ошибок). Это загадка. Я предполагаю, что это определение PInvoke для файла чтения или чего-то еще без буфера. Я добавлю код и определения.   -  person SmartK8    schedule 03.03.2013
comment
Я добавил еще код. Извините, это часть большого проекта, поэтому сложно разделить биты (особенно перечисления).   -  person SmartK8    schedule 03.03.2013
comment
Я теперь узнал. Что, наверное, еще хуже. Я проверил последовательность, и она не совпадает. 33 C0 8E D0 BC 00 7C совпадает со смещением 85 в Disk Inspector, но после этого оно другое (для того же тома / диска). Невероятный. Так что буфер потенциально может быть совершенно неправильным. Что за совпадение. Теперь я не знаю, где я нахожусь. Но и SetFilePointerEx, и ReadFile возвращают правильный результат и как минимум количество прочитанных байтов. Что касается данных буфера, я уже не уверен.   -  person SmartK8    schedule 03.03.2013
comment
Может быть, данные взяты из какого-то еще более позднего смещения (например, из раздела 1.)? Просканируйте весь диск для более длинной последовательности.   -  person usr    schedule 03.03.2013


Ответы (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 - это шаблон p / invoke для Win32 API.

Класс 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

}
person metadings    schedule 03.03.2013
comment
Спасибо. Я уже знаю, как это сделать, но он читает со смещения 85 вместо 0. Так что мне не хватает 84 байта. В этом суть проблемы. - person SmartK8; 03.03.2013
comment
@ SmartK8 звучит как деталь. вы используете смещения и длины, кратные BytesPerSector? - person metadings; 03.03.2013
comment
Да. Но данные были странными, поэтому я попытался установить смещение на 0 (чтобы быть уверенным). И ожидал увидеть инструкцию перехода в загрузочный сектор. То, что у меня есть, было 33C0BE ... взамен. Поэтому я использовал Disk Inspector для его поиска. И он был найден по необработанному смещению диска 85. Даже если я установил смещение на 0, и это удалось. - person SmartK8; 03.03.2013
comment
@ SmartK8 будьте осторожны, вы читаете (MBR) вперед, но вам нужно проанализировать его в обратном направлении (это немного с порядком байтов) ... - person metadings; 03.03.2013
comment
Я знаю это, но, к сожалению, мне это не удалось, потому что ReadFile просто не возвращает правильный буфер. Возможно какое-то несоответствие определений. Но тем не менее не подведет. - person SmartK8; 03.03.2013
comment
@ SmartK8 Используйте целые числа без знака! Измените весь Int32 на UInt32 и весь Int64 (long) на UInt64 (ulong), также в определениях p / invoke! - person metadings; 03.03.2013
comment
Хорошо, я попробую. Изменить: ничего, те же результаты. Этот Byte [] - это все нули, и после ReadFile он заполняется согласованными данными (каждый раз они одинаковы). Так что я определенно что-то читаю. Я просто не знаю какой и какой зачет. - person SmartK8; 03.03.2013
comment
Хорошо, я разобрался. Я установил WinHex. Я сначала открыл ручку регулировки громкости. Ситуация была такая же, как и с Disk Inspector. Потом я заметил, что есть физический носитель (диск). Я открыл тот, который сделал, и вуаля те же данные, что и мои. Так что я, наверное, читаю ручку привода, но я должен читать ручку громкости. Что теперь кажется логичным. Вместо этого я попробую ручку регулировки громкости. И я к тебе вернусь. - person SmartK8; 03.03.2013
comment
Вот и все .. (не) проблема решена. Я приму твой ответ, так как он в основном правильный. Проблема заключалась в типе дескриптора ввода. :) - person SmartK8; 03.03.2013
comment
Вы напоминаете мне, как я бьюсь головой о стену всю ночь напролет. но это довольно круто. Благодарность ;) - person metadings; 03.03.2013
comment
@ SmartK8 извините за старый пост, но как вы изменили дескриптор тома, если у меня аналогичная проблема и мне нужен логический дескриптор? - person vipersassassin; 12.07.2017
comment
Как я писал в комментариях, я использовал ручку диска (привода) вместо ручки громкости. Таким образом, он считывает байты с начала диска, а не с начала данного тома. Я просто использовал ручку громкости, то есть C, D и т. Д. Вместо диска (привода) 0,1 и т. Д. Это было автоматически смещено от ручки громкости. - person SmartK8; 14.07.2017