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

4343 lines
152 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
DWORD Local_GetLongPathNameW(LPCWSTR lpszShortPath, LPWSTR lpszLongPath, DWORD cchBuffer)
{
SCOPED_WRITE_LOCK(g_longPathNameCacheLock, lock);
auto findIt = g_longPathNameCache.find(lpszShortPath);
if (findIt != g_longPathNameCache.end())
{
const wchar_t* longPath = findIt->second;
u32 len = u32(wcslen(longPath));
if (len == 0)
{
SetLastError(ERROR_FILE_NOT_FOUND);
return 0;
}
SetLastError(ERROR_SUCCESS);
if (cchBuffer <= len)
return len + 1;
memcpy(lpszLongPath, longPath, (len + 1) * 2);
return len + 1;
}
const wchar_t* newLongPath = nullptr;
DWORD res = 0;
if (g_runningRemote)
{
u32 errorCode = 0;
StringBuffer<> longName;
{
RPC_MESSAGE(GetLongPathName, longPathName)
writer.WriteString(lpszShortPath);
writer.Flush();
BinaryReader reader;
errorCode = reader.ReadU32();
reader.ReadString(longName);
}
newLongPath = g_memoryBlock.Strdup(longName).data;
if (longName.count == 0)
{
// Error
}
if (cchBuffer > longName.count)
{
memcpy(lpszLongPath, longName.data, (longName.count+1)*sizeof(wchar_t));
res = longName.count;
}
else
{
res = longName.count + 1;
}
SetLastError(errorCode);
DEBUG_LOG_DETOURED(L"GetLongPathNameW", L"%ls", lpszShortPath);
}
else
{
DEBUG_LOG_TRUE(L"GetLongPathNameW", L"(Detour disabled under this call to handle ~) (%ls)", lpszShortPath);
SuppressDetourScope _;
res = True_GetLongPathNameW(lpszShortPath, lpszLongPath, cchBuffer);
if (res == 0)
return res;
newLongPath = g_memoryBlock.Strdup(lpszLongPath);
}
wchar_t* newShortPath = g_memoryBlock.Strdup(lpszShortPath);
g_longPathNameCache.insert({ newShortPath, newLongPath });
return res;
}
LPWSTR Detoured_GetCommandLineW()
{
DETOURED_CALL(GetCommandLineW);
if (!g_virtualCommandLineW)
{
LPWSTR str = True_GetCommandLineW();
DEBUG_LOG_TRUE(L"GetCommandLineW", L"");// str);
return str;
}
DEBUG_LOG_DETOURED(L"GetCommandLineW", L"");
return (LPWSTR)g_virtualCommandLineW;
}
DWORD Detoured_GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer)
{
DETOURED_CALL(GetCurrentDirectoryW);
u64 length = g_virtualWorkingDir.count - 1; // Skip last slash
SetLastError(ERROR_SUCCESS);
if (lpBuffer == nullptr || nBufferLength < length + 1)
{
DEBUG_LOG_DETOURED(L"GetCurrentDirectoryW", L"(buffer too small: %u) -> %llu", nBufferLength, length + 1);
return DWORD(length + 1);
}
memcpy(lpBuffer, g_virtualWorkingDir.data, length * 2);
lpBuffer[length] = 0; // Skip last slash
DEBUG_LOG_DETOURED(L"GetCurrentDirectoryW", L"(%ls)", lpBuffer);
return DWORD(length);
//DEBUG_LOG_TRUE(L"GetCurrentDirectoryW", L"");
//auto res = True_GetCurrentDirectoryW(nBufferLength, lpBuffer);
//return res;
}
DWORD Detoured_GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer)
{
DETOURED_CALL(GetCurrentDirectoryA);
u64 length = g_virtualWorkingDir.count - 1; // Skip last slash
SetLastError(ERROR_SUCCESS);
if (lpBuffer == nullptr || nBufferLength < length + 1)
{
DEBUG_LOG_DETOURED(L"GetCurrentDirectoryA", L"(buffer too small: %u)", nBufferLength);
return DWORD(length + 1);
}
size_t res;
errno_t err = wcstombs_s(&res, lpBuffer, nBufferLength, g_virtualWorkingDir.data, length);
if (err)
UBA_ASSERTF(false, L"wcstombs_s failed for string '%s' with error code: %u", g_virtualWorkingDir.data, err);
DEBUG_LOG_DETOURED(L"GetCurrentDirectoryA", L"(%hs)", lpBuffer);
return DWORD(length);
}
void Shared_SetCurrentDirectory(const wchar_t* workingDirBuffer)
{
u32 charLen = 0;
wchar_t temp[256];
FixPath2(workingDirBuffer, g_virtualWorkingDir.data, g_virtualWorkingDir.count, temp, sizeof_array(temp), &charLen);
g_virtualWorkingDir.Clear().Append(temp).Append('\\');
}
BOOL Detoured_SetCurrentDirectoryW(LPCWSTR lpPathName)
{
DETOURED_CALL(SetCurrentDirectoryW);
Shared_SetCurrentDirectory(lpPathName);
if (g_runningRemote)
{
DEBUG_LOG_DETOURED(L"SetCurrentDirectoryW", L"%ls", lpPathName);
return true;
}
DEBUG_LOG_TRUE(L"SetCurrentDirectoryW", L"%ls", lpPathName);
return True_SetCurrentDirectoryW(lpPathName);
}
BOOL Detoured_DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions)
{
DETOURED_CALL(DuplicateHandle);
if (hSourceHandle == PseudoHandle || !isDetouredHandle(hSourceHandle))
{
auto res = True_DuplicateHandle(hSourceProcessHandle, hSourceHandle, hTargetProcessHandle, lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions);
DEBUG_LOG_TRUE(L"DuplicateHandle", L"%llu to %llu -> %ls", uintptr_t(hSourceHandle), lpTargetHandle ? uintptr_t(*lpTargetHandle) : 0ull, ToString(res));
return res;
}
UBA_ASSERT(hSourceProcessHandle == hTargetProcessHandle);
UBA_ASSERT(!(dwOptions & DUPLICATE_CLOSE_SOURCE));
auto& dh = asDetouredHandle(hSourceHandle);
HANDLE trueHandle = dh.trueHandle;
HANDLE targetHandle = INVALID_HANDLE_VALUE;
BOOL res = TRUE;
if (trueHandle != INVALID_HANDLE_VALUE)
res = True_DuplicateHandle(hSourceProcessHandle, trueHandle, hTargetProcessHandle, &targetHandle, dwDesiredAccess, bInheritHandle, dwOptions);
else
SetLastError(ERROR_SUCCESS);
auto newDh = new DetouredHandle(dh.type);
newDh->trueHandle = targetHandle;
newDh->dirTableOffset = dh.dirTableOffset;
newDh->fileObject = dh.fileObject;
if (FileObject* fo = dh.fileObject)
{
//UBA_ASSERT(!fo->fileInfo->isFileMap);
InterlockedIncrement(&fo->refCount);
}
*lpTargetHandle = makeDetouredHandle(newDh);
DEBUG_LOG_DETOURED(L"DuplicateHandle", L"%llu to %llu -> %ls", uintptr_t(hSourceHandle), uintptr_t(*lpTargetHandle), ToString(res));
return res;
}
HANDLE Detoured_CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
DETOURED_CALL(CreateFileW);
DEBUG_LOG_DETOURED(L"CreateFileW", L"%ls", lpFileName);
u32 disallowDetour = !lpFileName || Equals(lpFileName, L"nul");
t_disallowDetour += disallowDetour;
t_createFileFileName = lpFileName;
HANDLE h = True_CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
t_createFileFileName = nullptr;
t_disallowDetour -= disallowDetour;
return h;
}
// Calls directly to NtCreateFile so need to be detoured
HANDLE Detoured_CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
DETOURED_CALL(CreateFileA);
DEBUG_LOG_TRUE(L"CreateFileA", L"%hs", lpFileName);
StringBuffer<> fileName;
fileName.Appendf(L"%hs", lpFileName);
u32 disallowDetour = fileName.Equals(L"nul");
t_disallowDetour += disallowDetour;
t_createFileFileName = fileName.data;
HANDLE h = True_CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
t_createFileFileName = nullptr;
t_disallowDetour -= disallowDetour;
return h;
}
BOOL Detoured_CreateDirectoryW(LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
DETOURED_CALL(CreateDirectoryW);
StringBuffer<> pathName;
FixPath(pathName, lpPathName);
if (pathName.StartsWith(g_systemTemp.data))
{
SuppressCreateFileDetourScope s;
BOOL res = True_CreateDirectoryW(lpPathName, lpSecurityAttributes);
DEBUG_LOG_TRUE(L"CreateDirectoryW", L"%ls -> %ls", lpPathName, ToString(res));
return res;
}
u32 directoryTableSize;
BOOL res;
u32 errorCode = 0;
StringKey pathNameKey = ToStringKeyLower(pathName);
{
RPC_MESSAGE(CreateDirectory, createFile)
writer.WriteStringKey(pathNameKey);
writer.WriteString(pathName);
writer.Flush();
BinaryReader reader;
res = reader.ReadBool();
errorCode = reader.ReadU32();
directoryTableSize = reader.ReadU32();
}
g_directoryTable.ParseDirectoryTable(directoryTableSize);
SetLastError(errorCode);
DEBUG_LOG_DETOURED(L"CreateDirectoryW", L"%ls -> %ls (%u)", lpPathName, ToString(res), errorCode);
return res;
}
BOOL Detoured_RemoveDirectoryW(LPCWSTR lpPathName)
{
DETOURED_CALL(RemoveDirectoryW);
StringBuffer<> pathName;
FixPath(pathName, lpPathName);
if (pathName.StartsWith(g_systemTemp.data))
{
SuppressCreateFileDetourScope s; // TODO: Revisit this.. will not work remotely
BOOL res = True_RemoveDirectoryW(lpPathName);
DEBUG_LOG_TRUE(L"RemoveDirectoryW", L"%ls -> %ls", lpPathName, ToString(res));
return res;
}
u32 directoryTableSize;
BOOL res;
u32 errorCode = 0;
StringKey pathNameKey = ToStringKeyLower(pathName);
{
RPC_MESSAGE(RemoveDirectory, deleteFile)
writer.WriteStringKey(pathNameKey);
writer.WriteString(pathName);
writer.Flush();
BinaryReader reader;
res = reader.ReadBool();
errorCode = reader.ReadU32();
directoryTableSize = reader.ReadU32();
}
g_directoryTable.ParseDirectoryTable(directoryTableSize);
SetLastError(errorCode);
DEBUG_LOG_DETOURED(L"RemoveDirectoryW", L"%ls -> %ls (%u)", lpPathName, ToString(res), errorCode);
return res;
}
BOOL Detoured_LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh)
{
DETOURED_CALL(LockFile);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
trueHandle = dh.trueHandle;
UBA_ASSERT(trueHandle != INVALID_HANDLE_VALUE);
}
DEBUG_LOG_TRUE(L"LockFile", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return True_LockFile(trueHandle, dwFileOffsetLow, dwFileOffsetHigh, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
}
BOOL Detoured_LockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped)
{
DETOURED_CALL(LockFileEx);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
trueHandle = dh.trueHandle;
UBA_ASSERT(trueHandle != INVALID_HANDLE_VALUE);
}
DEBUG_LOG_TRUE(L"LockFileEx", L"%llu %ls", uintptr_t(hFile), HandleToName(hFile));
return True_LockFileEx(trueHandle, dwFlags, dwReserved, nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh, lpOverlapped);
}
BOOL Detoured_UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh)
{
DETOURED_CALL(UnlockFile);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
trueHandle = dh.trueHandle;
UBA_ASSERT(trueHandle != INVALID_HANDLE_VALUE);
}
DEBUG_LOG_TRUE(L"UnlockFile", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return True_UnlockFile(trueHandle, dwFileOffsetLow, dwFileOffsetHigh, nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
}
BOOL Detoured_UnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped)
{
DETOURED_CALL(UnlockFileEx);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
trueHandle = dh.trueHandle;
UBA_ASSERT(trueHandle != INVALID_HANDLE_VALUE);
}
DEBUG_LOG_TRUE(L"UnlockFile", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return True_UnlockFileEx(trueHandle, dwReserved, nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh, lpOverlapped);
}
BOOL Detoured_ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
{
DETOURED_CALL(ReadFile);
HANDLE trueHandle = hFile;
UBA_ASSERT(!isListDirectoryHandle(hFile));
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
if (dh.type == HandleType_StdIn) // HACK HACK
{
UBA_ASSERTF(false, L"Trying to read input from stdin while application is running in a way console can not be accessed");
memcpy(lpBuffer, "Y\r\n", 3);
*lpNumberOfBytesRead = 3;
return TRUE;
}
auto& fo = *dh.fileObject;
fo.wasUsed = true;
FileInfo& fi = *fo.fileInfo;
if (fi.isFileMap || fi.memoryFile)
{
// TODO: Handle lpOverlapped - If a read happen and there is 0 left it should return 0 with SetLastError(ERROR_HANDLE_EOF)
if (!EnsureMapped(dh))
{
DEBUG_LOG_DETOURED(L"ReadFile", L"%llu %u (%ls) -> FAILED TO MAP", uintptr_t(hFile), nNumberOfBytesToRead, HandleToName(hFile));
return FALSE;
}
UBA_ASSERTF(fi.fileMapMem || !fi.memoryFile->isThrowAway, TC("Trying to read throw-away file %s"), HandleToName(hFile));
u8* mem = fi.fileMapMem ? fi.fileMapMem : fi.memoryFile->baseAddress;
u64 size = fi.fileMapMem ? fi.fileMapMemSize : fi.memoryFile->writtenSize;
UBA_ASSERTF(dh.pos <= size, L"Filepointer is higher than size of file (pointer: %llu, size: %llu) (%ls)", dh.pos, size, HandleToName(hFile));
u64 leftToRead = size - dh.pos;
if (nNumberOfBytesToRead > leftToRead)
nNumberOfBytesToRead = (DWORD)leftToRead;
if (nNumberOfBytesToRead)
memcpy(lpBuffer, mem + dh.pos, nNumberOfBytesToRead);
dh.pos += nNumberOfBytesToRead;
if (lpNumberOfBytesRead)
*lpNumberOfBytesRead = nNumberOfBytesToRead;
SetLastError(ERROR_SUCCESS);
DEBUG_LOG_DETOURED(L"ReadFile", L"%llu %u (%ls) -> Success", uintptr_t(hFile), nNumberOfBytesToRead, HandleToName(hFile));
return TRUE;
}
UBA_ASSERT(dh.trueHandle != INVALID_HANDLE_VALUE);
trueHandle = dh.trueHandle;
}
TimerScope ts(g_kernelStats.readFile);
BOOL res = True_ReadFile(trueHandle, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);
DEBUG_LOG_TRUE(L"ReadFile", L"%llu %u/%u (%ls) -> %ls", uintptr_t(hFile), lpNumberOfBytesRead ? *lpNumberOfBytesRead : ~0u, nNumberOfBytesToRead, HandleToName(hFile), ToString(res));
return res;
}
BOOL Detoured_WriteConsoleA(HANDLE hConsoleOutput, const VOID* lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved)
{
DETOURED_CALL(WriteConsoleA);
//DEBUG_LOG_DETOURED(L"WriteConsoleA", L"(%hs)", (char*)lpBuffer); // Too much spam
Shared_WriteConsole((const char*)lpBuffer, nNumberOfCharsToWrite, false);
if (lpNumberOfCharsWritten)
*lpNumberOfCharsWritten = nNumberOfCharsToWrite;
return TRUE;//True_WriteConsoleA(hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten, lpReserved);
}
BOOL Detoured_WriteConsoleW(HANDLE hConsoleOutput, const VOID* lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved)
{
DETOURED_CALL(WriteConsoleW);
//DEBUG_LOG_DETOURED(L"WriteConsoleW", L"(%s)", (const wchar_t*)lpBuffer); // Too much spam
Shared_WriteConsole((const wchar_t*)lpBuffer, nNumberOfCharsToWrite, false);
if (lpNumberOfCharsWritten)
*lpNumberOfCharsWritten = nNumberOfCharsToWrite;
return TRUE;//True_WriteConsoleW(hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten, lpReserved);
}
BOOL Detoured_ReadConsoleW(HANDLE hConsoleInput, LPVOID lpBuffer, DWORD nNumberOfCharsToRead, LPDWORD lpNumberOfCharsRead, PCONSOLE_READCONSOLE_CONTROL pInputControl)
{
DETOURED_CALL(ReadConsoleW);
#if UBA_DEBUG
Rpc_WriteLogf(L"WARNING Got call to ReadConsoleW.. this is not handled by Uba yet"); // seems like wine's cmd.exe likes calling this
#endif
return 0;
//return True_ReadConsoleW(hConsoleInput, lpBuffer, nNumberOfCharsToRead, lpNumberOfCharsRead, pInputControl);
}
UINT Detoured_GetDriveTypeW(LPCWSTR lpRootPathName)
{
DETOURED_CALL(GetDriveTypeW);
if (g_runningRemote || IsVfsEnabled())
{
DEBUG_LOG_DETOURED(L"GetDriveType", L"%ls -> DRIVE_FIXED", lpRootPathName);
return DRIVE_FIXED;
}
DEBUG_LOG_TRUE(L"GetDriveType", L"%ls", lpRootPathName);
SuppressCreateFileDetourScope s; // Convenient since it will call NtQueryVolumeInformationFile
return True_GetDriveTypeW(lpRootPathName);
}
BOOL Detoured_GetDiskFreeSpaceExW(LPCWSTR lpDirectoryName, PULARGE_INTEGER lpFreeBytesAvailableToCaller, PULARGE_INTEGER lpTotalNumberOfBytes, PULARGE_INTEGER lpTotalNumberOfFreeBytes)
{
DETOURED_CALL(GetDiskFreeSpaceExW);
StringBuffer<MaxPath> path;
if (g_runningRemote)
{
if (lpDirectoryName)
{
UBA_ASSERT(lpDirectoryName[1] == ':');
if (ToLower(lpDirectoryName[0]) == ToLower(g_virtualWorkingDir[0]))
{
if (lpDirectoryName[3] == 0)
path.Append(g_exeDir.data, 3);
else
path.Append(g_exeDir);
lpDirectoryName = path.data;
}
}
}
DEBUG_LOG_TRUE(L"GetDiskFreeSpaceExW", L"%ls", lpDirectoryName);
SuppressCreateFileDetourScope s; // Convenient since it will call NtQueryVolumeInformationFile
return True_GetDiskFreeSpaceExW(lpDirectoryName, lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes);
}
BOOL Detoured_GetVolumeInformationByHandleW(HANDLE hFile, LPWSTR lpVolumeNameBuffer, DWORD nVolumeNameSize, LPDWORD lpVolumeSerialNumber, LPDWORD lpMaximumComponentLength, LPDWORD lpFileSystemFlags, LPWSTR lpFileSystemNameBuffer, DWORD nFileSystemNameSize)
{
DETOURED_CALL(GetVolumeInformationByHandleW);
HANDLE trueHandle = hFile;
u32 entryOffset = ~u32(0);
if (isDetouredHandle(hFile))
{
DetouredHandle& dh = asDetouredHandle(hFile);
trueHandle = dh.trueHandle;
entryOffset = dh.dirTableOffset;
UBA_ASSERT(entryOffset != ~u32(0) || trueHandle != INVALID_HANDLE_VALUE);
}
else if (isListDirectoryHandle(hFile))
{
auto& listHandle = asListDirectoryHandle(hFile);
if (listHandle.dir.tableOffset != InvalidTableOffset)
entryOffset = listHandle.dir.tableOffset | 0x80000000;
else
UBA_ASSERT(false);
trueHandle = INVALID_HANDLE_VALUE;
}
if (entryOffset != ~u32(0))
{
UBA_ASSERT(!lpVolumeNameBuffer);
UBA_ASSERT(!lpMaximumComponentLength);
UBA_ASSERT(!lpFileSystemFlags);
DirectoryTable::EntryInformation entryInfo;
g_directoryTable.GetEntryInformation(entryInfo, entryOffset);
if (lpVolumeSerialNumber)
*lpVolumeSerialNumber = entryInfo.volumeSerial;
if (lpFileSystemNameBuffer)
{
UBA_ASSERT(nFileSystemNameSize > 5);
wcscpy_s(lpFileSystemNameBuffer, nFileSystemNameSize, L"NTFS"); // TODO: Not everyone has NTFS?
}
SetLastError(ERROR_SUCCESS);
DEBUG_LOG_DETOURED(L"GetVolumeInformationByHandleW", L"%llu (Serial: %u) (%ls) -> Success", uintptr_t(hFile), entryInfo.volumeSerial, HandleToName(hFile));
return true;
}
bool res = True_GetVolumeInformationByHandleW(trueHandle, lpVolumeNameBuffer, nVolumeNameSize, lpVolumeSerialNumber, lpMaximumComponentLength, lpFileSystemFlags, lpFileSystemNameBuffer, nFileSystemNameSize);
return res;
}
BOOL Detoured_GetVolumeInformationW(LPCWSTR lpRootPathName, LPWSTR lpVolumeNameBuffer, DWORD nVolumeNameSize, LPDWORD lpVolumeSerialNumber, LPDWORD lpMaximumComponentLength, LPDWORD lpFileSystemFlags, LPWSTR lpFileSystemNameBuffer, DWORD nFileSystemNameSize)
{
DETOURED_CALL(GetVolumeInformationW);
if (g_runningRemote)
{
if (lpVolumeSerialNumber)
*lpVolumeSerialNumber = lpRootPathName[0]; // Let's see if this works, LOL
if (lpMaximumComponentLength)
*lpMaximumComponentLength = 255; // TODO: Need to fix this
//UBA_ASSERT(!lpVolumeNameBuffer);
//UBA_ASSERT(!lpMaximumComponentLength);
UBA_ASSERT(!lpFileSystemFlags);
if (nFileSystemNameSize)
wcscpy_s(lpFileSystemNameBuffer, nFileSystemNameSize, L"NTFS"); // TODO: Not everyone has NTFS?
SetLastError(ERROR_SUCCESS);
DEBUG_LOG_DETOURED(L"GetVolumeInformationW", L"%ls", lpRootPathName);
return true;
}
SuppressCreateFileDetourScope s;
auto res = True_GetVolumeInformationW(lpRootPathName, lpVolumeNameBuffer, nVolumeNameSize, lpVolumeSerialNumber, lpMaximumComponentLength, lpFileSystemFlags, lpFileSystemNameBuffer, nFileSystemNameSize);
return res;
}
LPVOID Detoured_VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
{
DETOURED_CALL(VirtualAlloc);
// Special cl.exe handling
if (lpAddress != nullptr && g_clExeBaseReservedMemory != nullptr && lpAddress >= g_clExeBaseReservedMemory && uintptr_t(lpAddress) < uintptr_t(g_clExeBaseReservedMemory) + g_clExeBaseAddressSize)
{
DEBUG_LOG(L"VirtualAlloc releasing cl.exe reserved memory at 0x%llx", uintptr_t(lpAddress));
VirtualFree(g_clExeBaseReservedMemory, 0, MEM_RELEASE);
g_clExeBaseReservedMemory = nullptr;
}
u32 counter = 0;
do
{
void* res = True_VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
if (res)
return res;
if (!(flAllocationType & MEM_COMMIT))
return res;
DWORD error = GetLastError();
if (error != ERROR_NOT_ENOUGH_MEMORY && error != ERROR_COMMITMENT_LIMIT)
return res;
StringBuffer<128> reason;
reason.Append(TCV("VirtualAlloc ")).AppendValue(dwSize);
Rpc_AllocFailed(reason.data, error);
++counter;
} while (counter <= 10);
return nullptr;
}
BOOL Detoured_GetQueuedCompletionStatusEx(HANDLE CompletionPort, LPOVERLAPPED_ENTRY lpCompletionPortEntries, ULONG ulCount, PULONG ulNumEntriesRemoved, DWORD dwMilliseconds, BOOL fAlertable)
{
DETOURED_CALL(GetQueuedCompletionStatusEx);
DEBUG_LOG_TRUE(L"GetQueuedCompletionStatusEx", L"%llu (Timeout: %ums)", u64(CompletionPort), dwMilliseconds);
bool res = True_GetQueuedCompletionStatusEx(CompletionPort, lpCompletionPortEntries, ulCount, ulNumEntriesRemoved, dwMilliseconds, fAlertable);
if (res)
Rpc_UpdateTables(); // This is a bit ugly but we know this is how msbuild worker nodes sync with each other..
return res;
}
DWORD Detoured_GetSecurityInfo(HANDLE handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, PSID* ppsidOwner, PSID* ppsidGroup, PACL* ppDacl, PACL* ppSacl, PSECURITY_DESCRIPTOR* ppSecurityDescriptor)
{
DETOURED_CALL(GetSecurityInfo);
if (isDetouredHandle(handle))
{
handle = asDetouredHandle(handle).trueHandle;
UBA_ASSERTF(handle != INVALID_HANDLE_VALUE, L"GetSecurityInfo");
}
DEBUG_LOG_TRUE(L"GetSecurityInfo", L"");
return True_GetSecurityInfo(handle, ObjectType, SecurityInfo, ppsidOwner, ppsidGroup, ppDacl, ppSacl, ppSecurityDescriptor);
}
StringBuffer<32 * 1024> g_stdFile;
ReaderWriterLock& g_stdFileLock = *new ReaderWriterLock();
void WriteStdFile(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, bool isError)
{
if (!g_echoOn || g_suppressLogging)
return;
SCOPED_WRITE_LOCK(g_stdFileLock, lock);
u32 start = 0;
u32 i = 0;
auto bufferStr = (const char*)lpBuffer;
while (i != nNumberOfBytesToWrite)
{
if (bufferStr[i] == '\n')
{
int len = i - start;
if (len > 0 && bufferStr[i - 1] == '\r')
--len;
if (len)
g_stdFile.Appendf(L"%.*hs", len, bufferStr + start);
Rpc_WriteLog(g_stdFile.data, g_stdFile.count, false, isError);
g_stdFile.Clear();
start = i + 1;
}
++i;
}
if (u32 left = nNumberOfBytesToWrite - start)
g_stdFile.Appendf(L"%.*hs", left, bufferStr + start);
}
BOOL Detoured_WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
DETOURED_CALL(WriteFile);
HANDLE trueHandle = hFile;
UBA_ASSERT(!isListDirectoryHandle(hFile));
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
auto& fo = *dh.fileObject;
if (dh.type >= HandleType_StdErr)
{
if (dh.type != HandleType_StdIn)
{
//DEBUG_LOG_DETOURED(L"WriteStdFile1", L"%llu", uintptr_t(hFile));
WriteStdFile(lpBuffer, nNumberOfBytesToWrite, dh.type == HandleType_StdErr);
}
*lpNumberOfBytesWritten = nNumberOfBytesToWrite;
SetLastError(ERROR_SUCCESS);
return true;
}
auto& fi = *fo.fileInfo;
if (MemoryFile* mf = fi.memoryFile)
{
if (lpOverlapped)
{
u64 offset = ToLargeInteger(lpOverlapped->OffsetHigh, lpOverlapped->Offset).QuadPart;
u64 writtenSize = offset + nNumberOfBytesToWrite;
{
SCOPED_WRITE_LOCK(mf->lock, lock);
mf->EnsureCommitted(dh, writtenSize);
mf->writtenSize = Max(mf->writtenSize, writtenSize);
mf->isReported = false;
}
memcpy(mf->baseAddress + offset, lpBuffer, nNumberOfBytesToWrite);
//dh.pos = writtenSize;
if (lpOverlapped->hEvent)
SetEvent(lpOverlapped->hEvent);
}
else
mf->Write(dh, lpBuffer, nNumberOfBytesToWrite);
*lpNumberOfBytesWritten = nNumberOfBytesToWrite;
SetLastError(ERROR_SUCCESS);
DEBUG_LOG_DETOURED(L"WriteFile", L"(MEMORY)%s %llu (%ls) ToWrite: %u -> Success", (lpOverlapped ? L" OVERLAPPED" : L""), uintptr_t(hFile), HandleToName(hFile), nNumberOfBytesToWrite);
return TRUE;
}
UBA_ASSERTF(!fi.isFileMap, L"Trying to write to file %ls which is a filemap. This is not supported\n", HandleToName(hFile));
UBA_ASSERTF(dh.trueHandle != INVALID_HANDLE_VALUE, L"Trying to write to file %ls which does not have a valid handle\n", HandleToName(hFile));
trueHandle = dh.trueHandle;
}
else if (hFile == PseudoHandle)
{
DEBUG_LOG_DETOURED(L"WriteFile", L"(PseudoHandle) -> Success");
SetLastError(ERROR_SUCCESS);
return true;
}
else if (hFile == g_stdHandle[1] || hFile == g_stdHandle[0])
{
//DEBUG_LOG_DETOURED(L"WriteStdFile2", L"%llu", uintptr_t(hFile));
WriteStdFile(lpBuffer, nNumberOfBytesToWrite, hFile == g_stdHandle[0]);
*lpNumberOfBytesWritten = nNumberOfBytesToWrite;
SetLastError(ERROR_SUCCESS);
return true;
//if (GetFileType(trueHandle) != FILE_TYPE_CHAR )
//return True_WriteFile(trueHandle, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
}
DWORD temp;
if (!lpNumberOfBytesWritten)
lpNumberOfBytesWritten = &temp;
TimerScope ts(g_kernelStats.writeFile);
BOOL res = True_WriteFile(trueHandle, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
g_kernelStats.writeFile.bytes += *lpNumberOfBytesWritten;
DEBUG_LOG_TRUE(L"WriteFile", L"%llu (%ls) -> %ls", uintptr_t(hFile), HandleToName(hFile), ToString(res));
return res;
}
BOOL Detoured_WriteFileEx(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
DETOURED_CALL(WriteFileEx);
//DEBUG_LOG_TRUE(L"WriteFileEx", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
//UBA_ASSERT(!isDetouredHandle(hFile));
UBA_ASSERT(isDetouredHandle(hFile));
DetouredHandle& h = asDetouredHandle(hFile);
UBA_ASSERT(h.trueHandle != INVALID_HANDLE_VALUE);
TimerScope ts(g_kernelStats.writeFile);
return True_WriteFileEx(h.trueHandle, lpBuffer, nNumberOfBytesToWrite, lpOverlapped, lpCompletionRoutine);
}
BOOL Detoured_FlushFileBuffers(HANDLE hFile)
{
DETOURED_CALL(FlushFileBuffers);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
DetouredHandle& dh = asDetouredHandle(hFile);
FileInfo& fi = *dh.fileObject->fileInfo;
if (fi.memoryFile)
{
DEBUG_LOG_DETOURED(L"FlushFileBuffers", L"%llu (%ls) -> Success", uintptr_t(hFile), HandleToName(hFile));
SetLastError(ERROR_SUCCESS);
return true;
}
trueHandle = dh.trueHandle;
UBA_ASSERT(trueHandle != INVALID_HANDLE_VALUE);
}
BOOL res = True_FlushFileBuffers(trueHandle);
DEBUG_LOG_TRUE(L"FlushFileBuffers", L"%llu (%ls) -> %ls", uintptr_t(hFile), HandleToName(hFile), ToString(res));
return res;
}
DWORD Detoured_GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
{
DETOURED_CALL(GetFileSize);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
FileInfo& fi = *dh.fileObject->fileInfo;
if (fi.size != InvalidValue)
{
LARGE_INTEGER li = ToLargeInteger(fi.size);
if (lpFileSizeHigh)
*lpFileSizeHigh = li.HighPart;
DEBUG_LOG_DETOURED(L"GetFileSize", L"%llu (%ls) -> %u", uintptr_t(hFile), HandleToName(hFile), li.LowPart);
SetLastError(ERROR_SUCCESS);
return li.LowPart;
}
if (fi.memoryFile)
{
LARGE_INTEGER li = ToLargeInteger(fi.memoryFile->writtenSize);
if (lpFileSizeHigh)
*lpFileSizeHigh = li.HighPart;
DEBUG_LOG_DETOURED(L"GetFileSize", L"%llu (%ls) -> %u", uintptr_t(hFile), HandleToName(hFile), li.LowPart);
SetLastError(ERROR_SUCCESS);
return li.LowPart;
}
trueHandle = dh.trueHandle;
UBA_ASSERT(trueHandle != INVALID_HANDLE_VALUE);
}
DEBUG_LOG_TRUE(L"GetFileSize", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return True_GetFileSize(trueHandle, lpFileSizeHigh); // Calls NtQueryInformationFile
}
DWORD Detoured_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize)
{
DETOURED_CALL(GetFileSizeEx);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
DetouredHandle& dh = asDetouredHandle(hFile);
FileInfo& fi = *dh.fileObject->fileInfo;
if (fi.size != InvalidValue)
{
*lpFileSize = ToLargeInteger(fi.size);
SetLastError(ERROR_SUCCESS);
DEBUG_LOG_DETOURED(L"GetFileSizeEx", L"%llu (%ls) (Size:%llu) -> 1", uintptr_t(hFile), HandleToName(hFile), fi.size);
return 1;
}
if (fi.memoryFile)
{
*lpFileSize = ToLargeInteger(fi.memoryFile->writtenSize);
SetLastError(ERROR_SUCCESS);
DEBUG_LOG_DETOURED(L"GetFileSizeEx", L"%llu (%ls) (Size:%llu) -> 1", uintptr_t(hFile), HandleToName(hFile), fi.memoryFile->writtenSize);
return 1;
}
trueHandle = dh.trueHandle;
if (trueHandle == INVALID_HANDLE_VALUE)
{
u32 entryOffset = dh.dirTableOffset;
if (entryOffset != ~u32(0))
{
DirectoryTable::EntryInformation entryInfo;
g_directoryTable.GetEntryInformation(entryInfo, entryOffset);
*lpFileSize = ToLargeInteger(entryInfo.size);
SetLastError(ERROR_SUCCESS);
DEBUG_LOG_DETOURED(L"GetFileSizeEx", L"%llu (%ls) (Size:%llu) -> 1", uintptr_t(hFile), HandleToName(hFile), entryInfo.size);
return 1;
}
UBA_ASSERTF(false, L"GetFileSizeEx (%s)", HandleToName(hFile));
}
}
DEBUG_LOG_TRUE(L"GetFileSizeEx", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return True_GetFileSizeEx(trueHandle, lpFileSize); // This ends up in Detoured_NtQueryInformationFile
}
DWORD Detoured_SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod)
{
DETOURED_CALL(SetFilePointer);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
FileInfo& fi = *dh.fileObject->fileInfo;
if (fi.memoryFile || fi.isFileMap)
{
LARGE_INTEGER liDistanceToMove;
liDistanceToMove.LowPart = 0;
liDistanceToMove.HighPart = lpDistanceToMoveHigh ? *lpDistanceToMoveHigh : 0;
liDistanceToMove.QuadPart += lDistanceToMove;
if (dwMoveMethod == FILE_BEGIN)
dh.pos = liDistanceToMove.QuadPart;
else if (dwMoveMethod == FILE_CURRENT)
dh.pos += liDistanceToMove.QuadPart;
else if (dwMoveMethod == FILE_END)
{
u64 size = 0;
if (fi.memoryFile)
size = fi.memoryFile->writtenSize;
else
{
//UBA_ASSERT(fi.fileMapMemEnd);
size = fi.size;// fi.fileMapMemSize;
}
dh.pos = Max(0ll, (LONGLONG)size + liDistanceToMove.QuadPart);
}
DEBUG_LOG_DETOURED(L"SetFilePointer", L"%llu %lli %u (%ls) -> %u", uintptr_t(hFile), liDistanceToMove.QuadPart, dwMoveMethod, HandleToName(hFile), DWORD(dh.pos));
SetLastError(ERROR_SUCCESS);
return DWORD(dh.pos);
}
trueHandle = dh.trueHandle;
UBA_ASSERT(trueHandle != INVALID_HANDLE_VALUE);
}
DEBUG_LOG_TRUE(L"SetFilePointer", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return True_SetFilePointer(trueHandle, lDistanceToMove, lpDistanceToMoveHigh, dwMoveMethod);
}
DWORD Detoured_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod)
{
DETOURED_CALL(SetFilePointerEx);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
DetouredHandle& dh = asDetouredHandle(hFile);
if (dh.type >= HandleType_StdErr)
{
if (lpNewFilePointer)
*lpNewFilePointer = ToLargeInteger(0);
// TODO: What should we do with this?
SetLastError(ERROR_SUCCESS);
DEBUG_LOG_DETOURED(L"SetFilePointerEx", L"%llu %lli %u (%ls) -> Success", uintptr_t(hFile), liDistanceToMove.QuadPart, dwMoveMethod, HandleToName(hFile));
return TRUE;
}
FileInfo& fi = *dh.fileObject->fileInfo;
if (fi.memoryFile || fi.isFileMap)
{
if (dwMoveMethod == FILE_BEGIN)
dh.pos = liDistanceToMove.QuadPart;
else if (dwMoveMethod == FILE_CURRENT)
dh.pos += liDistanceToMove.QuadPart;
else if (dwMoveMethod == FILE_END)
{
u64 size = 0;
if (fi.memoryFile)
size = fi.memoryFile->writtenSize;
else if (fi.fileMapMem)
size = fi.fileMapMemSize;
else
size = fi.size;
dh.pos = Max(0ll, (LONGLONG)size + liDistanceToMove.QuadPart);
}
if (lpNewFilePointer)
*lpNewFilePointer = ToLargeInteger(dh.pos);
SetLastError(ERROR_SUCCESS);
DEBUG_LOG_DETOURED(L"SetFilePointerEx", L"%llu %lli %u (%ls) -> Success", uintptr_t(hFile), liDistanceToMove.QuadPart, dwMoveMethod, HandleToName(hFile));
return TRUE;
}
trueHandle = dh.trueHandle;
UBA_ASSERT(trueHandle != INVALID_HANDLE_VALUE);
}
DEBUG_LOG_TRUE(L"SetFilePointerEx", L"%llu %lli %u (%ls)", uintptr_t(hFile), liDistanceToMove.QuadPart, dwMoveMethod, HandleToName(hFile));
return True_SetFilePointerEx(trueHandle, liDistanceToMove, lpNewFilePointer, dwMoveMethod); // This ends up in NtSetInformationFile
}
BOOL Detoured_SetEndOfFile(HANDLE hFile)
{
DETOURED_CALL(SetEndOfFile);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
FileInfo& fi = *dh.fileObject->fileInfo;
if (MemoryFile* mf = fi.memoryFile)
{
DEBUG_LOG_DETOURED(L"SetEndOfFile (MEMORY)", L"%llu (%ls) -> Success", uintptr_t(hFile), HandleToName(hFile));
mf->writtenSize = dh.pos;
mf->isReported = false;
mf->EnsureCommitted(dh, mf->writtenSize);
SetLastError(ERROR_SUCCESS);
return true;
}
trueHandle = dh.trueHandle;
UBA_ASSERT(trueHandle != INVALID_HANDLE_VALUE);
}
BOOL res = True_SetEndOfFile(trueHandle);
DEBUG_LOG_TRUE(L"SetEndOfFile", L"%llu (%ls) -> %ls", uintptr_t(hFile), HandleToName(hFile), ToString(res));
return res;
}
BOOL Detoured_SetFileTime(HANDLE hFile, const FILETIME* lpCreationTime, const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
{
DETOURED_CALL(SetFileTime);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
if (!lpCreationTime && !lpLastWriteTime)
{
DEBUG_LOG_DETOURED(L"SetFileTime", L"%llu IGNORE (%ls)", uintptr_t(hFile), HandleToName(hFile));
return TRUE;
}
DetouredHandle& dh = asDetouredHandle(hFile);
trueHandle = dh.trueHandle;
UBA_ASSERTF(trueHandle != INVALID_HANDLE_VALUE, L"Want to SetFileTime on %ls which has no true file handle set", HandleToName(hFile));
}
DEBUG_LOG_TRUE(L"SetFileTime", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return True_SetFileTime(trueHandle, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
}
BOOL Detoured_GetFileTime(HANDLE hFile, LPFILETIME lpCreationTime, LPFILETIME lpLastAccessTime, LPFILETIME lpLastWriteTime)
{
DETOURED_CALL(GetFileTime);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
DetouredHandle& dh = asDetouredHandle(hFile);
u32 entryOffset = dh.dirTableOffset;
if (entryOffset != ~u32(0))
{
DirectoryTable::EntryInformation entryInfo;
g_directoryTable.GetEntryInformation(entryInfo, entryOffset);
if (lpLastWriteTime)
*(u64*)lpLastWriteTime = entryInfo.lastWrite;
if (lpCreationTime)
*(u64*)lpCreationTime = entryInfo.lastWrite;
if (lpLastAccessTime)
*(u64*)lpLastAccessTime = entryInfo.lastWrite;
//UBA_ASSERT(!lpLastAccessTime);
DEBUG_LOG_DETOURED(L"GetFileTime", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return TRUE;
}
trueHandle = asDetouredHandle(hFile).trueHandle;
UBA_ASSERT(trueHandle != INVALID_HANDLE_VALUE);
}
DEBUG_LOG_TRUE(L"GetFileTime", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return True_GetFileTime(trueHandle, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
}
DWORD Detoured_GetFileType(HANDLE hFile)
{
DETOURED_CALL(GetFileType);
if (isDetouredHandle(hFile))
{
DetouredHandle& dh = asDetouredHandle(hFile);
SetLastError(ERROR_SUCCESS);
if (dh.type >= HandleType_StdErr)
{
DEBUG_LOG_DETOURED(L"GetFileType", L"%llu (%ls) -> FILE_TYPE_CHAR", uintptr_t(hFile), HandleToName(hFile));
return FILE_TYPE_CHAR;
}
UBA_ASSERTF(dh.type == HandleType_File, L"HandleType: %u", dh.type);
DEBUG_LOG_DETOURED(L"GetFileType", L"%llu (%ls) -> FILE_TYPE_DISK", uintptr_t(hFile), HandleToName(hFile));
return FILE_TYPE_DISK;
}
if (isListDirectoryHandle(hFile))
{
DEBUG_LOG_DETOURED(L"GetFileType", L"%llu (%ls) -> FILE_TYPE_DISK", uintptr_t(hFile), HandleToName(hFile));
SetLastError(ERROR_SUCCESS);
return FILE_TYPE_DISK;
}
else if (hFile == PseudoHandle)
{
DEBUG_LOG_DETOURED(L"GetFileType", L"PseudoHandle -> FILE_TYPE_CHAR");
SetLastError(ERROR_SUCCESS);
return FILE_TYPE_CHAR;
}
DEBUG_LOG_TRUE(L"GetFileType", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return True_GetFileType(hFile); // Calling NtQueryVolumeInformationFile
}
BOOL Shared_GetFileAttributesExW(const tchar* caller, StringView fileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation, LPCWSTR originalName)
{
FileAttributes attr;
const wchar_t* realName = Shared_GetFileAttributes(attr, fileName);
if (!attr.useCache)
{
DEBUG_LOG_TRUE(L"GetFileAttributesExW", L"(%s) (%s)", caller, originalName);
TimerScope ts(g_kernelStats.getFileInfo);
return True_GetFileAttributesExW(realName, fInfoLevelId, lpFileInformation);
}
SetLastError(attr.lastError);
memcpy(lpFileInformation, &attr.data, sizeof(WIN32_FILE_ATTRIBUTE_DATA));
DEBUG_LOG_DETOURED(caller, L"(%ls) -> %s", originalName, (attr.exists ? L"Exists" : L"NotFound"));
return attr.exists;
}
BOOL Detoured_GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation)
{
DETOURED_CALL(GetFileAttributesExW);
if (!CanDetour(lpFileName) || Contains(lpFileName, L"::")) // Some weird .net path used by dotnet.exe ... ignore for now!
{
//UBA_ASSERTF(!g_runningRemote, TC("GetFileAttributesExW can't handle this path when running remote: %s"), lpFileName);
DEBUG_LOG_TRUE(L"GetFileAttributesExW", L"(%ls)", lpFileName);
TimerScope ts(g_kernelStats.getFileInfo);
return True_GetFileAttributesExW(lpFileName, fInfoLevelId, lpFileInformation);
}
StringBuffer<MaxPath> fixedName;
FixPath(fixedName, lpFileName);
DevirtualizePath(fixedName);
if (!g_rules->CanExist(fixedName.data))
{
SetLastError(ERROR_FILE_NOT_FOUND);
return FALSE;
}
return Shared_GetFileAttributesExW(TC("GetFileAttributesExW"), fixedName, fInfoLevelId, lpFileInformation, lpFileName);
}
DWORD Detoured_GetFileAttributesW(LPCWSTR lpFileName)
{
DETOURED_CALL(GetFileAttributesW);
if (!CanDetour(lpFileName))
{
TimerScope ts(g_kernelStats.getFileInfo);
DWORD res = True_GetFileAttributesW(lpFileName);
DEBUG_LOG_TRUE(L"GetFileAttributesW", L"(NODETOUR) (%ls) -> %u", lpFileName, res);
return res;
}
StringBuffer<MaxPath> fixedPath;
if (!FixPath(fixedPath, lpFileName))
return INVALID_FILE_ATTRIBUTES;
DevirtualizePath(fixedPath);
WIN32_FILE_ATTRIBUTE_DATA data;
if (!Shared_GetFileAttributesExW(TC("GetFileAttributesW"), fixedPath, GetFileExInfoStandard, &data, lpFileName))
return INVALID_FILE_ATTRIBUTES;
return data.dwFileAttributes;
}
BOOL Detoured_SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes)
{
DETOURED_CALL(SetFileAttributesW);
if (KeepInMemory(StringView(lpFileName, u32(wcslen(lpFileName))), true))
{
DEBUG_LOG_DETOURED(L"SetFileAttributesW", L"(%ls) %u", lpFileName, dwFileAttributes);
SetLastError(ERROR_SUCCESS);
return true;
}
DEBUG_LOG_TRUE(L"SetFileAttributesW", L"(%ls) %u", lpFileName, dwFileAttributes);
TimerScope ts(g_kernelStats.setFileInfo);
return True_SetFileAttributesW(lpFileName, dwFileAttributes);
}
DWORD Detoured_GetLongPathNameW(LPCWSTR lpszShortPath, LPWSTR lpszLongPath, DWORD cchBuffer)
{
DETOURED_CALL(GetLongPathNameW);
if (!lpszShortPath)
return Local_GetLongPathNameW(lpszShortPath, lpszLongPath, cchBuffer);
const wchar_t* path = lpszShortPath;
if (wcsncmp(path, L"\\\\?\\", 4) == 0)
path += 4;
bool foundQuestionMark = false;
for (const wchar_t* i = path, *e = i + 4; *i && i!=e; ++i)
foundQuestionMark |= *i == '?';
// TODO: Add support for ~ and "\\?\"
if (!foundQuestionMark)
{
StringBuffer<> fixedName;
FixPath(fixedName, path);
bool success;
{
DEBUG_LOG_DETOURED(L"GetLongPathNameW", L"(%ls)", path);
StringBuffer<> realName(fixedName);
DevirtualizePath(realName);
WIN32_FILE_ATTRIBUTE_DATA data;
success = Shared_GetFileAttributesExW(TC("GetLongPathNameW"), realName, GetFileExInfoStandard, &data, path);
}
DWORD res = 0;
if (success)
{
res = fixedName.count;
memcpy(lpszLongPath, fixedName.data, res * 2 + 2);
}
#if UBA_DEBUG_VALIDATE
if (g_validateFileAccess)
{
if (!wcschr(path, '~') && !wcschr(path, '?'))
{
wchar_t temp[MaxPath];
UBA_ASSERT(cchBuffer <= sizeof_array(temp));
SuppressDetourScope _;
DWORD res2 = True_GetLongPathNameW(path, temp, cchBuffer); (void)res2;
UBA_ASSERT(res == res2);
}
}
#endif
if (!success)
SetLastError(ERROR_FILE_NOT_FOUND);
return res;
}
return Local_GetLongPathNameW(lpszShortPath, lpszLongPath, cchBuffer);
}
DWORD Detoured_GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR* lpFilePart)
{
DETOURED_CALL(GetFullPathNameW);
if (!lpFileName)
{
SetLastError(ERROR_INVALID_NAME);
return 0;
}
StringBuffer<> fullPath;
FixPath(fullPath, lpFileName);
u64 requiredSize = fullPath.count + 1;
if (nBufferLength < requiredSize)
return DWORD(requiredSize);
memcpy(lpBuffer, fullPath.data, requiredSize * 2);
if (lpFilePart)
*lpFilePart = wcsrchr(lpBuffer, '\\') + 1;
auto res = DWORD(fullPath.count);
DEBUG_LOG_DETOURED(L"GetFullPathNameW", L"%ls TO %ls -> %u", lpFileName, fullPath.data, res);
SetLastError(ERROR_SUCCESS);
return res;
}
DWORD Detoured_GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR* lpFilePart)
{
// Is verified that this does NOT always call GetFullPathNameW
DETOURED_CALL(GetFullPathNameA);
if (!lpFileName)
{
SetLastError(ERROR_INVALID_NAME);
return 0;
}
StringBuffer<> temp;
temp.Append(lpFileName);
StringBuffer<> fullPath;
FixPath(fullPath, temp.data);
u64 requiredSize = fullPath.count + 1;
if (nBufferLength < requiredSize)
return DWORD(requiredSize);
fullPath.Parse(lpBuffer, requiredSize);
if (lpFilePart)
*lpFilePart = strrchr(lpBuffer, '\\') + 1;
auto res = DWORD(fullPath.count);
DEBUG_LOG_DETOURED(L"GetFullPathNameA", L"%ls TO %ls -> %u", temp.data, fullPath.data, res);
SetLastError(ERROR_SUCCESS);
return res;
}
BOOL Detoured_GetVolumePathNameW(LPCWSTR lpszFileName, LPWSTR lpszVolumePathName, DWORD cchBufferLength)
{
DETOURED_CALL(GetVolumePathNameW);
//if (lpszFileName[1] == ':')
//{
// memcpy(lpszVolumePathName, lpszFileName, 6);
// lpszVolumePathName[3] = 0;
// return TRUE;
//}
if (g_runningRemote || IsVfsEnabled())
{
UBA_ASSERT(cchBufferLength > 3);
memcpy(lpszVolumePathName, g_virtualWorkingDir.data, 6);
lpszVolumePathName[3] = 0;
DEBUG_LOG_DETOURED(L"GetVolumePathNameW", L"(%ls) -> %s", lpszFileName, lpszVolumePathName);
SetLastError(ERROR_SUCCESS);
return TRUE;
}
DEBUG_LOG_TRUE(L"GetVolumePathNameW", L"(%ls)", lpszFileName);
SuppressCreateFileDetourScope cfs;
auto res = True_GetVolumePathNameW(lpszFileName, lpszVolumePathName, cchBufferLength);
return res;
}
DWORD Shared_GetModuleFileNameInner(const tchar* func, HMODULE hModule, const StringView& moduleName, LPWSTR lpFilename, DWORD nSize)
{
if (nSize <= moduleName.count)
{
if (nSize)
{
memcpy(lpFilename, moduleName.data, nSize * 2);
lpFilename[nSize - 1] = 0;
}
SetLastError(ERROR_INSUFFICIENT_BUFFER);
DEBUG_LOG_DETOURED(func, L"%llu %u INSUFFICIENT BUFFER (%ls) -> %u", uintptr_t(hModule), nSize, moduleName.data, moduleName.count + 1);
return nSize;
}
memcpy(lpFilename, moduleName.data, moduleName.count * 2);
lpFilename[moduleName.count] = 0;
DEBUG_LOG_DETOURED(func, L"%llu %u (%ls) -> %u", uintptr_t(hModule), nSize, lpFilename, moduleName.count);
SetLastError(ERROR_SUCCESS);
return moduleName.count;
}
DWORD Shared_GetModuleFileNameW(const tchar* func, HMODULE hModule, LPWSTR lpFilename, DWORD nSize)
{
// If null we use the virtual application name
if (hModule == NULL)
{
if (g_virtualApplication.EndsWith(L".bat"))
{
DWORD res = True_GetModuleFileNameW(hModule, lpFilename, nSize);
DEBUG_LOG_TRUE(L"GetModuleFileNameW", L"%llu %u (%ls) -> %u", uintptr_t(hModule), nSize, lpFilename, res);
return res;
}
return Shared_GetModuleFileNameInner(func, hModule, g_virtualApplication, lpFilename, nSize);
}
{
// Check if there are any stored paths from dynamically loaded dlls
SCOPED_READ_LOCK(g_loadedModulesLock, lock);
auto findIt = g_loadedModules.find(hModule);
if (findIt != g_loadedModules.end())
return Shared_GetModuleFileNameInner(func, hModule, findIt->second, lpFilename, nSize);
}
if (!g_runningRemote && !IsVfsEnabled())
{
DWORD res = True_GetModuleFileNameW(hModule, lpFilename, nSize);
DEBUG_LOG_TRUE(L"GetModuleFileNameW", L"%llu %u (%ls) -> %u", uintptr_t(hModule), nSize, lpFilename, res);
return res;
}
StringBuffer<512> moduleName;
{
DWORD res = True_GetModuleFileNameW(hModule, moduleName.data, moduleName.capacity);
DEBUG_LOG_TRUE(L"GetModuleFileNameW", L"(INTERNAL) %llu %u (%ls) -> %u", uintptr_t(hModule), moduleName.capacity, moduleName.data, res);
if (res == 0)
return res;
UBA_ASSERT(GetLastError() != ERROR_INSUFFICIENT_BUFFER);
moduleName.count = res;
}
// This could be dlls that are loaded early one so might not exist in g_loadedModules
// TODO: These could be wrong.. since the files could have been copied from different directories into the remote exedir
if (!moduleName.StartsWith(g_exeDir.data))
{
VirtualizePath(moduleName);
return Shared_GetModuleFileNameInner(func, hModule, moduleName, lpFilename, nSize);
}
StringBuffer<350> fileName;
fileName.Append(g_virtualApplicationDir);
fileName.Append(moduleName.data + g_exeDir.count);
return Shared_GetModuleFileNameInner(func, hModule, fileName, lpFilename, nSize);
}
DWORD Detoured_GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize)
{
DETOURED_CALL(GetModuleFileNameW);
return Shared_GetModuleFileNameW(L"GetModuleFileNameW", hModule, lpFilename, nSize);
}
DWORD Detoured_GetModuleFileNameExW(HANDLE hProcess, HMODULE hModule, LPWSTR lpFilename, DWORD nSize)
{
if (hProcess != (HANDLE)-1)
{
UBA_ASSERT(!g_runningRemote); // Not implemented
DWORD res = True_GetModuleFileNameExW(hProcess, hModule, lpFilename, nSize);
DEBUG_LOG_TRUE(L"GetModuleFileNameExW", L"%llu %u (%ls) -> %u", uintptr_t(hModule), nSize, lpFilename, res);
return res;
}
return Shared_GetModuleFileNameW(L"GetModuleFileNameExW", hModule, lpFilename, nSize);
}
DWORD Shared_GetModuleFileNameA(const tchar* func, HMODULE hModule, LPSTR lpFilename, DWORD nSize)
{
// Verified called from used applications.. and does not automatically call W version
StringBuffer<> temp;
UBA_ASSERT(nSize < temp.capacity);
DWORD res = Shared_GetModuleFileNameW(func, hModule, temp.data, nSize);
temp.Parse(lpFilename, res);
return res;
}
DWORD Detoured_GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize)
{
// Verified called from used applications.. and does not automatically call W version
DETOURED_CALL(GetModuleFileNameA);
return Shared_GetModuleFileNameA(L"GetModuleFileNameA", hModule, lpFilename, nSize);
}
DWORD Detoured_GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize)
{
// Verified called from used applications.. and does not automatically call W version
DETOURED_CALL(GetModuleFileNameExA);
if (hProcess != (HANDLE)-1)
{
UBA_ASSERT(!g_runningRemote); // Not implemented
DWORD res = True_GetModuleFileNameExA(hProcess, hModule, lpFilename, nSize);
DEBUG_LOG_TRUE(L"GetModuleFileNameExA", L"%llu %u (%hs) -> %u", uintptr_t(hModule), nSize, lpFilename, res);
return res;
}
return Shared_GetModuleFileNameA(L"GetModuleFileNameExA", hModule, lpFilename, nSize);
}
BOOL Detoured_GetModuleHandleExW(DWORD dwFlags, LPCWSTR lpModuleName, HMODULE *phModule)
{
StringBuffer<> path;
if (lpModuleName && IsAbsolutePath(lpModuleName))
{
FixPath(path, lpModuleName);
DevirtualizePath(path);
lpModuleName = path.data;
}
// We don't want to trigger lots of downloading of dlls and searching through paths on remote machines...
// Will need to revisit this if it causes issues
if (g_runningRemote)
t_ntOpenFileDisallowed = true;
DEBUG_LOG_TRUE(L"GetModuleHandleExW", L"%s", lpModuleName);
BOOL res = True_GetModuleHandleExW(dwFlags, lpModuleName, phModule);
if (g_runningRemote)
t_ntOpenFileDisallowed = false;
return res;
}
BOOL Detoured_CopyFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, LPPROGRESS_ROUTINE lpProgressRoutine, LPVOID lpData, LPBOOL pbCancel, DWORD dwCopyFlags)
{
DETOURED_CALL(CopyFileExW);
StringBuffer<> fromName;
FixPath(fromName, lpExistingFileName);
DevirtualizePath(fromName);
StringKey fromKey = ToStringKeyLower(fromName);
StringBuffer<> toName;
FixPath(toName, lpNewFileName);
DevirtualizePath(toName);
StringKey toKey = ToStringKeyLower(toName);
StringBuffer<> newFromName;
StringBuffer<> newToName;
u32 closeId;
u32 lastError;
u32 directoryTableSize;
{
RPC_MESSAGE(CopyFile, copyFile)
writer.WriteStringKey(fromKey);
writer.WriteString(fromName);
writer.WriteStringKey(toKey);
writer.WriteString(toName);
writer.Flush();
BinaryReader reader;
reader.ReadString(newFromName);
reader.ReadString(newToName);
closeId = reader.ReadU32();
lastError = reader.ReadU32();
directoryTableSize = reader.ReadU32();
}
if (closeId == ~0u) // Copy was made server side
{
g_directoryTable.ParseDirectoryTable(directoryTableSize);
UBA_ASSERT(g_runningRemote);
SetLastError(lastError);
return lastError == ERROR_SUCCESS;
}
// TODO: This copy should probably be moved to session process instead.. to handle failing to copy better
bool res;
{
SuppressCreateFileDetourScope cfs;
res = True_CopyFileExW(newFromName.data, newToName.data, lpProgressRoutine, lpData, pbCancel, dwCopyFlags);
}
DEBUG_LOG_TRUE(L"CopyFileExW", L"%ls to %ls flags: %u (%ls to %ls) -> %ls", lpExistingFileName, lpNewFileName, dwCopyFlags, newFromName.data, newToName.data, ToString(res));
// We need to report the new file that has been added (and we must do it _after_ it has been copied
if (!closeId)
return res;
bool deleteOnClose = res == false; // If failing to copy we set deleteOnClose
Rpc_UpdateCloseHandle(newToName.data, closeId, deleteOnClose, L"", {}, 0, true);
return res;
}
BOOL Detoured_CopyFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, BOOL bFailIfExists)
{
DETOURED_CALL(CopyFileW);
DEBUG_LOG_TRUE(L"CopyFileW", L"");
return Detoured_CopyFileExW(lpExistingFileName, lpNewFileName, (LPPROGRESS_ROUTINE)NULL, (LPVOID)NULL, (LPBOOL)NULL, bFailIfExists ? (DWORD)COPY_FILE_FAIL_IF_EXISTS : 0);
}
BOOL Detoured_CreateHardLinkW(LPCWSTR lpFileName, LPCWSTR lpExistingFileName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
DETOURED_CALL(CreateHardLinkW);
// TODO: Merge this code with CopyFileEx which is identical
StringBuffer<> fromName;
FixPath(fromName, lpExistingFileName);
DevirtualizePath(fromName);
StringKey fromKey = ToStringKeyLower(fromName);
StringBuffer<> toName;
FixPath(toName, lpFileName);
DevirtualizePath(toName);
StringKey toKey = ToStringKeyLower(toName);
StringBuffer<> newFromName;
StringBuffer<> newToName;
u32 closeId;
{
RPC_MESSAGE(CopyFile, copyFile)
writer.WriteStringKey(fromKey);
writer.WriteString(fromName);
writer.WriteStringKey(toKey);
writer.WriteString(toName);
writer.Flush();
BinaryReader reader;
reader.ReadString(newFromName);
reader.ReadString(newToName);
closeId = reader.ReadU32();
}
bool res;
{
SuppressCreateFileDetourScope cfs;
res = True_CreateHardLinkW(lpFileName, lpExistingFileName, lpSecurityAttributes);
}
DEBUG_LOG_TRUE(L"CreateHardLinkW", L"%ls to %ls (%ls to %ls) -> %ls", lpExistingFileName, lpFileName, newFromName.data, newToName.data, ToString(res));
// We need to report the new file that has been added (and we must do it _after_ it has been copied
if (closeId)
Rpc_UpdateCloseHandle(newToName.data, closeId, false, L"", {}, 0, true);
return res;
}
BOOL Detoured_DeleteFileW(LPCWSTR lpFileName)
{
DETOURED_CALL(DeleteFileW);
LPCWSTR original = lpFileName;
if (!CanDetour(lpFileName))
{
DEBUG_LOG_TRUE(L"DeleteFileW", L"(%ls)", original);
return True_DeleteFileW(original);
}
StringBuffer<> fixedName;
FixPath(fixedName, lpFileName);
DevirtualizePath(fixedName);
StringBuffer<> fixedNameLower(fixedName);
fixedNameLower.MakeLower();
if (KeepInMemory(fixedNameLower, true))
{
DEBUG_LOG_DETOURED(L"DeleteFileW", L"(INMEMORY) (%ls) -> Success", lpFileName);
SetLastError(ERROR_SUCCESS);
return TRUE;
}
if (fixedName.StartsWith(g_systemTemp.data))
{
DEBUG_LOG_TRUE(L"DeleteFileW", L"(%ls)", original);
return True_DeleteFileW(original);
}
StringKey fileNameKey = ToStringKey(fixedNameLower);
u32 directoryTableSize;
bool result;
u32 errorCode;
{
u32 closeId = 0;
RPC_MESSAGE(DeleteFile, deleteFile)
writer.WriteString(fixedName);
writer.WriteStringKey(fileNameKey);
writer.WriteU32(closeId);
writer.Flush();
BinaryReader reader;
result = reader.ReadBool();
errorCode = reader.ReadU32();
directoryTableSize = reader.ReadU32();
pcs.Leave();
DEBUG_LOG_PIPE(L"DeleteFile", L"%ls", lpFileName);
}
DEBUG_LOG_DETOURED(L"DeleteFileW", L"(%ls) -> %ls (%u)", lpFileName, ToString(result), errorCode);
g_directoryTable.ParseDirectoryTable(directoryTableSize);
g_mappedFileTable.SetDeleted(fileNameKey, lpFileName, true);
SetLastError(errorCode);
return result;
}
bool Shared_MoveFile(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags)
{
DETOURED_CALL(MoveFileExW);
StringBuffer<> source;
FixPath(source, lpExistingFileName);
DevirtualizePath(source);
StringBuffer<> dest;
FixPath(dest, lpNewFileName);
DevirtualizePath(dest);
StringKey sourceKey = ToStringKeyLower(source);
if (KeepInMemory(source, false))
{
SCOPED_WRITE_LOCK(g_mappedFileTable.m_lookupLock, lock);
auto it = g_mappedFileTable.m_lookup.find(sourceKey);
UBA_ASSERTF(it != g_mappedFileTable.m_lookup.end(), L"Can't find %ls", source.data);
FileInfo& sourceInfo = it->second;
lock.Leave();
if (g_allowOutputFiles && g_rules->IsOutputFile(dest))
{
sourceInfo.deleted = true;
UBA_ASSERT(!sourceInfo.memoryFile->isLocalOnly);
dest.MakeLower();
StringKey destKey = ToStringKey(dest);
SCOPED_WRITE_LOCK(g_mappedFileTable.m_lookupLock, lock2);
auto insres = g_mappedFileTable.m_lookup.try_emplace(destKey);
lock2.Leave();
FileInfo& destInfo = insres.first->second;
UBA_ASSERTF(!insres.second, TC("%s -> %s"), lpExistingFileName, lpNewFileName); // This is here just to get a chance to investigate this scenario.. might work
HANDLE tempHandle = INVALID_HANDLE_VALUE;
if (destInfo.isFileMap || (destInfo.memoryFile && destInfo.memoryFile->isLocalOnly)) // File has been read before, let's just ignore that and take the new memoryFile
{
destInfo.isFileMap = false;
destInfo.trueFileMapHandle = 0;
destInfo.trueFileMapOffset = 0;
tempHandle = CreateFile(lpNewFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (tempHandle == INVALID_HANDLE_VALUE)
return false;
}
destInfo.memoryFile = sourceInfo.memoryFile;
sourceInfo.memoryFile = nullptr;
CloseHandle(tempHandle);
DEBUG_LOG_DETOURED(L"MoveFileExW", L"(memfile->memfile) %ls to %ls -> Success", lpExistingFileName, lpNewFileName);
SetLastError(ERROR_SUCCESS);
return true;
}
UBA_ASSERT(!KeepInMemory(dest, true));
DEBUG_LOG_DETOURED(L"MoveFileExW", L"(memfile->file) %ls to %ls", lpExistingFileName, lpNewFileName);
HANDLE h = CreateFile(lpNewFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
return false;
auto cg = MakeGuard([&]() { CloseHandle(h); });
UBA_ASSERT(sourceInfo.memoryFile->writtenSize < ~0u);
u32 toWrite = u32(sourceInfo.memoryFile->writtenSize);
u8* readPos = sourceInfo.memoryFile->baseAddress;
while (toWrite)
{
DWORD written = 0;
if (!WriteFile(h, readPos, toWrite, &written, NULL))
return false;
readPos += written;
toWrite -= written;
}
SetLastError(ERROR_SUCCESS);
sourceInfo.deleted = true;
return true;
}
StringKey destKey = ToStringKeyLower(dest);
u32 directoryTableSize;
u32 errorCode;
bool result;
{
RPC_MESSAGE(MoveFile, moveFile)
writer.WriteStringKey(sourceKey);
writer.WriteString(source);
writer.WriteStringKey(destKey);
writer.WriteString(dest);
writer.WriteU32(dwFlags);
writer.Flush();
BinaryReader reader;
result = reader.ReadBool();
errorCode = reader.ReadU32();
directoryTableSize = reader.ReadU32();
pcs.Leave();
DEBUG_LOG_PIPE(L"MoveFile", L"%ls to %ls", lpExistingFileName, lpNewFileName);
}
DEBUG_LOG_DETOURED(L"MoveFileExW", L"(PIPE) (%ls to %ls) -> %ls (%u)", lpExistingFileName, lpNewFileName, ToString(result), errorCode);
if (result)
{
g_directoryTable.ParseDirectoryTable(directoryTableSize);
g_mappedFileTable.SetDeleted(sourceKey, source.data, true);
g_mappedFileTable.SetDeleted(destKey, dest.data, false);
}
SetLastError(errorCode);
return result;
}
BOOL Detoured_MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags)
{
//UBA_ASSERT(!Contains(lpNewFileName, L"E:\\dev\\fn\\Engine\\Source\\Programs\\Shared\\EpicGames.UHT\\obj\\project.assets.json"));
return Shared_MoveFile(lpExistingFileName, lpNewFileName, dwFlags);
}
// MoveFileW ends up here
BOOL Detoured_MoveFileWithProgressW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, LPPROGRESS_ROUTINE lpProgressRoutine, LPVOID lpData, DWORD dwFlags)
{
DETOURED_CALL(MoveFileWithProgressW);
StringBuffer<> source;
FixPath(source, lpExistingFileName);
return Shared_MoveFile(lpExistingFileName, lpNewFileName, dwFlags);
//UBA_ASSERT(!g_runningRemote);
//DEBUG_LOG_TRUE(L"MoveFileWithProgressW", L"%ls to %ls", lpExistingFileName, lpNewFileName);
//return True_MoveFileWithProgressW(lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, dwFlags);
}
bool Shared_GetNextFile(WIN32_FIND_DATA& outData, ListDirectoryHandle& listHandle)
{
while (true)
{
if (listHandle.it == listHandle.fileTableOffsets.size())
return false;
constexpr u32 maxLen = sizeof_array(outData.cFileName);
if (listHandle.it < 0)
{
if (listHandle.it == -2)
wcscpy_s(outData.cFileName, maxLen, L".");
else
wcscpy_s(outData.cFileName, maxLen, L"..");
outData.nFileSizeHigh = 0;
outData.nFileSizeLow = 0;
outData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
outData.cAlternateFileName[0] = 0;
(u64&)outData.ftLastWriteTime = 0;
(u64&)outData.ftCreationTime = 0;
(u64&)outData.ftLastAccessTime = 0;
++listHandle.it;
return true;
}
DirectoryTable::EntryInformation info;
u32 fileTableOffset = listHandle.fileTableOffsets[listHandle.it++];
g_directoryTable.GetEntryInformation(info, fileTableOffset, outData.cFileName, maxLen);
if (info.attributes == 0) // File was deleted
continue;
LARGE_INTEGER li = ToLargeInteger(info.size);
outData.nFileSizeHigh = li.HighPart;
outData.nFileSizeLow = li.LowPart;
outData.dwFileAttributes = info.attributes;
outData.cAlternateFileName[0] = 0;
(u64&)outData.ftLastWriteTime = info.lastWrite;
// TODO: These are wrong..
(u64&)outData.ftCreationTime = info.lastWrite;
(u64&)outData.ftLastAccessTime = info.lastWrite;
return true;
}
}
__forceinline
HANDLE Local_FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags, const wchar_t* funcName)
{
DEBUG_LOG_TRUE(funcName, L"(NODETOUR) (%ls)", lpFileName);
SuppressCreateFileDetourScope s; // Needed for cmd.exe copy right now.. NtCreate's flags are set the same as directory search but the first file is not a directory.
auto res = True_FindFirstFileExW(lpFileName, fInfoLevelId, lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
UBA_ASSERT(!isDetouredHandle(res) && !isListDirectoryHandle(res));
return res;
}
__forceinline HANDLE Shared_FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags, const wchar_t* funcName)
{
if (t_disallowDetour != 0 || Equals(lpFileName, L"nul") || !g_allowFindFileDetour)
{
return Local_FindFirstFileExW(lpFileName, fInfoLevelId, lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags, funcName);
}
StringBuffer<> lowerName;
FixPath(lowerName, lpFileName);
DevirtualizePath(lowerName);
if (lowerName.StartsWith(g_systemTemp.data) || lowerName.StartsWith(g_systemRoot.data))
return Local_FindFirstFileExW(lpFileName, fInfoLevelId, lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags, funcName);
lowerName.MakeLower();
wchar_t* buf = lowerName.data;
wchar_t* fileName = lowerName.data;
wchar_t* lastBackslash = wcsrchr(fileName, '\\');
if (lastBackslash)
fileName = lastBackslash + 1;
UBA_ASSERT(lastBackslash);
u32 bufChars = u32(lastBackslash - buf + 1);
if (wcscmp(fileName, L"*") == 0 || wcscmp(fileName, L"*.*") == 0)
{
*fileName = 0;
}
// We must remove a slash at the end so it matches our cache entries
if (bufChars > 2)
{
wchar_t* temp = (wchar_t*)buf;
if (temp[bufChars - 1] == '\\')
temp[--bufChars] = 0;
}
DirHash hash(StringView(buf, bufChars));
SCOPED_WRITE_LOCK(g_directoryTable.m_lookupLock, lookLock);
auto insres = g_directoryTable.m_lookup.try_emplace(hash.key, g_memoryBlock);
DirectoryTable::Directory& dir = insres.first->second;
if (insres.second)
{
CHECK_PATH(StringView(buf, bufChars));
if (g_directoryTable.EntryExistsNoLock(hash.key, StringView(buf, bufChars)) != DirectoryTable::Exists_No)
Rpc_UpdateDirectory(hash.key, buf, bufChars, 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
HANDLE validateHandle = INVALID_HANDLE_VALUE;
/*
if (g_validateFileAccess)
{
NTSTATUS res = exists ? 0 : -1;
IO_STATUS_BLOCK IoStatusBlock2;
NTSTATUS res2 = True_NtCreateFile(&validateHandle, DesiredAccess, ObjectAttributes, &IoStatusBlock2, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);
UBA_ASSERT(res < 0 && res2 < 0 || res >= 0 && res2 >= 0);
}
*/
#endif
if (!exists)
{
if (g_systemTemp.StartsWith(lowerName.data)) // TODO: This is a big hack. We should make sure the uba system temp folder is virtualized and is always some root path that never can collide with the host file system
return Local_FindFirstFileExW(lpFileName, fInfoLevelId, lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags, funcName);
DEBUG_LOG_DETOURED(funcName, L"(%ls) -> NotFound", lpFileName);
SetLastError(ERROR_FILE_NOT_FOUND);
return INVALID_HANDLE_VALUE;
}
// TODO: Add support for more modes
UBA_ASSERT(fInfoLevelId == FindExInfoBasic || fInfoLevelId == FindExInfoStandard);
UBA_ASSERT(fSearchOp == FindExSearchNameMatch);
UBA_ASSERT(lpSearchFilter == nullptr);
//UBA_ASSERT(dwAdditionalFlags == 0);
g_directoryTable.PopulateDirectory(hash.open, dir);
auto listHandle = new ListDirectoryHandle{ hash.key, dir };
if (!*fileName)
listHandle->it = -2;
else
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();
listHandle->wildcard = fileName;
#if UBA_DEBUG_VALIDATE
if (g_validateFileAccess)
listHandle->validateHandle = validateHandle;
#endif
auto& data = *(WIN32_FIND_DATA*)lpFindFileData;
while (true)
{
if (!Shared_GetNextFile(data, *listHandle))
{
delete listHandle;
if (g_systemTemp.StartsWith(lowerName.data)) // TODO: This is a big hack. We should make sure the uba system temp folder is virtualized and is always some root path that never can collide with the host file system
return Local_FindFirstFileExW(lpFileName, fInfoLevelId, lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags, funcName);
DEBUG_LOG_DETOURED(funcName, L"(%ls) -> NotFound(2)", lpFileName);
return INVALID_HANDLE_VALUE;
}
if (listHandle->wildcard.empty() || PathMatchSpecW(data.cFileName, listHandle->wildcard.c_str()))
break;
}
HANDLE res = makeListDirectoryHandle(listHandle);
DEBUG_LOG_DETOURED(funcName, L"(%ls) \"%ls\" -> %llu", lpFileName, data.cFileName, uintptr_t(res));
return res;
}
HANDLE Detoured_FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags)
{
DETOURED_CALL(FindFirstFileExW);
return Shared_FindFirstFileExW(lpFileName, fInfoLevelId, lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags, L"FindFirstFileExW");
}
HANDLE Detoured_FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData)
{
DETOURED_CALL(FindFirstFileW);
return Shared_FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch, NULL, 0, L"FindFirstFileW");
}
BOOL Detoured_FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData)
{
DETOURED_CALL(FindNextFileW);
if (isListDirectoryHandle(hFindFile))
{
auto& listHandle = asListDirectoryHandle(hFindFile);
auto& data = *(WIN32_FIND_DATA*)lpFindFileData;
while (true)
{
if (!Shared_GetNextFile(data, listHandle))
{
DEBUG_LOG_DETOURED(L"FindNextFileW", L"%llu (NOMORE) -> False", u64(hFindFile));
SetLastError(ERROR_NO_MORE_FILES);
return false;
}
if (listHandle.wildcard.empty() || PathMatchSpecW(data.cFileName, listHandle.wildcard.c_str()))
{
DEBUG_LOG_DETOURED(L"FindNextFileW", L"%llu (%ls) -> True", u64(hFindFile), data.cFileName);
SetLastError(ERROR_SUCCESS);
return true;
}
}
}
UBA_ASSERT(!isDetouredHandle(hFindFile));
DEBUG_LOG_TRUE(L"FindNextFileW", L"%llu", uintptr_t(hFindFile));
return True_FindNextFileW(hFindFile, lpFindFileData);
}
HANDLE Detoured_FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
{
DETOURED_CALL(FindFirstFileW);
StringBuffer<> fileName;
fileName.Append(lpFileName);
WIN32_FIND_DATAW findFileData;
HANDLE res = Shared_FindFirstFileExW(fileName.data, FindExInfoStandard, &findFileData, FindExSearchNameMatch, NULL, 0, L"FindFirstFileA");
if (res == INVALID_HANDLE_VALUE)
return res;
memcpy(lpFindFileData, &findFileData, 48); // 48 is not exact but it is at least down to where the name starts (the types are identical down to the name)
size_t destLen;
errno_t err = wcstombs_s(&destLen, lpFindFileData->cFileName, MAX_PATH, findFileData.cFileName, MAX_PATH-1);
UBA_ASSERT(err == 0);(void)err;
return res;
}
BOOL Detoured_FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData)
{
WIN32_FIND_DATAW findFileData;
if (!Detoured_FindNextFileW(hFindFile, &findFileData))
return false;
memcpy(lpFindFileData, &findFileData, 48); // 48 is not exact but it is at least down to where the name starts (the types are identical down to the name)
size_t destLen;
errno_t err = wcstombs_s(&destLen, lpFindFileData->cFileName, MAX_PATH, findFileData.cFileName, MAX_PATH-1);
UBA_ASSERT(err == 0);(void)err;
return true;
}
BOOL Detoured_FindClose(HANDLE handle)
{
DETOURED_CALL(FindClose);
if (isListDirectoryHandle(handle))
{
DEBUG_LOG_DETOURED(L"FindClose", L"%llu -> Success", uintptr_t(handle));
delete& asListDirectoryHandle(handle);
SetLastError(ERROR_SUCCESS);
return true;
}
UBA_ASSERT(!isDetouredHandle(handle));
BOOL res = True_FindClose(handle);
DEBUG_LOG_TRUE(L"FindClose", L"%llu -> %ls", uintptr_t(handle), ToString(res));
return res;
}
BOOL Detoured_GetFileInformationByHandleEx(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS fileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize)
{
DETOURED_CALL(GetFileInformationByHandleEx);
HANDLE trueHandle = hFile;
u32 entryOffset = ~u32(0);
#if UBA_DEBUG_VALIDATE
const wchar_t* originalName = nullptr;
#endif
u64 fileSize = InvalidValue;
if (isDetouredHandle(hFile))
{
DetouredHandle& dh = asDetouredHandle(hFile);
trueHandle = dh.trueHandle;
entryOffset = dh.dirTableOffset;
FileObject& fo = *dh.fileObject;
FileInfo& fi = *fo.fileInfo;
fileSize = fi.size;
if (entryOffset == ~u32(0) && trueHandle == INVALID_HANDLE_VALUE)
{
MemoryFile* mf = fi.memoryFile;
UBA_ASSERTF(mf, L"GetFileInformationByHandleEx called on file %s which has no entry offset or real handle", HandleToName(hFile));
DEBUG_LOG_DETOURED(L"GetFileInformationByHandleEx", L"(MEMORY) (%u) %llu (%ls)", fileInformationClass, uintptr_t(hFile), HandleToName(hFile));
if (fileInformationClass == FileIdInfo)
{
auto& data = *(FILE_ID_INFO*)lpFileInformation;
data.VolumeSerialNumber = mf->volumeSerial;
u64* id = (u64*)&data.FileId;
id[0] = 0;
id[1] = mf->fileIndex;
return TRUE;
}
else if (fileInformationClass == FileStandardInfo)
{
auto& data = *(FILE_STANDARD_INFO*)lpFileInformation;
data.EndOfFile = ToLargeInteger(mf->writtenSize);
data.AllocationSize = ToLargeInteger(mf->committedSize);
data.DeletePending = fo.deleteOnClose;
data.NumberOfLinks = 1;
data.Directory = false;
return TRUE;
}
else
{
UBA_ASSERTF(!mf, L"GetFileInformationByHandleEx called for memory file using class %u which is not implemented (%s)", fileInformationClass, HandleToName(hFile));
}
}
#if UBA_DEBUG_VALIDATE
originalName = dh.fileObject->fileInfo->originalName;
#endif
}
else if (isListDirectoryHandle(hFile))
{
auto& listHandle = asListDirectoryHandle(hFile);
if (listHandle.dir.tableOffset != InvalidTableOffset)
entryOffset = listHandle.dir.tableOffset | 0x80000000;
else
UBA_ASSERT(false);
trueHandle = INVALID_HANDLE_VALUE;
}
if (entryOffset != ~u32(0))
{
DirectoryTable::EntryInformation entryInfo;
g_directoryTable.GetEntryInformation(entryInfo, entryOffset);
if (fileInformationClass == FileBasicInfo)
{
auto& data = *(FILE_BASIC_INFO*)lpFileInformation;
data.CreationTime = ToLargeInteger(entryInfo.lastWrite);
data.LastAccessTime = ToLargeInteger(entryInfo.lastWrite);
data.LastWriteTime = ToLargeInteger(entryInfo.lastWrite);
data.ChangeTime = ToLargeInteger(entryInfo.lastWrite);
data.FileAttributes = entryInfo.attributes;
DEBUG_LOG_DETOURED(L"GetFileInformationByHandleEx", L"(DIRTABLE) (FileBasicInfo) %llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return TRUE;
}
else if (fileInformationClass == FileIdInfo)
{
auto& data = *(FILE_ID_INFO*)lpFileInformation;
data.VolumeSerialNumber = entryInfo.volumeSerial;
u64* id = (u64*)&data.FileId;
id[0] = 0;
id[1] = entryInfo.fileIndex;
DEBUG_LOG_DETOURED(L"GetFileInformationByHandleEx", L"(DIRTABLE) (FileIdInfo) %llu (VolumeSerial: %u FileIndex: %llu), (%ls)", uintptr_t(hFile), entryInfo.volumeSerial, entryInfo.fileIndex, HandleToName(hFile));
return TRUE;
}
else if (fileInformationClass == FileStandardInfo)
{
if (fileSize == InvalidValue) // Always use FileInfo size if available since file could be decompressed and then directory info is wrong
fileSize = entryInfo.size;
auto& data = *(FILE_STANDARD_INFO*)lpFileInformation;
data.EndOfFile = ToLargeInteger(fileSize);
data.AllocationSize = ToLargeInteger(entryInfo.size);
data.DeletePending = false;
data.NumberOfLinks = 1;
data.Directory = (entryInfo.attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
#if UBA_DEBUG_VALIDATE
if (g_validateFileAccess && originalName)
{
SuppressDetourScope _;
WIN32_FILE_ATTRIBUTE_DATA validData;
if (True_GetFileAttributesExW(originalName, GetFileExInfoStandard, &validData))
{
u64 size = ToLargeInteger(validData.nFileSizeHigh, validData.nFileSizeLow).QuadPart; (void)size;
UBA_ASSERTF(u64(data.EndOfFile.QuadPart) == size, L"File size used: %llu Actual file size: %llu (%s)", data.EndOfFile.QuadPart, size, originalName);
}
else
{
Rpc_WriteLogf(L"FAILED TO GET FILE ATTRIBUTES %s", originalName);
}
}
#endif
DEBUG_LOG_DETOURED(L"GetFileInformationByHandleEx", L"(DIRTABLE) (FileStandardInfo) %llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return TRUE;
}
else if (fileInformationClass == FileRemoteProtocolInfo)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
/*
auto& data = *(FILE_REMOTE_PROTOCOL_INFO*)lpFileInformation;
SuppressCreateFileDetourScope s;
HANDLE h = CreateFile(HandleToName(hFile), FILE_READ_ATTRIBUTES|FILE_LIST_DIRECTORY|FILE_WRITE_EA, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
BOOL res = GetFileInformationByHandleEx(h, FileRemoteProtocolInfo, lpFileInformation, dwBufferSize);
auto err = GetLastError();
CloseHandle(h);
return FALSE;
*/
}
else if (fileInformationClass == FileAttributeTagInfo)
{
auto& data = *(FILE_ATTRIBUTE_TAG_INFO*)lpFileInformation;
data.FileAttributes = entryInfo.attributes;
data.ReparseTag = 0;
DEBUG_LOG_DETOURED(L"GetFileInformationByHandleEx", L"(DIRTABLE) (FileAttributeTagInfo) %llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
return TRUE;
}
else
{
UBA_ASSERTF(trueHandle != INVALID_HANDLE_VALUE, L"GetFileInformationByHandleEx with class %u not Implemented (%ls)", fileInformationClass, HandleToName(hFile));
}
}
DEBUG_LOG_TRUE(L"GetFileInformationByHandleEx", L"(%ls)", HandleToName(hFile));
TimerScope ts(g_kernelStats.getFileInfo);
return True_GetFileInformationByHandleEx(trueHandle, fileInformationClass, lpFileInformation, dwBufferSize); /// calls GetFileInformationByHandleEx
}
BOOL Detoured_GetFileInformationByHandle(HANDLE hFile, LPBY_HANDLE_FILE_INFORMATION lpFileInformation)
{
DETOURED_CALL(GetFileInformationByHandle);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
u32 dirTableOffset = dh.dirTableOffset;
UBA_ASSERT(dh.fileObject->fileInfo);
FileInfo& fi = *dh.fileObject->fileInfo;
if (dirTableOffset != ~u32(0))
{
DirectoryTable::EntryInformation entryInfo;
g_directoryTable.GetEntryInformation(entryInfo, dirTableOffset);
lpFileInformation->dwFileAttributes = entryInfo.attributes;
(u64&)lpFileInformation->ftCreationTime = entryInfo.lastWrite;
(u64&)lpFileInformation->ftLastAccessTime = entryInfo.lastWrite;
(u64&)lpFileInformation->ftLastWriteTime = entryInfo.lastWrite;
lpFileInformation->dwVolumeSerialNumber = entryInfo.volumeSerial;
LARGE_INTEGER li = ToLargeInteger(entryInfo.fileIndex);
lpFileInformation->nFileIndexHigh = li.HighPart;
lpFileInformation->nFileIndexLow = li.LowPart;
lpFileInformation->nNumberOfLinks = 1;//~u32(0); // TODO
u64 fileSize = fi.size;
if (fileSize == InvalidValue)
fileSize = entryInfo.size;
#if UBA_DEBUG_VALIDATE
if (g_validateFileAccess && !(entryInfo.attributes & FILE_ATTRIBUTE_DIRECTORY))
{
SuppressDetourScope _;
WIN32_FILE_ATTRIBUTE_DATA data;
if (dh.trueHandle != INVALID_HANDLE_VALUE)
{
BY_HANDLE_FILE_INFORMATION bhfi;
auto res2 = True_GetFileInformationByHandle(dh.trueHandle, &bhfi);
UBA_ASSERT(res2 == TRUE);
u64 size = ToLargeInteger(bhfi.nFileSizeHigh, bhfi.nFileSizeLow).QuadPart; (void)size;
u64 fileIndex = ToLargeInteger(bhfi.nFileIndexHigh, bhfi.nFileIndexLow).QuadPart; (void)fileIndex;
UBA_ASSERTF(fileSize == size, L"File size used: %llu Actual file size: %llu (%s)", fileSize, size, fi.originalName);
//UBA_ASSERTF(entryInfo.attributes == bhfi.dwFileAttributes, L"Attributes used: 0x%x Actual: 0x%x (%s)", entryInfo.attributes, bhfi.dwFileAttributes, fi.originalName);
UBA_ASSERTF(entryInfo.volumeSerial == bhfi.dwVolumeSerialNumber, L"VolumeSerial used: %u Actual: %u (%s)", entryInfo.volumeSerial, bhfi.dwVolumeSerialNumber, fi.originalName);
UBA_ASSERTF(entryInfo.fileIndex == fileIndex, L"FileIndex used: %llu Actual: %llu (%s)", entryInfo.fileIndex, fileIndex, fi.originalName);
UBA_ASSERTF(bhfi.nNumberOfLinks == 1, L"Links used: %i Actual: %u (%s)", 1, bhfi.nNumberOfLinks, fi.originalName);
}
else if (True_GetFileAttributesExW(fi.originalName, GetFileExInfoStandard, &data))
{
u64 size = ToLargeInteger(data.nFileSizeHigh, data.nFileSizeLow).QuadPart; (void)size;
UBA_ASSERTF(fileSize == size, L"File size used: %llu Actual file size: %llu (%s)", fileSize, size, fi.originalName);
}
else
{
Rpc_WriteLogf(L"FAILED TO GET FILE ATTRIBUTES %s", fi.originalName);
}
}
#endif
li = ToLargeInteger(fileSize);
lpFileInformation->nFileSizeHigh = li.HighPart;
lpFileInformation->nFileSizeLow = li.LowPart;
DEBUG_LOG_DETOURED(L"GetFileInformationByHandle", L"(file) %llu (%ls) -> Success (size: %llu)", uintptr_t(hFile), HandleToName(hFile), fileSize);
return TRUE;
}
if (MemoryFile* mf = fi.memoryFile)
{
DEBUG_LOG_DETOURED(L"GetFileInformationByHandle", L"(memoryfile) %llu (%ls) -> Success (Size: %llu)", uintptr_t(hFile), HandleToName(hFile), mf->writtenSize);
lpFileInformation->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
(u64&)lpFileInformation->ftCreationTime = mf->fileTime;
(u64&)lpFileInformation->ftLastAccessTime = mf->fileTime;
(u64&)lpFileInformation->ftLastWriteTime = mf->fileTime;
lpFileInformation->dwVolumeSerialNumber = mf->volumeSerial;
LARGE_INTEGER li = ToLargeInteger(mf->fileIndex);
lpFileInformation->nFileIndexHigh = li.HighPart;
lpFileInformation->nFileIndexLow = li.LowPart;
lpFileInformation->nNumberOfLinks = 1;//~u32(0); // TODO
li = ToLargeInteger(mf->writtenSize);
lpFileInformation->nFileSizeHigh = li.HighPart;
lpFileInformation->nFileSizeLow = li.LowPart;
return TRUE;
}
if (g_runningRemote || fi.isFileMap)
{
StringBuffer<> fixedName;
FixPath(fixedName, fi.originalName);
FileAttributes attr;
Shared_GetFileAttributes(attr, fixedName);
if (attr.useCache)
{
if (!attr.exists)
{
// this could be a file that was created locally and is not propagated to directory table
SetLastError(ERROR_FILE_NOT_FOUND);
DEBUG_LOG_DETOURED(L"GetFileInformationByHandle", L"remote %llu (%ls) -> NotFound", uintptr_t(hFile), HandleToName(hFile));
return false;
}
UBA_ASSERT(attr.fileIndex);
LARGE_INTEGER li = ToLargeInteger(attr.fileIndex);
/*
#if UBA_DEBUG_VALIDATE
if (g_validateFileAccess)
{
HANDLE h = True_CreateFileW(fileName, 0, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
auto res = True_GetFileInformationByHandle(h, lpFileInformation);
True_CloseHandle(h);
UBA_ASSERT(attr.data.dwFileAttributes == lpFileInformation->dwFileAttributes);
UBA_ASSERT(attr.volumeSerial == lpFileInformation->dwVolumeSerialNumber);
UBA_ASSERT(li.HighPart == lpFileInformation->nFileIndexHigh);
UBA_ASSERT(li.LowPart == lpFileInformation->nFileIndexLow);
//return res;
}
#endif
*/
SetLastError(ERROR_SUCCESS);
UBA_ASSERT(attr.volumeSerial);
lpFileInformation->dwFileAttributes = attr.data.dwFileAttributes;
lpFileInformation->ftCreationTime = attr.data.ftCreationTime;
lpFileInformation->ftCreationTime = attr.data.ftCreationTime;
lpFileInformation->ftLastAccessTime = attr.data.ftLastAccessTime;
lpFileInformation->ftLastWriteTime = attr.data.ftLastWriteTime;
lpFileInformation->dwVolumeSerialNumber = attr.volumeSerial;
lpFileInformation->nFileIndexHigh = li.HighPart;
lpFileInformation->nFileIndexLow = li.LowPart;
lpFileInformation->nNumberOfLinks = 1;//~u32(0); // TODO
lpFileInformation->nFileSizeHigh = attr.data.nFileSizeHigh;
lpFileInformation->nFileSizeLow = attr.data.nFileSizeLow;
DEBUG_LOG_DETOURED(L"GetFileInformationByHandle", L"remote %llu (%ls) -> Success", uintptr_t(hFile), HandleToName(hFile));
return TRUE;
}
}
UBA_ASSERTF(dh.trueHandle != INVALID_HANDLE_VALUE, TC("GetFileInformationByHandle needs true handle for %ls"), HandleToName(hFile));
trueHandle = dh.trueHandle;
}
TimerScope ts(g_kernelStats.getFileInfo);
auto res = True_GetFileInformationByHandle(trueHandle, lpFileInformation); // Calls NtQueryInformationFile
DEBUG_LOG_TRUE(L"GetFileInformationByHandle", L"%llu (%ls) -> %u", uintptr_t(hFile), HandleToName(hFile), res);
return res;
}
BOOL Detoured_SetFileInformationByHandle(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize)
{
DETOURED_CALL(SetFileInformationByHandle);
if (!isDetouredHandle(hFile))
{
DEBUG_LOG_TRUE(L"SetFileInformationByHandle", L"%llu (%u)", uintptr_t(hFile), FileInformationClass);
return True_SetFileInformationByHandle(hFile, FileInformationClass, lpFileInformation, dwBufferSize);
}
auto& dh = asDetouredHandle(hFile);
FileObject& fo = *dh.fileObject;
FileInfo& fi = *fo.fileInfo;
if (FileInformationClass == FileRenameInfo)
{
DEBUG_LOG_TRUE(L"SetFileInformationByHandle", L"%llu (FileRenameInfo)", uintptr_t(hFile));
auto& info = *(FILE_RENAME_INFO*)lpFileInformation;
StringBuffer<> newName;
newName.Append(info.FileName, info.FileNameLength / 2);
t_renameFileNewName = newName.data;
bool res = True_SetFileInformationByHandle(hFile, FileInformationClass, lpFileInformation, dwBufferSize);
t_renameFileNewName = nullptr;
return res;
}
else if (FileInformationClass == FileDispositionInfo)
{
auto& info = *(FILE_DISPOSITION_INFO*)lpFileInformation;
if (info.DeleteFileW)
{
DEBUG_LOG_DETOURED(L"SetFileInformationByHandle", L"File is set to be deleted on close (%ls)", HandleToName(hFile));
fo.deleteOnClose = true;
}
else if (fo.deleteOnClose)
{
DEBUG_LOG_DETOURED(L"SetFileInformationByHandle", L"File is set to NOT be deleted on close (%ls)", HandleToName(hFile));
fo.deleteOnClose = false;
}
else
{
DEBUG_LOG_DETOURED(L"SetFileInformationByHandle", L"%llu (FileDispositionInfo %u)", uintptr_t(hFile), info.DeleteFileW);
}
if (fo.fileInfo->memoryFile)
return true;
DEBUG_LOG_TRUE(L"SetFileInformationByHandle", L"%llu (FileDispositionInfo)", uintptr_t(hFile));
return True_SetFileInformationByHandle(hFile, FileInformationClass, lpFileInformation, dwBufferSize); // In here to be tabbed in log
}
else if (FileInformationClass == FileAllocationInfo)
{
if (MemoryFile* mf = fi.memoryFile)
{
auto& info = *(FILE_ALLOCATION_INFO*)lpFileInformation;
mf->EnsureCommitted(dh, info.AllocationSize.QuadPart);
DEBUG_LOG_TRUE(L"SetFileInformationByHandle", L"%llu (FileAllocationInfo) Size: %llu", uintptr_t(hFile), info.AllocationSize.QuadPart);
return true;
}
}
else if (FileInformationClass == FileEndOfFileInfo)
{
if (MemoryFile* mf = fi.memoryFile)
{
auto& info = *(FILE_END_OF_FILE_INFO*)lpFileInformation;
mf->writtenSize = info.EndOfFile.QuadPart;
mf->isReported = false;
mf->EnsureCommitted(dh, mf->writtenSize);
SetLastError(ERROR_SUCCESS);
DEBUG_LOG_TRUE(L"SetFileInformationByHandle", L"%llu (FileEndOfFileInfo) End: %llu", uintptr_t(hFile), info.EndOfFile.QuadPart);
return true;
}
}
DEBUG_LOG_TRUE(L"SetFileInformationByHandle", L"%llu (%u)", uintptr_t(hFile), FileInformationClass);
return True_SetFileInformationByHandle(hFile, FileInformationClass, lpFileInformation, dwBufferSize); // In here to be tabbed in log
}
HANDLE Detoured_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName)
{
DETOURED_CALL(CreateFileMappingW);
HANDLE trueHandle = hFile;
FileObject* fo = nullptr;
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
fo = dh.fileObject;
fo->wasUsed = true;
FileInfo& fi = *fo->fileInfo;
if (fi.memoryFile || fi.isFileMap)
{
auto mdh = new DetouredHandle(HandleType_FileMapping);
if (fi.isFileMap)
{
// If protection levels are the same we can reuse the "built-in" file mapping
UBA_ASSERTF((flProtect == PAGE_WRITECOPY ? PAGE_READONLY : flProtect) == fi.fileMapDesiredAccess, L"Code path not implemented (%ls)", HandleToName(hFile));
}
mdh->fileObject = dh.fileObject;
u64 maxSize = ToLargeInteger(dwMaximumSizeHigh, dwMaximumSizeLow).QuadPart;
if (MemoryFile* mf = fi.memoryFile)
{
if (!(flProtect & MEM_RESERVE) && maxSize)
{
mf->EnsureCommitted(*mdh, maxSize);
if (!mf->writtenSize && (flProtect & PAGE_READWRITE)) // TODO: Maybe we should always set writtenSize?
mf->writtenSize = maxSize;
}
}
InterlockedIncrement(&dh.fileObject->refCount);
HANDLE res = makeDetouredHandle(mdh);
DEBUG_LOG_DETOURED(L"CreateFileMappingW", L"(%ls) File %llu Protect %u Size %llu (%ls) -> %llu", (fi.memoryFile ? L"MEMORYFILE" : L"FILEMAP"), uintptr_t(hFile), flProtect, maxSize, HandleToName(hFile), uintptr_t(res));
SetLastError(ERROR_SUCCESS);
return res;
}
UBA_ASSERT(dh.trueHandle != INVALID_HANDLE_VALUE);
trueHandle = dh.trueHandle;
}
TimerScope ts(g_kernelStats.createFileMapping);
HANDLE mappingHandle = True_CreateFileMappingW(trueHandle, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpName);
if (!mappingHandle)
{
DEBUG_LOG_TRUE(L"CreateFileMappingW", L"File %llu (%s) -> Error", uintptr_t(hFile), HandleToName(hFile));
return NULL;
}
if (g_allowFileMappingDetour)
{
if (GetLastError() == ERROR_ALREADY_EXISTS)
ToInvestigate(L"Mapping already exists");
auto detouredHandle = new DetouredHandle(HandleType_FileMapping);
detouredHandle->trueHandle = mappingHandle;
//detouredHandle->fileObject = fo;
//if (fo)
// InterlockedIncrement(&fo->refCount);
mappingHandle = makeDetouredHandle(detouredHandle);
}
DEBUG_LOG_TRUE(L"CreateFileMappingW", L"File %llu, Size: %llu (%s) -> %llu", uintptr_t(hFile), ToLargeInteger(dwMaximumSizeHigh, dwMaximumSizeLow).QuadPart, HandleToName(hFile), u64(mappingHandle));
return mappingHandle;
}
HANDLE Detoured_CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName)
{
const wchar_t* name = nullptr;
wchar_t temp[512];
if (lpName)
{
swprintf_s(temp, sizeof_array(temp), L"%hs", lpName);
name = temp;
}
return Detoured_CreateFileMappingW(hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, name);
}
HANDLE Detoured_OpenFileMappingW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName)
{
DETOURED_CALL(OpenFileMappingW);
HANDLE mappingHandle = True_OpenFileMappingW(dwDesiredAccess, bInheritHandle, lpName);
if (!mappingHandle)
{
DEBUG_LOG_TRUE(L"OpenFileMappingW", L"%ls -> Error", lpName);
return NULL;
}
if (g_allowFileMappingDetour)
{
auto detouredHandle = new DetouredHandle(HandleType_FileMapping);
detouredHandle->trueHandle = mappingHandle;
mappingHandle = makeDetouredHandle(detouredHandle);
}
DEBUG_LOG_TRUE(L"OpenFileMappingW", L"%ls -> %llu", lpName, u64(mappingHandle));
return mappingHandle;
}
LPVOID Detoured_MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap, LPVOID lpBaseAddress)
{
DETOURED_CALL(MapViewOfFileEx);
HANDLE trueMappingObject = hFileMappingObject;
if (isDetouredHandle(hFileMappingObject))
{
auto& dh = asDetouredHandle(hFileMappingObject);
if (dh.fileObject)
{
u64 offset = ToLargeInteger(dwFileOffsetHigh, dwFileOffsetLow).QuadPart;
FileInfo& fi = *dh.fileObject->fileInfo;
if (fi.fileMapMem && lpBaseAddress && lpBaseAddress != fi.fileMapMem) // This scenario happens with pch files in msvc cl.exe
{
if (dh.trueHandle == INVALID_HANDLE_VALUE) // This means we are using the "built-in" mapping handle
trueMappingObject = fi.trueFileMapHandle;
u8* res;
// This actually uses more memory because "offset" can be really high
//if (res = (u8*)True_MapViewOfFileEx(trueMappingObject, dwDesiredAccess, ToHigh(fi.trueFileMapOffset), ToLow(fi.trueFileMapOffset), dwNumberOfBytesToMap + offset, (u8*)lpBaseAddress - offset))
//{
// // If we don't have any real MapViewOfFile on existing fileMapMem we can just drop the old one and use the new one.
// SCOPED_WRITE_LOCK(g_mappedFileTable.m_memLookupLock, lock);
// auto it = g_mappedFileTable.m_memLookup.find(fi.fileMapMem);
// if (it == g_mappedFileTable.m_memLookup.end())
// {
// if (!True_UnmapViewOfFile(fi.fileMapMem))
// {
// UBA_ASSERT(false);
// }
// fi.fileMapMem = (u8*)res;
// fi.fileMapMemSize = dwNumberOfBytesToMap;
// g_mappedFileTable.m_memLookup[res + offset] = 1;
// }
// res += offset;
//}
//else
{
offset += fi.trueFileMapOffset;
// We have retry here because this is typically where oom happen. Some of these mappings are 1gb pch files
u32 counter = 0;
do
{
TimerScope ts(g_kernelStats.mapViewOfFile);
res = (u8*)True_MapViewOfFileEx(trueMappingObject, dwDesiredAccess, ToHigh(offset), ToLow(offset), dwNumberOfBytesToMap, lpBaseAddress);
if (res)
break;
DWORD error = GetLastError();
if (error != ERROR_NOT_ENOUGH_MEMORY && error != ERROR_COMMITMENT_LIMIT)
break;
Rpc_AllocFailed(L"MapViewOfFile", error);
++counter;
} while (counter <= 10);
}
DEBUG_LOG_TRUE(L"MapViewOfFileEx", L"(INTERNAL) New FileObject for different base address %llu (%s) -> 0x%llx", uintptr_t(hFileMappingObject), HandleToName(hFileMappingObject), uintptr_t(res));
return res;
}
else if (!fi.fileMapMem)
fi.fileMapViewDesiredAccess = dwDesiredAccess;
if (!EnsureMapped(dh, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap, lpBaseAddress))
return nullptr;
SetLastError(ERROR_SUCCESS);
u8* mem = fi.fileMapMem ? fi.fileMapMem : fi.memoryFile->baseAddress;
mem += offset;
if (fi.memoryFile && (dwDesiredAccess & FILE_MAP_WRITE)) // We assume changes will happen
fi.memoryFile->isReported = false;
DEBUG_LOG_DETOURED(L"MapViewOfFileEx", L"%llu Size %llu (%s) -> 0x%llx", uintptr_t(hFileMappingObject), dwNumberOfBytesToMap, HandleToName(hFileMappingObject), u64(mem));
SCOPED_WRITE_LOCK(g_mappedFileTable.m_memLookupLock, lock);
auto& entry = g_mappedFileTable.m_memLookup[mem];
if (!entry.handle)
{
InterlockedIncrement(&dh.fileObject->refCount);
auto newDh = new DetouredHandle(dh.type);
newDh->fileObject = dh.fileObject;
entry.handle = newDh;
}
++entry.refCount;
return mem;
}
UBA_ASSERT(dh.trueHandle != INVALID_HANDLE_VALUE);
trueMappingObject = dh.trueHandle;
}
TimerScope ts(g_kernelStats.mapViewOfFile);
void* res = True_MapViewOfFileEx(trueMappingObject, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap, lpBaseAddress);
DEBUG_LOG_TRUE(L"MapViewOfFileEx", L"%llu (size %llu) (%ls) -> 0x%llx", uintptr_t(hFileMappingObject), dwNumberOfBytesToMap, HandleToName(hFileMappingObject), uintptr_t(res));
return res;
}
LPVOID Detoured_MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap)
{
DETOURED_CALL(MapViewOfFile);
return Detoured_MapViewOfFileEx(hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap, nullptr);
}
BOOL Detoured_UnmapViewOfFileEx(PVOID lpBaseAddress, ULONG UnmapFlags)
{
DETOURED_CALL(UnmapViewOfFileEx);
{
SCOPED_WRITE_LOCK(g_mappedFileTable.m_memLookupLock, lock);
auto it = g_mappedFileTable.m_memLookup.find(lpBaseAddress);
if (it != g_mappedFileTable.m_memLookup.end())
{
auto& entry = it->second;
if (!--entry.refCount)
{
if (entry.handle)
Detoured_NtClose(makeDetouredHandle(entry.handle));
g_mappedFileTable.m_memLookup.erase(it);
}
SetLastError(ERROR_SUCCESS);
return true;
}
}
auto res = True_UnmapViewOfFileEx(lpBaseAddress, UnmapFlags); (void)res;
DEBUG_LOG_TRUE(L"UnmapViewOfFileEx", L"0x%llx -> %ls", uintptr_t(lpBaseAddress), ToString(res));
// TerminateProcess unmaps same memory address twice.. causing this log entry. Ignore for now
//if (res == 0)
// ToInvestigate(L"Failed to Unmap 0x%llx -> %u", uintptr_t(lpBaseAddress), GetLastError());
return TRUE;
}
BOOL Detoured_UnmapViewOfFile(LPCVOID lpBaseAddress)
{
DETOURED_CALL(UnmapViewOfFile);
return Detoured_UnmapViewOfFileEx((PVOID)lpBaseAddress, 0);
}
DWORD Detoured_GetFinalPathNameByHandleW(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags)
{
DETOURED_CALL(GetFinalPathNameByHandleW);
HANDLE trueHandle = hFile;
if (isDetouredHandle(hFile))
{
auto& dh = asDetouredHandle(hFile);
auto fo = dh.fileObject;
UBA_ASSERT(fo);
auto& fi = *fo->fileInfo;
UBA_ASSERT(fi.originalName);
const wchar_t* fileName = fi.originalName;
if (dwFlags == 0 || dwFlags == 2)
{
if (!fo->newName.empty())
fileName = fo->newName.c_str();
StringBuffer<> buffer;
FixPath(fileName, g_virtualWorkingDir.data, g_virtualWorkingDir.count, buffer);
VirtualizePath(buffer);
u32 requiredBufferSize = buffer.count;
if (dwFlags == 2)
requiredBufferSize += 4;
if (cchFilePath <= requiredBufferSize)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
DEBUG_LOG_DETOURED(L"GetFinalPathNameByHandleW", L"%llu (%u) (%ls) -> Error (not enough mem)", uintptr_t(hFile), dwFlags, lpszFilePath);
return requiredBufferSize + 1;
}
// Unfortunately casing can be wrong here.. and we need to fix that. Let's use the directory table for that
// Note, this really only matters when building linux target from windows.. then there is path validation that errors if this is not properly fixed
g_directoryTable.GetFinalPath(buffer.Clear(), fileName);
VirtualizePath(buffer);
buffer.data[0] = ToUpper(buffer.data[0]);
if (dwFlags == 2)
buffer.Prepend(AsView(L"\\??\\"));
UBA_ASSERT(requiredBufferSize == buffer.count);
memcpy(lpszFilePath, buffer.data, (buffer.count + 1) * sizeof(tchar));
DEBUG_LOG_DETOURED(L"GetFinalPathNameByHandleW", L"%llu (%u) (%ls) -> Success", uintptr_t(hFile), dwFlags, lpszFilePath);
SetLastError(ERROR_SUCCESS);
return buffer.count;
}
trueHandle = dh.trueHandle;
UBA_ASSERTF(trueHandle != INVALID_HANDLE_VALUE, L"GetFinalPathNameByHandleW using flags (%u) on detoured file not handled (%s)", dwFlags, fileName);
}
auto res = True_GetFinalPathNameByHandleW(trueHandle, lpszFilePath, cchFilePath, dwFlags); // Calls NtQueryInformationFile and NtQueryObject
DEBUG_LOG_TRUE(L"GetFinalPathNameByHandleW", L"%llu (%u) (%ls) -> %u", uintptr_t(hFile), dwFlags, (res != 0 ? lpszFilePath : L"UNKNOWN"), res);
return res;
}
DWORD Detoured_SearchPathW(LPCWSTR lpPath, LPCWSTR lpFileName, LPCWSTR lpExtension, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR* lpFilePart)
{
DETOURED_CALL(SearchPathW);
if (g_runningRemote && !t_disallowDetour)
{
g_rules->RepairMalformedLibPath(lpFileName);
const wchar_t* original = lpFileName; (void)original;
u64 pathLen = wcslen(lpFileName);
StringBuffer<512> tempBuf;
Rpc_GetFullFileName(lpFileName, pathLen, tempBuf, true);
UBA_ASSERT(nBufferLength > pathLen);
memcpy(lpBuffer, lpFileName, pathLen * sizeof(tchar) + 2);
DEBUG_LOG_DETOURED(L"SearchPathW", L"%ls %ls -> %ls", lpPath, original, lpFileName);
SetLastError(ERROR_SUCCESS);
return DWORD(pathLen);
}
DWORD res = True_SearchPathW(lpPath, lpFileName, lpExtension, nBufferLength, lpBuffer, lpFilePart);
if (res && IsVfsEnabled() && !StartsWith(lpBuffer, g_systemRoot.data))
{
UBA_ASSERT(res < nBufferLength);
StringBuffer<> temp(lpBuffer);
if (VirtualizePath(temp))
{
memcpy(lpBuffer, temp.data, sizeof(tchar)*temp.count+1);
res = temp.count;
if (lpFilePart)
*lpFilePart = TStrrchr(lpBuffer, '\\') + 1;
}
}
DEBUG_LOG_TRUE(L"SearchPathW", L"%ls %ls -> %s", lpPath, lpFileName, lpBuffer);
return res;
}
using AdditionalLoads = Vector<HMODULE, GrowingAllocator<HMODULE>>;
using VisitedModules = std::unordered_set<StringKey, std::hash<StringKey>, std::equal_to<StringKey>, GrowingAllocator<StringKey>>;
HMODULE Recursive_LoadLibraryExW(const StringView& filePath, LPCWSTR originalName, DWORD dwFlags, AdditionalLoads& additionalLoads, VisitedModules& visitedModules)
{
if (!visitedModules.insert(ToStringKeyNoCheck(filePath.data, filePath.count)).second)
return 0;
// Important that this code is not doing allocations.. it could cause a recursive stack overflow
struct Import { wchar_t name[128]; bool isKnown; Import(const wchar_t* s, bool ik) : isKnown(ik) { wcscpy_s(name, sizeof_array(name), s); } };
std::vector<Import, GrowingAllocator<Import>> importedModules(g_memoryBlock);
{
SuppressCreateFileDetourScope cfs;
StringBuffer<256> error;
BinaryInfo info;
if (!ParseBinary(filePath, {}, info, [&](const wchar_t* import, bool isKnown, const char* const* importLoaderPaths)
{
if (!GetModuleHandleW(import))
importedModules.emplace_back(import, isKnown);
}, error))
{
UBA_ASSERTF(false, L"Failed to find imports for binary %ls (%ls)", filePath.data, originalName);
}
}
for (auto& importedModule : importedModules)
{
if (importedModule.isKnown && !g_isRunningWine)
continue;
{
SuppressCreateFileDetourScope cfs;
HMODULE checkModule = GetModuleHandleW(importedModule.name); // This function ends up in NtCreateFile when running in wine
if (checkModule)
continue;
}
if (importedModule.isKnown) // We need to catch dbghelp.dll and imagehlp.dll
{
if (HMODULE h = True_LoadLibraryExW(importedModule.name, 0, 0))
{
OnModuleLoaded(h, ToView(importedModule.name));
additionalLoads.push_back(h);
}
continue;
}
const wchar_t* path = importedModule.name;
if (path[1] == ':')
if (const wchar_t* lastSlash = wcsrchr(path, '\\'))
path = lastSlash + 1;
u64 pathLen = wcslen(path);
StringBuffer<512> tempBuf;
Rpc_GetFullFileName(path, pathLen, tempBuf, false);
if (HMODULE r = Recursive_LoadLibraryExW(ToView(path), importedModule.name, dwFlags, additionalLoads, visitedModules))
additionalLoads.push_back(r);
}
StringBuffer<512> newName;
if (originalName[1] != ':' && !filePath.Equals(originalName))
{
newName.Append(g_virtualApplicationDir).Append(originalName);
originalName = newName.data;
}
TrackInput(originalName);
DEBUG_LOG_TRUE(L"INTERNAL LoadLibraryExW", L"%ls", originalName);
SuppressCreateFileDetourScope cfs;
auto res = True_LoadLibraryExW(filePath.data, 0, 0);
if (res)
{
if (originalName[1] == ':')
{
// TODO: Virtualize!
SCOPED_WRITE_LOCK(g_loadedModulesLock, lock);
g_loadedModules[res] = originalName;
}
OnModuleLoaded(res, filePath);
}
return res;
}
#if defined(_M_ARM64)
#define ReadTebOffset(offset) (((BYTE*)NtCurrentTeb()) + offset)
#else
#define ReadTebOffset(offset) __readgsqword(offset)
#endif
HMODULE GetModuleHandleNoSyscall(const tchar* moduleName)
{
#if 1
const tchar* moduleFileName = GetFileName(moduleName);
PPEB peb = (PPEB)ReadTebOffset(0x60);
PPEB_LDR_DATA ldr = peb->Ldr;
PLIST_ENTRY head = &ldr->InMemoryOrderModuleList;
PLIST_ENTRY entry = head->Flink;
while (entry != head)
{
PLDR_DATA_TABLE_ENTRY mod = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
StringView view(mod->FullDllName.Buffer, mod->FullDllName.Length/2);
if (view.EndsWith(moduleFileName))
return (HMODULE)mod->DllBase;
entry = entry->Flink;
}
return NULL; // Module not found
#else
SuppressCreateFileDetourScope cfs;
return GetModuleHandle(moduleName);
#endif
}
HMODULE Shared_LoadLibrary(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags, bool& mightExist)
{
if (!g_runningRemote && !g_trackInputsMem || t_disallowDetour)
return NULL;
StringBuffer<> path;
path.Append(lpLibFileName).FixPathSeparators();
bool detourDll = path.EndsWith(L".exe") || path.EndsWith(L".dll");
if (detourDll && path.StartsWith(g_systemRoot.data))
detourDll = g_runningRemote && GetFileAttributes(path.data) == INVALID_FILE_ATTRIBUTES; // It might be that remote machine actually doesn't have the file in system32. then we need to detour
if (detourDll)
detourDll = !GetModuleHandleNoSyscall(lpLibFileName); // This internally can end up calling NtCreate and we don't want NtCreate to handle the download of the file because of paths
if (!detourDll)
return NULL;
u64 pathLen = path.count;
u64 toSkip = 0;
if (path.StartsWith(g_exeDir.data))
toSkip = g_exeDir.count;
else if (path.StartsWith(g_virtualApplicationDir.data))
toSkip = g_virtualApplicationDir.count;
const wchar_t* fileName = path.data + toSkip;
pathLen -= toSkip;
StringBuffer<512> tempBuf;
const wchar_t* newPath = fileName;
u64 newPathLen = pathLen;
if (g_runningRemote)
{
Rpc_GetFullFileName(newPath, newPathLen, tempBuf, false);
if (newPath[1] != ':')
{
mightExist = false;
return 0;
}
}
AdditionalLoads additionalLoads(g_memoryBlock); // Don't do allocations
VisitedModules visitedModules(g_memoryBlock);
HMODULE res = Recursive_LoadLibraryExW(ToView(newPath), fileName, dwFlags, additionalLoads, visitedModules);
for (HMODULE h : additionalLoads)
FreeLibrary(h);
return res;
}
HMODULE Detoured_LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
{
DETOURED_CALL(LoadLibraryExW);
DEBUG_LOG_DETOURED(L"LoadLibraryExW", L"(%ls)", lpLibFileName);
if (!g_rules->AllowLoadLibrary(lpLibFileName))
return 0;
if (IsKnownSystemFile(lpLibFileName))
{
SuppressCreateFileDetourScope cfs;
const tchar* fileName = lpLibFileName;
if (auto lastSeparator = TStrrchr(fileName, PathSeparator))
fileName = lastSeparator + 1;
DEBUG_LOG_TRUE(L"LoadLibraryExW", L"%ls (%s)", lpLibFileName, fileName);
return True_LoadLibraryExW(fileName, hFile, dwFlags);
}
bool mightExist = true;
if (HMODULE res = Shared_LoadLibrary(lpLibFileName, hFile, dwFlags, mightExist))
return res;
if (!mightExist)
{
DEBUG_LOG_TRUE(L"LoadLibraryExW", L"(NOTFOUND) %s", lpLibFileName);
return 0;
}
StringBuffer<> fileName;
if (lpLibFileName[1] == ':')
{
FixPath(fileName, lpLibFileName);
DevirtualizePath(fileName);
}
else
fileName.Append(lpLibFileName);
DEBUG_LOG_TRUE(L"LoadLibraryExW", L"%ls (%s)", lpLibFileName, fileName.data);
return True_LoadLibraryExW(fileName.data, hFile, dwFlags);
}
HANDLE Detoured_GetStdHandle(DWORD nStdHandle)
{
DETOURED_CALL(GetStdHandle);
if (g_isDetachedProcess)
{
HANDLE res = g_stdHandle[nStdHandle + 12]; // STD_INPUT_HANDLE -10, STD_OUTPUT_HANDLE -11, STD_ERROR_HANDLE -12
DEBUG_LOG_DETOURED(L"GetStdHandle", L"%u -> %llu", nStdHandle, u64(res));
SetLastError(ERROR_SUCCESS);
return res;
}
auto res = True_GetStdHandle(nStdHandle);
DEBUG_LOG_TRUE(L"GetStdHandle", L"%u -> %llu", nStdHandle, u64(res));
return res;
}
BOOL Detoured_SetStdHandle(DWORD nStdHandle, HANDLE hHandle)
{
DETOURED_CALL(SetStdHandle);
if (g_isDetachedProcess)
return true;
if (nStdHandle == STD_OUTPUT_HANDLE)
g_stdHandle[1] = (hHandle != g_nullFile && GetFileType(hHandle) == FILE_TYPE_CHAR) ? hHandle : 0;
else if (nStdHandle == STD_ERROR_HANDLE)
g_stdHandle[0] = (hHandle != g_nullFile && GetFileType(hHandle) == FILE_TYPE_CHAR) ? hHandle : 0;
//UBA_ASSERTF(!isDetouredHandle(hHandle), L"Trying to use handle %ls for std stream", HandleToName(hHandle));
HANDLE trueHandle = hHandle;
// TODO: Reason we have change to true handle is because this is transferred to child processes which can't use this process detoured handles
// ... need to fix this.
if (isDetouredHandle(hHandle))
{
auto& dh = asDetouredHandle(hHandle);
trueHandle = dh.trueHandle;
UBA_ASSERT(trueHandle != INVALID_HANDLE_VALUE);
}
DEBUG_LOG_TRUE(L"SetStdHandle", L"%u -> %llu", nStdHandle, u64(trueHandle));
return True_SetStdHandle(nStdHandle, trueHandle);
}
BOOL Detoured_GetConsoleMode(HANDLE hConsoleHandle, LPDWORD lpMode)
{
DETOURED_CALL(GetConsoleMode);
if (hConsoleHandle == g_stdHandle[0] || hConsoleHandle == g_stdHandle[1])
{
*lpMode = 0xffff;
return true;
}
else if (hConsoleHandle == g_stdHandle[2])
{
*lpMode = 0xffff;
return true;
}
if (g_isDetachedProcess)
{
SetLastError(ERROR_INVALID_HANDLE);
DEBUG_LOG_DETOURED(L"GetConsoleMode", L"%llu -> Error", uintptr_t(hConsoleHandle));
return false;
}
auto res = True_GetConsoleMode(hConsoleHandle, lpMode);
DEBUG_LOG_TRUE(L"GetConsoleMode", L"%llu %u-> %ls", uintptr_t(hConsoleHandle), *lpMode, ToString(res));
return res;
}
bool g_setConsoleModeCalled;
BOOL Detoured_SetConsoleMode(HANDLE hConsoleHandle, DWORD mode)
{
DETOURED_CALL(SetConsoleMode);
DEBUG_LOG_DETOURED(L"SetConsoleMode", L"%llu (0x%x)", u64(hConsoleHandle), mode);
HANDLE trueHandle = hConsoleHandle;
if (hConsoleHandle == g_stdHandle[0])
{
if (g_isDetachedProcess)
trueHandle = True_GetStdHandle(STD_ERROR_HANDLE);
}
else if (hConsoleHandle == g_stdHandle[1])
{
g_echoOn = (mode & ~503) != 0; // TODO: This might be wrong. Trying to figure out how echo off in batch files work in terms of win32 calls
DEBUG_LOG(L"Echo %s", (g_echoOn ? L"on" : L"off"));
RPC_MESSAGE(EchoOn, log)
writer.WriteBool(g_echoOn);
writer.Flush();
if (g_isDetachedProcess)
trueHandle = True_GetStdHandle(STD_OUTPUT_HANDLE);
}
else if (hConsoleHandle == g_stdHandle[0])
{
if (g_isDetachedProcess)
trueHandle = True_GetStdHandle(STD_INPUT_HANDLE);
}
else if (isDetouredHandle(hConsoleHandle))
{
g_setConsoleModeCalled = true;
DetouredHandle& dh = asDetouredHandle(hConsoleHandle);
trueHandle = dh.trueHandle;
UBA_ASSERTF(trueHandle != INVALID_HANDLE_VALUE, TC("SetConsoleMode is using %s as handle to set mode 0x%x"), HandleToName(hConsoleHandle), mode);
}
return True_SetConsoleMode(trueHandle, mode);
}
BOOL Detoured_GetConsoleTitleW(LPTSTR lpConsoleTitle, DWORD nSize)
{
DETOURED_CALL(GetConsoleTitleW);
DEBUG_LOG_DETOURED(L"GetConsoleTitleW", L"");
*lpConsoleTitle = 0;
return true;
}
BOOL Detoured_CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
{
StringView originalCmd = ToView(lpCommandLine ? lpCommandLine : TC(""));
DETOURED_CALL(CreateProcessW);
DEBUG_LOG_DETOURED(L"CreateProcessW", L"%ls %ls CreationFlags: 0x%x StartupFlags: 0x%u Stdin: %llu WorkDir: %s", lpApplicationName, originalCmd.data, dwCreationFlags, lpStartupInfo->dwFlags, u64(lpStartupInfo->hStdInput), (lpCurrentDirectory ? lpCurrentDirectory : L""));
StringView testName = ToView(lpApplicationName ? lpApplicationName : TC(""));
if (!testName.count)
{
if (!originalCmd.count)
{
SetLastError(ERROR_FILE_NOT_FOUND);
return FALSE;
}
testName = originalCmd;
}
// Debug binaries started when process crash... we don't want to detour these.
if (testName.Contains(L"winedbg") || testName.Contains(L"werfault.exe") || testName.Contains(L"vsjitdebugger.exe") || testName.Contains(L"crashpad_handler.exe"))
{
if (g_runningRemote)
{
UbaAssert(L"Unhandled exception/crash. Suppress debugger startup and try to report issue instead. This message is here to hopefully see callstack", "", 0, "", false, 0, nullptr, 1);
return false;
}
else
{
SuppressDetourScope _;
BOOL res = True_CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
True_WaitForSingleObject(lpProcessInformation->hProcess, 10000);
return res;
}
}
bool isChild = true;
// We don't care about tracking mspdbsrv or vctip.. they are services just spawned by this process
if (testName.Contains(L"mspdbsrv.exe") || testName.Contains(L"vctip.exe") || testName.Contains(L"git.exe"))
{
if (!g_runningRemote)
{
SuppressDetourScope _;
return True_CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
}
isChild = false;
}
StringView commandLineWithoutApplication;
{
const tchar* endOfApplication;
if (originalCmd.data[0] == '"')
{
const tchar* quoteEnd = TStrchr(originalCmd.data+1, '"');
UBA_ASSERT(quoteEnd);
endOfApplication = quoteEnd + 1;
}
else if (const tchar* firstSpace = TStrchr(originalCmd.data, ' '))
endOfApplication = firstSpace + 1;
else
endOfApplication = originalCmd.data + originalCmd.count;
commandLineWithoutApplication = StringView(endOfApplication, originalCmd.count - u32(endOfApplication - originalCmd.data));
}
StringBuffer<512> application;
if (lpApplicationName && *lpApplicationName)
{
if (lpApplicationName[1] == ':') // Only fixup absolute paths (since we want to devirtualize them
FixPath(application, lpApplicationName);
else
application.Append(lpApplicationName);
UBA_ASSERTF(application.count, TC("Invalid application name from application field: %s"), lpApplicationName);
}
else
{
StringBuffer<512> temp;
if (originalCmd.data[0] == '"')
temp.Append(originalCmd.data+1, commandLineWithoutApplication.data - originalCmd.data - 2);
else
temp.Append(originalCmd.data, commandLineWithoutApplication.data - originalCmd.data - 1);
if (temp[1] == ':')
FixPath(application, temp.data);
else
application.Append(temp.data);
UBA_ASSERTF(application.count, TC("Invalid application from command line (name: %s)"), originalCmd.data);
}
DevirtualizePath(application);
bool startSuspended = (dwCreationFlags & CREATE_SUSPENDED) != 0;
StringBuffer<> workingDir(lpCurrentDirectory ? lpCurrentDirectory : g_virtualWorkingDir.data);
DevirtualizePath(workingDir);
if (testName.Contains(TC("UbaCli.exe")))
{
{
RPC_MESSAGE(RunSpecialProgram, createProcess)
writer.WriteString(application);
writer.WriteLongString(commandLineWithoutApplication);
writer.WriteString(workingDir);
writer.Flush();
}
Rpc_UpdateTables();
StringBuffer<256> cmdExe;
cmdExe.Append(g_systemRoot).EnsureEndsWithSlash().Append(TCV("system32\\cmd.exe"));
SuppressDetourScope _;
return True_CreateProcessW(cmdExe.data, (LPWSTR)TC("cmd.exe /c exit 0"), lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, workingDir.data, lpStartupInfo, lpProcessInformation);
}
TString currentDir;
u32 processId = 0;
char dll[1024];
{
RPC_MESSAGE(CreateProcess, createProcess)
writer.WriteString(application);
writer.WriteLongString(commandLineWithoutApplication);
writer.WriteString(workingDir);
writer.WriteBool(startSuspended);
writer.WriteBool(isChild);
writer.Flush();
BinaryReader reader;
processId = reader.ReadU32();
UBA_ASSERTF(processId > 0, L"Failed to create process %s", originalCmd.data);
if (!processId)
return FALSE;
reader.Skip(sizeof(u32)); // Rules index
u32 dllNameSize = reader.ReadU32();
reader.ReadBytes(dll, dllNameSize);
dll[dllNameSize] = 0;
currentDir = reader.ReadString();
reader.ReadString(application.Clear());
DEBUG_LOG_PIPE(L"CreateProcess", L"%ls %ls", application.data, originalCmd.data);
}
auto handleFileDetour = [&](HANDLE& handle, HandleType type, const tchar* name, DWORD nativeHandleId)
{
if (!isDetouredHandle(handle))
return;
DetouredHandle& dh = asDetouredHandle(handle);
if (dh.type == type)
{
handle = g_isDetachedProcess ? 0 : True_GetStdHandle(nativeHandleId);
return;
}
handle = dh.trueHandle;
if (dh.trueHandle != INVALID_HANDLE_VALUE)
return;
auto assertGuard = MakeGuard([&]() { UBA_ASSERTF(false, L"%s is detoured and there is no proper handle (%s)", name, lpApplicationName); });
if (dh.type != HandleType_File)
return;
if (!dh.fileObject)
return;
auto& fo = *dh.fileObject;
if (!fo.fileInfo)
return;
FileInfo& fi = *fo.fileInfo;
auto mf = fi.memoryFile;
if (!mf)
return;
if (mf->isLocalOnly)
return;
// TODO: This is a memory file
assertGuard.Cancel();
};
handleFileDetour(lpStartupInfo->hStdError, HandleType_StdErr, TC("hStdError"), STD_ERROR_HANDLE);
handleFileDetour(lpStartupInfo->hStdOutput, HandleType_StdOut, TC("hStdOutput"), STD_OUTPUT_HANDLE);
if (isDetouredHandle(lpStartupInfo->hStdInput))
{
DetouredHandle& dh = asDetouredHandle(lpStartupInfo->hStdInput);
if (dh.type == HandleType_StdIn)
lpStartupInfo->hStdInput = g_isDetachedProcess ? 0 : True_GetStdHandle(STD_INPUT_HANDLE);
else if (dh.type == HandleType_StdErr)
lpStartupInfo->hStdInput = g_isDetachedProcess ? 0 : True_GetStdHandle(STD_ERROR_HANDLE);
else
{
UBA_ASSERTF(false, L"hStdInput is detoured (%s)", lpApplicationName);
lpStartupInfo->hStdInput = dh.trueHandle;
}
}
lpStartupInfo->dwFlags |= STARTF_USESHOWWINDOW;
lpStartupInfo->wShowWindow = SW_HIDE;
if (g_rules->AllowDetach())
dwCreationFlags |= DETACHED_PROCESS;
else if (!g_setConsoleModeCalled) // If console mode has been called there are probably some sort of stdin/out rerouting stuff.. so let's not set CREATE_NO_WINDOW
dwCreationFlags |= CREATE_NO_WINDOW;
dwCreationFlags |= CREATE_SUSPENDED;
BOOL res = true;
u32 lastError = ERROR_SUCCESS;
u32 retryCount = 0;
++t_disallowDetour;
LPCSTR dlls[] = { dll };
while (true)
{
res = true;
if (DetourCreateProcessWithDllsW(application.data, lpCommandLine, NULL, NULL, bInheritHandles, dwCreationFlags, lpEnvironment, currentDir.c_str(), lpStartupInfo, lpProcessInformation, sizeof_array(dlls), dlls, True_CreateProcessW))
break;
res = false;
lastError = GetLastError();
if (lastError != ERROR_ACCESS_DENIED && lastError != ERROR_INTERNAL_ERROR)
break;
// We have no idea why this is happening.. but it seems to recover when retrying.
// Could it be related to two process spawning at the exact same time or something?
// It happens extremely rarely and can happen on both host and remotes
if (retryCount++ >= 5)
break;
const wchar_t* errorText = lastError == ERROR_ACCESS_DENIED ? L"access denied" : L"internal error";
Rpc_WriteLogf(L"DetourCreateProcessWithDllEx failed with %ls, retrying %ls (Working dir: %ls)", errorText, originalCmd.data, currentDir.c_str());
Sleep(100 + (rand() % 200)); // We have no idea
continue;
}
--t_disallowDetour;
if (isChild)
{
RPC_MESSAGE(StartProcess, createProcess)
--g_stats.createProcess.count; // Don't want double count for one process
writer.WriteU32(processId);
writer.WriteBool(res);
writer.WriteU32(lastError);
writer.WriteU64(u64(lpProcessInformation->hProcess));
writer.WriteU32(lpProcessInformation->dwProcessId);
writer.WriteU64(u64(lpProcessInformation->hThread));
writer.Flush();
DEBUG_LOG_PIPE(L"StartProcess", L"%ls %ls", application.data, originalCmd.data);
}
UBA_ASSERTF(res, L"Failed to spawn process %ls (Error code: %u)", originalCmd.data, lastError);
HANDLE trueHandle = lpProcessInformation->hProcess;
if (!res || trueHandle == INVALID_HANDLE_VALUE)
{
DEBUG_LOG_DETOURED(L"CreateProcessW", L"FAILED");
return FALSE;
}
auto detouredHandle = new DetouredHandle(HandleType_Process);
detouredHandle->trueHandle = trueHandle;
lpProcessInformation->hProcess = makeDetouredHandle(detouredHandle);
DEBUG_LOG_DETOURED(L"CreateProcessW", L"%llu (0x%llx)", u64(lpProcessInformation->hProcess), u64(trueHandle));
return TRUE;
}
BOOL Detoured_CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
{
wchar_t* lpApplicationNameW = nullptr;
TString lpApplicationNameTemp;
if (lpApplicationName)
{
lpApplicationNameTemp = TString(lpApplicationName, lpApplicationName + strlen(lpApplicationName));
lpApplicationNameW = lpApplicationNameTemp.data();
}
wchar_t* lpCommandLineW = nullptr;
TString lpCommandLineTemp;
if (lpCommandLine)
{
lpCommandLineTemp = TString(lpCommandLine, lpCommandLine + strlen(lpCommandLine));
lpCommandLineW = lpCommandLineTemp.data();
}
wchar_t* lpCurrentDirectoryW = nullptr;
TString lpCurrentDirectoryTemp;
if (lpCurrentDirectory)
{
lpCurrentDirectoryTemp = TString(lpCurrentDirectory, lpCurrentDirectory + strlen(lpCurrentDirectory));
lpCurrentDirectoryW = lpCurrentDirectoryTemp.data();
}
UBA_ASSERT(!lpStartupInfo->lpReserved);
UBA_ASSERT(!lpStartupInfo->lpDesktop);
UBA_ASSERT(!lpStartupInfo->lpTitle);
STARTUPINFOW lpStartupInfoW = *(LPSTARTUPINFOW)lpStartupInfo;
return Detoured_CreateProcessW(lpApplicationNameW, lpCommandLineW, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectoryW, &lpStartupInfoW, lpProcessInformation);
}
void Detoured_ExitProcess(UINT uExitCode)
{
// Can't log this one
DETOURED_CALL(ExitProcess);
//DEBUG_LOG_TRUE(L"ExitProcess", L"(%u)", uExitCode);
{
CloseCaches();
SendExitMessage((DWORD)uExitCode, GetTime());
PostDeinit();
}
#if UBA_DEBUG_LOG_ENABLED
FlushDebugLog();
#endif
True_ExitProcess(uExitCode);
}
BOOL Detoured_TerminateProcess(HANDLE hProcess, UINT uExitCode)
{
DETOURED_CALL(TerminateProcess);
DEBUG_LOG_DETOURED(L"TerminateProcess", L"%llu (%ls) ExitCode: %u", u64(hProcess), HandleToName(hProcess), uExitCode);
// Some processes actually call terminateprocess on themselves when exiting, ugh.
if (hProcess == INVALID_HANDLE_VALUE)
{
CloseCaches();
SendExitMessage((DWORD)uExitCode, GetTime());
PostDeinit();
}
else
{
if (isDetouredHandle(hProcess))
hProcess = asDetouredHandle(hProcess).trueHandle;
if (GetProcessId(hProcess) == GetCurrentProcessId())
{
CloseCaches();
SendExitMessage((DWORD)uExitCode, GetTime());
PostDeinit();
}
}
return True_TerminateProcess(hProcess, uExitCode);
}
BOOL Detoured_GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode)
{
DETOURED_CALL(GetExitCodeProcess);
HANDLE trueHandle = hProcess;
if (isDetouredHandle(hProcess))
trueHandle = asDetouredHandle(hProcess).trueHandle;
BOOL res = True_GetExitCodeProcess(trueHandle, lpExitCode);
DEBUG_LOG_DETOURED(L"GetExitCodeProcess", L"%llu Exit code: %u -> %ls", uintptr_t(trueHandle), *lpExitCode, ToString(res));
if (res != STILL_ACTIVE)
Rpc_UpdateTables();
return res;
}
BOOL Detoured_CreateTimerQueueTimer(PHANDLE phNewTimer, HANDLE TimerQueue, WAITORTIMERCALLBACK Callback, PVOID Parameter, DWORD DueTime, DWORD Period, ULONG Flags)
{
DETOURED_CALL(CreateTimerQueueTimer);
BOOL res = True_CreateTimerQueueTimer(phNewTimer, TimerQueue, Callback, Parameter, DueTime, Period, Flags);
DEBUG_LOG_TRUE(L"CreateTimerQueueTimer", L"%p -> %ls", *phNewTimer, ToString(res));
return res;
}
BOOL Detoured_DeleteTimerQueueTimer(HANDLE TimerQueue, HANDLE Timer, HANDLE CompletionEvent)
{
DETOURED_CALL(DeleteTimerQueueTimer);
BOOL res = True_DeleteTimerQueueTimer(TimerQueue, Timer, CompletionEvent);
if (!res && IsRunningWine())
{
DEBUG_LOG_DETOURED(L"DeleteTimerQueueTimer", L"%p %p %p -> %ls (WINE ignored)", TimerQueue, Timer, CompletionEvent, ToString(res));
return true;
}
DEBUG_LOG_TRUE(L"DeleteTimerQueueTimer", L"%p %p %p -> %ls", TimerQueue, Timer, CompletionEvent, ToString(res));
return res;
}
DWORD Detoured_WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
{
return WaitForSingleObjectEx(hHandle, dwMilliseconds, false);
}
// Both WaitForSingleObject and WaitForSingleObjectEx is needed to support Wine
DWORD Detoured_WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, BOOL bAlertable)
{
DETOURED_CALL(WaitForSingleObjectEx);
bool isProcess = false;
HANDLE trueHandle = hHandle;
if (isDetouredHandle(hHandle))
{
DetouredHandle& dh = asDetouredHandle(hHandle);
trueHandle = asDetouredHandle(hHandle).trueHandle;
isProcess = dh.type == HandleType_Process;
}
auto res = True_WaitForSingleObjectEx(trueHandle, dwMilliseconds, bAlertable);
if (res != WAIT_OBJECT_0 || !isProcess)
return res;
#if UBA_DEBUG_LOG_ENABLED
if (isLogging())
{
auto lastError = GetLastError();
DWORD exitCode;
True_GetExitCodeProcess(trueHandle, &exitCode);
DEBUG_LOG_DETOURED(L"WaitForSingleObjectEx", L"for process %llu (0x%llx). Exit code: %u", u64(hHandle), u64(trueHandle), exitCode);
SetLastError(lastError);
}
#endif
Rpc_UpdateTables();
return res;
}
DWORD Detoured_WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds)
{
DETOURED_CALL(WaitForMultipleObjects);
bool isProcess = false;
HANDLE* tempHandles = (HANDLE*)_malloca(nCount * sizeof(HANDLE));
if (!tempHandles)
FatalError(1355, L"Here to please static analyzer");
for (u32 i = 0; i != nCount; ++i)
{
HANDLE hHandle = lpHandles[i];
if (isDetouredHandle(hHandle))
{
DetouredHandle& dh = asDetouredHandle(hHandle);
hHandle = asDetouredHandle(hHandle).trueHandle;
isProcess |= dh.type == HandleType_Process;
}
tempHandles[i] = hHandle;
}
auto res = True_WaitForMultipleObjectsEx(nCount, tempHandles, bWaitAll, dwMilliseconds, false);
#ifndef __clang_analyzer__
_freea(tempHandles);
#endif
if (!isProcess || res != WAIT_OBJECT_0)
return res;
DEBUG_LOG_DETOURED(L"WaitForMultipleObjects", L"");
Rpc_UpdateTables();
return res;
}
DWORD Detoured_WaitForMultipleObjectsEx(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll, DWORD dwMilliseconds, BOOL bAlertable)
{
DETOURED_CALL(WaitForMultipleObjectsEx);
bool isProcess = false;
HANDLE* tempHandles = (HANDLE*)_malloca(nCount * sizeof(HANDLE));
if (!tempHandles)
FatalError(1355, L"Here to please static analyzer");
for (u32 i = 0; i != nCount; ++i)
{
HANDLE hHandle = lpHandles[i];
if (isDetouredHandle(hHandle))
{
DetouredHandle& dh = asDetouredHandle(hHandle);
hHandle = asDetouredHandle(hHandle).trueHandle;
isProcess |= dh.type == HandleType_Process;
}
tempHandles[i] = hHandle;
}
auto res = True_WaitForMultipleObjectsEx(nCount, tempHandles, bWaitAll, dwMilliseconds, bAlertable);
#ifndef __clang_analyzer__
_freea(tempHandles);
#endif
if (!isProcess || res != WAIT_OBJECT_0)
return res;
DEBUG_LOG_DETOURED(L"WaitForMultipleObjectsEx", L"");
Rpc_UpdateTables();
return res;
}
LANGID Detoured_GetUserDefaultUILanguage()
{
DETOURED_CALL(GetUserDefaultUILanguage);
DEBUG_LOG_DETOURED(L"GetUserDefaultUILanguage", L"");
//UBA_ASSERTF(g_runningRemote || True_GetUserDefaultUILanguage() == g_uiLanguage, L"Session process has language id %u while this process is set to use %u", g_uiLanguage, True_GetUserDefaultUILanguage());
return LANGID(g_uiLanguage);
}
BOOL Detoured_GetThreadPreferredUILanguages(DWORD dwFlags, PULONG pulNumLanguages, PZZWSTR pwszLanguagesBuffer, PULONG pcchLanguagesBuffer)
{
DETOURED_CALL(GetThreadPreferredUILanguages);
if (dwFlags & MUI_LANGUAGE_ID)
{
UBA_ASSERT(pulNumLanguages);
UBA_ASSERT(pcchLanguagesBuffer);
//UBA_ASSERT(!(dwFlags & ~MUI_LANGUAGE_ID);
*pulNumLanguages = 1;
*pcchLanguagesBuffer = 6;
if (!pwszLanguagesBuffer)
{
DEBUG_LOG_DETOURED(L"GetThreadPreferredUILanguages", L"(nobuf) -> TRUE");
return TRUE;
}
swprintf_s(pwszLanguagesBuffer, 6, L"%04x", g_uiLanguage);
pwszLanguagesBuffer[5] = 0;
DEBUG_LOG_DETOURED(L"GetThreadPreferredUILanguages", L"(%s) -> TRUE", pwszLanguagesBuffer);
return TRUE;
}
else // MUI_LANGUAGE_NAME
{
// TODO: We need to get the string of g_uiLanguage
//UBA_ASSERTF(!g_runningRemote, L"GetThreadPreferredUILanguages uses unsupported flag on remote execution: %u", dwFlags);
auto res = True_GetThreadPreferredUILanguages(dwFlags, pulNumLanguages, pwszLanguagesBuffer, pcchLanguagesBuffer);
DEBUG_LOG_TRUE(L"GetThreadPreferredUILanguages", L"-> %ls", ToString(res));
return res;
}
}
#if defined(DETOURED_INCLUDE_DEBUG)
BOOL Detoured_GetDiskFreeSpaceExA(LPCSTR lpDirectoryName, PULARGE_INTEGER lpFreeBytesAvailableToCaller, PULARGE_INTEGER lpTotalNumberOfBytes, PULARGE_INTEGER lpTotalNumberOfFreeBytes)
{
DETOURED_CALL(GetDiskFreeSpaceExA);
DEBUG_LOG_TRUE(L"GetDiskFreeSpaceExA", L"%hs", lpDirectoryName);
return True_GetDiskFreeSpaceExA(lpDirectoryName, lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes);
}
DWORD Detoured_GetLongPathNameA(LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer)
{
DETOURED_CALL(GetLongPathNameA);
DEBUG_LOG_TRUE(L"GetLongPathNameA", L"");
UBA_ASSERT(!g_runningRemote);
return True_GetLongPathNameA(lpszShortPath, lpszLongPath, cchBuffer);
}
BOOL Detoured_GetVolumePathNameA(LPCSTR lpszFileName, LPSTR lpszVolumePathName, DWORD cchBufferLength)
{
DETOURED_CALL(GetVolumePathNameA);
DEBUG_LOG_TRUE(L"GetVolumePathNameA", L"%S", lpszFileName);
return True_GetVolumePathNameA(lpszFileName, lpszVolumePathName, cchBufferLength);
}
DWORD Detoured_GetFileAttributesA(LPCSTR lpFileName)
{
// Is verified that both windows and wine are calling GetFileAttributesW
DEBUG_LOG_TRUE(L"GetFileAttributesA", L"");
return True_GetFileAttributesA(lpFileName);
}
BOOL Detoured_GetFileAttributesExA(LPCSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation)
{
DETOURED_CALL(GetFileAttributesExA);
DEBUG_LOG_TRUE(L"GetFileAttributesExA", L""); // Calls ExW on both windows and wine
return True_GetFileAttributesExA(lpFileName, fInfoLevelId, lpFileInformation);
}
#if !defined(_M_ARM64)
HMODULE Detoured_LoadLibraryW(LPCWSTR lpLibFileName)
{
DETOURED_CALL(LoadLibraryW);
DEBUG_LOG_TRUE(L"LoadLibraryW", L"(%ls)", lpLibFileName);
return True_LoadLibraryW(lpLibFileName);
}
#endif
BOOL Detoured_SetDllDirectoryW(LPCWSTR lpPathName)
{
DETOURED_CALL(SetDllDirectoryW);
BOOL res = True_SetDllDirectoryW(lpPathName);
DEBUG_LOG_TRUE(L"SetDllDirectoryW", L"(%ls) -> %s", lpPathName, ToString(res));
return res;
}
BOOL Detoured_GetDllDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer)
{
DETOURED_CALL(GetDllDirectoryW);
BOOL res = True_GetDllDirectoryW(nBufferLength, lpBuffer);
DEBUG_LOG_TRUE(L"GetDllDirectoryW", L"%u -> %s (%ls)", nBufferLength, ToString(res), lpBuffer);
return res;
}
DWORD Detoured_GetModuleBaseNameA(HANDLE hProcess, HMODULE hModule, LPSTR lpBaseName, DWORD nSize)
{
DETOURED_CALL(GetModuleBaseNameA);
DEBUG_LOG_TRUE(L"GetModuleBaseNameA", L"");
char temp[1024];
DWORD res = GetModuleFileNameExA(hProcess, hModule, temp, sizeof_array(temp)); (void)res;
UBA_ASSERT(res != 0 && res < sizeof_array(temp));
char* moduleName = temp;
if (char* lastSlash = strrchr(temp, '\\'))
moduleName = lastSlash + 1;
DWORD len = (DWORD)strlen(moduleName);
UBA_ASSERTF(len < nSize, L"Module name %hs does not fit in buffer size (is %u, needs %u)", moduleName, nSize, len);
strcpy_s(lpBaseName, nSize, moduleName);
memset(lpBaseName + len, 0, nSize - len);
return len;
}
DWORD Detoured_GetModuleBaseNameW(HANDLE hProcess, HMODULE hModule, LPWSTR lpBaseName, DWORD nSize)
{
DETOURED_CALL(GetModuleBaseNameW);
DEBUG_LOG_TRUE(L"GetModuleBaseNameW", L"");
wchar_t temp[1024];
DWORD res = GetModuleFileNameExW(hProcess, hModule, temp, sizeof_array(temp)); (void)res;
UBA_ASSERT(res != 0 && res < sizeof_array(temp));
wchar_t* moduleName = temp;
if (wchar_t* lastSlash = wcsrchr(temp, '\\'))
moduleName = lastSlash + 1;
DWORD len = (DWORD)wcslen(moduleName);
UBA_ASSERTF(len < nSize, L"Module name %s does not fit in buffer size (is %u, needs %u)", moduleName, nSize, len);
wcscpy_s(lpBaseName, nSize, moduleName);
return len;
}
LPTOP_LEVEL_EXCEPTION_FILTER Detoured_SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
DETOURED_CALL(SetUnhandledExceptionFilter);
DEBUG_LOG_TRUE(L"SetUnhandledExceptionFilter", L"");
return True_SetUnhandledExceptionFilter(lpTopLevelExceptionFilter);
}
BOOL Detoured_FlushInstructionCache(HANDLE hProcess, LPCVOID lpBaseAddress, SIZE_T dwSize)
{
DETOURED_CALL(FlushInstructionCache);
UBA_ASSERT(!isDetouredHandle(hProcess));
BOOL res = True_FlushInstructionCache(hProcess, lpBaseAddress, dwSize);
//DEBUG_LOG_DETOURED(L"FlushInstructionCache", L"%llu -> %ls", uintptr_t(hProcess), ToString(res));
return res;
}
HANDLE Detoured_CreateFile2(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwCreationDisposition, LPCREATEFILE2_EXTENDED_PARAMETERS pCreateExParams)
{
DETOURED_CALL(CreateFile2);
DEBUG_LOG_TRUE(L"CreateFile2", L"(%ls)", lpFileName);
return True_CreateFile2(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, pCreateExParams);
}
HANDLE Detoured_CreateFileTransactedW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes, HANDLE hTemplateFile, HANDLE hTransaction, PUSHORT pusMiniVersion, PVOID lpExtendedParameter)
{
DETOURED_CALL(CreateFileTransactedW);
DEBUG_LOG_TRUE(L"CreateFileTransacted", L"(%ls)", lpFileName);
return True_CreateFileTransactedW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile, hTransaction, pusMiniVersion, lpExtendedParameter);
}
HFILE Detoured_OpenFile(LPCSTR lpFileName, LPOFSTRUCT lpReOpenBuff, UINT uStyle)
{
DETOURED_CALL(OpenFile);
DEBUG_LOG_TRUE(L"OpenFile", L"(%hs)", lpFileName);
return True_OpenFile(lpFileName, lpReOpenBuff, uStyle);
}
HANDLE Detoured_ReOpenFile(HANDLE hOriginalFile, DWORD dwDesiredAccess, DWORD dwShareMode, DWORD dwFlagsAndAttributes)
{
DETOURED_CALL(ReOpenFile);
if (isDetouredHandle(hOriginalFile))
{
DEBUG_LOG_DETOURED(L"TODO ReOpenFile", L"(%ls)", HandleToName(hOriginalFile));
return INVALID_HANDLE_VALUE;
}
DEBUG_LOG_TRUE(L"ReOpenFile", L"(%ls)", HandleToName(hOriginalFile));
return True_ReOpenFile(hOriginalFile, dwDesiredAccess, dwShareMode, dwFlagsAndAttributes);
}
BOOL Detoured_ReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
DETOURED_CALL(ReadFileEx);
DEBUG_LOG_TRUE(L"ReadFileEx", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
UBA_ASSERT(!isDetouredHandle(hFile));
UBA_ASSERT(!isListDirectoryHandle(hFile));
TimerScope ts(g_kernelStats.readFile);
return True_ReadFileEx(hFile, lpBuffer, nNumberOfBytesToRead, lpOverlapped, lpCompletionRoutine);
}
BOOL Detoured_ReadFileScatter(HANDLE hFile, FILE_SEGMENT_ELEMENT* aSegmentArray, DWORD nNumberOfBytesToRead, LPDWORD lpReserved, LPOVERLAPPED lpOverlapped)
{
DETOURED_CALL(ReadFileScatter);
DEBUG_LOG_TRUE(L"ReadFileScatter", L"%llu (%ls)", uintptr_t(hFile), HandleToName(hFile));
UBA_ASSERT(!isDetouredHandle(hFile));
UBA_ASSERT(!isListDirectoryHandle(hFile));
return True_ReadFileScatter(hFile, aSegmentArray, nNumberOfBytesToRead, lpReserved, lpOverlapped);
}
#if !defined(_M_ARM64)
void Detoured_SetLastError(DWORD dwErrCode)
{
DETOURED_CALL(SetLastError);
if (dwErrCode != ERROR_SUCCESS)
while (false) {}
True_SetLastError(dwErrCode);
}
#endif
#if !defined(_M_ARM64)
DWORD Detoured_GetLastError()
{
DETOURED_CALL(GetLastError);
auto res = True_GetLastError();
if (res != ERROR_SUCCESS)
while (false) {}
return res;
}
#endif
BOOL Detoured_SetFileValidData(HANDLE hFile, LONGLONG ValidDataLength)
{
DETOURED_CALL(SetFileValidData);
DEBUG_LOG_TRUE(L"SetFileValidData", L"(%ls)", HandleToName(hFile));
UBA_ASSERT(!isDetouredHandle(hFile));
return True_SetFileValidData(hFile, ValidDataLength);
}
BOOL Detoured_ReplaceFileW(LPCWSTR lpReplacedFileName, LPCWSTR lpReplacementFileName, LPCWSTR lpBackupFileName, DWORD dwReplaceFlags, LPVOID lpExclude, LPVOID lpReserved)
{
UBA_ASSERT(!g_runningRemote);
DETOURED_CALL(ReplaceFileW);
DEBUG_LOG_TRUE(L"ReplaceFileW", L"");
return True_ReplaceFileW(lpReplacedFileName, lpReplacementFileName, lpBackupFileName, dwReplaceFlags, lpExclude, lpReserved);
}
BOOL Detoured_CreateHardLinkA(LPCSTR lpFileName, LPCSTR lpExistingFileName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
UBA_ASSERT(!g_runningRemote);
DETOURED_CALL(CreateHardLinkA);
DEBUG_LOG_TRUE(L"CreateHardLinkA", L"");
return True_CreateHardLinkA(lpFileName, lpExistingFileName, lpSecurityAttributes);
}
BOOL Detoured_DeleteFileA(LPCSTR lpFileName)
{
UBA_ASSERT(!g_runningRemote);
DETOURED_CALL(DeleteFileA);
DEBUG_LOG_TRUE(L"DeleteFileA", L"");
return True_DeleteFileA(lpFileName);
}
BOOL Detoured_SetCurrentDirectoryA(LPCSTR lpPathName)
{
DETOURED_CALL(SetCurrentDirectoryA);
DEBUG_LOG_TRUE(L"SetCurrentDirectoryA", L"%hs", lpPathName);
return True_SetCurrentDirectoryA(lpPathName);
}
BOOLEAN Detoured_CreateSymbolicLinkW(LPCWSTR lpSymlinkFileName, LPCWSTR lpTargetFileName, DWORD dwFlags)
{
UBA_ASSERT(!g_runningRemote);
DETOURED_CALL(CreateSymbolicLinkW);
DEBUG_LOG_TRUE(L"CreateSymbolicLinkW", L"");
return True_CreateSymbolicLinkW(lpSymlinkFileName, lpTargetFileName, dwFlags);
}
BOOLEAN Detoured_CreateSymbolicLinkA(LPCSTR lpSymlinkFileName, LPCSTR lpTargetFileName, DWORD dwFlags)
{
UBA_ASSERT(!g_runningRemote);
DETOURED_CALL(CreateSymbolicLinkA);
DEBUG_LOG_TRUE(L"CreateSymbolicLinkA", L"");
return True_CreateSymbolicLinkA(lpSymlinkFileName, lpTargetFileName, dwFlags);
}
DWORD Detoured_SetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpValue)
{
DETOURED_CALL(SetEnvironmentVariableW);
DWORD res = True_SetEnvironmentVariableW(lpName, lpValue);
DEBUG_LOG_TRUE(L"SetEnvironmentVariableW", L"%ls -> %ls", lpName, lpValue);
return res;
}
DWORD Detoured_GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize)
{
DETOURED_CALL(GetEnvironmentVariableW);
DWORD res = True_GetEnvironmentVariableW(lpName, lpBuffer, nSize);
DEBUG_LOG_TRUE(L"GetEnvironmentVariableW", L"%ls -> %ls", lpName, res ? lpBuffer : L"NOTFOUND");
return res;
}
DWORD Detoured_GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize)
{
DETOURED_CALL(GetEnvironmentVariableA);
DWORD res = True_GetEnvironmentVariableA(lpName, lpBuffer, nSize);
DEBUG_LOG_TRUE(L"GetEnvironmentVariableA", L"%hs -> %hs", lpName, res ? lpBuffer : "NOTFOUND");
return res;
}
LPWCH Detoured_GetEnvironmentStringsW()
{
DETOURED_CALL(GetEnvironmentStringsW);
DEBUG_LOG_TRUE(L"GetEnvironmentStringsW", L"");
auto res = True_GetEnvironmentStringsW();
#if 0 // Enable to print out environment variables in the log
auto it = res;
while (*it)
{
DEBUG_LOG(L" VAR: %ls", it);
it += wcslen(it) + 1;
}
#endif
return res;
}
DWORD Detoured_ExpandEnvironmentStringsW(LPCWSTR lpSrc, LPWSTR lpDst, DWORD nSize)
{
DETOURED_CALL(ExpandEnvironmentStringsW);
DEBUG_LOG_TRUE(L"ExpandEnvironmentStringsW", L"%ls", lpSrc);
return True_ExpandEnvironmentStringsW(lpSrc, lpDst, nSize);
}
UINT Detoured_GetTempFileNameW(LPCWSTR lpPathName, LPCWSTR lpPrefixString, UINT uUnique, LPTSTR lpTempFileName)
{
DETOURED_CALL(GetTempFileNameW);
DEBUG_LOG_TRUE(L"GetTempFileNameW", L"%s %s", lpPathName, lpPrefixString);
return True_GetTempFileNameW(lpPathName, lpPrefixString, uUnique, lpTempFileName);
}
BOOL Detoured_CreateDirectoryExW(LPCWSTR lpTemplateDirectory, LPCWSTR lpNewDirectory, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
DETOURED_CALL(CreateDirectoryExW);
DEBUG_LOG_TRUE(L"CreateDirectoryExW", L"");
return True_CreateDirectoryExW(lpTemplateDirectory, lpNewDirectory, lpSecurityAttributes);
}
BOOL Detoured_DecryptFileW(LPCWSTR lpFileName, DWORD dwReserved)
{
DETOURED_CALL(DecryptFileW);
DEBUG_LOG_TRUE(L"DecryptFileW", L"");
return True_DecryptFileW(lpFileName, dwReserved);
}
BOOL Detoured_DecryptFileA(LPCSTR lpFileName, DWORD dwReserved)
{
DETOURED_CALL(DecryptFileA);
DEBUG_LOG_TRUE(L"DecryptFileA", L"");
return True_DecryptFileA(lpFileName, dwReserved);
}
BOOL Detoured_EncryptFileW(LPCWSTR lpFileName)
{
DETOURED_CALL(EncryptFileW);
DEBUG_LOG_TRUE(L"EncryptFileW", L"");
return True_EncryptFileW(lpFileName);
}
BOOL Detoured_EncryptFileA(LPCSTR lpFileName)
{
DETOURED_CALL(EncryptFileA);
DEBUG_LOG_TRUE(L"EncryptFileA", L"");
return True_EncryptFileA(lpFileName);
}
DWORD Detoured_OpenEncryptedFileRawW(LPCWSTR lpFileName, ULONG ulFlags, PVOID* pvContext)
{
DETOURED_CALL(OpenEncryptedFileRawW);
DEBUG_LOG_TRUE(L"OpenEncryptedFileRawW", L"");
return True_OpenEncryptedFileRawW(lpFileName, ulFlags, pvContext);
}
DWORD Detoured_OpenEncryptedFileRawA(LPCSTR lpFileName, ULONG ulFlags, PVOID* pvContext)
{
DETOURED_CALL(OpenEncryptedFileRawA);
DEBUG_LOG_TRUE(L"OpenEncryptedFileRawA", L"");
return True_OpenEncryptedFileRawA(lpFileName, ulFlags, pvContext);
}
HANDLE Detoured_OpenFileById(HANDLE hFile, LPFILE_ID_DESCRIPTOR lpFileID, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwFlags)
{
DETOURED_CALL(OpenFileById);
DEBUG_LOG_TRUE(L"OpenFileById", L"");
UBA_ASSERT(!isDetouredHandle(hFile));
return True_OpenFileById(hFile, lpFileID, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlags);
}
HANDLE Detoured_CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName)
{
DETOURED_CALL(CreateEvent);
if (lpName)
{
DEBUG_LOG_TRUE(L"CreateEvent", L"%ls", lpName);
}
return True_CreateEventW(lpEventAttributes, bManualReset, bInitialState, lpName);
}
HANDLE Detoured_CreateEventExW(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCWSTR lpName, DWORD dwFlags, DWORD dwDesiredAccess)
{
DETOURED_CALL(CreateEventEx);
if (lpName)
{
DEBUG_LOG_TRUE(L"CreateEventEx", L"%ls", lpName);
}
return True_CreateEventExW(lpEventAttributes, lpName, dwFlags, dwDesiredAccess);
}
HANDLE Detoured_CreateMutexExW(LPSECURITY_ATTRIBUTES lpMutexAttributes, LPCWSTR lpName, DWORD dwFlags, DWORD dwDesiredAccess)
{
DETOURED_CALL(CreateMutexEx);
if (lpName)
{
DEBUG_LOG_TRUE(L"CreateMutexEx", L"%ls", lpName);
}
return True_CreateMutexExW(lpMutexAttributes, lpName, dwFlags, dwDesiredAccess);
}
HANDLE Detoured_CreateWaitableTimerExW(LPSECURITY_ATTRIBUTES lpTimerAttributes, LPCWSTR lpTimerName, DWORD dwFlags, DWORD dwDesiredAccess)
{
DETOURED_CALL(CreateWaitableTimerExW);
if (lpTimerName)
{
DEBUG_LOG_TRUE(L"CreateWaitableTimerExW", L"%ls", lpTimerName);
}
return True_CreateWaitableTimerExW(lpTimerAttributes, lpTimerName, dwFlags, dwDesiredAccess);
}
HANDLE Detoured_CreateIoCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort, ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads)
{
DETOURED_CALL(CreateIoCompletionPort);
DEBUG_LOG_TRUE(L"CreateIoCompletionPort", L"%llu %llu", u64(FileHandle), u64(ExistingCompletionPort));
HANDLE trueHandle = FileHandle;
if (isDetouredHandle(FileHandle))
trueHandle = asDetouredHandle(FileHandle).trueHandle;
return True_CreateIoCompletionPort(trueHandle, ExistingCompletionPort, CompletionKey, NumberOfConcurrentThreads);
}
BOOL Detoured_CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize)
{
DETOURED_CALL(CreatePipe);
DEBUG_LOG_TRUE(L"CreatePipe", L"");
return True_CreatePipe(hReadPipe, hWritePipe, lpPipeAttributes, nSize);
}
BOOL Detoured_SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags)
{
DETOURED_CALL(SetHandleInformation);
DEBUG_LOG_TRUE(L"SetHandleInformation", L"%llu", uintptr_t(hObject));
return True_SetHandleInformation(hObject, dwMask, dwFlags); // Calls NtQueryObject and NtSetInformationObject internally
}
HANDLE Detoured_CreateNamedPipeW(LPCWSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
{
DETOURED_CALL(CreateNamedPipeW);
HANDLE h = True_CreateNamedPipeW(lpName, dwOpenMode, dwPipeMode, nMaxInstances, nOutBufferSize, nInBufferSize, nDefaultTimeOut, lpSecurityAttributes);
DEBUG_LOG_TRUE(L"CreateNamedPipeW", L"%ls -> %llu", lpName, u64(h));
return h;
}
BOOL Detoured_CallNamedPipeW(LPCWSTR lpNamedPipeName, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesRead, DWORD nTimeOut)
{
DETOURED_CALL(CreateNamedPipeW);
DEBUG_LOG_TRUE(L"CallNamedPipeW", L"%ls %u %u", lpNamedPipeName, nInBufferSize, nOutBufferSize);
return True_CallNamedPipeW(lpNamedPipeName, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesRead, nTimeOut);
}
BOOL Detoured_PeekNamedPipe(HANDLE hNamedPipe, LPVOID lpBuffer, DWORD nBufferSize, LPDWORD lpBytesRead, LPDWORD lpTotalBytesAvail, LPDWORD lpBytesLeftThisMessage)
{
UBA_ASSERT(!isDetouredHandle(hNamedPipe));
return True_PeekNamedPipe(hNamedPipe, lpBuffer, nBufferSize, lpBytesRead, lpTotalBytesAvail, lpBytesLeftThisMessage);
}
BOOL Detoured_GetKernelObjectSecurity(HANDLE Handle, SECURITY_INFORMATION RequestedInformation, PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD nLength, LPDWORD lpnLengthNeeded)
{
HANDLE trueHandle = Handle;
if (isDetouredHandle(Handle))
trueHandle = asDetouredHandle(Handle).trueHandle;
return True_GetKernelObjectSecurity(trueHandle, RequestedInformation, pSecurityDescriptor, nLength, lpnLengthNeeded);
}
BOOL Detoured_ImpersonateNamedPipeClient(HANDLE hNamedPipe)
{
UBA_ASSERT(!isDetouredHandle(hNamedPipe));
return True_ImpersonateNamedPipeClient(hNamedPipe);
}
BOOL Detoured_TransactNamedPipe(HANDLE hNamedPipe, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesRead, LPOVERLAPPED lpOverlapped)
{
UBA_ASSERT(!isDetouredHandle(hNamedPipe));
return True_TransactNamedPipe(hNamedPipe, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesRead, lpOverlapped);
}
BOOL Detoured_SetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpMode, LPDWORD lpMaxCollectionCount, LPDWORD lpCollectDataTimeout)
{
UBA_ASSERT(!isDetouredHandle(hNamedPipe));
return True_SetNamedPipeHandleState(hNamedPipe, lpMode, lpMaxCollectionCount, lpCollectDataTimeout);
}
BOOL Detoured_GetNamedPipeInfo(HANDLE hNamedPipe, LPDWORD lpFlags, LPDWORD lpOutBufferSize, LPDWORD lpInBufferSize, LPDWORD lpMaxInstances)
{
UBA_ASSERT(!isDetouredHandle(hNamedPipe));
return True_GetNamedPipeInfo(hNamedPipe, lpFlags, lpOutBufferSize, lpInBufferSize, lpMaxInstances);
}
BOOL Detoured_GetNamedPipeHandleStateW(HANDLE hNamedPipe, LPDWORD lpState, LPDWORD lpCurInstances, LPDWORD lpMaxCollectionCount, LPDWORD lpCollectDataTimeout, LPWSTR lpUserName, DWORD nMaxUserNameSize)
{
UBA_ASSERT(!isDetouredHandle(hNamedPipe));
return True_GetNamedPipeHandleStateW(hNamedPipe, lpState, lpCurInstances, lpMaxCollectionCount, lpCollectDataTimeout, lpUserName, nMaxUserNameSize);
}
BOOL Detoured_GetNamedPipeServerProcessId(HANDLE Pipe, PULONG ServerProcessId)
{
UBA_ASSERT(!isDetouredHandle(Pipe));
return True_GetNamedPipeServerProcessId(Pipe, ServerProcessId);
}
BOOL Detoured_GetNamedPipeServerSessionId(HANDLE Pipe, PULONG ServerSessionId)
{
UBA_ASSERT(!isDetouredHandle(Pipe));
return True_GetNamedPipeServerSessionId(Pipe, ServerSessionId);
}
HANDLE Detoured_OpenFileMappingA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName)
{
DETOURED_CALL(OpenFileMappingA);
DEBUG_LOG_TRUE(L"OpenFileMappingA", L"");
return True_OpenFileMappingA(dwDesiredAccess, bInheritHandle, lpName);
}
DWORD Detoured_GetMappedFileNameW(HANDLE hProcess, LPVOID lpv, LPWSTR lpFilename, DWORD nSize)
{
DETOURED_CALL(GetMappedFileNameW);
DEBUG_LOG_TRUE(L"GetMappedFileNameW", L"");
return True_GetMappedFileNameW(hProcess, lpv, lpFilename, nSize);
}
BOOL Detoured_IsProcessorFeaturePresent(DWORD ProcessorFeature)
{
DETOURED_CALL(IsProcessorFeaturePresent);
auto res = True_IsProcessorFeaturePresent(ProcessorFeature);
DEBUG_LOG_TRUE(L"IsProcessorFeaturePresent", L"%u -> %ls", ProcessorFeature, ToString(res));
return res;
}
BOOL Detoured_UnmapViewOfFile2(HANDLE Process, PVOID BaseAddress, ULONG UnmapFlags)
{
DETOURED_CALL(UnmapViewOfFile2);
auto res = True_UnmapViewOfFile2(Process, BaseAddress, UnmapFlags);
DEBUG_LOG_TRUE(L"UnmapViewOfFile2", L"0x%llx -> %ls", uintptr_t(BaseAddress), ToString(res));
return res;
}
// These functions require a very new version of win api (1-1-7).. so skip detouring these for now
//HANDLE Detoured_CreateFileMapping2(HANDLE File, SECURITY_ATTRIBUTES* SecurityAttributes, ULONG DesiredAccess, ULONG PageProtection, ULONG AllocationAttributes, ULONG64 MaximumSize, PCWSTR Name, MEM_EXTENDED_PARAMETER* ExtendedParameters, ULONG ParameterCount)
//{
// DEBUG_LOG_TRUE(L"CreateFileMapping2", L"(%ls)", HandleToName(File));
// UBA_ASSERT(!isDetouredHandle(File));
// return True_CreateFileMapping2(File, SecurityAttributes, DesiredAccess, PageProtection, AllocationAttributes, MaximumSize, Name, ExtendedParameters, ParameterCount);
//}
//HANDLE Detoured_CreateFileMappingNumaW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName, DWORD nndPreferred)
//{
// DEBUG_LOG_TRUE(L"CreateFileMappingNumaW", L"(%ls)", HandleToName(hFile));
// UBA_ASSERT(!isDetouredHandle(hFile));
// return True_CreateFileMappingNumaW(hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpName, nndPreferred);
//}
LPSTR Detoured_GetCommandLineA()
{
DETOURED_CALL(GetCommandLineA);
if (!g_virtualCommandLineA)
{
auto str = True_GetCommandLineA();
DEBUG_LOG_TRUE(L"GetCommandLineA", L"");// str);
return str;
}
DEBUG_LOG_DETOURED(L"GetCommandLineA", L"");
return g_virtualCommandLineA;
}
BOOL Detoured_FreeLibrary(HMODULE hModule)
{
DETOURED_CALL(FreeLibrary);
BOOL res = True_FreeLibrary(hModule);
DEBUG_LOG_TRUE(L"FreeLibrary", L"%llu -> %ls", uintptr_t(hModule), ToString(res));
return res;
}
LSTATUS Detoured_RegOpenKeyW(HKEY hKey, LPCWSTR lpSubKey, PHKEY phkResult)
{
DETOURED_CALL(RegOpenKeyW);
//UBA_NOT_IMPLEMENTED();
SuppressCreateFileDetourScope cfs;
auto res = True_RegOpenKeyW(hKey, lpSubKey, phkResult);
DEBUG_LOG_TRUE(L"RegOpenKeyW", L"(%ls) -> %ls", lpSubKey, ToString(res == ERROR_SUCCESS));
return res;
}
LSTATUS Detoured_RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
{
DETOURED_CALL(RegOpenKeyExW);
//UBA_NOT_IMPLEMENTED();
SuppressCreateFileDetourScope cfs;
auto res = True_RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
DEBUG_LOG_TRUE(L"RegOpenKeyExW", L"(%ls) -> %ls", lpSubKey, ToString(res == ERROR_SUCCESS));
return res;
}
LSTATUS Detoured_RegCreateKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, REGSAM samDesired, const LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition)
{
DETOURED_CALL(RegOpenKeyExW);
//UBA_NOT_IMPLEMENTED();
SuppressCreateFileDetourScope cfs;
auto res = True_RegCreateKeyExW(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition);
DEBUG_LOG_TRUE(L"RegCreateKeyExW", L"(%ls) -> %ls", lpSubKey, ToString(res == ERROR_SUCCESS));
return res;
}
LSTATUS Detoured_RegOpenKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
{
DETOURED_CALL(RegOpenKeyExA);
auto res = True_RegOpenKeyExA(hKey, lpSubKey, ulOptions, samDesired, phkResult);
//UBA_NOT_IMPLEMENTED();
DEBUG_LOG_TRUE(L"RegOpenKeyExA", L"%llu (%hs) -> %ls", uintptr_t(*phkResult), lpSubKey, ToString(res == ERROR_SUCCESS));
return res;
}
LSTATUS Detoured_RegCloseKey(HKEY hKey)
{
DETOURED_CALL(RegCloseKey);
return True_RegCloseKey(hKey);
}
HANDLE Detoured_CreateConsoleScreenBuffer(DWORD dwDesiredAccess, DWORD dwShareMode, const SECURITY_ATTRIBUTES* lpSecurityAttributes, DWORD dwFlags, LPVOID lpScreenBufferData)
{
DETOURED_CALL(CreateConsoleScreenBuffer);
DEBUG_LOG_TRUE(L"CreateConsoleScreenBuffer", L"");
return True_CreateConsoleScreenBuffer(dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlags, lpScreenBufferData);
}
BOOL Detoured_CreateProcessAsUserW(HANDLE hToken, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
{
DETOURED_CALL(CreateProcessAsUserW);
DEBUG_LOG_DETOURED(L"CreateProcessAsUserW", L"%ls %ls %u", lpApplicationName, lpCommandLine ? lpCommandLine : L"", dwCreationFlags);
return True_CreateProcessAsUserW(hToken, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
}
BOOL WINAPI Detoured_SetConsoleCtrlHandler(PHANDLER_ROUTINE HandlerRoutine, BOOL Add)
{
DETOURED_CALL(SetConsoleCtrlHandler);
DEBUG_LOG_DETOURED(L"SetConsoleCtrlHandler", L"");
return TRUE;
}
UINT Detoured_GetConsoleOutputCP()
{
DETOURED_CALL(GetConsoleOutputCP);
DEBUG_LOG_DETOURED(L"GetConsoleOutputCP", L"");
return 437;
//auto res = True_GetConsoleOutputCP();
//return res;
}
BOOL Detoured_ReadConsoleInputA(HANDLE hConsoleInput, PINPUT_RECORD lpBuffer, DWORD nLength, LPDWORD lpNumberOfEventsRead)
{
DETOURED_CALL(ReadConsoleInput);
DEBUG_LOG_DETOURED(L"ReadConsoleInput", L"");
return FALSE;// True_ReadConsoleInput(hConsoleInput, lpBuffer, nLength, lpNumberOfEventsRead);
}
HWND Detoured_GetConsoleWindow()
{
DETOURED_CALL(GetConsoleWindow);
HWND res = True_GetConsoleWindow();
DEBUG_LOG_TRUE(L"GetConsoleWindow", L"-> %llu", uintptr_t(res));
return res;
}
BOOL Detoured_SetConsoleCursorPosition(HANDLE hConsoleOutput, COORD dwCursorPosition)
{
DETOURED_CALL(SetConsoleCursorPosition);
DEBUG_LOG_DETOURED(L"SetConsoleCursorPosition", L"");
return True_SetConsoleCursorPosition(hConsoleOutput, dwCursorPosition);
}
BOOL Detoured_GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo)
{
DETOURED_CALL(GetConsoleScreenBufferInfo);
DEBUG_LOG_DETOURED(L"GetConsoleScreenBufferInfo", L"");
return True_GetConsoleScreenBufferInfo(hConsoleOutput, lpConsoleScreenBufferInfo);
/*
// We make these up just to make clang output errors with proper line breaks..
lpConsoleScreenBufferInfo->dwSize = COORD{ 120,200 };
lpConsoleScreenBufferInfo->dwCursorPosition = COORD{ 0,0 };
lpConsoleScreenBufferInfo->wAttributes = 0;
lpConsoleScreenBufferInfo->srWindow = SMALL_RECT{ 0,0,120,200 };
lpConsoleScreenBufferInfo->dwMaximumWindowSize = COORD{120,8000};
return true;
*/
}
BOOL Detoured_ScrollConsoleScreenBufferW(HANDLE hConsoleOutput, const SMALL_RECT* lpScrollRectangle, const SMALL_RECT* lpClipRectangle, COORD dwDestinationOrigin, const CHAR_INFO* lpFill)
{
DETOURED_CALL(ScrollConsoleScreenBufferW);
DEBUG_LOG_DETOURED(L"ScrollConsoleScreenBufferW", L"");
return True_ScrollConsoleScreenBufferW(hConsoleOutput, lpScrollRectangle, lpClipRectangle, dwDestinationOrigin, lpFill);
}
BOOL Detoured_FillConsoleOutputAttribute(HANDLE hConsoleOutput, WORD wAttribute, DWORD nLength, COORD dwWriteCoord, LPDWORD lpNumberOfAttrsWritten)
{
DETOURED_CALL(FillConsoleOutputAttribute);
DEBUG_LOG_DETOURED(L"FillConsoleOutputAttribute", L"");
return True_FillConsoleOutputAttribute(hConsoleOutput, wAttribute, nLength, dwWriteCoord, lpNumberOfAttrsWritten);
}
BOOL Detoured_FillConsoleOutputCharacterW(HANDLE hConsoleOutput, TCHAR cCharacter, DWORD nLength, COORD dwWriteCoord, LPDWORD lpNumberOfCharsWritten)
{
DETOURED_CALL(FillConsoleOutputCharacterW);
DEBUG_LOG_DETOURED(L"FillConsoleOutputCharacterW", L"");
return True_FillConsoleOutputCharacterW(hConsoleOutput, cCharacter, nLength, dwWriteCoord, lpNumberOfCharsWritten);
}
BOOL Detoured_FlushConsoleInputBuffer(HANDLE hConsoleInput)
{
DETOURED_CALL(FlushConsoleInputBuffer);
DEBUG_LOG_DETOURED(L"FlushConsoleInputBuffer", L"");
return True_FlushConsoleInputBuffer(hConsoleInput);
}
BOOL Detoured_SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes)
{
DETOURED_CALL(SetConsoleTextAttribute);
DEBUG_LOG_DETOURED(L"SetConsoleTextAttribute", L"%llu %i", u64(hConsoleOutput), wAttributes);
return True_SetConsoleTextAttribute(hConsoleOutput, wAttributes);
}
BOOL Detoured_SetConsoleTitleW(LPCTSTR lpConsoleTitle)
{
DETOURED_CALL(SetConsoleTitleW);
DEBUG_LOG_DETOURED(L"SetConsoleTitleW", L"");
return true;
}
int Detoured_GetLocaleInfoEx(LPCWSTR lpLocaleName, LCTYPE LCType, LPWSTR lpLCData, int cchData)
{
DETOURED_CALL(GetLocaleInfoEx);
//DEBUG_LOG_TRUE(L"GetLocaleInfoEx", L"(%ls)", lpLocaleName);
auto res = True_GetLocaleInfoEx(lpLocaleName, LCType, lpLCData, cchData);
//UBA_ASSERTF(false, L"%i %ls %u %ls %i", res, lpLocaleName, LCType, lpLCData, cchData);
return res;
}
int Detoured_GetUserDefaultLocaleName(LPWSTR lpLocaleName, int cchLocaleName)
{
DETOURED_CALL(GetUserDefaultLocaleName);
//wcscpy_s(lpLocaleName, cchLocaleName, L"en-US");
//int res = 6;
int res = True_GetUserDefaultLocaleName(lpLocaleName, cchLocaleName);
DEBUG_LOG_TRUE(L"GetUserDefaultLocaleName", L"(%ls) -> %u", lpLocaleName, res);
return res;
}
BOOL Detoured_IsValidCodePage(UINT CodePage)
{
DETOURED_CALL(IsValidCodePage);
BOOL res = True_IsValidCodePage(CodePage);
DEBUG_LOG_TRUE(L"IsValidCodePage", L"-> %u", res);
return res;
}
UINT Detoured_GetACP()
{
DETOURED_CALL(GetACP);
auto res = True_GetACP();
DEBUG_LOG_TRUE(L"GetACP", L"-> %u", res);
return res;
}
LPCWSTR Detoured_PathFindFileNameW(LPCWSTR pszPath) // This is called by Ps4SymbolTool.exe and vctip.exe
{
auto res = True_PathFindFileNameW(pszPath);
DEBUG_LOG_TRUE(L"PathFindFileNameW", L"(%ls) -> %ls", pszPath, res);
return res;
}
BOOL Detoured_PathIsRelativeW(LPCWSTR pszPath)
{
//UBA_ASSERTF(!g_runningRemote, L"%ls", pszPath); // intel compiler uses PathIsRelativeW.. don't know if this function touches file system but will comment out this assert for now
auto res = True_PathIsRelativeW(pszPath);
DEBUG_LOG_TRUE(L"PathIsRelativeW", L"(%ls) -> %u", pszPath, res);
return res;
}
BOOL Detoured_PathIsDirectoryEmptyW(LPCWSTR pszPath)
{
UBA_ASSERTF(!g_runningRemote, L"%ls", pszPath);
auto res = True_PathIsDirectoryEmptyW(pszPath);
DEBUG_LOG_TRUE(L"PathIsDirectoryEmptyW", L"(%ls) -> %u", pszPath, res);
return res;
}
HRESULT Detoured_SHCreateStreamOnFileW(LPCWSTR pszFile, DWORD grfMode, IStream** ppstm)
{
//UBA_ASSERTF(!g_runningRemote, L"%ls", pszFile);
return True_SHCreateStreamOnFileW(pszFile, grfMode, ppstm);
}
BOOL Detoured_PathFileExistsW(LPCWSTR pszPath)
{
DEBUG_LOG_DETOURED(L"PathFileExistsW", L"CALLING GetFileAttributesW (%s)", pszPath);
DWORD attributes = Detoured_GetFileAttributesW(pszPath);
return attributes != INVALID_FILE_ATTRIBUTES;
//UBA_ASSERTF(!g_runningRemote, L"%ls", pszPath);
//auto res = True_PathFileExistsW(pszPath);
//DEBUG_LOG_TRUE(L"PathFileExistsW", L"(%ls) -> %ls", pszPath, res);
//return res;
}
#endif // DETOURED_INCLUDE_DEBUG