From dd41765b9a20972cca99b2683c29f45fd36f4616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1?= Date: Thu, 19 Mar 2026 08:10:01 +0800 Subject: [PATCH 1/3] Refactor CLI argument parsing to use `--` delimiter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace `--args="..."` flag with standard `--` delimiter for passing arguments to the target executable. This follows conventional CLI patterns where arguments after `--` are forwarded directly to the spawned process rather than being parsed by Syringe. Changes: - Update usage message to reflect new `--` syntax - Modify `parse_command_line` to recognize `--` as the delimiter - Append all arguments after `--` to the game arguments string - Remove deprecated `--args=` flag handling Signed-off-by: 舰队的偶像-岛风酱 --- Main.cpp | 2 +- README.md | 4 ++-- Support.h | 26 +++++++++++++++++--------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Main.cpp b/Main.cpp index 27bc1dc..740c364 100644 --- a/Main.cpp +++ b/Main.cpp @@ -84,7 +84,7 @@ int Run(const std::vector& arguments) { MessageBoxA( nullptr, "Syringe cannot be run like that.\n\n" - "Usage:\nSyringe.exe [-i= ...] [--args=\"\"]", + "Usage:\nSyringe.exe [-i= ...] [-- ]", VersionString, MB_OK | MB_ICONINFORMATION); Log::WriteLine( diff --git a/README.md b/README.md index fe1af63..34da3d1 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ syringe.exe game.exe ### Passing Arguments to the Target Executable -Use `--args="..."` to provide arguments for the launched process: +Use `--` as a delimiter to pass arguments to the launched process. All arguments after `--` will be forwarded directly to the target executable: ``` -syringe.exe game.exe --args="-CD. -SPAWN" +syringe.exe game.exe -- -CD. -SPAWN ``` ### DLL Injection Behavior diff --git a/Support.h b/Support.h index e3b699b..7bc723e 100644 --- a/Support.h +++ b/Support.h @@ -28,8 +28,6 @@ inline auto trim(std::string_view string) noexcept inline auto parse_command_line(const std::vector& arguments) { - static constexpr std::string_view ARGS_FLAG = "--args="; - struct argument_set { std::vector syringe_arguments; @@ -45,6 +43,8 @@ inline auto parse_command_line(const std::vector& arguments) // First non-flag argument becomes executable name bool exe_found = false; + bool exe_arguments = false; + for (const auto& arg : arguments) { // executable name: first argument not starting with '-' @@ -55,22 +55,30 @@ inline auto parse_command_line(const std::vector& arguments) continue; } - // game arguments: --args="blob" - if (arg.starts_with(ARGS_FLAG)) + if (arg == "--") { - // extract after --args= - std::string blob = arg.substr(ARGS_FLAG.size()); - ret.game_arguments = blob; + exe_arguments = true; continue; } - // Syringe arguments - ret.syringe_arguments.push_back(arg); + if (exe_arguments) + { + // game arguments + ret.game_arguments += " "; + ret.game_arguments += arg; + } + else + { + // Syringe arguments + ret.syringe_arguments.push_back(arg); + } } if (!exe_found || ret.executable_name.empty()) throw invalid_command_arguments{}; + ret.game_arguments = ret.game_arguments.substr(1); + return ret; } From 83e3ec6ee38441e4a884953f8bfb597d30678bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1?= Date: Thu, 19 Mar 2026 08:43:34 +0800 Subject: [PATCH 2/3] Add support for quoted arguments in CLI parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle quoted strings in command-line arguments to properly parse paths with spaces. Both executable names and injection DLL paths can now be wrapped in double quotes. Changes: - Strip surrounding quotes from executable name if present - Parse `-i="path with spaces.dll"` format by removing quotes - Preserve argument integrity when paths contain spaces Signed-off-by: 舰队的偶像-岛风酱 --- Support.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Support.h b/Support.h index 7bc723e..c7e4dc7 100644 --- a/Support.h +++ b/Support.h @@ -44,14 +44,18 @@ inline auto parse_command_line(const std::vector& arguments) bool exe_found = false; bool exe_arguments = false; - + for (const auto& arg : arguments) { // executable name: first argument not starting with '-' if (!exe_found && !arg.starts_with("-")) { exe_found = true; - ret.executable_name = arg; + if (arg.starts_with('"') && arg.ends_with('"')) + ret.executable_name = arg.substr(1, arg.length() - 2); + else + ret.executable_name = arg; + continue; } @@ -70,7 +74,10 @@ inline auto parse_command_line(const std::vector& arguments) else { // Syringe arguments - ret.syringe_arguments.push_back(arg); + if (arg.starts_with("-i=\"") && arg.ends_with('"')) + ret.syringe_arguments.push_back("-i=" + arg.substr(4, arg.length() - 5)); + else + ret.syringe_arguments.push_back(arg); } } From 7c8c5e7cf60b013c9558be27fc07fe84857ac2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1?= Date: Thu, 19 Mar 2026 13:42:55 +0800 Subject: [PATCH 3/3] Implement raw command-line parsing for `--` delimiter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parse the raw command line string to properly detect the `--` separator before argument splitting. This ensures that quoted arguments containing the `--` delimiter are handled correctly and not mistakenly treated as separators. The previous approach used `CommandLineToArgvW` which splits arguments before parsing, making it impossible to distinguish between a quoted `--` in an argument and the actual separator. Changes: - Add `FindSeparator()` to locate `--` in raw command line with proper quote and escape handling - Modify `GetArguments()` to split raw command line at separator - Remove `--` handling from `parse_command_line()` since it's now done earlier - Return separate `syringe_args` and `game_args` from `GetArguments()` Signed-off-by: 舰队的偶像-岛风酱 --- Main.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++++++------- Support.h | 27 +++----------------- 2 files changed, 70 insertions(+), 32 deletions(-) diff --git a/Main.cpp b/Main.cpp index 740c364..c6e5153 100644 --- a/Main.cpp +++ b/Main.cpp @@ -8,11 +8,61 @@ #include #include -std::vector GetArguments() +struct Arguments { + std::vector syringe_args; + std::string game_args; +}; + +size_t FindSeparator(const std::wstring& cmdLine) +{ + bool inQuotes = false; + size_t len = cmdLine.length(); + for (size_t i = 0; i < len; ++i) + { + if (cmdLine[i] == L'\\' && i + 1 < len && cmdLine[i + 1] == L'"') + { + i += 1; + continue; + } + + if (cmdLine[i] == L'"') + { + inQuotes = !inQuotes; + continue; + } + + if (!inQuotes && cmdLine[i] == L' ') + { + if (i + 3 < len && cmdLine[i + 1] == L'-' && cmdLine[i + 2] == L'-' && cmdLine[i + 3] == L' ') + { + return i; + } + } + } + return std::wstring::npos; +} + +Arguments GetArguments() +{ + std::wstring wszSyringeArgs; + std::wstring wszGameArgs; + std::wstring lpCmdLine = GetCommandLineW(); + auto separator = FindSeparator(lpCmdLine); + if (separator != std::wstring::npos) + { + wszSyringeArgs = lpCmdLine.substr(0, separator); + wszGameArgs = lpCmdLine.substr(separator + 4); + } + else + { + wszSyringeArgs = lpCmdLine; + wszGameArgs = L""; + } + // Get argc, argv in wide chars int argc = 0; - LPWSTR* argvW = CommandLineToArgvW(GetCommandLineW(), &argc); + LPWSTR* argvW = CommandLineToArgvW(wszSyringeArgs.c_str(), &argc); // Convert to UTF-8. Skip the first argument as it contains the path to Syringe itself std::vector argv(argc - 1); @@ -25,10 +75,17 @@ std::vector GetArguments() LocalFree(argvW); - return argv; + int len = WideCharToMultiByte(CP_UTF8, 0, wszGameArgs.c_str(), -1, nullptr, 0, nullptr, nullptr); + std::string gameArgs = std::string(len - 1, '\0'); + WideCharToMultiByte(CP_UTF8, 0, wszGameArgs.c_str(), -1, gameArgs.data(), len, nullptr, nullptr); + + return { + argv, + gameArgs, + }; } -int Run(const std::vector& arguments) +int Run(const Arguments& arguments) { constexpr auto const VersionString = "SyringeEx " SYRINGEEX_VER_TEXT ", based on Syringe 0.7.2.0"; @@ -39,14 +96,14 @@ int Run(const std::vector& arguments) Log::WriteLine(VersionString); Log::WriteLine("==============="); Log::WriteLine(); - Log::WriteLine("WinMain: arguments = \"%.*s\"", printable(arguments)); + Log::WriteLine("WinMain: arguments = \"%.*s\"", printable(arguments.syringe_args)); auto failure = "Could not load executable."; auto exit_code = ERROR_ERRORS_ENCOUNTERED; try { - auto const command = parse_command_line(arguments); + auto const command = parse_command_line(arguments.syringe_args); Log::WriteLine( "WinMain: Trying to load executable file \"%.*s\"...", @@ -62,10 +119,10 @@ int Run(const std::vector& arguments) Log::WriteLine( "WinMain: SyringeDebugger::Run(\"%.*s\");", - printable(command.game_arguments)); + printable(arguments.game_args)); Log::WriteLine(); - Debugger.Run(command.game_arguments); + Debugger.Run(arguments.game_args); Log::WriteLine("WinMain: SyringeDebugger::Run finished."); Log::WriteLine("WinMain: Exiting on success."); return ERROR_SUCCESS; @@ -84,7 +141,7 @@ int Run(const std::vector& arguments) { MessageBoxA( nullptr, "Syringe cannot be run like that.\n\n" - "Usage:\nSyringe.exe [-i= ...] [-- ]", + "Usage:\nSyringe.exe [-i= ...] [-- ]", VersionString, MB_OK | MB_ICONINFORMATION); Log::WriteLine( diff --git a/Support.h b/Support.h index c7e4dc7..56b36dc 100644 --- a/Support.h +++ b/Support.h @@ -32,7 +32,6 @@ inline auto parse_command_line(const std::vector& arguments) { std::vector syringe_arguments; std::string executable_name; - std::string game_arguments; }; if (arguments.empty()) @@ -43,8 +42,6 @@ inline auto parse_command_line(const std::vector& arguments) // First non-flag argument becomes executable name bool exe_found = false; - bool exe_arguments = false; - for (const auto& arg : arguments) { // executable name: first argument not starting with '-' @@ -59,32 +56,16 @@ inline auto parse_command_line(const std::vector& arguments) continue; } - if (arg == "--") - { - exe_arguments = true; - continue; - } - - if (exe_arguments) - { - // game arguments - ret.game_arguments += " "; - ret.game_arguments += arg; - } + // Syringe arguments + if (arg.starts_with("-i=\"") && arg.ends_with('"')) + ret.syringe_arguments.push_back("-i=" + arg.substr(4, arg.length() - 5)); else - { - // Syringe arguments - if (arg.starts_with("-i=\"") && arg.ends_with('"')) - ret.syringe_arguments.push_back("-i=" + arg.substr(4, arg.length() - 5)); - else - ret.syringe_arguments.push_back(arg); - } + ret.syringe_arguments.push_back(arg); } if (!exe_found || ret.executable_name.empty()) throw invalid_command_arguments{}; - ret.game_arguments = ret.game_arguments.substr(1); return ret; }