WriteFile() to exFAT backup boot sector has no effect on drive

165 Views Asked by At

here is a short explanation of what I am trying to do:

  • Reading out VolumeSerialNumber of an USB flash drive
  • Increment the VolumeSerialNumber
  • Writing it back to the flash drive.

In general it works fine for USB drives using FAT or FAT32 as filesystem but I can't get it working with filesystem exFAT. As read in the filesystem specifications (https://learn.microsoft.com/en-us/windows/win32/fileio/exfat-specification) I already do some 'special handling' what seems to be necessary for exFAT:

  • Set VolumeFlag/VolumeDirty to true, in Main- and Backup Boot Sector
  • Reading, incrementing and writing back serial number, in Main- and Backup Boot Sector
  • Recalculating and writing back the BootChecksum in the corresponding main and backup checksum sector
  • Setting back the VolumeDirty flag to false for both sectors.

I have a general issue when trying to write into the Backup sectors. When I call ReadFile() after WriteFile() has been called I always get the old value back. The SetFilePointer() seems to be correct as I get the expected values/range from ReadFile() but values don't get changed after calling WriteFile().

This does only happens in the Backup Sectors. Writing inside the Main sectors seems to work. But because of different values in the end in Main- and backup sector Windows prompt me to format the USB drive again.

I am using C# / .Net Framework. Here is a code snipping for maybe a better understanding (not complete).

      [System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int SetFilePointer(
    IntPtr hFile,
    int lDistanceToMove,
    ref int lpDistanceToMoveHigh,
    uint dwMoveMethod);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer,
   uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten,
   [In] ref System.Threading.NativeOverlapped lpOverlapped);


[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadFile(IntPtr hFile, byte[] lpBuffer,
   uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);

[System.Runtime.InteropServices.DllImport("Kernel32.dll", SetLastError = true)]
private extern static IntPtr CreateFile(
    String filename,
    UInt32 desiredAccess,
    UInt32 shareMode,
    IntPtr attributes,
    UInt32 creationDisposition,
    UInt32 flagsAndAttributes,
    IntPtr templateFile);

CloseHandle(...)
....

//example:
IntPtr exFatFileHandle = CreateFile(...);
//Set volume dirty flag...

///Starting with MainBoot
        int tempOut = 0;
        if (SetFilePointer(exFatFileHandle, 0, ref tempOut, 0) == -1)
        {
          return false;
        }

        byte[] bufferMainBoot = new byte[512];
        uint read;

        //reading first sector
        if (!ReadFile(exFatFileHandle, bufferMainBoot , 512, out read, IntPtr.Zero)) 
        {
          return false;
        }

        uint serial = BitConverter.ToUInt32(bufferMainBoot , 100); //get Serial at postion 100 (0x64) and icrement++
        serial++;
        byte[] bytesSerial = BitConverter.GetBytes(serial); //return serial as 4 bytes
         bytesSerial.CopyTo(bufferMainBoot , 100); //change values in sector buffer

        if (SetFilePointer(exFatFileHandle, 0, ref newtmp, 0) == -1) //jump to zero, maybe unnecessary..
        {
          return false;
        }

        if (!WriteFile(exFatFileHandle, bufferMainBoot , 512, out _, ref over)) //write back first sector with changed serial  
        {
          return false;
        }

//Now the same for Backup Boot with different offset at read and write
        int newtmp = 0;
        if (SetFilePointer(exFatFileHandle, 0, ref newtmp, 0) == -1) //jump to zero at first, maybe unnecessary..
        {
          return false;
        }
        if (SetFilePointer(exFatFileHandle, 0x1800, ref newtmp, 0) == -1)//jump to sector 12 (Backup Boot Sector)
        {
          return false;
        }

        byte[] bufferBackupBoot = new byte[512];
        uint read;

        //reading first sector
        if (!ReadFile(exFatFileHandle, bufferBackupBoot , 512, out read, IntPtr.Zero)) 
        {
          return false;
        }

        uint serial = BitConverter.ToUInt32(bufferBackupBoot , 100); //get Serial at postion 100 (0x64) and icrement++
        serial++;
        byte[] bytesSerial = BitConverter.GetBytes(serial); //return serial as 4 byte value
        bytesSerial.CopyTo(bufferBackupBoot , 100); //change values in sector buffer

        if (SetFilePointer(exFatFileHandle, 0, ref newtmp, 0) == -1) //jump to zero at first, maybe unnecessary..
        {
          return false;
        }
        if (SetFilePointer(exFatFileHandle, 0x1800, ref newtmp, 0) == -1)//jump to sector 12 (Backup Boot Sector)
        {
          return false;
        }
        if (!WriteFile(exFatFileHandle, bufferBackupBoot , 512, out _, ref over)) //write back first sector with changed serial  
        {
          return false;
        }

//Same procedure as for calculating and writing Checksum to sector
// 0x1600, sector 11 (checksum sector of MainBoot) and 
// 0x2E00, sector 23 (checksum sector of Backup Boot)
//Considering first 11 sectors for calculating the 32-Bit repeating checksum, should be right according to exFat specs. 

//set volumeDirty back to false...
//CloseHandle(...)

Thanks for any help or suggestions in advance.

EDIT Added Invokes of CreateFile, ReadFile, WriteFile, SetFilePointer..

0

There are 0 best solutions below