Another Day, Another MFT Program Example: List, Recover and Delete the Deleted Files from Master File Table
The following program example tries to extend the previous example by adding a 'feature' that can 'delete' a file in the 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 <assert.h>
#include ntfs.h
// Fixing the offset
#define FIXOFFSET(x, y) ((CHAR*)(x) + (y))
// Global variables. Not a good practice
ULONG BytesPerFileRecord;
UINT BytesPerCluster;
BOOT_BLOCK BootBlk;
PFILE_RECORD_HEADER MFT;
HANDLE hVolume;
FILE *pFLog;
BOOL BitSet(PUCHAR Bitmap, ULONG Idx)
{
return (Bitmap[Idx >> 3] & (1 << (Idx & 7))) != 0;
}
void FixupUpdateSequenceArray(PFILE_RECORD_HEADER FileRecord)
{
PUSHORT UsAry = PUSHORT(FIXOFFSET(FileRecord, FileRecord->Ntfs.UsaOffset));
PUSHORT Sector = PUSHORT(FileRecord);
for (ULONG Idx = 1; Idx < FileRecord->Ntfs.UsaCount; Idx++)
{
// If( UsAry[0] != Sector[255] ) then this sector is corrupt or broken
Sector[255] = UsAry[Idx];
Sector += 256;
}
}
void ZeroSequenceArray(PFILE_RECORD_HEADER FileRecord)
{
PUSHORT UsAry = PUSHORT(FIXOFFSET(FileRecord, FileRecord->Ntfs.UsaOffset));
for (ULONG Idx = 1; Idx < FileRecord->Ntfs.UsaCount; Idx++)
{
UsAry[Idx] = 0x3030;
}
}
void ReadSector(ULONGLONG Sector, ULONG Cnt, PVOID Buffer)
{
ULARGE_INTEGER Offset;
OVERLAPPED Overlap = {0};
ULONG ReadBytes, CntIdx = 0, NeedReadByte = Cnt * BootBlk.BytesPerSector;
// Assign the physical position.
Offset.QuadPart = Sector * BootBlk.BytesPerSector;
// Set position to Overlap.
Overlap.Offset = Offset.LowPart;
Overlap.OffsetHigh = Offset.HighPart;
ReadFile(hVolume, Buffer, NeedReadByte, &ReadBytes, &Overlap);
if(ReadBytes != NeedReadByte)
{
while(CntIdx < Cnt)
{
//Set position to Overlap.
Overlap.Offset = Offset.LowPart;
Overlap.OffsetHigh = Offset.HighPart;
ReadFile(hVolume, Buffer, BootBlk.BytesPerSector, &ReadBytes, &Overlap);
if(ReadBytes != BootBlk.BytesPerSector)
{
wprintf(LRead Sector failed: %d:%d:%d\n, Offset.LowPart, Cnt * BootBlk.BytesPerSector, ReadBytes);
return;
}
Buffer = (UCHAR*)Buffer + BootBlk.BytesPerSector;
// Update the physical position.
Offset.QuadPart += BootBlk.BytesPerSector;
++CntIdx;
}
}
return;
}
void ReadLCN(ULONGLONG LCN, ULONG Cnt, PVOID Buffer)
{
ReadSector(LCN * BootBlk.SectorsPerCluster, Cnt * BootBlk.SectorsPerCluster, Buffer);
}
void WriteSector(ULONGLONG Sector, ULONG Cnt, PVOID Buffer)
{
ULARGE_INTEGER Offset;
OVERLAPPED Overlap = {0};
ULONG Written;
// Assign the physical position.
Offset.QuadPart = Sector * BootBlk.BytesPerSector;
// Set position to Overlap.
Overlap.Offset = Offset.LowPart;
Overlap.OffsetHigh = Offset.HighPart;
WriteFile(hVolume, Buffer, Cnt * BootBlk.BytesPerSector, &Written, &Overlap);
if(Written != Cnt * BootBlk.BytesPerSector)
wprintf(LWrote failed: %d:%d:%d\n, Overlap.Offset, Cnt * BootBlk.BytesPerSector, Written);
}
void WriteLCN(ULONGLONG LCN, ULONG Cnt, PVOID Buffer)
{
WriteSector(LCN * BootBlk.SectorsPerCluster, Cnt * BootBlk.SectorsPerCluster, Buffer);
}
void ZeroLCN(ULONGLONG LCN, ULONG Cnt)
{
ULONGLONG ZeroSectorNum=512;
BYTE *p512Sector = new BYTE[(UINT)BootBlk.BytesPerSector * (UINT)ZeroSectorNum];
ULONGLONG SectorNum = Cnt * (UINT)BootBlk.SectorsPerCluster;
ULONGLONG SectorSrtIdx = LCN * BootBlk.SectorsPerCluster, Idx;
ULONGLONG SectorEndIdx = SectorSrtIdx + SectorNum;
wprintf(L->Sector Start Index: %d, Zero Sector number is %d\n, (UINT)SectorSrtIdx, (UINT)SectorNum);
memset(p512Sector, '0', (UINT)BootBlk.BytesPerSector * (UINT)ZeroSectorNum);
if(p512Sector)
{
Idx = SectorSrtIdx;
while((SectorNum > 0) && (ZeroSectorNum > 0))
{
wprintf(L\n%d--->, ZeroSectorNum);
for(; (Idx < SectorEndIdx) && (SectorNum >= ZeroSectorNum); Idx += ZeroSectorNum)
{
WriteSector(Idx, (UINT)ZeroSectorNum, p512Sector);
SectorNum -= ZeroSectorNum;
wprintf(L.);
}
ZeroSectorNum >>= 1;
}
if(SectorNum)
{
// Evaluates an expression and, when the result is false,
// prints a diagnostic message and aborts the program.
assert(SectorNum == 1);
WriteSector(Idx, 1, p512Sector);
wprintf(L\n%3d--->., 1);
}
wprintf(L\n);
delete [] p512Sector;
}
}
ULONG AttributeLength(PATTRIBUTE Attr)
{
return Attr->Nonresident == FALSE
? PRESIDENT_ATTRIBUTE(Attr)->ValueLength: ULONG(PNONRESIDENT_ATTRIBUTE(Attr)->DataSize);
}
ULONG AttributeLengthAllocated(PATTRIBUTE Attr)
{
return Attr->Nonresident == FALSE? PRESIDENT_ATTRIBUTE(Attr)->ValueLength:
ULONG(PNONRESIDENT_ATTRIBUTE(Attr)->AllocatedSize);
}
ULONG RunLength(PUCHAR Run)
{
return (*Run & 0xf) + ((*Run >> 4) & 0xf) + 1;
}
ULONGLONG RunCount(PUCHAR Run)
{
// Get the end index.
UCHAR Idx = *Run & 0xF;
ULONGLONG Cnt = 0;
for (; Idx > 0; Idx--)
Cnt = (Cnt << 8) + Run[Idx];
return Cnt;
}
LONGLONG RunLCN(PUCHAR Run)
{
UCHAR VCNNumEndIdx = *Run & 0xf;
UCHAR LCNIdxValNum = (*Run >> 4) & 0xf;
LONGLONG LCN = LCNIdxValNum == 0 ? 0 : CHAR(Run[VCNNumEndIdx + LCNIdxValNum]);
for (LONG Idx = VCNNumEndIdx + LCNIdxValNum - 1; Idx > VCNNumEndIdx; Idx--)
LCN = (LCN << 8) + Run[Idx];
return LCN;
}
BOOL FindRun(PNONRESIDENT_ATTRIBUTE Attr, ULONGLONG VCN, PULONGLONG LCN, PULONGLONG Cnt)
{
INT Idx;
if (VCN < Attr->LowVcn || VCN > Attr->HighVcn)
return FALSE;
*LCN = 0;
ULONGLONG Base = Attr->LowVcn;
PUCHAR Run = PUCHAR(FIXOFFSET(Attr, Attr->RunArrayOffset));
for (Idx = 0; *Run != 0; Run += RunLength(Run), ++Idx)
{
*LCN += RunLCN(Run);
*Cnt = RunCount(Run);
if (Base <= VCN && VCN < Base + *Cnt)
{
*LCN = RunLCN(Run) == 0 ? 0 : *LCN + VCN - Base;
*Cnt -= ULONG(VCN - Base);
return TRUE;
}
else
{
Base += *Cnt;
}
}
*Cnt = 0;
return FALSE;
}
void ZeroExternalAttribute(PNONRESIDENT_ATTRIBUTE Attr, ULONGLONG VCN, ULONG Cnt)
{
ULONGLONG LCN, RunClstrCnt;
ULONG RdCnt, Left;
ULONG ZOBytes = 0;
for (Left = Cnt; Left > 0; Left -= RdCnt)
{
FindRun(Attr, VCN, &LCN, &RunClstrCnt);
RdCnt = ULONG(min(RunClstrCnt, Left));
if(RdCnt != 0)
{
ZOBytes += RdCnt * BytesPerCluster;
ZeroLCN(LCN, RdCnt);
VCN += RdCnt;
}
else
break;
}
wprintf(LZero data bytes number %d\n, ZOBytes);
}
void ReadExternalAttribute(PNONRESIDENT_ATTRIBUTE Attr, ULONGLONG VCN, ULONG Cnt, PVOID Buffer)
{
ULONGLONG LCN, RunClstrCnt;
ULONG RdCnt, Left;
PUCHAR DataPtr = PUCHAR(Buffer);
for (Left = Cnt; Left > 0; Left -= RdCnt)
{
FindRun(Attr, VCN, &LCN, &RunClstrCnt);
RdCnt = ULONG(min(RunClstrCnt, Left));
ULONG RdBytes = RdCnt * BytesPerCluster;
if (LCN == 0)
{
memset(DataPtr, 0, RdBytes);
}
else
{
// LCN is physical index of cluster.
ReadLCN(LCN, RdCnt, DataPtr);
}
// Update virtual cluster index.
VCN += RdCnt;
DataPtr += RdBytes;
}
}
void ReadAttribute(PATTRIBUTE Attr, PVOID Buffer)
{
if (Attr->Nonresident == FALSE)
{
PRESIDENT_ATTRIBUTE RAttr = PRESIDENT_ATTRIBUTE(Attr);
memcpy(Buffer, FIXOFFSET(RAttr, RAttr->ValueOffset), RAttr->ValueLength);
}
else
{
PNONRESIDENT_ATTRIBUTE NAttr = PNONRESIDENT_ATTRIBUTE(Attr);
ReadExternalAttribute(NAttr, 0, ULONG(NAttr->HighVcn) + 1, Buffer);
}
}
PATTRIBUTE FindAttribute(PFILE_RECORD_HEADER FileRecord, ATTRIBUTE_TYPE Tp, PWSTR Name)
{
for (PATTRIBUTE Attr = PATTRIBUTE(FIXOFFSET(FileRecord, FileRecord->AttributesOffset));
Attr->AttributeType != -1;
Attr = PATTRIBUTE(FIXOFFSET(Attr, Attr->Length)))
{
if (Attr->AttributeType == Tp)
{
// This Attribute hasn't name, found return.
if (Name == 0 && Attr->NameLength == 0)
{
return Attr;
}
if (Name != 0 && wcslen(Name) == Attr->NameLength
&& _wcsicmp(Name, PWSTR(FIXOFFSET(Attr, Attr->NameOffset))) == 0)
{
return Attr;
}
}
}
return NULL;
}
PATTRIBUTE FindAttributeFileName(PFILE_RECORD_HEADER FileRecord, PWSTR Name)
{
PATTRIBUTE AttrCp = NULL;
PFILENAME_ATTRIBUTE FileName;
for (PATTRIBUTE Attr = PATTRIBUTE(FIXOFFSET(FileRecord, FileRecord->AttributesOffset));
Attr->AttributeType != -1;
Attr = PATTRIBUTE(FIXOFFSET(Attr, Attr->Length)))
{
if (Attr->AttributeType == AttributeFileName)
{
// This Attribute has no name, found return.
if (Name == 0 && Attr->NameLength == 0)
{
AttrCp = Attr;
FileName = PFILENAME_ATTRIBUTE(FIXOFFSET(AttrCp,
PRESIDENT_ATTRIBUTE(AttrCp)->ValueOffset));
if(FileName->NameType == 1)
return AttrCp;
}
if (Name != 0 && wcslen(Name) == Attr->NameLength
&& _wcsicmp(Name, PWSTR(FIXOFFSET(Attr, Attr->NameOffset))) == 0)
{
AttrCp = Attr;
FileName = PFILENAME_ATTRIBUTE(FIXOFFSET(AttrCp,
PRESIDENT_ATTRIBUTE(AttrCp)->ValueOffset));
if(FileName->NameType == 1)
return AttrCp;
}
}
}
return AttrCp;
}
void ReadVCN(PFILE_RECORD_HEADER FileRecord, ATTRIBUTE_TYPE Tp, ULONGLONG VCN, ULONG Cnt, PVOID Buffer)
{
PNONRESIDENT_ATTRIBUTE Attr = PNONRESIDENT_ATTRIBUTE(FindAttribute(FileRecord, Tp, 0));
if (Attr == 0 || (VCN < Attr->LowVcn || VCN > Attr->HighVcn))
{
PATTRIBUTE Attrlist = FindAttribute(FileRecord, AttributeAttributeList, 0);
// Will cause a breakpoint exception to occur in the current process.
DebugBreak();
}
ReadExternalAttribute(Attr, VCN, Cnt, Buffer);
}
ULONGLONG GetLCN(ULONG Idx)
{
ULONGLONG VCN = ULONGLONG(Idx) * BytesPerFileRecord / BytesPerCluster;
PNONRESIDENT_ATTRIBUTE Attr = PNONRESIDENT_ATTRIBUTE(FindAttribute(MFT, AttributeData, 0));
ULONGLONG LCN, RunClstrCnt;
if(FindRun(Attr, VCN, &LCN, &RunClstrCnt) == FALSE)
return 0;
return LCN;
}
void ReadFileRecord(ULONG Idx, PFILE_RECORD_HEADER FileRecord)
{
ULONG ClstrNum = BootBlk.ClustersPerFileRecord;
if (ClstrNum > 0x80)
ClstrNum = 1;
PUCHAR BufPtr = new UCHAR[BytesPerCluster * ClstrNum];
ULONGLONG VCN = ULONGLONG(Idx) * BytesPerFileRecord / BytesPerCluster;
ReadVCN(MFT, AttributeData, VCN, ClstrNum, BufPtr);
LONG FRPerCluster = (BytesPerCluster / BytesPerFileRecord) - 1;
ULONG FRIdx = FRPerCluster > 0 ? (Idx & FRPerCluster) : 0;
memcpy(FileRecord, BufPtr + FRIdx * BytesPerFileRecord, BytesPerFileRecord);
delete [] BufPtr;
}
void ListDeleted()
{
ULONG Idx;
PATTRIBUTE Attr = (PATTRIBUTE)FindAttribute(MFT, AttributeBitmap, 0);
PUCHAR Bitmap = new UCHAR[AttributeLengthAllocated(Attr)];
ReadAttribute(Attr, Bitmap);
ULONG Num = AttributeLength(FindAttribute(MFT, AttributeData, 0)) / BytesPerFileRecord;
fwprintf(pFLog, L\nMFT Data number %u\n\n, Num);
PFILE_RECORD_HEADER FileRecord = PFILE_RECORD_HEADER(new UCHAR[BytesPerFileRecord]);
for (Idx = 0; Idx < Num; Idx++)
{
if (BitSet(Bitmap, Idx))
continue;
ReadFileRecord(Idx, FileRecord);
FixupUpdateSequenceArray(FileRecord);
if (FileRecord->Ntfs.Type == 'ELIF' && (FileRecord->Flags & 1) == 0)
{
Attr = (PATTRIBUTE)FindAttributeFileName(FileRecord, 0);
if (Attr == 0)
continue;
PFILENAME_ATTRIBUTE Name = PFILENAME_ATTRIBUTE(FIXOFFSET(Attr, PRESIDENT_ATTRIBUTE(Attr)->ValueOffset));
fwprintf(pFLog, L%10u, %3d, %ws\n, Idx, INT(Name->NameLength), Name->Name);
// To see the index, file name length and the file name displayed on the standard output,
// uncomment the following line
// wprintf(L%10u %u, %ws\n, Idx, INT(Name->NameLength), Name->Name);
}
}
delete [] Bitmap;
delete [] (UCHAR*)FileRecord;
}
void LoadMFT()
{
BytesPerCluster = BootBlk.SectorsPerCluster * BootBlk.BytesPerSector;
BytesPerFileRecord = BootBlk.ClustersPerFileRecord < 0x80
? BootBlk.ClustersPerFileRecord * BytesPerCluster : (1 << (0x100 - BootBlk.ClustersPerFileRecord));
wprintf(LCluster Per File Record: %u\n, BootBlk.ClustersPerFileRecord);
wprintf(LBytes Per File Record: %u\n, BytesPerFileRecord);
MFT = PFILE_RECORD_HEADER(new UCHAR[BytesPerFileRecord]);
ReadSector(BootBlk.MftStartLcn * BootBlk.SectorsPerCluster,BytesPerFileRecord / BootBlk.BytesPerSector, MFT);
FixupUpdateSequenceArray(MFT);
}
VOID UnloadMFT()
{
// Clean up
wprintf(LUnloading MFT...\n);
delete [] (UCHAR*)MFT;
}
void RemoveFile(ULONG Idx)
{
PFILE_RECORD_HEADER FileRecord = PFILE_RECORD_HEADER(new UCHAR[BytesPerFileRecord]);
UCHAR *ClustersBuf;
ULONG BufferSize, DataSize, AllocatedSize, Position, LCN, ClstrNum;
ReadFileRecord(Idx, FileRecord);
FixupUpdateSequenceArray(FileRecord);
if (FileRecord->Ntfs.Type != 'ELIF')
{
wprintf(LRemoveFile() - FileRecord->Ntfs.Type != \'ELIF\'...\n);
delete [] (UCHAR*)FileRecord;
return;
}
PATTRIBUTE Attr = FindAttribute(FileRecord, AttributeData, 0);
if (Attr == 0)
{
wprintf(LRemoveFile() - Attr == 0...\n);
delete [] (UCHAR*)FileRecord;
return;
}
DataSize = AttributeLength(Attr);
AllocatedSize = AttributeLengthAllocated(Attr);
BufferSize = AllocatedSize > DataSize ? AllocatedSize : DataSize;
PUCHAR Buffer = new UCHAR[BufferSize + 1];
ClstrNum = BootBlk.ClustersPerFileRecord;
if (ClstrNum > 0x80)
ClstrNum = 1;
ClustersBuf = new UCHAR[BytesPerCluster * ClstrNum];
if (Attr->Nonresident == FALSE)
{
wprintf(LThis file data is resident!\n);
PRESIDENT_ATTRIBUTE RAttr = PRESIDENT_ATTRIBUTE(Attr);
LONG FRPerCluster = (BytesPerCluster / BytesPerFileRecord) - 1;
ULONG FRIdx = FRPerCluster > 0 ? (Idx & FRPerCluster) : 0;
Position = (ULONG)(FIXOFFSET(RAttr, RAttr->ValueOffset)) - (ULONG)FileRecord;
Position = FRIdx * BytesPerFileRecord + Position;
if(LCN = (ULONG)GetLCN(Idx), LCN)
{
// Read file record
ReadLCN(LCN, ClstrNum, ClustersBuf);
FixupUpdateSequenceArray((FILE_RECORD_HEADER*)&ClustersBuf[FRIdx * BytesPerFileRecord]);
ZeroSequenceArray((FILE_RECORD_HEADER*)&ClustersBuf[FRIdx * BytesPerFileRecord]);
memset(&ClustersBuf[Position], '0', DataSize);
WriteLCN(LCN, ClstrNum, ClustersBuf);
}
}
else
{
wprintf(LThis file data is nonresident!\n);
PNONRESIDENT_ATTRIBUTE NAttr = PNONRESIDENT_ATTRIBUTE(Attr);
ZeroExternalAttribute(NAttr, 0, ULONG(NAttr->HighVcn) + 1);
}
wprintf(L Removed file should be succeeded!\n);
wprintf(L The file\'s content should be empty!!!\n);
}
void RecoverFile(ULONG Idx, LPCWSTR NewFileName)
{
PFILE_RECORD_HEADER FileRecord = PFILE_RECORD_HEADER(new UCHAR[BytesPerFileRecord]);
ULONG Written, BufferSize, DataSize, AllocatedSize;
ReadFileRecord(Idx, FileRecord);
FixupUpdateSequenceArray(FileRecord);
if (FileRecord->Ntfs.Type != 'ELIF')
{
delete [] (UCHAR*)FileRecord;
wprintf(LFailed. FileRecord->Ntfs.Type != 'ELIF'\n);
return;
}
PATTRIBUTE Attr = FindAttribute(FileRecord, AttributeData, 0);
if (Attr == 0)
{
delete [] (UCHAR*)FileRecord;
wprintf(LFailed, Attr == 0!\n);
return;
}
DataSize = AttributeLength(Attr);
AllocatedSize = AttributeLengthAllocated(Attr);
BufferSize = AllocatedSize > DataSize ? AllocatedSize : DataSize;
// Align
BufferSize = BufferSize / BootBlk.BytesPerSector * BootBlk.BytesPerSector + BootBlk.BytesPerSector;
PUCHAR Buffer = new UCHAR[BufferSize];
wprintf(LRecoverFile() - Reading the deleted file data...\n);
ReadAttribute(Attr, Buffer);
HANDLE hFile = CreateFile(NewFileName, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
if(hFile == INVALID_HANDLE_VALUE)
{
wprintf(LRecoverFile() - CreateFile() failed, error %u\n, GetLastError());
exit(1);
}
if(WriteFile(hFile, Buffer, DataSize, &Written, 0) == 0)
{
wprintf(LRecoverFile() - WriteFile() failed, error %u\n, GetLastError());
exit(1);
}
// Another check
// Written = the number of byte to be written
if(Written != DataSize)
wprintf(LWriting the file data failed!, error %u\n, GetLastError());
CloseHandle(hFile);
delete [] Buffer;
delete [] (UCHAR*)FileRecord;
}
int wmain(int argc, WCHAR *argv[])
{
// Default partition
WCHAR Drive[] = L\\\\?\\C:;
ULONG Read, Idx;
LPCWSTR OriFileName = L;
errno_t ernoFLog1;
WCHAR *cOption = LA;
// Give some info to clueless user
wprintf(LUsages:\n);
wprintf(L(Default primary partition is C:\\\n);
wprintf(L1. %s - Attempting to list the deleted files...\n, argv[0]);
wprintf(L (The deleted files stored in C:\\DeletedFile.txt)\n);
wprintf(L2. Finding the deleted file\n);
wprintf(L %s <file_index> <original_file_name>\n, argv[0]);
wprintf(L (The index and file name can be found in C:\\DeletedFile.txt)\n);
wprintf(L e.g. %s 123546 tergedik.txt\n, argv[0]);
wprintf(L3. Removing the deleted file\n);
wprintf(L %s <file_index_to_be_removed>\n, argv[0]);
wprintf(L e.g. %s 123546\n, argv[0]);
wprintf(L===Press any key to continue!===\n);
// Let them read for a while
getwchar();
ernoFLog1 = _wfopen_s(&pFLog, LC:\\DeletedFile.txt, Lw);
if(ernoFLog1 != 0)
{
wprintf(L_wfopen_s() failed, error %u\n, _get_errno(&ernoFLog1));
exit(1);
}
hVolume = CreateFile(
Drive,
GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
0,
0);
if( hVolume == INVALID_HANDLE_VALUE)
{
wprintf(LCreateFile() failed, error %u\n, GetLastError());
exit(1);
}
if(ReadFile(hVolume, &BootBlk, sizeof(BOOT_BLOCK), &Read, 0) == 0)
{
wprintf(LReadFile() failed, error %u\n, GetLastError());
exit(1);
}
wprintf(LRead volume should succeeded...\n);
LoadMFT();
wprintf(LLoad MFT succeed:\n);
wprintf(L Bytes Per Sector:Sectors Per Cluster:Clusters Per FileRecord-->);
wprintf(L %u:%u:%u.\n, BootBlk.BytesPerSector, BootBlk.SectorsPerCluster, BootBlk.ClustersPerFileRecord);
wprintf(LAttempt to list the deleted files.........\n);
wprintf(L Find them in C:\\DeletedFile.txt lol!\n);
ListDeleted();
// Attempt to find the deleted file from MFT
if(argc == 3)
{
// Use the index and the original file name for recovery.
// The recovered file stored under the project's debug folder.
// The index and original file name can be found in C:\DeletedFile.txt
Idx = _wtoi(argv[1]);
OriFileName = argv[2];
wprintf(LRecovered file should be in the projects\'s Debug folder!\n);
RecoverFile(Idx, OriFileName);
}
// Attempt to delete the 'file' from MFT
if(argc == 2)
{
// Use the index to remove the file
Idx = _wtoi(argv[1]);
RemoveFile(Idx);
}
// Free up all the resources.
// Look likes some of the malloc() are not freed lol! Who cares?
// You can do it...
UnloadMFT();
CloseHandle(hVolume);
fclose(pFLog);
}