Files
UnrealEngine/Engine/Source/Programs/UnrealBuildAccelerator/Detours/Private/Windows/UbaDetoursFunctionsNtDll.inl
2025-05-18 13:04:45 +08:00

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