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

1290 lines
40 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#define UBA_IS_DETOURED_INCLUDE 1
#include "UbaDetoursFunctionsWin.h"
#include "UbaDetoursFileMappingTable.h"
#include "UbaDetoursApi.h"
#if !defined(UBA_USE_MIMALLOC)
#define True_malloc malloc
#else
// Taken from mimalloc/types.h
#define MI_ZU(x) x##ULL
#define MI_SEGMENT_MASK ((uintptr_t)(MI_SEGMENT_ALIGN - 1))
#define MI_SEGMENT_ALIGN MI_SEGMENT_SIZE
#define MI_SEGMENT_SIZE (MI_ZU(1)<<MI_SEGMENT_SHIFT)
#define MI_SEGMENT_SHIFT ( 9 + MI_SEGMENT_SLICE_SHIFT) // 32MiB
#define MI_SEGMENT_SLICE_SHIFT (13 + MI_INTPTR_SHIFT)
#define MI_INTPTR_SHIFT (3)
#endif
#include <ntstatus.h>
#define WIN32_NO_STATUS
#include <malloc.h>
NTSTATUS NtCopyFileChunk(HANDLE Source, HANDLE Dest, HANDLE Event, PIO_STATUS_BLOCK IoStatusBlock, ULONG Length, PULONG SourceOffset, PULONG DestOffset, PULONG SourceKey, PULONG DestKey, ULONG Flags) { return 0; }
#define NtCurrentProcess() ((HANDLE)-1)
#define DETOURED_FUNCTION(Func) decltype(Func)* True_##Func = ::Func;
DETOURED_FUNCTIONS
DETOURED_FUNCTIONS_MEMORY
DETOURED_FUNCTIONS_MEMORY_NON_WINE
#undef DETOURED_FUNCTION
#define DETOURED_FUNCTION(Func) void* Local_##Func = ::Func;
DETOURED_FUNCTIONS_MEMORY
DETOURED_FUNCTIONS_MEMORY_NON_WINE
#undef DETOURED_FUNCTION
#include "UbaBinaryParser.h"
#include "UbaDirectoryTable.h"
#include "UbaProcessStats.h"
#include "UbaProtocol.h"
#include "UbaDetoursPayload.h"
#include "UbaApplicationRules.h"
#include "UbaDetoursShared.h"
#include "UbaDetoursUtilsWin.h"
#include "Shlwapi.h"
#include <detours/detours.h>
#include <stdio.h>
namespace uba
{
bool g_useMiMalloc;
constexpr u64 g_pageSize = 64*1024;
thread_local u32 t_disallowCreateFileDetour = 0; // Set this to 1 to disallow file detour.. note that this will prevent directory cache from properly being updated
// Beautiful! cl.exe needs an exact address in that range to be able to map in pch file
// So we'll reserve a bigger range than will be requested and give it back when needed.
constexpr uintptr_t g_clExeBaseAddress = 0x6bb00000000;
constexpr u64 g_clExeBaseAddressSize = 0x400000000;
void* g_clExeBaseReservedMemory = 0;
HANDLE PseudoHandle = (HANDLE)0xfffffffffffffffe;
constexpr int StdOutFd = -2;
#if UBA_DEBUG_LOG_ENABLED
bool g_debugFileFlushOnWrite = false;
void WriteDebug(const char* s, u32 strLen)
{
DWORD toWrite = (DWORD)strLen;
DWORD lastError = GetLastError();
while (true)
{
DWORD written;
if (True_WriteFile((HANDLE)g_debugFile, s, toWrite, &written, NULL))
break;
if (GetLastError() != ERROR_IO_PENDING)
break;// ExitProcess(1340); // During shutdown this might actually cause error and we just ignore that and break out
s += written;
toWrite -= written;
}
if (g_debugFileFlushOnWrite)
True_FlushFileBuffers((HANDLE)g_debugFile);
SetLastError(lastError);
}
void FlushDebugLog()
{
if (isLogging())
True_FlushFileBuffers((HANDLE)g_debugFile);
}
#endif
//#define UBA_PROFILE_DETOURED_CALLS
#if defined(UBA_PROFILE_DETOURED_CALLS)
#define DETOURED_FUNCTION(name) Timer timer##name;
DETOURED_FUNCTIONS
DETOURED_FUNCTIONS_MEMORY
DETOURED_FUNCTIONS_MEMORY_NON_WINE
#undef DETOURED_FUNCTION
#define DETOURED_CALL(name) TimerScope _(timer##name)
#else
#define DETOURED_CALL(name) //DEBUG_LOG(TC(#name));
#endif
bool g_isDetachedProcess;
bool g_isRunningWine;
int g_uiLanguage;
u32 g_processId;
StringBuffer<256> g_exeDir;
const wchar_t* g_virtualCommandLineW;
char* g_virtualCommandLineA;
constexpr u32 TrackInputsMemCapacity = 512 * 1024;
u8* g_trackInputsMem;
u32 g_trackInputsBufPos;
void SendInput()
{
u32 left = g_trackInputsBufPos;
u32 reserveSize = left;
u32 pos = 0;
while (left)
{
RPC_MESSAGE(InputDependencies, log)
writer.Write7BitEncoded(reserveSize);
reserveSize = 0;
u32 toWrite = Min(left, u32(writer.GetCapacityLeft() - sizeof(u32)));
writer.WriteU32(toWrite);
writer.WriteBytes(g_trackInputsMem + pos, toWrite);
writer.Flush();
left -= toWrite;
pos += toWrite;
}
g_trackInputsBufPos = 0;
}
void TrackInput(const wchar_t* file)
{
if (!g_trackInputsMem)
return;
if (g_trackInputsBufPos > TrackInputsMemCapacity - 2048)
SendInput();
BinaryWriter w(g_trackInputsMem, g_trackInputsBufPos, TrackInputsMemCapacity);
w.WriteString(file);
g_trackInputsBufPos = u32(w.GetPosition());
}
void SkipTrackInput(const wchar_t* file)
{
// Just here to easily log out what we are ignoring in terms of input
}
u8 g_emptyMemoryFileMem;
MemoryFile& g_emptyMemoryFile = *new MemoryFile(&g_emptyMemoryFileMem, true);
constexpr u64 DetouredHandleMaxCount = 200*1024; // ~200000 handles enough?
constexpr u64 DetouredHandleStart = 300000; // Let's hope noone uses the handles starting at 300000! :)
constexpr u64 DetouredHandleEnd = DetouredHandleStart + DetouredHandleMaxCount;
constexpr u64 DetouredHandlesMemReserve = AlignUp(DetouredHandleMaxCount*sizeof(DetouredHandle), 64*1024);
constexpr u64 DetouredHandlesMemStart = 0;
MemoryBlock& g_detouredHandleMemoryBlock = *new MemoryBlock(DetouredHandlesMemReserve, (void*)DetouredHandlesMemStart);
u64 g_detouredHandlesStart = u64(g_detouredHandleMemoryBlock.memory);
u64 g_detouredHandlesEnd = g_detouredHandlesStart + g_detouredHandleMemoryBlock.reserveSize;
BlockAllocator<DetouredHandle> g_detouredHandleAllocator(g_detouredHandleMemoryBlock);
void* DetouredHandle::operator new(size_t size) { return g_detouredHandleAllocator.Allocate(); }
void DetouredHandle::operator delete(void* p) { g_detouredHandleAllocator.Free(p); }
inline bool isDetouredHandle(HANDLE h)
{
return u64(h) >= DetouredHandleStart && u64(h) < DetouredHandleEnd;
}
inline HANDLE makeDetouredHandle(DetouredHandle* p)
{
u64 index = (u64(p) - g_detouredHandlesStart) / sizeof(DetouredHandle);
UBA_ASSERT(index < DetouredHandleMaxCount);
return HANDLE(DetouredHandleStart + index);
}
inline DetouredHandle& asDetouredHandle(HANDLE h)
{
u64 index = u64(h) - DetouredHandleStart;
u64 p = (index * sizeof(DetouredHandle)) + g_detouredHandlesStart;
return *(DetouredHandle*)p;
}
HANDLE g_stdHandle[3];
HANDLE g_nullFile;
struct ListDirectoryHandle
{
void* operator new(size_t size);
void operator delete(void* p);
StringKey dirNameKey;
DirectoryTable::Directory& dir;
int it;
Vector<u32> fileTableOffsets;
HANDLE validateHandle;
TString wildcard;
const wchar_t* originalName = nullptr;
};
constexpr u64 ListDirHandlesRange = 4*1024*1024;
MemoryBlock& g_listDirHandleMemoryBlock = *new MemoryBlock(ListDirHandlesRange);
constexpr u64 ListDirHandlesStart = DetouredHandleEnd;
constexpr u64 ListDirHandlesEnd = ListDirHandlesStart + ListDirHandlesRange/sizeof(ListDirectoryHandle);
BlockAllocator<ListDirectoryHandle>& g_listDirectoryHandleAllocator = *new BlockAllocator<ListDirectoryHandle>(g_listDirHandleMemoryBlock);
void* ListDirectoryHandle::operator new(size_t size) { return g_listDirectoryHandleAllocator.Allocate(); }
void ListDirectoryHandle::operator delete(void* p) { g_listDirectoryHandleAllocator.Free(p); }
inline bool isListDirectoryHandle(HANDLE h) { return (u64)h >= ListDirHandlesStart && (u64)h < ListDirHandlesEnd; }
inline HANDLE makeListDirectoryHandle(ListDirectoryHandle* p) { return HANDLE(ListDirHandlesStart + (p - (ListDirectoryHandle*)g_listDirHandleMemoryBlock.memory)); }
inline ListDirectoryHandle& asListDirectoryHandle(HANDLE h) { return *(((ListDirectoryHandle*)g_listDirHandleMemoryBlock.memory) + (u64(h) - ListDirHandlesStart)); }
ReaderWriterLock& g_loadedModulesLock = *new ReaderWriterLock();
UnorderedMap<HMODULE, TString>& g_loadedModules = *new UnorderedMap<HMODULE, TString>();
u64 g_memoryFileIndexCounter = ~u64(0) - 1000000; // I really hope this will not collide with anything
bool g_filesCouldBeCompressed;
bool CouldBeCompressedFile(const StringView& fileName)
{
return g_filesCouldBeCompressed && g_globalRules.FileCanBeCompressed(fileName);
}
bool CanDetour(const tchar* path)
{
if (t_disallowDetour || !path)
return false;
if (path[0] == '\\')
{
if (path[1] == '\\')
{
if (path[2] == '.' && path[3] == '\\') // \\.\ - Win32 namespace for files and devices
{
if (path[5] != ':') // Not file
return false;
path += 4;
}
else if (path[2] == '?') // \\?\ - Win32 path prefix to send through unmodified to nt layer
{
if (path[3] != '\\')
return false;
path += 4;
}
else
{
if (path[2] == '\\' || path[2] == '/' || path[2] == ':' || path[2] == '*' || path[2] == '?' || path[2] == '\"' || path[2] == '<' || path[2] == '>' || path[2] == '|')
return false; // Unknown
}
}
else if (path[1] == '?' && path[2] == '?' && path[3] == '\\')
{
if (path[4] == 'U' && path[5] == 'N' && path[6] == 'C') // All network paths ok?
return true;
if (path[5] == ':')
path += 4;
else
return false; // Unknown
}
}
if (Equals(path, TC("nul")))
return false;
return g_rules->CanDetour(path, g_runningRemote);
}
struct SuppressCreateFileDetourScope
{
SuppressCreateFileDetourScope() { ++t_disallowCreateFileDetour; }
~SuppressCreateFileDetourScope() { --t_disallowCreateFileDetour; }
};
const wchar_t* HandleToName(const DetouredHandle& dh)
{
if (dh.fileObject)
if (const wchar_t* name = dh.fileObject->fileInfo->name)
return name;
return L"Unknown";
}
BlockAllocator<FileObject>& g_fileObjectAllocator = *new BlockAllocator<FileObject>(g_memoryBlock);
MemoryFile::MemoryFile(u8* data, bool localOnly)
: baseAddress(data)
, isLocalOnly(localOnly)
{
}
MemoryFile::MemoryFile(bool localOnly, u64 reserveSize_, bool isThrowAway_, const tchar* fileName)
: isLocalOnly(localOnly)
, isThrowAway(isThrowAway_)
{
if (!isThrowAway)
Reserve(reserveSize_, fileName);
}
void MemoryFile::Reserve(u64 reserveSize_, const tchar* fileName)
{
UBA_ASSERT(!isThrowAway);
reserveSize = reserveSize_;
if (isLocalOnly)
{
baseAddress = (u8*)VirtualAlloc(NULL, reserveSize, MEM_RESERVE, PAGE_READWRITE);
if (!baseAddress)
FatalError(1354, L"VirtualAlloc failed trying to reserve %llu for %s. (Error code: %u)", reserveSize, fileName, GetLastError());
mappedSize = reserveSize;
}
#if UBA_ENABLE_ON_DISK_FILE_MAPPINGS
else if (fileName && !g_runningRemote)
{
StringBuffer<> tempFileName;
tempFileName.Appendf(L"\\??\\").Append(fileName);
if (false)
tempFileName.Append(L".uba.tmp");
UNICODE_STRING us;
us.Length = USHORT(tempFileName.count*sizeof(tchar));
us.MaximumLength = us.Length;
us.Buffer = tempFileName.data;
SuppressDetourScope _;
{
TimerScope ts(g_kernelStats.createFile);
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE, NULL, NULL);
IO_STATUS_BLOCK iosb;
ACCESS_MASK desiredAccess = GENERIC_READ | GENERIC_WRITE | DELETE | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES;
ULONG shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
ULONG createOptions = FILE_SYNCHRONOUS_IO_NONALERT;// | FILE_DELETE_ON_CLOSE;
NTSTATUS status = ZwCreateFile(&mappingHandle.fh, desiredAccess, &oa, &iosb, 0, FILE_ATTRIBUTE_NORMAL, shareAccess, FILE_OVERWRITE_IF, createOptions, 0, 0);
if (status != STATUS_SUCCESS)
FatalError(1347, L"ZwCreateFile failed to create %s. (Error code: 0x%x)", us.Buffer, status);
}
FILE_DISPOSITION_INFO info;
info.DeleteFile = true;
if (!::SetFileInformationByHandle(mappingHandle.fh, FileDispositionInfo, &info, sizeof(info)))
FatalError(1347, L"SetFileInformationByHandle failed to set delete-on-close on %s. (Error code: 0x%x)", us.Buffer, GetLastError());
{
TimerScope ts(g_kernelStats.createFileMapping);
LARGE_INTEGER liMaxSize; liMaxSize.QuadPart = 2;
NTSTATUS status = ZwCreateSection(&mappingHandle.mh, SECTION_ALL_ACCESS, 0, &liMaxSize, PAGE_READWRITE, SEC_COMMIT, mappingHandle.fh);
if (status != STATUS_SUCCESS)
FatalError(1348, L"NtCreateSection failed to reserve %llu. (Error code: 0x%x)", reserveSize, status);
}
TimerScope ts(g_kernelStats.mapViewOfFile);
NTSTATUS status = ZwMapViewOfSection(mappingHandle.mh, NtCurrentProcess(), (void**)&baseAddress, 0, 0, NULL, &reserveSize, 2, MEM_RESERVE, PAGE_READWRITE);
if (status != STATUS_SUCCESS)
FatalError(1348, L"ZwMapViewOfSection failed trying to reserve %llu. (Error code: 0x%x)", reserveSize, status);
mappedSize = reserveSize;
}
#endif
else
{
HANDLE handle;
{
mappedSize = 32 * 1024 * 1024;
TimerScope ts(g_kernelStats.createFileMapping);
handle = True_CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_RESERVE, ToHigh(reserveSize), ToLow(reserveSize), NULL);
if (!handle)
FatalError(1348, L"CreateFileMappingW failed trying to reserve %llu for %s. (Error code: %u)", reserveSize, fileName, GetLastError());
}
TimerScope ts(g_kernelStats.mapViewOfFile);
baseAddress = (u8*)True_MapViewOfFile(handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, mappedSize);
if (!baseAddress)
FatalError(1353, L"MapViewOfFile failed trying to map %llu for %s. ReservedSize: %llu (Error code: %u)", mappedSize, fileName, reserveSize, GetLastError());
mappingHandle = { 0, handle };
}
}
void MemoryFile::Unreserve()
{
if (isLocalOnly)
{
VirtualFree(baseAddress, 0, MEM_RELEASE);
}
else
{
True_UnmapViewOfFile(baseAddress);
CloseHandle(mappingHandle.mh);
mappingHandle.mh = nullptr;
}
baseAddress = nullptr;
committedSize = 0;
}
void MemoryFile::Write(DetouredHandle& handle, LPCVOID lpBuffer, u64 nNumberOfBytesToWrite)
{
u64 newPos = handle.pos + nNumberOfBytesToWrite;
if (isThrowAway)
{
writtenSize = newPos;
return;
}
EnsureCommitted(handle, newPos);
memcpy(baseAddress + handle.pos, lpBuffer, nNumberOfBytesToWrite);
handle.pos += nNumberOfBytesToWrite;
if (writtenSize < newPos)
{
writtenSize = newPos;
isReported = false;
}
}
void MemoryFile::EnsureCommitted(const DetouredHandle& handle, u64 size)
{
if (isThrowAway)
return;
if (committedSize >= size)
return;
if (size > mappedSize)
{
bool shouldRemap = true;
if (size > reserveSize)
{
if (writtenSize == 0 && !isReported)
{
u64 newReserve = AlignUp(size, g_pageSize);
if (reserveSize)
Rpc_WriteLogf(L"TODO: RE-RESERVING MemoryFile. Initial reserve: %llu, New reserve: %llu. Please fix application rules", reserveSize, newReserve);
Unreserve();
Reserve(newReserve);
shouldRemap = false;
}
else
FatalError(1347, L"Reserved size of %ls is smaller than what is requested to be. ReserveSize: %llu Written: %llu Requested: %llu", HandleToName(handle), reserveSize, writtenSize, size);
}
if (shouldRemap)
Remap(handle, size);
}
#if UBA_ENABLE_ON_DISK_FILE_MAPPINGS
if (mappingHandle.fh)
{
if (!committedSize)
committedSize = size;
else
committedSize = Min(reserveSize, Max(size, committedSize * 2));
auto lg = ToLargeInteger(committedSize);
NtExtendSection(mappingHandle.mh, &lg);
}
else
#endif
{
u64 toCommit = Min(reserveSize, AlignUp(size - committedSize, g_pageSize));
if (!VirtualAlloc(baseAddress + committedSize, toCommit, MEM_COMMIT, PAGE_READWRITE))
FatalError(1347, L"Failed to ensure virtual memory for %ls trying to commit %llu at %llx. MappedSize: %llu, CommittedSize: %llu RequestedSize: %llu. (%u)", HandleToName(handle), toCommit, baseAddress + committedSize, mappedSize, committedSize, size, GetLastError());
committedSize += toCommit;
}
}
void MemoryFile::Remap(const DetouredHandle& handle, u64 size)
{
UBA_ASSERT(!mappingHandle.fh);
True_UnmapViewOfFile(baseAddress);
mappedSize = Min(reserveSize, AlignUp(Max(size, mappedSize * 4), g_pageSize));
TimerScope ts(g_kernelStats.mapViewOfFile);
baseAddress = (u8*)True_MapViewOfFile(mappingHandle.mh, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, mappedSize);
if (!baseAddress)
FatalError(1347, L"MapViewOfFile failed trying to map %llu for %ls. ReservedSize: %llu (Error code: %u)", mappedSize, HandleToName(handle), reserveSize, GetLastError());
}
void ToInvestigate(const wchar_t* format, ...)
{
#if UBA_DEBUG_LOG_ENABLED
va_list arg;
va_start (arg, format);
StringBuffer<> buffer;
buffer.Append(format, arg);
va_end (arg);
DEBUG_LOG(buffer.data);
FlushDebugLog();
Rpc_WriteLogf(L"%ls\n", buffer.data);
#endif
}
UBA_NOINLINE void UbaAssert(const tchar* text, const char* file, u32 line, const char* expr, bool allowTerminate, u32 terminateCode, void* context, u32 skipCallstackCount)
{
SuppressDetourScope _;
static CriticalSection cs;
ScopedCriticalSection s(cs);
#if UBA_DEBUG_LOG_ENABLED
FlushDebugLog();
#endif
static auto& sb = *new StringBuffer<16*1024>();
WriteAssertInfo(sb.Clear(), text, file, line, expr);
Rpc_ResolveCallstack(sb, 3 + skipCallstackCount, context);
Rpc_WriteLog(sb.data, sb.count, true, true);
#if UBA_DEBUG_LOG_ENABLED
FlushDebugLog();
#endif
#if UBA_ASSERT_MESSAGEBOX
StringBuffer<> title;
title.Appendf(L"Assert %ls - pid %u", GetApplicationShortName(), GetCurrentProcessId());
int ret = MessageBoxW(GetConsoleWindow(), sb.data, title.data, MB_ABORTRETRYIGNORE|MB_SYSTEMMODAL);
if (ret == IDABORT)
ExitProcess(terminateCode);
else if (ret == IDRETRY && IsDebuggerPresent())
DebugBreak();
#else
if (allowTerminate)
ExitProcess(terminateCode);
#endif
}
extern HANDLE g_hostProcess;
const wchar_t* HandleToName(HANDLE handle)
{
if (handle == INVALID_HANDLE_VALUE)
return L"INVALID";
if (isListDirectoryHandle(handle))
{
#if UBA_DEBUG
return asListDirectoryHandle(handle).originalName;
#else
return L"DIRECTORY";
#endif
}
if (!isDetouredHandle(handle))
return L"UNKNOWN";
DetouredHandle& dh = asDetouredHandle(handle);
if (FileObject* fo = dh.fileObject)
if (FileInfo* fi = fo->fileInfo)
if (const wchar_t* name = fi->name)
return name;
return L"DETOURED";
}
bool NeedsSharedMemory(const wchar_t* file) { return g_allowKeepFilesInMemory && g_rules->NeedsSharedMemory(file); }
u64 FileTypeMaxSize(const StringBufferBase& file, bool isSystemOrTempFile) { return g_rules->FileTypeMaxSize(file, isSystemOrTempFile); }
bool EnsureMapped(DetouredHandle& handle, DWORD dwFileOffsetHigh = 0, DWORD dwFileOffsetLow = 0, SIZE_T numberOfBytesToMap = 0, void* baseAddress = nullptr)
{
FileInfo& info = *handle.fileObject->fileInfo;
if (info.memoryFile || info.fileMapMem)
return true;
u64 offset = ToLargeInteger(dwFileOffsetHigh, dwFileOffsetLow).QuadPart;
if (!numberOfBytesToMap)
{
UBA_ASSERTF(info.size && info.size != InvalidValue || (info.isFileMap && info.size == 0), L"FileInfo file size is bad: %llu", info.size);
numberOfBytesToMap = info.size;
}
u64 alignedOffsetStart = 0;
if (info.trueFileMapOffset)
{
offset += info.trueFileMapOffset;
u64 endOffset = offset + numberOfBytesToMap;
alignedOffsetStart = AlignUp(offset - (g_pageSize - 1), g_pageSize);
u64 alignedOffsetEnd = AlignUp(endOffset, g_pageSize);
u64 mapSize = alignedOffsetEnd - alignedOffsetStart;
TimerScope ts(g_kernelStats.mapViewOfFile);
info.fileMapMem = (u8*)True_MapViewOfFileEx(info.trueFileMapHandle, info.fileMapViewDesiredAccess, ToHigh(alignedOffsetStart), ToLow(alignedOffsetStart), mapSize, baseAddress);
// TODO: This is ugly.. but in some cases we have created virtual files pointing into segments of real file
// and real file size is not aligned to page size so we need to set map size to 0 to allow it to map up to size of file
if (!info.fileMapMem)
info.fileMapMem = (u8*)True_MapViewOfFileEx(info.trueFileMapHandle, info.fileMapViewDesiredAccess, ToHigh(alignedOffsetStart), ToLow(alignedOffsetStart), 0, baseAddress);
}
else
{
UBA_ASSERTF(!info.freeFileMapOnClose, TC("File %s has been freed because of earlier close and is now reopened (%s)"), info.name, info.originalName);
TimerScope ts(g_kernelStats.mapViewOfFile);
info.fileMapMem = (u8*)True_MapViewOfFileEx(info.trueFileMapHandle, info.fileMapViewDesiredAccess, 0, 0, numberOfBytesToMap, baseAddress);
}
if (info.fileMapMem == nullptr || (baseAddress && info.fileMapMem != baseAddress))
{
ToInvestigate(L"MapViewOfFileEx failed trying to map %llu bytes on address 0x%llx with offset %llu, using %llu with access %u (%u)", numberOfBytesToMap, u64(baseAddress), alignedOffsetStart, u64(info.trueFileMapHandle), info.fileMapViewDesiredAccess, GetLastError());
return false;
}
info.fileMapMem += (offset - alignedOffsetStart);
info.fileMapMemSize = info.size;
DEBUG_LOG_TRUE(L"INTERNAL MapViewOfFileEx", L"(%ls) (size: %llu) (%ls) -> 0x%llx", info.name, numberOfBytesToMap, info.originalName, uintptr_t(info.fileMapMem));
return true;
}
ReaderWriterLock& g_longPathNameCacheLock = *new ReaderWriterLock();
using LongPathMap = GrowingUnorderedMap<const wchar_t*, const wchar_t*, HashString, EqualString>;
LongPathMap& g_longPathNameCache = *new LongPathMap(g_memoryBlock);
void Rpc_AllocFailed(const wchar_t* allocType, u32 error)
{
RPC_MESSAGE(VirtualAllocFailed, virtualAllocFailed)
writer.WriteString(allocType);
writer.WriteU32(error);
writer.Flush();
Sleep(5*1000);
}
void CloseCaches()
{
for (auto& it : g_mappedFileTable.m_lookup)
{
FileInfo& info = it.second;
if (info.fileMapMem)
{
DEBUG_LOG_TRUE(L"INTERNAL UnmapViewOfFile", L"0x%llx (%ls) (%ls)", uintptr_t(info.fileMapMem), info.name, info.originalName);
True_UnmapViewOfFile(info.fileMapMem);
}
if (info.trueFileMapHandle != 0)
{
DEBUG_LOG_TRUE(L"INTERNAL CloseHandle", L"%llu (%ls) (%ls)", uintptr_t(info.trueFileMapHandle), info.name, info.originalName);
CloseHandle(info.trueFileMapHandle);
}
// Let them leak
if (info.memoryFile && !info.memoryFile->isLocalOnly)
{
UnmapViewOfFile(info.memoryFile->baseAddress);
CloseHandle(info.memoryFile->mappingHandle.mh);
CloseHandle(info.memoryFile->mappingHandle.fh);
}
//if (info.memoryFile && info.memoryFile != &g_emptyMemoryFile)
// delete info.memoryFile;
}
}
bool g_exitMessageSent;
void SendExitMessage(DWORD exitCode, u64 startTime);
void OnModuleLoaded(HMODULE moduleHandle, const StringView& name);
// Variables used to communicate state from kernelbase functions to ntdll functions
thread_local const wchar_t* t_renameFileNewName;
thread_local const wchar_t* t_createFileFileName;
const wchar_t* ToString(BOOL b) { return b ? L"Success" : L"Error"; }
#include "UbaDetoursFunctionsMiMalloc.inl"
#include "UbaDetoursFunctionsNtDll.inl"
#include "UbaDetoursFunctionsKernelBase.inl"
#include "UbaDetoursFunctionsUcrtBase.inl"
#include "UbaDetoursFunctionsImagehlp.inl"
#include "UbaDetoursFunctionsDbgHelp.inl"
#include "UbaDetoursFunctionsShell32.inl"
#include "UbaDetoursFunctionsRpcrt4.inl"
extern u32 g_consoleStringIndex;
void SendExitMessage(DWORD exitCode, u64 startTime)
{
if (g_exitMessageSent)
return;
g_exitMessageSent = true;
if (g_consoleStringIndex)
Shared_WriteConsole(L"\n", 1, 0);
if (g_trackInputsMem)
SendInput();
g_stats.detoursMemory = g_memoryBlock.writtenSize; // + g_directoryTable.m_memorySize + g_mappedFileTable.m_memPosition;
RPC_MESSAGE(Exit, log)
writer.WriteU32(exitCode);
writer.WriteString(g_logName);
g_stats.detach.time += GetTime() - startTime;
g_stats.detach.count = 1;
g_stats.Write(writer);
g_kernelStats.Write(writer);
// We must flush here if this is a child because,
// if there is a parent process waiting for this to finish,
// the parent might move on before Exit message has been processed on session side
writer.Flush(g_isChild);
}
void DetourAttachFunction(void** trueFunc, void* detouredFunc, const char* funcName)
{
if (!*trueFunc)
return;
auto error = DetourAttach(trueFunc, detouredFunc);
if (error == NO_ERROR)
return;
const char* errorString = "Unknown error";
switch (error)
{
case ERROR_INVALID_BLOCK: errorString = "The function referenced is too small to be detoured."; break;
case ERROR_INVALID_HANDLE: errorString = "The ppPointer parameter is NULL or points to a NULL pointer."; break;
case ERROR_INVALID_OPERATION: errorString = "No pending transaction exists."; break;
case ERROR_NOT_ENOUGH_MEMORY: errorString = "Not enough memory exists to complete the operation."; break;
}
Rpc_WriteLogf(L"Failed to detour %hs (%hs)", funcName, errorString);
ExitProcess(error);
}
void DetourDetachFunction(void** trueFunc, void* detouredFunc, const char* funcName)
{
if (!*trueFunc)
return;
auto error = DetourDetach(trueFunc, detouredFunc);
if (error == NO_ERROR)
return;
Rpc_WriteLogf(L"Failed to detach detoured %hs", funcName);
}
void DetourTransactionBegin()
{
LONG error = ::DetourTransactionBegin();
if (error != NO_ERROR)
FatalError(1357, L"DetourTransactionBegin failed (%ld)", error);
error = ::DetourUpdateThread(GetCurrentThread());
if (error != NO_ERROR)
FatalError(1358, L"DetourUpdateThread failed (%ld)", error);
}
void DetourTransactionCommit()
{
LONG error = ::DetourTransactionCommit();
if (error != NO_ERROR)
FatalError(1343, L"DetourTransactionCommit failed (%ld)", error);
}
int DetourAttachFunctions(bool runningRemote)
{
DetourTransactionBegin();
#define DETOURED_FUNCTION(Func) True_##Func = (decltype(True_##Func))GetProcAddress(moduleHandle, #Func);
if (HMODULE moduleHandle = GetModuleHandleW(L"kernelbase.dll"))
{
DETOURED_FUNCTIONS_KERNELBASE
}
if (HMODULE moduleHandle = GetModuleHandleW(L"kernel32.dll"))
{
DETOURED_FUNCTIONS_KERNEL32
}
if (HMODULE moduleHandle = GetModuleHandleW(L"ntdll.dll"))
{
DETOURED_FUNCTIONS_NTDLL
}
if (HMODULE moduleHandle = GetModuleHandleW(L"ucrtbase.dll"))
{
DETOURED_FUNCTIONS_UCRTBASE
if (g_useMiMalloc)
{
DETOURED_FUNCTIONS_MEMORY
if (!g_isRunningWine)
{
DETOURED_FUNCTIONS_MEMORY_NON_WINE
}
}
}
if (HMODULE moduleHandle = GetModuleHandleW(L"shlwapi.dll"))
{
DETOURED_FUNCTIONS_SHLWAPI
}
#if UBA_SUPPORT_MSPDBSRV
if (HMODULE moduleHandle = GetModuleHandleW(L"rpcrt4.dll"))
{
DETOURED_FUNCTIONS_RPCRT4
}
#endif
#undef DETOURED_FUNCTION
// Can't attach to these when running through debugger with some vs extensions (Microsoft child process debugging)
#if UBA_DEBUG
if (IsDebuggerPresent())
{
True_CreateProcessW = nullptr;
#if defined(DETOURED_INCLUDE_DEBUG)
True_CreateProcessA = nullptr;
True_CreateProcessAsUserW = nullptr;
#endif
}
#endif
#define DETOURED_FUNCTION(Func) DetourAttachFunction((PVOID*)&True_##Func, Detoured_##Func, #Func);
DETOURED_FUNCTIONS
if (g_useMiMalloc)
{
DETOURED_FUNCTIONS_MEMORY
if (!g_isRunningWine)
{
DETOURED_FUNCTIONS_MEMORY_NON_WINE
}
}
#undef DETOURED_FUNCTION
DetourTransactionCommit();
#if UBA_SUPPORT_MSPDBSRV
True2_NdrClientCall2 = True_NdrClientCall2;
#endif
return 0;
}
void OnModuleLoaded(HMODULE moduleHandle, const StringView& name)
{
// SymLoadModuleExW do something bad that cause remote wine to fail everything after this call.. TODO: Revisit
if (g_isRunningWine && !True_SymLoadModuleExW && name.Contains(L"dbghelp.dll"))
{
True_SymLoadModuleExW = (SymLoadModuleExWFunc*)GetProcAddress(moduleHandle, "SymLoadModuleExW");
UBA_ASSERT(True_SymLoadModuleExW);
DetourTransactionBegin();
DetourAttachFunction((PVOID*)&True_SymLoadModuleExW, Detoured_SymLoadModuleExW, "SymLoadModuleExW");
DetourTransactionCommit();
}
// ImageGetDigestStream is buggy in wine so we have to detour it for ShaderCompileWorker
if (g_isRunningWine && !True_ImageGetDigestStream && name.Contains(L"imagehlp.dll"))
{
True_ImageGetDigestStream = (ImageGetDigestStreamFunc*)GetProcAddress(moduleHandle, "ImageGetDigestStream");
UBA_ASSERT(True_ImageGetDigestStream);
DetourTransactionBegin();
DetourAttachFunction((PVOID*)&True_ImageGetDigestStream, Detoured_ImageGetDigestStream, "ImageGetDigestStream");
DetourTransactionCommit();
}
// SHGetKnownFolderPath is used by Metal.exe and must always execute on host
if (!True_SHGetKnownFolderPath && name.Contains(L"shell32.dll"))
{
True_SHGetKnownFolderPath = (SHGetKnownFolderPathFunc*)GetProcAddress(moduleHandle, "SHGetKnownFolderPath");
UBA_ASSERT(True_SHGetKnownFolderPath);
DetourTransactionBegin();
DetourAttachFunction((PVOID*)&True_SHGetKnownFolderPath, Detoured_SHGetKnownFolderPath, "SHGetKnownFolderPath");
DetourTransactionCommit();
}
}
int DetourDetachFunctions()
{
if (g_directoryTable.m_memory)
True_UnmapViewOfFile(g_directoryTable.m_memory);
if (g_mappedFileTable.m_mem)
True_UnmapViewOfFile(g_mappedFileTable.m_mem);
//assert(g_wantsOnCloseLookup.empty());
//UBA_ASSERT(g_mappedFileTable.m_memLookup.empty());
CloseCaches();
#define DETOURED_FUNCTION(Func) DetourDetachFunction((PVOID*)&True_##Func, Detoured_##Func, #Func);
if (g_useMiMalloc)
{
DETOURED_FUNCTIONS_MEMORY
if (!g_isRunningWine)
{
DETOURED_FUNCTIONS_MEMORY_NON_WINE
}
}
DETOURED_FUNCTIONS
#undef DETOURED_FUNCTION
return 0;
}
extern bool g_reportAllExceptions;
void PreInit(const DetoursPayload& payload)
{
#if UBA_USE_MIMALLOC
//mi_option_enable(mi_option_large_os_pages);
mi_option_disable(mi_option_abandoned_page_reset);
#endif
InitSharedVariables();
g_reportAllExceptions = payload.reportAllExceptions;
g_rulesIndex = payload.rulesIndex;
g_rules = GetApplicationRules()[payload.rulesIndex].rules;
g_useMiMalloc = payload.useCustomAllocator;
g_runningRemote = payload.runningRemote;
g_isChild = payload.isChild;
g_allowKeepFilesInMemory = payload.allowKeepFilesInMemory;
g_allowOutputFiles = g_allowKeepFilesInMemory && payload.allowOutputFiles;
g_suppressLogging = payload.suppressLogging;
g_isDetachedProcess = g_rules->AllowDetach();
g_isRunningWine = payload.isRunningWine;
g_uiLanguage = payload.uiLanguage;
if (g_isRunningWine) // There are crashes when running in Wine and really hard to debug
{
//g_useMiMalloc = false;
//g_checkRtlHeap = false;
}
#if UBA_DEBUG_VALIDATE
if (g_runningRemote)
g_validateFileAccess = false;
#endif
{
if (!payload.logFile.IsEmpty())
{
g_logName.Append(payload.logFile);
HANDLE debugFile = CreateFileW(payload.logFile.data, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
#if UBA_DEBUG_LOG_ENABLED
g_debugFile = (FileHandle)(u64)debugFile;
#else
if (debugFile != INVALID_HANDLE_VALUE)
{
const char str[] = "Run in debug to get this file populated";
DWORD written = 0;
WriteFile(debugFile, str, sizeof(str), &written, NULL);
CloseHandle(debugFile);
}
#endif
}
}
if (g_runningRemote)
{
ULONG languageCount = 1;
wchar_t languageBuffer[6];
swprintf_s(languageBuffer, 6, L"%04x", g_uiLanguage);
languageBuffer[5] = 0;
if (!SetProcessPreferredUILanguages(MUI_LANGUAGE_ID, languageBuffer, &languageCount))
{
DEBUG_LOG(L"Failed to set locale");
}
}
{
wchar_t exeFullName[256];
if (!GetModuleFileNameW(NULL, exeFullName, sizeof_array(exeFullName)))
FatalError(1350, L"GetModuleFileNameW failed (%u)", GetLastError());
wchar_t* lastSlash = wcsrchr(exeFullName, '\\');
*lastSlash = 0;
FixPath(g_exeDir, exeFullName);
g_exeDir.Append('\\');
}
// Special cl.exe handling.. this is needed for compiles using pch files where this address _must_ be available.
if (payload.rulesIndex == SpecialRulesIndex_ClExe)
{
g_clExeBaseReservedMemory = VirtualAlloc((void*)g_clExeBaseAddress, g_clExeBaseAddressSize, MEM_RESERVE, PAGE_READWRITE);
DEBUG_LOG(L"Reserving %llu bytes at 0x%llx for cl.exe", g_clExeBaseAddressSize, g_clExeBaseAddress);
if (!g_clExeBaseReservedMemory)
FatalError(1349, L"Failed to reserve memory for cl.exe (%u)", GetLastError());
}
if (const tchar* const* preloads = g_rules->LibrariesToPreload())
for (auto it = preloads; *it; ++it)
if (!LoadLibraryExW(*it, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))
FatalError(1351, L"Failed to preload %s (%u)", *it, GetLastError());
}
void Init(const DetoursPayload& payload, u64 startTime)
{
AddExceptionHandler();
DetourAttachFunctions(g_runningRemote);
if (!g_isDetachedProcess)
{
// If GetStdHandle returns 0 it is likely that there is a parent process and that one has detached process set (which means now conhost is created)
// .. solve this by detaching this process too
HANDLE stdoutHandle = True_GetStdHandle(STD_OUTPUT_HANDLE);
if (stdoutHandle == 0)
{
g_isDetachedProcess = true;
}
else
{
HANDLE stderrHandle = True_GetStdHandle(STD_ERROR_HANDLE);
g_stdHandle[0] = GetFileType(stderrHandle) == FILE_TYPE_CHAR ? stderrHandle : 0;
g_stdHandle[1] = GetFileType(stdoutHandle) == FILE_TYPE_CHAR ? stdoutHandle : 0;
}
}
if (g_isDetachedProcess)
{
g_stdHandle[0] = makeDetouredHandle(new DetouredHandle(HandleType_StdErr)); // STD_ERR
g_stdHandle[1] = makeDetouredHandle(new DetouredHandle(HandleType_StdOut)); // STD_OUT
g_stdHandle[2] = makeDetouredHandle(new DetouredHandle(HandleType_StdIn)); // STD_IN
}
g_processId = payload.processId;
if (payload.trackInputs)
g_trackInputsMem = (u8*)g_memoryBlock.Allocate(TrackInputsMemCapacity, 1, L"TrackInputs");
g_systemRoot.count = GetEnvironmentVariableW(L"SystemRoot", g_systemRoot.data, g_systemRoot.capacity);
g_systemRoot.MakeLower();
wchar_t systemTemp[256];
GetEnvironmentVariableW(L"TEMP", systemTemp, 256);
FixPath(g_systemTemp, systemTemp);
StringBuffer<512> applicationBuffer;
StringBuffer<512> workingDirBuffer;
HANDLE directoryTableHandle;
u32 directoryTableSize;
u32 directoryTableCount;
HANDLE mappedFileTableHandle;
u32 mappedFileTableSize;
u32 mappedFileTableCount;
{
RPC_MESSAGE(Init, init)
writer.Flush();
BinaryReader reader;
g_echoOn = reader.ReadBool();
g_isChild = reader.ReadBool();
reader.ReadString(applicationBuffer);
reader.ReadString(workingDirBuffer);
directoryTableHandle = FileMappingHandle::FromU64(reader.ReadU64()).mh;
directoryTableSize = reader.ReadU32();
directoryTableCount = reader.ReadU32();
mappedFileTableHandle = FileMappingHandle::FromU64(reader.ReadU64()).mh;
mappedFileTableSize = reader.ReadU32();
mappedFileTableCount = reader.ReadU32();
if (u16 vfsSize = reader.ReadU16())
{
BinaryReader vfsReader(reader.GetPositionData(), 0, vfsSize);
PopulateVfs(vfsReader);
}
DEBUG_LOG_PIPE(L"Init", L"");
}
TrackInput(applicationBuffer.data);
VirtualizePath(applicationBuffer);
VirtualizePath(workingDirBuffer);
VirtualizePath(g_exeDir);
Shared_SetCurrentDirectory(workingDirBuffer.data);
{
FixPath(applicationBuffer.data, g_virtualWorkingDir.data, g_virtualWorkingDir.count, g_virtualApplication);
if (const wchar_t* lastBackslash = g_virtualApplication.Last('\\'))
g_virtualApplicationDir.Append(g_virtualApplication.data, (lastBackslash + 1 - g_virtualApplication.data));
else
FatalError(4444, L"What the heck: %s (%s)", g_virtualApplication.data, applicationBuffer.data);
}
const wchar_t* cmdLine = True_GetCommandLineW();
const wchar_t* exePos;
if (g_runningRemote && Contains(cmdLine, g_exeDir.data, true, &exePos))
{
StringBuffer<> buf;
buf.Append(cmdLine, exePos - cmdLine);
buf.Append(g_virtualApplicationDir);
TString realCmdLine(buf.data);
realCmdLine += (cmdLine + g_exeDir.count + 1);
g_virtualCommandLineW = g_memoryBlock.Strdup(realCmdLine).data;
}
//else
// g_virtualCommandLineW = g_memoryBlock.Strdup(cmdLine);
if (g_virtualCommandLineW)
{
u64 len = wcslen(g_virtualCommandLineW);
g_virtualCommandLineA = (char*)g_memoryBlock.Allocate(len + 1, 1, L"");
size_t res;
wcstombs_s(&res, g_virtualCommandLineA, len + 1, g_virtualCommandLineW, len);
}
#if UBA_DEBUG_LOG_ENABLED
if (isLogging())
{
if (g_virtualCommandLineW)
cmdLine = g_virtualCommandLineW;
u64 cmdLineLen = wcslen(cmdLine);
wchar_t temp[LogBufSize - 10];
if (cmdLineLen > sizeof_array(temp))
{
memcpy(temp, cmdLine, sizeof(temp));
temp[sizeof_array(temp)-1] = 0;
cmdLine = temp;
}
DEBUG_LOG(L"ProcessId: %u", payload.processId);
DEBUG_LOG(L"Cmdline: %ls", cmdLine);
DEBUG_LOG(L"WorkingDir: %ls", g_virtualWorkingDir.data);
DEBUG_LOG(L"ExeDir: %ls", g_virtualApplicationDir.data);
DEBUG_LOG(L"ExeDir (true): %ls", g_exeDir.data);
DEBUG_LOG(L"Rules: %u (%u)", g_rules->index, GetApplicationRules()[g_rules->index].hash);
if (g_runningRemote)
{
StringBuffer<256> computerName;
GetComputerNameW(computerName);
DEBUG_LOG(L"Remote: %s", computerName.data);
}
LogVfsInfo();
}
#endif
if (!True_DuplicateHandle(g_hostProcess, mappedFileTableHandle, GetCurrentProcess(), &mappedFileTableHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
UBA_ASSERTF(false, L"Failed to duplicate filetable handle (%u)", GetLastError());
u8* mappedFileTableMem;
{
TimerScope ts(g_kernelStats.mapViewOfFile);
mappedFileTableMem = (u8*)True_MapViewOfFile(mappedFileTableHandle, FILE_MAP_READ, 0, 0, 0);
UBA_ASSERT(mappedFileTableMem);
}
{
TimerScope ts2(g_stats.fileTable);
g_mappedFileTable.Init(mappedFileTableMem, mappedFileTableCount, mappedFileTableSize);
}
if (!True_DuplicateHandle(g_hostProcess, directoryTableHandle, GetCurrentProcess(), &directoryTableHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
UBA_ASSERTF(false, L"Failed to duplicate directorytable handle (%u)", GetLastError());
u8* directoryTableMem;
{
TimerScope ts(g_kernelStats.mapViewOfFile);
directoryTableMem = (u8*)True_MapViewOfFile(directoryTableHandle, FILE_MAP_READ, 0, 0, 0);
UBA_ASSERT(directoryTableMem);
}
{
TimerScope ts2(g_stats.dirTable);
g_directoryTable.Init(directoryTableMem, directoryTableCount, directoryTableSize);
}
if (g_isChild)
Rpc_GetWrittenFiles();
g_stats.attach.time += GetTime() - startTime;
g_stats.attach.count = 1;
g_filesCouldBeCompressed = payload.readIntermediateFilesCompressed && g_rules->CanDependOnCompressedFiles();
}
void Deinit(u64 startTime)
{
if (g_isRunningWine) // mt.exe etc fails if detaching is not done during shutdown
{
DetourTransactionBegin();
DetourDetachFunctions();
::DetourTransactionCommit(); // Ignore errors
}
#if defined(UBA_PROFILE_DETOURED_CALLS)
#define DETOURED_FUNCTION(name) if (timer##name.count != 0) { char sb[1024]; sprintf_s(sb, sizeof(sb), "%s: %u %llu\n", #name, timer##name.count.load(), TimeToMs(timer##name.time.load())); WriteDebug(sb); }
DETOURED_FUNCTIONS
DETOURED_FUNCTIONS_MEMORY
DETOURED_FUNCTIONS_MEMORY_NON_WINE
#undef DETOURED_FUNCTION
#endif
DWORD exitCode = STILL_ACTIVE;
if (!True_GetExitCodeProcess(GetCurrentProcess(), &exitCode))
exitCode = STILL_ACTIVE;
if (!g_exitMessageSent)
SendExitMessage(exitCode, startTime); // This should never happen? ExitProcess is always called after main function
}
void PostDeinit()
{
DEBUG_LOG(L"Finished");
#if UBA_DEBUG_LOG_ENABLED
if (isLogging())
{
FlushDebugLog();
HANDLE debugFile = (HANDLE)g_debugFile;
g_debugFile = InvalidFileHandle;
CloseHandle(debugFile);
}
#endif
}
} // namespace uba
extern "C"
{
using namespace uba;
UBA_DETOURED_API u32 UbaSendCustomMessage(const void* send, u32 sendSize, void* recv, u32 recvCapacity)
{
RPC_MESSAGE(Custom, log)
writer.WriteU32(sendSize);
writer.WriteBytes(send, sendSize);
writer.Flush();
BinaryReader reader;
u32 recvSize = reader.ReadU32();
UBA_ASSERT(recvSize < recvCapacity);
reader.ReadBytes(recv, recvSize);
return recvSize;
}
UBA_DETOURED_API bool UbaFlushWrittenFiles()
{
RPC_MESSAGE(FlushWrittenFiles, log)
writer.Flush();
BinaryReader reader;
return reader.ReadBool();
}
UBA_DETOURED_API bool UbaUpdateEnvironment(const wchar_t* reason, bool resetStats)
{
{
RPC_MESSAGE(UpdateEnvironment, log)
writer.WriteString(reason ? reason : L"");
writer.WriteBool(resetStats);
writer.Flush();
BinaryReader reader;
if (!reader.ReadBool())
return false;
}
Rpc_UpdateTables();
return true;
}
UBA_DETOURED_API bool UbaRunningRemote()
{
return g_runningRemote;
}
UBA_DETOURED_API bool UbaRequestNextProcess(u32 prevExitCode, wchar_t* outArguments, u32 outArgumentsCapacity)
{
#if UBA_DEBUG_LOG_ENABLED
FlushDebugLog();
#endif
*outArguments = 0;
bool newProcess;
{
RPC_MESSAGE(GetNextProcess, log)
writer.WriteU32(prevExitCode);
g_stats.Write(writer);
g_kernelStats.Write(writer);
writer.Flush();
BinaryReader reader;
newProcess = reader.ReadBool();
if (newProcess)
{
reader.ReadString(outArguments, outArgumentsCapacity);
reader.SkipString(); // workingDir
reader.SkipString(); // description
reader.ReadString(g_logName.Clear());
}
}
if (newProcess)
{
g_kernelStats = {};
g_stats = {};
#if UBA_DEBUG_LOG_ENABLED
SuppressCreateFileDetourScope scope;
HANDLE debugFile = (HANDLE)g_debugFile;
g_debugFile = InvalidFileHandle;
CloseHandle(debugFile);
debugFile = CreateFileW(g_logName.data, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
g_debugFile = (FileHandle)(u64)debugFile;
#endif
}
Rpc_UpdateTables();
return newProcess;
}
}