Win32 Windows Volume Program and Code Example 22

 

 

 

 

 

Master File Table Program Example 3: Using Non-Windows Types (undocumented)

 

The following program example tries to read the Master File Table using custom made types.

Create a new Win32 console application project and give a suitable project name.

 

Master File Table Program Example 3: Using Non-Windows Types - creating a new Win32 console application project

 

Add the source file and give a suitable name.

 

Master File Table Program Example 3: Using Non-Windows Types - adding a new C++ source file

 

Add the following source code.

 

#include <windows.h>

#include <stdio.h>

#include <winioctl.h>

 

// These types should be stored in separate

// include file, not done here

typedef struct {

      ULONG Type;

      USHORT UsaOffset;

      USHORT UsaCount;

      USN Usn;

} NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER;

 

typedef struct {

      NTFS_RECORD_HEADER Ntfs;

      USHORT SequenceNumber;

      USHORT LinkCount;

      USHORT AttributesOffset;

      USHORT Flags; // 0x0001 = InUse, 0x0002 = Directory

      ULONG BytesInUse;

      ULONG BytesAllocated;

      ULONGLONG BaseFileRecord;

      USHORT NextAttributeNumber;

} FILE_RECORD_HEADER, *PFILE_RECORD_HEADER;

 

typedef enum {

      AttributeStandardInformation = 0x10,

      AttributeAttributeList = 0x20,

      AttributeFileName = 0x30,

      AttributeObjectId = 0x40,

      AttributeSecurityDescriptor = 0x50,

      AttributeVolumeName = 0x60,

      AttributeVolumeInformation = 0x70,

      AttributeData = 0x80,

      AttributeIndexRoot = 0x90,

      AttributeIndexAllocation = 0xA0,

      AttributeBitmap = 0xB0,

      AttributeReparsePoint = 0xC0,

      AttributeEAInformation = 0xD0,

      AttributeEA = 0xE0,

      AttributePropertySet = 0xF0,

      AttributeLoggedUtilityStream = 0x100

} ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE;

 

typedef struct {

      ATTRIBUTE_TYPE AttributeType;

      ULONG Length;

      BOOLEAN Nonresident;

      UCHAR NameLength;

      USHORT NameOffset;

      USHORT Flags; // 0x0001 = Compressed

      USHORT AttributeNumber;

} ATTRIBUTE, *PATTRIBUTE;

 

typedef struct {

      ATTRIBUTE Attribute;

      ULONG ValueLength;

      USHORT ValueOffset;

      USHORT Flags; // 0x0001 = Indexed

} RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE;

 

typedef struct {

      ATTRIBUTE Attribute;

      ULONGLONG LowVcn;

      ULONGLONG HighVcn;

      USHORT RunArrayOffset;

      UCHAR CompressionUnit;

      UCHAR AlignmentOrReserved[5];

      ULONGLONG AllocatedSize;

      ULONGLONG DataSize;

      ULONGLONG InitializedSize;

      ULONGLONG CompressedSize; // Only when compressed

} NONRESIDENT_ATTRIBUTE, *PNONRESIDENT_ATTRIBUTE;

 

typedef struct {

      ULONGLONG CreationTime;

      ULONGLONG ChangeTime;

      ULONGLONG LastWriteTime;

      ULONGLONG LastAccessTime;

      ULONG FileAttributes;

      ULONG AlignmentOrReservedOrUnknown[3];

      ULONG QuotaId; // NTFS 3.0 only

      ULONG SecurityId; // NTFS 3.0 only

      ULONGLONG QuotaCharge; // NTFS 3.0 only

      USN Usn; // NTFS 3.0 only

} STANDARD_INFORMATION, *PSTANDARD_INFORMATION;

 

typedef struct {

      ATTRIBUTE_TYPE AttributeType;

      USHORT Length;

      UCHAR NameLength;

      UCHAR NameOffset;

      ULONGLONG LowVcn;

      ULONGLONG FileReferenceNumber;

      USHORT AttributeNumber;

      USHORT AlignmentOrReserved[3];

} ATTRIBUTE_LIST, *PATTRIBUTE_LIST;

 

typedef struct {

      ULONGLONG DirectoryFileReferenceNumber;

      ULONGLONG CreationTime; // Saved when filename last changed

      ULONGLONG ChangeTime; // ditto

      ULONGLONG LastWriteTime; // ditto

      ULONGLONG LastAccessTime; // ditto

      ULONGLONG AllocatedSize; // ditto

      ULONGLONG DataSize; // ditto

      ULONG FileAttributes; // ditto

      ULONG AlignmentOrReserved;

      UCHAR NameLength;

      UCHAR NameType; // 0x01 = Long, 0x02 = Short

      WCHAR Name[1];

} FILENAME_ATTRIBUTE, *PFILENAME_ATTRIBUTE;

 

// Format the Win32 system error code to string

void ErrorMessage(DWORD dwCode);

 

int wmain(int argc, WCHAR **argv)

{

      HANDLE hVolume;

      LPWSTR lpDrive = L"\\\\.\\c:";

      NTFS_VOLUME_DATA_BUFFER ntfsVolData = {0};

      BOOL bDioControl = FALSE;

      DWORD dwWritten = 0;

      LARGE_INTEGER num;

      LONGLONG total_file_count, i;

      NTFS_FILE_RECORD_INPUT_BUFFER mftRecordInput;

      PNTFS_FILE_RECORD_OUTPUT_BUFFER output_buffer;

     

      hVolume = CreateFile(lpDrive,

            GENERIC_READ | GENERIC_WRITE,

            FILE_SHARE_READ | FILE_SHARE_WRITE,

            NULL,

            OPEN_EXISTING,

            0,

            NULL);

 

      if(hVolume == INVALID_HANDLE_VALUE)

      {

            wprintf(L"CreateFile() failed!\n");

            ErrorMessage(GetLastError());

            if(CloseHandle(hVolume) != 0)

                  wprintf(L"hVolume handle was closed successfully!\n");

            else

            {

                  wprintf(L"Failed to close hVolume handle!\n");

                  ErrorMessage(GetLastError());

            }

            exit(1);

      }

      else

            wprintf(L"CreateFile() is pretty fine!\n");    

 

      // a call to FSCTL_GET_NTFS_VOLUME_DATA returns the structure NTFS_VOLUME_DATA_BUFFER

      bDioControl = DeviceIoControl(hVolume, FSCTL_GET_NTFS_VOLUME_DATA, NULL, 0, &ntfsVolData,

            sizeof(NTFS_VOLUME_DATA_BUFFER), &dwWritten, NULL);

 

      if(bDioControl == 0)

      {

            wprintf(L"DeviceIoControl() failed!\n");

            ErrorMessage(GetLastError());

            if(CloseHandle(hVolume) != 0)

                  wprintf(L"hVolume handle was closed successfully!\n");

            else

            {

                  wprintf(L"Failed to close hVolume handle!\n");

                  ErrorMessage(GetLastError());

            }

            exit(1);

      }

      else

            wprintf(L"DeviceIoControl(...,FSCTL_GET_NTFS_VOLUME_DATA,...) is working...\n\n");

     

      wprintf(L"Volume Serial Number: 0X%.8X%.8X\n",ntfsVolData.VolumeSerialNumber.HighPart,ntfsVolData.VolumeSerialNumber.LowPart);

      wprintf(L"The number of bytes in a cluster: %u\n",ntfsVolData.BytesPerCluster);

      wprintf(L"The number of bytes in a file record segment: %u\n",ntfsVolData.BytesPerFileRecordSegment);

      wprintf(L"The number of bytes in a sector: %u\n",ntfsVolData.BytesPerSector);

      wprintf(L"The number of clusters in a file record segment: %u\n",ntfsVolData.ClustersPerFileRecordSegment);

      wprintf(L"The number of free clusters in the specified volume: %u\n",ntfsVolData.FreeClusters);

      wprintf(L"The starting logical cluster number of the master file table mirror: 0X%.8X%.8X\n",ntfsVolData.Mft2StartLcn.HighPart, ntfsVolData.Mft2StartLcn.LowPart);

      wprintf(L"The starting logical cluster number of the master file table: 0X%.8X%.8X\n",ntfsVolData.MftStartLcn.HighPart, ntfsVolData.MftStartLcn.LowPart);

      wprintf(L"The length of the master file table, in bytes: %u\n",ntfsVolData.MftValidDataLength);

      wprintf(L"The ending logical cluster number of the master file table zone: 0X%.8X%.8X\n",ntfsVolData.MftZoneEnd.HighPart, ntfsVolData.MftZoneEnd.LowPart);

      wprintf(L"The starting logical cluster number of the master file table zone: 0X%.8X%.8X\n",ntfsVolData.MftZoneStart.HighPart, ntfsVolData.MftZoneStart.LowPart);

      wprintf(L"The number of sectors: %u\n",ntfsVolData.NumberSectors);

      wprintf(L"Total Clusters (used and free): %u\n",ntfsVolData.TotalClusters);

      wprintf(L"The number of reserved clusters: %u\n\n",ntfsVolData.TotalReserved);

 

      num.QuadPart = 1024; // 1024 or 2048

     

      // We divide the MftValidDataLength (Master file table length) by 1024 to find

      // the total entry count for the MFT

      total_file_count = (ntfsVolData.MftValidDataLength.QuadPart/num.QuadPart);

     

      wprintf(L"Total file count = %u\n", total_file_count);

 

      for(i = 0; i < total_file_count;i++)

      {

            mftRecordInput.FileReferenceNumber.QuadPart = i;

 

            // prior to calling the DeviceIoControl() we need to load

            // an input record with which entry number we want

 

            // setup outputbuffer - FSCTL_GET_NTFS_FILE_RECORD depends on this

            output_buffer = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)malloc(sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER)+ntfsVolData.BytesPerFileRecordSegment-1);

 

            if(output_buffer == NULL)

            {

                  wprintf(L"malloc() failed - insufficient memory!\n");

                  ErrorMessage(GetLastError());

                  exit(1);

            }

 

            bDioControl = DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD,&mftRecordInput,

                  sizeof(mftRecordInput), output_buffer,

                  sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER) + (sizeof(ntfsVolData.BytesPerFileRecordSegment)- 1), &dwWritten, NULL);

 

            // More data will make DeviceIoControl() fails...

            /*if(bDioControl == 0)

            {

                  wprintf(L"DeviceIoControl(...,FSCTL_GET_NTFS_FILE_RECORD,...) failed!\n");

                  ErrorMessage(GetLastError());

                  exit(1);

            }*/

 

            // FSCTL_GET_NTFS_FILE_RECORD retrieves one MFT entry

            // FILE_RECORD_HEADER is the Base struct for the MFT entry

            // that we will work from

            PFILE_RECORD_HEADER p_file_record_header =       (PFILE_RECORD_HEADER)output_buffer->FileRecordBuffer;

      }

     

      // Let verify

      wprintf(L"i\'s count = %u\n", i);

 

      //======================

      if(CloseHandle(hVolume) != 0)

            wprintf(L"hVolume handle was closed successfully!\n");

      else

      {

            wprintf(L"Failed to close hVolume handle!\n");

            ErrorMessage(GetLastError());

      }

      // De-allocate the memory by malloc()

      free(output_buffer);

 

      return 0;

}

 

void ErrorMessage(DWORD dwCode)

{

    // get the error code...

    DWORD dwErrCode = dwCode;

      DWORD dwNumChar;

 

    LPWSTR szErrString = NULL;  // will be allocated and filled by FormatMessage

 

    dwNumChar = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |

                 FORMAT_MESSAGE_FROM_SYSTEM, // use windows internal message table

                 0,         // 0 since source is internal message table

                 dwErrCode, // this is the error code number

                 0,         // auto-determine language to use

                 (LPWSTR)&szErrString, // the messsage

                 0,                    // min size for buffer

                 0 );                  // since getting message from system tables

      if(dwNumChar == 0)

            wprintf(L"FormatMessage() failed, error %u\n", GetLastError());

      //else

      //    wprintf(L"FormatMessage() should be fine!\n");

 

     wprintf(L"Error code %u:\n  %s\n", dwErrCode, szErrString) ;

 

      // This buffer used by FormatMessage()

    if(LocalFree(szErrString) != NULL)

            wprintf(L"Failed to free up the buffer, error %u\n", GetLastError());

      //else

      //    wprintf(L"Buffer has been freed\n");

  }

 

Build and run the project. The following screenshot is an output sample.

 

Master File Table Program Example 3: Using Non-Windows Types - a sample output with the number of deleted files found in the MFT

 

 

 

  < Windows Volume 21 | Win32 Programming Index | Windows Volume Index | Windows Volume 23 >