aboutsummaryrefslogtreecommitdiff
path: root/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_FileRecord.h
diff options
context:
space:
mode:
authorclymb3r <bialek.joseph@gmail.com>2013-10-01 09:47:05 -0700
committerclymb3r <bialek.joseph@gmail.com>2013-10-01 09:47:05 -0700
commit59cd18360764af6e6133ad11ec9cd8295372e587 (patch)
tree758a4f12cd6d2bddb0006df7d1fcac3736b61b8f /Exfiltration/NTFSParser/NTFSParserDLL/NTFS_FileRecord.h
parentb17272eb98933c62baa5a21bcd23713f9182ee38 (diff)
downloadPowerSploit-59cd18360764af6e6133ad11ec9cd8295372e587.tar.gz
PowerSploit-59cd18360764af6e6133ad11ec9cd8295372e587.zip
Adding Invoke-Mimikatz and Invoke-Ninjacopy
Diffstat (limited to 'Exfiltration/NTFSParser/NTFSParserDLL/NTFS_FileRecord.h')
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/NTFS_FileRecord.h989
1 files changed, 989 insertions, 0 deletions
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_FileRecord.h b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_FileRecord.h
new file mode 100644
index 0000000..5a232ff
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_FileRecord.h
@@ -0,0 +1,989 @@
+/*
+ * NTFS Volume and File Record Class
+ *
+ * Copyright(C) 2010 cyb70289 <cyb70289@gmail.com>
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __NTFS_FILERECORD_H_CYB70289
+#define __NTFS_FILERECORD_H_CYB70289
+
+
+///////////////////////////////////////
+// NTFS Volume forward declaration
+///////////////////////////////////////
+class CNTFSVolume
+{
+public:
+ CNTFSVolume(_TCHAR volume);
+ virtual ~CNTFSVolume();
+
+ friend class CFileRecord;
+ friend class CAttrBase;
+
+private:
+ WORD SectorSize;
+ DWORD ClusterSize;
+ DWORD FileRecordSize;
+ DWORD IndexBlockSize;
+ ULONGLONG MFTAddr;
+ HANDLE hVolume;
+ BOOL VolumeOK;
+ ATTR_RAW_CALLBACK AttrRawCallBack[ATTR_NUMS];
+ WORD Version;
+
+ // MFT file records ($MFT file itself) may be fragmented
+ // Get $MFT Data attribute to translate FileRecord to correct disk offset
+ CFileRecord *MFTRecord; // $MFT File Record
+ const CAttrBase *MFTData; // $MFT Data Attribute
+
+ BOOL OpenVolume(_TCHAR volume);
+
+public:
+ __inline BOOL IsVolumeOK() const;
+ __inline WORD GetVersion() const;
+ __inline ULONGLONG GetRecordsCount() const;
+
+ __inline DWORD GetSectorSize() const;
+ __inline DWORD GetClusterSize() const;
+ __inline DWORD GetFileRecordSize() const;
+ __inline DWORD GetIndexBlockSize() const;
+ __inline ULONGLONG GetMFTAddr() const;
+
+ BOOL InstallAttrRawCB(DWORD attrType, ATTR_RAW_CALLBACK cb);
+ __inline void ClearAttrRawCB();
+}; // CNTFSVolume
+
+
+////////////////////////////////////////////
+// List to hold Attributes of the same type
+////////////////////////////////////////////
+typedef class CSList<CAttrBase> CAttrList;
+
+// It seems VC6.0 doesn't support template class friends
+#if _MSC_VER <= 1200
+class CAttrResident;
+class CAttrNonResident;
+template <class TYPE_RESIDENT> class CAttr_AttrList;
+#endif
+
+////////////////////////////////
+// Process a single File Record
+////////////////////////////////
+class CFileRecord
+{
+public:
+ CFileRecord(const CNTFSVolume *volume);
+ virtual ~CFileRecord();
+
+ friend class CAttrBase;
+#if _MSC_VER <= 1200
+ // Walk around VC6.0 compiler defect
+ friend class CAttr_AttrList<CAttrResident>;
+ friend class CAttr_AttrList<CAttrNonResident>;
+#else
+ template <class TYPE_RESIDENT> friend class CAttr_AttrList; // Won't compiler in VC6.0, why?
+#endif
+
+private:
+ const CNTFSVolume *Volume;
+ FILE_RECORD_HEADER *FileRecord;
+ ULONGLONG FileReference;
+ ATTR_RAW_CALLBACK AttrRawCallBack[ATTR_NUMS];
+ DWORD AttrMask;
+ CAttrList AttrList[ATTR_NUMS]; // Attributes
+
+ void ClearAttrs();
+ BOOL PatchUS(WORD *sector, int sectors, WORD usn, WORD *usarray);
+ __inline void UserCallBack(DWORD attType, ATTR_HEADER_COMMON *ahc, BOOL *bDiscard);
+ CAttrBase* AllocAttr(ATTR_HEADER_COMMON *ahc, BOOL *bUnhandled);
+ BOOL ParseAttr(ATTR_HEADER_COMMON *ahc);
+ FILE_RECORD_HEADER* ReadFileRecord(ULONGLONG &fileRef);
+ BOOL VisitIndexBlock(const ULONGLONG &vcn, const _TCHAR *fileName, CIndexEntry &ieFound) const;
+ void TraverseSubNode(const ULONGLONG &vcn, SUBENTRY_CALLBACK seCallBack) const;
+
+public:
+ BOOL ParseFileRecord(ULONGLONG fileRef);
+ BOOL ParseAttrs();
+
+ BOOL InstallAttrRawCB(DWORD attrType, ATTR_RAW_CALLBACK cb);
+ __inline void ClearAttrRawCB();
+
+ __inline void SetAttrMask(DWORD mask);
+ void TraverseAttrs(ATTRS_CALLBACK attrCallBack, void *context);
+ __inline const CAttrBase* FindFirstAttr(DWORD attrType) const;
+ const CAttrBase* FindNextAttr(DWORD attrType) const;
+
+ int GetFileName(_TCHAR *buf, DWORD bufLen) const;
+ __inline ULONGLONG GetFileSize() const;
+ void GetFileTime(FILETIME *writeTm, FILETIME *createTm = NULL, FILETIME *accessTm = NULL) const;
+
+ void TraverseSubEntries(SUBENTRY_CALLBACK seCallBack) const;
+ __inline const BOOL FindSubEntry(const _TCHAR *fileName, CIndexEntry &ieFound) const;
+ const CAttrBase* FindStream(_TCHAR *name = NULL);
+
+ __inline BOOL IsDeleted() const;
+ __inline BOOL IsDirectory() const;
+ __inline BOOL IsReadOnly() const;
+ __inline BOOL IsHidden() const;
+ __inline BOOL IsSystem() const;
+ __inline BOOL IsCompressed() const;
+ __inline BOOL IsEncrypted() const;
+ __inline BOOL IsSparse() const;
+}; // CFileRecord
+
+
+#include "NTFS_Attribute.h"
+
+
+CFileRecord::CFileRecord(const CNTFSVolume *volume)
+{
+ _ASSERT(volume);
+ Volume = volume;
+ FileRecord = NULL;
+ FileReference = (ULONGLONG)-1;
+
+ ClearAttrRawCB();
+
+ // Default to parse all attributes
+ AttrMask = MASK_ALL;
+}
+
+CFileRecord::~CFileRecord()
+{
+ ClearAttrs();
+
+ if (FileRecord)
+ delete FileRecord;
+}
+
+// Free all CAttr_xxx
+void CFileRecord::ClearAttrs()
+{
+ for (int i=0; i<ATTR_NUMS; i++)
+ {
+ AttrList[i].RemoveAll();
+ }
+}
+
+// Verify US and update sectors
+BOOL CFileRecord::PatchUS(WORD *sector, int sectors, WORD usn, WORD *usarray)
+{
+ int i;
+
+ for (i=0; i<sectors; i++)
+ {
+ sector += ((Volume->SectorSize>>1) - 1);
+ if (*sector != usn)
+ return FALSE; // USN error
+ *sector = usarray[i]; // Write back correct data
+ sector++;
+ }
+ return TRUE;
+}
+
+// Call user defined Callback routines for an attribute
+__inline void CFileRecord::UserCallBack(DWORD attType, ATTR_HEADER_COMMON *ahc, BOOL *bDiscard)
+{
+ *bDiscard = FALSE;
+
+ if (AttrRawCallBack[attType])
+ AttrRawCallBack[attType](ahc, bDiscard);
+ else if (Volume->AttrRawCallBack[attType])
+ Volume->AttrRawCallBack[attType](ahc, bDiscard);
+}
+
+CAttrBase* CFileRecord::AllocAttr(ATTR_HEADER_COMMON *ahc, BOOL *bUnhandled)
+{
+ switch (ahc->Type)
+ {
+ case ATTR_TYPE_STANDARD_INFORMATION:
+ return new CAttr_StdInfo(ahc, this);
+
+ case ATTR_TYPE_ATTRIBUTE_LIST:
+ if (ahc->NonResident)
+ return new CAttr_AttrList<CAttrNonResident>(ahc, this);
+ else
+ return new CAttr_AttrList<CAttrResident>(ahc, this);
+
+ case ATTR_TYPE_FILE_NAME:
+ return new CAttr_FileName(ahc, this);
+
+ case ATTR_TYPE_VOLUME_NAME:
+ return new CAttr_VolName(ahc, this);
+
+ case ATTR_TYPE_VOLUME_INFORMATION:
+ return new CAttr_VolInfo(ahc, this);
+
+ case ATTR_TYPE_DATA:
+ if (ahc->NonResident)
+ return new CAttr_Data<CAttrNonResident>(ahc, this);
+ else
+ return new CAttr_Data<CAttrResident>(ahc, this);
+
+ case ATTR_TYPE_INDEX_ROOT:
+ return new CAttr_IndexRoot(ahc, this);
+
+ case ATTR_TYPE_INDEX_ALLOCATION:
+ return new CAttr_IndexAlloc(ahc, this);
+
+ case ATTR_TYPE_BITMAP:
+ if (ahc->NonResident)
+ return new CAttr_Bitmap<CAttrNonResident>(ahc, this);
+ else
+ // Resident Bitmap may exist in a directory's FileRecord
+ // or in $MFT for a very small volume in theory
+ return new CAttr_Bitmap<CAttrResident>(ahc, this);
+
+ // Unhandled Attributes
+ default:
+ *bUnhandled = TRUE;
+ if (ahc->NonResident)
+ return new CAttrNonResident(ahc, this);
+ else
+ return new CAttrResident(ahc, this);
+ }
+}
+
+// Parse a single Attribute
+// Return False on error
+BOOL CFileRecord::ParseAttr(ATTR_HEADER_COMMON *ahc)
+{
+ DWORD attrIndex = ATTR_INDEX(ahc->Type);
+ if (attrIndex < ATTR_NUMS)
+ {
+ BOOL bDiscard = FALSE;
+ UserCallBack(attrIndex, ahc, &bDiscard);
+
+ if (!bDiscard)
+ {
+ BOOL bUnhandled = FALSE;
+ CAttrBase *attr = AllocAttr(ahc, &bUnhandled);
+ if (attr)
+ {
+ if (bUnhandled)
+ {
+ NTFS_TRACE1("Unhandled attribute: 0x%04X\n", ahc->Type);
+ }
+ AttrList[attrIndex].InsertEntry(attr);
+ return TRUE;
+ }
+ else
+ {
+ NTFS_TRACE1("Attribute Parse error: 0x%04X\n", ahc->Type);
+ return FALSE;
+ }
+ }
+ else
+ {
+ NTFS_TRACE1("User Callback has processed this Attribute: 0x%04X\n", ahc->Type);
+ return TRUE;
+ }
+ }
+ else
+ {
+ NTFS_TRACE1("Invalid Attribute Type: 0x%04X\n", ahc->Type);
+ return FALSE;
+ }
+}
+
+// Read File Record
+FILE_RECORD_HEADER* CFileRecord::ReadFileRecord(ULONGLONG &fileRef)
+{
+ FILE_RECORD_HEADER *fr = NULL;
+ DWORD len;
+
+ if (fileRef < MFT_IDX_USER || Volume->MFTData == NULL)
+ {
+ // Take as continuous disk allocation
+ LARGE_INTEGER frAddr;
+ frAddr.QuadPart = Volume->MFTAddr + (Volume->FileRecordSize) * fileRef;
+ frAddr.LowPart = SetFilePointer(Volume->hVolume, frAddr.LowPart, &frAddr.HighPart, FILE_BEGIN);
+
+ if (frAddr.LowPart == DWORD(-1) && GetLastError() != NO_ERROR)
+ return FALSE;
+ else
+ {
+ fr = (FILE_RECORD_HEADER*)new BYTE[Volume->FileRecordSize];
+
+ if (ReadFile(Volume->hVolume, fr, Volume->FileRecordSize, &len, NULL)
+ && len==Volume->FileRecordSize)
+ return fr;
+ else
+ {
+ delete fr;
+ return NULL;
+ }
+ }
+ }
+ else
+ {
+ // May be fragmented $MFT
+ ULONGLONG frAddr;
+ frAddr = (Volume->FileRecordSize) * fileRef;
+
+ fr = (FILE_RECORD_HEADER*)new BYTE[Volume->FileRecordSize];
+
+ if (Volume->MFTData->ReadData(frAddr, fr, Volume->FileRecordSize, &len)
+ && len == Volume->FileRecordSize)
+ return fr;
+ else
+ {
+ delete fr;
+ return NULL;
+ }
+ }
+}
+
+// Read File Record, verify and patch the US (update sequence)
+BOOL CFileRecord::ParseFileRecord(ULONGLONG fileRef)
+{
+ // Clear previous data
+ ClearAttrs();
+ if (FileRecord)
+ {
+ delete FileRecord;
+ FileRecord = NULL;
+ }
+
+ FILE_RECORD_HEADER *fr = ReadFileRecord(fileRef);
+ if (fr == NULL)
+ {
+ NTFS_TRACE1("Cannot read file record %I64u\n", fileRef);
+
+ FileReference = (ULONGLONG)-1;
+ }
+ else
+ {
+ FileReference = fileRef;
+
+ if (fr->Magic == FILE_RECORD_MAGIC)
+ {
+ // Patch US
+ WORD *usnaddr = (WORD*)((BYTE*)fr + fr->OffsetOfUS);
+ WORD usn = *usnaddr;
+ WORD *usarray = usnaddr + 1;
+ if (PatchUS((WORD*)fr, Volume->FileRecordSize/Volume->SectorSize, usn, usarray))
+ {
+ NTFS_TRACE1("File Record %I64u Found\n", fileRef);
+ FileRecord = fr;
+
+ return TRUE;
+ }
+ else
+ {
+ NTFS_TRACE("Update Sequence Number error\n");
+ }
+ }
+ else
+ {
+ NTFS_TRACE("Invalid file record\n");
+ }
+
+ delete fr;
+ }
+
+ return FALSE;
+}
+
+// Visit IndexBlocks recursivly to find a specific FileName
+BOOL CFileRecord::VisitIndexBlock(const ULONGLONG &vcn, const _TCHAR *fileName, CIndexEntry &ieFound) const
+{
+ CAttr_IndexAlloc *ia = (CAttr_IndexAlloc*)FindFirstAttr(ATTR_TYPE_INDEX_ALLOCATION);
+ if (ia == NULL)
+ return FALSE;
+
+ CIndexBlock ib;
+ if (ia->ParseIndexBlock(vcn, ib))
+ {
+ CIndexEntry *ie = ib.FindFirstEntry();
+ while (ie)
+ {
+ if (ie->HasName())
+ {
+ // Compare name
+ int i = ie->Compare(fileName);
+ if (i == 0)
+ {
+ ieFound = *ie;
+ return TRUE;
+ }
+ else if (i < 0) // fileName is smaller than IndexEntry
+ {
+ // Visit SubNode
+ if (ie->IsSubNodePtr())
+ {
+ // Search in SubNode (IndexBlock), recursive call
+ if (VisitIndexBlock(ie->GetSubNodeVCN(), fileName, ieFound))
+ return TRUE;
+ }
+ else
+ return FALSE; // not found
+ }
+ // Just step forward if fileName is bigger than IndexEntry
+ }
+ else if (ie->IsSubNodePtr())
+ {
+ // Search in SubNode (IndexBlock), recursive call
+ if (VisitIndexBlock(ie->GetSubNodeVCN(), fileName, ieFound))
+ return TRUE;
+ }
+
+ ie = ib.FindNextEntry();
+ }
+ }
+
+ return FALSE;
+}
+
+// Traverse SubNode recursivly in ascending order
+// Call user defined callback routine once found an subentry
+void CFileRecord::TraverseSubNode(const ULONGLONG &vcn, SUBENTRY_CALLBACK seCallBack) const
+{
+ CAttr_IndexAlloc *ia = (CAttr_IndexAlloc*)FindFirstAttr(ATTR_TYPE_INDEX_ALLOCATION);
+ if (ia == NULL)
+ return;
+
+ CIndexBlock ib;
+ if (ia->ParseIndexBlock(vcn, ib))
+ {
+ CIndexEntry *ie = ib.FindFirstEntry();
+ while (ie)
+ {
+ if (ie->IsSubNodePtr())
+ TraverseSubNode(ie->GetSubNodeVCN(), seCallBack); // recursive call
+
+ if (ie->HasName())
+ seCallBack(ie);
+
+ ie = ib.FindNextEntry();
+ }
+ }
+}
+
+// Parse all the attributes in a File Record
+// And insert them into a link list
+BOOL CFileRecord::ParseAttrs()
+{
+ _ASSERT(FileRecord);
+
+ // Clear previous data
+ ClearAttrs();
+
+ // Visit all attributes
+
+ DWORD dataPtr = 0; // guard if data exceeds FileRecordSize bounds
+ ATTR_HEADER_COMMON *ahc = (ATTR_HEADER_COMMON*)((BYTE*)FileRecord + FileRecord->OffsetOfAttr);
+ dataPtr += FileRecord->OffsetOfAttr;
+
+ while (ahc->Type != (DWORD)-1 && (dataPtr+ahc->TotalSize) <= Volume->FileRecordSize)
+ {
+ if (ATTR_MASK(ahc->Type) & AttrMask) // Skip unwanted attributes
+ {
+ if (!ParseAttr(ahc)) // Parse error
+ return FALSE;
+
+ if (IsEncrypted() || IsCompressed())
+ {
+ NTFS_TRACE("Compressed and Encrypted file not supported yet !\n");
+ return FALSE;
+ }
+ }
+
+ dataPtr += ahc->TotalSize;
+ ahc = (ATTR_HEADER_COMMON*)((BYTE*)ahc + ahc->TotalSize); // next attribute
+ }
+
+ return TRUE;
+}
+
+// Install Attribute raw data CallBack routines for a single File Record
+BOOL CFileRecord::InstallAttrRawCB(DWORD attrType, ATTR_RAW_CALLBACK cb)
+{
+ DWORD atIdx = ATTR_INDEX(attrType);
+ if (atIdx < ATTR_NUMS)
+ {
+ AttrRawCallBack[atIdx] = cb;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+// Clear all Attribute CallBack routines
+__inline void CFileRecord::ClearAttrRawCB()
+{
+ for (int i = 0; i < ATTR_NUMS; i ++)
+ AttrRawCallBack[i] = NULL;
+}
+
+// Choose attributes to handle, unwanted attributes will be discarded silently
+__inline void CFileRecord::SetAttrMask(DWORD mask)
+{
+ // Standard Information and Attribute List is needed always
+ AttrMask = mask | MASK_STANDARD_INFORMATION | MASK_ATTRIBUTE_LIST;
+}
+
+// Traverse all Attribute and return CAttr_xxx classes to User Callback routine
+void CFileRecord::TraverseAttrs(ATTRS_CALLBACK attrCallBack, void *context)
+{
+ _ASSERT(attrCallBack);
+
+ for (int i = 0; i < ATTR_NUMS; i ++)
+ {
+ if (AttrMask & (((DWORD)1)<<i)) // skip masked attributes
+ {
+ const CAttrBase *ab = AttrList[i].FindFirstEntry();
+ while (ab)
+ {
+ BOOL bStop;
+ bStop = FALSE;
+ attrCallBack(ab, context, &bStop);
+ if (bStop)
+ return;
+
+ ab = AttrList[i].FindNextEntry();
+ }
+ }
+ }
+}
+
+// Find Attributes
+__inline const CAttrBase* CFileRecord::FindFirstAttr(DWORD attrType) const
+{
+ DWORD attrIdx = ATTR_INDEX(attrType);
+
+ return attrIdx < ATTR_NUMS ? AttrList[attrIdx].FindFirstEntry() : NULL;
+}
+
+const CAttrBase* CFileRecord::FindNextAttr(DWORD attrType) const
+{
+ DWORD attrIdx = ATTR_INDEX(attrType);
+
+ return attrIdx < ATTR_NUMS ? AttrList[attrIdx].FindNextEntry() : NULL;
+}
+
+// Get File Name (First Win32 name)
+int CFileRecord::GetFileName(_TCHAR *buf, DWORD bufLen) const
+{
+ // A file may have several filenames
+ // Return the first Win32 filename
+ CAttr_FileName *fn = (CAttr_FileName*)AttrList[ATTR_INDEX(ATTR_TYPE_FILE_NAME)].FindFirstEntry();
+ while (fn)
+ {
+ if (fn->IsWin32Name())
+ {
+ int len = fn->GetFileName(buf, bufLen);
+ if (len != 0)
+ return len; // success or fail
+ }
+
+ fn = (CAttr_FileName*)AttrList[ATTR_INDEX(ATTR_TYPE_FILE_NAME)].FindNextEntry();
+ }
+
+ return 0;
+}
+
+// Get File Size
+__inline ULONGLONG CFileRecord::GetFileSize() const
+{
+ CAttr_FileName *fn = (CAttr_FileName*)AttrList[ATTR_INDEX(ATTR_TYPE_FILE_NAME)].FindFirstEntry();
+ return fn ? fn->GetFileSize() : 0;
+}
+
+// Get File Times
+void CFileRecord::GetFileTime(FILETIME *writeTm, FILETIME *createTm, FILETIME *accessTm) const
+{
+ // Standard Information attribute hold the most updated file time
+ CAttr_StdInfo *si = (CAttr_StdInfo*)AttrList[ATTR_INDEX(ATTR_TYPE_STANDARD_INFORMATION)].FindFirstEntry();
+ if (si)
+ si->GetFileTime(writeTm, createTm, accessTm);
+ else
+ {
+ writeTm->dwHighDateTime = 0;
+ writeTm->dwLowDateTime = 0;
+ if (createTm)
+ {
+ createTm->dwHighDateTime = 0;
+ createTm->dwLowDateTime = 0;
+ }
+ if (accessTm)
+ {
+ accessTm->dwHighDateTime = 0;
+ accessTm->dwLowDateTime = 0;
+ }
+ }
+}
+
+// Traverse all sub directories and files contained
+// Call user defined callback routine once found an entry
+void CFileRecord::TraverseSubEntries(SUBENTRY_CALLBACK seCallBack) const
+{
+ _ASSERT(seCallBack);
+
+ // Start traversing from IndexRoot (B+ tree root node)
+
+ CAttr_IndexRoot* ir = (CAttr_IndexRoot*)FindFirstAttr(ATTR_TYPE_INDEX_ROOT);
+ if (ir == NULL || !ir->IsFileName())
+ return;
+
+ CIndexEntryList *ieList = (CIndexEntryList*)ir;
+ CIndexEntry *ie = ieList->FindFirstEntry();
+ while (ie)
+ {
+ // Visit subnode first
+ if (ie->IsSubNodePtr())
+ TraverseSubNode(ie->GetSubNodeVCN(), seCallBack);
+
+ if (ie->HasName())
+ seCallBack(ie);
+
+ ie = ieList->FindNextEntry();
+ }
+}
+
+// Find a specific FileName from InexRoot described B+ tree
+__inline const BOOL CFileRecord::FindSubEntry(const _TCHAR *fileName, CIndexEntry &ieFound) const
+{
+ // Start searching from IndexRoot (B+ tree root node)
+ CAttr_IndexRoot *ir = (CAttr_IndexRoot*)FindFirstAttr(ATTR_TYPE_INDEX_ROOT);
+ if (ir == NULL || !ir->IsFileName())
+ return FALSE;
+
+ CIndexEntryList *ieList = (CIndexEntryList*)ir;
+ CIndexEntry *ie = ieList->FindFirstEntry();
+ while (ie)
+ {
+ if (ie->HasName())
+ {
+ // Compare name
+ int i = ie->Compare(fileName);
+ if (i == 0)
+ {
+ ieFound = *ie;
+ return TRUE;
+ }
+ else if (i < 0) // fileName is smaller than IndexEntry
+ {
+ // Visit SubNode
+ if (ie->IsSubNodePtr())
+ {
+ // Search in SubNode (IndexBlock)
+ if (VisitIndexBlock(ie->GetSubNodeVCN(), fileName, ieFound))
+ return TRUE;
+ }
+ else
+ return FALSE; // not found
+ }
+ // Just step forward if fileName is bigger than IndexEntry
+ }
+ else if (ie->IsSubNodePtr())
+ {
+ // Search in SubNode (IndexBlock)
+ if (VisitIndexBlock(ie->GetSubNodeVCN(), fileName, ieFound))
+ return TRUE;
+ }
+
+ ie = ieList->FindNextEntry();
+ }
+
+ return FALSE;
+}
+
+// Find Data attribute class of
+const CAttrBase* CFileRecord::FindStream(_TCHAR *name)
+{
+ const CAttrBase *data = FindFirstAttr(ATTR_TYPE_DATA);
+ while (data)
+ {
+ if (data->IsUnNamed() && name == NULL) // Unnamed stream
+ break;
+ if ((!data->IsUnNamed()) && name) // Named stream
+ {
+ _TCHAR an[MAX_PATH];
+ if (data->GetAttrName(an, MAX_PATH))
+ {
+ if (_tcscmp(an, name) == 0)
+ break;
+ }
+ }
+
+ data = FindNextAttr(ATTR_TYPE_DATA);
+ }
+
+ return data;
+}
+
+// Check if it's deleted or in use
+__inline BOOL CFileRecord::IsDeleted() const
+{
+ return !(FileRecord->Flags & FILE_RECORD_FLAG_INUSE);
+}
+
+// Check if it's a directory
+__inline BOOL CFileRecord::IsDirectory() const
+{
+ return FileRecord->Flags & FILE_RECORD_FLAG_DIR;
+}
+
+__inline BOOL CFileRecord::IsReadOnly() const
+{
+ // Standard Information attribute holds the most updated file time
+ const CAttr_StdInfo *si = (CAttr_StdInfo*)AttrList[ATTR_INDEX(ATTR_TYPE_STANDARD_INFORMATION)].FindFirstEntry();
+ return si ? si->IsReadOnly() : FALSE;
+}
+
+__inline BOOL CFileRecord::IsHidden() const
+{
+ const CAttr_StdInfo *si = (CAttr_StdInfo*)AttrList[ATTR_INDEX(ATTR_TYPE_STANDARD_INFORMATION)].FindFirstEntry();
+ return si ? si->IsHidden() : FALSE;
+}
+
+__inline BOOL CFileRecord::IsSystem() const
+{
+ const CAttr_StdInfo *si = (CAttr_StdInfo*)AttrList[ATTR_INDEX(ATTR_TYPE_STANDARD_INFORMATION)].FindFirstEntry();
+ return si ? si->IsSystem() : FALSE;
+}
+
+__inline BOOL CFileRecord::IsCompressed() const
+{
+ const CAttr_StdInfo *si = (CAttr_StdInfo*)AttrList[ATTR_INDEX(ATTR_TYPE_STANDARD_INFORMATION)].FindFirstEntry();
+ return si ? si->IsCompressed() : FALSE;
+}
+
+__inline BOOL CFileRecord::IsEncrypted() const
+{
+ const CAttr_StdInfo *si = (CAttr_StdInfo*)AttrList[ATTR_INDEX(ATTR_TYPE_STANDARD_INFORMATION)].FindFirstEntry();
+ return si ? si->IsEncrypted() : FALSE;
+}
+
+__inline BOOL CFileRecord::IsSparse() const
+{
+ const CAttr_StdInfo *si = (CAttr_StdInfo*)AttrList[ATTR_INDEX(ATTR_TYPE_STANDARD_INFORMATION)].FindFirstEntry();
+ return si ? si->IsSparse() : FALSE;
+}
+
+
+///////////////////////////////////////
+// NTFS Volume Implementation
+///////////////////////////////////////
+CNTFSVolume::CNTFSVolume(_TCHAR volume)
+{
+ hVolume = INVALID_HANDLE_VALUE;
+ VolumeOK = FALSE;
+ MFTRecord = NULL;
+ MFTData = NULL;
+ Version = 0;
+ ClearAttrRawCB();
+
+ if (!OpenVolume(volume))
+ return;
+
+ // Verify NTFS volume version (must >= 3.0)
+
+ CFileRecord vol(this);
+ vol.SetAttrMask(MASK_VOLUME_NAME | MASK_VOLUME_INFORMATION);
+ if (!vol.ParseFileRecord(MFT_IDX_VOLUME))
+ return;
+
+ vol.ParseAttrs();
+ CAttr_VolInfo *vi = (CAttr_VolInfo*)vol.FindFirstAttr(ATTR_TYPE_VOLUME_INFORMATION);
+ if (!vi)
+ return;
+
+ Version = vi->GetVersion();
+ NTFS_TRACE2("NTFS volume version: %u.%u\n", HIBYTE(Version), LOBYTE(Version));
+ if (Version < 0x0300) // NT4 ?
+ return;
+
+#ifdef _DEBUG
+ CAttr_VolName *vn = (CAttr_VolName*)vol.FindFirstAttr(ATTR_TYPE_VOLUME_NAME);
+ if (vn)
+ {
+ char volname[MAX_PATH];
+ if (vn->GetName(volname, MAX_PATH) > 0)
+ {
+ NTFS_TRACE1("NTFS volume name: %s\n", volname);
+ }
+ }
+#endif
+
+ VolumeOK = TRUE;
+
+ MFTRecord = new CFileRecord(this);
+ MFTRecord->SetAttrMask(MASK_DATA);
+ if (MFTRecord->ParseFileRecord(MFT_IDX_MFT))
+ {
+ MFTRecord->ParseAttrs();
+ MFTData = MFTRecord->FindFirstAttr(ATTR_TYPE_DATA);
+ if (MFTData == NULL)
+ {
+ delete MFTRecord;
+ MFTRecord = NULL;
+ }
+ }
+}
+
+CNTFSVolume::~CNTFSVolume()
+{
+ if (hVolume != INVALID_HANDLE_VALUE)
+ CloseHandle(hVolume);
+
+ if (MFTRecord)
+ delete MFTRecord;
+}
+
+// Open a volume ('a' - 'z', 'A' - 'Z'), get volume handle and BPB
+BOOL CNTFSVolume::OpenVolume(_TCHAR volume)
+{
+ // Verify parameter
+ if (!_istalpha(volume))
+ {
+ NTFS_TRACE("Volume name error, should be like 'C', 'D'\n");
+ return FALSE;
+ }
+
+ _TCHAR volumePath[7];
+ _sntprintf(volumePath, 6, _T("\\\\.\\%c:"), volume);
+ volumePath[6] = _T('\0');
+
+ hVolume = CreateFile(volumePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
+ if (hVolume != INVALID_HANDLE_VALUE)
+ {
+ DWORD num;
+ NTFS_BPB bpb;
+
+ // Read the first sector (boot sector)
+ if (ReadFile(hVolume, &bpb, 512, &num, NULL) && num==512)
+ {
+ if (strncmp((const char*)bpb.Signature, NTFS_SIGNATURE, 8) == 0)
+ {
+ // Log important volume parameters
+
+ SectorSize = bpb.BytesPerSector;
+ NTFS_TRACE1("Sector Size = %u bytes\n", SectorSize);
+
+ ClusterSize = SectorSize * bpb.SectorsPerCluster;
+ NTFS_TRACE1("Cluster Size = %u bytes\n", ClusterSize);
+
+ int sz = (char)bpb.ClustersPerFileRecord;
+ if (sz > 0)
+ FileRecordSize = ClusterSize * sz;
+ else
+ FileRecordSize = 1 << (-sz);
+ NTFS_TRACE1("FileRecord Size = %u bytes\n", FileRecordSize);
+
+ sz = (char)bpb.ClustersPerIndexBlock;
+ if (sz > 0)
+ IndexBlockSize = ClusterSize * sz;
+ else
+ IndexBlockSize = 1 << (-sz);
+ NTFS_TRACE1("IndexBlock Size = %u bytes\n", IndexBlockSize);
+
+ MFTAddr = bpb.LCN_MFT * ClusterSize;
+ NTFS_TRACE1("MFT address = 0x%016I64X\n", MFTAddr);
+ }
+ else
+ {
+ NTFS_TRACE("Volume file system is not NTFS\n");
+ goto IOError;
+ }
+ }
+ else
+ {
+ NTFS_TRACE("Read boot sector error\n");
+ goto IOError;
+ }
+ }
+ else
+ {
+ NTFS_TRACE1("Cannnot open volume %c\n", (char)volume);
+IOError:
+ if (hVolume != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hVolume);
+ hVolume = INVALID_HANDLE_VALUE;
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// Check if Volume is successfully opened
+__inline BOOL CNTFSVolume::IsVolumeOK() const
+{
+ return VolumeOK;
+}
+
+// Get NTFS volume version
+__inline WORD CNTFSVolume::GetVersion() const
+{
+ return Version;
+}
+
+// Get File Record count
+__inline ULONGLONG CNTFSVolume::GetRecordsCount() const
+{
+ return (MFTData->GetDataSize() / FileRecordSize);
+}
+
+// Get BPB information
+
+__inline DWORD CNTFSVolume::GetSectorSize() const
+{
+ return SectorSize;
+}
+
+__inline DWORD CNTFSVolume::GetClusterSize() const
+{
+ return ClusterSize;
+}
+
+__inline DWORD CNTFSVolume::GetFileRecordSize() const
+{
+ return FileRecordSize;
+}
+
+__inline DWORD CNTFSVolume::GetIndexBlockSize() const
+{
+ return IndexBlockSize;
+}
+
+// Get MFT starting address
+__inline ULONGLONG CNTFSVolume::GetMFTAddr() const
+{
+ return MFTAddr;
+}
+
+// Install Attribute CallBack routines for the whole Volume
+BOOL CNTFSVolume::InstallAttrRawCB(DWORD attrType, ATTR_RAW_CALLBACK cb)
+{
+ DWORD atIdx = ATTR_INDEX(attrType);
+ if (atIdx < ATTR_NUMS)
+ {
+ AttrRawCallBack[atIdx] = cb;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+// Clear all Attribute CallBack routines
+__inline void CNTFSVolume::ClearAttrRawCB()
+{
+ for (int i = 0; i < ATTR_NUMS; i ++)
+ AttrRawCallBack[i] = NULL;
+}
+
+#endif