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.
Add the source file and give a suitable name.
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(LCreateFile() failed!\n);
ErrorMessage(GetLastError());
if(CloseHandle(hVolume) != 0)
wprintf(LhVolume handle was closed successfully!\n);
else
{
wprintf(LFailed to close hVolume handle!\n);
ErrorMessage(GetLastError());
}
exit(1);
}
else
wprintf(LCreateFile() 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(LDeviceIoControl() failed!\n);
ErrorMessage(GetLastError());
if(CloseHandle(hVolume) != 0)
wprintf(LhVolume handle was closed successfully!\n);
else
{
wprintf(LFailed to close hVolume handle!\n);
ErrorMessage(GetLastError());
}
exit(1);
}
else
wprintf(LDeviceIoControl(...,FSCTL_GET_NTFS_VOLUME_DATA,...) is working...\n\n);
wprintf(LVolume Serial Number: 0X%.8X%.8X\n,ntfsVolData.VolumeSerialNumber.HighPart,ntfsVolData.VolumeSerialNumber.LowPart);
wprintf(LThe number of bytes in a cluster: %u\n,ntfsVolData.BytesPerCluster);
wprintf(LThe number of bytes in a file record segment: %u\n,ntfsVolData.BytesPerFileRecordSegment);
wprintf(LThe number of bytes in a sector: %u\n,ntfsVolData.BytesPerSector);
wprintf(LThe number of clusters in a file record segment: %u\n,ntfsVolData.ClustersPerFileRecordSegment);
wprintf(LThe number of free clusters in the specified volume: %u\n,ntfsVolData.FreeClusters);
wprintf(LThe starting logical cluster number of the master file table mirror: 0X%.8X%.8X\n,ntfsVolData.Mft2StartLcn.HighPart, ntfsVolData.Mft2StartLcn.LowPart);
wprintf(LThe starting logical cluster number of the master file table: 0X%.8X%.8X\n,ntfsVolData.MftStartLcn.HighPart, ntfsVolData.MftStartLcn.LowPart);
wprintf(LThe length of the master file table, in bytes: %u\n,ntfsVolData.MftValidDataLength);
wprintf(LThe ending logical cluster number of the master file table zone: 0X%.8X%.8X\n,ntfsVolData.MftZoneEnd.HighPart, ntfsVolData.MftZoneEnd.LowPart);
wprintf(LThe starting logical cluster number of the master file table zone: 0X%.8X%.8X\n,ntfsVolData.MftZoneStart.HighPart, ntfsVolData.MftZoneStart.LowPart);
wprintf(LThe number of sectors: %u\n,ntfsVolData.NumberSectors);
wprintf(LTotal Clusters (used and free): %u\n,ntfsVolData.TotalClusters);
wprintf(LThe 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(LTotal 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(Lmalloc() 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(LDeviceIoControl(...,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(Li\'s count = %u\n, i);
//======================
if(CloseHandle(hVolume) != 0)
wprintf(LhVolume handle was closed successfully!\n);
else
{
wprintf(LFailed 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(LFormatMessage() failed, error %u\n, GetLastError());
//else
// wprintf(LFormatMessage() should be fine!\n);
wprintf(LError code %u:\n %s\n, dwErrCode, szErrString) ;
// This buffer used by FormatMessage()
if(LocalFree(szErrString) != NULL)
wprintf(LFailed to free up the buffer, error %u\n, GetLastError());
//else
// wprintf(LBuffer has been freed\n);
}
Build and run the project. The following screenshot is an output sample.