1881 lines
69 KiB
C++
1881 lines
69 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
struct FILE_NETWORK_OPEN_INFORMATION
|
|
{
|
|
LARGE_INTEGER CreationTime;
|
|
LARGE_INTEGER LastAccessTime;
|
|
LARGE_INTEGER LastWriteTime;
|
|
LARGE_INTEGER ChangeTime;
|
|
LARGE_INTEGER AllocationSize;
|
|
LARGE_INTEGER EndOfFile;
|
|
ULONG FileAttributes;
|
|
};
|
|
|
|
bool IsContentWrite(u32 desiredAccess, u32 createDisposition)
|
|
{
|
|
if (desiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE))
|
|
return true;
|
|
if (createDisposition == FILE_CREATE || createDisposition == FILE_OVERWRITE || createDisposition == FILE_OVERWRITE_IF)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool IsContentRead(u32 desiredAccess, u32 createDisposition)
|
|
{
|
|
return (desiredAccess & (GENERIC_READ | FILE_READ_DATA)) != 0;
|
|
}
|
|
|
|
bool IsContentUse(u32 desiredAccess, u32 createDisposition)
|
|
{
|
|
return IsContentRead(desiredAccess, createDisposition) || IsContentWrite(desiredAccess, createDisposition);
|
|
}
|
|
|
|
bool IsWrite(u32 desiredAccess, u32 createDisposition)
|
|
{
|
|
return IsContentWrite(desiredAccess, createDisposition) || (desiredAccess & (FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA)) != 0;
|
|
}
|
|
|
|
u8 GetFileAccessFlags(DWORD desiredAccess, u32 createDisposition)
|
|
{
|
|
u8 access = 0;
|
|
if (IsContentRead(desiredAccess, createDisposition))
|
|
access |= AccessFlag_Read;
|
|
if (IsWrite(desiredAccess, createDisposition))
|
|
access |= AccessFlag_Write;
|
|
return access;
|
|
}
|
|
|
|
#if UBA_DEBUG_LOG_ENABLED
|
|
StringBuffer<32> ToString(NTSTATUS s)
|
|
{
|
|
StringBuffer<32> res;
|
|
if (NT_SUCCESS(s))
|
|
return res.Append(L"Success");
|
|
if (s == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
return res.Append(L"STATUS_OBJECT_NAME_NOT_FOUND");
|
|
if (s == STATUS_OBJECT_PATH_NOT_FOUND)
|
|
return res.Append(L"STATUS_OBJECT_PATH_NOT_FOUND");
|
|
if (s == STATUS_INVALID_HANDLE)
|
|
return res.Append(L"STATUS_INVALID_HANDLE");
|
|
if (s == STATUS_SHARING_VIOLATION)
|
|
return res.Append(L"STATUS_SHARING_VIOLATION");
|
|
if (s == STATUS_ACCESS_DENIED)
|
|
return res.Append(L"STATUS_ACCESS_DENIED");
|
|
return res.Appendf(L"Error (0x%x)", u32(s));
|
|
}
|
|
#endif
|
|
|
|
struct FILE_FS_DEVICE_INFORMATION
|
|
{
|
|
DEVICE_TYPE DeviceType;
|
|
ULONG Characteristics;
|
|
};
|
|
|
|
struct FILE_FS_ATTRIBUTE_INFORMATION
|
|
{
|
|
ULONG FileSystemAttributes;
|
|
LONG MaximumComponentNameLength;
|
|
ULONG FileSystemNameLength;
|
|
WCHAR FileSystemName[1];
|
|
};
|
|
|
|
NTSTATUS Detoured_NtQueryVolumeInformationFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FsInformation, ULONG Length, FS_INFORMATION_CLASS FsInformationClass)
|
|
{
|
|
DETOURED_CALL(NtQueryVolumeInformationFile);
|
|
HANDLE TrueHandle = FileHandle;
|
|
if (isDetouredHandle(FileHandle))
|
|
{
|
|
auto& dh = asDetouredHandle(FileHandle);
|
|
if (dh.fileObject->fileInfo->memoryFile)
|
|
{
|
|
if (FsInformationClass == 4) // FileFsDeviceInformation
|
|
{
|
|
auto& info = *(FILE_FS_DEVICE_INFORMATION*)FsInformation;
|
|
info.DeviceType = FILE_DEVICE_FILE_SYSTEM;
|
|
info.Characteristics = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
TrueHandle = dh.trueHandle;
|
|
if (TrueHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
if (FsInformationClass == 1) // FileFsVolumeInformation
|
|
{
|
|
// TODO This code path is here to handle nodejs queries..
|
|
|
|
auto& info = *(FILE_FS_VOLUME_INFORMATION*)FsInformation;
|
|
UBA_ASSERT(dh.dirTableOffset != ~0u);
|
|
DirectoryTable::EntryInformation entryInfo;
|
|
g_directoryTable.GetEntryInformation(entryInfo, dh.dirTableOffset);
|
|
UBA_ASSERT(entryInfo.attributes != 0);
|
|
info.VolumeCreationTime.QuadPart = 0;
|
|
info.VolumeSerialNumber = entryInfo.volumeSerial;
|
|
info.VolumeLabelLength = 0;
|
|
info.SupportsObjects = false;
|
|
info.VolumeLabel[0] = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
UBA_ASSERTF(false, L"NtQueryVolumeInformationFile using class %u not handled %ls (%ls)", FsInformationClass, dh.fileObject->fileInfo->name, dh.fileObject->fileInfo->originalName);
|
|
}
|
|
}
|
|
else if (isListDirectoryHandle(FileHandle))
|
|
{
|
|
if (FsInformationClass == 4) // FileFsDeviceInformation
|
|
{
|
|
auto& info = *(FILE_FS_DEVICE_INFORMATION*)FsInformation;
|
|
info.DeviceType = FILE_DEVICE_FILE_SYSTEM;
|
|
info.Characteristics = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
UBA_ASSERTF(false, L"NtQueryVolumeInformationFile called in ListDirectoryHandle using class %u which is not implemented (%ls)", FsInformationClass, HandleToName(FileHandle));
|
|
}
|
|
auto res = True_NtQueryVolumeInformationFile(TrueHandle, IoStatusBlock, FsInformation, Length, FsInformationClass);
|
|
DEBUG_LOG_TRUE(L"NtQueryVolumeInformationFile", L"%llu (%ls) -> %ls", uintptr_t(FileHandle), HandleToName(FileHandle), ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
NTSTATUS Detoured_NtQueryInformationFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass)
|
|
{
|
|
DETOURED_CALL(NtQueryInformationFile);
|
|
if (isListDirectoryHandle(FileHandle))
|
|
{
|
|
auto& listHandle = asListDirectoryHandle(FileHandle);
|
|
if (FileInformationClass == 51) // FileIsRemoteDeviceInformation
|
|
{
|
|
auto& info = *(FILE_IS_REMOTE_DEVICE_INFORMATION*)FileInformation;
|
|
info.IsRemote = FALSE;
|
|
DEBUG_LOG_DETOURED(L"NtQueryInformationFile", L"(FileIsRemoteDeviceInformation) %llu (%ls) -> Success", uintptr_t(FileHandle), HandleToName(FileHandle));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (FileInformationClass == 59) // FileIdInformation
|
|
{
|
|
auto& info = *(FILE_ID_INFORMATION*)FileInformation;
|
|
if (listHandle.dir.tableOffset != InvalidTableOffset)
|
|
{
|
|
u32 entryOffset = listHandle.dir.tableOffset | 0x80000000;
|
|
DirectoryTable::EntryInformation entryInfo;
|
|
g_directoryTable.GetEntryInformation(entryInfo, entryOffset);
|
|
info.VolumeSerialNumber = entryInfo.volumeSerial;
|
|
u64* id = (u64*)&info.FileId;
|
|
id[0] = 0;
|
|
id[1] = entryInfo.fileIndex;
|
|
}
|
|
else
|
|
{
|
|
UBA_ASSERT(false);
|
|
info.VolumeSerialNumber = 0;//attr.volumeSerial;
|
|
memcpy(info.FileId.Identifier, &listHandle.dirNameKey, 16);
|
|
}
|
|
DEBUG_LOG_DETOURED(L"NtQueryInformationFile", L"(FileIdInformation) %llu (%ls) -> Success", uintptr_t(FileHandle), HandleToName(FileHandle));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
/*
|
|
else if (FileInformationClass == 9) // FileNameInformation
|
|
{
|
|
auto& info = *(FILE_NAME_INFORMATION*)FileInformation;
|
|
u32 nameLen = u32(wcslen(listHandle.name));
|
|
//UBA_ASSERT(info.FileNameLength/2 > nameLen);
|
|
memcpy(info.FileName, listHandle.name, nameLen*2+2);
|
|
info.FileNameLength = nameLen*2;
|
|
UBA_ASSERT(false);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (FileInformationClass == 55) // Undefined, some old compilers using this it seems
|
|
{
|
|
DEBUG_LOG_DETOURED(L"NtQueryInformationFile", L"TODO_THIS (55) %llu (%ls) -> Error", uintptr_t(FileHandle), HandleToName(FileHandle));
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
*/
|
|
else
|
|
{
|
|
FatalError(1348, L"NtQueryInformationFile with class %u not implemented", FileInformationClass);
|
|
}
|
|
}
|
|
|
|
HANDLE TrueHandle = FileHandle;
|
|
if (isDetouredHandle(FileHandle))
|
|
{
|
|
auto& dh = asDetouredHandle(FileHandle);
|
|
TrueHandle = dh.trueHandle;
|
|
|
|
if (TrueHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
if (FileInformationClass == 18) // FileAllInformation
|
|
{
|
|
UBA_ASSERT(dh.dirTableOffset != ~0u);
|
|
DirectoryTable::EntryInformation entryInfo;
|
|
g_directoryTable.GetEntryInformation(entryInfo, dh.dirTableOffset);
|
|
UBA_ASSERT(entryInfo.attributes != 0);
|
|
|
|
// TODO This code path is here to handle nodejs queries.. Is not properly implemented and miss things
|
|
auto& info = *(FILE_ALL_INFORMATION*)FileInformation;
|
|
info.BasicInformation.CreationTime.QuadPart = entryInfo.lastWrite;
|
|
info.BasicInformation.LastAccessTime.QuadPart = entryInfo.lastWrite;
|
|
info.BasicInformation.LastWriteTime.QuadPart = entryInfo.lastWrite;
|
|
info.BasicInformation.ChangeTime.QuadPart = entryInfo.lastWrite;
|
|
info.BasicInformation.FileAttributes = entryInfo.attributes;
|
|
info.StandardInformation.AllocationSize.QuadPart = entryInfo.size;
|
|
info.StandardInformation.EndOfFile.QuadPart = entryInfo.size;
|
|
info.StandardInformation.NumberOfLinks = 0;
|
|
info.StandardInformation.DeletePending = false;
|
|
info.StandardInformation.Directory = false;
|
|
info.InternalInformation.IndexNumber.QuadPart = entryInfo.fileIndex;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
if (FileInformationClass == 34) // FileNetworkOpenInformation
|
|
{
|
|
UBA_ASSERT(dh.dirTableOffset != ~0u);
|
|
DirectoryTable::EntryInformation entryInfo;
|
|
g_directoryTable.GetEntryInformation(entryInfo, dh.dirTableOffset);
|
|
UBA_ASSERT(entryInfo.attributes != 0);
|
|
|
|
// TODO This code path is here to handle nodejs queries.. Is not properly implemented and miss things
|
|
auto& info = *(FILE_NETWORK_OPEN_INFORMATION*)FileInformation;
|
|
info.CreationTime.QuadPart = entryInfo.lastWrite;
|
|
info.LastAccessTime.QuadPart = entryInfo.lastWrite;
|
|
info.LastWriteTime.QuadPart = entryInfo.lastWrite;
|
|
info.ChangeTime.QuadPart = entryInfo.lastWrite;
|
|
info.AllocationSize.QuadPart = entryInfo.size;
|
|
u64 fileSize = dh.fileObject->fileInfo->size;
|
|
if (fileSize == InvalidValue)
|
|
fileSize = entryInfo.size;
|
|
info.EndOfFile.QuadPart = fileSize;
|
|
info.FileAttributes = entryInfo.attributes;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//if (FileInformationClass == 9) // FileNameInformation
|
|
//{
|
|
// const wchar_t* name = dh.fileObject->fileInfo->originalName;
|
|
// auto& info = *(FILE_NAME_INFORMATION*)FileInformation;
|
|
// info.FileName[0] = '\\';
|
|
// info.FileNameLength = 2;
|
|
// //return STATUS_SUCCESS;
|
|
// u32 nameLen = u32(wcslen(name));
|
|
// //UBA_ASSERT(info.FileNameLength/2 > nameLen);
|
|
// //memcpy(info.FileName, name, nameLen*2+2);
|
|
// //info.FileNameLength = nameLen;
|
|
// memcpy(info.FileName, L"\\\\", 4);
|
|
// info.FileNameLength = 2;
|
|
// return STATUS_SUCCESS;
|
|
//}
|
|
UBA_ASSERTF(false, L"NtQueryInformationFile (%u) failed using detoured handle %ls (%ls)", FileInformationClass, dh.fileObject->fileInfo->name, dh.fileObject->fileInfo->originalName);
|
|
}
|
|
}
|
|
|
|
TimerScope ts(g_kernelStats.getFileInfo);
|
|
auto res = True_NtQueryInformationFile(TrueHandle, IoStatusBlock, FileInformation, Length, FileInformationClass);
|
|
DEBUG_LOG_TRUE(L"NtQueryInformationFile", L"(%u) %llu (%ls) -> %ls", FileInformationClass, uintptr_t(FileHandle), HandleToName(FileHandle), ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtQueryDirectoryFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length,
|
|
FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, PUNICODE_STRING FileName, BOOLEAN RestartScan)
|
|
{
|
|
DETOURED_CALL(NtQueryDirectoryFile);
|
|
|
|
if (isListDirectoryHandle(FileHandle))
|
|
{
|
|
IoStatusBlock->Information = 0;
|
|
|
|
auto& listHandle = asListDirectoryHandle(FileHandle);
|
|
NTSTATUS res = STATUS_NO_MORE_FILES;
|
|
|
|
UBA_ASSERT(Event == 0 && ApcRoutine == nullptr && ApcContext == nullptr);
|
|
|
|
if (RestartScan)
|
|
listHandle.it = 0;
|
|
|
|
u8* prevInformation = nullptr;
|
|
u8* it = (u8*)FileInformation;
|
|
u8* bufferEnd = it + Length;
|
|
|
|
while (true)
|
|
{
|
|
if (listHandle.it == listHandle.fileTableOffsets.size())
|
|
break;
|
|
|
|
u32 fileOffset = listHandle.fileTableOffsets[listHandle.it++];
|
|
|
|
DirectoryTable::EntryInformation entryInfo;
|
|
wchar_t fileName[512];
|
|
g_directoryTable.GetEntryInformation(entryInfo, fileOffset, fileName, sizeof_array(fileName));
|
|
if (entryInfo.attributes == 0) // File was deleted
|
|
continue;
|
|
|
|
if (FileName && wcsncmp(FileName->Buffer, fileName, FileName->Length / 2) != 0)
|
|
continue;
|
|
|
|
u32 fileNameBytes = u32(wcslen(fileName) * 2);
|
|
|
|
wchar_t* fileNamePos = nullptr;
|
|
u32 structSize = 0;
|
|
if (FileInformationClass == FileDirectoryInformation)
|
|
{
|
|
structSize = sizeof(FILE_DIRECTORY_INFORMATION);
|
|
fileNamePos = ((FILE_DIRECTORY_INFORMATION*)it)->FileName;
|
|
}
|
|
else if (FileInformationClass == 2)//FileFullDirectoryInformation)
|
|
{
|
|
structSize = sizeof(FILE_FULL_DIR_INFORMATION);
|
|
fileNamePos = ((FILE_FULL_DIR_INFORMATION*)it)->FileName;
|
|
}
|
|
else
|
|
{
|
|
UBA_ASSERT(false);
|
|
IoStatusBlock->Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
u8* writeEnd = (u8*)fileNamePos + fileNameBytes;
|
|
if (writeEnd > bufferEnd)
|
|
{
|
|
--listHandle.it;
|
|
if (!prevInformation)
|
|
res = STATUS_BUFFER_OVERFLOW;
|
|
break;
|
|
}
|
|
|
|
memset(it, 0, structSize);
|
|
auto& info = *(FILE_DIRECTORY_INFORMATION*)it;
|
|
|
|
memcpy(fileNamePos, fileName, fileNameBytes);
|
|
|
|
info.FileNameLength = fileNameBytes;
|
|
info.FileAttributes = entryInfo.attributes;
|
|
info.LastWriteTime.QuadPart = entryInfo.lastWrite;
|
|
info.EndOfFile.QuadPart = entryInfo.size;
|
|
//info.FileIndex = entryInfo.fileIndex; // This needs serialno too?
|
|
info.AllocationSize.QuadPart = entryInfo.size;
|
|
info.CreationTime.QuadPart = entryInfo.lastWrite;
|
|
|
|
if (prevInformation)
|
|
{
|
|
((FILE_DIRECTORY_INFORMATION*)prevInformation)->NextEntryOffset = u32(it - prevInformation);
|
|
}
|
|
|
|
prevInformation = it;
|
|
it = (u8*)fileNamePos + info.FileNameLength + 2;
|
|
|
|
DEBUG_LOG_DETOURED(L"NtQueryDirectoryFile", L"%llu %ls", u64(FileHandle), fileNamePos);
|
|
|
|
res = STATUS_SUCCESS;
|
|
|
|
if (ReturnSingleEntry)
|
|
break;
|
|
}
|
|
|
|
IoStatusBlock->Status = res;
|
|
IoStatusBlock->Information = it - (u8*)FileInformation;
|
|
|
|
#if 0//UBA_DEBUG_VALIDATE
|
|
if (false) // Sorting can mismatch
|
|
{
|
|
u8 info2Mem[1024];
|
|
UBA_ASSERT(Length <= sizeof(info2Mem));
|
|
auto& info2 = *(FILE_DIRECTORY_INFORMATION*)info2Mem;
|
|
NTSTATUS res2;
|
|
do
|
|
{
|
|
res2 = True_NtQueryDirectoryFile(listHandle.validateHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, &info2, Length, FileInformationClass, ReturnSingleEntry, FileName, RestartScan);
|
|
if (res2 >= 0)
|
|
{
|
|
info2.FileName[info2.FileNameLength / 2] = 0;
|
|
ToLower(info2.FileName);
|
|
}
|
|
} while (wcscmp(info2.FileName, L".") == 0 || wcscmp(info2.FileName, L"..") == 0);
|
|
UBA_ASSERT(res < 0 && res2 < 0 || res >= 0 && res2 >= 0);
|
|
UBA_ASSERT(res < 0 || wcscmp(info.FileName, info2.FileName) == 0);
|
|
}
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
HANDLE trueHandle = FileHandle;
|
|
if (isDetouredHandle(FileHandle))
|
|
{
|
|
DetouredHandle& h = asDetouredHandle(FileHandle);
|
|
trueHandle = h.trueHandle;
|
|
UBA_ASSERTF(trueHandle != INVALID_HANDLE_VALUE, L"NtQueryDirectoryFile for using class %u not implemented for detoured handles (%ls)", FileInformationClass, HandleToName(FileHandle));
|
|
}
|
|
|
|
NTSTATUS res = True_NtQueryDirectoryFile(trueHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, FileInformation, Length, FileInformationClass, ReturnSingleEntry, FileName, RestartScan);
|
|
|
|
#if UBA_DEBUG_LOG_ENABLED
|
|
if (res == STATUS_SUCCESS)
|
|
{
|
|
u8* it = (u8*)FileInformation;
|
|
while (true)
|
|
{
|
|
const wchar_t* fileNamePos;
|
|
if (FileInformationClass == FileDirectoryInformation)
|
|
fileNamePos = ((FILE_DIRECTORY_INFORMATION*)it)->FileName;
|
|
else if (FileInformationClass == 2)//FileFullDirectoryInformation)
|
|
fileNamePos = ((FILE_FULL_DIR_INFORMATION*)it)->FileName;
|
|
else
|
|
break;
|
|
StringBuffer<> b;
|
|
b.Append(fileNamePos, ((FILE_DIRECTORY_INFORMATION*)it)->FileNameLength / 2);
|
|
DEBUG_LOG_TRUE(L"NtQueryDirectoryFile", L"%llu %ls", u64(FileHandle), b.data);
|
|
|
|
u32 nextOffset = ((FILE_DIRECTORY_INFORMATION*)it)->NextEntryOffset;
|
|
if (!nextOffset)
|
|
break;
|
|
it += nextOffset;
|
|
//DEBUG_LOG_TRUE(L"NtQueryDirectoryFile", L"(%u) %llu (%ls) -> %ls", FileInformationClass, uintptr_t(FileHandle), HandleToName(FileHandle), ToString(res));
|
|
}
|
|
//DEBUG_LOG_TRUE(L"NtQueryDirectoryFile", L"(%u) %llu (%ls) -> %ls", FileInformationClass, uintptr_t(FileHandle), HandleToName(FileHandle), ToString(res));
|
|
}
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
NTSTATUS Detoured_NtQueryFullAttributesFile(POBJECT_ATTRIBUTES ObjectAttributes, PVOID Attributes)
|
|
{
|
|
auto fileName = (const wchar_t*)ObjectAttributes->ObjectName->Buffer;
|
|
u32 fileNameLen = ObjectAttributes->ObjectName->Length/sizeof(tchar);(void)fileNameLen;
|
|
UBA_ASSERT(fileName[fileNameLen] == 0);
|
|
|
|
if (!CanDetour(fileName) || Contains(fileName, L"::")) // Some weird .net path used by dotnet.exe ... ignore for now!
|
|
{
|
|
TimerScope ts(g_kernelStats.getFileInfo);
|
|
auto res = True_NtQueryFullAttributesFile(ObjectAttributes, Attributes);
|
|
DEBUG_LOG_TRUE(L"NtQueryFullAttributesFile", L"(%.*s) -> %s", fileNameLen, fileName, ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
|
|
StringBuffer<MaxPath> fixedName;
|
|
FixPath(fixedName, fileName);
|
|
/*
|
|
if (fixedName.StartsWith(g_systemTemp.data))
|
|
{
|
|
auto res = True_NtQueryFullAttributesFile(ObjectAttributes, Attributes);
|
|
DEBUG_LOG_TRUE(L"NtQueryFullAttributesFile", L"(%.*s) -> %s", fileNameLen, fileName, ToString(res).data);
|
|
return res;
|
|
}
|
|
*/
|
|
|
|
DevirtualizePath(fixedName);
|
|
|
|
FileAttributes attr;
|
|
Shared_GetFileAttributes(attr, fixedName);
|
|
|
|
if (!attr.useCache)
|
|
{
|
|
auto res = True_NtQueryFullAttributesFile(ObjectAttributes, Attributes);
|
|
DEBUG_LOG_TRUE(L"NtQueryFullAttributesFile", L"(%.*s) -> %s", fileNameLen, fileName, ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
UBA_ASSERT(!ObjectAttributes->RootDirectory);
|
|
|
|
NTSTATUS res = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
if (attr.exists && attr.lastError == ErrorSuccess)
|
|
{
|
|
WIN32_FILE_ATTRIBUTE_DATA& data = attr.data;
|
|
res = STATUS_SUCCESS;
|
|
auto& info = *(FILE_NETWORK_OPEN_INFORMATION*)Attributes;;
|
|
info.CreationTime = ToLargeInteger(data.ftCreationTime.dwHighDateTime, data.ftCreationTime.dwLowDateTime);
|
|
info.LastAccessTime = ToLargeInteger(data.ftLastAccessTime.dwHighDateTime, data.ftLastAccessTime.dwLowDateTime);
|
|
info.LastWriteTime = ToLargeInteger(data.ftLastWriteTime.dwHighDateTime, data.ftLastWriteTime.dwLowDateTime);
|
|
info.ChangeTime = ToLargeInteger(data.ftLastWriteTime.dwHighDateTime, data.ftLastWriteTime.dwLowDateTime);
|
|
info.AllocationSize = ToLargeInteger(data.nFileSizeHigh, data.nFileSizeLow);
|
|
info.EndOfFile = info.AllocationSize;
|
|
info.FileAttributes = data.dwFileAttributes;
|
|
}
|
|
|
|
DEBUG_LOG_DETOURED(L"NtQueryFullAttributesFile", L"(%.*s) -> %s (Size: %llu)", fileNameLen, fileName, ToString(res).data, ToLargeInteger(attr.data.nFileSizeHigh, attr.data.nFileSizeLow).QuadPart);
|
|
return res;
|
|
}
|
|
|
|
NTSTATUS Detoured_NtSetInformationFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass)
|
|
{
|
|
DETOURED_CALL(NtSetInformationFile);
|
|
|
|
|
|
u8 tempBuffer[sizeof(FILE_RENAME_INFORMATION) + 512];
|
|
|
|
HANDLE trueHandle = FileHandle;
|
|
if (isDetouredHandle(FileHandle))
|
|
{
|
|
DetouredHandle& h = asDetouredHandle(FileHandle);
|
|
|
|
if (FileInformationClass == 10) // FileRenameInformation
|
|
{
|
|
// We can end up in here through MoveFileEx
|
|
auto& info = *(FILE_RENAME_INFORMATION*)FileInformation;
|
|
const wchar_t* newNamePtr = t_renameFileNewName;
|
|
StringBuffer<> newNameTemp;
|
|
if (!newNamePtr)
|
|
{
|
|
newNameTemp.Append(info.FileName, info.FileNameLength / 2);
|
|
newNamePtr = newNameTemp.data;
|
|
}
|
|
if (StartsWith(newNamePtr, L"\\??\\"))
|
|
newNamePtr += 4;
|
|
StringBuffer<> newName;
|
|
FixPath(newName, newNamePtr);
|
|
DevirtualizePath(newName);
|
|
FileObject& fo = *h.fileObject;
|
|
fo.newName = newName.data;
|
|
|
|
StringKey newFileNameKey = ToStringKeyLower(newName);
|
|
|
|
// TODO: Revisit this.. don't know what could go wrong with this
|
|
#if 0
|
|
FileInfo* newFileInfo = nullptr;
|
|
{
|
|
SCOPED_READ_LOCK(g_mappedFileTable.m_lookupLock, _);
|
|
auto findIt = g_mappedFileTable.m_lookup.find(newFileNameKey);
|
|
if (findIt != g_mappedFileTable.m_lookup.end())
|
|
newFileInfo = &findIt->second;
|
|
}
|
|
|
|
UBA_ASSERTF(newFileInfo, L"File info already exists for the rename we are doing from %ls to %ls", fo.fileInfo->originalName, newName.data); // TODO: Implement when we find a test case
|
|
#endif
|
|
|
|
if (!fo.closeId)
|
|
{
|
|
wchar_t temp[1024];
|
|
u64 size;
|
|
StringBuffer<> fixedPath;
|
|
FixPath(fixedPath, newName.data);
|
|
Rpc_CreateFileW(fixedPath, newFileNameKey, AccessFlag_Write, temp, sizeof_array(temp), size, fo.closeId, true);
|
|
}
|
|
DEBUG_LOG_DETOURED(L"NtSetInformationFile", L"File is set to be renamed on close (from %ls to %ls)", HandleToName(FileHandle), fo.newName.c_str());
|
|
|
|
if (auto memoryFile = fo.fileInfo->memoryFile)
|
|
{
|
|
memoryFile->isReported = false;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
UBA_ASSERT(!fo.fileInfo->isFileMap);
|
|
|
|
#if 0
|
|
// This is for the odd rename logic in clang.. foo.so -> foo.so123123.tmp
|
|
// Clang first create a tmp file, to see if it exists..
|
|
// It then renames the old file to that file..
|
|
// ..and finally opens it again with attributes only and deleteonclose flag.. and closes it.
|
|
// TODO: This is wrong.. we just delete the file.. but what we should do is copy it over in memory to the memory file before setting it to delete
|
|
SCOPED_READ_LOCK(g_mappedFileTable.m_lookupLock, _);
|
|
auto findIt = g_mappedFileTable.m_lookup.find(newFileNameKey);
|
|
if (findIt != g_mappedFileTable.m_lookup.end())
|
|
{
|
|
FileInfo& info = findIt->second;
|
|
if (info.memoryFile)
|
|
{
|
|
Rpc_WriteLogf(L"HNNMMMM");
|
|
FILE_DISPOSITION_INFO info;
|
|
info.DeleteFileW = true;
|
|
True_SetFileInformationByHandle(FileHandle, FileDispositionInfo, &info, sizeof(info));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (g_runningRemote) // This needs a proper solution as the comments above.
|
|
return STATUS_SUCCESS;
|
|
|
|
// In case we are using vfs we need to replace the information before calling the true NtSetInformationFile
|
|
auto& info2 = *(FILE_RENAME_INFORMATION*)tempBuffer;
|
|
memcpy(&info2, &info, sizeof(FILE_RENAME_INFORMATION));
|
|
memcpy(info2.FileName, TC("\\??\\"), 8);
|
|
memcpy(info2.FileName + 4, newName.data, newName.count * 2);
|
|
info2.FileNameLength = (newName.count+4) * 2;
|
|
|
|
FileInformation = &info2;
|
|
Length = sizeof(FILE_RENAME_INFORMATION) + info2.FileNameLength + 2;
|
|
}
|
|
|
|
trueHandle = h.trueHandle;
|
|
if (trueHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
//UBA_ASSERT(!g_runningRemote);
|
|
// TODO: This needs to be sent back to Session.. so session can set whatever needs to be set.
|
|
DEBUG_LOG_DETOURED(L"NtSetInformationFile", L"(%u) SKIPPED!!!!!!!!! %llu (%ls) -> Skipped", FileInformationClass, uintptr_t(FileHandle), HandleToName(FileHandle));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
TimerScope ts(g_kernelStats.setFileInfo);
|
|
auto res = True_NtSetInformationFile(trueHandle, IoStatusBlock, FileInformation, Length, FileInformationClass);
|
|
DEBUG_LOG_TRUE(L"NtSetInformationFile", L"(%u) %llu (%ls) -> %ls", FileInformationClass, uintptr_t(FileHandle), HandleToName(FileHandle), ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtSetInformationObject(HANDLE ObjectHandle, OBJECT_INFORMATION_CLASS ObjectInformationClass, PVOID ObjectInformation, ULONG Length)
|
|
{
|
|
if (isDetouredHandle(ObjectHandle))
|
|
{
|
|
DetouredHandle& h = asDetouredHandle(ObjectHandle);
|
|
ObjectHandle = h.trueHandle;
|
|
UBA_ASSERT(ObjectHandle != INVALID_HANDLE_VALUE);
|
|
}
|
|
auto res = True_NtSetInformationObject(ObjectHandle, ObjectInformationClass, ObjectInformation, Length);
|
|
DEBUG_LOG_TRUE(L"NtSetInformationObject", L"(%u) %llu (%ls) -> %ls", ObjectInformationClass, uintptr_t(ObjectHandle), HandleToName(ObjectHandle), ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtCreateSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PLARGE_INTEGER MaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle)
|
|
{
|
|
DETOURED_CALL(NtCreateSection);
|
|
if (isDetouredHandle(FileHandle))
|
|
{
|
|
DetouredHandle& h = asDetouredHandle(FileHandle);
|
|
FileHandle = h.trueHandle;
|
|
UBA_ASSERT(FileHandle != INVALID_HANDLE_VALUE);
|
|
}
|
|
return True_NtCreateSection(SectionHandle, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle);
|
|
}
|
|
|
|
bool g_checkRtlHeap = true;
|
|
|
|
SIZE_T Detoured_RtlSizeHeap(HANDLE HeapPtr, ULONG Flags, PVOID Ptr)
|
|
{
|
|
#if UBA_USE_MIMALLOC
|
|
if (g_checkRtlHeap && IsInMiMalloc(Ptr))
|
|
return mi_usable_size(Ptr);
|
|
#endif
|
|
return True_RtlSizeHeap(HeapPtr, Flags, Ptr);
|
|
}
|
|
|
|
BOOLEAN Detoured_RtlFreeHeap(PVOID HeapHandle, ULONG Flags, PVOID BaseAddress)
|
|
{
|
|
#if UBA_USE_MIMALLOC
|
|
if (g_checkRtlHeap && IsInMiMalloc(BaseAddress))
|
|
{
|
|
mi_free(BaseAddress);
|
|
return true;
|
|
}
|
|
#endif
|
|
return True_RtlFreeHeap(HeapHandle, Flags, BaseAddress);
|
|
}
|
|
|
|
NTSTATUS Detoured_RtlAnsiStringToUnicodeString(PUNICODE_STRING DestinationString, PCANSI_STRING SourceString, BOOLEAN AllocateDestinationString)
|
|
{
|
|
#if UBA_USE_MIMALLOC
|
|
if (AllocateDestinationString && g_useMiMalloc)
|
|
{
|
|
DestinationString->MaximumLength = SourceString->MaximumLength * 2;
|
|
DestinationString->Buffer = (wchar_t*)mi_malloc(DestinationString->MaximumLength);
|
|
AllocateDestinationString = false;
|
|
}
|
|
#endif
|
|
auto res = True_RtlAnsiStringToUnicodeString(DestinationString, SourceString, AllocateDestinationString);
|
|
return res;
|
|
}
|
|
|
|
NTSTATUS Detoured_RtlUnicodeStringToAnsiString(PANSI_STRING DestinationString, PCUNICODE_STRING SourceString, BOOLEAN AllocateDestinationString)
|
|
{
|
|
#if UBA_USE_MIMALLOC
|
|
if (AllocateDestinationString && g_useMiMalloc)
|
|
{
|
|
DestinationString->MaximumLength = SourceString->MaximumLength / 2;
|
|
DestinationString->Buffer = (char*)mi_malloc(DestinationString->MaximumLength);
|
|
AllocateDestinationString = false;
|
|
}
|
|
#endif
|
|
return True_RtlUnicodeStringToAnsiString(DestinationString, SourceString, AllocateDestinationString);
|
|
}
|
|
|
|
NTSTATUS NTAPI Local_NtCreateFile(bool IsCreateFunc, PHANDLE hFileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes,
|
|
ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength)
|
|
{
|
|
#if 0
|
|
if (IsContentWrite(DesiredAccess, CreateDisposition))
|
|
{
|
|
StringBuffer<> b;
|
|
b.Append(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / 2);
|
|
if (!b.Contains(L"\\Device\\") && !b.EndsWith(L"\\nul"))
|
|
Rpc_WriteLogf(L"[%ls] WRITTEN: %ls", g_rulesIndex ? GetApplicationRules()[g_rulesIndex].app : wcsrchr(g_virtualApplication.data, '\\') + 1, ObjectAttributes->ObjectName->Buffer);
|
|
}
|
|
#endif
|
|
|
|
TimerScope ts(g_kernelStats.createFile);
|
|
|
|
constexpr u32 retryCount = 15;
|
|
u32 retriesLeft = retryCount;
|
|
while (true)
|
|
{
|
|
//if (!Contains(ObjectAttributes->ObjectName->Buffer, L".dll") && !Contains(ObjectAttributes->ObjectName->Buffer, L".mui"))
|
|
//Rpc_WriteLogf(L"NtCreateFile: %ls", ObjectAttributes->ObjectName->Buffer);
|
|
auto res = True_NtCreateFile(hFileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
|
|
|
|
UBA_ASSERTF(res != STATUS_SUCCESS || u64(*hFileHandle) < DetouredHandleStart - 10000, L"Normal handle range is closing in on detoured. Bump detour range (normal: %llu, detour start: %llu)", u64(*hFileHandle), DetouredHandleStart);
|
|
|
|
// I have no idea why we get this sometimes when trying to open pch for read after recently being written..
|
|
// All scenarios I've seen this succeeds after 1 second.
|
|
// Only theory I have is some antivirus or something.
|
|
if (res == STATUS_SHARING_VIOLATION)
|
|
{
|
|
if (!--retriesLeft)
|
|
return res;
|
|
|
|
StringView fileName(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / 2);
|
|
if (!fileName.EndsWith(TCV(".pch")))
|
|
return res;
|
|
|
|
#if UBA_DEBUG
|
|
StringBuffer<> b;
|
|
b.Appendf(L"Got access denied trying to open %.*s. Retrying in one second", ObjectAttributes->ObjectName->Length / 2, ObjectAttributes->ObjectName->Buffer);
|
|
Rpc_WriteLog(b.data, b.count, true, false);
|
|
#endif
|
|
Sleep(1000);
|
|
continue;
|
|
}
|
|
|
|
#if UBA_DEBUG
|
|
if (retriesLeft != retryCount)
|
|
{
|
|
StringBuffer<> b;
|
|
b.Appendf(L"SUCCEEDED to open %.*s after %u retries.", ObjectAttributes->ObjectName->Length / 2, ObjectAttributes->ObjectName->Buffer, retryCount - retriesLeft);
|
|
Rpc_WriteLog(b.data, b.count, true, true);
|
|
}
|
|
#endif
|
|
|
|
return res;
|
|
}
|
|
}
|
|
|
|
NTSTATUS NTAPI Shared_NtCreateFile(bool IsCreateFunc, PHANDLE hFileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes,
|
|
ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength)
|
|
{
|
|
*hFileHandle = INVALID_HANDLE_VALUE;
|
|
|
|
#if UBA_DEBUG_LOG_ENABLED
|
|
const wchar_t* funcName = IsCreateFunc ? L"NtCreateFile" : L"NtOpenFile"; (void)funcName;
|
|
#endif
|
|
|
|
const wchar_t* createFileName = t_createFileFileName;
|
|
|
|
// NOTE - ObjectAttributes->ObjectName->Buffer might not be null terminated, so we need to copy over to another buffer
|
|
StringBuffer<> fileName;
|
|
bool suppressCreateFileDetour = t_disallowCreateFileDetour || t_disallowDetour;
|
|
HANDLE rootDir = ObjectAttributes->RootDirectory;
|
|
{
|
|
const wchar_t* buf = ObjectAttributes->ObjectName->Buffer;
|
|
u32 bufChars = ObjectAttributes->ObjectName->Length / 2;
|
|
|
|
if (suppressCreateFileDetour)
|
|
{
|
|
}
|
|
else if (!buf)
|
|
{
|
|
suppressCreateFileDetour = true;
|
|
}
|
|
else if ((bufChars >= 7u && wcsncmp(buf, L"\\Device", 7u) == 0) || (bufChars >= 10u && wcsncmp(buf, L"\\Global??\\", 10u) == 0)) // \Global is for FilterConnectCommunicationPort and friends
|
|
{
|
|
suppressCreateFileDetour = true;
|
|
}
|
|
else if (createFileName)
|
|
{
|
|
if (!CanDetour(createFileName))
|
|
suppressCreateFileDetour = true;
|
|
else if (!FixPath(fileName, createFileName))
|
|
UBA_ASSERTF(false, L"FixPath failed for string '%ls'", createFileName);
|
|
|
|
// TODO: Instead of using t_createFileFileName.. should we just resolve the path from ObjectAttributes->RootDirectory?
|
|
ObjectAttributes->RootDirectory = nullptr;
|
|
}
|
|
else if (bufChars >= 8 && memcmp(buf, L"\\??\\", 8) == 0)
|
|
{
|
|
if (!CanDetour(buf))
|
|
suppressCreateFileDetour = true;
|
|
else if (!FixPath(fileName, buf))
|
|
UBA_ASSERTF(false, L"FixPath failed for string '%ls'", buf + 4);
|
|
}
|
|
else
|
|
{
|
|
if (ObjectAttributes->RootDirectory)
|
|
{
|
|
if (isDetouredHandle(ObjectAttributes->RootDirectory))
|
|
{
|
|
auto& dh = asDetouredHandle(ObjectAttributes->RootDirectory);
|
|
fileName.Append(dh.fileObject->fileInfo->originalName).EnsureEndsWithSlash().Append(buf, bufChars);
|
|
rootDir = dh.trueHandle;
|
|
ObjectAttributes->RootDirectory = nullptr;
|
|
}
|
|
else if (isListDirectoryHandle(ObjectAttributes->RootDirectory))
|
|
{
|
|
auto& lh = asListDirectoryHandle(ObjectAttributes->RootDirectory);
|
|
fileName.Append(lh.originalName).EnsureEndsWithSlash().Append(buf, bufChars);
|
|
ObjectAttributes->RootDirectory = nullptr;
|
|
}
|
|
else
|
|
{
|
|
// TODO: Revisit!
|
|
//UBA_ASSERT(false);
|
|
suppressCreateFileDetour = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fileName.Append(buf, bufChars);
|
|
if (fileName.StartsWith(L"\\DosDevices")) // Something used in msbuild..
|
|
suppressCreateFileDetour = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!suppressCreateFileDetour && fileName[fileName.count - 1] == '$')
|
|
{
|
|
constexpr const wchar_t* stdStr[] = { L"conerr$", L"conout$", L"conin$" };
|
|
for (u32 i = 0; i != 3; ++i)
|
|
{
|
|
if (!fileName.EndsWith(stdStr[i]))
|
|
continue;
|
|
if (g_isDetachedProcess)
|
|
{
|
|
*hFileHandle = g_stdHandle[i];
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
suppressCreateFileDetour = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (suppressCreateFileDetour)
|
|
{
|
|
NTSTATUS res = Local_NtCreateFile(IsCreateFunc, hFileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
|
|
DEBUG_LOG_TRUE(funcName, L"(SUPPRESSDETOUR) %llu (%ls) -> %ls", uintptr_t(*hFileHandle), ObjectAttributes->ObjectName->Buffer, ToString(res).data);
|
|
if (createFileName && Equals(createFileName, L"NUL"))
|
|
g_nullFile = *hFileHandle;
|
|
return res;
|
|
}
|
|
|
|
DevirtualizePath(fileName);
|
|
|
|
u32 dirTableOffset = ~u32(0);
|
|
|
|
//UBA_ASSERT(CreateDisposition != FILE_SUPERSEDE);
|
|
bool isDeleteOnClose = (CreateOptions & FILE_DELETE_ON_CLOSE) != 0; // clang is using CreateFile with DeleteOnClose to delete files after build errors
|
|
|
|
bool useContent = IsContentUse(DesiredAccess, CreateDisposition);
|
|
bool isWrite = IsWrite(DesiredAccess, CreateDisposition);
|
|
bool isThrowAway = g_rules->IsThrowAway(fileName, g_runningRemote);
|
|
bool keepInMemory = KeepInMemory(fileName, isWrite) || ((isWrite || isDeleteOnClose) && g_rules->IsOutputFile(fileName) && g_allowKeepFilesInMemory) || (isWrite && isThrowAway);
|
|
|
|
#if UBA_DEBUG_LOG_ENABLED
|
|
const wchar_t* isWriteStr = isWrite ? L" WRITE" : L""; (void)isWriteStr;
|
|
#endif
|
|
|
|
bool isSystemFile = fileName.StartsWith(g_systemRoot.data);
|
|
bool checkIfDir = false;
|
|
// This is here just to avoid getting a NtQueryVolumeInformationFile to get volume information
|
|
if (fileName[3] == 0 && fileName[1] == ':')
|
|
{
|
|
isSystemFile = ToLower(fileName[0]) == g_systemRoot[0];
|
|
checkIfDir = true;
|
|
}
|
|
|
|
bool isSystemOrTempFile = isSystemFile || fileName.StartsWith(g_systemTemp.data);
|
|
|
|
if (isSystemFile || (isSystemOrTempFile && !keepInMemory))
|
|
{
|
|
ObjectAttributes->RootDirectory = rootDir;
|
|
NTSTATUS res = Local_NtCreateFile(IsCreateFunc, hFileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
|
|
if (NT_ERROR(res))
|
|
*hFileHandle = INVALID_HANDLE_VALUE;
|
|
DEBUG_LOG_TRUE(funcName, L"(NODETOUR)%ls %llu (%.*ls) -> %ls", isWriteStr, uintptr_t(*hFileHandle), ObjectAttributes->ObjectName->Length / 2, ObjectAttributes->ObjectName->Buffer, ToString(res).data);
|
|
if (NT_ERROR(res))
|
|
return res;
|
|
|
|
if (!isSystemOrTempFile && !isWrite && !t_disallowDetour && fileName[fileName.count-1] != ':')
|
|
TrackInput(fileName.data);
|
|
else
|
|
SkipTrackInput(fileName.data);
|
|
return res;
|
|
}
|
|
|
|
StringBuffer<> fileNameLower(fileName);
|
|
fileNameLower.MakeLower();
|
|
StringKey fileNameKey = ToStringKey(fileNameLower);
|
|
|
|
if (createFileName)// && (keepInMemory || canDetour))
|
|
{
|
|
if (g_allowDirectoryCache)
|
|
{
|
|
// This is an optimization where we populate directory table and use that to figure out if file exists or not..
|
|
// .. in msvc's case it doesn't matter much because these tables are already up to date when msvc use CreateFile.
|
|
// .. clang otoh is using CreateFile with tooons of different paths trying to open files.. in remote worker case this becomes super expensive
|
|
if (!isWrite && !isSystemOrTempFile) // We need to skip SystemTemp.. lots of stuff going on there.
|
|
{
|
|
CHECK_PATH(fileNameLower);
|
|
dirTableOffset = Rpc_GetEntryOffset(fileNameKey, fileName, checkIfDir);
|
|
|
|
bool allowEarlyOut = true;
|
|
if (dirTableOffset == ~u32(0))
|
|
{
|
|
// This could be a written file not reported to server yet
|
|
{
|
|
SCOPED_READ_LOCK(g_mappedFileTable.m_lookupLock, lock);
|
|
auto findIt = g_mappedFileTable.m_lookup.find(fileNameKey);
|
|
if (findIt != g_mappedFileTable.m_lookup.end())
|
|
allowEarlyOut = findIt->second.deleted;
|
|
}
|
|
if (allowEarlyOut)
|
|
{
|
|
//SetLastError(ERROR_FILE_NOT_FOUND); // Don't think this is needed
|
|
*hFileHandle = INVALID_HANDLE_VALUE;
|
|
DEBUG_LOG_DETOURED(funcName, L"NOTFOUND_USINGTABLE (%ls) -> Error", fileName.data);
|
|
|
|
#if UBA_DEBUG_VALIDATE
|
|
if (g_validateFileAccess)
|
|
{
|
|
SuppressDetourScope _;
|
|
UBA_ASSERTF(True_GetFileAttributesW(fileName.data) == INVALID_FILE_ATTRIBUTES, L"DIRTABLE claims file %ls does not exist but it does", fileName.data);
|
|
}
|
|
#endif
|
|
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
}
|
|
else if (!checkIfDir)
|
|
{
|
|
// File could have been deleted.
|
|
DirectoryTable::EntryInformation entryInfo;
|
|
g_directoryTable.GetEntryInformation(entryInfo, dirTableOffset);
|
|
if (entryInfo.attributes == 0)
|
|
{
|
|
DEBUG_LOG_DETOURED(funcName, L"DELETED %llu, (%ls) -> Success", uintptr_t(*hFileHandle), fileName.data);
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
else if (useContent && IsDirectory(entryInfo.attributes))
|
|
{
|
|
DEBUG_LOG_DETOURED(funcName, L"%llu, (%ls) -> STATUS_FILE_IS_A_DIRECTORY", uintptr_t(*hFileHandle), fileName.data);
|
|
return STATUS_FILE_IS_A_DIRECTORY;
|
|
}
|
|
}
|
|
|
|
bool isWriteAttributes = (DesiredAccess & FILE_WRITE_ATTRIBUTES) != 0;
|
|
|
|
// If file is an output file we still allllow this path and accept wrong (compressed) size.
|
|
// This is a bit hacky but we don't want to transfer and decompress file just to get the size
|
|
if (allowEarlyOut && !useContent && !isWriteAttributes && (!CouldBeCompressedFile(fileName) || g_rules->IsOutputFile(fileName)))
|
|
{
|
|
auto dh = new DetouredHandle(HandleType_File);
|
|
dh->fileObject = new FileObject();
|
|
dh->fileObject->desiredAccess = DesiredAccess;
|
|
dh->dirTableOffset = dirTableOffset;
|
|
|
|
FileInfo* tempFileInfo = new FileInfo();
|
|
dh->fileObject->fileInfo = tempFileInfo;
|
|
dh->fileObject->ownsFileInfo = true;
|
|
dh->fileObject->deleteOnClose = isDeleteOnClose;
|
|
tempFileInfo->originalName = _wcsdup(fileName.data);
|
|
tempFileInfo->name = L"GETATTRIBUTES";
|
|
tempFileInfo->refCount = 1;
|
|
*hFileHandle = makeDetouredHandle(dh);
|
|
//SetLastError(ERROR_SUCCESS); // Don't think this is needed
|
|
DEBUG_LOG_DETOURED(funcName, L"GETATTRIBUTES %llu, (%ls) -> Success", uintptr_t(*hFileHandle), fileName.data);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isSystemOrTempFile)
|
|
{
|
|
}
|
|
else if ((DesiredAccess & FILE_LIST_DIRECTORY) != 0 && (CreateOptions & FILE_DIRECTORY_FILE) != 0)
|
|
{
|
|
if (isWrite || !g_allowListDirectoryHandle)
|
|
{
|
|
TimerScope ts(g_kernelStats.createFile);
|
|
UBA_ASSERT(!g_runningRemote);
|
|
NTSTATUS res = True_NtCreateFile(hFileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
|
|
|
|
DEBUG_LOG_DETOURED(funcName, L"(CREATE_DIR) %llu, (%ls) -> %ls", uintptr_t(*hFileHandle), fileName.data, ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
UBA_ASSERT(fileNameLower.data[fileNameLower.count - 1] != '\\');
|
|
DirHash hash(fileNameLower);
|
|
|
|
SCOPED_WRITE_LOCK(g_directoryTable.m_lookupLock, lookupLock);
|
|
auto insres = g_directoryTable.m_lookup.try_emplace(hash.key, g_memoryBlock);
|
|
DirectoryTable::Directory& dir = insres.first->second;
|
|
if (insres.second)
|
|
{
|
|
auto existsResult = g_directoryTable.EntryExistsNoLock(hash.key, fileNameLower);
|
|
if (existsResult != DirectoryTable::Exists_No)
|
|
Rpc_UpdateDirectory(hash.key, fileNameLower.data, fileNameLower.count, false);
|
|
}
|
|
|
|
bool exists = false;
|
|
if (dir.tableOffset != InvalidTableOffset)
|
|
{
|
|
u32 entryOffset = dir.tableOffset | 0x80000000;
|
|
DirectoryTable::EntryInformation entryInfo;
|
|
g_directoryTable.GetEntryInformation(entryInfo, entryOffset);
|
|
exists = entryInfo.attributes != 0;
|
|
}
|
|
|
|
#if UBA_DEBUG_VALIDATE
|
|
NTSTATUS res = exists ? 0 : -1; (void)res;
|
|
HANDLE validateHandle = INVALID_HANDLE_VALUE;
|
|
if (g_validateFileAccess && !isListDirectoryHandle(rootDir))
|
|
{
|
|
IO_STATUS_BLOCK IoStatusBlock2;
|
|
ObjectAttributes->RootDirectory = rootDir;
|
|
NTSTATUS res2 = True_NtCreateFile(&validateHandle, DesiredAccess, ObjectAttributes, &IoStatusBlock2, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength); (void)res2;
|
|
UBA_ASSERT(res < 0 && res2 < 0 || res >= 0 && res2 >= 0);
|
|
ObjectAttributes->RootDirectory = nullptr;
|
|
}
|
|
#endif
|
|
|
|
if (!exists)
|
|
{
|
|
DEBUG_LOG_DETOURED(funcName, L"(AS_DIRECTORY) (%ls) -> NOT EXISTS", fileName.data);
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
g_directoryTable.PopulateDirectory(hash.open, dir);
|
|
|
|
auto listHandle = new ListDirectoryHandle{ hash.key, insres.first->second };
|
|
listHandle->it = 0;
|
|
|
|
SCOPED_READ_LOCK(dir.lock, lock);
|
|
listHandle->fileTableOffsets.resize(dir.files.size());
|
|
u32 it = 0;
|
|
for (auto& pair : dir.files)
|
|
listHandle->fileTableOffsets[it++] = pair.second;
|
|
lock.Leave();
|
|
|
|
#if UBA_DEBUG_VALIDATE
|
|
if (g_validateFileAccess)
|
|
listHandle->validateHandle = validateHandle;
|
|
#endif
|
|
*hFileHandle = makeListDirectoryHandle(listHandle);
|
|
|
|
listHandle->originalName = g_memoryBlock.Strdup(fileName).data;
|
|
|
|
IoStatusBlock->Information = 1;
|
|
IoStatusBlock->Pointer = nullptr;
|
|
IoStatusBlock->Status = 0;
|
|
DEBUG_LOG_DETOURED(funcName, L"(AS_DIRECTORY) (%ls) -> %llu", fileName.data, uintptr_t(*hFileHandle));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!keepInMemory || !isWrite) // we might get \\pipe\ here...
|
|
CHECK_PATH(fileNameLower);
|
|
|
|
const wchar_t* lpFileName = fileName.data;
|
|
u32 closeId = 0;
|
|
|
|
SCOPED_WRITE_LOCK(g_mappedFileTable.m_lookupLock, lookupLock);
|
|
auto insres = g_mappedFileTable.m_lookup.try_emplace(fileNameKey);
|
|
FileInfo& info = insres.first->second;
|
|
u32 lastDesiredAccess = info.lastDesiredAccess;
|
|
if (insres.second)
|
|
{
|
|
u64 size = InvalidValue;
|
|
info.originalName = g_memoryBlock.Strdup(fileName).data;
|
|
info.name = info.originalName;
|
|
if (!keepInMemory && !isSystemOrTempFile)
|
|
{
|
|
u8 access = GetFileAccessFlags(DesiredAccess, CreateDisposition);
|
|
wchar_t newFileName[512];
|
|
Rpc_CreateFileW(fileName, fileNameKey, access, newFileName, sizeof_array(newFileName), size, closeId, false);
|
|
info.name = g_memoryBlock.Strdup(newFileName);
|
|
lpFileName = info.name;
|
|
}
|
|
|
|
info.size = size;
|
|
info.fileNameKey = fileNameKey;
|
|
info.lastDesiredAccess = DesiredAccess;
|
|
}
|
|
else
|
|
{
|
|
if (!info.originalName)
|
|
info.originalName = g_memoryBlock.Strdup(fileName).data;
|
|
if (isWrite)
|
|
{
|
|
bool lastWasWrite = IsContentWrite(info.lastDesiredAccess, 0);
|
|
UBA_ASSERT(!info.isFileMap);
|
|
bool shouldReport = !lastWasWrite || info.deleted || isDeleteOnClose;
|
|
shouldReport = shouldReport && !keepInMemory;
|
|
if (shouldReport)
|
|
{
|
|
u64 size = InvalidValue;
|
|
info.deleted = false;
|
|
wchar_t newFileName[1024];
|
|
u8 access = GetFileAccessFlags(DesiredAccess, CreateDisposition);
|
|
Rpc_CreateFileW(fileName, fileNameKey, access, newFileName, sizeof_array(newFileName), size, closeId, false);
|
|
info.name = g_memoryBlock.Strdup(newFileName);
|
|
//info.size = size; // TODO: Should this be set?
|
|
lpFileName = info.name;
|
|
}
|
|
bool lastUseContent = IsContentUse(info.lastDesiredAccess, 0);
|
|
if (!useContent || !lastUseContent)
|
|
lpFileName = info.name;
|
|
info.lastDesiredAccess |= DesiredAccess;
|
|
}
|
|
else if (info.deleted)
|
|
{
|
|
lpFileName = L"";
|
|
}
|
|
else
|
|
{
|
|
if (!info.mappingChecked && info.name[0] == '^' && !g_runningRemote && CouldBeCompressedFile(fileName))
|
|
{
|
|
Rpc_CheckRemapping(fileName, fileNameKey);
|
|
info.mappingChecked = true;
|
|
}
|
|
lpFileName = info.name;
|
|
}
|
|
}
|
|
|
|
if (!*lpFileName)
|
|
{
|
|
DEBUG_LOG_DETOURED(funcName, L"(deleted) not found (%ls)", fileName.data);
|
|
//UBA_ASSERTF(dwFlagsAndAttributes != FILE_FLAG_BACKUP_SEMANTICS, L"Not finding %ls", fileName);
|
|
//SetLastError(ERROR_FILE_NOT_FOUND);
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
|
|
auto TrackFileInput = [&]()
|
|
{
|
|
if (!keepInMemory && useContent && !isWrite)
|
|
{
|
|
if (!info.tracked)
|
|
{
|
|
info.tracked = true;
|
|
TrackInput(fileName.data);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SkipTrackInput(fileName.data);
|
|
}
|
|
};
|
|
|
|
auto CreateFileHandle = [&](HANDLE th = INVALID_HANDLE_VALUE)
|
|
{
|
|
auto fo = new FileObject();
|
|
fo->desiredAccess = DesiredAccess;
|
|
fo->closeId = closeId;
|
|
fo->fileInfo = &info;
|
|
InterlockedIncrement(&info.refCount);
|
|
fo->deleteOnClose = isDeleteOnClose;
|
|
auto dh = new DetouredHandle(HandleType_File, th);
|
|
dh->dirTableOffset = dirTableOffset;
|
|
dh->fileObject = fo;
|
|
return makeDetouredHandle(dh);
|
|
};
|
|
|
|
if (lpFileName[0] == '$')
|
|
{
|
|
lookupLock.Leave();
|
|
|
|
UBA_ASSERT(!lpFileName[2]);
|
|
|
|
bool isDir = lpFileName[1] == 'd';
|
|
if (isDir && useContent)
|
|
return STATUS_FILE_IS_A_DIRECTORY;
|
|
|
|
MemoryFile& mf = g_emptyMemoryFile;
|
|
info.memoryFile = &mf;
|
|
|
|
UBA_ASSERT(!isDeleteOnClose);
|
|
*hFileHandle = CreateFileHandle();
|
|
|
|
TrackFileInput();
|
|
|
|
DEBUG_LOG_DETOURED(funcName, L"(EMPTY) %llu (%ls) (%ls)", uintptr_t(*hFileHandle), lpFileName, (fileName.data != lpFileName ? fileName.data : L""));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (lpFileName[0] == '^') // It is a HANDLE from session process
|
|
{
|
|
UBA_ASSERTF(!isWrite, TC("Mapped file cant be open for write (%s)"), info.originalName);
|
|
|
|
const wchar_t* handleStr = lpFileName + 1;
|
|
const wchar_t* handleStrEnd = wcschr(handleStr, '-');
|
|
if (!handleStrEnd)
|
|
{
|
|
UBA_ASSERT(handleStrEnd);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
HANDLE mappingHandle = FileMappingHandle::FromU64(StringToValue(handleStr, handleStrEnd - handleStr)).mh;
|
|
const wchar_t* mappingOffsetStr = handleStrEnd + 1;
|
|
u64 mappingOffset = StringToValue(mappingOffsetStr, wcslen(mappingOffsetStr));
|
|
info.trueFileMapOffset = mappingOffset;
|
|
|
|
info.isFileMap = true;
|
|
if (!True_DuplicateHandle(g_hostProcess, mappingHandle, GetCurrentProcess(), &info.trueFileMapHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
Rpc_WriteLogf(L"Can't duplicate handle 0x%llx (%ls) for file %ls (Error %u)", uintptr_t(mappingHandle), lpFileName, info.originalName, GetLastError());
|
|
UBA_ASSERTF(info.trueFileMapHandle, L"Can't duplicate handle 0x%llx (%ls) for file %ls (Error %u)", uintptr_t(mappingHandle), lpFileName, info.originalName, GetLastError());
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
lookupLock.Leave();
|
|
|
|
UBA_ASSERT(info.size != InvalidValue);
|
|
UBA_ASSERTF(!isDeleteOnClose, TC("Creating file mapping %s that has delete on close"), info.originalName);
|
|
*hFileHandle = CreateFileHandle();
|
|
|
|
TrackFileInput();
|
|
|
|
DEBUG_LOG_DETOURED(funcName, L"(MAPPED)%ls %llu (%ls) (%ls) -> Success", isWriteStr, uintptr_t(*hFileHandle), lpFileName, (fileName.data != lpFileName ? fileName.data : L""));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
auto TakeFileOwnershipFromSession = [&](FileMappingHandle mappingHandle, u64 mappingHandleSize)
|
|
{
|
|
info.memoryFile = new MemoryFile(nullptr, false);
|
|
MemoryFile& mf = *info.memoryFile;
|
|
HANDLE newHandle = 0;
|
|
True_DuplicateHandle(g_hostProcess, mappingHandle.mh, GetCurrentProcess(), &newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
|
UBA_ASSERTF(newHandle, L"DuplicateHandle failed when opening temp file %ls (%u)", fileName.data, GetLastError());
|
|
mf.writtenSize = mappingHandleSize;
|
|
mf.committedSize = AlignUp(mappingHandleSize, g_pageSize);
|
|
mf.mappedSize = mf.committedSize;
|
|
TimerScope ts2(g_kernelStats.mapViewOfFile);
|
|
mf.baseAddress = (u8*)True_MapViewOfFile(newHandle, FILE_MAP_READ | FILE_MAP_ALL_ACCESS, 0, 0, mf.mappedSize);
|
|
UBA_ASSERTF(mf.baseAddress, L"MapViewOfFile failed when opening temp file %ls (%u)", fileName.data, GetLastError());
|
|
mf.reserveSize = FileTypeMaxSize(fileName, isSystemOrTempFile);
|
|
mf.mappingHandle.mh = newHandle;
|
|
};
|
|
|
|
if (lpFileName[0] == ':') // It is a HANDLE from session process. A written file that is writable
|
|
{
|
|
if (!info.memoryFile)
|
|
{
|
|
const wchar_t* handleStr = lpFileName + 1;
|
|
const wchar_t* handleStrEnd = wcschr(handleStr, '-');
|
|
if (!handleStrEnd)
|
|
{
|
|
UBA_ASSERT(handleStrEnd);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
auto mappingHandle = FileMappingHandle::FromU64(StringToValue(handleStr, handleStrEnd - handleStr));
|
|
UBA_ASSERT(!StringToValue(handleStrEnd + 1, wcslen(handleStrEnd + 1)));
|
|
UBA_ASSERT(info.size != InvalidValue);
|
|
|
|
TakeFileOwnershipFromSession(mappingHandle, info.size);
|
|
}
|
|
|
|
if (CreateDisposition != FILE_OPEN && CreateDisposition != FILE_OPEN_IF)
|
|
info.memoryFile->writtenSize = 0;
|
|
|
|
lookupLock.Leave();
|
|
*hFileHandle = CreateFileHandle();
|
|
TrackFileInput();
|
|
DEBUG_LOG_DETOURED(funcName, L"(WRITTENFILE)%ls %llu (%ls) (%ls) -> Success", isWriteStr, uintptr_t(*hFileHandle), lpFileName, (fileName.data != lpFileName ? fileName.data : L""));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (keepInMemory || info.memoryFile)
|
|
{
|
|
#if UBA_DEBUG_LOG_ENABLED
|
|
const wchar_t* memoryType = L"MEMORY";
|
|
#endif
|
|
|
|
if (!info.memoryFile)
|
|
{
|
|
if (NeedsSharedMemory(fileName.data))
|
|
{
|
|
#if UBA_DEBUG_LOG_ENABLED
|
|
memoryType = L"SHAREDMEMORY";
|
|
#endif
|
|
{
|
|
RPC_MESSAGE(OpenTempFile, openTempFile)
|
|
writer.WriteStringKey(fileNameKey);
|
|
writer.WriteString(fileNameLower);
|
|
writer.Flush();
|
|
BinaryReader reader;
|
|
FileMappingHandle mappingHandle = FileMappingHandle::FromU64(reader.ReadU64());
|
|
u64 mappingHandleSize = reader.ReadU64();
|
|
pcs.Leave();
|
|
if (mappingHandle.IsValid())
|
|
{
|
|
TakeFileOwnershipFromSession(mappingHandle, mappingHandleSize);
|
|
}
|
|
else if (isWrite)
|
|
{
|
|
info.memoryFile = new MemoryFile(false, FileTypeMaxSize(fileName, isSystemOrTempFile), false, fileName.data);
|
|
}
|
|
else
|
|
{
|
|
DEBUG_LOG_DETOURED(funcName, L"(memory) not found (%ls)", fileName.data);
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!isThrowAway && CreateDisposition == FILE_OPEN)
|
|
{
|
|
if (!info.memoryFile)
|
|
{
|
|
*hFileHandle = INVALID_HANDLE_VALUE;
|
|
DEBUG_LOG_DETOURED(funcName, L"ALREADYEXISTS (%ls) -> Error", fileName.data);
|
|
return STATUS_OBJECT_NAME_EXISTS;//STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool isOutput = (isWrite || isDeleteOnClose) && g_rules->IsOutputFile(fileName);
|
|
if (isOutput && (CreateDisposition & FILE_OPEN_IF) == 0)
|
|
{
|
|
UBA_ASSERTF(false, TC("Trying to open %s with openif. This is not supported"), fileName.data);
|
|
}
|
|
|
|
bool isLocal = !isOutput;
|
|
//UBA_ASSERTF(CreateDisposition != FILE_OPEN || Contains(fileName.data, L"vctip_"), TC("Unsupported disposition %u for file %s"), CreateDisposition, fileName.data);
|
|
info.memoryFile = new MemoryFile(isLocal, FileTypeMaxSize(fileName, isSystemOrTempFile), isThrowAway, fileName.data);
|
|
}
|
|
}
|
|
|
|
// TODO: Time should be in sync with host machine!
|
|
FILETIME ft;
|
|
SYSTEMTIME st;
|
|
GetSystemTime(&st);
|
|
SystemTimeToFileTime(&st, &ft);
|
|
info.memoryFile->fileTime = (u64&)ft;
|
|
info.memoryFile->volumeSerial = 1;
|
|
info.memoryFile->fileIndex = InterlockedDecrement(&g_memoryFileIndexCounter);
|
|
}
|
|
else
|
|
{
|
|
#if UBA_DEBUG_LOG_ENABLED
|
|
if (!info.memoryFile->isLocalOnly)
|
|
memoryType = L"SHAREDMEMORY";
|
|
#endif
|
|
}
|
|
|
|
lookupLock.Leave();
|
|
|
|
*hFileHandle = CreateFileHandle();
|
|
|
|
TrackFileInput();
|
|
|
|
DEBUG_LOG_DETOURED(funcName, L"(%s)%ls %llu (%ls) (%ls) -> Success", memoryType, isWriteStr, uintptr_t(*hFileHandle), lpFileName, (fileName.data != lpFileName ? fileName.data : L""));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
lookupLock.Leave();
|
|
|
|
StringView tempFileName;
|
|
if (lpFileName[0] == '#')
|
|
tempFileName = fileName;
|
|
else
|
|
tempFileName = ToView(info.name);
|
|
|
|
StringBuffer<> temp;
|
|
temp.Append(TCV("\\??\\"));
|
|
if (IsUncPath(tempFileName.data))
|
|
temp.Append(TCV("UNC")).Append(StringView(tempFileName).Skip(1));
|
|
else
|
|
temp.Append(tempFileName);
|
|
|
|
UNICODE_STRING* old = ObjectAttributes->ObjectName;
|
|
UNICODE_STRING str;
|
|
str.Buffer = temp.data;
|
|
str.Length = u16(temp.count * 2);
|
|
str.MaximumLength = str.Length + 2;
|
|
ObjectAttributes->ObjectName = &str;
|
|
// TODO!!! THIS NEEDS TO set the ObjectAttributes->ObjectName->Buffer and ObjectAttributes->ObjectName->Length;
|
|
//wcscpy_s(ObjectAttributes->ObjectName->Buffer + 4, ObjectAttributes->ObjectName->MaximumLength/2 - 8, lpFileName);
|
|
//ObjectAttributes->ObjectName->Length = u16(wcslen(lpFileName)*2) + 8;
|
|
|
|
NTSTATUS res = Local_NtCreateFile(IsCreateFunc, hFileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
|
|
|
|
ObjectAttributes->ObjectName = old;
|
|
|
|
if (NT_ERROR(res))
|
|
{
|
|
if (closeId)
|
|
{
|
|
info.lastDesiredAccess = lastDesiredAccess;
|
|
Rpc_UpdateCloseHandle(L"", closeId, false, L"", {}, 0, false);
|
|
}
|
|
DEBUG_LOG_TRUE(funcName, L"%ls (%ls) (%ls) -> %ls", isWriteStr, lpFileName, (fileName.data != lpFileName ? fileName.data : L""), ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
TrackFileInput();
|
|
|
|
UBA_ASSERT(info.originalName);
|
|
*hFileHandle = CreateFileHandle(*hFileHandle);
|
|
DEBUG_LOG_TRUE(funcName, L"%ls %llu (%ls)%s -> %ls", isWriteStr, uintptr_t(*hFileHandle), tempFileName.data, isDeleteOnClose ? TC(" DeleteOnClose") : TC(""), ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes,
|
|
ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength)
|
|
{
|
|
DETOURED_CALL(NtCreateFile);
|
|
return Shared_NtCreateFile(true, FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
|
|
}
|
|
|
|
thread_local bool t_ntOpenFileDisallowed;
|
|
|
|
NTSTATUS NTAPI Detoured_NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG OpenOptions)
|
|
{
|
|
DETOURED_CALL(NtOpenFile);
|
|
if (t_ntOpenFileDisallowed)
|
|
{
|
|
DEBUG_LOG_DETOURED(L"NtOpenFile", L"(DISALLOWED)(%.*s) -> STATUS_OBJECT_NAME_NOT_FOUND", ObjectAttributes->ObjectName->Length / 2, ObjectAttributes->ObjectName->Buffer);
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
return Shared_NtCreateFile(false, FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, (PLARGE_INTEGER)NULL, 0L, ShareAccess, FILE_OPEN, OpenOptions, (PVOID)NULL, 0L);
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtFsControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG FsControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength)
|
|
{
|
|
DETOURED_CALL(NtFsControlFile);
|
|
HANDLE trueHandle = FileHandle;
|
|
if (isDetouredHandle(FileHandle))
|
|
{
|
|
auto& dh = asDetouredHandle(FileHandle);
|
|
trueHandle = dh.trueHandle;
|
|
//if (trueHandle == INVALID_HANDLE_VALUE)
|
|
//{
|
|
// return STATUS_UNSUCCESSFUL;
|
|
//}
|
|
UBA_ASSERTF(trueHandle != INVALID_HANDLE_VALUE, L"NtFsControlFile code %u (%ls)", FsControlCode, HandleToName(FileHandle));
|
|
}
|
|
UBA_ASSERT(!isListDirectoryHandle(FileHandle));
|
|
|
|
return True_NtFsControlFile(trueHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, FsControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength);
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtCopyFileChunk(HANDLE Source, HANDLE Dest, HANDLE Event, PIO_STATUS_BLOCK IoStatusBlock, ULONG Length, PULONG SourceOffset, PULONG DestOffset, PULONG SourceKey, PULONG DestKey, ULONG Flags)
|
|
{
|
|
DETOURED_CALL(NtCopyFileChunk);
|
|
HANDLE trueSourceHandle = Source;
|
|
if (isDetouredHandle(Source))
|
|
{
|
|
auto& dh = asDetouredHandle(Source);
|
|
trueSourceHandle = dh.trueHandle;
|
|
UBA_ASSERT(trueSourceHandle != INVALID_HANDLE_VALUE);
|
|
}
|
|
HANDLE trueDestHandle = Dest;
|
|
if (isDetouredHandle(Dest))
|
|
{
|
|
auto& dh = asDetouredHandle(Dest);
|
|
trueDestHandle = dh.trueHandle;
|
|
UBA_ASSERT(trueDestHandle != INVALID_HANDLE_VALUE);
|
|
}
|
|
return True_NtCopyFileChunk(trueSourceHandle, trueDestHandle, Event, IoStatusBlock, Length, SourceOffset, DestOffset, SourceKey, DestKey, Flags);
|
|
}
|
|
NTSTATUS NTAPI Detoured_NtClose(HANDLE handle)
|
|
{
|
|
DETOURED_CALL(NtClose);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE || handle == PseudoHandle)
|
|
{
|
|
TimerScope ts(g_kernelStats.closeHandle);
|
|
return True_NtClose(handle);
|
|
}
|
|
|
|
if (isListDirectoryHandle(handle))
|
|
{
|
|
auto& listHandle = asListDirectoryHandle(handle);
|
|
|
|
#if UBA_DEBUG_VALIDATE
|
|
if (g_validateFileAccess)
|
|
{
|
|
auto res = True_NtClose(listHandle.validateHandle);
|
|
if (res != 0)
|
|
ToInvestigate(L"NtClose failed for validate handle");
|
|
}
|
|
#endif
|
|
|
|
delete& listHandle;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!isDetouredHandle(handle))
|
|
{
|
|
TimerScope ts(g_kernelStats.closeHandle);
|
|
auto res = True_NtClose(handle);
|
|
#if !defined(_M_ARM64) // For some reason this log line crashes on arm64 with access violation on internal tls variable
|
|
DEBUG_LOG_TRUE(L"NtClose", L"%llu (%ls) -> %ls", uintptr_t(handle), HandleToName(handle), ToString(res).data);
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
DetouredHandle& dh = asDetouredHandle(handle);
|
|
|
|
NTSTATUS res = STATUS_SUCCESS;
|
|
|
|
if (dh.trueHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
TimerScope ts(g_kernelStats.closeFile);
|
|
res = True_NtClose(dh.trueHandle);
|
|
}
|
|
|
|
FileObject* fo = dh.fileObject;
|
|
if (!fo)
|
|
{
|
|
if (dh.type >= HandleType_StdErr) // TODO: This might leak if handle is duplicated.. but ignore for now
|
|
return res;
|
|
DEBUG_LOG_TRUE(L"NtClose", L"%llu (%ls) -> %ls", uintptr_t(handle), HandleToName(handle), ToString(res).data);
|
|
delete& dh;
|
|
return res;
|
|
}
|
|
|
|
auto foRefCount = InterlockedDecrement(&fo->refCount);
|
|
UBA_ASSERT(foRefCount != ~u64(0));
|
|
if (foRefCount)
|
|
{
|
|
DEBUG_LOG_TRUE(L"NtClose", L"%llu (%ls) -> %ls", uintptr_t(handle), HandleToName(handle), ToString(res).data);
|
|
delete& dh;
|
|
return res;
|
|
}
|
|
|
|
FileMappingHandle mappingHandle;
|
|
u64 mappingWritten = 0;
|
|
FileInfo& fi = *fo->fileInfo;
|
|
const wchar_t* path = fi.name;
|
|
wchar_t temp[512];
|
|
if (MemoryFile* mf = fi.memoryFile)
|
|
{
|
|
if (IsWrite(fo->desiredAccess, 0))
|
|
{
|
|
// TODO: There are race conditions in this code. There could be other file handles accessing the same piece of memory (allthough unlikely)
|
|
u64 alignedWritten = AlignUp(mf->writtenSize, 64 * 1024);
|
|
if (alignedWritten < mf->committedSize)
|
|
{
|
|
u64 decommitSize = u64(mf->committedSize - alignedWritten);
|
|
if (mf->isLocalOnly)
|
|
{
|
|
#pragma warning(push)
|
|
#pragma warning(disable:6250)
|
|
if (!::VirtualFree(mf->baseAddress + alignedWritten, decommitSize, MEM_DECOMMIT))
|
|
ToInvestigate(L"Failed to decommit memory (%u)", GetLastError());
|
|
#pragma warning(pop)
|
|
}
|
|
else
|
|
{
|
|
// Speculative change. According to stackoverflow (I know) this can hint the system that this memory is not needed anymore.
|
|
// Building UnrealEditor and friends put huge pressure on committed space and anything that can reduce that is valuable
|
|
if (!::VirtualUnlock(mf->baseAddress + alignedWritten, decommitSize))
|
|
if (GetLastError() != ERROR_NOT_LOCKED)
|
|
ToInvestigate(L"Failed to unlock memory (%u)", GetLastError());
|
|
}
|
|
mf->committedSize = alignedWritten;
|
|
}
|
|
}
|
|
|
|
mappingHandle = mf->mappingHandle;
|
|
mappingWritten = mf->writtenSize;
|
|
|
|
u32 orginalNameLen = TStrlen(fi.originalName);
|
|
if ((fo->deleteOnClose || IsWrite(fo->desiredAccess, 0)) && g_rules->IsOutputFile({fi.originalName, orginalNameLen}) && !g_rules->IsThrowAway(StringView(fi.originalName, orginalNameLen), g_runningRemote))
|
|
{
|
|
// Need to report this file to host so it can be tracked in directory table
|
|
if (!mf->isReported)
|
|
{
|
|
path = temp;
|
|
mf->isReported = true;
|
|
const wchar_t* fileName = fi.originalName;
|
|
if (!fo->newName.empty())
|
|
fileName = fo->newName.c_str();
|
|
StringBuffer<> fixedName;
|
|
FixPath(fixedName, fileName);
|
|
StringKey fileNameKey = fi.fileNameKey;
|
|
if (!fo->newName.empty())
|
|
fileNameKey = ToStringKeyLower(fixedName);
|
|
|
|
u64 size;
|
|
Rpc_CreateFileW(fixedName, fileNameKey, AccessFlag_Write, temp, sizeof_array(temp), size, fo->closeId, true);
|
|
}
|
|
|
|
if (!fo->newName.empty())
|
|
{
|
|
// It might be that same process will open it again, so we will need to update the mapping table
|
|
StringBuffer<> fixedNewName;
|
|
FixPath(fixedNewName, fo->newName.c_str());
|
|
fixedNewName.MakeLower();
|
|
StringKey fileNameKey = ToStringKey(fixedNewName);
|
|
SCOPED_WRITE_LOCK(g_mappedFileTable.m_lookupLock, _);
|
|
auto insres = g_mappedFileTable.m_lookup.try_emplace(fileNameKey);
|
|
FileInfo& newInfo = insres.first->second;
|
|
newInfo = fi;
|
|
newInfo.originalName = g_memoryBlock.Strdup(fo->newName).data;
|
|
newInfo.name = newInfo.originalName;
|
|
newInfo.fileNameKey = fileNameKey;
|
|
UBA_ASSERT(!fo->deleteOnClose);
|
|
fi = {};
|
|
fi.deleted = true;
|
|
fo->ownsFileInfo = false;
|
|
fo->newName.clear();
|
|
}
|
|
}
|
|
else if (NeedsSharedMemory(fi.originalName) && IsWrite(fo->desiredAccess, 0))
|
|
{
|
|
UBA_ASSERT(!mf->isLocalOnly);
|
|
StringBuffer<> fixedName;
|
|
FixPath(fixedName, path);
|
|
|
|
RPC_MESSAGE(CreateTempFile, createTempFile)
|
|
writer.WriteStringKey(ToStringKeyLower(fixedName));
|
|
writer.WriteString(fixedName);
|
|
writer.WriteU64(mappingHandle.ToU64());
|
|
writer.WriteU64(mappingWritten);
|
|
writer.Flush();
|
|
BinaryReader reader;
|
|
}
|
|
}
|
|
else if (fo->deleteOnClose && dh.trueHandle == INVALID_HANDLE_VALUE) // We have used an optimized handle that actually never opens the file so we need to delete it manually
|
|
{
|
|
DeleteFileW(fi.originalName);
|
|
}
|
|
|
|
if (fo->closeId)
|
|
{
|
|
Rpc_UpdateCloseHandle(path, fo->closeId, fo->deleteOnClose, fo->newName.c_str(), mappingHandle, mappingWritten, true);
|
|
}
|
|
else
|
|
{
|
|
// TODO: Update g_mappedFileTable.m_lookup?
|
|
//UBA_ASSERTF(fo->newName.empty(), L"Got close of file that was renamed but had no closeId. Old: %ls New: %ls", fi.originalName, fo->newName.c_str());
|
|
}
|
|
|
|
InterlockedDecrement(&fi.refCount);
|
|
|
|
DEBUG_LOG_DETOURED(L"NtClose", L"%llu (%ls) -> %ls", uintptr_t(handle), HandleToName(handle), ToString(res).data);
|
|
|
|
if (fo->ownsFileInfo)
|
|
{
|
|
UBA_ASSERT(!fi.memoryFile);
|
|
if (fi.fileMapMem)
|
|
{
|
|
bool success = True_UnmapViewOfFile(fi.fileMapMem); (void)success;
|
|
DEBUG_LOG_TRUE(L"INTERNAL UnmapViewOfFile", L"%llu (%ls) (%ls) -> %ls", uintptr_t(fi.fileMapMem), fi.name, fi.originalName, ToString(success));
|
|
}
|
|
|
|
free((void*)fi.originalName);
|
|
delete& fi;
|
|
}
|
|
|
|
delete fo;
|
|
delete& dh;
|
|
return res;
|
|
}
|
|
|
|
NTSTATUS Detoured_NtQueryObject(HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength)
|
|
{
|
|
DETOURED_CALL(NtQueryObject);
|
|
|
|
HANDLE trueHandle = Handle;
|
|
|
|
// This can be other things than FILES.. Is used by GetHandleInformation
|
|
if (isDetouredHandle(Handle))
|
|
{
|
|
DetouredHandle& dh = asDetouredHandle(Handle);
|
|
trueHandle = dh.trueHandle;
|
|
|
|
if (trueHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
if (ObjectInformationClass == 1) // ObjectNameInformation
|
|
{
|
|
auto fo = dh.fileObject;
|
|
UBA_ASSERT(fo);
|
|
auto& fi = *fo->fileInfo;
|
|
UBA_ASSERT(fi.originalName);
|
|
const wchar_t* fileName = fi.originalName;
|
|
|
|
StringBuffer<> fixedPath;
|
|
FixPath(fileName, g_virtualWorkingDir.data, g_virtualWorkingDir.count, fixedPath);
|
|
|
|
StringBuffer<> buffer;
|
|
|
|
g_directoryTable.GetFinalPath(buffer, fixedPath.data);
|
|
VirtualizePath(buffer);
|
|
|
|
if (g_runningRemote || buffer[0] != fixedPath[0])
|
|
{
|
|
// It was remote or virtualized.. let's make up device drive.
|
|
// TODO: Maybe we need to replicate device drive names to remotes
|
|
buffer.Prepend(AsView(L"\\Device\\HarddiskVolume100"), 2);
|
|
}
|
|
else
|
|
{
|
|
wchar_t drive[3] = { buffer[0], ':', 0 };
|
|
wchar_t device[256];
|
|
DWORD deviceLen = QueryDosDeviceW(drive, device, sizeof_array(device));
|
|
UBA_ASSERT(deviceLen);
|
|
buffer.Prepend(StringView(device, deviceLen), 2);
|
|
}
|
|
|
|
u64 bufferSize = (buffer.count+1)*sizeof(tchar);
|
|
u64 totalSize = sizeof(UNICODE_STRING) + bufferSize;
|
|
|
|
if (ObjectInformationLength < totalSize)
|
|
{
|
|
DEBUG_LOG_DETOURED(L"NtQueryObject", L"(ObjectNameInformation) %s -> STATUS_BUFFER_OVERFLOW", HandleToName(Handle));
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
auto& ustr = *(PUNICODE_STRING)ObjectInformation;
|
|
ustr.Length = u16(bufferSize - sizeof(tchar));
|
|
ustr.MaximumLength = u16(bufferSize);
|
|
ustr.Buffer = (wchar_t*)((&ustr) + 1);
|
|
memcpy(ustr.Buffer, buffer.data, bufferSize);
|
|
*ReturnLength = (ULONG)totalSize;
|
|
|
|
DEBUG_LOG_DETOURED(L"NtQueryObject", L"(ObjectNameInformation) %llu -> Success (%s)", uintptr_t(Handle), buffer.data);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
UBA_ASSERTF(false, L"NtQueryObject NOT_IMPLEMENTED (class %i) (%s)", ObjectInformationClass, HandleToName(Handle));
|
|
}
|
|
}
|
|
auto res = True_NtQueryObject(trueHandle, ObjectInformationClass, ObjectInformation, ObjectInformationLength, ReturnLength);
|
|
DEBUG_LOG_TRUE(L"NtQueryObject", L"(%i) %llu -> %ls", ObjectInformationClass, uintptr_t(Handle), ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
NTSTATUS Detoured_NtQueryInformationProcess(HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength)
|
|
{
|
|
DETOURED_CALL(NtQueryInformationProcess);
|
|
if (isDetouredHandle(ProcessHandle))
|
|
{
|
|
ProcessHandle = asDetouredHandle(ProcessHandle).trueHandle;
|
|
}
|
|
|
|
NTSTATUS res = True_NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength);
|
|
DEBUG_LOG_TRUE(L"NtQueryInformationProcess", L"(class %u) %llu -> %ls", ProcessInformationClass, uintptr_t(ProcessHandle), ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
#if defined(DETOURED_INCLUDE_DEBUG)
|
|
|
|
NTSTATUS NTAPI Detoured_NtCreateIoCompletion(PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG Count)
|
|
{
|
|
UBA_ASSERT(!isDetouredHandle(*IoCompletionHandle));
|
|
return True_NtCreateIoCompletion(IoCompletionHandle, DesiredAccess, ObjectAttributes, Count);
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtFlushBuffersFileEx(HANDLE FileHandle, ULONG Flags, PVOID Parameters, ULONG ParametersSize, PIO_STATUS_BLOCK IoStatusBlock)
|
|
{
|
|
DETOURED_CALL(NtFlushBuffersFileEx);
|
|
UBA_ASSERT(!isDetouredHandle(FileHandle));
|
|
return True_NtFlushBuffersFileEx(FileHandle, Flags, Parameters, ParametersSize, IoStatusBlock);
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID Buffer, ULONG Length, PLARGE_INTEGER ByteOffset, PULONG Key)
|
|
{
|
|
DETOURED_CALL(NtReadFile);
|
|
UBA_ASSERT(!isDetouredHandle(FileHandle));
|
|
return True_NtReadFile(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, Buffer, Length, ByteOffset, Key);
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtAlpcCreatePort(PHANDLE PortHandle, POBJECT_ATTRIBUTES ObjectAttributes, PALPC_PORT_ATTRIBUTES PortAttributes)
|
|
{
|
|
//DEBUG_LOG_TRUE(L"NtAlpcCreatePort", L"");
|
|
return True_NtAlpcCreatePort(PortHandle, ObjectAttributes, PortAttributes);
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtAlpcConnectPort(PHANDLE PortHandle, PUNICODE_STRING PortName, POBJECT_ATTRIBUTES ObjectAttributes, PALPC_PORT_ATTRIBUTES PortAttributes, DWORD ConnectionFlags, PSID RequiredServerSid, PPORT_MESSAGE ConnectionMessage, PSIZE_T ConnectMessageSize, PALPC_MESSAGE_ATTRIBUTES OutMessageAttributes, PALPC_MESSAGE_ATTRIBUTES InMessageAttributes, PLARGE_INTEGER Timeout)
|
|
{
|
|
//DEBUG_LOG_TRUE(L"NtAlpcConnectPort", L"");
|
|
return True_NtAlpcConnectPort(PortHandle, PortName, ObjectAttributes, PortAttributes, ConnectionFlags, RequiredServerSid, ConnectionMessage, ConnectMessageSize, OutMessageAttributes, InMessageAttributes, Timeout);
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtAlpcCreatePortSection(HANDLE PortHandle, ULONG Flags, HANDLE SectionHandle, SIZE_T SectionSize, PHANDLE AlpcSectionHandle, PSIZE_T ActualSectionSize)
|
|
{
|
|
//DEBUG_LOG_TRUE(L"NtAlpcCreatePortSection", L"");
|
|
return True_NtAlpcCreatePortSection(PortHandle, Flags, SectionHandle, SectionSize, AlpcSectionHandle, ActualSectionSize);
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtAlpcSendWaitReceivePort(HANDLE PortHandle, DWORD Flags, PPORT_MESSAGE SendMessage_, PALPC_MESSAGE_ATTRIBUTES SendMessageAttributes, PPORT_MESSAGE ReceiveMessage, PSIZE_T BufferLength, PALPC_MESSAGE_ATTRIBUTES ReceiveMessageAttributes, PLARGE_INTEGER Timeout)
|
|
{
|
|
//u64 size = 0;
|
|
//if (SendMessage_)
|
|
// size = SendMessage_->u1.s1.DataLength;
|
|
//DEBUG_LOG_TRUE(L"NtAlpcSendWaitReceivePort", L"%llu", size);
|
|
return True_NtAlpcSendWaitReceivePort(PortHandle, Flags, SendMessage_, SendMessageAttributes, ReceiveMessage, BufferLength, ReceiveMessageAttributes, Timeout);
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_NtAlpcDisconnectPort(HANDLE PortHandle, ULONG Flags)
|
|
{
|
|
//DEBUG_LOG_TRUE(L"NtAlpcDisconnectPort", L"");
|
|
return True_NtAlpcDisconnectPort(PortHandle, Flags);
|
|
}
|
|
|
|
NTSTATUS NTAPI Detoured_ZwQueryDirectoryFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length,
|
|
FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, PUNICODE_STRING FileName, BOOLEAN RestartScan)
|
|
{
|
|
DETOURED_CALL(ZwQueryDirectoryFile);
|
|
DEBUG_LOG_TRUE(L"ZwQueryDirectoryFile", L"(%ls)", HandleToName(FileHandle));
|
|
UBA_ASSERT(!isDetouredHandle(FileHandle));
|
|
return True_ZwQueryDirectoryFile(FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, FileInformation, Length, FileInformationClass, ReturnSingleEntry, FileName, RestartScan);
|
|
}
|
|
|
|
//NTSTATUS NTAPI Detoured_ZwCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes,
|
|
// ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength)
|
|
//{
|
|
// DETOURED_CALL(ZwCreateFile);
|
|
// DEBUG_LOG_TRUE(L"ZwCreateFile", L"");
|
|
// UBA_ASSERT(!isDetouredHandle(FileHandle));
|
|
// return True_ZwCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
|
|
//}
|
|
|
|
//NTSTATUS NTAPI Detoured_ZwOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG OpenOptions)
|
|
//{
|
|
// DETOURED_CALL(ZwOpenFile);
|
|
// DEBUG_LOG_TRUE(L"ZwOpenFile", L"");
|
|
// UBA_ASSERT(!isDetouredHandle(FileHandle));
|
|
// return True_ZwCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, (PLARGE_INTEGER)NULL, 0L, ShareAccess, FILE_OPEN, OpenOptions, (PVOID)NULL, 0L);
|
|
//}
|
|
|
|
NTSTATUS NTAPI Detoured_ZwSetInformationFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass)
|
|
{
|
|
DETOURED_CALL(ZwSetInformationFile);
|
|
DEBUG_LOG_TRUE(L"ZwSetInformationFile", L"%llu (%ls)", uintptr_t(FileHandle), HandleToName(FileHandle));
|
|
UBA_ASSERT(!isDetouredHandle(FileHandle));
|
|
return True_ZwSetInformationFile(FileHandle, IoStatusBlock, FileInformation, Length, FileInformationClass);
|
|
}
|
|
|
|
PVOID Detoured_RtlAllocateHeap(PVOID HeapHandle, ULONG Flags, SIZE_T Size)
|
|
{
|
|
//if (Flags & HEAP_ZERO_MEMORY)
|
|
// return mi_zalloc(Size);
|
|
//else
|
|
// return mi_malloc(Size);
|
|
return True_RtlAllocateHeap(HeapHandle, Flags, Size);
|
|
}
|
|
|
|
PVOID Detoured_RtlReAllocateHeap(PVOID HeapHandle, ULONG Flags, PVOID BaseAddress, SIZE_T Size)
|
|
{
|
|
#if UBA_USE_MIMALLOC
|
|
if (g_checkRtlHeap && IsInMiMalloc(BaseAddress))
|
|
{
|
|
return mi_realloc(BaseAddress, Size);
|
|
//Rpc_WriteLogf(L"ERROR: RtlReAllocateHeap - This is not implemented");
|
|
//return 0;
|
|
}
|
|
#endif
|
|
//if (Flags & HEAP_ZERO_MEMORY)
|
|
// return mi_realloc(BaseAddress, Size);
|
|
//else
|
|
// return mi_realloc(BaseAddress, Size);
|
|
return True_RtlReAllocateHeap(HeapHandle, Flags, BaseAddress, Size);
|
|
}
|
|
|
|
BOOLEAN Detoured_RtlValidateHeap(HANDLE HeapPtr, ULONG Flags, PVOID Block)
|
|
{
|
|
//return true;
|
|
return True_RtlValidateHeap(HeapPtr, Flags, Block);
|
|
}
|
|
|
|
NTSTATUS Detoured_RtlDosPathNameToNtPathName_U_WithStatus(PCWSTR dos_path, PUNICODE_STRING ntpath, PWSTR* file_part, VOID* reserved)
|
|
{
|
|
auto res = True_RtlDosPathNameToNtPathName_U_WithStatus(dos_path, ntpath, file_part, reserved);
|
|
//DEBUG_LOG_TRUE(L"Detoured_RtlDosPathNameToNtPathName_U_WithStatus", L"-> %s", ToString(res).data);
|
|
return res;
|
|
}
|
|
|
|
|
|
#endif
|