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 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 <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++;	// 恢复个数加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 <stdio.h>
int main()
{
	FileRecovery	MyRecovery;
	MyRecovery.GetFileList(L"procexp",L"", L'E');
	MyRecovery.RecoveryIndex(0);
	return 0;
}



  • 11
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值