diff options
author | clymb3r <bialek.joseph@gmail.com> | 2013-10-01 09:47:05 -0700 |
---|---|---|
committer | clymb3r <bialek.joseph@gmail.com> | 2013-10-01 09:47:05 -0700 |
commit | 59cd18360764af6e6133ad11ec9cd8295372e587 (patch) | |
tree | 758a4f12cd6d2bddb0006df7d1fcac3736b61b8f /Exfiltration/NTFSParser/NTFSParserDLL | |
parent | b17272eb98933c62baa5a21bcd23713f9182ee38 (diff) | |
download | PowerSploit-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.h | 28 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.cpp | 161 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.vcxproj | 172 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/NTFSParserDLL.vcxproj.filters | 39 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h | 1663 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Common.h | 317 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/NTFS_DataType.h | 380 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/NTFS_FileRecord.h | 989 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/ReadMe.txt | 48 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/dllmain.cpp | 36 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/stdafx.cpp | 8 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/stdafx.h | 18 | ||||
-rw-r--r-- | Exfiltration/NTFSParser/NTFSParserDLL/targetver.h | 8 |
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> |