NTFS – 获取盘符内所有数据
概述
1:NTFS(New Technology File System)是Windows NT内核的系列操作系统支持的、一个特别为网络和磁盘配额、文件加密等管理安全特性设计的磁盘格式,提供长文件名、数据保护和恢复,能通过目录和文件许可实现安全性,并支持跨越分区。
百度百科
2:新技术文件系统(NTFS) 是Microsoft开发的专有 日志文件系统。从Windows NT 3.1开始,它是Windows NT家族的默认文件系统。它取代了文件分配表(FAT) 作为Windows上的首选文件系统,并且在Linux和BSD中也受支持。NTFS 读写支持是使用Linux中称为NTFS3和NTFS-3G的免费和开源内核实现提供的BSD中的驱动程序。 Windows 可以将FAT32 /16/12 转换为 NTFS,无需重写所有文件。NTFS 使用几个通常对用户隐藏的文件来存储有关存储在驱动器上的其他文件的元数据,这有助于提高读取数据时的速度和性能。与 FAT 和高性能文件系统(HPFS) 不同,NTFS 支持访问控制列表(ACL)、文件系统加密、透明压缩、稀疏文件和文件系统日志。NTFS 还支持卷影复制允许在系统运行时对其进行备份,但卷影副本的功能因 Windows 的不同版本而异。
维基百科
二、资料
1:文档
NTFS文件系统结构探索
密码:4nbu
三、图解
1:boot头部数据
/*序列号*/
typedef union _SerialNumber{
/*+0x48*/ union _32{
quint8 SerialNumber_32[4];
quint32 SerialNumber_SN;
} SerialNumberLow_32;
/*+0x48*/ quint8 SerialNumber_64[8];
}SerialNumber,pSerialNumber;
typedef struct _NTFS_Boot_Sector{
/*+0x00*/ quint8 JmpCode[3]; // 跳转指令
/*+0x03*/ quint8 OemID[8]; // 文件系统ID
/*+0x0B*/ BIOS_PARAMETER_BLOCK PackedBpb; // BPB
/*+0x24*/ quint32 Unused; // 未使用,总是为
/*+0x28*/ quint64 NumberSectors; // 扇区总数
/*+0x30*/ quint64 MftStartLcn; // 开始C# $MFT (簇) 乘以 BIOS_PARAMETER_BLOCK.SectorsPerCluster 值得到扇区号
/*+0x38*/ quint64 Mft2StartLcn; // 开始C# $MFTMirr (簇)
/*+0x40*/ quint8 ClustersPerFileRecordSegment; // 文件记录大小指示器 2的负一次方× -10=1024
/*+0x41*/ quint8 Reserved0[3]; // 未使用
/*+0x44*/ quint8 DefaultClustersPerIndexAllocationBuffer; // 簇/索引块
/*+0x45*/ quint8 Reserved1[3]; // 未使用
/*+0x48*/ SerialNumber serialNumber; // 64位序列号(卷标)
/*+0x50*/ quint32 Checksum; // 校验和
/*+0x54*/ quint8 BootStrap[426]; // 启动代码
/*+0x1FE*/ quint16 RecordEndSign; // 0xAA55 结束标记
}NTFS_Boot_Sector, *pNTFS_Boot_Sector;
2:MFT表数据
通过 BOOT 头数据 的MftStartLcn 簇 *0x1000 获取mft文件的开始位置,通过第一个mft文件记录区,获取所有mft文件的记录和偏移,每个mft文件占两个扇区。
3:获取盘符中的所有文件
四、代码实现
//获取mft文件头
bool CNTFS::NTFS_MFTFileHeader(S_FILE_RECORD& fileRecordHead,QByteArray& rawData)
{
//NTFS_unsetError();
errno_t err = memcpy_s(&fileRecordHead,sizeof(S_FILE_RECORD),rawData.data(),sizeof(S_FILE_RECORD));//返回值为0,拷贝成功
if(err)
{
NTFS_setErrorType(CopyDataError);
return false;
}
if(!NTFS_isFILE(fileRecordHead,rawData))
return false;
return true;
}
//判断是file记录格式?
bool CNTFS::NTFS_isFILE(S_FILE_RECORD& fileRecordHead,QByteArray &rawData)
{
//NTFS_unsetError();
if(fileRecordHead.Ntfs.Type != 0x454c4946)
{
NTFS_setErrorType(NoMftFileErrot);
return false;
}
return NTFS_fixup( fileRecordHead, rawData);
}
//USA和USN的建立称为fixup。
bool CNTFS::NTFS_fixup(S_FILE_RECORD& fileRecordHead,QByteArray& rawData)
{
//NTFS_unsetError();
quint16 USN_1 =0;
quint16 USN_2 =0;
errno_t err = memcpy_s(&USN_1,2,rawData.mid(0x1FE,2).data(),2);//返回值为0,拷贝成功
errno_t err2 = memcpy_s(&USN_2,2,rawData.mid(0x3FE,2).data(),2);//返回值为0,拷贝成功
if(err || err2)
{
NTFS_setErrorType(CopyDataError);
return false;
}
/*这两项的作用在于对记录(文件记录,索引记录,RCRD记录,RSTR记录等等)进行扇区检查。
* 其中,USA中的内容是该记录的每一个扇区的最后两个字节。
* 同时,用USN的值取代每一个扇区的最后两个字节。
* 这样,当记录被读取的时候,从文件头部分读取USN,并将其与该记录的每一个扇区的最后两位进行比较,如果相等,说明扇区无误,
* 然后,将USA按顺序读入记录的各个扇区的最后一个字中。USA和USN的建立称为fixup。
* 更新序号(USN)的值是一个记载被保护记录写入硬盘次数的循环数。
* 其中,0和0XFFFF不被使用。
*/
if((fileRecordHead.MFTUSN & USN_1) != USN_2 )//三个数相等比较
{
NTFS_setErrorType(CopyDataError);
return false;
}
rawData.replace(0x1FE,2,fileRecordHead.MFTUSA_0,2);
rawData.replace(0x3FE,2,fileRecordHead.MFTUSA_1,2);
return true;
}
//获取文件的基本信息
bool CNtfsInfo::getMFTBasicInfo(MFT_BASIC_INFO& basicInfo,QByteArray byteData)
{
S_FILE_RECORD fileRecordHead;//文件头
bool isOk = NTFS_MFTFileHeader(fileRecordHead,byteData);
if(!isOk)
return false;
if(basicInfo.ExMFT != fileRecordHead.BaseFileRecord )//初始PMFT等于0;当mft扩展 不为0,直接返回;递归调用PMFT和当前mft扩展相等---遍历时防止多次调用同一个mft扩展
{
return false;
}
if(basicInfo.ExMFT == 0)//使用同一个mft(扩展mft为0的 mft)
{
basicInfo.ExMFT = fileRecordHead.MFTRecordNumber;
}
basicInfo.attBody.flag = fileRecordHead.Flags;
quint32 allLength = fileRecordHead.BytesInUse-4;//所有属性的大小
quint32 byteOffset = fileRecordHead.AttributeOffset;
int whileCount = allLength ;
while(byteOffset<allLength)
{
if((--whileCount) == 0)//死循环异常
{
NTFS_setErrorType(CNTFS::WhileError);
return false;
}
if(!(byteData.mid(byteOffset,sizeof (quint32)).toHex()).compare("FFFFFFFF",Qt::CaseInsensitive))
{
return true;
}
S_NTFS_ATTRIBUTE_HEADER attH;//属性头
qint8 NonResidentFlag = NTFS_MFTAtt(attH,byteData.mid(byteOffset,sizeof(S_NTFS_ATTRIBUTE_HEADER)));//获取属性 返回0常驻,1非常驻,-1:失败
if(NonResidentFlag == -1)
return false;
switch(attH.AttributeType)
{
//0x10
case AttributeStandardInformation:
{
byteOffset += attH.NonResidentFlag==0 ? attH.CCommon.CResident.StreamOffset : attH.CCommon.CNonResident.RunListOffset;
//0常驻
if(attH.NonResidentFlag==0 && (attH.AttributeLength - attH.CCommon.CResident.StreamOffset)>0)
{
S_ATTRIBUTE_0X10 x10;
if(!NTFS_0X10Att(x10,byteData.mid(byteOffset,sizeof(S_ATTRIBUTE_0X10))))
return false;
basicInfo.attBody.creationTime = x10.CreationTime;//获取文件创建时间
basicInfo.attBody.changeTime = x10.ChangeTime;//获取文件最后修改时间
basicInfo.attBody.lastReadTime = x10.LastReadTime;//获取文件最后读时间
basicInfo.attBody.FileAttributes = x10.FileAttribute;//获取文件属性
byteOffset += (attH.AttributeLength-attH.CCommon.CResident.StreamOffset);
}
//1非常驻
else if(attH.NonResidentFlag==1 && (attH.AttributeLength - attH.CCommon.CNonResident.RunListOffset)>0)//预防
{
byteOffset += (attH.AttributeLength - attH.CCommon.CNonResident.RunListOffset);
}
}
break;
//0x20
case AttributeAttributeList:
{
byteOffset += attH.NonResidentFlag==0 ? attH.CCommon.CResident.StreamOffset : attH.CCommon.CNonResident.RunListOffset;
if(attH.NonResidentFlag==0 && (attH.AttributeLength - attH.CCommon.CResident.StreamOffset)>0)
{
QList<S_ATTRIBUTE_0X20> x20L = NTFS_0X20Att(byteData.mid(byteOffset,attH.AttributeLength - attH.CCommon.CResident.StreamOffset));
for(int i=0;i<x20L.count();i++)
{
QList<MFTNUM> _temp;
//只获取不同的mft 属性x10和x30和x80
if((x20L.at(i).AttributeType == AttributeStandardInformation || x20L.at(i).AttributeType == AttributeFileName || x20L.at(i).AttributeType == AttributeData )
&& x20L.at(i).Fdir.FileReferenceNumber != fileRecordHead.MFTRecordNumber)
{
if(!_temp.contains(x20L.at(i).Fdir.FileReferenceNumber))//以防多次执行,不同的mft只执行一次
{
_temp.append(x20L.at(i).Fdir.FileReferenceNumber);
quint64 offset = getMTFOffset(x20L.at(i).Fdir.FileReferenceNumber);
QByteArray data = getRawData(m_NTFSOffset + offset,BYTES_PER_MFT_FILE);
if(data.isNull())
return false;
bool isOk = getMFTBasicInfo(basicInfo,data);
if(!isOk)
{
return false;
}
}
}
}
byteOffset += (attH.AttributeLength-attH.CCommon.CResident.StreamOffset);
}
else if(attH.NonResidentFlag==1 && (attH.AttributeLength - attH.CCommon.CNonResident.RunListOffset)>0)//预防
{
QList<S_DATARUN> DRunList;
DRunList = NTFS_DatasRun(byteData.mid(byteOffset,attH.AttributeLength - attH.CCommon.CNonResident.RunListOffset));
for(int i=0;i<DRunList.count();i++)
{
QByteArray data = getRawData(m_NTFSOffset + DRunList[i].dataStartCluster * BYTES_PER_CLUSTER,attH.CCommon.CNonResident.StreamRealSize);
if(!data.isNull())
{
QList<S_ATTRIBUTE_0X20> x20L = NTFS_0X20Att(data);
for(int i=0;i<x20L.count();i++)
{
QList<MFTNUM> _temp;
//只获取不同的mft 属性x10和x30和x80
if((x20L.at(i).AttributeType == AttributeStandardInformation || x20L.at(i).AttributeType == AttributeFileName || x20L.at(i).AttributeType == AttributeData )
&& x20L.at(i).Fdir.FileReferenceNumber != fileRecordHead.MFTRecordNumber)
{
if(!_temp.contains(x20L.at(i).Fdir.FileReferenceNumber))//以防多次执行,不同的mft只执行一次
{
_temp.append(x20L.at(i).Fdir.FileReferenceNumber);
quint64 offset = getMTFOffset(x20L.at(i).Fdir.FileReferenceNumber);
QByteArray data = getRawData(m_NTFSOffset + offset,BYTES_PER_MFT_FILE);
if(data.isNull())
return false;
bool isOk = getMFTBasicInfo(basicInfo,data);
if(!isOk)
{
return false;
}
}
}
}
}
}
byteOffset += (attH.AttributeLength - attH.CCommon.CNonResident.RunListOffset);
}
}
break;
//x30
case AttributeFileName:
{
byteOffset += attH.NonResidentFlag==0 ? attH.CCommon.CResident.StreamOffset : attH.CCommon.CNonResident.RunListOffset;
if(attH.NonResidentFlag==0 && (attH.AttributeLength - attH.CCommon.CResident.StreamOffset)>0)
{
S_ATTRIBUTE_0X30 x30;
QByteArray data = byteData.mid(byteOffset,attH.AttributeLength - attH.CCommon.CResident.StreamOffset);
QByteArray dataName = NTFS_0X30AttName(x30, data);
if(!dataName.isNull())
{
if(x30.NameType != 2)//忽略DOS命名空间
{
basicInfo.attBody.refCount++;//当前mft,包含refCount个文件
S_DATA_BODY* dataBody =new S_DATA_BODY;
if(fileRecordHead.MFTRecordNumber == ROOT_DIRECTORY_MFT_NUMBER)//mft == 5 ,根目录(盘符)MFTNumber在一个盘符中是唯一的
{
dataBody->name = m_partitionName.toUtf8();
dataBody->MFTNumber = fileRecordHead.MFTRecordNumber;//获取mft
dataBody->PMFTNumber = x30.Fdir.DirectoryFile;//获取pMft
basicInfo.attBody.fileAiiocSize = m_initDatasInfo.info.bytesTotal;
basicInfo.attBody.fileRealSize = usedSpaceSize();
basicInfo.fileL.append(dataBody);
return true;
}
dataBody->name = dataName;
dataBody->MFTNumber = basicInfo.ExMFT;//获取mft
dataBody->PMFTNumber = x30.Fdir.DirectoryFile;//获取pMft
basicInfo.fileL.append(dataBody);
}
byteOffset += (attH.AttributeLength-attH.CCommon.CResident.StreamOffset);
}
}
else if(attH.NonResidentFlag==1 && (attH.AttributeLength - attH.CCommon.CNonResident.RunListOffset)>0)//预防
{
byteOffset += (attH.AttributeLength - attH.CCommon.CNonResident.RunListOffset);
}
}
break;
//0x80--存放文件的位置->获取文件的大小
case AttributeData:
{
byteOffset += attH.NonResidentFlag==0 ? attH.CCommon.CResident.StreamOffset : attH.CCommon.CNonResident.RunListOffset;
//常驻大小,不会超过1kb
if(attH.NonResidentFlag==0 && (attH.AttributeLength - attH.CCommon.CResident.StreamOffset)>=0)
{
if(basicInfo.attBody.fileAiiocSize == 0)//数据大小主要以非常驻为准,非常驻有数据就不获取常驻数据大小 -- 非常驻bodyAtt->fileAiiocSize一定是大于0的
{
basicInfo.attBody.fileRealSize = attH.CCommon.CResident.StreamLength;
basicInfo.attBody.fileAiiocSize = 0;
}
byteOffset += (attH.AttributeLength-attH.CCommon.CResident.StreamOffset);
}
//非常驻大小
else if(attH.NonResidentFlag==1 && (attH.AttributeLength - attH.CCommon.CNonResident.RunListOffset)>=0)
{
basicInfo.attBody.fileRealSize = attH.CCommon.CNonResident.StreamRealSize;
basicInfo.attBody.fileAiiocSize = attH.CCommon.CNonResident.StreamAiiocSize;
byteOffset += (attH.AttributeLength - attH.CCommon.CNonResident.RunListOffset);
}
}
break;
case 0xFFFFFFFF:
{
return true;
}
default:
byteOffset += (attH.AttributeLength);
break;
}
}
return false;
}
//获取所有文件信息
void CNtfsInfo::getAllFilesBasicInfo()
{
qDebug()<<m_name_uuid<<"获取所有文件信息Start!";
//恢复默认值
m_initDatasInfo.dataBodys.clear();
m_initDatasInfo.dataBodyCount._falg_0_count=0;
m_initDatasInfo.dataBodyCount._falg_1_count=0;
m_initDatasInfo.dataBodyCount._falg_2_count=0;
m_initDatasInfo.dataBodyCount._falg_3_count=0;
m_initDatasInfo.dataBodyCount._falg_other_count=0;
QElapsedTimer t;
t.start();//将此时间设置为当前时间
for(int i=0;i<m_mftTable.count();++i)
{
quint64 byteOffset=0;
quint64 startOffset = m_mftTable[i].dataStartByte;
quint64 dataLength = m_mftTable[i].datalengthByte;
while(byteOffset<dataLength && !QThread::currentThread()->isInterruptionRequested())
{
MFT_BASIC_INFO basicInfo;
bool isOk=false;
QByteArray byteData = getRawData(startOffset + m_NTFSOffset,BYTES_PER_MFT_FILE);
if(!byteData.isNull())
{
isOk = getMFTBasicInfo(basicInfo,byteData);
}
startOffset += BYTES_PER_MFT_FILE;
byteOffset += BYTES_PER_MFT_FILE;
if(!isOk)
{
continue;
}
if(basicInfo.fileL.count()==1)
{
basicInfo.fileL.at(0)->bodyAtt = basicInfo.attBody;
dataCountAdd(basicInfo.fileL.at(0)->bodyAtt.flag);
m_initDatasInfo.dataBodys.insertMulti(basicInfo.fileL.at(0)->MFTNumber,basicInfo.fileL.at(0));
}
else if(basicInfo.fileL.count()>1)
{
for(int i=0;i<basicInfo.fileL.count();i++)
{
basicInfo.fileL.at(i)->bodyAtt = basicInfo.attBody;
dataCountAdd(basicInfo.fileL.at(i)->bodyAtt.flag);
m_initDatasInfo.dataBodys.insertMulti(basicInfo.fileL.at(i)->MFTNumber,basicInfo.fileL.at(i));
}
}
}
}
qDebug()<<m_name_uuid<<"获取所有文件信息End:"+QString::number(t.elapsed())+" ms --- Count:"<<m_initDatasInfo.dataBodys.count();
}
上面代码用到的结构体,可在
这里
查找