Master File Table Program Example 2: Reading and Dumping the Deleted Files
The following program example tries to read the file record header from Master File Table.
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>
typedef struct {
ULONG Type;
USHORT UsaOffset;
USHORT UsaCount;
USN Usn;
} NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER;
// Type needed for interpreting the MFT-records
typedef struct {
NTFS_RECORD_HEADER RecHdr; // An NTFS_RECORD_HEADER structure with a Type of 'FILE'.
USHORT SequenceNumber; // Sequence number - The number of times
// that the MFT entry has been reused.
USHORT LinkCount; // Hard link count - The number of directory links to the MFT entry
USHORT AttributeOffset; // Offset to the first Attribute - The offset, in bytes,
// from the start of the structure to the first attribute of the MFT
USHORT Flags; // Flags - A bit array of flags specifying properties of the MFT entry
// InUse 0x0001 - The MFT entry is in use
// Directory 0x0002 - The MFT entry represents a directory
ULONG BytesInUse; // Real size of the FILE record - The number of bytes used by the MFT entry.
ULONG BytesAllocated; // Allocated size of the FILE record - The number of bytes
// allocated for the MFT entry
ULONGLONG BaseFileRecord; // reference to the base FILE record - If the MFT entry contains
// attributes that overflowed a base MFT entry, this member
// contains the file reference number of the base entry;
// otherwise, it contains zero
USHORT NextAttributeNumber; // Next Attribute Id - The number that will be assigned to
// the next attribute added to the MFT entry.
USHORT Pading; // Align to 4 byte boundary (XP)
ULONG MFTRecordNumber; // Number of this MFT Record (XP)
USHORT UpdateSeqNum; //
} FILE_RECORD_HEADER, *PFILE_RECORD_HEADER;
// Convert 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;
DWORD lpBytesReturned = 0;
FILE_RECORD_HEADER FileRecHdr = {0};
// Variables for MFT-reading
NTFS_FILE_RECORD_INPUT_BUFFER ntfsFileRecordInput;
PNTFS_FILE_RECORD_OUTPUT_BUFFER ntfsFileRecordOutput;
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);
// get ntfsVolData by calling DeviceIoControl()
// with CtlCode FSCTL_GET_NTFS_VOLUME_DATA
// setup output buffer - FSCTL_GET_NTFS_FILE_RECORD depends on this
// 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(ntfsVolData), &dwWritten, NULL);
// Failed or pending
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(L1st DeviceIoControl(...,FSCTL_GET_NTFS_VOLUME_DATA,...) call is working...\n);
//a call to FSCTL_GET_NTFS_VOLUME_DATA returns the structure NTFS_VOLUME_DATA_BUFFER
ntfsFileRecordOutput = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)
malloc(sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER)+ntfsVolData.BytesPerFileRecordSegment-1);
if(ntfsFileRecordOutput == NULL)
wprintf(LInsufficient memory lol!\n);
else
wprintf(LMemory allocated successfully!\n);
// The MFT-record #5 is the root-dir???
ntfsFileRecordInput.FileReferenceNumber.QuadPart = 5;
bDioControl = DeviceIoControl(
hVolume,
FSCTL_GET_NTFS_FILE_RECORD,
&ntfsFileRecordInput,
sizeof(NTFS_FILE_RECORD_INPUT_BUFFER),
ntfsFileRecordOutput,
sizeof(NTFS_FILE_RECORD_OUTPUT_BUFFER)+ntfsVolData.BytesPerFileRecordSegment-1,
&lpBytesReturned, NULL);
// Failed or pending
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(L2nd DeviceIoControl(...,FSCTL_GET_NTFS_FILE_RECORD,...) call is working...\n);
// read the record header from start of MFT-record
if(!(memcpy(&FileRecHdr, &ntfsFileRecordOutput->FileRecordBuffer[0], sizeof(FILE_RECORD_HEADER))))
wprintf(Lmemcpy() failed!\n);
else
wprintf(Lmemcpy() is OK!\n\n);
wprintf(LAttributeOffset: %u\n,FileRecHdr.AttributeOffset);
wprintf(LBaseFileRecord: %u\n,FileRecHdr.BaseFileRecord);
wprintf(LBytesAllocated: %u\n,FileRecHdr.BytesAllocated);
wprintf(LBytesInUse: %u\n,FileRecHdr.BytesInUse);
wprintf(LFlags: %u\n,FileRecHdr.Flags);
wprintf(LLinkCount: %u\n,FileRecHdr.LinkCount);
wprintf(LMFTRecordNumber: %u\n,FileRecHdr.MFTRecordNumber);
wprintf(LNextAttributeNumber: %u\n,FileRecHdr.NextAttributeNumber);
wprintf(LPading: %u\n,FileRecHdr.Pading);
wprintf(LRecHdr: %u\n,FileRecHdr.RecHdr);
wprintf(LSequenceNumber: %u\n,FileRecHdr.SequenceNumber);
wprintf(LUpdateSeqNum: %u\n,FileRecHdr.UpdateSeqNum);
if(CloseHandle(hVolume) != 0)
wprintf(LhVolume handle was closed successfully!\n);
else
{
wprintf(LFailed to close hVolume handle!\n);
ErrorMessage(GetLastError());
}
// Free up the allocated memory by malloc()
free(ntfsFileRecordOutput);
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.