/*
Userspace Virtual Filesystem
Copyright (C) 2015 Sebastian Herbord. All rights reserved.
This file is part of usvfs.
usvfs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
usvfs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with usvfs. If not, see .
*/
#include
#include
#include
#include
#include "inject.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace ush = usvfs::shared;
using namespace winapi;
void usvfs::injectProcess(const std::wstring& applicationPath,
const usvfsParameters& parameters,
const PROCESS_INFORMATION& processInfo)
{
injectProcess(applicationPath, parameters, processInfo.hProcess, processInfo.hThread);
}
void usvfs::injectProcess(const std::wstring& applicationPath,
const usvfsParameters& parameters, HANDLE processHandle,
HANDLE threadHandle)
{
bool proc64 = false;
bool sameBitness = false;
{
SYSTEM_INFO info;
GetSystemInfo(&info);
BOOL wow64;
IsWow64Process(processHandle, &wow64);
if (wow64) {
// process is running under wow64 so it has to be a 32bit process running on 64bit
// windows
proc64 = false;
BOOL temp;
IsWow64Process(GetCurrentProcess(), &temp);
sameBitness = temp == TRUE;
} else {
BOOL selfWow64;
IsWow64Process(GetCurrentProcess(), &selfWow64);
if (selfWow64) {
// WE are a 32 bit process running on 64bit windows. the other process isn't, so
// its 64bit
proc64 = true;
} else {
sameBitness = true;
// we have the same bitness as that other process, but which is it?
#ifdef _WIN64
proc64 = true;
#else
proc64 = false;
#endif
}
}
}
boost::filesystem::path binPath = boost::filesystem::path(applicationPath);
spdlog::get("usvfs")->info("injecting to process {} with {} bitness",
::GetProcessId(processHandle),
sameBitness ? "same" : "different");
if (sameBitness) {
static constexpr auto USVFS_DLL =
#ifdef _WIN64
L"usvfs_x64.dll";
#else
L"usvfs_x86.dll";
#endif
const auto& preferedDll = binPath / USVFS_DLL;
boost::filesystem::path dllPath = preferedDll;
bool dllFound = boost::filesystem::exists(dllPath);
// support for runing tests using a usvfs dll in lib folder (and proxy under bin):
if (!dllFound && binPath.filename() == L"bin") {
dllPath = binPath.parent_path() / L"lib" / USVFS_DLL;
dllFound = boost::filesystem::exists(dllPath);
}
if (!dllFound) {
USVFS_THROW_EXCEPTION(
file_not_found_error()
<< ex_msg(std::string("dll missing: ") +
ush::string_cast(preferedDll.wstring()).c_str()));
}
spdlog::get("usvfs")->info("dll path: {}", dllPath.wstring());
InjectLib::InjectDLL(processHandle, threadHandle, dllPath.c_str(), "InitHooks",
¶meters, sizeof(parameters));
spdlog::get("usvfs")->info("injection to same bitness process {} successful",
::GetProcessId(processHandle));
} else {
// first try platform specific proxy exe:
static constexpr auto USVFS_PREFERED_EXE =
#ifdef _WIN64
L"usvfs_proxy_x86.exe";
#else
L"usvfs_proxy_x64.exe";
#endif
const auto& preferedExe = binPath / USVFS_PREFERED_EXE;
boost::filesystem::path exePath = preferedExe;
bool exeFound = boost::filesystem::exists(exePath);
// support for runing tests using a usvfs dll in lib folder (and proxy under bin):
if (!exeFound && binPath.filename() == L"lib") {
exePath = binPath.parent_path() / L"bin" / USVFS_PREFERED_EXE;
exeFound = boost::filesystem::exists(exePath);
}
// finally fallback to old proxy naming (but only for 64bit as we don't have a 64bit
// proxy in this case):
#ifdef _WIN64
if (!exeFound) {
exePath = binPath / L"usvfs_proxy.exe";
exeFound = boost::filesystem::exists(exePath);
}
#endif
if (!exeFound) {
USVFS_THROW_EXCEPTION(file_not_found_error() << ex_msg(
std::string("usvfs proxy not found: ") +
ush::string_cast(preferedExe.wstring())));
} else
spdlog::get("usvfs")->info("using usvfs proxy: {}",
ush::string_cast(preferedExe.wstring()));
// need to use proxy aplication to inject
auto proxyProcess =
std::move(wide::createProcess(exePath.wstring())
.arg(L"--instance")
.arg(ush::string_cast(parameters.instanceName))
.arg(L"--pid")
.arg(GetProcessId(processHandle)));
if (threadHandle != INVALID_HANDLE_VALUE) {
proxyProcess.arg("--tid").arg(GetThreadId(threadHandle));
}
process::Result result = proxyProcess();
if (!result.valid) {
USVFS_THROW_EXCEPTION(unknown_error()
<< ex_msg(std::string("failed to start proxy ") +
ush::string_cast(exePath.wstring()))
<< ex_win_errcode(result.errorCode));
} else {
// wait for proxy completion. this shouldn't take long, 15 seconds is very
// generous
switch (WaitForSingleObject(result.processInfo.hProcess, 15000)) {
case WAIT_TIMEOUT: {
spdlog::get("usvfs")->debug("proxy timeout");
TerminateProcess(result.processInfo.hProcess, 1);
USVFS_THROW_EXCEPTION(timeout_error()
<< ex_msg(std::string("proxy didn't complete in time")));
} break;
case WAIT_FAILED: {
spdlog::get("usvfs")->debug("proxy wait failed");
TerminateProcess(result.processInfo.hProcess, 1);
USVFS_THROW_EXCEPTION(unknown_error()
<< ex_msg(
std::string("failed to wait for proxy completion"))
<< ex_win_errcode(result.errorCode));
} break;
default: {
spdlog::get("usvfs")->debug("proxy run successful");
// nop
} break;
}
}
}
}