From aaf9b50433af7d340bbef8df5a2a1fdd328ac8f9 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 23 Oct 2021 13:33:01 +0200 Subject: [PATCH] Add initial form of appLauncher --- GlosSITarget/AppLauncher.cpp | 171 ++++++++++++++++++++++ GlosSITarget/AppLauncher.h | 53 +++++++ GlosSITarget/GlosSITarget.vcxproj | 2 + GlosSITarget/GlosSITarget.vcxproj.filters | 6 + GlosSITarget/SteamTarget.cpp | 8 +- GlosSITarget/SteamTarget.h | 2 + 6 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 GlosSITarget/AppLauncher.cpp create mode 100644 GlosSITarget/AppLauncher.h diff --git a/GlosSITarget/AppLauncher.cpp b/GlosSITarget/AppLauncher.cpp new file mode 100644 index 0000000..06c7f7b --- /dev/null +++ b/GlosSITarget/AppLauncher.cpp @@ -0,0 +1,171 @@ +/* +Copyright 2021 Peter Repukat - FlatspotSoftware + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#include "AppLauncher.h" + +#include + +#ifdef _WIN32 +#include +#include +#endif +#include + +AppLauncher::AppLauncher(std::function shutdown) : shutdown_(std::move(shutdown)) +{ +#ifdef _WIN32 + UnPatchValveHooks(); +#endif +}; + +void AppLauncher::launchApp(const std::wstring& path, const std::wstring& args) +{ + launchWin32App(path, args); +} + +void AppLauncher::update() +{ + if (process_check_clock_.getElapsedTime().asSeconds() > 1) { +#ifdef _WIN32 + if (process_info.dwProcessId > 0) { + if (!IsProcessRunning(process_info.dwProcessId)) { + shutdown_(); + } + } + if (uwp_pid_ > 0) { + if (!IsProcessRunning(uwp_pid_)) { + shutdown_(); + } + } +#endif + process_check_clock_.restart(); + } +} + +void AppLauncher::close() +{ +#ifdef _WIN32 + if (process_info.dwProcessId > 0) { + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + } +#endif +} +#ifdef _WIN32 +bool AppLauncher::IsProcessRunning(DWORD pid) +{ + const HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid); + if (process == nullptr) + return false; + const DWORD ret = WaitForSingleObject(process, 0); + CloseHandle(process); + return ret == WAIT_TIMEOUT; +} +#endif + +#ifdef _WIN32 +void AppLauncher::UnPatchValveHooks() +{ + spdlog::debug("Unpatching Valve CreateProcess hook..."); + // need to load addresses that way.. Otherwise we may land before some jumps... + auto kernel32dll = GetModuleHandle(L"kernel32.dll"); + if (kernel32dll) { + BYTE* address = reinterpret_cast(GetProcAddress(kernel32dll, "CreateProcessW")); + if (address) { + DWORD dw_old_protect, dw_bkup; + const auto len = CREATE_PROC_ORIG_BYTES.size(); + VirtualProtect(address, len, PAGE_EXECUTE_READWRITE, &dw_old_protect); //Change permissions of memory.. + for (DWORD i = 0; i < len; i++) //unpatch Valve's hook + { + *(address + i) = CREATE_PROC_ORIG_BYTES[i]; + } + VirtualProtect(address, len, dw_old_protect, &dw_bkup); //Revert permission change... + spdlog::trace("Unpatched CreateProcessW"); + } + else { + spdlog::error("failed to unpatch CreateProcessW"); + } + } + else { + spdlog::error("kernel32.dll not found... sure..."); + } +} + +void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& args) +{ + const auto native_seps_path = std::regex_replace(path, std::wregex(L"(\\/|\\\\)"), L"\\"); + std::wstring launch_dir; + std::wsmatch m; + if (!std::regex_search(native_seps_path, m, std::wregex(L"(.*?\\\\)*"))) { + spdlog::warn("Couldn't detect launch application directory"); // Shouldn't ever happen... + } else { + launch_dir = m[0]; + } + std::wstring args_cpy(args); + if (CreateProcessW(native_seps_path.data(), + args_cpy.data(), + nullptr, + nullptr, + TRUE, + 0, + nullptr, + launch_dir.empty() ? nullptr : launch_dir.data(), + &info, + &process_info)) { + spdlog::info(L"Started Program: \"{}\" in directory: {}", native_seps_path, launch_dir); + } + else { + spdlog::error(L"Couldn't start program: \"{}\" in directory: {}", native_seps_path, launch_dir); + } +} + +void AppLauncher::launchUWPApp(const LPCWSTR package_full_name) +{ + HRESULT result = CoInitialize(nullptr); + if (SUCCEEDED(result)) { + + CComPtr sp_app_activation_manager; + // Initialize IApplicationActivationManager + result = CoCreateInstance( + CLSID_ApplicationActivationManager, + nullptr, + CLSCTX_LOCAL_SERVER, + IID_IApplicationActivationManager, + reinterpret_cast(&sp_app_activation_manager)); + + if (SUCCEEDED(result)) { + // This call ensures that the app is launched as the foreground window and sometimes may randomly fail... + result = CoAllowSetForegroundWindow(sp_app_activation_manager, nullptr); + if (!SUCCEEDED(result)) { + spdlog::warn("CoAllowSetForegroundWindow failed. Code: {}", result); + } + + // Launch the app + result = sp_app_activation_manager->ActivateApplication(package_full_name, nullptr, AO_NONE, &uwp_pid_); + if (!SUCCEEDED(result)) { + spdlog::error("ActivateApplication failed: Code {}", result); + } else { + spdlog::info(L"Launched UWP Package \"{}\"", package_full_name); + } + } else { + spdlog::error("CoCreateInstance failed: Code {}", result); + } + CoUninitialize(); + } + else { + spdlog::error("CoInitialize failed: Code {}", result); + } +} +#endif \ No newline at end of file diff --git a/GlosSITarget/AppLauncher.h b/GlosSITarget/AppLauncher.h new file mode 100644 index 0000000..50853dd --- /dev/null +++ b/GlosSITarget/AppLauncher.h @@ -0,0 +1,53 @@ +/* +Copyright 2021 Peter Repukat - FlatspotSoftware + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once + +#ifdef _WIN32 +#define NOMINMAX +#include +#endif +#include +#include +#include + +class AppLauncher { + public: + explicit AppLauncher(std::function shutdown = [](){}); + + void launchApp(const std::wstring& path, const std::wstring& args = L""); + void update(); + void close(); + +private: + + std::function shutdown_; + + sf::Clock process_check_clock_; + +#ifdef _WIN32 + static bool IsProcessRunning(DWORD pid); + + // Valve also hooks "CreateProcess" + // Unpatch that so that launched programs don't also get hooked... + static inline const std::string CREATE_PROC_ORIG_BYTES = "\x4C\x8B\xDC\x48\x83"; + static void UnPatchValveHooks(); + void launchWin32App(const std::wstring& path, const std::wstring& args = L""); + void launchUWPApp(LPCWSTR package_full_name); + STARTUPINFO info{sizeof(info)}; + PROCESS_INFORMATION process_info{}; + DWORD uwp_pid_ = 0; +#endif +}; diff --git a/GlosSITarget/GlosSITarget.vcxproj b/GlosSITarget/GlosSITarget.vcxproj index 3a7eaa4..1943438 100644 --- a/GlosSITarget/GlosSITarget.vcxproj +++ b/GlosSITarget/GlosSITarget.vcxproj @@ -163,6 +163,7 @@ + @@ -175,6 +176,7 @@ + diff --git a/GlosSITarget/GlosSITarget.vcxproj.filters b/GlosSITarget/GlosSITarget.vcxproj.filters index 6a20838..14718e6 100644 --- a/GlosSITarget/GlosSITarget.vcxproj.filters +++ b/GlosSITarget/GlosSITarget.vcxproj.filters @@ -72,6 +72,9 @@ Source Files\imgui-sfml + + Source Files + @@ -110,6 +113,9 @@ Header Files + + Header Files + diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index d38640a..547b5ca 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -28,7 +28,8 @@ limitations under the License. SteamTarget::SteamTarget(int argc, char* argv[]) : window_([this] { run_ = false; }, getScreenshotHotkey()), overlay_(window_.getOverlay()), - detector_([this](bool overlay_open) { onOverlayChanged(overlay_open); }) + detector_([this](bool overlay_open) { onOverlayChanged(overlay_open); }), + launcher_([this] { run_ = false; }) { target_window_handle_ = window_.getSystemHandle(); } @@ -57,18 +58,21 @@ Application will not function!"); input_redirector_.run(); #endif + // launcher_.launchApp(L"1234"); // TODO + keepControllerConfig(true); while (run_) { detector_.update(); window_.update(); overlayHotkeyWorkaround(); + launcher_.update(); } #ifdef _WIN32 input_redirector_.stop(); hidhide_.disableHidHide(); #endif - + launcher_.close(); return 1; } diff --git a/GlosSITarget/SteamTarget.h b/GlosSITarget/SteamTarget.h index bf65719..2f1d7e0 100644 --- a/GlosSITarget/SteamTarget.h +++ b/GlosSITarget/SteamTarget.h @@ -25,6 +25,7 @@ limitations under the License. #include #endif +#include "AppLauncher.h" #include "Overlay.h" #include @@ -76,6 +77,7 @@ class SteamTarget { TargetWindow window_; Overlay& overlay_; SteamOverlayDetector detector_; + AppLauncher launcher_; WindowHandle last_foreground_window_ = nullptr; static inline WindowHandle target_window_handle_ = nullptr;