Created
December 13, 2020 00:11
-
-
Save juntalis/4a90ee024732b88539a65d92d0b6a296 to your computer and use it in GitHub Desktop.
Win32 hard link enumeration that should be compatible back to Windows XP.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file findlinks.c | |
* @brief Find links | |
*/ | |
#include "pch.h" | |
#include "ntfsdefs.h" | |
#define FILE_SHARE_READWRITE (FILE_SHARE_READ | FILE_SHARE_WRITE) | |
typedef struct _FILENAME_PART { | |
LPWSTR Data; | |
UCHAR DataLength; | |
struct _FILENAME_PART *Next; | |
struct _FILENAME_PART *Prev; | |
} FILENAME_PART; | |
typedef BOOL(*PROCESSFILERECORD)(IN PFILE_RECORD_HEADER lpFileRecordHeader, IN OPTIONAL LPVOID lpUserData); | |
typedef BOOL(*ENUMATTRIBUTEPROC)(IN PATTRIBUTE lpAttr, IN OPTIONAL LPVOID lpUserData, IN OUT LPBOOL lpbContinue); | |
BOOL ReadFileNamePartsProcessFileRecord(IN PFILE_RECORD_HEADER lpFileRecordHeader, IN OPTIONAL LPVOID lpUserData); | |
FILENAME_PART* AllocateNextPart(FILENAME_PART* lpCurrent) | |
{ | |
FILENAME_PART* lpNext = NULL; | |
if(lpNext = (FILENAME_PART*)malloc(sizeof(FILENAME_PART))) { | |
memset((void*)lpNext, 0, sizeof(FILENAME_PART)); | |
lpNext->Prev = lpCurrent; | |
} | |
return (lpCurrent->Next = lpNext); | |
} | |
BOOL CopyFileNamePart(IN OUT FILENAME_PART* lpPart, IN PFILENAME_ATTRIBUTE lpFileName) | |
{ | |
BOOL bResult = FALSE; | |
SIZE_T szBuffer = sizeof(WCHAR) * (lpFileName->NameLength + 1); | |
if(lpPart->Data = (LPWSTR)malloc(szBuffer)) { | |
lpPart->DataLength = lpFileName->NameLength; | |
memset((void*)lpPart->Data, 0, szBuffer); | |
wcsncpy(lpPart->Data, (const wchar_t*)lpFileName->Name, lpPart->DataLength); | |
bResult = TRUE; | |
} | |
return bResult; | |
} | |
BOOL EnumRecordAttributes(PFILE_RECORD_HEADER lpFileRecordHeader, IN ENUMATTRIBUTEPROC fnEnumAttrs, IN OPTIONAL LPVOID lpUserData) | |
{ | |
BOOL bSuccess = TRUE, bContinue = TRUE; | |
PATTRIBUTE lpAttr = (PATTRIBUTE)((LPBYTE)lpFileRecordHeader + lpFileRecordHeader->AttributesOffset); | |
// Loop through our attributes (unless our handler says to stop) | |
while(bContinue && lpAttr && lpAttr->AttributeType <= AttributeLoggedUtilityStream) { | |
// Process attribute | |
if(!(bSuccess = fnEnumAttrs(lpAttr, lpUserData, &bContinue))) | |
break; | |
// Figure out the current attribute size, then move on to the next. | |
if(lpAttr->Nonresident) { | |
lpAttr = (PATTRIBUTE)ADD2PTR(lpAttr, sizeof(NONRESIDENT_ATTRIBUTE)); | |
} else if(lpAttr->Length && lpAttr->Length < lpFileRecordHeader->BytesInUse) { | |
lpAttr = (PATTRIBUTE)ADD2PTR(lpAttr, lpAttr->Length); | |
} else { | |
lpAttr = NULL; | |
} | |
} | |
return bSuccess; | |
} | |
BOOL ProcessFileRecord(IN HANDLE hVolume, IN LARGE_INTEGER FileReferenceNumber, IN PROCESSFILERECORD fnProcessFileRecord, IN OPTIONAL LPVOID lpUserData) | |
{ | |
BOOL bSuccess = FALSE; | |
DWORD dwRead, dwBufferSize = FILE_RECORD_OUTPUT_BUFFER_SIZE; | |
C_ASSERT(sizeof(FileReferenceNumber) == sizeof(NTFS_FILE_RECORD_INPUT_BUFFER)); | |
NTFS_FILE_RECORD_OUTPUT_BUFFER* nrb = (NTFS_FILE_RECORD_OUTPUT_BUFFER*)malloc(dwBufferSize); | |
if(DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD, &FileReferenceNumber, sizeof(FileReferenceNumber), nrb, dwBufferSize, &dwRead, NULL)) { | |
// FSCTL_GET_NTFS_FILE_RECORD retrieves one MFT entry | |
// FILE_RECORD_HEADER is the Base struct for the MFT entry | |
// that we will work from | |
PFILE_RECORD_HEADER lpFileRecordHeader = (PFILE_RECORD_HEADER)nrb->FileRecordBuffer; | |
// Verify that the first four bytes are: FILE | |
if(lpFileRecordHeader->Ntfs.Type == 'ELIF') { | |
bSuccess = fnProcessFileRecord(lpFileRecordHeader, lpUserData); | |
} | |
} | |
return bSuccess; | |
} | |
typedef struct { | |
HANDLE hVolume; | |
FILENAME_PART* Tail; | |
} ReadFileNamePartsData; | |
typedef struct { | |
HANDLE hVolume; | |
USHORT LinkCount; | |
USHORT CurrentLink; | |
ReadFileNamePartsData* LinkData; | |
} ReadHardlinksData; | |
BOOL ReadFileNamePartsEnumAttributes(IN PATTRIBUTE lpAttr, IN OPTIONAL LPVOID lpUserData, IN OUT LPBOOL lpbContinue) | |
{ | |
BOOL bSuccess = TRUE; | |
ReadFileNamePartsData* lpData = (ReadFileNamePartsData*)lpUserData; | |
*lpbContinue = TRUE; | |
if(lpAttr->AttributeType == AttributeFileName) { | |
PRESIDENT_ATTRIBUTE lpResident = (PRESIDENT_ATTRIBUTE)lpAttr; | |
PFILENAME_ATTRIBUTE lpFileName = (PFILENAME_ATTRIBUTE)ADD2PTR(lpAttr, lpResident->ValueOffset); | |
if(lpFileName->NameType & WIN32_NAME || lpFileName->NameType == 0) { | |
FILENAME_PART* lpNext; | |
bSuccess = FALSE; | |
if((lpNext = AllocateNextPart(lpData->Tail)) && CopyFileNamePart(lpNext, lpFileName)) { | |
lpData->Tail = lpNext; | |
if(lpFileName->DirectoryFileReferenceNumber == 0x5 || lpFileName->NameType == 3) { | |
bSuccess = TRUE; | |
} else { | |
bSuccess = ProcessFileRecord(lpData->hVolume, *((LARGE_INTEGER*)&lpFileName->DirectoryFileReferenceNumber), ReadFileNamePartsProcessFileRecord, lpUserData); | |
} | |
} | |
*lpbContinue = FALSE; | |
} | |
} | |
return bSuccess; | |
} | |
BOOL ReadFileNamePartsProcessFileRecord(IN PFILE_RECORD_HEADER lpFileRecordHeader, IN OPTIONAL LPVOID lpUserData) | |
{ | |
return EnumRecordAttributes(lpFileRecordHeader, ReadFileNamePartsEnumAttributes, lpUserData); | |
} | |
BOOL ReadHardlinksEnumAttributes(IN PATTRIBUTE lpAttr, IN OPTIONAL LPVOID lpUserData, IN OUT LPBOOL lpbContinue) | |
{ | |
BOOL bSuccess = TRUE; | |
ReadHardlinksData* lpData = (ReadHardlinksData*)lpUserData; | |
*lpbContinue = TRUE; | |
if(lpAttr->AttributeType == AttributeFileName) { | |
ReadFileNamePartsData* lpReadData; | |
PRESIDENT_ATTRIBUTE lpResident = (PRESIDENT_ATTRIBUTE)lpAttr; | |
PFILENAME_ATTRIBUTE lpFileName = (PFILENAME_ATTRIBUTE)ADD2PTR(lpAttr, lpResident->ValueOffset); | |
if(lpFileName->NameType & WIN32_NAME || lpFileName->NameType == 0) { | |
bSuccess = FALSE; | |
lpReadData = &(lpData->LinkData[lpData->CurrentLink++]); | |
if(lpReadData->Tail = (FILENAME_PART*)malloc(sizeof(FILENAME_PART))) { | |
memset((void*)lpReadData->Tail, 0, sizeof(FILENAME_PART)); | |
if(CopyFileNamePart(lpReadData->Tail, lpFileName)) { | |
lpReadData->hVolume = lpData->hVolume; | |
bSuccess = ProcessFileRecord(lpData->hVolume, *((LARGE_INTEGER*)&lpFileName->DirectoryFileReferenceNumber), ReadFileNamePartsProcessFileRecord, (LPVOID)lpReadData); | |
} | |
} | |
} | |
} | |
return bSuccess; | |
} | |
BOOL ReadHardlinksProcessFileRecord(IN PFILE_RECORD_HEADER lpFileRecordHeader, IN OPTIONAL LPVOID lpUserData) | |
{ | |
BOOL bSuccess = FALSE; | |
ReadHardlinksData* lpData = (ReadHardlinksData*)lpUserData; | |
size_t szDataSize = sizeof(ReadFileNamePartsData) * lpFileRecordHeader->LinkCount; | |
lpData->LinkCount = lpFileRecordHeader->LinkCount; | |
if(lpData->LinkData = (ReadFileNamePartsData*)malloc(szDataSize)) { | |
lpData->CurrentLink = 0; | |
memset((void*)lpData->LinkData, 0, szDataSize); | |
if(!(bSuccess = EnumRecordAttributes(lpFileRecordHeader, ReadHardlinksEnumAttributes, lpUserData))) { | |
//free((void*)lpData->LinkData); | |
//lpData->LinkData = NULL; | |
} | |
} | |
return bSuccess; | |
} | |
VOID ReadHardLinks(IN HANDLE hVolume, IN LARGE_INTEGER FileRef) | |
{ | |
ReadHardlinksData rhlData = { hVolume }; | |
if(ProcessFileRecord(hVolume, FileRef, ReadHardlinksProcessFileRecord, (LPVOID)&rhlData)) { | |
USHORT i = 0; | |
for(; i < rhlData.LinkCount; i++) { | |
wprintf(L"%u/%u", i, rhlData.LinkCount); | |
ReadFileNamePartsData pRFNPD = rhlData.LinkData[i]; | |
FILENAME_PART* lpCurrent = pRFNPD.Tail; | |
while(lpCurrent->Prev) { | |
FILENAME_PART* lpLast = lpCurrent; | |
if(!lpLast) break; | |
wprintf(L"%s\\", lpCurrent->Data); | |
lpCurrent = lpCurrent->Prev; | |
free((void*)lpLast->Data); | |
free((void*)lpLast); | |
if(!lpCurrent->Prev) { | |
wprintf(L"%s\n", lpCurrent->Data); | |
} | |
} | |
free((void*)lpCurrent->Data); | |
free((void*)lpCurrent); | |
} | |
free(rhlData.LinkData); | |
} else { | |
wprintf(L"ReadHardLinks failed!\n"); | |
} | |
} | |
VOID ReadHardLinkParts(IN HANDLE hVolume, IN LARGE_INTEGER FileRef) | |
{ | |
DWORD dwRead, dwBufferSize = FILE_RECORD_OUTPUT_BUFFER_SIZE; | |
C_ASSERT(sizeof(FileRef) == sizeof(NTFS_FILE_RECORD_INPUT_BUFFER)); | |
NTFS_FILE_RECORD_OUTPUT_BUFFER* nrb = (NTFS_FILE_RECORD_OUTPUT_BUFFER*)malloc(dwBufferSize); | |
if(DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD, &FileRef, sizeof(FileRef), nrb, dwBufferSize, &dwRead, NULL)) { | |
// FSCTL_GET_NTFS_FILE_RECORD retrieves one MFT entry | |
// FILE_RECORD_HEADER is the Base struct for the MFT entry | |
// that we will work from | |
PFILE_RECORD_HEADER lpFileRecordHeader = (PFILE_RECORD_HEADER)nrb->FileRecordBuffer; | |
if(lpFileRecordHeader->Ntfs.Type == 'ELIF') { | |
PATTRIBUTE lpAttr = (PATTRIBUTE)((LPBYTE)lpFileRecordHeader + lpFileRecordHeader->AttributesOffset); | |
while(lpAttr && lpAttr->AttributeType <= AttributeLoggedUtilityStream) { | |
// Only process $FILE_NAME attributes | |
if(lpAttr->AttributeType == AttributeFileName) { | |
PRESIDENT_ATTRIBUTE lpResident = (PRESIDENT_ATTRIBUTE)lpAttr; | |
PFILENAME_ATTRIBUTE lpFileName = (PFILENAME_ATTRIBUTE)ADD2PTR(lpAttr, lpResident->ValueOffset); | |
if(lpFileName->NameType & WIN32_NAME || lpFileName->NameType == 0) { | |
// Reached the root | |
if(lpFileName->DirectoryFileReferenceNumber == 0x5) { | |
wprintf(L"Hit Root\n"); | |
} | |
lpFileName->Name[lpFileName->NameLength] = L'\0'; | |
wprintf(L"FileName :%s\n", lpFileName->Name) ; | |
} | |
} | |
if(lpAttr->Nonresident) { | |
lpAttr = (PATTRIBUTE)ADD2PTR(lpAttr, sizeof(NONRESIDENT_ATTRIBUTE)); | |
} else if(lpAttr->Length && lpAttr->Length < lpFileRecordHeader->BytesInUse) { | |
lpAttr = (PATTRIBUTE)ADD2PTR(lpAttr, lpAttr->Length); | |
} else { | |
lpAttr = NULL; | |
} | |
} | |
} | |
} | |
free(nrb); | |
} | |
// Open the volume | |
HANDLE OpenVolume(WCHAR wcVolume) | |
{ | |
static HANDLE hVolume = NULL; | |
static WCHAR lpsVolumeRoot[] = L"\\\\.\\A:"; | |
// Uppercase volume letters. | |
if(iswlower((wint_t)wcVolume)) { | |
wcVolume = towupper((wint_t)wcVolume); | |
} | |
if(VALID_HANDLE(hVolume)) { | |
if(wcVolume == lpsVolumeRoot[4]) | |
return hVolume; | |
CloseHandle(hVolume); | |
} | |
lpsVolumeRoot[4] = wcVolume; | |
hVolume = CreateFileW(lpsVolumeRoot, GENERIC_READ, FILE_SHARE_READWRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |
return VALID_HANDLE(hVolume) ? hVolume : NULL; | |
} | |
HANDLE OpenVolumeOfFilePath(IN LPCWSTR lpsFilePath, OUT WCHAR* lpwcVolume) | |
{ | |
HANDLE hVolume = NULL; | |
WCHAR lpVolumePath[MAX_PATH + 1] = W_EMPTY; | |
*lpwcVolume = L'\0'; | |
if(GetVolumePathName(lpsFilePath, lpVolumePath, WLEN(lpVolumePath))) { | |
hVolume = OpenVolume(*lpwcVolume = *lpVolumePath); | |
} | |
return hVolume; | |
} | |
int __cdecl _tmain(int argc, TCHAR* argv[]) | |
{ | |
WCHAR wcVolume; | |
LPCWSTR lpFileName; | |
HANDLE hFile, hVolume; | |
LARGE_INTEGER FileRef; | |
BY_HANDLE_FILE_INFORMATION fiItem; | |
DWORD dwFileAttrs, dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; | |
// Check command-line arguments. | |
if(argc == 1) { | |
_tprintf(_T("Usage: %s <filepath>\n"), argv[0]); | |
return 0; | |
} | |
// Determine the flags we'll use for opening our filepath (based on whether or not lpFileName | |
// contains a folder path) | |
lpFileName = (LPCWSTR)argv[1]; | |
dwFileAttrs = GetFileAttributesW(lpFileName); | |
if(HAS_FLAG(dwFileAttrs, INVALID_FILE_ATTRIBUTES)) { | |
FATAL_ERROR(ERROR_PATH_NOT_FOUND, _T("%s does not exist."), lpFileName); | |
} else if(HAS_FLAG(dwFileAttrs, FILE_ATTRIBUTE_DIRECTORY)) { | |
dwFlagsAndAttributes |= FILE_FLAG_BACKUP_SEMANTICS; | |
} | |
hFile = CreateFile(lpFileName, GENERIC_READ, FILE_SHARE_READWRITE, NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL); | |
ENSURE_SUCCESS(VALID_HANDLE(hFile)); | |
GetFileInformationByHandle(hFile, &fiItem); | |
CloseHandle(hFile); | |
if(!(hVolume = OpenVolumeOfFilePath(lpFileName, &wcVolume))) { | |
FATAL_API(CreateFile); | |
} | |
FileRef.LowPart = fiItem.nFileIndexLow; | |
FileRef.HighPart = fiItem.nFileIndexHigh; | |
ReadHardLinks(hVolume, FileRef); | |
CloseHandle(hVolume); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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