www.pudn.com > NTFSUndelete_src.zip > MFTRecord.cpp


// MFTRecord.cpp: implementation of the CMFTRecord class. 
// 
////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "MFTRecord.h" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
////////////////////////////////////////////////////////////////////// 
 
CMFTRecord::CMFTRecord() 
{ 
	m_hDrive = 0; 
 
	m_dwMaxMFTRecSize = 1023; // usual size 
	m_pMFTRecord = 0; 
	m_dwCurPos = 0; 
 
	m_puchFileData = 0; // collected file data buffer 
	m_dwFileDataSz = 0; // file data size , ie. m_pchFileData buffer length 
 
	memset(&m_attrStandard,0,sizeof(ATTR_STANDARD)); 
	memset(&m_attrFilename,0,sizeof(ATTR_FILENAME)); 
 
	m_bInUse = false;; 
} 
 
CMFTRecord::~CMFTRecord() 
{ 
	if(m_puchFileData) 
		delete m_puchFileData; 
	m_puchFileData = 0; 
	m_dwFileDataSz = 0; 
} 
 
// set the drive handle 
void CMFTRecord::SetDriveHandle(HANDLE hDrive) 
{ 
	m_hDrive = hDrive; 
} 
 
// set the detail 
//  n64StartPos is the byte from the starting of the physical disk 
//  dwRecSize is the record size in the MFT table 
//  dwBytesPerCluster is the bytes per cluster 
int CMFTRecord::SetRecordInfo(LONGLONG  n64StartPos, DWORD dwRecSize, DWORD dwBytesPerCluster) 
{ 
	if(!dwRecSize) 
		return ERROR_INVALID_PARAMETER; 
 
	if(dwRecSize%2) 
		return ERROR_INVALID_PARAMETER; 
 
	if(!dwBytesPerCluster) 
		return ERROR_INVALID_PARAMETER; 
 
	if(dwBytesPerCluster%2) 
		return ERROR_INVALID_PARAMETER; 
 
	m_dwMaxMFTRecSize = dwRecSize; 
	m_dwBytesPerCluster = dwBytesPerCluster; 
	m_n64StartPos =  n64StartPos; 
	return ERROR_SUCCESS; 
} 
 
 
/// puchMFTBuffer is the MFT record buffer itself (normally 1024 bytes) 
//  dwLen is the MFT record buffer length 
//  bExcludeData = if true the file data will not be extracted 
//                 This is useful for only file browsing 
int CMFTRecord::ExtractFile(BYTE *puchMFTBuffer, DWORD dwLen, bool bExcludeData) 
{ 
	if(m_dwMaxMFTRecSize > dwLen) 
		return ERROR_INVALID_PARAMETER; 
	if(!puchMFTBuffer) 
		return ERROR_INVALID_PARAMETER; 
 
	NTFS_MFT_FILE	ntfsMFT; 
	NTFS_ATTRIBUTE	ntfsAttr; 
 
	BYTE *puchTmp = 0; 
	BYTE *uchTmpData =0; 
	DWORD dwTmpDataLen; 
	int nRet; 
	 
	m_pMFTRecord = puchMFTBuffer; 
	m_dwCurPos = 0; 
 
	if(m_puchFileData) 
		delete m_puchFileData; 
	m_puchFileData = 0; 
	m_dwFileDataSz = 0; 
 
	// read the record header in MFT table 
	memcpy(&ntfsMFT,&m_pMFTRecord[m_dwCurPos],sizeof(NTFS_MFT_FILE)); 
 
	if(memcmp(ntfsMFT.szSignature,"FILE",4)) 
		return ERROR_INVALID_PARAMETER; // not the right signature 
 
	m_bInUse = (ntfsMFT.wFlags&0x01); //0x01  	Record is in use 
									  //0x02 	Record is a directory 
 
	//m_dwCurPos = (ntfsMFT.wFixupOffset + ntfsMFT.wFixupSize*2);  
	m_dwCurPos = ntfsMFT.wAttribOffset; 
 
	do 
	{	// extract the attribute header 
		memcpy(&ntfsAttr,&m_pMFTRecord[m_dwCurPos],sizeof(NTFS_ATTRIBUTE)); 
 
		switch(ntfsAttr.dwType) // extract the attribute data  
		{ 
			// here I haven' implemented the processing of all the attributes. 
			//  I have implemented attributes necessary for file & file data extraction 
		case 0://UNUSED 
			break; 
 
		case 0x10: //STANDARD_INFORMATION 
			nRet = ExtractData(ntfsAttr,uchTmpData,dwTmpDataLen); 
			if(nRet) 
				return nRet; 
			memcpy(&m_attrStandard,uchTmpData,sizeof(ATTR_STANDARD)); 
 
			delete uchTmpData; 
			uchTmpData = 0; 
			dwTmpDataLen = 0; 
			break; 
 
		case 0x30: //FILE_NAME 
			nRet = ExtractData(ntfsAttr,uchTmpData,dwTmpDataLen); 
			if(nRet) 
				return nRet; 
			memcpy(&m_attrFilename,uchTmpData,dwTmpDataLen); 
 
			delete uchTmpData; 
			uchTmpData = 0; 
			dwTmpDataLen = 0; 
 
			break; 
 
		case 0x40: //OBJECT_ID 
			break; 
		case 0x50: //SECURITY_DESCRIPTOR 
			break; 
		case 0x60: //VOLUME_NAME 
			break; 
		case 0x70: //VOLUME_INFORMATION 
			break; 
		case 0x80: //DATA 
			if(!bExcludeData)  
			{ 
				nRet = ExtractData(ntfsAttr,uchTmpData,dwTmpDataLen); 
				if(nRet) 
					return nRet; 
 
				if(!m_puchFileData) 
				{ 
					m_dwFileDataSz = dwTmpDataLen; 
					m_puchFileData = new BYTE[dwTmpDataLen]; 
					 
					memcpy(m_puchFileData,uchTmpData,dwTmpDataLen); 
				} 
				else 
				{ 
					puchTmp = new BYTE[m_dwFileDataSz+dwTmpDataLen]; 
					memcpy(puchTmp,m_puchFileData,m_dwFileDataSz); 
					memcpy(puchTmp+m_dwFileDataSz,uchTmpData,dwTmpDataLen); 
 
					m_dwFileDataSz += dwTmpDataLen; 
					delete m_puchFileData; 
					m_puchFileData = puchTmp; 
				} 
 
				delete uchTmpData; 
				uchTmpData = 0; 
				dwTmpDataLen = 0; 
			} 
			break; 
 
		case 0x90: //INDEX_ROOT 
		case 0xa0: //INDEX_ALLOCATION 
			// todo: not implemented to read the index mapped records 
			return ERROR_SUCCESS; 
			continue; 
			break; 
		case 0xb0: //BITMAP 
			break; 
		case 0xc0: //REPARSE_POINT 
			break; 
		case 0xd0: //EA_INFORMATION 
			break; 
		case 0xe0: //EA 
			break; 
		case 0xf0: //PROPERTY_SET 
			break; 
		case 0x100: //LOGGED_UTILITY_STREAM 
			break; 
		case 0x1000: //FIRST_USER_DEFINED_ATTRIBUTE 
			break; 
 
		case 0xFFFFFFFF: // END  
			if(uchTmpData) 
				delete uchTmpData; 
			uchTmpData = 0; 
			dwTmpDataLen = 0; 
			return ERROR_SUCCESS; 
 
		default: 
			break; 
		}; 
 
		m_dwCurPos += ntfsAttr.dwFullLength; // go to the next location of attribute 
	} 
	while(ntfsAttr.dwFullLength); 
 
	if(uchTmpData) 
		delete uchTmpData; 
	uchTmpData = 0; 
	dwTmpDataLen = 0; 
	return ERROR_SUCCESS; 
} 
 
// extract the attribute data from the MFT table 
//   Data can be Resident & non-resident 
int CMFTRecord::ExtractData(NTFS_ATTRIBUTE ntfsAttr, BYTE *&puchData, DWORD &dwDataLen) 
{ 
	DWORD dwCurPos = m_dwCurPos; 
 
	if(!ntfsAttr.uchNonResFlag) 
	{// residence attribute, this always resides in the MFT table itself 
		 
		puchData = new BYTE[ntfsAttr.Attr.Resident.dwLength]; 
		dwDataLen = ntfsAttr.Attr.Resident.dwLength; 
 
		memcpy(puchData,&m_pMFTRecord[dwCurPos+ntfsAttr.Attr.Resident.wAttrOffset],dwDataLen); 
	} 
	else 
	{// non-residence attribute, this resides in the other part of the physical drive 
 
		if(!ntfsAttr.Attr.NonResident.n64AllocSize) // i don't know Y, but fails when its zero 
			ntfsAttr.Attr.NonResident.n64AllocSize = (ntfsAttr.Attr.NonResident.n64EndVCN - ntfsAttr.Attr.NonResident.n64StartVCN) + 1; 
 
		// ATTR_STANDARD size may not be correct 
		dwDataLen = ntfsAttr.Attr.NonResident.n64RealSize; 
 
		// allocate for reading data 
		puchData = new BYTE[ntfsAttr.Attr.NonResident.n64AllocSize]; 
 
		BYTE chLenOffSz; // length & offset sizes 
		BYTE chLenSz; // length size 
		BYTE chOffsetSz; // offset size 
		LONGLONG n64Len, n64Offset; // the actual lenght & offset 
		LONGLONG n64LCN =0; // the pointer pointing the actual data on a physical disk 
		BYTE *pTmpBuff = puchData; 
		int nRet; 
 
		dwCurPos += ntfsAttr.Attr.NonResident.wDatarunOffset;; 
 
		for(;;) 
		{ 
			///// read the length of LCN/VCN and length /////////////////////// 
			chLenOffSz = 0; 
 
			memcpy(&chLenOffSz,&m_pMFTRecord[dwCurPos],sizeof(BYTE)); 
 
			dwCurPos += sizeof(BYTE); 
 
			if(!chLenOffSz) 
				break; 
 
			chLenSz		= chLenOffSz & 0x0F; 
			chOffsetSz	= (chLenOffSz & 0xF0) >> 4; 
 
			///// read the data length //////////////////////////////////////// 
	 
			n64Len = 0; 
 
			memcpy(&n64Len,&m_pMFTRecord[dwCurPos],chLenSz); 
 
			dwCurPos += chLenSz; 
 
			///// read the LCN/VCN offset ////////////////////////////////////// 
	 
			n64Offset = 0; 
 
			memcpy(&n64Offset,&m_pMFTRecord[dwCurPos],chOffsetSz); 
			 
			dwCurPos += chOffsetSz; 
 
			////// if the last bit of n64Offset is 1 then its -ve so u got to make it -ve ///// 
			if((((char*)&n64Offset)[chOffsetSz-1])&0x80) 
				for(int i=sizeof(LONGLONG)-1;i>(chOffsetSz-1);i--) 
					((char*)&n64Offset)[i] = 0xff; 
			 
			n64LCN += n64Offset; 
			 
			n64Len *= m_dwBytesPerCluster; 
			///// read the actual data ///////////////////////////////////////// 
			/// since the data is available out side the MFT table, physical drive should be accessed 
			nRet = ReadRaw(n64LCN,pTmpBuff,(DWORD&)n64Len); 
			if(nRet) 
				return nRet; 
 
			pTmpBuff += n64Len; 
		} 
	} 
	return ERROR_SUCCESS; 
} 
 
// read the data from the physical drive 
int CMFTRecord::ReadRaw(LONGLONG n64LCN, BYTE *chData, DWORD &dwLen) 
{ 
	int nRet; 
 
	LARGE_INTEGER n64Pos; 
	 
	n64Pos.QuadPart = (n64LCN)*m_dwBytesPerCluster; 
	n64Pos.QuadPart += m_n64StartPos; 
 
	//   data is available in the relative sector from the begining od the drive	 
	//    so point that data 
	nRet = SetFilePointer(m_hDrive,n64Pos.LowPart,&n64Pos.HighPart,FILE_BEGIN); 
	if(nRet == 0xFFFFFFFF) 
		return GetLastError(); 
 
	BYTE *pTmp			= chData; 
	DWORD dwBytesRead	=0; 
	DWORD dwBytes		=0; 
	DWORD dwTotRead		=0; 
 
	while(dwTotRead