NTFS(文件恢复)最简单情况
NTFS文件恢复(最简单情况)
原理:这里我们不深究NTFS 系统的细节,只根据需要了解相关知识。
工具:WinHex,DiskExplorer
问题一:NTFS 是什么?
是Microsoft公司开发的专用文件系统,从Windows NT 3.1开始成为Windows NT家族的标准文件系统(磁盘主要文件系统)。使用更高级的数据结构以提升性能、可靠性和磁盘空间利用率,并附带一系列增强功能,如访问控制列表(ACL)和文件系统日志。
https://zh.wikipedia.org/wiki/NTFS
问题二: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 的遍历又到什么时候截止呢?
刚开始我们提到过,$MFT 文件的第一个记录就是描述它自己的,其中包含了文件的大小,因此我们很容易确定搜索遍历的范围。
至此,我们已经可以愉快地进行删除文件的自定义文件名或扩展名的搜索,实践表明,可以查到的文件的数量有限,即使查找所有的已删除文件,而这仅仅查到而已。所以,数据备份是好习惯,尤其是桌面文件,由于习惯性在桌面进行文件操作,而C 盘文件的增删最频繁,特别容易导致刚刚删除的文件记录已经被覆盖掉,数据不可恢复。另外,时间越短,磁盘文件增删越少恢复概率越高(但是也高不了多少)。
下面我们就为了这渺茫的概率付出努力吧。
问题六:如何找到数据,数据如何恢复?
前面提到过“NTFS 将文件作为属性/属性值的集合来处理,不同的属性类型有着同样的属性头(属性的共有信息)但是有不一样的属性体结构”
我们需要了解文件数据的属性ID,和结构,然后根据其结构来进行数据恢复:
介绍这些之前我们应该先了解两个基本的概念:
常驻属性与非常驻属性:
当属性值能放在MFT 中,属性称为常驻属性,有些属性总是常驻的,这样NTFS 才能确定其它非常驻属性,对于常驻属性,标准头还包含着属性值的偏移量和属性值的长度。
命名与未命名:
命名:已经在$AttrDef 中定义过的属性
未命名:未定义过的属性,需要自己定义属性名
LCN(逻辑簇号) 和 VCN(虚拟簇号):
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 <Windows.h>
#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 0x1000typedef 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 <tchar.h>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++; // 恢复个数加1m_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 <stdio.h>
int main()
{FileRecovery MyRecovery;MyRecovery.GetFileList(L"procexp",L"", L'E');MyRecovery.RecoveryIndex(0);return 0;
}
NTFS(文件恢复)最简单情况相关推荐
- 分区恢复和NTFS文件恢复试验
一.实验室名称:主楼实验室A2-412 二.实验项目名称:分区恢复和NTFS文件恢复试验 三.实验学时:6学时 四.实验原理: 借助fdisk.diskgen软件对磁 ...
- 回收站文件恢复教程:如何找回误删除的重要文件
在使用电脑的过程中,误删除文件是一件很常见的事情,尤其是对于那些不太熟悉电脑操作的人来说,这种情况更为普遍.当重要文件误删除之后,人们最希望的就是能够快速地找回它.其实,在 Windows 操作系统中 ...
- Word临时文件怎么恢复?可持续的文件恢复方法
在word进行文档编辑时,有时候会发生误删除word文件的情况,这个时候word临时文件怎么恢复呢?其实word临时文件还存在于电脑中,本篇文章就来为大家讲解word临时文件怎么恢复. word临时文 ...
- 【Windows数据恢复】Fat32和NTFS文件系统文件恢复
目录 一.磁盘存储结构 (一)分区表 1.MBR 分区表(主引导记录Master Boot Record , mbr) 小实验:将磁盘改为活动分区 2.GPT 分区表(全局唯一标识分区表GUID Pa ...
- FAT32转NTFS文件全部丢失怎么恢复
使用NTFS分区,可以更好的管理磁盘及提高系统的安全性:硬盘为NTFS格式时,碎片整理也快很多.FAT32转NTFS文件全部丢失怎么恢复但是有小部分情况是也会转换失败导致数据全部丢失 工具/软件:光明 ...
- 计算机里如何找ppt文件,PPT文件在电脑上误删怎么恢复?简单方法,值得尝试
PPT文件在电脑上误删怎么恢复?随着使用电脑频率地不断增多,在互联网上召开面对面会议.远程会议或在网上给观众展示的时候,PPT文件是被最为广泛使用的!PPT的入门虽然简单,但是要制作出一档精美又吸引人 ...
- 误删除文件怎样恢复更简单
误删除的文件还是可以恢复的吗?不知道大家以前是否曾经就这个问题感到烦恼,误删除的文件如果是电脑中的文件的话,尚可以在回收站找到,但是如果是其他设备中的文件呢?如果是硬盘中的文件呢?如果是u盘中的文件呢 ...
- oracle的rac控制文件备份,RAC控制文件恢复(三种不同情况)
RAC控制文件恢复(三种不同情况) 测试环境: 系统:LINUX-64 数据库:10.2.0.1 二节点的RAC(RACDB1,RACDB2),存储用的ASM 有备份情况下,丢失控制文件,我们改如何恢 ...
- 博图注册表删除方法_误删回收站文件怎么恢复?简单方法教你一招
误删回收站文件怎么恢复?在日常工作和学习中,相信很多人也都会定期对电脑桌面和磁盘进行清理,删除一些不重要的数据文件,虽然这是一种比较好的习惯,但是很多人在操作过程中也都难免会出现一些错误,不经意间就将 ...
最新文章
- 【博客】博客资源汇总
- 【原创】FlashFXP_4.0.0.1510 值得研究
- TypeScript 3.9 发布
- Eclipse中Java编程时快速生成set和get方法
- loadrunner11录制不成功解决方法
- ligerui+json_002_Grid用法、属性总结
- 机器学习(六)——降维处理原理
- C语言自己编写头文件
- 服务器远程开多个桌面,远程桌面多开,远程桌面多开的工具介绍,操作方法
- miui删除内置不卡米教程_MIUI11卸载系统自带软件,无需ROOT也可以
- 冰蝎3 冰蝎2 behinder流量分析 流量解密
- Linux电脑睡眠后黑屏打不开,电脑睡眠唤醒后一直是黑屏状态怎么办?
- input输入框只能输入字母
- 《数值分析》-- The great 平方逼近
- vue项目的简体繁体切换
- 同一局域网下 macOS 和 windows 电脑 如何快速共享文件
- 马哥教育大数据专家:深入解读大数据的就业前景
- 如何用MATLAB绘制真值表,编写真值表
- MATLAB Simulink
- MathWorks 中国
- 1166 - Unknown error 1166[mysql 错误
- 手把手教你制作 中英文 词云 | python demo
热门文章
- 《果壳中的C# C# 5.0 权威指南》 (09-26章) - 学习笔记
- 教你怎么筛选排除百度搜索引擎的屏蔽违规词
- 802.11n HT模式配置说明
- 一意孤行亚马逊----一个钓鱼疯子的巴西亚马逊之行( 11.9月 27日 最惨的一天 ) 作者:咸水鱼...
- 常见主从复制报错处理案例
- 深度学习的过拟合与欠拟合问题的介绍与解决方案
- Apereo CAS 5.0.X 配置数据库认证方式
- 实现ensp的单臂路由
- 超好用的Server酱,推送微信消息
- 旅行青蛙分析(Android篇)