in PsfRuntime/CreateProcessHook.cpp [75:243]
BOOL WINAPI CreateProcessFixup(
_In_opt_ const CharT* applicationName,
_Inout_opt_ CharT* commandLine,
_In_opt_ LPSECURITY_ATTRIBUTES processAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES threadAttributes,
_In_ BOOL inheritHandles,
_In_ DWORD creationFlags,
_In_opt_ LPVOID environment,
_In_opt_ const CharT* currentDirectory,
_In_ startup_info_t<CharT>* startupInfo,
_Out_ LPPROCESS_INFORMATION processInformation) noexcept try
{
// We can't detour child processes whose executables are located outside of the package as they won't have execute
// access to the fixup dlls. Instead of trying to replicate the executable search logic when determining the location
// of the target executable, create the process as suspended and let the system tell us where the executable is
PROCESS_INFORMATION pi;
if (!processInformation)
{
processInformation = π
}
if (!CreateProcessImpl(
applicationName,
commandLine,
processAttributes,
threadAttributes,
inheritHandles,
creationFlags | CREATE_SUSPENDED,
environment,
currentDirectory,
startupInfo,
processInformation))
{
return FALSE;
}
iwstring path;
DWORD size = MAX_PATH;
path.resize(size - 1);
while (true)
{
if (::QueryFullProcessImageNameW(processInformation->hProcess, 0, path.data(), &size))
{
path.resize(size);
break;
}
else if (auto err = ::GetLastError(); err == ERROR_INSUFFICIENT_BUFFER)
{
size *= 2;
path.resize(size - 1);
}
else
{
// Unexpected error
::TerminateProcess(processInformation->hProcess, ~0u);
::CloseHandle(processInformation->hProcess);
::CloseHandle(processInformation->hThread);
::SetLastError(err);
return FALSE;
}
}
// std::filesystem::path comparison doesn't seem to handle case-insensitivity or root-local device paths...
iwstring_view packagePath(PackageRootPath().native().c_str(), PackageRootPath().native().length());
iwstring_view finalPackagePath(FinalPackageRootPath().native().c_str(), FinalPackageRootPath().native().length());
iwstring_view exePath = path;
auto fixupPath = [](iwstring_view& p)
{
if ((p.length() >= 4) && (p.substr(0, 4) == LR"(\\?\)"_isv))
{
p = p.substr(4);
}
};
fixupPath(packagePath);
fixupPath(finalPackagePath);
fixupPath(exePath);
#if _DEBUG
Log("\tPossible injection to process %ls %d.\n", exePath.data(), processInformation->dwProcessId);
#endif
if (((exePath.length() >= packagePath.length()) && (exePath.substr(0, packagePath.length()) == packagePath)) ||
((exePath.length() >= finalPackagePath.length()) && (exePath.substr(0, finalPackagePath.length()) == finalPackagePath)))
{
#if _DEBUG
Log("\tInject %ls into PID=%d", psf::runtime_dll_name, processInformation->dwProcessId);
#endif
// The target executable is in the package, so we _do_ want to fixup it
static const auto pathToPsfRuntime = (PackageRootPath() / psf::runtime_dll_name).string();
PCSTR targetDll = pathToPsfRuntime.c_str();
if (!std::filesystem::exists(targetDll))
{
// Possibly the dll is in the folder with the exe and not at the package root.
Log("\t%s not found at package root, try target folder.", targetDll);
std::filesystem::path altPathToExeRuntime = exePath.data();
static const auto altPathToPsfRuntime = (altPathToExeRuntime.parent_path() / psf::runtime_dll_name).string();
targetDll = altPathToPsfRuntime.c_str();
#if _DEBUG
Log("\talt target filename is now %s", altPathToPsfRuntime.c_str());
#endif
}
if (!std::filesystem::exists(targetDll))
{
#if _DEBUG
Log("\tNot present there either, try elsewhere in package.");
#endif
// If not in those two locations, must check everywhere in package.
// The child process might also be in another package folder, so look elsewhere in the package.
for (auto& dentry : std::filesystem::recursive_directory_iterator(PackageRootPath()))
{
try
{
if (dentry.path().filename().compare(psf::runtime_dll_name) == 0)
{
static const auto altDirPathToPsfRuntime = narrow(dentry.path().c_str());
#if _DEBUG
Log("\tFound match as %ls", dentry.path().c_str());
#endif
targetDll = altDirPathToPsfRuntime.c_str();
break;
}
}
catch (...)
{
Log("Non-fatal error enumerating directories while looking for PsfRuntime.");
}
}
}
Log("\tAttempt injection into %d using %s", processInformation->dwProcessId, targetDll);
if (!::DetourUpdateProcessWithDll(processInformation->hProcess, &targetDll, 1))
{
Log("\t%s not found at target folder, try PsfRunDll.", targetDll);
// We failed to detour the created process. Assume that it the failure was due to an architecture mis-match
// and try the launch using PsfRunDll
if (!::DetourProcessViaHelperDllsW(processInformation->dwProcessId, 1, &targetDll, CreateProcessWithPsfRunDll))
{
// Could not detour the target process, so return failure
auto err = ::GetLastError();
Log("\tUnable to inject %ls into PID=%d err=0x%x\n", psf::runtime_dll_name, processInformation->dwProcessId, err);
::TerminateProcess(processInformation->hProcess, ~0u);
::CloseHandle(processInformation->hProcess);
::CloseHandle(processInformation->hThread);
::SetLastError(err);
return FALSE;
}
}
}
Log("\tInjected %ls into PID=%d\n", psf::runtime_dll_name, processInformation->dwProcessId);
if ((creationFlags & CREATE_SUSPENDED) != CREATE_SUSPENDED)
{
// Caller did not want the process to start suspended
::ResumeThread(processInformation->hThread);
}
if (processInformation == &pi)
{
::CloseHandle(processInformation->hProcess);
::CloseHandle(processInformation->hThread);
}
return TRUE;
}