From 7705237f10d51061879722aa2d16731be9d34385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 16 Jul 2023 18:26:04 +0800 Subject: [PATCH] Terminal (Windows): better terminal detection when running shell directly in Windows Explorer --- .../terminalshell/terminalshell_windows.c | 170 ++++++++++---- src/util/windows/registry.c | 219 ++++++++++-------- src/util/windows/registry.h | 1 + 3 files changed, 244 insertions(+), 146 deletions(-) diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index d7a42e0c5..165c14520 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -1,7 +1,10 @@ #include "terminalshell.h" +#include "common/io/io.h" #include "common/processing.h" #include "common/thread.h" #include "util/mallocHelper.h" +#include "util/windows/registry.h" +#include "util/windows/unicode.h" #include #include @@ -161,6 +164,118 @@ static uint32_t getShellInfo(FFTerminalShellResult* result, uint32_t pid) return ppid; } +static bool getTerminalFromEnv(FFTerminalShellResult* result) +{ + if( + result->terminalProcessName.length > 0 && + ffStrbufIgnCaseCompS(&result->terminalProcessName, "explorer") != 0 + ) return false; + + const char* term = getenv("ConEmuPID"); + + if(term) + { + //ConEmu + uint32_t pid = (uint32_t) strtoul(term, NULL, 10); + result->terminalPid = pid; + getProcessInfo(pid, NULL, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName); + return true; + } + + //SSH + if(getenv("SSH_CONNECTION") != NULL) + term = getenv("SSH_TTY"); + + //Windows Terminal + if(!term && ( + getenv("WT_SESSION") != NULL || + getenv("WT_PROFILE_ID") != NULL + )) term = "Windows Terminal"; + + //Alacritty + if(!term && ( + getenv("ALACRITTY_SOCKET") != NULL || + getenv("ALACRITTY_LOG") != NULL || + getenv("ALACRITTY_WINDOW_ID") != NULL + )) term = "Alacritty"; + + if(!term) + term = getenv("TERM_PROGRAM"); + + //Normal Terminal + if(!term) + term = getenv("TERM"); + + if(term) + { + ffStrbufSetS(&result->terminalProcessName, term); + ffStrbufSetS(&result->terminalPrettyName, term); + ffStrbufSetS(&result->terminalExe, term); + result->terminalExeName = ""; + return true; + } + + return false; +} + +static bool detectDefaultTerminal(FFTerminalShellResult* result) +{ + wchar_t regPath[128] = L"SOFTWARE\\Classes\\PackagedCom\\ClassIndex\\"; + wchar_t* uuid = regPath + strlen("SOFTWARE\\Classes\\PackagedCom\\ClassIndex\\"); + DWORD bufSize = 80; + if (RegGetValueW(HKEY_CURRENT_USER, L"Console\\%%Startup", L"DelegationTerminal", RRF_RT_REG_SZ, NULL, uuid, &bufSize) == ERROR_SUCCESS) + { + if(wcscmp(uuid, L"{00000000-0000-0000-0000-000000000000}") == 0) + { + // Let Windows deside + return false; + } + if(wcscmp(uuid, L"{B23D10C0-E52E-411E-9D5B-C09FDF709C7D}") == 0) + { + goto conhost; + } + + FF_HKEY_AUTO_DESTROY hKey = NULL; + if(ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regPath, &hKey, NULL)) + { + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + if(ffRegGetSubKey(hKey, 0, &path, NULL)) + { + if (ffStrbufStartsWithS(&path, "Microsoft.WindowsTerminal")) + { + ffStrbufSetS(&result->terminalProcessName, "WindowsTerminal.exe"); + ffStrbufSetS(&result->terminalPrettyName, "WindowsTerminal"); + ffStrbufSetF(&result->terminalExe, "%s\\WindowsApps\\%s\\WindowsTerminal.exe", getenv("ProgramFiles"), path.chars); + if(ffPathExists(result->terminalExe.chars, FF_PATHTYPE_FILE)) + { + result->terminalExeName = result->terminalExe.chars + ffStrbufLastIndexC(&result->terminalExe, '\\') + 1; + } + else + { + ffStrbufDestroy(&result->terminalExe); + ffStrbufInitMove(&result->terminalExe, &path); + result->terminalExeName = ""; + } + return true; + } + } + } + } + +conhost: + ffStrbufSetF(&result->terminalExe, "%s\\System32\\conhost.exe", getenv("SystemRoot")); + if(ffPathExists(result->terminalExe.chars, FF_PATHTYPE_FILE)) + { + ffStrbufSetS(&result->terminalProcessName, "conhost.exe"); + ffStrbufSetS(&result->terminalPrettyName, "conhost"); + result->terminalExeName = result->terminalExe.chars + ffStrbufLastIndexC(&result->terminalExe, '\\') + 1; + return true; + } + + ffStrbufClear(&result->terminalExe); + return false; +} + static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid) { uint32_t ppid; @@ -196,11 +311,14 @@ static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid) // A CUI program created by Windows Explorer will spawn a conhost as its child. // However the conhost process is just a placeholder; // The true terminal can be Windows Terminal or others. - ffStrbufClear(&result->terminalProcessName); - ffStrbufClear(&result->terminalPrettyName); - ffStrbufClear(&result->terminalExe); - result->terminalExeName = ""; - return 0; + if (!getTerminalFromEnv(result) && !detectDefaultTerminal(result)) + { + ffStrbufClear(&result->terminalProcessName); + ffStrbufClear(&result->terminalPrettyName); + ffStrbufClear(&result->terminalExe); + result->terminalExeName = ""; + return 0; + } } else result->terminalPid = pid; @@ -224,48 +342,6 @@ static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid) return ppid; } -static void getTerminalFromEnv(FFTerminalShellResult* result) -{ - if( - result->terminalProcessName.length > 0 && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "explorer") != 0 - ) return; - - const char* term = NULL; - - //SSH - if(getenv("SSH_CONNECTION") != NULL) - term = getenv("SSH_TTY"); - - //Windows Terminal - if(!term && ( - getenv("WT_SESSION") != NULL || - getenv("WT_PROFILE_ID") != NULL - )) term = "Windows Terminal"; - - //Alacritty - if(!term && ( - getenv("ALACRITTY_SOCKET") != NULL || - getenv("ALACRITTY_LOG") != NULL || - getenv("ALACRITTY_WINDOW_ID") != NULL - )) term = "Alacritty"; - - if(!term) - term = getenv("TERM_PROGRAM"); - - //Normal Terminal - if(!term) - term = getenv("TERM"); - - if(term) - { - ffStrbufSetS(&result->terminalProcessName, term); - ffStrbufSetS(&result->terminalPrettyName, term); - ffStrbufSetS(&result->terminalExe, term); - result->terminalExeName = ""; - } -} - bool fftsGetTerminalVersion(FFstrbuf* processName, FFstrbuf* exe, FFstrbuf* version); const FFTerminalShellResult* ffDetectTerminalShell(void) diff --git a/src/util/windows/registry.c b/src/util/windows/registry.c index 0a80781d3..d4dbffa93 100644 --- a/src/util/windows/registry.c +++ b/src/util/windows/registry.c @@ -1,99 +1,120 @@ -#include "registry.h" -#include "unicode.h" -#include "util/mallocHelper.h" - -static const char* hKey2Str(HKEY hKey) -{ - #define HKEY_CASE(compareKey) if(hKey == compareKey) return #compareKey; - HKEY_CASE(HKEY_CLASSES_ROOT) - HKEY_CASE(HKEY_CURRENT_USER) - HKEY_CASE(HKEY_LOCAL_MACHINE) - HKEY_CASE(HKEY_USERS) - HKEY_CASE(HKEY_PERFORMANCE_DATA) - HKEY_CASE(HKEY_PERFORMANCE_TEXT) - HKEY_CASE(HKEY_PERFORMANCE_NLSTEXT) - HKEY_CASE(HKEY_CURRENT_CONFIG) - HKEY_CASE(HKEY_DYN_DATA) - HKEY_CASE(HKEY_CURRENT_USER_LOCAL_SETTINGS) - #undef HKEY_CASE - - return "UNKNOWN"; -} - -bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error) -{ - if(RegOpenKeyExW(hKey, subKeyW, 0, KEY_READ, result) != ERROR_SUCCESS) - { - if(error) - { - FF_STRBUF_AUTO_DESTROY subKeyA = ffStrbufCreateWS(subKeyW); - ffStrbufAppendF(error, "RegOpenKeyExW(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars); - } - return false; - } - return true; -} - -bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error) -{ - DWORD bufSize; //with tailing '\0' - if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, NULL, &bufSize) != ERROR_SUCCESS) - { - if(error) - { - if(!valueNameW) - valueNameW = L"(default)"; - FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW); - ffStrbufAppendF(error, "RegGetValueW(%s, NULL, RRF_RT_REG_SZ) failed", valueNameA.chars); - } - return false; - } - wchar_t* FF_AUTO_FREE resultW = (wchar_t*)malloc(bufSize); - if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, resultW, &bufSize) != ERROR_SUCCESS) - { - if(error) - { - if(!valueNameW) - valueNameW = L"(default)"; - FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW); - ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_REG_SZ) failed", valueNameA.chars); - } - return false; - } - ffStrbufSetWS(result, resultW); - return true; -} - -bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error) -{ - DWORD bufSize = sizeof(*result); - if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_DWORD, NULL, result, &bufSize) != ERROR_SUCCESS) - { - if(error) - { - if(!valueNameW) - valueNameW = L"(default)"; - FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW); - ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_DWORD) failed", valueNameA.chars); - } - return false; - } - return true; -} - -bool ffRegReadUint64(HKEY hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error) -{ - DWORD bufSize = sizeof(*result); - if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_QWORD, NULL, result, &bufSize) != ERROR_SUCCESS) - { - if(error) - { - if(!valueNameW) - valueNameW = L"(default)"; - FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW); - ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_QWORD) failed", valueNameA.chars); - } - return false; - } - return true; -} +#include "registry.h" +#include "unicode.h" +#include "util/mallocHelper.h" + +static const char* hKey2Str(HKEY hKey) +{ + #define HKEY_CASE(compareKey) if(hKey == compareKey) return #compareKey; + HKEY_CASE(HKEY_CLASSES_ROOT) + HKEY_CASE(HKEY_CURRENT_USER) + HKEY_CASE(HKEY_LOCAL_MACHINE) + HKEY_CASE(HKEY_USERS) + HKEY_CASE(HKEY_PERFORMANCE_DATA) + HKEY_CASE(HKEY_PERFORMANCE_TEXT) + HKEY_CASE(HKEY_PERFORMANCE_NLSTEXT) + HKEY_CASE(HKEY_CURRENT_CONFIG) + HKEY_CASE(HKEY_DYN_DATA) + HKEY_CASE(HKEY_CURRENT_USER_LOCAL_SETTINGS) + #undef HKEY_CASE + + return "UNKNOWN"; +} + +bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error) +{ + if(RegOpenKeyExW(hKey, subKeyW, 0, KEY_READ, result) != ERROR_SUCCESS) + { + if(error) + { + FF_STRBUF_AUTO_DESTROY subKeyA = ffStrbufCreateWS(subKeyW); + ffStrbufAppendF(error, "RegOpenKeyExW(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars); + } + return false; + } + return true; +} + +bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error) +{ + DWORD bufSize; //with tailing '\0' + if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, NULL, &bufSize) != ERROR_SUCCESS) + { + if(error) + { + if(!valueNameW) + valueNameW = L"(default)"; + FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW); + ffStrbufAppendF(error, "RegGetValueW(%s, NULL, RRF_RT_REG_SZ) failed", valueNameA.chars); + } + return false; + } + wchar_t* FF_AUTO_FREE resultW = (wchar_t*)malloc(bufSize); + if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, resultW, &bufSize) != ERROR_SUCCESS) + { + if(error) + { + if(!valueNameW) + valueNameW = L"(default)"; + FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW); + ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_REG_SZ) failed", valueNameA.chars); + } + return false; + } + ffStrbufSetWS(result, resultW); + return true; +} + +bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error) +{ + DWORD bufSize = sizeof(*result); + if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_DWORD, NULL, result, &bufSize) != ERROR_SUCCESS) + { + if(error) + { + if(!valueNameW) + valueNameW = L"(default)"; + FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW); + ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_DWORD) failed", valueNameA.chars); + } + return false; + } + return true; +} + +bool ffRegReadUint64(HKEY hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error) +{ + DWORD bufSize = sizeof(*result); + if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_QWORD, NULL, result, &bufSize) != ERROR_SUCCESS) + { + if(error) + { + if(!valueNameW) + valueNameW = L"(default)"; + FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW); + ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_QWORD) failed", valueNameA.chars); + } + return false; + } + return true; +} + +bool ffRegGetSubKey(HKEY hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error) +{ + DWORD bufSize = 0; + if(RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, &bufSize, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) + { + if (error) + ffStrbufAppendS(error, "RegQueryInfoKeyW(hKey) failed"); + return false; + } + ++bufSize; + wchar_t* FF_AUTO_FREE resultW = (wchar_t*) malloc(bufSize * sizeof(*resultW)); + if(RegEnumKeyExW(hKey, index, resultW, &bufSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) + { + if (error) + ffStrbufAppendF(error, "RegEnumKeyExW(hKey, %u) failed", (unsigned) index); + return false; + } + ffStrbufSetWS(result, resultW); + return true; +} diff --git a/src/util/windows/registry.h b/src/util/windows/registry.h index 44fd5c430..222b468a7 100644 --- a/src/util/windows/registry.h +++ b/src/util/windows/registry.h @@ -19,5 +19,6 @@ bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrb bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error); bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error); bool ffRegReadUint64(HKEY hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error); +bool ffRegGetSubKey(HKEY hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error); #endif