aboutsummaryrefslogtreecommitdiff
path: root/Exfiltration/NTFSParser/NTFSParserDLL
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
parentb17272eb98933c62baa5a21bcd23713f9182ee38 (diff)
downloadPowerSploit-59cd18360764af6e6133ad11ec9cd8295372e587.tar.gz
PowerSploit-59cd18360764af6e6133ad11ec9cd8295372e587.zip
Adding Invoke-Mimikatz and Invoke-Ninjacopy
Diffstat (limited to 'Exfiltration/NTFSParser/NTFSParserDLL')
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/NTFS.h28
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.cpp161
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.vcxproj172
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.vcxproj.filters39
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h1663
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Common.h317
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/NTFS_DataType.h380
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/NTFS_FileRecord.h989
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/ReadMe.txt48
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/dllmain.cpp36
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/stdafx.cpp8
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/stdafx.h18
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/targetver.h8
13 files changed, 3867 insertions, 0 deletions
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/NTFS.h b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS.h
new file mode 100644
index 0000000..ef6117b
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS.h
@@ -0,0 +1,28 @@
+/*
+ * NTFS include files
+ *
+ * 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_H_CYB70289
+#define __NTFS_H_CYB70289
+
+#pragma pack(8)
+
+#include "NTFS_Common.h"
+#include "NTFS_FileRecord.h"
+#include "NTFS_Attribute.h"
+
+#pragma pack()
+
+#endif
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.cpp b/Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.cpp
new file mode 100644
index 0000000..e71d8ee
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.cpp
@@ -0,0 +1,161 @@
+/*
+ *
+ * Copyright(C) 2013 Joe Bialek Twitter:@JosephBialek
+ *
+ * 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.
+ */
+//
+// This code uses libraries released under GPLv2(or later) written by cyb70289 <cyb70289@gmail.com>
+
+#include "stdafx.h"
+#include "NTFS.h"
+#include "NTFS_DataType.h"
+
+using namespace std;
+
+struct FileInfo_t
+{
+ CNTFSVolume* volume;
+ CFileRecord* fileRecord;
+ CIndexEntry* indexEntry;
+ CAttrBase* data;
+};
+
+extern "C" HANDLE __declspec(dllexport) StealthOpenFile(char* filePathCStr)
+{
+ FileInfo_t* fileInfo = new FileInfo_t;
+
+ string filePath = string(filePathCStr);
+ _TCHAR volumeName = filePath.at(0);
+
+ fileInfo->volume = new CNTFSVolume(volumeName);
+ if (!fileInfo->volume->IsVolumeOK())
+ {
+ return NULL;
+ }
+
+ //Parse root directory
+ fileInfo->fileRecord = new CFileRecord(fileInfo->volume);
+ fileInfo->fileRecord->SetAttrMask(MASK_INDEX_ROOT | MASK_INDEX_ALLOCATION);
+
+ if (!fileInfo->fileRecord->ParseFileRecord(MFT_IDX_ROOT))
+ {
+ return NULL;
+ }
+ if (!fileInfo->fileRecord->ParseAttrs())
+ {
+ return NULL;
+ }
+
+ //Find subdirectory
+ fileInfo->indexEntry = new CIndexEntry;
+ int dirs = filePath.find(_T('\\'), 0);
+ int dire = filePath.find(_T('\\'), dirs+1);
+
+ while (dire != string::npos)
+ {
+ string pathname = filePath.substr(dirs+1, dire-dirs-1);
+ const _TCHAR* pathnameCStr = (const _TCHAR*)pathname.c_str();
+ if (fileInfo->fileRecord->FindSubEntry(pathnameCStr, *(fileInfo->indexEntry)))
+ {
+ if (!fileInfo->fileRecord->ParseFileRecord(fileInfo->indexEntry->GetFileReference()))
+ {
+ return NULL;
+ }
+
+ if (!fileInfo->fileRecord->ParseAttrs())
+ {
+ if (fileInfo->fileRecord->IsCompressed())
+ {
+ return NULL;
+ }
+ else if (fileInfo->fileRecord->IsEncrypted())
+ {
+ return NULL;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ }
+ else
+ {
+ return NULL;
+ }
+
+
+ dirs = dire;
+ dire = filePath.find(_T('\\'), dirs+1);
+ }
+
+ string fileName = filePath.substr(dirs+1, filePath.size()-1);
+ const _TCHAR* fileNameCStr = (const _TCHAR*)fileName.c_str();
+ if (fileInfo->fileRecord->FindSubEntry(fileNameCStr, *(fileInfo->indexEntry)))
+ {
+ if (!fileInfo->fileRecord->ParseFileRecord(fileInfo->indexEntry->GetFileReference()))
+ {
+ return NULL;
+ }
+
+ fileInfo->fileRecord->SetAttrMask(MASK_DATA);
+ if (!fileInfo->fileRecord->ParseAttrs())
+ {
+ return NULL;
+ }
+
+ fileInfo->data = (CAttrBase*)fileInfo->fileRecord->FindStream();
+
+ return fileInfo;
+ }
+
+ return NULL;
+}
+
+
+extern "C" DWORD __declspec(dllexport) StealthReadFile(FileInfo_t* fileInfo, BYTE* buffer, DWORD bufferSize, ULONGLONG offset, DWORD* bytesRead, ULONGLONG* dataRemaining)
+{
+
+ if (fileInfo->data)
+ {
+ ULONGLONG dataLength = (ULONGLONG)fileInfo->data->GetDataSize();
+ ULONGLONG fullDataLength = dataLength;
+
+ dataLength = dataLength - offset;
+ if (dataLength > bufferSize)
+ {
+ dataLength = bufferSize;
+ }
+ if (dataLength > MAXUINT32)
+ {
+ return 1;
+ }
+
+ DWORD len;
+ if (fileInfo->data->ReadData(offset, buffer, dataLength, &len) && len == dataLength)
+ {
+ *bytesRead = len;
+ *dataRemaining = fullDataLength - len - offset;
+ return 0; //Success
+ }
+ return 3;
+ }
+ return 2;
+}
+
+
+extern "C" void __declspec(dllexport) StealthCloseFile(FileInfo_t* fileInfo)
+{
+ delete (fileInfo->data);
+ delete (fileInfo->indexEntry);
+ delete (fileInfo->volume);
+ delete fileInfo;
+}
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.vcxproj b/Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.vcxproj
new file mode 100644
index 0000000..00f4963
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.vcxproj
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{5E42B778-F231-4797-B7FD-7D5BCA9738D0}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>NTFSParserDLL</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>NotSet</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v110</PlatformToolset>
+ <CharacterSet>NotSet</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110_xp</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>NotSet</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v110_xp</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>NotSet</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;NTFSPARSERDLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;NTFSPARSERDLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;NTFSPARSERDLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;NTFSPARSERDLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Text Include="ReadMe.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp">
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</CompileAsManaged>
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="NTFSParserDLL.cpp" />
+ <ClCompile Include="stdafx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.vcxproj.filters b/Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.vcxproj.filters
new file mode 100644
index 0000000..8bbd5fc
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.vcxproj.filters
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Text Include="ReadMe.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="stdafx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="targetver.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="stdafx.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="NTFSParserDLL.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dllmain.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h
new file mode 100644
index 0000000..19ab7ce
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h
@@ -0,0 +1,1663 @@
+/*
+ * NTFS Attribute Classes
+ *
+ * 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_ATTRIBUTE_H_CYB70289
+#define __NTFS_ATTRIBUTE_H_CYB70289
+
+
+////////////////////////////////
+// List to hold parsed DataRuns
+////////////////////////////////
+typedef struct tagDataRun_Entry
+{
+ LONGLONG LCN; // -1 to indicate sparse data
+ ULONGLONG Clusters;
+ ULONGLONG StartVCN;
+ ULONGLONG LastVCN;
+} DataRun_Entry;
+typedef class CSList<DataRun_Entry> CDataRunList;
+
+////////////////////////////////////
+// List to hold Index Entry objects
+////////////////////////////////////
+class CIndexEntry;
+typedef class CSList<CIndexEntry> CIndexEntryList;
+
+
+////////////////////////////////
+// Attributes base class
+////////////////////////////////
+class CAttrBase
+{
+public:
+ CAttrBase(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttrBase();
+
+protected:
+ const ATTR_HEADER_COMMON *AttrHeader;
+ WORD _SectorSize;
+ DWORD _ClusterSize;
+ DWORD _IndexBlockSize;
+ HANDLE _hVolume;
+ const CFileRecord *FileRecord;
+
+public:
+ __inline const ATTR_HEADER_COMMON* GetAttrHeader() const;
+ __inline DWORD GetAttrType() const;
+ __inline DWORD GetAttrTotalSize() const;
+ __inline BOOL IsNonResident() const;
+ __inline WORD GetAttrFlags() const;
+ int GetAttrName(char *buf, DWORD bufLen) const;
+ int GetAttrName(wchar_t *buf, DWORD bufLen) const;
+ __inline BOOL IsUnNamed() const;
+
+protected:
+ virtual __inline BOOL IsDataRunOK() const = 0;
+
+public:
+ virtual __inline ULONGLONG GetDataSize(ULONGLONG *allocSize = NULL) const = 0;
+ virtual BOOL ReadData(const ULONGLONG &offset, void *bufv, DWORD bufLen, DWORD *actural) const = 0;
+}; // CAttrBase
+
+CAttrBase::CAttrBase(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr)
+{
+ _ASSERT(ahc);
+ _ASSERT(fr);
+
+ AttrHeader = ahc;
+ FileRecord = fr;
+
+ _SectorSize = fr->Volume->SectorSize;
+ _ClusterSize = fr->Volume->ClusterSize;
+ _IndexBlockSize = fr->Volume->IndexBlockSize;
+ _hVolume = fr->Volume->hVolume;
+}
+
+CAttrBase::~CAttrBase()
+{
+}
+
+__inline const ATTR_HEADER_COMMON* CAttrBase::GetAttrHeader() const
+{
+ return AttrHeader;
+}
+
+__inline DWORD CAttrBase::GetAttrType() const
+{
+ return AttrHeader->Type;
+}
+
+__inline DWORD CAttrBase::GetAttrTotalSize() const
+{
+ return AttrHeader->TotalSize;
+}
+
+__inline BOOL CAttrBase::IsNonResident() const
+{
+ return AttrHeader->NonResident;
+}
+
+__inline WORD CAttrBase::GetAttrFlags() const
+{
+ return AttrHeader->Flags;
+}
+
+// Get ANSI Attribute name
+// Return 0: Unnamed, <0: buffer too small, -buffersize, >0 Name length
+int CAttrBase::GetAttrName(char *buf, DWORD bufLen) const
+{
+ if (AttrHeader->NameLength)
+ {
+ if (bufLen < AttrHeader->NameLength)
+ return -1*AttrHeader->NameLength; // buffer too small
+
+ wchar_t *namePtr = (wchar_t*)((BYTE*)AttrHeader + AttrHeader->NameOffset);
+ int len = WideCharToMultiByte(CP_ACP, 0, namePtr, AttrHeader->NameLength,
+ buf, bufLen, NULL, NULL);
+ if (len)
+ {
+ buf[len] = '\0';
+ NTFS_TRACE1("Attribute name: %s\n", buf);
+ return len;
+ }
+ else
+ {
+ NTFS_TRACE("Unrecognized attribute name or Name buffer too small\n");
+ return -1*AttrHeader->NameLength;
+ }
+ }
+ else
+ {
+ NTFS_TRACE("Attribute is unnamed\n");
+ return 0;
+ }
+}
+
+// Get UNICODE Attribute name
+// Return 0: Unnamed, <0: buffer too small, -buffersize, >0 Name length
+int CAttrBase::GetAttrName(wchar_t *buf, DWORD bufLen) const
+{
+ if (AttrHeader->NameLength)
+ {
+ if (bufLen < AttrHeader->NameLength)
+ return -1*AttrHeader->NameLength; // buffer too small
+
+ bufLen = AttrHeader->NameLength;
+ wchar_t *namePtr = (wchar_t*)((BYTE*)AttrHeader + AttrHeader->NameOffset);
+ wcsncpy(buf, namePtr, bufLen);
+ buf[bufLen] = '\0\0';
+
+ NTFS_TRACE("Unicode Attribute Name\n");
+ return bufLen;
+ }
+ else
+ {
+ NTFS_TRACE("Attribute is unnamed\n");
+ return 0;
+ }
+}
+
+// Verify if this attribute is unnamed
+// Useful in analyzing MultiStream files
+__inline BOOL CAttrBase::IsUnNamed() const
+{
+ return (AttrHeader->NameLength == 0);
+}
+
+
+////////////////////////////////
+// Resident Attributes
+////////////////////////////////
+class CAttrResident : public CAttrBase
+{
+public:
+ CAttrResident(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttrResident();
+
+protected:
+ const ATTR_HEADER_RESIDENT *AttrHeaderR;
+ const void *AttrBody; // Points to Resident Data
+ DWORD AttrBodySize; // Attribute Data Size
+
+ virtual __inline BOOL IsDataRunOK() const;
+
+public:
+ virtual __inline ULONGLONG GetDataSize(ULONGLONG *allocSize = NULL) const;
+ virtual BOOL ReadData(const ULONGLONG &offset, void *bufv, DWORD bufLen, DWORD *actural) const;
+}; // CAttrResident
+
+CAttrResident::CAttrResident(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrBase(ahc, fr)
+{
+ AttrHeaderR = (ATTR_HEADER_RESIDENT*)ahc;
+ AttrBody = (void*)((BYTE*)AttrHeaderR + AttrHeaderR->AttrOffset);
+ AttrBodySize = AttrHeaderR->AttrSize;
+}
+
+CAttrResident::~CAttrResident()
+{
+}
+
+__inline BOOL CAttrResident::IsDataRunOK() const
+{
+ return TRUE; // Always OK for a resident attribute
+}
+
+// Return Actural Data Size
+// *allocSize = Allocated Size
+__inline ULONGLONG CAttrResident::GetDataSize(ULONGLONG *allocSize) const
+{
+ if (allocSize)
+ *allocSize = AttrBodySize;
+
+ return (ULONGLONG)AttrBodySize;
+}
+
+// Read "bufLen" bytes from "offset" into "bufv"
+// Number of bytes acturally read is returned in "*actural"
+BOOL CAttrResident::ReadData(const ULONGLONG &offset, void *bufv, DWORD bufLen, DWORD *actural) const
+{
+ _ASSERT(bufv);
+
+ *actural = 0;
+ if (bufLen == 0)
+ return TRUE;
+
+ DWORD offsetd = (DWORD)offset;
+ if (offsetd >= AttrBodySize)
+ return FALSE; // offset parameter error
+
+ if ((offsetd + bufLen) > AttrBodySize)
+ *actural = AttrBodySize - offsetd; // Beyond scope
+ else
+ *actural = bufLen;
+
+ memcpy(bufv, (BYTE*)AttrBody + offsetd, *actural);
+
+ return TRUE;
+}
+
+
+////////////////////////////////
+// NonResident Attributes
+////////////////////////////////
+class CAttrNonResident : public CAttrBase
+{
+public:
+ CAttrNonResident(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttrNonResident();
+
+protected:
+ const ATTR_HEADER_NON_RESIDENT *AttrHeaderNR;
+ CDataRunList DataRunList;
+
+private:
+ BOOL bDataRunOK;
+ BYTE *UnalignedBuf; // Buffer to hold not cluster aligned data
+ BOOL PickData(const BYTE **dataRun, LONGLONG *length, LONGLONG *LCNOffset);
+ BOOL ParseDataRun();
+ BOOL ReadClusters(void *buf, DWORD clusters, LONGLONG lcn);
+ BOOL ReadVirtualClusters(ULONGLONG vcn, DWORD clusters,
+ void *bufv, DWORD bufLen, DWORD *actural);
+
+protected:
+ virtual __inline BOOL IsDataRunOK() const;
+
+public:
+ virtual __inline ULONGLONG GetDataSize(ULONGLONG *allocSize = NULL) const;
+ virtual BOOL ReadData(const ULONGLONG &offset, void *bufv, DWORD bufLen, DWORD *actural) const;
+}; // CAttrNonResident
+
+CAttrNonResident::CAttrNonResident(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrBase(ahc, fr)
+{
+ AttrHeaderNR = (ATTR_HEADER_NON_RESIDENT*)ahc;
+
+ UnalignedBuf = new BYTE[_ClusterSize];
+
+ bDataRunOK = ParseDataRun();
+}
+
+CAttrNonResident::~CAttrNonResident()
+{
+ delete UnalignedBuf;
+
+ DataRunList.RemoveAll();
+}
+
+// Parse a single DataRun unit
+BOOL CAttrNonResident::PickData(const BYTE **dataRun, LONGLONG *length, LONGLONG *LCNOffset)
+{
+ BYTE size = **dataRun;
+ (*dataRun)++;
+ int lengthBytes = size & 0x0F;
+ int offsetBytes = size >> 4;
+
+ if (lengthBytes > 8 || offsetBytes > 8)
+ {
+ NTFS_TRACE1("DataRun decode error 1: 0x%02X\n", size);
+ return FALSE;
+ }
+
+ *length = 0;
+ memcpy(length, *dataRun, lengthBytes);
+ if (*length < 0)
+ {
+ NTFS_TRACE1("DataRun length error: %I64d\n", *length);
+ return FALSE;
+ }
+
+ (*dataRun) += lengthBytes;
+ *LCNOffset = 0;
+ if (offsetBytes) // Not Sparse File
+ {
+ if ((*dataRun)[offsetBytes-1] & 0x80)
+ *LCNOffset = -1;
+ memcpy(LCNOffset, *dataRun, offsetBytes);
+
+ (*dataRun) += offsetBytes;
+ }
+
+ return TRUE;
+}
+
+// Travers DataRun and insert into a link list
+BOOL CAttrNonResident::ParseDataRun()
+{
+ NTFS_TRACE("Parsing Non Resident DataRun\n");
+ NTFS_TRACE2("Start VCN = %I64u, End VCN = %I64u\n",
+ AttrHeaderNR->StartVCN, AttrHeaderNR->LastVCN);
+
+ const BYTE *dataRun = (BYTE*)AttrHeaderNR + AttrHeaderNR->DataRunOffset;
+ LONGLONG length;
+ LONGLONG LCNOffset;
+ LONGLONG LCN = 0;
+ ULONGLONG VCN = 0;
+
+ while (*dataRun)
+ {
+ if (PickData(&dataRun, &length, &LCNOffset))
+ {
+ LCN += LCNOffset;
+ if (LCN < 0)
+ {
+ NTFS_TRACE("DataRun decode error 2\n");
+ return FALSE;
+ }
+
+ NTFS_TRACE2("Data length = %I64d clusters, LCN = %I64d", length, LCN);
+ NTFS_TRACE(LCNOffset == 0 ? ", Sparse Data\n" : "\n");
+
+ // Store LCN, Data size (clusters) into list
+ DataRun_Entry *dr = new DataRun_Entry;
+ dr->LCN = (LCNOffset == 0) ? -1 : LCN;
+ dr->Clusters = length;
+ dr->StartVCN = VCN;
+ VCN += length;
+ dr->LastVCN = VCN - 1;
+
+ if (dr->LastVCN <= (AttrHeaderNR->LastVCN - AttrHeaderNR->StartVCN))
+ {
+ DataRunList.InsertEntry(dr);
+ }
+ else
+ {
+ NTFS_TRACE("DataRun decode error: VCN exceeds bound\n");
+
+ // Remove entries
+ DataRunList.RemoveAll();
+
+ return FALSE;
+ }
+ }
+ else
+ break;
+ }
+
+ return TRUE;
+}
+
+// Read clusters from disk, or sparse data
+// *actural = Clusters acturally read
+BOOL CAttrNonResident::ReadClusters(void *buf, DWORD clusters, LONGLONG lcn)
+{
+ if (lcn == -1) // sparse data
+ {
+ NTFS_TRACE("Sparse Data, Fill the buffer with 0\n");
+
+ // Fill the buffer with 0
+ memset(buf, 0, clusters * _ClusterSize);
+
+ return TRUE;
+ }
+
+ LARGE_INTEGER addr;
+ DWORD len;
+
+ addr.QuadPart = lcn * _ClusterSize;
+ len = SetFilePointer(_hVolume, addr.LowPart, &addr.HighPart, FILE_BEGIN);
+
+ if (len == (DWORD)-1 && GetLastError() != NO_ERROR)
+ {
+ NTFS_TRACE1("Cannot locate cluster with LCN %I64d\n", lcn);
+ }
+ else
+ {
+ if (ReadFile(_hVolume, buf, clusters*_ClusterSize, &len, NULL) &&
+ len == clusters*_ClusterSize)
+ {
+ NTFS_TRACE2("Successfully read %u clusters from LCN %I64d\n", clusters, lcn);
+ return TRUE;
+ }
+ else
+ {
+ NTFS_TRACE1("Cannot read cluster with LCN %I64d\n", lcn);
+ }
+ }
+
+ return FALSE;
+}
+
+// Read Data, cluster based
+// clusterNo: Begnning cluster Number
+// clusters: Clusters to read
+// bufv, bufLen: Returned data
+// *actural = Number of bytes acturally read
+BOOL CAttrNonResident::ReadVirtualClusters(ULONGLONG vcn, DWORD clusters,
+ void *bufv, DWORD bufLen, DWORD *actural)
+{
+ _ASSERT(bufv);
+ _ASSERT(clusters);
+
+ *actural = 0;
+ BYTE *buf = (BYTE*)bufv;
+
+ // Verify if clusters exceeds DataRun bounds
+ if (vcn + clusters > (AttrHeaderNR->LastVCN - AttrHeaderNR->StartVCN +1))
+ {
+ NTFS_TRACE("Cluster exceeds DataRun bounds\n");
+ return FALSE;
+ }
+
+ // Verify buffer size
+ if (bufLen < clusters*_ClusterSize)
+ {
+ NTFS_TRACE("Buffer size too small\n");
+ return FALSE;
+ }
+
+ // Traverse the DataRun List to find the according LCN
+ const DataRun_Entry *dr = DataRunList.FindFirstEntry();
+ while(dr)
+ {
+ if (vcn>=dr->StartVCN && vcn<=dr->LastVCN)
+ {
+ DWORD clustersToRead;
+
+ ULONGLONG vcns = dr->LastVCN - vcn + 1; // Clusters from read pointer to the end
+
+ if ((ULONGLONG)clusters > vcns) // Fragmented data, we must go on
+ clustersToRead = (DWORD)vcns;
+ else
+ clustersToRead = clusters;
+ if (ReadClusters(buf, clustersToRead, dr->LCN+(vcn-dr->StartVCN)))
+ {
+ buf += clustersToRead*_ClusterSize;
+ clusters -= clustersToRead;
+ *actural += clustersToRead;
+ vcn += clustersToRead;
+ }
+ else
+ break;
+
+ if (clusters == 0)
+ break;
+ }
+
+ dr = DataRunList.FindNextEntry();
+ }
+
+ *actural *= _ClusterSize;
+ return TRUE;
+}
+
+// Judge if the DataRun is successfully parsed
+__inline BOOL CAttrNonResident::IsDataRunOK() const
+{
+ return bDataRunOK;
+}
+
+// Return Actural Data Size
+// *allocSize = Allocated Size
+__inline ULONGLONG CAttrNonResident::GetDataSize(ULONGLONG *allocSize) const
+{
+ if (allocSize)
+ *allocSize = AttrHeaderNR->AllocSize;
+
+ return AttrHeaderNR->RealSize;
+}
+
+// Read "bufLen" bytes from "offset" into "bufv"
+// Number of bytes acturally read is returned in "*actural"
+BOOL CAttrNonResident::ReadData(const ULONGLONG &offset, void *bufv, DWORD bufLen, DWORD *actural) const
+{
+ // Hard disks can only be accessed by sectors
+ // To be simple and efficient, only implemented cluster based accessing
+ // So cluster unaligned data address should be processed carefully here
+
+ _ASSERT(bufv);
+
+ *actural = 0;
+ if (bufLen == 0)
+ return TRUE;
+
+ // Bounds check
+ if (offset > AttrHeaderNR->RealSize)
+ return FALSE;
+ if ((offset + bufLen) > AttrHeaderNR->RealSize)
+ bufLen = (DWORD)(AttrHeaderNR->RealSize - offset);
+
+ DWORD len;
+ BYTE *buf = (BYTE*)bufv;
+
+ // First cluster Number
+ ULONGLONG startVCN = offset / _ClusterSize;
+ // Bytes in first cluster
+ DWORD startBytes = _ClusterSize - (DWORD)(offset % _ClusterSize);
+ // Read first cluster
+ if (startBytes != _ClusterSize)
+ {
+ // First cluster, Unaligned
+ if (((CAttrNonResident*)this)->ReadVirtualClusters(startVCN, 1, UnalignedBuf, _ClusterSize, &len)
+ && len == _ClusterSize)
+ {
+ len = (startBytes < bufLen) ? startBytes : bufLen;
+ memcpy(buf, UnalignedBuf + _ClusterSize - startBytes, len);
+ buf += len;
+ bufLen -= len;
+ *actural += len;
+ startVCN++;
+ }
+ else
+ return FALSE;
+ }
+ if (bufLen == 0)
+ return TRUE;
+
+ DWORD alignedClusters = bufLen / _ClusterSize;
+ if (alignedClusters)
+ {
+ // Aligned clusters
+ DWORD alignedSize = alignedClusters*_ClusterSize;
+ if (((CAttrNonResident*)this)->ReadVirtualClusters(startVCN, alignedClusters, buf, alignedSize, &len)
+ && len == alignedSize)
+ {
+ startVCN += alignedClusters;
+ buf += alignedSize;
+ bufLen %= _ClusterSize;
+ *actural += len;
+
+ if (bufLen == 0)
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+
+ // Last cluster, Unaligned
+ if (((CAttrNonResident*)this)->ReadVirtualClusters(startVCN, 1, UnalignedBuf, _ClusterSize, &len)
+ && len == _ClusterSize)
+ {
+ memcpy(buf, UnalignedBuf, bufLen);
+ *actural += bufLen;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+///////////////////////////////////
+// Attribute: Standard Information
+///////////////////////////////////
+class CAttr_StdInfo : public CAttrResident
+{
+public:
+ CAttr_StdInfo(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttr_StdInfo();
+
+private:
+ const ATTR_STANDARD_INFORMATION *StdInfo;
+
+public:
+ void GetFileTime(FILETIME *writeTm, FILETIME *createTm = NULL, FILETIME *accessTm = NULL) const;
+ __inline DWORD GetFilePermission() 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;
+
+ static void UTC2Local(const ULONGLONG &ultm, FILETIME *lftm);
+}; // CAttr_StdInfo
+
+CAttr_StdInfo::CAttr_StdInfo(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrResident(ahc, fr)
+{
+ NTFS_TRACE("Attribute: Standard Information\n");
+
+ StdInfo = (ATTR_STANDARD_INFORMATION*)AttrBody;
+}
+
+CAttr_StdInfo::~CAttr_StdInfo()
+{
+ NTFS_TRACE("CAttr_StdInfo deleted\n");
+}
+
+// Change from UTC time to local time
+void CAttr_StdInfo::GetFileTime(FILETIME *writeTm, FILETIME *createTm, FILETIME *accessTm) const
+{
+ UTC2Local(StdInfo->AlterTime, writeTm);
+
+ if (createTm)
+ UTC2Local(StdInfo->CreateTime, createTm);
+
+ if (accessTm)
+ UTC2Local(StdInfo->ReadTime, accessTm);
+}
+
+__inline DWORD CAttr_StdInfo::GetFilePermission() const
+{
+ return StdInfo->Permission;
+}
+
+__inline BOOL CAttr_StdInfo::IsReadOnly() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_READONLY);
+}
+
+__inline BOOL CAttr_StdInfo::IsHidden() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_HIDDEN);
+}
+
+__inline BOOL CAttr_StdInfo::IsSystem() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_SYSTEM);
+}
+
+__inline BOOL CAttr_StdInfo::IsCompressed() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_COMPRESSED);
+}
+
+__inline BOOL CAttr_StdInfo::IsEncrypted() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_ENCRYPTED);
+}
+
+__inline BOOL CAttr_StdInfo::IsSparse() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_SPARSE);
+}
+
+// UTC filetime to Local filetime
+void CAttr_StdInfo::UTC2Local(const ULONGLONG &ultm, FILETIME *lftm)
+{
+ LARGE_INTEGER fti;
+ FILETIME ftt;
+
+ fti.QuadPart = ultm;
+ ftt.dwHighDateTime = fti.HighPart;
+ ftt.dwLowDateTime = fti.LowPart;
+
+ if (!FileTimeToLocalFileTime(&ftt, lftm))
+ *lftm = ftt;
+}
+
+
+////////////////////////////////////////
+// FileName helper class
+// used by FileName and IndexEntry
+////////////////////////////////////////
+class CFileName
+{
+public:
+ CFileName(ATTR_FILE_NAME *fn = NULL);
+ virtual ~CFileName();
+
+protected:
+ const ATTR_FILE_NAME *FileName; // May be NULL for an IndexEntry
+ wchar_t *FileNameWUC; // Uppercase Unicode File Name, used to compare file names
+ int FileNameLength;
+ BOOL IsCopy;
+
+ __inline void SetFileName(ATTR_FILE_NAME *fn);
+ void CFileName::CopyFileName(const CFileName *fn, const ATTR_FILE_NAME *afn);
+
+private:
+ void GetFileNameWUC();
+
+public:
+ int Compare(const wchar_t *fn) const;
+ int Compare(const char *fn) const;
+
+ __inline ULONGLONG GetFileSize() const;
+ __inline DWORD GetFilePermission() const;
+ __inline BOOL IsReadOnly() const;
+ __inline BOOL IsHidden() const;
+ __inline BOOL IsSystem() const;
+ __inline BOOL IsDirectory() const;
+ __inline BOOL IsCompressed() const;
+ __inline BOOL IsEncrypted() const;
+ __inline BOOL IsSparse() const;
+
+ int GetFileName(char *buf, DWORD bufLen) const;
+ int GetFileName(wchar_t *buf, DWORD bufLen) const;
+ __inline BOOL HasName() const;
+ __inline BOOL IsWin32Name() const;
+
+ void GetFileTime(FILETIME *writeTm, FILETIME *createTm = NULL, FILETIME *accessTm = NULL) const;
+}; // CFileName
+
+CFileName::CFileName(ATTR_FILE_NAME *fn)
+{
+ IsCopy = FALSE;
+
+ FileName = fn;
+
+ FileNameWUC = NULL;
+ FileNameLength = 0;
+
+ if (fn)
+ GetFileNameWUC();
+}
+
+CFileName::~CFileName()
+{
+ if (FileNameWUC)
+ delete FileNameWUC;
+}
+
+__inline void CFileName::SetFileName(ATTR_FILE_NAME *fn)
+{
+ FileName = fn;
+
+ GetFileNameWUC();
+}
+
+// Copy pointer buffers
+void CFileName::CopyFileName(const CFileName *fn, const ATTR_FILE_NAME *afn)
+{
+ if (!IsCopy)
+ {
+ NTFS_TRACE("Cannot call this routine\n");
+ return;
+ }
+
+ _ASSERT(fn && afn);
+
+ NTFS_TRACE("FileName Copied\n");
+
+ if (FileNameWUC)
+ delete FileNameWUC;
+
+ FileNameLength = fn->FileNameLength;
+ FileName = afn;
+
+ if (fn->FileNameWUC)
+ {
+ FileNameWUC = new wchar_t[FileNameLength+1];
+ wcsncpy(FileNameWUC, fn->FileNameWUC, FileNameLength);
+ FileNameWUC[FileNameLength] = wchar_t('\0');
+ }
+ else
+ FileNameWUC = NULL;
+}
+
+// Get uppercase unicode filename and store it in a buffer
+void CFileName::GetFileNameWUC()
+{
+#ifdef _DEBUG
+ char fna[MAX_PATH];
+ GetFileName(fna, MAX_PATH); // Just show filename in debug window
+#endif
+
+ if (FileNameWUC)
+ {
+ delete FileNameWUC;
+ FileNameWUC = NULL;
+ FileNameLength = 0;
+ }
+
+ wchar_t fns[MAX_PATH];
+ FileNameLength = GetFileName(fns, MAX_PATH);
+
+ if (FileNameLength > 0)
+ {
+ FileNameWUC = new wchar_t[FileNameLength+1];
+ for (int i=0; i<FileNameLength; i++)
+ FileNameWUC[i] = towupper(fns[i]);
+ FileNameWUC[FileNameLength] = wchar_t('\0');
+ }
+ else
+ {
+ FileNameLength = 0;
+ FileNameWUC = NULL;
+ }
+}
+
+// Compare Unicode file name
+int CFileName::Compare(const wchar_t *fn) const
+{
+ // Change fn to upper case
+ int len = wcslen(fn);
+ if (len > MAX_PATH)
+ return 1; // Assume bigger
+
+ wchar_t fns[MAX_PATH];
+
+ for (int i=0; i<len; i++)
+ fns[i] = towupper(fn[i]);
+ fns[len] = wchar_t('\0');
+
+ return wcscmp(fns, FileNameWUC);
+}
+
+// Compare ANSI file name
+int CFileName::Compare(const char *fn) const
+{
+ wchar_t fnw[MAX_PATH];
+
+ int len = MultiByteToWideChar(CP_ACP, 0, fn, -1, fnw, MAX_PATH);
+ if (len)
+ return Compare(fnw);
+ else
+ return 1; // Assume bigger
+}
+
+__inline ULONGLONG CFileName::GetFileSize() const
+{
+ return FileName ? FileName->RealSize : 0;
+}
+
+__inline DWORD CFileName::GetFilePermission() const
+{
+ return FileName ? FileName->Flags : 0;
+}
+
+__inline BOOL CFileName::IsReadOnly() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_READONLY) : FALSE;
+}
+
+__inline BOOL CFileName::IsHidden() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_HIDDEN) : FALSE;
+}
+
+__inline BOOL CFileName::IsSystem() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_SYSTEM) : FALSE;
+}
+
+__inline BOOL CFileName::IsDirectory() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_DIRECTORY) : FALSE;
+}
+
+__inline BOOL CFileName::IsCompressed() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_COMPRESSED) : FALSE;
+}
+
+__inline BOOL CFileName::IsEncrypted() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_ENCRYPTED) : FALSE;
+}
+
+__inline BOOL CFileName::IsSparse() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_SPARSE) : FALSE;
+}
+
+// Get ANSI File Name
+// Return 0: Unnamed, <0: buffer too small, -buffersize, >0 Name length
+int CFileName::GetFileName(char *buf, DWORD bufLen) const
+{
+ if (FileName == NULL)
+ return 0;
+
+ int len = 0;
+
+ if (FileName->NameLength)
+ {
+ if (bufLen < FileName->NameLength)
+ return -1*FileName->NameLength; // buffer too small
+
+ len = WideCharToMultiByte(CP_ACP, 0, (wchar_t*)FileName->Name, FileName->NameLength,
+ buf, bufLen, NULL, NULL);
+ if (len)
+ {
+ buf[len] = '\0';
+ NTFS_TRACE1("File Name: %s\n", buf);
+ NTFS_TRACE4("File Permission: %s\t%c%c%c\n", IsDirectory()?"Directory":"File",
+ IsReadOnly()?'R':' ', IsHidden()?'H':' ', IsSystem()?'S':' ');
+ }
+ else
+ {
+ NTFS_TRACE("Unrecognized File Name or FileName buffer too small\n");
+ }
+ }
+
+ return len;
+}
+
+// Get Unicode File Name
+// Return 0: Unnamed, <0: buffer too small, -buffersize, >0 Name length
+int CFileName::GetFileName(wchar_t *buf, DWORD bufLen) const
+{
+ if (FileName == NULL)
+ return 0;
+
+ if (FileName->NameLength)
+ {
+ if (bufLen < FileName->NameLength)
+ return -1*FileName->NameLength; // buffer too small
+
+ bufLen = FileName->NameLength;
+ wcsncpy(buf, (wchar_t*)FileName->Name, bufLen);
+ buf[bufLen] = wchar_t('\0');
+
+ return bufLen;
+ }
+
+ return 0;
+}
+
+__inline BOOL CFileName::HasName() const
+{
+ return FileNameLength > 0;
+}
+
+__inline BOOL CFileName::IsWin32Name() const
+{
+ if (FileName == NULL || FileNameLength <= 0)
+ return FALSE;
+
+ return (FileName->NameSpace != ATTR_FILENAME_NAMESPACE_DOS); // POSIX, WIN32, WIN32_DOS
+}
+
+// Change from UTC time to local time
+void CFileName::GetFileTime(FILETIME *writeTm, FILETIME *createTm, FILETIME *accessTm) const
+{
+ CAttr_StdInfo::UTC2Local(FileName ? FileName->AlterTime : 0, writeTm);
+
+ if (createTm)
+ CAttr_StdInfo::UTC2Local(FileName ? FileName->CreateTime : 0, createTm);
+
+ if (accessTm)
+ CAttr_StdInfo::UTC2Local(FileName ? FileName->ReadTime : 0, accessTm);
+}
+
+
+////////////////////////////////
+// Attribute: File Name
+////////////////////////////////
+class CAttr_FileName : public CAttrResident, public CFileName
+{
+public:
+ CAttr_FileName(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrResident(ahc, fr)
+ {
+ NTFS_TRACE("Attribute: File Name\n");
+
+ SetFileName((ATTR_FILE_NAME*)AttrBody);
+ }
+
+ virtual ~CAttr_FileName()
+ {
+ NTFS_TRACE("CAttr_FileName deleted\n");
+ }
+
+private:
+ // File permission and time in $FILE_NAME only updates when the filename changes
+ // So hide these functions to prevent user from getting the error information
+ // Standard Information and IndexEntry keeps the most recent file time and permission infomation
+ void GetFileTime(FILETIME *writeTm, FILETIME *createTm = NULL, FILETIME *accessTm = NULL) const {}
+ __inline DWORD GetFilePermission(){}
+ __inline BOOL IsReadOnly() const {}
+ __inline BOOL IsHidden() const {}
+ __inline BOOL IsSystem() const {}
+ __inline BOOL IsCompressed() const {}
+ __inline BOOL IsEncrypted() const {}
+ __inline BOOL IsSparse() const {}
+}; // CAttr_FileName
+
+
+//////////////////////////////////
+// Attribute: Volume Information
+//////////////////////////////////
+class CAttr_VolInfo : public CAttrResident
+{
+public:
+ CAttr_VolInfo(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrResident(ahc, fr)
+ {
+ NTFS_TRACE("Attribute: Volume Information\n");
+
+ VolInfo = (ATTR_VOLUME_INFORMATION*)AttrBody;
+ }
+
+ virtual ~CAttr_VolInfo()
+ {
+ NTFS_TRACE("CAttr_VolInfo deleted\n");
+ }
+
+private:
+ const ATTR_VOLUME_INFORMATION *VolInfo;
+
+public:
+ // Get NTFS Volume Version
+ __inline WORD GetVersion()
+ {
+ return MAKEWORD(VolInfo->MinorVersion, VolInfo->MajorVersion);
+ }
+}; // CAttr_VolInfo
+
+
+///////////////////////////
+// Attribute: Volume Name
+///////////////////////////
+class CAttr_VolName : public CAttrResident
+{
+public:
+ CAttr_VolName(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrResident(ahc, fr)
+ {
+ NTFS_TRACE("Attribute: Volume Name\n");
+
+ NameLength = AttrBodySize >> 1;
+ VolNameU = new wchar_t[NameLength+1];
+ VolNameA = new char[NameLength+1];
+
+ memcpy(VolNameU, AttrBody, AttrBodySize);
+ VolNameU[NameLength] = wchar_t('\0');
+
+ int len = WideCharToMultiByte(CP_ACP, 0, VolNameU, NameLength,
+ VolNameA, NameLength, NULL, NULL);
+ VolNameA[NameLength] = '\0';
+ }
+
+ virtual ~CAttr_VolName()
+ {
+ NTFS_TRACE("CAttr_VolName deleted\n");
+
+ delete VolNameU;
+ delete VolNameA;
+ }
+
+private:
+ wchar_t *VolNameU;
+ char *VolNameA;
+ DWORD NameLength;
+
+public:
+ // Get NTFS Volume Unicode Name
+ __inline int GetName(wchar_t *buf, DWORD len) const
+ {
+ if (len < NameLength)
+ return -1*NameLength; // buffer too small
+
+ wcsncpy(buf, VolNameU, NameLength+1);
+ return NameLength;
+ }
+
+ // ANSI Name
+ __inline int GetName(char *buf, DWORD len) const
+ {
+ if (len < NameLength)
+ return -1*NameLength; // buffer too small
+
+ strncpy(buf, VolNameA, NameLength+1);
+ return NameLength;
+ }
+}; // CAttr_VolInfo
+
+
+/////////////////////////////////////
+// Attribute: Data
+/////////////////////////////////////
+template <class TYPE_RESIDENT>
+class CAttr_Data : public TYPE_RESIDENT
+{
+public:
+ CAttr_Data(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : TYPE_RESIDENT(ahc, fr)
+ {
+ NTFS_TRACE1("Attribute: Data (%sResident)\n", IsNonResident() ? "Non" : "");
+ }
+
+ virtual ~CAttr_Data()
+ {
+ NTFS_TRACE("CAttr_Data deleted\n");
+ }
+}; // CAttr_Data
+
+
+/////////////////////////////
+// Index Entry helper class
+/////////////////////////////
+class CIndexEntry : public CFileName
+{
+public:
+ CIndexEntry()
+ {
+ NTFS_TRACE("Index Entry\n");
+
+ IsDefault = TRUE;
+
+ IndexEntry = NULL;
+ SetFileName(NULL);
+ }
+
+ CIndexEntry(const INDEX_ENTRY *ie)
+ {
+ NTFS_TRACE("Index Entry\n");
+
+ IsDefault = FALSE;
+
+ _ASSERT(ie);
+ IndexEntry = ie;
+
+ if (IsSubNodePtr())
+ {
+ NTFS_TRACE("Points to sub-node\n");
+ }
+
+ if (ie->StreamSize)
+ {
+ SetFileName((ATTR_FILE_NAME*)(ie->Stream));
+ }
+ else
+ {
+ NTFS_TRACE("No FileName stream found\n");
+ }
+ }
+
+ virtual ~CIndexEntry()
+ {
+ // Never touch *IndexEntry here if IsCopy == FALSE !
+ // As the memory have been deallocated by ~CIndexBlock()
+
+ if (IsCopy && IndexEntry)
+ delete (void*)IndexEntry;
+
+ NTFS_TRACE("CIndexEntry deleted\n");
+ }
+
+private:
+ BOOL IsDefault;
+
+protected:
+ const INDEX_ENTRY *IndexEntry;
+
+public:
+ // Use with caution !
+ CIndexEntry& operator = (const CIndexEntry &ieClass)
+ {
+ if (!IsDefault)
+ {
+ NTFS_TRACE("Cannot call this routine\n");
+ return *this;
+ }
+
+ NTFS_TRACE("Index Entry Copied\n");
+
+ IsCopy = TRUE;
+
+ if (IndexEntry)
+ {
+ delete (void*)IndexEntry;
+ IndexEntry = NULL;
+ }
+
+ const INDEX_ENTRY *ie = ieClass.IndexEntry;
+ _ASSERT(ie && (ie->Size > 0));
+
+ IndexEntry = (INDEX_ENTRY*)new BYTE[ie->Size];
+ memcpy((void*)IndexEntry, ie, ie->Size);
+ CopyFileName(&ieClass, (ATTR_FILE_NAME*)(IndexEntry->Stream));
+
+ return *this;
+ }
+
+ __inline ULONGLONG GetFileReference() const
+ {
+ if (IndexEntry)
+ return IndexEntry->FileReference & 0x0000FFFFFFFFFFFFUL;
+ else
+ return (ULONGLONG)-1;
+ }
+
+ __inline BOOL IsSubNodePtr() const
+ {
+ if (IndexEntry)
+ return (IndexEntry->Flags & INDEX_ENTRY_FLAG_SUBNODE);
+ else
+ return FALSE;
+ }
+
+ __inline ULONGLONG GetSubNodeVCN() const
+ {
+ if (IndexEntry)
+ return *(ULONGLONG*)((BYTE*)IndexEntry + IndexEntry->Size - 8);
+ else
+ return (ULONGLONG)-1;
+ }
+}; // CIndexEntry
+
+
+///////////////////////////////
+// Index Block helper class
+///////////////////////////////
+class CIndexBlock : public CIndexEntryList
+{
+public:
+ CIndexBlock()
+ {
+ NTFS_TRACE("Index Block\n");
+
+ IndexBlock = NULL;
+ }
+
+ virtual ~CIndexBlock()
+ {
+ NTFS_TRACE("IndexBlock deleted\n");
+
+ if (IndexBlock)
+ delete IndexBlock;
+ }
+
+private:
+ INDEX_BLOCK *IndexBlock;
+
+public:
+ INDEX_BLOCK *AllocIndexBlock(DWORD size)
+ {
+ // Free previous data if any
+ if (GetCount() > 0)
+ RemoveAll();
+ if (IndexBlock)
+ delete IndexBlock;
+
+ IndexBlock = (INDEX_BLOCK*)new BYTE[size];
+
+ return IndexBlock;
+ }
+}; // CIndexBlock
+
+
+/////////////////////////////////////
+// Attribute: Index Root (Resident)
+/////////////////////////////////////
+class CAttr_IndexRoot : public CAttrResident, public CIndexEntryList
+{
+public:
+ CAttr_IndexRoot(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttr_IndexRoot();
+
+private:
+ const ATTR_INDEX_ROOT *IndexRoot;
+
+ void ParseIndexEntries();
+
+public:
+ __inline BOOL IsFileName() const;
+}; // CAttr_IndexRoot
+
+CAttr_IndexRoot::CAttr_IndexRoot(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr): CAttrResident(ahc, fr)
+{
+ NTFS_TRACE("Attribute: Index Root\n");
+
+ IndexRoot = (ATTR_INDEX_ROOT*)AttrBody;
+
+ if (IsFileName())
+ {
+ ParseIndexEntries();
+ }
+ else
+ {
+ NTFS_TRACE("Index View not supported\n");
+ }
+}
+
+CAttr_IndexRoot::~CAttr_IndexRoot()
+{
+ NTFS_TRACE("CAttr_IndexRoot deleted\n");
+}
+
+// Get all the index entries
+void CAttr_IndexRoot::ParseIndexEntries()
+{
+ INDEX_ENTRY *ie;
+ ie = (INDEX_ENTRY*)((BYTE*)(&(IndexRoot->EntryOffset)) + IndexRoot->EntryOffset);
+
+ DWORD ieTotal = ie->Size;
+
+ while (ieTotal <= IndexRoot->TotalEntrySize)
+ {
+ CIndexEntry *ieClass = new CIndexEntry(ie);
+ InsertEntry(ieClass);
+
+ if (ie->Flags & INDEX_ENTRY_FLAG_LAST)
+ {
+ NTFS_TRACE("Last Index Entry\n");
+ break;
+ }
+
+ ie = (INDEX_ENTRY*)((BYTE*)ie + ie->Size); // Pick next
+ ieTotal += ie->Size;
+ }
+}
+
+// Check if this IndexRoot contains FileName or IndexView
+__inline BOOL CAttr_IndexRoot::IsFileName() const
+{
+ return (IndexRoot->AttrType == ATTR_TYPE_FILE_NAME);
+}
+
+
+/////////////////////////////////////////////
+// Attribute: Index Allocation (NonResident)
+/////////////////////////////////////////////
+class CAttr_IndexAlloc : public CAttrNonResident
+{
+public:
+ CAttr_IndexAlloc(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttr_IndexAlloc();
+
+private:
+ ULONGLONG IndexBlockCount;
+
+ BOOL PatchUS(WORD *sector, int sectors, WORD usn, WORD *usarray);
+
+public:
+ __inline ULONGLONG GetIndexBlockCount();
+ BOOL ParseIndexBlock(const ULONGLONG &vcn, CIndexBlock &ibClass);
+}; // CAttr_IndexAlloc
+
+CAttr_IndexAlloc::CAttr_IndexAlloc(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrNonResident(ahc, fr)
+{
+ NTFS_TRACE("Attribute: Index Allocation\n");
+
+ IndexBlockCount = 0;
+
+ if (IsDataRunOK())
+ {
+ // Get total number of Index Blocks
+ ULONGLONG ibTotalSize;
+ ibTotalSize = GetDataSize();
+ if (ibTotalSize % _IndexBlockSize)
+ {
+ NTFS_TRACE2("Cannot calulate number of IndexBlocks, total size = %I64u, unit = %u\n",
+ ibTotalSize, _IndexBlockSize);
+ return;
+ }
+ IndexBlockCount = ibTotalSize / _IndexBlockSize;
+ }
+ else
+ {
+ NTFS_TRACE("Index Allocation DataRun parse error\n");
+ }
+}
+
+CAttr_IndexAlloc::~CAttr_IndexAlloc()
+{
+ NTFS_TRACE("CAttr_IndexAlloc deleted\n");
+}
+
+// Verify US and update sectors
+BOOL CAttr_IndexAlloc::PatchUS(WORD *sector, int sectors, WORD usn, WORD *usarray)
+{
+ int i;
+
+ for (i=0; i<sectors; i++)
+ {
+ sector += ((_SectorSize>>1) - 1);
+ if (*sector != usn)
+ return FALSE; // USN error
+ *sector = usarray[i]; // Write back correct data
+ sector++;
+ }
+ return TRUE;
+}
+
+__inline ULONGLONG CAttr_IndexAlloc::GetIndexBlockCount()
+{
+ return IndexBlockCount;
+}
+
+// Parse a single Index Block
+// vcn = Index Block VCN in Index Allocation Data Attributes
+// ibClass holds the parsed Index Entries
+BOOL CAttr_IndexAlloc::ParseIndexBlock(const ULONGLONG &vcn, CIndexBlock &ibClass)
+{
+ if (vcn >= IndexBlockCount) // Bounds check
+ return FALSE;
+
+ // Allocate buffer for a single Index Block
+ INDEX_BLOCK *ibBuf = ibClass.AllocIndexBlock(_IndexBlockSize);
+
+ // Sectors Per Index Block
+ DWORD sectors = _IndexBlockSize / _SectorSize;
+
+ // Read one Index Block
+ DWORD len;
+ if (ReadData(vcn*_IndexBlockSize, ibBuf, _IndexBlockSize, &len) &&
+ len == _IndexBlockSize)
+ {
+ if (ibBuf->Magic != INDEX_BLOCK_MAGIC)
+ {
+ NTFS_TRACE("Index Block parse error: Magic mismatch\n");
+ return FALSE;
+ }
+
+ // Patch US
+ WORD *usnaddr = (WORD*)((BYTE*)ibBuf + ibBuf->OffsetOfUS);
+ WORD usn = *usnaddr;
+ WORD *usarray = usnaddr + 1;
+ if (!PatchUS((WORD*)ibBuf, sectors, usn, usarray))
+ {
+ NTFS_TRACE("Index Block parse error: Update Sequence Number\n");
+ return FALSE;
+ }
+
+ INDEX_ENTRY *ie;
+ ie = (INDEX_ENTRY*)((BYTE*)(&(ibBuf->EntryOffset)) + ibBuf->EntryOffset);
+
+ DWORD ieTotal = ie->Size;
+
+ while (ieTotal <= ibBuf->TotalEntrySize)
+ {
+ CIndexEntry *ieClass = new CIndexEntry(ie);
+ ibClass.InsertEntry(ieClass);
+
+ if (ie->Flags & INDEX_ENTRY_FLAG_LAST)
+ {
+ NTFS_TRACE("Last Index Entry\n");
+ break;
+ }
+
+ ie = (INDEX_ENTRY*)((BYTE*)ie + ie->Size); // Pick next
+ ieTotal += ie->Size;
+ }
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+////////////////////////////////////////////
+// Attribute: Bitmap
+////////////////////////////////////////////
+template <class TYPE_RESIDENT>
+class CAttr_Bitmap : public TYPE_RESIDENT
+{
+public:
+ CAttr_Bitmap(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttr_Bitmap();
+
+private:
+ ULONGLONG BitmapSize; // Bitmap data size
+ BYTE *BitmapBuf; // Bitmap data buffer
+ LONGLONG CurrentCluster;
+
+public:
+ BOOL IsClusterFree(const ULONGLONG &cluster) const;
+}; // CAttr_Bitmap
+
+template <class TYPE_RESIDENT>
+CAttr_Bitmap<TYPE_RESIDENT>::CAttr_Bitmap(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : TYPE_RESIDENT(ahc, fr)
+{
+ NTFS_TRACE1("Attribute: Bitmap (%sResident)\n", IsNonResident() ? "Non" : "");
+
+ CurrentCluster = -1;
+
+ if (IsDataRunOK())
+ {
+ BitmapSize = GetDataSize();
+
+ if (IsNonResident())
+ BitmapBuf = new BYTE[_ClusterSize];
+ else
+ {
+ BitmapBuf = new BYTE[(DWORD)BitmapSize];
+
+ DWORD len;
+ if (!(ReadData(0, BitmapBuf, (DWORD)BitmapSize, &len)
+ && len == (DWORD)BitmapSize))
+ {
+ BitmapBuf = NULL;
+ NTFS_TRACE("Read Resident Bitmap data failed\n");
+ }
+ else
+ {
+ NTFS_TRACE1("%u bytes of resident Bitmap data read\n", len);
+ }
+ }
+ }
+ else
+ {
+ BitmapSize = 0;
+ BitmapBuf = 0;
+ }
+}
+
+template <class TYPE_RESIDENT>
+CAttr_Bitmap<TYPE_RESIDENT>::~CAttr_Bitmap()
+{
+ if (BitmapBuf)
+ delete BitmapBuf;
+
+ NTFS_TRACE("CAttr_Bitmap deleted\n");
+}
+
+// Verify if a single cluster is free
+template <class TYPE_RESIDENT>
+BOOL CAttr_Bitmap<TYPE_RESIDENT>::IsClusterFree(const ULONGLONG &cluster) const
+{
+ if (!IsDataRunOK() || !BitmapBuf)
+ return FALSE;
+
+ if (IsNonResident())
+ {
+ LONGLONG idx = (LONGLONG)cluster >> 3;
+ DWORD clusterSize = ((CNTFSVolume*)Volume)->GetClusterSize();
+
+ LONGLONG clusterOffset = idx/clusterSize;
+ cluster -= (clusterOffset*clusterSize*8);
+
+ // Read one cluster of data if buffer mismatch
+ if (CurrentCluster != clusterOffset)
+ {
+ DWORD len;
+ if (ReadData(clusterOffset, BitmapBuf, clusterSize, &len) && len == clusterSize)
+ {
+ CurrentCluster = clusterOffset;
+ }
+ else
+ {
+ CurrentCluster = -1;
+ return FALSE;
+ }
+ }
+ }
+
+ // All the Bitmap data is already in BitmapBuf
+ DWORD idx = (DWORD)(cluster >> 3);
+ if (IsNonResident() == FALSE)
+ {
+ if (idx >= BitmapSize)
+ return TRUE; // Resident data bounds check error
+ }
+
+ BYTE fac = (BYTE)(cluster % 8);
+
+ return ((BitmapBuf[idx] & (1<<fac)) == 0);
+}
+
+
+////////////////////////////////////////////
+// List to hold external File Records
+////////////////////////////////////////////
+typedef CSList<CFileRecord> CFileRecordList;
+
+////////////////////////////////////////////
+// Attribute: Attribute List
+////////////////////////////////////////////
+template <class TYPE_RESIDENT>
+class CAttr_AttrList : public TYPE_RESIDENT
+{
+public:
+ CAttr_AttrList(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttr_AttrList();
+
+private:
+ CFileRecordList FileRecordList;
+}; // CAttr_AttrList
+
+template <class TYPE_RESIDENT>
+CAttr_AttrList<TYPE_RESIDENT>::CAttr_AttrList(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : TYPE_RESIDENT(ahc, fr)
+{
+ NTFS_TRACE("Attribute: Attribute List\n");
+ if (fr->FileReference == (ULONGLONG)-1)
+ return;
+
+ ULONGLONG offset = 0;
+ DWORD len;
+ ATTR_ATTRIBUTE_LIST alRecord;
+
+ while (ReadData(offset, &alRecord, sizeof(ATTR_ATTRIBUTE_LIST), &len) &&
+ len == sizeof(ATTR_ATTRIBUTE_LIST))
+ {
+ if (ATTR_INDEX(alRecord.AttrType) > ATTR_NUMS)
+ {
+ NTFS_TRACE("Attribute List parse error1\n");
+ break;
+ }
+
+ NTFS_TRACE1("Attribute List: 0x%04x\n", alRecord.AttrType);
+
+ ULONGLONG recordRef = alRecord.BaseRef & 0x0000FFFFFFFFFFFFUL;
+ if (recordRef != fr->FileReference) // Skip contained attributes
+ {
+ DWORD am = ATTR_MASK(alRecord.AttrType);
+ if (am & fr->AttrMask) // Skip unwanted attributes
+ {
+ CFileRecord *frnew = new CFileRecord(fr->Volume);
+ FileRecordList.InsertEntry(frnew);
+
+ frnew->AttrMask = am;
+ if (!frnew->ParseFileRecord(recordRef))
+ {
+ NTFS_TRACE("Attribute List parse error2\n");
+ break;
+ }
+ frnew->ParseAttrs();
+
+ // Insert new found AttrList to fr->AttrList
+ const CAttrBase *ab = (CAttrBase*)frnew->FindFirstAttr(alRecord.AttrType);
+ while (ab)
+ {
+ CAttrList *al = (CAttrList*)&fr->AttrList[ATTR_INDEX(alRecord.AttrType)];
+ al->InsertEntry((CAttrBase*)ab);
+ ab = frnew->FindNextAttr(alRecord.AttrType);
+ }
+
+ // Throw away frnew->AttrList entries to prevent free twice (fr will delete them)
+ frnew->AttrList[ATTR_INDEX(alRecord.AttrType)].ThrowAll();
+ }
+ }
+
+ offset += alRecord.RecordSize;
+ }
+}
+
+template <class TYPE_RESIDENT>
+CAttr_AttrList<TYPE_RESIDENT>::~CAttr_AttrList()
+{
+ NTFS_TRACE("CAttr_AttrList deleted\n");
+}
+
+#endif
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Common.h b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Common.h
new file mode 100644
index 0000000..b7c2813
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Common.h
@@ -0,0 +1,317 @@
+/*
+ * NTFS Class common definitions
+ *
+ * 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_COMMON_H_CYB70289
+#define __NTFS_COMMON_H_CYB70289
+
+#include <windows.h>
+#include <stdio.h>
+#include <tchar.h>
+#include <crtdbg.h>
+
+#include "NTFS_DataType.h"
+
+#define ATTR_NUMS 16 // Attribute Types count
+#define ATTR_INDEX(at) (((at)>>4)-1) // Attribute Type to Index, eg. 0x10->0, 0x30->2
+#define ATTR_MASK(at) (((DWORD)1)<<ATTR_INDEX(at)) // Attribute Bit Mask
+
+// Bit masks of Attributes
+#define MASK_STANDARD_INFORMATION ATTR_MASK(ATTR_TYPE_STANDARD_INFORMATION)
+#define MASK_ATTRIBUTE_LIST ATTR_MASK(ATTR_TYPE_ATTRIBUTE_LIST)
+#define MASK_FILE_NAME ATTR_MASK(ATTR_TYPE_FILE_NAME)
+#define MASK_OBJECT_ID ATTR_MASK(ATTR_TYPE_OBJECT_ID)
+#define MASK_SECURITY_DESCRIPTOR ATTR_MASK(ATTR_TYPE_SECURITY_DESCRIPTOR)
+#define MASK_VOLUME_NAME ATTR_MASK(ATTR_TYPE_VOLUME_NAME)
+#define MASK_VOLUME_INFORMATION ATTR_MASK(ATTR_TYPE_VOLUME_INFORMATION)
+#define MASK_DATA ATTR_MASK(ATTR_TYPE_DATA)
+#define MASK_INDEX_ROOT ATTR_MASK(ATTR_TYPE_INDEX_ROOT)
+#define MASK_INDEX_ALLOCATION ATTR_MASK(ATTR_TYPE_INDEX_ALLOCATION)
+#define MASK_BITMAP ATTR_MASK(ATTR_TYPE_BITMAP)
+#define MASK_REPARSE_POINT ATTR_MASK(ATTR_TYPE_REPARSE_POINT)
+#define MASK_EA_INFORMATION ATTR_MASK(ATTR_TYPE_EA_INFORMATION)
+#define MASK_EA ATTR_MASK(ATTR_TYPE_EA)
+#define MASK_LOGGED_UTILITY_STREAM ATTR_MASK(ATTR_TYPE_LOGGED_UTILITY_STREAM)
+
+#define MASK_ALL ((DWORD)-1)
+
+#define NTFS_TRACE(t1) _RPT0(_CRT_WARN, t1)
+#define NTFS_TRACE1(t1, t2) _RPT1(_CRT_WARN, t1, t2)
+#define NTFS_TRACE2(t1, t2, t3) _RPT2(_CRT_WARN, t1, t2, t3)
+#define NTFS_TRACE3(t1, t2, t3, t4) _RPT3(_CRT_WARN, t1, t2, t3, t4)
+#define NTFS_TRACE4(t1, t2, t3, t4, t5) _RPT4(_CRT_WARN, t1, t2, t3, t4, t5)
+
+// User defined Callback routines to process raw attribute data
+// Set bDiscard to TRUE if this Attribute is to be discarded
+// Set bDiscard to FALSE to let CFileRecord process it
+typedef void (*ATTR_RAW_CALLBACK)(const ATTR_HEADER_COMMON *attrHead, BOOL *bDiscard);
+
+// User defined Callback routine to handle CFileRecord parsed attributes
+// Will be called by CFileRecord::TraverseAttrs() for each attribute
+// attrClass is the according attribute's wrapping class, CAttr_xxx
+// Set bStop to TRUE if don't want to continue
+// Set bStop to FALSE to continue processing
+class CAttrBase;
+typedef void (*ATTRS_CALLBACK)(const CAttrBase *attr, void *context, BOOL *bStop);
+
+// User defined Callback routine to handle Directory traversing
+// Will be called by CFileRecord::TraverseSubEntries for each sub entry
+class CIndexEntry;
+typedef void (*SUBENTRY_CALLBACK)(const CIndexEntry *ie);
+
+
+// List Entry
+template <class ENTRY_TYPE>
+struct NTSLIST_ENTRY
+{
+ NTSLIST_ENTRY *Next;
+ ENTRY_TYPE *Entry;
+};
+
+// List Entry Smart Pointer
+template <class ENTRY_TYPE>
+class CEntrySmartPtr
+{
+public:
+ CEntrySmartPtr(ENTRY_TYPE *ptr = NULL)
+ {
+ EntryPtr = ptr;
+ }
+
+ virtual ~CEntrySmartPtr()
+ {
+ if (EntryPtr)
+ delete EntryPtr;
+ }
+
+private:
+ const ENTRY_TYPE *EntryPtr;
+
+public:
+ __inline CEntrySmartPtr<ENTRY_TYPE> operator = (const ENTRY_TYPE* ptr)
+ {
+ // Delete previous pointer if allocated
+ if (EntryPtr)
+ delete EntryPtr;
+
+ EntryPtr = ptr;
+
+ return *this;
+ }
+
+ __inline const ENTRY_TYPE* operator->() const
+ {
+ _ASSERT(EntryPtr);
+ return EntryPtr;
+ }
+
+ __inline BOOL IsValid() const
+ {
+ return EntryPtr != NULL;
+ }
+};
+
+//////////////////////////////////////
+// Single list implementation
+//////////////////////////////////////
+template <class ENTRY_TYPE>
+class CSList
+{
+public:
+ CSList()
+ {
+ ListHead = ListTail = NULL;
+ ListCurrent = NULL;
+ EntryCount = 0;
+ }
+
+ virtual ~CSList()
+ {
+ RemoveAll();
+ }
+
+private:
+ int EntryCount;
+ NTSLIST_ENTRY<ENTRY_TYPE> *ListHead;
+ NTSLIST_ENTRY<ENTRY_TYPE> *ListTail;
+ NTSLIST_ENTRY<ENTRY_TYPE> *ListCurrent;
+
+public:
+ // Get entry count
+ __inline int GetCount() const
+ {
+ return EntryCount;
+ }
+
+ // Insert to tail
+ BOOL InsertEntry(ENTRY_TYPE *entry)
+ {
+ NTSLIST_ENTRY<ENTRY_TYPE> *le = new NTSLIST_ENTRY<ENTRY_TYPE>;
+ if (!le)
+ return FALSE;
+
+ le->Entry = entry;
+ le->Next = NULL;
+
+ if (ListTail == NULL)
+ ListHead = le; // Empty list
+ else
+ ListTail->Next = le;
+
+ ListTail = le;
+
+ EntryCount++;
+ return TRUE;
+ }
+
+ // Remove all entries
+ void RemoveAll()
+ {
+ while (ListHead)
+ {
+ ListCurrent = ListHead->Next;
+ delete ListHead->Entry;
+ delete ListHead;
+
+ ListHead = ListCurrent;
+ }
+
+ ListHead = ListTail = NULL;
+ ListCurrent = NULL;
+ EntryCount = 0;
+ }
+
+ // Find first entry
+ __inline ENTRY_TYPE *FindFirstEntry() const
+ {
+ ((CSList<ENTRY_TYPE>*)this)->ListCurrent = ListHead;
+
+ if (ListCurrent)
+ return ListCurrent->Entry;
+ else
+ return NULL;
+ }
+
+ // Find next entry
+ __inline ENTRY_TYPE *FindNextEntry() const
+ {
+ if (ListCurrent)
+ ((CSList<ENTRY_TYPE>*)this)->ListCurrent = ListCurrent->Next;
+
+ if (ListCurrent)
+ return ListCurrent->Entry;
+ else
+ return NULL;
+ }
+
+ // Throw all entries
+ // Caution! All entries are just thrown without free
+ __inline void ThrowAll()
+ {
+ ListHead = ListTail = NULL;
+ ListCurrent = NULL;
+ EntryCount = 0;
+ }
+}; //CSList
+
+
+//////////////////////////////////////
+// Stack implementation
+//////////////////////////////////////
+template <class ENTRY_TYPE>
+class CStack
+{
+public:
+ CStack()
+ {
+ ListHead = ListTail = NULL;
+ EntryCount = 0;
+ }
+
+ virtual ~CStack()
+ {
+ RemoveAll();
+ }
+
+private:
+ int EntryCount;
+ NTSLIST_ENTRY<ENTRY_TYPE> *ListHead;
+ NTSLIST_ENTRY<ENTRY_TYPE> *ListTail;
+
+public:
+ // Get entry count
+ __inline int GetCount() const
+ {
+ return EntryCount;
+ }
+
+ // Insert to head
+ BOOL Push(ENTRY_TYPE *entry)
+ {
+ NTSLIST_ENTRY<ENTRY_TYPE> *le = new NTSLIST_ENTRY<ENTRY_TYPE>;
+ if (!le)
+ return FALSE;
+
+ le->Entry = entry;
+ le->Next = ListHead;
+
+ ListHead = le;
+
+ if (ListTail == NULL)
+ ListTail = le; // Empty list
+
+ EntryCount ++;
+ return TRUE;
+ }
+
+ // Remove from head
+ ENTRY_TYPE* Pop()
+ {
+ if (ListHead == NULL)
+ return NULL;
+
+ NTSLIST_ENTRY<ENTRY_TYPE> *le = ListHead;
+ ENTRY_TYPE *e = le->Entry;
+
+ if (ListTail == ListHead)
+ ListTail = ListHead->Next;
+ ListHead = ListHead->Next;
+
+ delete le;
+ EntryCount --;
+
+ return e;
+ }
+
+ // Remove all entries
+ void RemoveAll()
+ {
+ NTSLIST_ENTRY<ENTRY_TYPE> *le;
+
+ while (ListHead)
+ {
+ le = ListHead->Next;
+ delete ListHead->Entry;
+ delete ListHead;
+
+ ListHead = le;
+ }
+
+ ListHead = ListTail = NULL;
+ EntryCount = 0;
+ }
+}; //CStack
+
+#endif
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_DataType.h b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_DataType.h
new file mode 100644
index 0000000..7d9ccc0
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_DataType.h
@@ -0,0 +1,380 @@
+/*
+ * NTFS data structures and definitions
+ *
+ * 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_DATATYPE_H_CYB70289
+#define __NTFS_DATATYPE_H_CYB70289
+
+// NTFS Boot Sector BPB
+
+#define NTFS_SIGNATURE "NTFS "
+
+#pragma pack(1)
+typedef struct tagNTFS_BPB
+{
+ // jump instruction
+ BYTE Jmp[3];
+
+ // signature
+ BYTE Signature[8];
+
+ // BPB and extended BPB
+ WORD BytesPerSector;
+ BYTE SectorsPerCluster;
+ WORD ReservedSectors;
+ BYTE Zeros1[3];
+ WORD NotUsed1;
+ BYTE MediaDescriptor;
+ WORD Zeros2;
+ WORD SectorsPerTrack;
+ WORD NumberOfHeads;
+ DWORD HiddenSectors;
+ DWORD NotUsed2;
+ DWORD NotUsed3;
+ ULONGLONG TotalSectors;
+ ULONGLONG LCN_MFT;
+ ULONGLONG LCN_MFTMirr;
+ DWORD ClustersPerFileRecord;
+ DWORD ClustersPerIndexBlock;
+ BYTE VolumeSN[8];
+
+ // boot code
+ BYTE Code[430];
+
+ //0xAA55
+ BYTE _AA;
+ BYTE _55;
+} NTFS_BPB;
+#pragma pack()
+
+
+// MFT Indexes
+#define MFT_IDX_MFT 0
+#define MFT_IDX_MFT_MIRR 1
+#define MFT_IDX_LOG_FILE 2
+#define MFT_IDX_VOLUME 3
+#define MFT_IDX_ATTR_DEF 4
+#define MFT_IDX_ROOT 5
+#define MFT_IDX_BITMAP 6
+#define MFT_IDX_BOOT 7
+#define MFT_IDX_BAD_CLUSTER 8
+#define MFT_IDX_SECURE 9
+#define MFT_IDX_UPCASE 10
+#define MFT_IDX_EXTEND 11
+#define MFT_IDX_RESERVED12 12
+#define MFT_IDX_RESERVED13 13
+#define MFT_IDX_RESERVED14 14
+#define MFT_IDX_RESERVED15 15
+#define MFT_IDX_USER 16
+
+
+/******************************
+ File Record
+ ---------------------
+ | File Record Header|
+ ---------------------
+ | Attribute 1 |
+ ---------------------
+ | Attribute 2 |
+ ---------------------
+ | ...... |
+ ---------------------
+ | 0xFFFFFFFF |
+ ---------------------
+*******************************/
+
+// File Record Header
+
+#define FILE_RECORD_MAGIC 'ELIF'
+#define FILE_RECORD_FLAG_INUSE 0x01 // File record is in use
+#define FILE_RECORD_FLAG_DIR 0x02 // File record is a directory
+
+typedef struct tagFILE_RECORD_HEADER
+{
+ DWORD Magic; // "FILE"
+ WORD OffsetOfUS; // Offset of Update Sequence
+ WORD SizeOfUS; // Size in words of Update Sequence Number & Array
+ ULONGLONG LSN; // $LogFile Sequence Number
+ WORD SeqNo; // Sequence number
+ WORD Hardlinks; // Hard link count
+ WORD OffsetOfAttr; // Offset of the first Attribute
+ WORD Flags; // Flags
+ DWORD RealSize; // Real size of the FILE record
+ DWORD AllocSize; // Allocated size of the FILE record
+ ULONGLONG RefToBase; // File reference to the base FILE record
+ WORD NextAttrId; // Next Attribute Id
+ WORD Align; // Align to 4 byte boundary
+ DWORD RecordNo; // Number of this MFT Record
+} FILE_RECORD_HEADER;
+
+
+/******************************
+ Attribute
+ --------------------
+ | Attribute Header |
+ --------------------
+ | Attribute Data |
+ --------------------
+*******************************/
+
+// Attribute Header
+
+#define ATTR_TYPE_STANDARD_INFORMATION 0x10
+#define ATTR_TYPE_ATTRIBUTE_LIST 0x20
+#define ATTR_TYPE_FILE_NAME 0x30
+#define ATTR_TYPE_OBJECT_ID 0x40
+#define ATTR_TYPE_SECURITY_DESCRIPTOR 0x50
+#define ATTR_TYPE_VOLUME_NAME 0x60
+#define ATTR_TYPE_VOLUME_INFORMATION 0x70
+#define ATTR_TYPE_DATA 0x80
+#define ATTR_TYPE_INDEX_ROOT 0x90
+#define ATTR_TYPE_INDEX_ALLOCATION 0xA0
+#define ATTR_TYPE_BITMAP 0xB0
+#define ATTR_TYPE_REPARSE_POINT 0xC0
+#define ATTR_TYPE_EA_INFORMATION 0xD0
+#define ATTR_TYPE_EA 0xE0
+#define ATTR_TYPE_LOGGED_UTILITY_STREAM 0x100
+
+#define ATTR_FLAG_COMPRESSED 0x0001
+#define ATTR_FLAG_ENCRYPTED 0x4000
+#define ATTR_FLAG_SPARSE 0x8000
+
+typedef struct tagATTR_HEADER_COMMON
+{
+ DWORD Type; // Attribute Type
+ DWORD TotalSize; // Length (including this header)
+ BYTE NonResident; // 0 - resident, 1 - non resident
+ BYTE NameLength; // name length in words
+ WORD NameOffset; // offset to the name
+ WORD Flags; // Flags
+ WORD Id; // Attribute Id
+} ATTR_HEADER_COMMON;
+
+typedef struct tagATTR_HEADER_RESIDENT
+{
+ ATTR_HEADER_COMMON Header; // Common data structure
+ DWORD AttrSize; // Length of the attribute body
+ WORD AttrOffset; // Offset to the Attribute
+ BYTE IndexedFlag; // Indexed flag
+ BYTE Padding; // Padding
+} ATTR_HEADER_RESIDENT;
+
+typedef struct tagATTR_HEADER_NON_RESIDENT
+{
+ ATTR_HEADER_COMMON Header; // Common data structure
+ ULONGLONG StartVCN; // Starting VCN
+ ULONGLONG LastVCN; // Last VCN
+ WORD DataRunOffset; // Offset to the Data Runs
+ WORD CompUnitSize; // Compression unit size
+ DWORD Padding; // Padding
+ ULONGLONG AllocSize; // Allocated size of the attribute
+ ULONGLONG RealSize; // Real size of the attribute
+ ULONGLONG IniSize; // Initialized data size of the stream
+} ATTR_HEADER_NON_RESIDENT;
+
+
+// Attribute: STANDARD_INFORMATION
+
+#define ATTR_STDINFO_PERMISSION_READONLY 0x00000001
+#define ATTR_STDINFO_PERMISSION_HIDDEN 0x00000002
+#define ATTR_STDINFO_PERMISSION_SYSTEM 0x00000004
+#define ATTR_STDINFO_PERMISSION_ARCHIVE 0x00000020
+#define ATTR_STDINFO_PERMISSION_DEVICE 0x00000040
+#define ATTR_STDINFO_PERMISSION_NORMAL 0x00000080
+#define ATTR_STDINFO_PERMISSION_TEMP 0x00000100
+#define ATTR_STDINFO_PERMISSION_SPARSE 0x00000200
+#define ATTR_STDINFO_PERMISSION_REPARSE 0x00000400
+#define ATTR_STDINFO_PERMISSION_COMPRESSED 0x00000800
+#define ATTR_STDINFO_PERMISSION_OFFLINE 0x00001000
+#define ATTR_STDINFO_PERMISSION_NCI 0x00002000
+#define ATTR_STDINFO_PERMISSION_ENCRYPTED 0x00004000
+
+typedef struct tagATTR_STANDARD_INFORMATION
+{
+ ULONGLONG CreateTime; // File creation time
+ ULONGLONG AlterTime; // File altered time
+ ULONGLONG MFTTime; // MFT changed time
+ ULONGLONG ReadTime; // File read time
+ DWORD Permission; // Dos file permission
+ DWORD MaxVersionNo; // Maxim number of file versions
+ DWORD VersionNo; // File version number
+ DWORD ClassId; // Class Id
+ DWORD OwnerId; // Owner Id
+ DWORD SecurityId; // Security Id
+ ULONGLONG QuotaCharged; // Quota charged
+ ULONGLONG USN; // USN Journel
+} ATTR_STANDARD_INFORMATION;
+
+
+// Attribute: ATTRIBUTE_LIST
+
+typedef struct tagATTR_ATTRIBUTE_LIST
+{
+ DWORD AttrType; // Attribute type
+ WORD RecordSize; // Record length
+ BYTE NameLength; // Name length in characters
+ BYTE NameOffset; // Name offset
+ ULONGLONG StartVCN; // Start VCN
+ ULONGLONG BaseRef; // Base file reference to the attribute
+ WORD AttrId; // Attribute Id
+} ATTR_ATTRIBUTE_LIST;
+
+// Attribute: FILE_NAME
+
+#define ATTR_FILENAME_FLAG_READONLY 0x00000001
+#define ATTR_FILENAME_FLAG_HIDDEN 0x00000002
+#define ATTR_FILENAME_FLAG_SYSTEM 0x00000004
+#define ATTR_FILENAME_FLAG_ARCHIVE 0x00000020
+#define ATTR_FILENAME_FLAG_DEVICE 0x00000040
+#define ATTR_FILENAME_FLAG_NORMAL 0x00000080
+#define ATTR_FILENAME_FLAG_TEMP 0x00000100
+#define ATTR_FILENAME_FLAG_SPARSE 0x00000200
+#define ATTR_FILENAME_FLAG_REPARSE 0x00000400
+#define ATTR_FILENAME_FLAG_COMPRESSED 0x00000800
+#define ATTR_FILENAME_FLAG_OFFLINE 0x00001000
+#define ATTR_FILENAME_FLAG_NCI 0x00002000
+#define ATTR_FILENAME_FLAG_ENCRYPTED 0x00004000
+#define ATTR_FILENAME_FLAG_DIRECTORY 0x10000000
+#define ATTR_FILENAME_FLAG_INDEXVIEW 0x20000000
+
+#define ATTR_FILENAME_NAMESPACE_POSIX 0x00
+#define ATTR_FILENAME_NAMESPACE_WIN32 0x01
+#define ATTR_FILENAME_NAMESPACE_DOS 0x02
+
+typedef struct tagATTR_FILE_NAME
+{
+ ULONGLONG ParentRef; // File reference to the parent directory
+ ULONGLONG CreateTime; // File creation time
+ ULONGLONG AlterTime; // File altered time
+ ULONGLONG MFTTime; // MFT changed time
+ ULONGLONG ReadTime; // File read time
+ ULONGLONG AllocSize; // Allocated size of the file
+ ULONGLONG RealSize; // Real size of the file
+ DWORD Flags; // Flags
+ DWORD ER; // Used by EAs and Reparse
+ BYTE NameLength; // Filename length in characters
+ BYTE NameSpace; // Filename space
+ WORD Name[1]; // Filename
+} ATTR_FILE_NAME;
+
+
+// Attribute: VOLUME_INFORMATION
+
+#define ATTR_VOLINFO_FLAG_DIRTY 0x0001 // Dirty
+#define ATTR_VOLINFO_FLAG_RLF 0x0002 // Resize logfile
+#define ATTR_VOLINFO_FLAG_UOM 0x0004 // Upgrade on mount
+#define ATTR_VOLINFO_FLAG_MONT 0x0008 // Mounted on NT4
+#define ATTR_VOLINFO_FLAG_DUSN 0x0010 // Delete USN underway
+#define ATTR_VOLINFO_FLAG_ROI 0x0020 // Repair object Ids
+#define ATTR_VOLINFO_FLAG_MBC 0x8000 // Modified by chkdsk
+
+typedef struct tagATTR_VOLUME_INFORMATION
+{
+ BYTE Reserved1[8]; // Always 0 ?
+ BYTE MajorVersion; // Major version
+ BYTE MinorVersion; // Minor version
+ WORD Flags; // Flags
+ BYTE Reserved2[4]; // Always 0 ?
+} ATTR_VOLUME_INFORMATION;
+
+
+// Attribute: INDEX_ROOT
+/******************************
+ INDEX_ROOT
+ ---------------------
+ | Index Root Header |
+ ---------------------
+ | Index Header |
+ ---------------------
+ | Index Entry |
+ ---------------------
+ | Index Entry |
+ ---------------------
+ | ...... |
+ ---------------------
+*******************************/
+
+#define ATTR_INDEXROOT_FLAG_SMALL 0x00 // Fits in Index Root File Record
+#define ATTR_INDEXROOT_FLAG_LARGE 0x01 // Index Allocation and Bitmap needed
+
+typedef struct tagATTR_INDEX_ROOT
+{
+ // Index Root Header
+ DWORD AttrType; // Attribute type (ATTR_TYPE_FILE_NAME: Directory, 0: Index View)
+ DWORD CollRule; // Collation rule
+ DWORD IBSize; // Size of index block
+ BYTE ClustersPerIB; // Clusters per index block (same as BPB?)
+ BYTE Padding1[3]; // Padding
+ // Index Header
+ DWORD EntryOffset; // Offset to the first index entry, relative to this address(0x10)
+ DWORD TotalEntrySize; // Total size of the index entries
+ DWORD AllocEntrySize; // Allocated size of the index entries
+ BYTE Flags; // Flags
+ BYTE Padding2[3]; // Padding
+} ATTR_INDEX_ROOT;
+
+
+// INDEX ENTRY
+
+#define INDEX_ENTRY_FLAG_SUBNODE 0x01 // Index entry points to a sub-node
+#define INDEX_ENTRY_FLAG_LAST 0x02 // Last index entry in the node, no Stream
+
+typedef struct tagINDEX_ENTRY
+{
+ ULONGLONG FileReference; // Low 6B: MFT record index, High 2B: MFT record sequence number
+ WORD Size; // Length of the index entry
+ WORD StreamSize; // Length of the stream
+ BYTE Flags; // Flags
+ BYTE Padding[3]; // Padding
+ BYTE Stream[1]; // Stream
+ // VCN of the sub node in Index Allocation, Offset = Size - 8
+} INDEX_ENTRY;
+
+
+// INDEX BLOCK
+/******************************
+ INDEX_BLOCK
+ -----------------------
+ | Index Block Header |
+ -----------------------
+ | Index Header |
+ -----------------------
+ | Index Entry |
+ -----------------------
+ | Index Entry |
+ -----------------------
+ | ...... |
+ -----------------------
+*******************************/
+
+#define INDEX_BLOCK_MAGIC 'XDNI'
+
+typedef struct tagINDEX_BLOCK
+{
+ // Index Block Header
+ DWORD Magic; // "INDX"
+ WORD OffsetOfUS; // Offset of Update Sequence
+ WORD SizeOfUS; // Size in words of Update Sequence Number & Array
+ ULONGLONG LSN; // $LogFile Sequence Number
+ ULONGLONG VCN; // VCN of this index block in the index allocation
+ // Index Header
+ DWORD EntryOffset; // Offset of the index entries, relative to this address(0x18)
+ DWORD TotalEntrySize; // Total size of the index entries
+ DWORD AllocEntrySize; // Allocated size of index entries
+ BYTE NotLeaf; // 1 if not leaf node (has children)
+ BYTE Padding[3]; // Padding
+} INDEX_BLOCK;
+
+#endif
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
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/ReadMe.txt b/Exfiltration/NTFSParser/NTFSParserDLL/ReadMe.txt
new file mode 100644
index 0000000..f0918b0
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/ReadMe.txt
@@ -0,0 +1,48 @@
+========================================================================
+ DYNAMIC LINK LIBRARY : NTFSParserDLL Project Overview
+========================================================================
+
+AppWizard has created this NTFSParserDLL DLL for you.
+
+This file contains a summary of what you will find in each of the files that
+make up your NTFSParserDLL application.
+
+
+NTFSParserDLL.vcxproj
+ This is the main project file for VC++ projects generated using an Application Wizard.
+ It contains information about the version of Visual C++ that generated the file, and
+ information about the platforms, configurations, and project features selected with the
+ Application Wizard.
+
+NTFSParserDLL.vcxproj.filters
+ This is the filters file for VC++ projects generated using an Application Wizard.
+ It contains information about the association between the files in your project
+ and the filters. This association is used in the IDE to show grouping of files with
+ similar extensions under a specific node (for e.g. ".cpp" files are associated with the
+ "Source Files" filter).
+
+NTFSParserDLL.cpp
+ This is the main DLL source file.
+
+ When created, this DLL does not export any symbols. As a result, it
+ will not produce a .lib file when it is built. If you wish this project
+ to be a project dependency of some other project, you will either need to
+ add code to export some symbols from the DLL so that an export library
+ will be produced, or you can set the Ignore Input Library property to Yes
+ on the General propert page of the Linker folder in the project's Property
+ Pages dialog box.
+
+/////////////////////////////////////////////////////////////////////////////
+Other standard files:
+
+StdAfx.h, StdAfx.cpp
+ These files are used to build a precompiled header (PCH) file
+ named NTFSParserDLL.pch and a precompiled types file named StdAfx.obj.
+
+/////////////////////////////////////////////////////////////////////////////
+Other notes:
+
+AppWizard uses "TODO:" comments to indicate parts of the source code you
+should add to or customize.
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/dllmain.cpp b/Exfiltration/NTFSParser/NTFSParserDLL/dllmain.cpp
new file mode 100644
index 0000000..c434a17
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/dllmain.cpp
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright(C) 2013 Joe Bialek Twitter:@JosephBialek
+ *
+ * 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.
+ */
+//
+// This code uses libraries released under GPLv2(or later) written by cyb70289 <cyb70289@gmail.com>
+
+// dllmain.cpp : Defines the entry point for the DLL application.
+#include "stdafx.h"
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/stdafx.cpp b/Exfiltration/NTFSParser/NTFSParserDLL/stdafx.cpp
new file mode 100644
index 0000000..2f18cb0
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// NTFSParserDLL.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/stdafx.h b/Exfiltration/NTFSParser/NTFSParserDLL/stdafx.h
new file mode 100644
index 0000000..a11216a
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/stdafx.h
@@ -0,0 +1,18 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include <windows.h>
+#include <string>
+#include <iostream>
+
+
+
+// TODO: reference additional headers your program requires here
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/targetver.h b/Exfiltration/NTFSParser/NTFSParserDLL/targetver.h
new file mode 100644
index 0000000..87c0086
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/targetver.h
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>