diff --git a/src/common/FFstrbuf.h b/src/common/FFstrbuf.h index 879508a07..be286c012 100644 --- a/src/common/FFstrbuf.h +++ b/src/common/FFstrbuf.h @@ -466,6 +466,13 @@ static inline bool ffStrbufInitJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) { return ptr ? (uint32_t) (ptr - strbuf->chars) : strbuf->length; } +// +[[gnu::nonnull(1), gnu::pure, nodiscard]] static inline bool ffStrbufContainAnyC(const FFstrbuf* strbuf, const char* cs) { + assert(cs != nullptr); + + return strpbrk(strbuf->chars, cs) != nullptr; +} + [[gnu::nonnull(1, 3), gnu::pure, nodiscard]] static inline uint32_t ffStrbufNextIndexS(const FFstrbuf* strbuf, uint32_t start, const char* str) { assert(start <= strbuf->length); diff --git a/src/common/impl/processing_windows.c b/src/common/impl/processing_windows.c index f123ec11a..61467bf1d 100644 --- a/src/common/impl/processing_windows.c +++ b/src/common/impl/processing_windows.c @@ -29,7 +29,7 @@ static void argvToCmdline(char* const argv[], FFstrbuf* result) { } // Add quotes around string if whitespace chars are present (with slash duplicating at the end of string) - if (ffStrbufFirstIndexS(&temp, " \t") != temp.length) { + if (ffStrbufContainAnyC(&temp, " \t")) { uint32_t pos = temp.length; uint32_t cnt = 0; while (pos > 0 && temp.chars[pos - 1] == '\\') { ++cnt, --pos; } @@ -142,6 +142,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFNativeFD stdinF { FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); argvToCmdline(argv, &buf); + FF_DEBUG("Command line: %s", buf.chars); uint32_t cmdlineBytes = (buf.length + 1) * sizeof(wchar_t); cmdline = malloc(cmdlineBytes); NTSTATUS status = RtlUTF8ToUnicodeN(cmdline, cmdlineBytes, nullptr, buf.chars, buf.length + 1); @@ -265,6 +266,12 @@ exit: { if (NT_SUCCESS(status)) { assert(size == sizeof(info)); if (info.ExitStatus != STILL_ACTIVE && info.ExitStatus != 0) { + if (info.ExitStatus == 9009) { + // Note: CMD swallows 9009 errorlevel and returns 1 by default. + // One must use `& call exit %^ERRORLEVEL%` to propagate the correct error code. + FF_DEBUG("Child process exited with error code 9009 (command not found)"); + return "command not found"; + } FF_DEBUG("Child process exited with an error: %x %s", (unsigned) info.ExitStatus, ffDebugNtStatus(info.ExitStatus)); return "Child process exited with an error"; } diff --git a/src/detection/command/command.c b/src/detection/command/command.c index 18ea9c915..4d2b12e36 100644 --- a/src/detection/command/command.c +++ b/src/detection/command/command.c @@ -15,6 +15,12 @@ static const char* spawnProcess(FFCommandOptions* options, FFProcessHandle* hand return "No command text specified"; } +#if _WIN32 + if (ffStrbufEqualS(&options->shell, "cmd.exe") && ffStrbufEqualS(&options->param, "/d/c")) { + ffStrbufAppendS(&options->text, " & exit %^errorlevel%"); // Necessary to propagate 9009 from CMD + } +#endif + return ffProcessSpawn(options->param.length ? (char* const[]) { options->shell.chars, options->param.chars, diff --git a/src/modules/command/command.c b/src/modules/command/command.c index 9008340d3..e3f5dc175 100644 --- a/src/modules/command/command.c +++ b/src/modules/command/command.c @@ -162,7 +162,7 @@ void ffInitCommandOptions(FFCommandOptions* options) { ); ffStrbufInitStatic(&options->param, #ifdef _WIN32 - "/c" + "/d/c" #else "-c" #endif