mirror of
https://github.com/kurikomoe/L4D2Fix.git
synced 2026-10-10 04:43:07 +08:00
feat: prevent recursive self-launching
- Prevent the launcher from starting itself to avoid infinite loops caused by misconfiguration. - Refactor and clean up related code.
This commit is contained in:
@@ -11,7 +11,7 @@ target := "main"
|
||||
|
||||
build:
|
||||
xmake f -m {{XMAKE_TYPE}} -y
|
||||
xmake
|
||||
xmake -j
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
+44
-33
@@ -1,11 +1,8 @@
|
||||
//#pragma comment(linker, "/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
|
||||
// #pragma comment(linker, "/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <shellapi.h>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
#include <detours/detours.h>
|
||||
#include <inipp.h>
|
||||
@@ -14,44 +11,54 @@
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
bool debug = false;
|
||||
std::wstring game_name = L"left4dead2.exe";
|
||||
const LPCSTR dll_path = "kpatch.dll";
|
||||
std::wstring gameName = L"left4dead2.exe";
|
||||
|
||||
void init_cfg() {
|
||||
LoadIni();
|
||||
if (cfg::Redirect::enable) {
|
||||
game_name = cfg::Redirect::target;
|
||||
gameName = cfg::Redirect::target;
|
||||
} else {
|
||||
game_name = L"left4dead2.exe";
|
||||
gameName = L"left4dead2.exe";
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename ... Args>
|
||||
template <typename T, typename... Args>
|
||||
void debugPrint(T fmt, Args... args) {
|
||||
std::wstring errMsg = std::wstring(L"[L4D2Fix] ") + std::vformat(fmt, std::make_wformat_args(args...));
|
||||
OutputDebugStringW(errMsg.c_str());
|
||||
}
|
||||
|
||||
int WINAPI wWinMain(
|
||||
_In_ HINSTANCE hInstance,
|
||||
_In_opt_ HINSTANCE hPrevInstance,
|
||||
_In_ HINSTANCE /*hInstance*/,
|
||||
_In_opt_ HINSTANCE /*hPrevInstance*/,
|
||||
_In_ LPWSTR lpwCmdLine,
|
||||
_In_ int nShowCmd
|
||||
) {
|
||||
std::wstring errMsg;
|
||||
_In_ int /*nShowCmd*/) {
|
||||
init_cfg();
|
||||
|
||||
WCHAR working_path[MAX_PATH];
|
||||
GetModuleFileNameW(nullptr, working_path, MAX_PATH);
|
||||
WCHAR buffer[MAX_PATH];
|
||||
GetModuleFileNameW(nullptr, buffer, MAX_PATH);
|
||||
|
||||
// Change the working directory to the directory containing the DLL.
|
||||
fs::path path(working_path);
|
||||
debugPrint(L"working_path {}\n", working_path);
|
||||
fs::path selfPath(buffer);
|
||||
fs::path workingPath = selfPath.parent_path();
|
||||
debugPrint(L"working_path {}\n", workingPath.c_str());
|
||||
|
||||
LPCSTR dll_path = "kpatch.dll";
|
||||
LPCWSTR targetExe = gameName.c_str();
|
||||
debugPrint(L"target_exe_path {}\n", targetExe);
|
||||
|
||||
LPCWSTR target_exe_path = game_name.c_str();
|
||||
debugPrint(L"target_exe_path {}\n", target_exe_path);
|
||||
if (selfPath.filename() == gameName) {
|
||||
debugPrint(L"Avoid launching self, exiting.\n");
|
||||
MessageBoxW(
|
||||
NULL,
|
||||
L"警告:配置错误,补丁尝试启动的目标为自身,请检查配置!\n"
|
||||
L"为防止卡死系统 L4D2Fix 将强制退出。\n"
|
||||
L"Fatal:Invalid config or setup!\n"
|
||||
L"The patch is trying to launch itself, please check the config!\n"
|
||||
L"Forcing exit to prevent system freeze.",
|
||||
L"L4D2Fix", MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
|
||||
ExitProcess(0);
|
||||
}
|
||||
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
@@ -65,18 +72,19 @@ int WINAPI wWinMain(
|
||||
|
||||
SetLastError(0);
|
||||
if (TRUE != DetourCreateProcessWithDllExW(
|
||||
target_exe_path,
|
||||
lpwCmdLine,
|
||||
nullptr,
|
||||
nullptr,
|
||||
TRUE,
|
||||
dwFlags,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&si,
|
||||
&pi,
|
||||
dll_path,
|
||||
nullptr)) {
|
||||
targetExe,
|
||||
lpwCmdLine,
|
||||
nullptr,
|
||||
nullptr,
|
||||
TRUE,
|
||||
dwFlags,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&si,
|
||||
&pi,
|
||||
dll_path,
|
||||
nullptr
|
||||
)) {
|
||||
auto dwError = GetLastError();
|
||||
debugPrint(L"DetourCreateProcessWithDllEx failed with error {}\n", dwError);
|
||||
|
||||
@@ -87,5 +95,8 @@ int WINAPI wWinMain(
|
||||
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+11
-15
@@ -59,10 +59,10 @@ void Logging() {
|
||||
auto start_time = std::chrono::system_clock::now();
|
||||
|
||||
if (cfg::System::debug) {
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
}
|
||||
else {
|
||||
spdlog::set_level(spdlog::level::info);
|
||||
spdlog::set_level(spdlog::level::info);
|
||||
}
|
||||
|
||||
spdlog::flush_on(spdlog::level::debug);
|
||||
@@ -88,13 +88,13 @@ void FirstRunCheck() {
|
||||
std::filesystem::path startup_check = sLogFile;
|
||||
|
||||
if (!std::filesystem::exists(startup_check)) {
|
||||
auto ret = MessageBoxW(
|
||||
MessageBoxW(
|
||||
NULL,
|
||||
L"这是一个启动测试,用于检验补丁是否正常运行。\n"
|
||||
"请注意由于修改内存,请不要进 VAC 服,后果自负。\n"
|
||||
"本弹窗仅首次启动出现,后续运行情况参见 L4D2Fix.log 日志文件。\n\n"
|
||||
"项目地址: https://github.com/kurikomoe/L4D2Fix\n"
|
||||
"关注B站 5050 直播间,谢谢喵! --KurikoMoe",
|
||||
L"请注意由于修改内存,请不要进 VAC 服,后果自负。\n"
|
||||
L"本弹窗仅首次启动出现,后续运行情况参见 L4D2Fix.log 日志文件。\n\n"
|
||||
L"项目地址: https://github.com/kurikomoe/L4D2Fix\n"
|
||||
L"关注B站 5050 直播间,谢谢喵! --KurikoMoe",
|
||||
pMsgboxTitle, MB_OK|MB_SYSTEMMODAL);
|
||||
}
|
||||
|
||||
@@ -135,13 +135,11 @@ DWORD __stdcall Main(void*) {
|
||||
spdlog::info(L"Startup commandline: {}", cmdline.c_str());
|
||||
|
||||
std::wstring dllName = L"shaderapidx9.dll";
|
||||
bool isVulkan = false;
|
||||
|
||||
std::transform(cmdline.cbegin(), cmdline.cend(), cmdline.begin(), ::tolower);
|
||||
std::transform(cmdline.cbegin(), cmdline.cend(), cmdline.begin(), ::towlower);
|
||||
if (cmdline.find(L"-vulkan") != std::wstring::npos) {
|
||||
dllName = L"shaderapivk.dll";
|
||||
spdlog::info(L"Detected Vulkan: using {}", dllName);
|
||||
isVulkan = true;
|
||||
} else {
|
||||
// Preload d3d9.dll
|
||||
LoadLibraryW(L"d3d9.dll");
|
||||
@@ -161,7 +159,7 @@ DWORD __stdcall Main(void*) {
|
||||
|
||||
if (hDll == nullptr) {
|
||||
auto errCode = GetLastError();
|
||||
wchar_t buf[255];
|
||||
wchar_t buf[MAX_PATH];
|
||||
FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, errCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
@@ -178,10 +176,8 @@ DWORD __stdcall Main(void*) {
|
||||
|
||||
// Fix exe name
|
||||
initExeNameHook();
|
||||
auto* buf = new char[255];
|
||||
GetModuleFileNameA(NULL, buf, 255); // Trigger the hook once
|
||||
|
||||
// Not known
|
||||
auto* buf = new char[MAX_PATH];
|
||||
GetModuleFileNameA(NULL, buf, MAX_PATH); // Trigger the hook once
|
||||
|
||||
auto ret = 0;
|
||||
ret += PatchIndices(hDll, dllName);
|
||||
|
||||
+67
-5
@@ -31,7 +31,6 @@ DWORD __stdcall hGetModuleFileNameA(
|
||||
const std::vector<std::string> moduleNameList = {
|
||||
"launcher.dll",
|
||||
};
|
||||
spdlog::info("GetModuleFileNameA called by module: {}", moduleName);
|
||||
for (auto& name : moduleNameList) {
|
||||
if (!strstr(moduleName, name.c_str())) {
|
||||
return ret;
|
||||
@@ -43,11 +42,74 @@ DWORD __stdcall hGetModuleFileNameA(
|
||||
std::wstring exeName = filePath.filename().wstring();
|
||||
if (cfg::Redirect::enable && exeName == cfg::Redirect::target) {
|
||||
spdlog::info("GetModuleFileNameA called by module: {}", moduleName);
|
||||
std::string newExeName = std::string(
|
||||
cfg::Redirect::origin.begin(), cfg::Redirect::origin.end());
|
||||
std::wstring newExeName = cfg::Redirect::origin;
|
||||
newFilePath = newFilePath.replace_filename(newExeName);
|
||||
std::string newFilePathStr = newFilePath.string();
|
||||
memcpy_s(lpFilename, nSize, newFilePath.string().c_str(), newFilePathStr.size() + 1);
|
||||
std::wstring newFilePathStr = newFilePath.wstring();
|
||||
|
||||
BOOL usedDefaultChar = FALSE;
|
||||
int requiredSize = WideCharToMultiByte(
|
||||
CP_ACP,
|
||||
WC_NO_BEST_FIT_CHARS,
|
||||
newFilePathStr.c_str(),
|
||||
-1,
|
||||
nullptr,
|
||||
0,
|
||||
"?",
|
||||
&usedDefaultChar
|
||||
);
|
||||
|
||||
if (requiredSize <= 0) {
|
||||
spdlog::warn("WideCharToMultiByte size query failed, error={}", GetLastError());
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string ansiPath(static_cast<size_t>(requiredSize), '\0');
|
||||
int convertedSize = WideCharToMultiByte(
|
||||
CP_ACP,
|
||||
WC_NO_BEST_FIT_CHARS,
|
||||
newFilePathStr.c_str(),
|
||||
-1,
|
||||
ansiPath.data(),
|
||||
requiredSize,
|
||||
"?",
|
||||
&usedDefaultChar
|
||||
);
|
||||
|
||||
if (convertedSize <= 0) {
|
||||
spdlog::warn("WideCharToMultiByte conversion failed, error={}", GetLastError());
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (nSize == 0) {
|
||||
spdlog::warn("Cannot write redirected path: lpFilename buffer size is 0");
|
||||
return ret;
|
||||
}
|
||||
|
||||
const size_t ansiLen = strnlen_s(ansiPath.c_str(), ansiPath.size());
|
||||
if (ansiLen + 1 > static_cast<size_t>(nSize)) {
|
||||
spdlog::warn(
|
||||
"Redirected path exceeds caller buffer: required={} bytes, capacity={} bytes; path will be truncated",
|
||||
ansiLen + 1,
|
||||
nSize
|
||||
);
|
||||
}
|
||||
|
||||
size_t copyLen = ansiLen;
|
||||
const size_t maxWritable = static_cast<size_t>(nSize - 1);
|
||||
if (copyLen > maxWritable) {
|
||||
copyLen = maxWritable;
|
||||
}
|
||||
|
||||
errno_t copyErr = memcpy_s(lpFilename, nSize, ansiPath.c_str(), copyLen);
|
||||
if (copyErr != 0) {
|
||||
spdlog::warn("memcpy_s failed while writing redirected path, errno={}", copyErr);
|
||||
return ret;
|
||||
}
|
||||
lpFilename[copyLen] = '\0';
|
||||
|
||||
if (usedDefaultChar) {
|
||||
spdlog::warn("Path conversion used fallback characters; non-ANSI characters may be lost");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user