NTFS(文件恢复)最简单情况

来源:互联网 发布:java环境变量配置脚本 编辑:程序博客网 时间:2024/06/16 17:12

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_{ULONG32Type;ULONG32AttributeLength;BYTEbUnrsident;BYTENameLength;USHORTNameOffset;USHORTTrueValue;USHORTFlags;ULONG64BeginVCN;ULONG64EndVCN;USHORTRunOffset;USHORTDepress;ULONG32NoMings;ULONG64AttributeAllocSize;ULONG64AttributeTrueSize;ULONG64CompressSize;}UNNAME_UNRSIDENT, *PUNNAME_UNRSIDENT;class FileRecovery{#define LENGTH1024*1024private:LPBYTEm_lpDBR;LPBYTEm_lpMFT;LPBYTEm_DataRecoverBuffer;PWCHARm_lpFileRealName;LPBYTEszSystem_Id;WORDm_wClusterSize;ULONG64 m_ulong64FileSize;DWORDm_dwLittleFileSize;ULONG64m_ulong64TotalClusterNumber;DWORDm_dwRecoveried;DWORDm_dwRidentFlag;ULONG64m_ulong64StartCluster;DWORDm_dwNumberOfCluster;ULONG64m_ulong64SizeOfRun;BYTEm_bStartClusterOffset;BYTEm_bByteToExpressNumOfCluster;HANDLEm_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_{ULONG64MFTOffset;ULONG64FileSize;USHORTuNext;WCHAR szFileName[1];}FILE_INFO, *PFILE_INFO;typedef struct _FILE_LIST_{ULONGulCount;ULONGulTotalSize;ULONGulLeftSize;ULONG_PTRCurrentOffset;FILE_INFO lpFileInfo[1];}FILE_LIST, *PFILE_LIST;PFILE_LISTm_FileList;};
实现:

#include "FileRecoveryClass.h"#include <tchar.h>void FileRecovery::ToCaps(PWCHAR lpFileName){PWCHARtravel = lpFileName;WCHARwChar = *travel;while (wChar != 0){if (wChar >= 0x61 && wChar <= 0x7a){*travel = wChar - 0x20;}travel += 1;wChar = *travel;}}BOOL FileRecovery::ReadSectors(HANDLEhDevice, ULONG64 dwStartSector,DWORD wSectors, LPBYTE lpSectBuff)// 对磁盘扇区数据的读取{LONGulHigh = dwStartSector >> 32;LONGulLow = dwStartSector % 0x100000000;DWORDdwReturn = 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){BYTEv1 = *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){LPBYTElpAttribute = lpMFT + *(WORD*)(lpMFT + 0x14);LPBYTElpTravel = lpAttribute;while (1){if (*(DWORD*)lpTravel == 0x80)// 代表的是数据{if (*(lpTravel + 0x9) == 0) // 没有属性名{if (*(lpTravel + 0x8) == 0) // 未命名常驻属性,这里是小文件的数据{DWORDdwAttributeHeadLength = *(WORD*)(lpTravel + 0x14);// 属性头长度DWORDdwAttributeTotalLength = *(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_UNRSIDENTEsayCalculate = (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){DWORDdwNumberToOperate = 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, LPBYTElpByteArray){BYTEbFindFileNameLength = wcslen(szFileName);BYTEbFindFilterLength = wcslen(szFilter);LPBYTElpFind = lpByteArray;if (*(DWORD*)lpFind == 0x454c4946) // FILE,如果不是合法MFT,代表已经读完了卷中所有MFT{if (*(WORD*)(lpFind + 0x16) != 0)// 当前文件未被删除{return 1;}else{LPBYTElpAttribute = lpFind + *(WORD*)(lpFind + 0x14);while (1){if ((*(WORD*)lpAttribute) == 0x30) // 是30H 属性,即文件名属性{LPBYTElpBegin = lpAttribute + *(WORD*)(lpAttribute + 0x14);BYTE bNameLength = *(PBYTE)(lpBegin + 0x40);if (bFindFileNameLength && bNameLength < bFindFileNameLength){return 1;}else{PWCHARlpFileName = (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){WCHARszTemp[MAX_PATH] = { 0 };WCHARszTempFilter[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{LPBYTElpAttribute = (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 ULONGCount = 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);LPBYTElpAttribute = m_DataRecoverBuffer + *(WORD*)(m_DataRecoverBuffer + 0x14);LPBYTElpTravel = lpAttribute;while (1){if (*(DWORD*)lpTravel == 0x80)// 代表的是数据{if (*(lpTravel + 0x9) == 0) // 没有属性名{if (*(lpTravel + 0x8) == 0) // 未命名常驻属性,这里是小文件的数据{DWORDdwAttributeHeadLength = *(WORD*)(lpTravel + 0x14);// 属性头长度DWORDdwAttributeTotalLength = *(DWORD*)(lpTravel + 0x4);// 总的属性长度m_ulong64FileSize = dwAttributeTotalLength - dwAttributeHeadLength;// 文件大小break;}else if (*(lpTravel + 0x8) == 1)// 未命名非常驻属性,这里是较大文件的真实文件数据{PUNNAME_UNRSIDENTEsayCalculate = (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_PTRCurrentOffset = m_FileList->CurrentOffset;ULONG_PTROldBuffer = (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_INFOlpCurrent = (PFILE_INFO)m_FileList->CurrentOffset;lpCurrent->FileSize = m_ulong64FileSize;lpCurrent->MFTOffset = MFT_Offset + i * 1024;ULONG32NameLength = 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){WCHARszDrive[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(){FileRecoveryMyRecovery;MyRecovery.GetFileList(L"procexp",L"", L'E');MyRecovery.RecoveryIndex(0);return 0;}



0 0
原创粉丝点击