diff --git a/src/common/processing.h b/src/common/processing.h index ff2689a52..69d3ddc88 100644 --- a/src/common/processing.h +++ b/src/common/processing.h @@ -19,3 +19,10 @@ static inline const char* ffProcessAppendStdErr(FFstrbuf* buffer, char* const ar ffStrbufTrimRightSpace(buffer); return error; } + +#ifdef _WIN32 +bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath, bool* gui); +#else +void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath); +const char* ffProcessGetBasicInfoLinux(pid_t pid, char* name, pid_t* ppid, int32_t* tty); +#endif diff --git a/src/common/processing_linux.c b/src/common/processing_linux.c index 4401e3212..442be9c59 100644 --- a/src/common/processing_linux.c +++ b/src/common/processing_linux.c @@ -2,6 +2,7 @@ #include "common/processing.h" #include "common/io/io.h" #include "common/time.h" +#include "util/stringUtils.h" #include #include @@ -11,6 +12,12 @@ #include #include +#if defined(__FreeBSD__) || defined(__APPLE__) + #include + #include + #include +#endif + enum { FF_PIPE_BUFSIZ = 8192 }; static inline int ffPipe2(int *fds, int flags) @@ -101,3 +108,192 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use return "read(childPipeFd, str, FF_PIPE_BUFSIZ) failed"; } + +void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath) +{ + assert(processName->length > 0); + ffStrbufClear(exe); + + #ifdef __linux__ + + char filePath[64]; + snprintf(filePath, sizeof(filePath), "/proc/%d/cmdline", (int)pid); + + if(ffAppendFileBuffer(filePath, exe)) + { + ffStrbufTrimRightSpace(exe); + ffStrbufRecalculateLength(exe); //Trim the arguments + ffStrbufTrimLeft(exe, '-'); //Login shells start with a dash + } + + snprintf(filePath, sizeof(filePath), "/proc/%d/exe", (int)pid); + ffStrbufEnsureFixedLengthFree(exePath, PATH_MAX); + ssize_t length = readlink(filePath, exePath->chars, exePath->allocated - 1); + if (length > 0) // doesn't contain trailing NUL + { + exePath->chars[length] = '\0'; + exePath->length = (uint32_t) length; + } + + #elif defined(__APPLE__) + + size_t len = 0; + int mibs[] = { CTL_KERN, KERN_PROCARGS2, pid }; + if (sysctl(mibs, sizeof(mibs) / sizeof(*mibs), NULL, &len, NULL, 0) == 0) + { + #ifndef MAC_OS_X_VERSION_10_15 + //don't know why if don't let len longer, proArgs2 and len will change during the following sysctl() in old MacOS version. + len++; + #endif + FF_AUTO_FREE char* const procArgs2 = malloc(len); + if (sysctl(mibs, sizeof(mibs) / sizeof(*mibs), procArgs2, &len, NULL, 0) == 0) + { + // https://gist.github.com/nonowarn/770696#file-getargv-c-L46 + uint32_t argc = *(uint32_t*) procArgs2; + const char* realExePath = procArgs2 + sizeof(argc); + + const char* arg0 = memchr(realExePath, '\0', len - (size_t) (realExePath - procArgs2)); + ffStrbufSetNS(exePath, (uint32_t) (arg0 - realExePath), realExePath); + + do arg0++; while (*arg0 == '\0'); + assert(arg0 < procArgs2 + len); + if (*arg0 == '-') arg0++; // Login shells + + ffStrbufSetS(exe, arg0); + } + } + + #elif defined(__FreeBSD__) + + size_t size = ARG_MAX; + FF_AUTO_FREE char* args = malloc(size); + + static_assert(ARG_MAX > PATH_MAX, ""); + + if(sysctl( + (int[]){CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid}, 4, + args, &size, + NULL, 0 + ) == 0) + ffStrbufSetNS(exePath, (uint32_t) (size - 1), args); + + size = ARG_MAX; + if(sysctl( + (int[]){CTL_KERN, KERN_PROC, KERN_PROC_ARGS, pid}, 4, + args, &size, + NULL, 0 + ) == 0) + { + char* arg0 = args; + size_t arg0Len = strlen(args); + if (size > arg0Len + 1) + { + char* p = (char*) memrchr(args, '/', arg0Len); + if (p) + { + p++; + if (ffStrStartsWith(p, "python")) // /usr/local/bin/python3.9 /home/carter/.local/bin/xonsh + { + arg0 += arg0Len + 1; + } + } + } + if (arg0[0] == '-') arg0++; + ffStrbufSetS(exe, arg0); + } + + #endif + + if(exe->length == 0) + ffStrbufSet(exe, processName); + + assert(exe->length > 0); + uint32_t lastSlashIndex = ffStrbufLastIndexC(exe, '/'); + if(lastSlashIndex < exe->length) + *exeName = exe->chars + lastSlashIndex + 1; +} + +const char* ffProcessGetBasicInfoLinux(pid_t pid, char* name, pid_t* ppid, int32_t* tty) +{ + if (pid <= 0) + return "Invalid pid"; + + #ifdef __linux__ + + char statFilePath[64]; + snprintf(statFilePath, sizeof(statFilePath), "/proc/%d/stat", (int)pid); + char buf[PROC_FILE_BUFFSIZ]; + ssize_t nRead = ffReadFileData(statFilePath, sizeof(buf) - 1, buf); + if(nRead < 0) + return "ffReadFileData(statFilePath, sizeof(buf)-1, buf) failed"; + buf[nRead] = '\0'; + + *ppid = 0; + static_assert(sizeof(*ppid) == sizeof(int), ""); + + int tty_; + if( + sscanf(buf, "%*s (%255[^)]) %*c %d %*d %*d %d", name, ppid, &tty_) < 2 || //stat (comm) state ppid pgrp session tty + !ffStrSet(name) || + *ppid == 0 + ) + return "sscanf(stat) failed"; + + if (tty) + *tty = tty_ & 0xFF; + + #elif defined(__APPLE__) + + struct kinfo_proc proc; + size_t size = sizeof(proc); + if(sysctl( + (int[]){CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}, 4, + &proc, &size, + NULL, 0 + )) + return "sysctl(KERN_PROC_PID) failed"; + + *ppid = (pid_t)proc.kp_eproc.e_ppid; + strcpy(name, proc.kp_proc.p_comm); //trancated to 16 chars + if (tty) + { + *tty = ((proc.kp_eproc.e_tdev >> 24) & 0xFF) == 0x10 + ? proc.kp_eproc.e_tdev & 0xFFFFFF + : -1; + } + + #elif defined(__FreeBSD__) + + struct kinfo_proc proc; + size_t size = sizeof(proc); + if(sysctl( + (int[]){CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}, 4, + &proc, &size, + NULL, 0 + )) + return "sysctl(KERN_PROC_PID) failed"; + + *ppid = (pid_t)proc.ki_ppid; + strcpy(name, proc.ki_comm); + if (tty) + { + if (proc.ki_tdev != NODEV && proc.ki_flag & P_CONTROLT) + { + const char* ttyName = devname(proc.ki_tdev, S_IFCHR); + if (ffStrStartsWith(ttyName, "pts/")) + *tty = (int32_t) strtol(ttyName + strlen("pts/"), NULL, 10); + else + *tty = -1; + } + else + *tty = -1; + } + + #else + + return "Unsupported platform"; + + #endif + + return NULL; +} diff --git a/src/common/processing_windows.c b/src/common/processing_windows.c index 5cbd0819f..03fb7cbfd 100644 --- a/src/common/processing_windows.c +++ b/src/common/processing_windows.c @@ -3,6 +3,8 @@ #include "common/io/io.h" #include +#include +#include enum { FF_PIPE_BUFSIZ = 8192 }; @@ -124,3 +126,46 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use return NULL; } + +bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath, bool* gui) +{ + FF_AUTO_CLOSE_FD HANDLE hProcess = pid == 0 + ? GetCurrentProcess() + : OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + + if (gui) + *gui = GetGuiResources(hProcess, GR_GDIOBJECTS) > 0; + + if(ppid) + { + PROCESS_BASIC_INFORMATION info = {}; + ULONG size; + if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), &size))) + { + assert(size == sizeof(info)); + *ppid = (uint32_t)info.InheritedFromUniqueProcessId; + } + else + return false; + } + if(exe) + { + DWORD bufSize = exe->allocated; + if(QueryFullProcessImageNameA(hProcess, 0, exe->chars, &bufSize)) + { + // We use full path here + // Querying command line of remote processes in Windows requires either WMI or ReadProcessMemory + exe->length = bufSize; + if (exePath) ffStrbufSet(exePath, exe); + } + else + return false; + } + if(pname && exeName) + { + *exeName = exe->chars + ffStrbufLastIndexC(exe, '\\') + 1; + ffStrbufSetS(pname, *exeName); + } + + return true; +} diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index 77fd23c2a..6f6f6d804 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -10,12 +10,6 @@ #include #include -#if defined(__FreeBSD__) || defined(__APPLE__) - #include - #include - #include -#endif - static void setExeName(FFstrbuf* exe, const char** exeName) { assert(exe->length > 0); @@ -24,192 +18,6 @@ static void setExeName(FFstrbuf* exe, const char** exeName) *exeName = exe->chars + lastSlashIndex + 1; } -static void getProcessInformation(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath) -{ - assert(processName->length > 0); - ffStrbufClear(exe); - - #ifdef __linux__ - - char filePath[64]; - snprintf(filePath, sizeof(filePath), "/proc/%d/cmdline", (int)pid); - - if(ffAppendFileBuffer(filePath, exe)) - { - ffStrbufTrimRightSpace(exe); - ffStrbufRecalculateLength(exe); //Trim the arguments - ffStrbufTrimLeft(exe, '-'); //Login shells start with a dash - } - - snprintf(filePath, sizeof(filePath), "/proc/%d/exe", (int)pid); - ffStrbufEnsureFixedLengthFree(exePath, PATH_MAX); - ssize_t length = readlink(filePath, exePath->chars, exePath->allocated - 1); - if (length > 0) // doesn't contain trailing NUL - { - exePath->chars[length] = '\0'; - exePath->length = (uint32_t) length; - } - - #elif defined(__APPLE__) - - size_t len = 0; - int mibs[] = { CTL_KERN, KERN_PROCARGS2, pid }; - if (sysctl(mibs, sizeof(mibs) / sizeof(*mibs), NULL, &len, NULL, 0) == 0) - { - #ifndef MAC_OS_X_VERSION_10_15 - //don't know why if don't let len longer, proArgs2 and len will change during the following sysctl() in old MacOS version. - len++; - #endif - FF_AUTO_FREE char* const procArgs2 = malloc(len); - if (sysctl(mibs, sizeof(mibs) / sizeof(*mibs), procArgs2, &len, NULL, 0) == 0) - { - // https://gist.github.com/nonowarn/770696#file-getargv-c-L46 - uint32_t argc = *(uint32_t*) procArgs2; - const char* realExePath = procArgs2 + sizeof(argc); - - const char* arg0 = memchr(realExePath, '\0', len - (size_t) (realExePath - procArgs2)); - ffStrbufSetNS(exePath, (uint32_t) (arg0 - realExePath), realExePath); - - do arg0++; while (*arg0 == '\0'); - assert(arg0 < procArgs2 + len); - if (*arg0 == '-') arg0++; // Login shells - - ffStrbufSetS(exe, arg0); - } - } - - #elif defined(__FreeBSD__) - - size_t size = ARG_MAX; - FF_AUTO_FREE char* args = malloc(size); - - static_assert(ARG_MAX > PATH_MAX, ""); - - if(sysctl( - (int[]){CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid}, 4, - args, &size, - NULL, 0 - ) == 0) - ffStrbufSetNS(exePath, (uint32_t) (size - 1), args); - - size = ARG_MAX; - if(sysctl( - (int[]){CTL_KERN, KERN_PROC, KERN_PROC_ARGS, pid}, 4, - args, &size, - NULL, 0 - ) == 0) - { - char* arg0 = args; - size_t arg0Len = strlen(args); - if (size > arg0Len + 1) - { - char* p = (char*) memrchr(args, '/', arg0Len); - if (p) - { - p++; - if (ffStrStartsWith(p, "python")) // /usr/local/bin/python3.9 /home/carter/.local/bin/xonsh - { - arg0 += arg0Len + 1; - } - } - } - if (arg0[0] == '-') arg0++; - ffStrbufSetS(exe, arg0); - } - - #endif - - if(exe->length == 0) - ffStrbufSet(exe, processName); - - setExeName(exe, exeName); -} - -static const char* getProcessNameAndPpid(pid_t pid, char* name, pid_t* ppid, int32_t* tty) -{ - if (pid <= 0) - return "Invalid pid"; - - #ifdef __linux__ - - char statFilePath[64]; - snprintf(statFilePath, sizeof(statFilePath), "/proc/%d/stat", (int)pid); - char buf[PROC_FILE_BUFFSIZ]; - ssize_t nRead = ffReadFileData(statFilePath, sizeof(buf) - 1, buf); - if(nRead < 0) - return "ffReadFileData(statFilePath, sizeof(buf)-1, buf) failed"; - buf[nRead] = '\0'; - - *ppid = 0; - static_assert(sizeof(*ppid) == sizeof(int), ""); - - int tty_; - if( - sscanf(buf, "%*s (%255[^)]) %*c %d %*d %*d %d", name, ppid, &tty_) < 2 || //stat (comm) state ppid pgrp session tty - !ffStrSet(name) || - *ppid == 0 - ) - return "sscanf(stat) failed"; - - if (tty) - *tty = tty_ & 0xFF; - - #elif defined(__APPLE__) - - struct kinfo_proc proc; - size_t size = sizeof(proc); - if(sysctl( - (int[]){CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}, 4, - &proc, &size, - NULL, 0 - )) - return "sysctl(KERN_PROC_PID) failed"; - - *ppid = (pid_t)proc.kp_eproc.e_ppid; - strcpy(name, proc.kp_proc.p_comm); //trancated to 16 chars - if (tty) - { - *tty = ((proc.kp_eproc.e_tdev >> 24) & 0xFF) == 0x10 - ? proc.kp_eproc.e_tdev & 0xFFFFFF - : -1; - } - - #elif defined(__FreeBSD__) - - struct kinfo_proc proc; - size_t size = sizeof(proc); - if(sysctl( - (int[]){CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}, 4, - &proc, &size, - NULL, 0 - )) - return "sysctl(KERN_PROC_PID) failed"; - - *ppid = (pid_t)proc.ki_ppid; - strcpy(name, proc.ki_comm); - if (tty) - { - if (proc.ki_tdev != NODEV && proc.ki_flag & P_CONTROLT) - { - const char* ttyName = devname(proc.ki_tdev, S_IFCHR); - if (ffStrStartsWith(ttyName, "pts/")) - *tty = (int32_t) strtol(ttyName + strlen("pts/"), NULL, 10); - else - *tty = -1; - } - else - *tty = -1; - } - - #else - - return "Unsupported platform"; - - #endif - - return NULL; -} - static pid_t getShellInfo(FFShellResult* result, pid_t pid) { char name[256]; @@ -227,7 +35,7 @@ static pid_t getShellInfo(FFShellResult* result, pid_t pid) userShellName = instance.state.platform.userShell.chars + index + 1; } - while (getProcessNameAndPpid(pid, name, &ppid, &tty) == NULL) + while (ffProcessGetBasicInfoLinux(pid, name, &ppid, &tty) == NULL) { if (!ffStrEquals(userShellName, name)) { @@ -264,7 +72,7 @@ static pid_t getShellInfo(FFShellResult* result, pid_t pid) result->ppid = (uint32_t) ppid; result->tty = tty; ffStrbufSetS(&result->processName, name); - getProcessInformation(pid, &result->processName, &result->exe, &result->exeName, &result->exePath); + ffProcessGetInfoLinux(pid, &result->processName, &result->exe, &result->exeName, &result->exePath); break; } return ppid; @@ -277,7 +85,7 @@ static pid_t getTerminalInfo(FFTerminalResult* result, pid_t pid) pid_t ppid = 0; - while (getProcessNameAndPpid(pid, name, &ppid, NULL) == NULL) + while (ffProcessGetBasicInfoLinux(pid, name, &ppid, NULL) == NULL) { //Known shells if ( @@ -324,7 +132,7 @@ static pid_t getTerminalInfo(FFTerminalResult* result, pid_t pid) for (; pLeft < pRight; ++pLeft) if (*pLeft < 'a' || *pLeft > 'z') break; - if (pLeft == pRight && getProcessNameAndPpid(ppid, name, &ppid, NULL) != NULL) + if (pLeft == pRight && ffProcessGetBasicInfoLinux(ppid, name, &ppid, NULL) != NULL) return 0; } } @@ -333,7 +141,7 @@ static pid_t getTerminalInfo(FFTerminalResult* result, pid_t pid) result->pid = (uint32_t) pid; result->ppid = (uint32_t) ppid; ffStrbufSetS(&result->processName, name); - getProcessInformation(pid, &result->processName, &result->exe, &result->exeName, &result->exePath); + ffProcessGetInfoLinux(pid, &result->processName, &result->exe, &result->exeName, &result->exePath); break; } return ppid; @@ -348,10 +156,10 @@ static bool getTerminalInfoByPidEnv(FFTerminalResult* result, const char* pidEnv pid_t pid = (pid_t) strtol(envStr, NULL, 10); result->pid = (uint32_t) pid; char name[256]; - if (getProcessNameAndPpid(pid, name, (pid_t*) &result->ppid, NULL) == NULL) + if (ffProcessGetBasicInfoLinux(pid, name, (pid_t*) &result->ppid, NULL) == NULL) { ffStrbufSetS(&result->processName, name); - getProcessInformation(pid, &result->processName, &result->exe, &result->exeName, &result->exePath); + ffProcessGetInfoLinux(pid, &result->processName, &result->exe, &result->exeName, &result->exePath); return true; } diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index 4ba1b92d8..4470f22bc 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -9,8 +9,6 @@ #include #include #include -#include -#include static bool getProductVersion(const wchar_t* filePath, FFstrbuf* version) { @@ -39,56 +37,13 @@ static bool getProductVersion(const wchar_t* filePath, FFstrbuf* version) return false; } -static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath, bool* gui) -{ - FF_AUTO_CLOSE_FD HANDLE hProcess = pid == 0 - ? GetCurrentProcess() - : OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, TRUE, pid); - - if (gui) - *gui = GetGuiResources(hProcess, GR_GDIOBJECTS) > 0; - - if(ppid) - { - PROCESS_BASIC_INFORMATION info = {}; - ULONG size; - if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), &size))) - { - assert(size == sizeof(info)); - *ppid = (uint32_t)info.InheritedFromUniqueProcessId; - } - else - return false; - } - if(exe) - { - DWORD bufSize = exe->allocated; - if(QueryFullProcessImageNameA(hProcess, 0, exe->chars, &bufSize)) - { - // We use full path here - // Querying command line of remote processes in Windows requires either WMI or ReadProcessMemory - exe->length = bufSize; - if (exePath) ffStrbufSet(exePath, exe); - } - else - return false; - } - if(pname && exeName) - { - *exeName = exe->chars + ffStrbufLastIndexC(exe, '\\') + 1; - ffStrbufSetS(pname, *exeName); - } - - return true; -} - bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version); static uint32_t getShellInfo(FFShellResult* result, uint32_t pid) { uint32_t ppid = 0; - while (pid != 0 && getProcessInfo(pid, &ppid, &result->processName, &result->exe, &result->exeName, &result->exePath, NULL)) + while (pid != 0 && ffProcessGetInfoWindows(pid, &ppid, &result->processName, &result->exe, &result->exeName, &result->exePath, NULL)) { ffStrbufSet(&result->prettyName, &result->processName); if(ffStrbufEndsWithIgnCaseS(&result->prettyName, ".exe")) @@ -183,7 +138,7 @@ static bool getTerminalFromEnv(FFTerminalResult* result) //ConEmu uint32_t pid = (uint32_t) strtoul(term, NULL, 10); result->pid = pid; - if(getProcessInfo(pid, NULL, &result->processName, &result->exe, &result->exeName, &result->exePath, NULL)) + if(ffProcessGetInfoWindows(pid, NULL, &result->processName, &result->exe, &result->exeName, &result->exePath, NULL)) { ffStrbufSet(&result->prettyName, &result->processName); if(ffStrbufEndsWithIgnCaseS(&result->prettyName, ".exe")) @@ -299,7 +254,7 @@ static uint32_t getTerminalInfo(FFTerminalResult* result, uint32_t pid) uint32_t ppid = 0; bool hasGui; - while (pid != 0 && getProcessInfo(pid, &ppid, &result->processName, &result->exe, &result->exeName, &result->exePath, &hasGui)) + while (pid != 0 && ffProcessGetInfoWindows(pid, &ppid, &result->processName, &result->exe, &result->exeName, &result->exePath, &hasGui)) { if(!hasGui || ffStrbufIgnCaseEqualS(&result->processName, "far.exe")) // Far includes GUI objects... { @@ -380,7 +335,7 @@ const FFShellResult* ffDetectShell(void) result.tty = -1; uint32_t ppid; - if(!getProcessInfo(0, &ppid, NULL, NULL, NULL, NULL, NULL)) + if(!ffProcessGetInfoWindows(0, &ppid, NULL, NULL, NULL, NULL, NULL)) return &result; ppid = getShellInfo(&result, ppid);