Skip to content

Instantly share code, notes, and snippets.

@juntalis
Created December 13, 2020 00:11
Show Gist options
  • Save juntalis/4a90ee024732b88539a65d92d0b6a296 to your computer and use it in GitHub Desktop.
Save juntalis/4a90ee024732b88539a65d92d0b6a296 to your computer and use it in GitHub Desktop.
Win32 hard link enumeration that should be compatible back to Windows XP.
/**
* @file ntfsdefs.h
*
* TODO: Description
*/
#ifndef _NTFSDEFS_H_
#define _NTFSDEFS_H_
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// See NTFS_VOLUME_DATA_BUFFER::BytesPerFileRecordSegment
#define BytesPerFileRecordSegment 1024
#define FILE_RECORD_OUTPUT_BUFFER_SIZE (FIELD_OFFSET(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer) + BytesPerFileRecordSegment)
// These types should be stored in separate
// include file, not done here
typedef struct {
ULONG Type;
USHORT UsaOffset;
USHORT UsaCount;
USN Usn;// LONGLONG
} NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER;
typedef struct {
NTFS_RECORD_HEADER Ntfs;
USHORT SequenceNumber;
USHORT LinkCount;
USHORT AttributesOffset;
USHORT Flags; // 0x0001 = InUse, 0x0002 = Directory
ULONG BytesInUse;
ULONG BytesAllocated;
ULONGLONG BaseFileRecord;
USHORT NextAttributeNumber;
} FILE_RECORD_HEADER, *PFILE_RECORD_HEADER;
typedef enum ATTRIBUTE_TYPE {
AttributeInvalid = 0x00, /* Not defined by Windows */
AttributeStandardInformation = 0x10,
AttributeAttributeList = 0x20,
AttributeFileName = 0x30,
AttributeObjectId = 0x40,
AttributeSecurityDescriptor = 0x50,
AttributeVolumeName = 0x60,
AttributeVolumeInformation = 0x70,
AttributeData = 0x80,
AttributeIndexRoot = 0x90,
AttributeIndexAllocation = 0xA0,
AttributeBitmap = 0xB0,
AttributeReparsePoint = 0xC0, /* Reparse Point = Symbolic link */
AttributeEAInformation = 0xD0,
AttributeEA = 0xE0,
AttributePropertySet = 0xF0,
AttributeLoggedUtilityStream = 0x100
} ATTRIBUTE_TYPE;
typedef struct {
ATTRIBUTE_TYPE AttributeType;
ULONG Length;
BOOLEAN Nonresident;
UCHAR NameLength;
USHORT NameOffset;
USHORT Flags; // 0x0001 = Compressed
USHORT AttributeNumber;
} ATTRIBUTE, *PATTRIBUTE;
typedef struct {
ATTRIBUTE Attribute;
ULONG ValueLength;
USHORT ValueOffset;
USHORT Flags; // 0x0001 = Indexed
} RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE;
typedef struct {
ATTRIBUTE Attribute;
ULONGLONG LowVcn;
ULONGLONG HighVcn;
USHORT RunArrayOffset;
UCHAR CompressionUnit;
UCHAR AlignmentOrReserved[5];
ULONGLONG AllocatedSize;
ULONGLONG DataSize;
ULONGLONG InitializedSize;
ULONGLONG CompressedSize; // Only when compressed
} NONRESIDENT_ATTRIBUTE, *PNONRESIDENT_ATTRIBUTE;
typedef struct {
ULONGLONG CreationTime;
ULONGLONG ChangeTime;
ULONGLONG LastWriteTime;
ULONGLONG LastAccessTime;
ULONG FileAttributes;
ULONG AlignmentOrReservedOrUnknown[3];
ULONG QuotaId; // NTFS 3.0 only
ULONG SecurityId; // NTFS 3.0 only
ULONGLONG QuotaCharge; // NTFS 3.0 only
USN Usn; // NTFS 3.0 only
} STANDARD_INFORMATION, *PSTANDARD_INFORMATION;
typedef struct {
ATTRIBUTE_TYPE AttributeType;
USHORT Length;
UCHAR NameLength;
UCHAR NameOffset;
ULONGLONG LowVcn;
ULONGLONG FileReferenceNumber;
USHORT AttributeNumber;
USHORT AlignmentOrReserved[3];
} ATTRIBUTE_LIST, *PATTRIBUTE_LIST;
typedef struct {
ULONGLONG DirectoryFileReferenceNumber;
ULONGLONG CreationTime; // Saved when filename last changed
ULONGLONG ChangeTime; // ditto
ULONGLONG LastWriteTime; // ditto
ULONGLONG LastAccessTime; // ditto
ULONGLONG AllocatedSize; // ditto
ULONGLONG DataSize; // ditto
ULONG FileAttributes; // ditto
ULONG AlignmentOrReserved;
UCHAR NameLength;
UCHAR NameType; // 0x01 = Long, 0x02 = Short
WCHAR Name[1];
} FILENAME_ATTRIBUTE, *PFILENAME_ATTRIBUTE;
typedef struct {
GUID ObjectId;
union {
struct {
GUID BirthVolumeId;
GUID BirthObjectId;
GUID DomainId;
} ;
UCHAR ExtendedInfo[48];
};
} OBJECTID_ATTRIBUTE, *POBJECTID_ATTRIBUTE;
typedef struct {
//GUID ObjectId;
ULONGLONG DirectoryFileReferenceNumber;
BOOLEAN IsDirectory;
ULONGLONG Size;
ULONGLONG CreationTime;
ULONGLONG LastWriteTime;
ULONGLONG LastAccessTime;
LPWSTR Name;
} MFT_FILE_INFO, *PMFT_FILE_INFO;
#define WIN32_NAME 1
typedef DWORD (__stdcall *pNtCreateFile)(
PHANDLE hFile,
DWORD dwAccess,
PVOID lpObjectAttrs,
PVOID lpIoStatusBlock,
PLARGE_INTEGER AllocationSize,
DWORD dwAttributes,
DWORD dwShareAccess,
DWORD dwCreateDisposition,
DWORD dwCreateOptions,
PVOID lpEaBuffer,
DWORD dwEaLength
);
typedef DWORD (__stdcall *pNtReadFile)(
IN HANDLE hFile,
IN HANDLE hEvent,
IN PVOID lpApcRoutine,
IN PVOID lpApcContext,
OUT PVOID lpIoStatusBlock,
OUT PVOID lpBuffer,
IN DWORD dwLength,
IN PLARGE_INTEGER lpqByteOffset,
IN PULONG lpdKey
);
typedef struct _UNICODE_STRING {
USHORT Length, MaximumLength;
PWCH Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
#define InitializeObjectAttributes( p, n, a, r, s ) { \
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
(p)->RootDirectory = r; \
(p)->Attributes = a; \
(p)->ObjectName = n; \
(p)->SecurityDescriptor = s; \
(p)->SecurityQualityOfService = NULL; \
}
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define FILE_NON_DIRECTORY_FILE 0x00000040
#define FILE_OPEN_BY_FILE_ID 0x00002000
#define FILE_OPEN 0x00000001
#ifdef __cplusplus
}
#endif
#endif /* _NTFSDEFS_H_ */
/**
* @file pch.h
*
* TODO: Description
*/
#ifndef _PCH_H_
#define _PCH_H_
#pragma once
#ifndef _MSC_VER
#error "Currently only supports compilation by MSVC"
#endif
/** Char Type Config */
#ifndef _UNICODE
# define _UNICODE 1
#endif
#ifndef UNICODE
# define UNICODE 1
#endif
/** Macro used to combine two things. */
#define XPASTE2(A,B) A##B
#define XPASTE(A,B) XPASTE2(A,B)
/** Stringifying macros */
#define XWIDE(X) XPASTE2(L,X)
#define XSTR2(X) #X
#define XSTRA(X) XSTR2(X)
#define XSTRW(X) XWIDE(XSTRA(X))
/** Windows Version */
#if !defined(WINVER)
# define WINVER 0x0501
#elif (WINVER < 0x0501)
# error Windows XP is currently the lowest version of Windows supported by this project.
#endif
#if !defined(_WIN32_WINNT)
# define _WIN32_WINNT 0x0501
#elif (_WIN32_WINNT < 0x0501)
# error Windows XP is currently the lowest version of Windows supported by this project.
#endif
#if !defined(NTDDI_VERSION)
# define NTDDI_VERSION 0x05010000
#elif (NTDDI_VERSION < 0x05010000)
# error Windows XP is currently the lowest version of Windows supported by this project.
#endif
/** Speed up build process with minimal headers. */
#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
// Reduce the headers further
#define VC_EXTRALEAN
/** Compiler/Architecture Detection */
#if defined(_MSC_VER)
# define BUILD_MSVC
#if (defined(_M_IA64) || defined(_M_AMD64) || defined(_WIN64))
# define BUILD_X64
#elif defined(_M_IX86)
# define BUILD_X86
#endif
// Insecure function usage warnings
#ifndef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS
#endif
// Depreciation warnings
#pragma warning(disable:4996)
// SDK Version Definitions
#include <SDKDDKVer.h>
#endif
/** If we can't detect the architecture, error out */
#if !defined(BUILD_X86) && !defined(BUILD_X64)
# error Could not detect platform architecture.
#endif
/** System Includes */
#include <tchar.h>
#include <windows.h>
#include <winioctl.h>
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <assert.h>
/** Utility Macros */
#define ARRAYCOUNT(ARRAY) \
(sizeof(ARRAY) / sizeof(*(ARRAY)))
#define ADD2PTR(PTR,OFFSET) \
(((PUCHAR)(PTR)) + (OFFSET))
#define HAS_FLAG(dwSubject,dwFlag) \
(((dwSubject) & (dwFlag)) == (dwFlag))
#define VALID_HANDLE(HANDLE) \
((HANDLE) && ((HANDLE) != INVALID_HANDLE_VALUE))
#define INVALID_HANDLE(HANDLE) \
(!VALID_HANDLE(HANDLE))
// Wchar Utilities
#define W_EMPTY L""
#define WLEN(WSTRING) (sizeof(WSTRING) / sizeof(WCHAR))
#define ZWLEN(WSTRING) (WLEN(WSTRING) - 1)
#define WSIZE(COUNT) ((COUNT) * sizeof(WCHAR))
#define ZWSIZE(COUNT) WSIZE((COUNT) + 1)
/** Normalize the code style */
#ifndef CONST
# define CONST const
#endif
#ifndef STATIC
# define STATIC static
#endif
#define VOID void
#ifndef INLINE
# define INLINE __forceinline
#endif
#define NOINLINE __declspec(noinline)
#define NORETURN void __declspec(noreturn)
#define TSIZE(COUNT) ((COUNT) * sizeof(TCHAR))
// Dumb error handling inlined
STATIC INLINE NORETURN FatalStatement(char* prefix, char* statement)
{
fprintf(stderr, "%s: FATAL:%s\n", prefix, statement);
ExitProcess(1);
}
#define ENSURE_SUCCESS(statement) \
if(!(statement)) {\
FatalStatement("[" __FILE__ ":" XSTRA(__LINE__) "]", XSTRA(statement)); \
}
STATIC INLINE TCHAR* vtcsprintf_alloc(const TCHAR* lpMessage, va_list lpArgs)
{
TCHAR *lpResult;
size_t szDisplayBuf;
// Check the resulting size of the buffer.
szDisplayBuf = (size_t)_vsctprintf(lpMessage, lpArgs) + 1;
// Allocate our buffer.
if(!(lpResult = (TCHAR*)calloc(szDisplayBuf, sizeof(TCHAR)))) {
return NULL;
}
// Finally, fill in the message.
_vsntprintf(lpResult, szDisplayBuf, lpMessage, lpArgs);
return lpResult;
}
STATIC INLINE TCHAR* tcsprintf_alloc(const TCHAR* lpMessage, ...)
{
va_list lpArgs = NULL;
TCHAR* lpDisplayBuf = NULL;
// Allocate buffer for our resulting format string.
va_start(lpArgs, lpMessage);
lpDisplayBuf = vtcsprintf_alloc(lpMessage, lpArgs);
va_end(lpArgs);
}
STATIC INLINE ErrorMessage(DWORD dwError, LPCTSTR lpMessage, ...)
{
va_list lpArgs = NULL;
TCHAR* lpMsgBuf = NULL, *lpDisplayBuf = NULL, *lpFormatted = NULL;
size_t szErrLen, szFormatted, szMessage;
szErrLen = (szMessage = _tcslen(lpMessage));
if(dwError == ERROR_SUCCESS) dwError = GetLastError();
if(dwError != ERROR_SUCCESS) {
const TCHAR *lpErrMsg = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (TCHAR*)&lpErrMsg, 0, NULL
);
szErrLen += _tcslen(lpErrMsg) + 39;
// Allocate our buffer for the error message.
ENSURE_SUCCESS(lpMsgBuf = (TCHAR*)calloc(szErrLen + 1, sizeof(TCHAR)));
memset(lpMsgBuf, 0, TSIZE(szErrLen + 1));
_sntprintf(lpMsgBuf, szErrLen, _T("ERROR: %s - Windows Error [0x%08X]: %s\n"), lpMessage, dwError, lpErrMsg);
LocalFree((HLOCAL)lpErrMsg);
} else {
szErrLen += 8;
ENSURE_SUCCESS(lpMsgBuf = (TCHAR*)calloc(szErrLen + 1, sizeof(TCHAR)));
memset(lpMsgBuf, 0, TSIZE(szErrLen));
_sntprintf(lpMsgBuf, szErrLen, _T("ERROR: %s\n"), lpMessage);
}
va_start(lpArgs, lpMessage);
if(szMessage != (szFormatted = _vsctprintf(lpMessage, lpArgs))) {
ENSURE_SUCCESS(lpDisplayBuf = vtcsprintf_alloc(lpMsgBuf, lpArgs));
free(lpMsgBuf);
} else {
lpDisplayBuf = lpMsgBuf;
}
va_end(lpArgs);
_ftprintf(stderr, (LPCTSTR)lpDisplayBuf);
free((void*)lpDisplayBuf);
}
#define FATAL_ERROR(dwError,...) { \
ErrorMessage(dwError, __VA_ARGS__); \
ExitProcess(dwError); \
}
#define FATAL_API(fnFailed) FATAL_ERROR(ERROR_SUCCESS, XSTRW(fnFailed))
#endif /* _PCH_H_ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment