NTFS文件恢复(最简单情况)
原理:这里我们不深究NTFS 系统的细节,只根据需要了解相关知识。
工具:WinHex,DiskExplorer
是Microsoft公司开发的专用文件系统,从Windows NT 3.1开始成为Windows NT家族的标准文件系统(磁盘主要文件系统)。使用更高级的数据结构以提升性能、可靠性和磁盘空间利用率,并附带一系列增强功能,如访问控制列表(ACL)和文件系统日志。
https://zh.wikipedia.org/wiki/NTFS
分区引导扇区 |
Master file table 主文件表 |
System files 系统文件 |
File area 文件区域 |
NTFS 分区的区域划分 |
MFT(Master Files Table):一个数据库,由一系列的文件记录组成---卷中的每一个文件都有一个文件记录(对于大型文件还可能有多个记录与之对应)。文件通过主文件表来确定其在磁盘上的存储位置。
分区刚开始的部分称为DBR :分区引导扇区,位于逻辑扇区0,即第一个扇区。每个分区都有引导扇区,但是只有被设为活动区:的才会被MBR(主引导记录区,整个磁盘的第一个扇区,用于引导系统启动)装入内存运行,以引导系统。DBR分为DOS引导程序和BPB(BIOS 参数块),该块用来描述本分区的磁盘信息,如下图:其中,文件系统标识,扇区大小,簇大小和MFT起始簇号对我们有用,主文件表位置=起始簇号*簇大小*扇区大小。
1. 扇区:磁盘驱动器在向磁盘读写数据的单位(一般512字节)。
2. 簇 :文件大小分配粒度,为扇区大小的整数倍(一般 8 * 扇区大小,即4KB)
首先我们测试小文件如下:
D:盘下新建HelloWorld.c文件如下:
这就要研究MFT 的格式了:
首先,MFT 文件记录头部结构布局:
偏移 |
长度 |
描述 |
0x00 |
4 |
固定值,一定是“FILE” |
0x04 |
2 |
头部大小 |
0x06 |
2 |
固定列表大小 |
0x08 |
8 |
日志文件序列号 |
0x10 |
2 |
序列号(用于记录本文件记录被重复使用的次数,每次文件删除时+1,跳过0值,如果为0,保持为0 |
0x12 |
2 |
硬链接数 |
0x14 |
2 |
第一个属性偏移值 |
0x16 |
2 |
1:使用中。2:目录。0:删除 |
0x18 |
4 |
文件记录实际大小 |
0x1C |
4 |
文件记录分配大小 |
0x20 |
8 |
对应的基本文件记录的文件参考号 |
0x28 |
2 |
下一个自由ID号,当增加新属性,将该值分配给新属性,然后该值增加,如果MFT记录重新使用,将它置0,第一个实例总是0 |
上面三个信息中:日志文件序列号代表NTFS 日志中记录文件更改的一个序号,我们并不需要用到,序列号对我们同样不重要。我们关心的数据有没有改变呢?NTFS除了将上述两个属性改变以及将使用标识改为0,是否还有其它动作?通过对比MFT记录可以发现,没有任何改变,对于小文件而言,我们需要的数据完整地放在那里,并没有任何改变,由此我们可以很有把握地找到标识为删除地文件记录,读取其中的数据并还原我们的文件。
通过上面的介绍我们可以发现,文件的搜索就是MFT 的遍历搜索,而删除文件的搜索最基本的要求就是要满足已删除标识,而这仅仅枚举所有的已删除文件。
但是这样的操作太过繁杂。
回顾刚才的MTF文件头我们可以发现,其中并没有文件名的属性,只有一个“第一个属性的偏移”,将文件作为属性/属性值的集合来处理,不同的属性类型有着同样的属性头(属性的共有信息)但是有不一样的属性体结构,而属性定义文件$AttrDef预定义了标准的属性标识和属性名如下:
上述只是一部分数据,可以看到有标准属性,文件名属性,对象ID属性等,还有未显示的,数据属性等。
文件名属性结构布局:
偏移 |
大小 |
值 |
描述 |
0x00 |
4 |
0x30 |
属性类型 |
0x04 |
4 |
|
总长度 |
0x08 |
1 |
|
非常驻? |
0x09 |
1 |
0x00 |
属性名的名称长度 |
0x0A |
2 |
0x18 |
属性名的名称偏移 |
0x0C |
2 |
0x00 |
标识(1 压缩,0x4000,加密,0x8000,稀疏文件,非常驻属性不会被压缩) |
0x0E |
2 |
|
标识 |
0x10 |
4 |
|
属性长度(L) |
0x14 |
2 |
|
属性内容的起始偏移 |
0x16 |
1 |
|
索引标志 |
0x17 |
1 |
|
填充 |
0x18 |
L |
|
从此处开始,共L字节为属性 |
文件名属性结构布局:
偏移 |
大小 |
值 |
0x00 |
8 |
父目录的文件参考号 |
0x08 |
8 |
文件创建时间 |
0x10 |
8 |
文件修改时间 |
0x18 |
8 |
最后一次的MFT 更新时间 |
0x20 |
8 |
最后一次的访问时间 |
0x10 |
8 |
文件分配大小(被删除时为0) |
0x30 |
8 |
文件实际大小(被删除时为0) |
0x38 |
4 |
标志,如目录、压缩、隐藏等(这里我们暂时不考虑,只进行文件名的查找操作) |
0x3C |
4 |
用于EAs和重解析点 |
0x40 |
1 |
以字符计的文件名长度,每字符占字节数 由下一字节命名空间确定,一个字节长度,所以文件名最大为256字节长 |
0x41 |
1 |
文件名命名空间(暂时只考虑Win32) |
0x42 |
2L |
以Unicode方式表达的文件名 |
好了到了这里我们终于有了一些小突破,貌似可以编码进行被删除文件的查找操作了,很容易发现,文件名中已经包含了后缀名。
NTFS 规定,当属性开始为0xFFFFFFFF,此文件的属性已经遍历完毕。
刚开始我们提到过,$MFT 文件的第一个记录就是描述它自己的,其中包含了文件的大小,因此我们很容易确定搜索遍历的范围。
至此,我们已经可以愉快地进行删除文件的自定义文件名或扩展名的搜索,实践表明,可以查到的文件的数量有限,即使查找所有的已删除文件,而这仅仅查到而已。所以,数据备份是好习惯,尤其是桌面文件,由于习惯性在桌面进行文件操作,而C 盘文件的增删最频繁,特别容易导致刚刚删除的文件记录已经被覆盖掉,数据不可恢复。另外,时间越短,磁盘文件增删越少恢复概率越高(但是也高不了多少)。
下面我们就为了这渺茫的概率付出努力吧。
前面提到过“NTFS 将文件作为属性/属性值的集合来处理,不同的属性类型有着同样的属性头(属性的共有信息)但是有不一样的属性体结构”
我们需要了解文件数据的属性ID,和结构,然后根据其结构来进行数据恢复:
介绍这些之前我们应该先了解两个基本的概念:
当属性值能放在MFT 中,属性称为常驻属性,有些属性总是常驻的,这样NTFS 才能确定其它非常驻属性,对于常驻属性,标准头还包含着属性值的偏移量和属性值的长度。
命名:已经在$AttrDef 中定义过的属性
未命名:未定义过的属性,需要自己定义属性名
LCN 对整个卷中所有的簇从头到尾进行的简单编号。卷因子乘以LCN,NTFS 就能得到卷上的物理字节偏移量,从而得到物理磁盘地址。VCN对属于特定文件的簇从头到尾进行编号,便于引用文件中的数据。VCN可以射成LCN,而不必要求在物理上连续。
下面罗列了四种属性头的格式如下:
未命名常驻属性标准属性头结构:
偏移 |
大小 |
值 |
描述 |
0x00 |
4 |
|
属性类型 |
0x04 |
4 |
|
总长度 |
0x08 |
1 |
0x00 |
非常驻? |
0x09 |
1 |
0x00 |
属性名的名称长度 |
0x0A |
2 |
0x00 |
属性名的名称偏移 |
0x0C |
2 |
0x00 |
标识(1 压缩,0x4000,加密,0x8000,稀疏文件) |
0x0E |
2 |
|
属性标识 |
0x10 |
4 |
L |
属性长度 |
0x14 |
2 |
0x18 |
属性偏移 |
0x16 |
1 |
|
索引标志 |
0x17 |
1 |
0x00 |
填充 |
0x18 |
L |
|
具体的属性值 |
未命名非常驻属性
偏移 |
大小 |
值 |
描述 |
0x00 |
4 |
|
属性类型 |
0x04 |
4 |
|
总长度 |
0x08 |
1 |
|
非常驻? |
0x09 |
1 |
N |
属性名的名称长度 |
0x0A |
2 |
0x40 |
属性名的名称偏移 |
0x0C |
2 |
|
标识(1 压缩,0x4000,加密,0x8000,稀疏文件) |
0x0E |
2 |
|
标识 |
0x10 |
8 |
|
起始VCN |
0x18 |
8 |
|
结束VCN |
0x20 |
2 |
0x40+2N |
数据运行的起始偏移 |
0x22 |
2 |
|
压缩引擎 |
0x24 |
4 |
|
填充 |
0x28 |
8 |
|
属性值分配大小 |
0x30 |
8 |
|
属性值实际大小 |
0x38 |
8 |
|
属性值压缩大小 |
0x40 |
2N |
Unicode字符 |
属性名 |
2N+0x40 |
|
|
数据运行(为4个字节的整倍数) |
命名常驻属性
偏移 |
大小 |
值 |
描述 |
0x00 |
4 |
0x80 |
属性类型 |
0x04 |
4 |
|
总长度 |
0x08 |
1 |
|
非常驻? |
0x09 |
1 |
N |
属性名的名称长度 |
0x0A |
2 |
0x18 |
属性名的名称偏移 |
0x0C |
2 |
|
标识(1 压缩,0x4000,加密,0x8000,稀疏文件) |
0x0E |
2 |
|
标识 |
0x10 |
4 |
L |
属性长度 |
0x14 |
4 |
2N+0x18 |
属性偏移 |
0x16 |
1 |
实际值 |
索引标志 |
0x17 |
1 |
0x00 |
填充 |
0x18 |
2N |
Unicode字符 |
属性名 |
2N+0x18 |
L |
|
具体的属性值(为4个字节的整倍数) |
命名非常驻属性
偏移 |
大小 |
值 |
描述 |
0x00 |
4 |
|
属性类型 |
0x04 |
4 |
|
总长度 |
0x08 |
1 |
0x01 |
非常驻? |
0x09 |
1 |
0x00 |
属性名的名称长度 |
0x0A |
2 |
0x00 |
属性名的名称偏移 |
0x0C |
2 |
|
标识(1 压缩,0x4000,加密,0x8000,稀疏文件) |
0x0E |
2 |
|
标识 |
0x10 |
8 |
|
起始VCN |
0x18 |
8 |
|
结束VCN |
0x20 |
2 |
0x40 |
数据运行的起始偏移 |
0x22 |
2 |
|
压缩引擎 |
0x24 |
4 |
0x00 |
填充 |
0x28 |
8 |
|
属性值分配大小 |
0x30 |
8 |
|
属性值实际大小 |
0x38 |
8 |
|
属性值压缩大小 |
0x40 |
… |
|
数据运行 |
数据为命名属性
当前我们实验的文件HelloWorld.c 为小文件,其对应的数据流属性为常驻属性如下图所示:
如何得到较大文件的数据?
我们使用常用的procexp.exe进行测试如下:
可以看到,数据运行为:“42 65 02 30 4A B0 00 00”
首先:”42”中的“2”说明后面的头两个字节“65 02”代表的是文件的是这个数据段的长度,为”0x0265”簇,“4”说明,从第三个字节开始的4个字节“30 4A B0 00”代表数据起始簇号,即“0xB04A30”。这样我们就得到了文件的第一段长度。
后面的“00”代表数据运行的结束。
我们来看该簇号对应的数据:
我们可以看到”MZ”两个字母,至少确定数据头一段的正确性。
常驻数据长度:属性总长度- 属性数据的偏移
非常驻数据长度:属性值实际大小
至此我们已经知道我们需要了解的所有信息,可以自定义文件的恢复。
类声明:
#pragma once
#include
#pragma pack(1)
typedef struct _UNNAME_UNRSIDENT_
{
ULONG32 Type;
ULONG32 AttributeLength;
BYTE bUnrsident;
BYTE NameLength;
USHORT NameOffset;
USHORT TrueValue;
USHORT Flags;
ULONG64 BeginVCN;
ULONG64 EndVCN;
USHORT RunOffset;
USHORT Depress;
ULONG32 NoMings;
ULONG64 AttributeAllocSize;
ULONG64 AttributeTrueSize;
ULONG64 CompressSize;
}UNNAME_UNRSIDENT, *PUNNAME_UNRSIDENT;
class FileRecovery
{
#define LENGTH 1024*1024
private:
LPBYTE m_lpDBR;
LPBYTE m_lpMFT;
LPBYTE m_DataRecoverBuffer;
PWCHAR m_lpFileRealName;
LPBYTE szSystem_Id;
WORD m_wClusterSize;
ULONG64 m_ulong64FileSize;
DWORD m_dwLittleFileSize;
ULONG64 m_ulong64TotalClusterNumber;
DWORD m_dwRecoveried;
DWORD m_dwRidentFlag;
ULONG64 m_ulong64StartCluster;
DWORD m_dwNumberOfCluster;
ULONG64 m_ulong64SizeOfRun;
BYTE m_bStartClusterOffset;
BYTE m_bByteToExpressNumOfCluster;
HANDLE m_hFile;
BYTE FindFile(WCHAR * szFileName, WCHAR* szFilter, LPBYTE lpByteArray);
void ReadAndRecoveryFile(HANDLE hDrive,WCHAR * szFileName, ULONG64 MFTOffset);
public:
void ToCaps(PWCHAR lpFileName);
BOOL ReadSectors(HANDLE hDevice, ULONG64 dwStartSector, DWORD wSectors, LPBYTE lpSectBuff);
ULONG64 GetRunInfor(LPBYTE lpRun);
void ReadFileAndRecovery(HANDLE hDisk, LPBYTE lpMFT);
void GetFileList(WCHAR * szFileName, WCHAR * szFilter,WCHAR* szDrive);
FileRecovery();
void RecoveryIndex(ULONG iIndex);
void GetFileList(WCHAR * szFileName, WCHAR* szFilter,WCHAR cDrive);
~FileRecovery();
#define ALIGN 0x1000
typedef struct _FILE_INFO_
{
ULONG64 MFTOffset;
ULONG64 FileSize;
USHORT uNext;
WCHAR szFileName[1];
}FILE_INFO, *PFILE_INFO;
typedef struct _FILE_LIST_
{
ULONG ulCount;
ULONG ulTotalSize;
ULONG ulLeftSize;
ULONG_PTR CurrentOffset;
FILE_INFO lpFileInfo[1];
}FILE_LIST, *PFILE_LIST;
PFILE_LIST m_FileList;
};
实现:
#include "FileRecoveryClass.h"
#include
void FileRecovery::ToCaps(PWCHAR lpFileName)
{
PWCHAR travel = lpFileName;
WCHAR wChar = *travel;
while (wChar != 0)
{
if (wChar >= 0x61 && wChar <= 0x7a)
{
*travel = wChar - 0x20;
}
travel += 1;
wChar = *travel;
}
}
BOOL FileRecovery::ReadSectors(HANDLE hDevice, ULONG64 dwStartSector,
DWORD wSectors, LPBYTE lpSectBuff)
// 对磁盘扇区数据的读取
{
LONG ulHigh = dwStartSector >> 32;
LONG ulLow = dwStartSector % 0x100000000;
DWORD dwReturn = SetFilePointer(hDevice, ulLow, &ulHigh, FILE_BEGIN);
if (dwReturn == INVALID_SET_FILE_POINTER)
{
int a = 0;
a++;
}
if (GetLastError() != 0)
{
int a = 0;
a++;
}
DWORD dwCB;
BOOL bRet = ReadFile(hDevice, lpSectBuff, ((wSectors+0x200 - 1)/0x200)*0x200, &dwCB, NULL);
return bRet;
}
ULONG64 FileRecovery::GetRunInfor(LPBYTE lpRun)
{
BYTE v1 = *lpRun; // V1 高四位表示多少运行列表中多少个字节为起始簇
// 低四位表示多少个字节表示簇大小
m_bStartClusterOffset = v1 >> 4;
m_bByteToExpressNumOfCluster = v1 & 0xF;
memcpy(&m_ulong64StartCluster, lpRun + m_bByteToExpressNumOfCluster + 1, m_bStartClusterOffset);
memcpy(&m_dwNumberOfCluster, lpRun + 1, m_bByteToExpressNumOfCluster);
m_ulong64SizeOfRun = m_dwNumberOfCluster*m_wClusterSize;
return (m_ulong64StartCluster * m_wClusterSize);
}
void FileRecovery::ReadFileAndRecovery(HANDLE hDisk, LPBYTE lpMFT)
{
LPBYTE lpAttribute = lpMFT + *(WORD*)(lpMFT + 0x14);
LPBYTE lpTravel = lpAttribute;
while (1)
{
if (*(DWORD*)lpTravel == 0x80) // 代表的是数据
{
if (*(lpTravel + 0x9) == 0) // 没有属性名
{
if (*(lpTravel + 0x8) == 0) // 未命名常驻属性,这里是小文件的数据
{
DWORD dwAttributeHeadLength = *(WORD*)(lpTravel + 0x14); // 属性头长度
DWORD dwAttributeTotalLength = *(DWORD*)(lpTravel + 0x4); // 总的属性长度
m_dwLittleFileSize = dwAttributeTotalLength - dwAttributeHeadLength;// 文件大小
memcpy(m_DataRecoverBuffer, lpTravel + dwAttributeHeadLength, m_dwLittleFileSize);
m_dwRecoveried++; // 恢复个数加1
m_dwRidentFlag = 0; // 常驻属性
return;
}
else if (*(lpTravel + 0x8) == 1) // 未命名非常驻属性,这里是较大文件的真实文件数据
{
HANDLE hFile = CreateFileW(m_lpFileRealName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, NULL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return;
}
PUNNAME_UNRSIDENT EsayCalculate = (PUNNAME_UNRSIDENT)lpTravel;
m_ulong64FileSize = EsayCalculate->AttributeTrueSize;
WORD RunOffset = *(WORD*)(lpTravel + 0x20);
lpTravel = lpTravel + *(WORD*)(lpTravel + 0x20);// EsayCalculate->NameLength * 2 + 40;
while (1)
{
ULONG64 RunStartOffset = GetRunInfor(lpTravel);
if (m_ulong64SizeOfRun == 0)
{
CloseHandle(hFile);
return;
}
while (m_ulong64SizeOfRun > 0 && m_ulong64FileSize > 0)
{
DWORD dwNumberToOperate = m_ulong64SizeOfRun > 1024 * 1024 ? 1024 * 1024 : m_ulong64SizeOfRun;
if (m_ulong64FileSize < dwNumberToOperate)
{
dwNumberToOperate = m_ulong64FileSize;
}
ReadSectors(hDisk, RunStartOffset, dwNumberToOperate, m_DataRecoverBuffer);
WriteFile(hFile, m_DataRecoverBuffer, dwNumberToOperate, NULL, NULL);
m_ulong64SizeOfRun -= dwNumberToOperate;
m_ulong64FileSize -= dwNumberToOperate;
RunStartOffset += dwNumberToOperate;
}
lpTravel += m_bByteToExpressNumOfCluster + m_bStartClusterOffset + 1;
if (*lpTravel == 0)
{
CloseHandle(hFile);
if (m_ulong64FileSize)
{
//ShowMessage(_T("大小不对啊!"));
}
return;
}
}
}
}
else // 命名属性--->系统使用,我们不处理
{
lpTravel = lpTravel + *(WORD*)(lpTravel + 0x4);
}
}
else if (*(DWORD*)lpTravel == 0xFFFFFFFF) //属性末尾了
{
return;
}
else
{
lpTravel = lpTravel + *(WORD*)(lpTravel + 0x4); // 下一个属性
}
}
return;
}
BYTE FileRecovery::FindFile(WCHAR* szFileName,WCHAR* szFilter, LPBYTE lpByteArray)
{
BYTE bFindFileNameLength = wcslen(szFileName);
BYTE bFindFilterLength = wcslen(szFilter);
LPBYTE lpFind = lpByteArray;
if (*(DWORD*)lpFind == 0x454c4946) // FILE,如果不是合法MFT,代表已经读完了卷中所有MFT
{
if (*(WORD*)(lpFind + 0x16) != 0)// 当前文件未被删除
{
return 1;
}
else
{
LPBYTE lpAttribute = lpFind + *(WORD*)(lpFind + 0x14);
while (1)
{
if ((*(WORD*)lpAttribute) == 0x30) // 是30H 属性,即文件名属性
{
LPBYTE lpBegin = lpAttribute + *(WORD*)(lpAttribute + 0x14);
BYTE bNameLength = *(PBYTE)(lpBegin + 0x40);
if (bFindFileNameLength && bNameLength < bFindFileNameLength)
{
return 1;
}
else
{
PWCHAR lpFileName = (PWCHAR)(lpBegin + 0x42);
memcpy(m_lpFileRealName, lpFileName, bNameLength * sizeof(WCHAR));
m_lpFileRealName[bNameLength] = 0;
ToCaps(lpFileName);// 转换大小写的操作
if ((!bFindFileNameLength || wcsstr(lpFileName, szFileName)) != 0 && (!bFindFilterLength || wcsstr(lpFileName, szFilter) != 0))
{
return 2;// 找到了
}
}
}
else if (*((PULONG)lpAttribute) == 0xffffffff)//到了属性列表的结尾,没有找到
{
return 1; // 此MFT 不是我们要找的
}
lpAttribute = lpAttribute + *(PULONG)(lpAttribute + 0x4);
}
}
}
else
{
return 1;
}
}
void FileRecovery::ReadAndRecoveryFile(HANDLE hDisk,WCHAR* szFileName, ULONG64 MFTOffset)
{
if (hDisk == INVALID_HANDLE_VALUE || hDisk == NULL)
{
hDisk = m_hFile;
}
ReadSectors(hDisk, MFTOffset, 1024, m_lpMFT);
wcscpy(m_lpFileRealName, szFileName);
ReadFileAndRecovery(hDisk, m_lpMFT);
if (!m_dwRidentFlag)// 常驻属性
{
HANDLE hNewFile = CreateFileW(m_lpFileRealName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
WriteFile(hNewFile, m_DataRecoverBuffer, m_dwLittleFileSize, NULL, NULL);
CloseHandle(hNewFile);
}
else
{
}
}
void FileRecovery::GetFileList(WCHAR* szFileName, WCHAR* szFilter,WCHAR* szDrive)
{
WCHAR szTemp[MAX_PATH] = { 0 };
WCHAR szTempFilter[0x20] = { 0 };
if (szFileName != NULL)
{
wcscpy(szTemp, szFileName);
ToCaps(szTemp);
}
if (szFilter != NULL)
{
wcscpy(szTempFilter, szFilter);
ToCaps(szTempFilter);
}
m_hFile = CreateFileW(szDrive, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
if (m_hFile == INVALID_HANDLE_VALUE)
{
//ShowMessage(_T("UAC啊\r\n大兄弟"));
return;
}
else
{
if (ReadSectors(m_hFile, 0, 0x200, m_lpDBR))
{
if (*(DWORD*)szSystem_Id == *(DWORD*)(m_lpDBR + 3))
{
m_wClusterSize = *((WORD*)(m_lpDBR + 0xD)) * 0x200;
ULONG64 MFT_Offset = *(PULONG64)(m_lpDBR + 0x30);
MFT_Offset = (ULONG64)m_wClusterSize * MFT_Offset;
ReadSectors(m_hFile, MFT_Offset, 1024, m_lpMFT);
if (*(DWORD*)m_lpMFT == 0x454c4946) // FILE,如果不是合法MFT,代表已经读完了卷中所有MFT
{
LPBYTE lpAttribute = (LPBYTE)m_lpMFT + *(WORD*)(m_lpMFT + 0x14);
while (1)
{
if ((*(WORD*)lpAttribute) == 0x80) // $MFT 文件的数据流属性,得到MFT 文件总大小
{
m_ulong64TotalClusterNumber = *(PULONG64)(lpAttribute + 0x18) - *(PULONG64)(lpAttribute + 0x10)+1;
m_ulong64TotalClusterNumber = m_ulong64TotalClusterNumber * m_wClusterSize / 1024;
break;
}
else if (*((PULONG)lpAttribute) == 0xffffffff)//到了属性列表的结尾,没有找到
{
//ShowMessage(_T("错误,未能获得MFT文件大小"));
}
lpAttribute = lpAttribute + *(PULONG)(lpAttribute + 0x4);
}
}
while (1)
{
if (ReadSectors(m_hFile, MFT_Offset, 1024 * 1024, m_lpMFT))
{
static ULONG Count = 0;
for (int i = 0; i < 1024; i++)
{
switch (FindFile(szTemp, szTempFilter, m_lpMFT + i * 1024))
{
case 0:
{
//ShowMessage(_T("应该不会走这里的"));
return;
}
case 1:
{
break;
}
case 2:
{
ReadSectors(m_hFile, MFT_Offset + i * 1024, 1024, m_DataRecoverBuffer);
LPBYTE lpAttribute = m_DataRecoverBuffer + *(WORD*)(m_DataRecoverBuffer + 0x14);
LPBYTE lpTravel = lpAttribute;
while (1)
{
if (*(DWORD*)lpTravel == 0x80) // 代表的是数据
{
if (*(lpTravel + 0x9) == 0) // 没有属性名
{
if (*(lpTravel + 0x8) == 0) // 未命名常驻属性,这里是小文件的数据
{
DWORD dwAttributeHeadLength = *(WORD*)(lpTravel + 0x14); // 属性头长度
DWORD dwAttributeTotalLength = *(DWORD*)(lpTravel + 0x4); // 总的属性长度
m_ulong64FileSize = dwAttributeTotalLength - dwAttributeHeadLength;// 文件大小
break;
}
else if (*(lpTravel + 0x8) == 1) // 未命名非常驻属性,这里是较大文件的真实文件数据
{
PUNNAME_UNRSIDENT EsayCalculate = (PUNNAME_UNRSIDENT)lpTravel;
m_ulong64FileSize = EsayCalculate->AttributeTrueSize ;
break;
}
}
else // 命名属性--->系统使用,我们不处理
{
lpTravel = lpTravel + *(WORD*)(lpTravel + 0x4);
}
}
else if (*(DWORD*)lpTravel == 0xFFFFFFFF) //属性末尾了
{
break;
}
else
{
lpTravel = lpTravel + *(WORD*)(lpTravel + 0x4); // 下一个属性
}
}
if (m_FileList->ulLeftSize < sizeof(FILE_INFO) + wcslen(m_lpFileRealName) * sizeof(WCHAR))
{
ULONG_PTR CurrentOffset = m_FileList->CurrentOffset;
ULONG_PTR OldBuffer = (ULONG_PTR)m_FileList;
m_FileList = (PFILE_LIST)realloc(m_FileList, m_FileList->ulTotalSize + ALIGN);
m_FileList->ulLeftSize += ALIGN;
m_FileList->ulTotalSize += ALIGN;
m_FileList->CurrentOffset = (ULONG_PTR)m_FileList + CurrentOffset - OldBuffer;
}
m_FileList->ulCount++;
PFILE_INFO lpCurrent = (PFILE_INFO)m_FileList->CurrentOffset;
lpCurrent->FileSize = m_ulong64FileSize;
lpCurrent->MFTOffset = MFT_Offset + i * 1024;
ULONG32 NameLength = wcslen(m_lpFileRealName) * sizeof(WCHAR) + 2;
wcscpy(lpCurrent->szFileName, m_lpFileRealName);
lpCurrent->uNext = sizeof(FILE_INFO) + NameLength;
m_FileList->ulLeftSize -= lpCurrent->uNext;
m_FileList->CurrentOffset = m_FileList->CurrentOffset + lpCurrent->uNext;
break;
}
}
if (!(--m_ulong64TotalClusterNumber))
{
//ShowMessage(_T("没有文件了\r\n找不到就算了\r\n大兄弟"));
return;
}
}
}
MFT_Offset = MFT_Offset + 1024 * 1024;
}
}
else
{
//ShowMessage(_T("不是NTFS 文件系统啊\r\n大兄弟"));
}
}
else
{
return;
}
}
}
FileRecovery::FileRecovery()
{
m_lpDBR = new BYTE[0x200];
m_lpMFT = new BYTE[1024 * 1024];
m_DataRecoverBuffer = new BYTE[1024 * 1024];
m_lpFileRealName = new WCHAR[0x100];
szSystem_Id = (LPBYTE)"NTFS";
m_wClusterSize = 0;
m_ulong64FileSize = 0;
m_dwLittleFileSize = 0;
m_ulong64TotalClusterNumber = 0;
m_dwRecoveried = 0;
m_dwRidentFlag = 0;
m_ulong64StartCluster = 0;
m_dwNumberOfCluster = 0;
m_ulong64SizeOfRun = 0;
m_bStartClusterOffset = 0;
m_bByteToExpressNumOfCluster = 0;
m_FileList = (PFILE_LIST)malloc(ALIGN);
m_FileList->ulCount = 0;
m_FileList->ulTotalSize = ALIGN;
m_FileList->ulLeftSize = ALIGN - sizeof(FILE_LIST);
m_FileList->CurrentOffset = (ULONG_PTR)m_FileList->lpFileInfo;
}
void FileRecovery::RecoveryIndex(ULONG iIndex)
{
PFILE_INFO lpRecovery = m_FileList->lpFileInfo;
while(iIndex --) lpRecovery = (PFILE_INFO)((ULONG_PTR)lpRecovery + (ULONG_PTR)lpRecovery->uNext);
ReadAndRecoveryFile(m_hFile, lpRecovery->szFileName, lpRecovery->MFTOffset);
}
void FileRecovery::GetFileList(WCHAR* szFileName, WCHAR* szFilter,WCHAR cDrive)
{
WCHAR szDrive[100] = L"\\\\.\\A:";
szDrive[4] = szDrive[4] + (cDrive >= L'a' ? cDrive - 'a' : cDrive - L'A');
WCHAR* FileName = szFileName;
WCHAR* Filter = szFilter;
if (szFileName[0] == 0)
{
FileName = NULL;
}
if (Filter[0] == 0)
{
Filter = NULL;
}
GetFileList(FileName, Filter, szDrive);
}
FileRecovery::~FileRecovery()
{
if (m_lpDBR != NULL)
{
delete[] m_lpDBR;
}
if (m_lpMFT != NULL)
{
delete[] m_lpMFT;
}
if (m_DataRecoverBuffer != NULL)
{
delete[] m_DataRecoverBuffer;
}
if (m_lpFileRealName != NULL)
{
delete[] m_lpFileRealName;
}
if (m_hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hFile);
}
if (m_FileList != NULL)
{
free(m_FileList);
}
}
#include "FileRecoveryClass.h"
#include
int main()
{
FileRecovery MyRecovery;
MyRecovery.GetFileList(L"procexp",L"", L'E');
MyRecovery.RecoveryIndex(0);
return 0;
}