diff --git a/driver/LKM/include/filter.h b/driver/LKM/include/filter.h index 76e45904e..b7748c78b 100644 --- a/driver/LKM/include/filter.h +++ b/driver/LKM/include/filter.h @@ -20,4 +20,8 @@ int execve_argv_check(char *data, int len); size_t filter_process_allowlist(const __user char *buff, size_t len); +/* opened-instances tracing for trace proc entry and filter device */ +void filter_show_instances(void); +void trace_show_instances(void); + #endif /* FILTER_H */ diff --git a/driver/LKM/src/filter.c b/driver/LKM/src/filter.c index c73d9ddac..1a703ad51 100644 --- a/driver/LKM/src/filter.c +++ b/driver/LKM/src/filter.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "../include/util.h" #include "../include/filter.h" @@ -30,6 +31,9 @@ #define DEL_EXECVE_ARGV_ALLOWLIST 74 /* J */ #define DEL_ALL_EXECVE_ARGV_ALLOWLIST 117 /* u */ #define EXECVE_ARGV_CHECK 122 /* z */ +#define OPEN_INSTANCES_LIST_ALL 79 /* O */ +#define OPEN_INSTANCES_LIST_TRACE 116 /* t */ +#define OPEN_INSTANCES_LIST_FILTER 102 /* f */ #define ALLOWLIST_NODE_MIN 5 #define ALLOWLIST_NODE_MAX 4090 @@ -37,7 +41,6 @@ static struct class *filter_class; static int filter_major; -static char *sh_mem = NULL; static struct rb_root execve_exe_allowlist = RB_ROOT; static struct rb_root execve_argv_allowlist = RB_ROOT; @@ -49,17 +52,6 @@ static int execve_argv_allowlist_limit = 0; static DEFINE_RWLOCK(exe_allowlist_lock); static DEFINE_RWLOCK(argv_allowlist_lock); -static int device_mmap(struct file *filp, struct vm_area_struct *vma); - -static ssize_t device_write(struct file *filp, const __user char *buff, - size_t len, loff_t * off); - -static const struct file_operations mchar_fops = { - .owner = THIS_MODULE, - .mmap = device_mmap, - .write = device_write, -}; - struct allowlist_node { struct rb_node node; char *data; @@ -347,11 +339,24 @@ size_t filter_process_allowlist(const __user char *buff, size_t len) { char *data_main; int res; - char flag; + char flag = 0; if (smith_get_user(flag, buff)) return len; + /* diagnosis cases: enmerate opened instances */ + if (flag == OPEN_INSTANCES_LIST_ALL) { + trace_show_instances(); + filter_show_instances(); + return len; + } else if (flag == OPEN_INSTANCES_LIST_FILTER) { + filter_show_instances(); + return len; + } else if (flag == OPEN_INSTANCES_LIST_TRACE) { + trace_show_instances(); + return len; + } + /* check whether length is valid */ if (len < ALLOWLIST_NODE_MIN || len > ALLOWLIST_NODE_MAX) return len; @@ -436,28 +441,93 @@ static ssize_t device_write(struct file *filp, const __user char *buff, return filter_process_allowlist(buff, len); } -static int device_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct page *page; - unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start); +struct filter_instance { + struct list_head next; + char comm[TASK_COMM_LEN]; + char node[__NEW_UTS_LEN]; + pid_t owner; +}; - if ((vma_pages(vma) + vma->vm_pgoff) > (SHMEM_MAX_SIZE >> PAGE_SHIFT)) { - return -EINVAL; +static LIST_HEAD(filter_list); +static struct mutex filter_lock; +static int filter_n_instances; + +void filter_show_instances(void) +{ + struct filter_instance *iter; + int niters = 0; + + mutex_lock(&filter_lock); + printk("filter device opened %d times:\n", filter_n_instances); + list_for_each_entry(iter, &filter_list, next) { + niters++; + printk("%6d: %u %16s %s\n", niters, iter->owner, iter->comm, iter->node); } + if (niters != filter_n_instances) + printk("inconsistent values: %d %d\n", niters, filter_n_instances); + mutex_unlock(&filter_lock); +} + +/* handling device open */ +static int device_open(struct inode *inode, struct file *filp) +{ + struct filter_instance *iter; + + iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; - page = virt_to_page((unsigned long)sh_mem + (vma->vm_pgoff << PAGE_SHIFT)); + /* tracing current task for this opened instance */ + iter->owner = current->pid; + memcpy(iter->comm, current->comm, TASK_COMM_LEN); + memcpy(iter->node, current->nsproxy->uts_ns->name.nodename, __NEW_UTS_LEN), - return remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), size, - vma->vm_page_prot); + mutex_lock(&filter_lock); + filter_n_instances++; + list_add_tail(&iter->next, &filter_list); + mutex_unlock(&filter_lock); + + filp->private_data = iter; + nonseekable_open(inode, filp); + __module_get(THIS_MODULE); + + return 0; } +/* handling deivce close */ +static int device_release(struct inode *inode, struct file *filp) +{ + struct filter_instance *iter = filp->private_data; + + if (!iter) + return 0; + + /* removing iter from trace_list */ + mutex_lock(&filter_lock); + list_del(&iter->next); + filter_n_instances--; + mutex_unlock(&filter_lock); + + kfree(iter); + module_put(THIS_MODULE); + + return 0; +} + +static const struct file_operations mchar_fops = { + .owner = THIS_MODULE, + .write = device_write, + .open = device_open, + .release = device_release +}; + int filter_init(void) { int ret; struct device *dev; + mutex_init(&filter_lock); filter_major = register_chrdev(0, FILTER_DEVICE_NAME, &mchar_fops); - if (filter_major < 0) { pr_err("[ELKEID FILTER] REGISTER_CHRDEV_ERROR\n"); return filter_major; @@ -476,27 +546,19 @@ int filter_init(void) dev = device_create(filter_class, NULL, MKDEV(filter_major, 0), NULL, FILTER_DEVICE_NAME); - if (IS_ERR(dev)) { pr_err("[ELKEID FILTER] DEVICE_CREATE_ERROR"); ret = PTR_ERR(dev); goto class_destroy; } - sh_mem = smith_kzalloc(SHMEM_MAX_SIZE, GFP_KERNEL); - if (sh_mem == NULL) { - pr_err("[ELKEID FILTER] SHMEM_INIT_ERROR\n"); - ret = -ENOMEM; - goto device_destroy; - } return 0; -device_destroy: - device_destroy(filter_class, MKDEV(filter_major, 0)); class_destroy: class_destroy(filter_class); chrdev_unregister: unregister_chrdev(filter_major, FILTER_DEVICE_NAME); + mutex_destroy(&filter_lock); return ret; } @@ -506,7 +568,11 @@ void filter_cleanup(void) device_destroy(filter_class, MKDEV(filter_major, 0)); class_destroy(filter_class); unregister_chrdev(filter_major, FILTER_DEVICE_NAME); + + if (filter_n_instances) + filter_show_instances(); + mutex_destroy(&filter_lock); + del_all_execve_exe_allowlist(); del_all_execve_argv_allowlist(); - smith_kfree(sh_mem); } diff --git a/driver/LKM/src/init.c b/driver/LKM/src/init.c index cb0422089..e38950ae0 100644 --- a/driver/LKM/src/init.c +++ b/driver/LKM/src/init.c @@ -60,7 +60,7 @@ static void __exit kprobes_exit(void) module_init(kprobes_init); module_exit(kprobes_exit); -MODULE_VERSION("1.7.0.21"); +MODULE_VERSION("1.7.0.22"); MODULE_LICENSE("GPL"); MODULE_INFO(homepage, "https://github.com/bytedance/Elkeid/tree/main/driver"); diff --git a/driver/LKM/src/smith_hook.c b/driver/LKM/src/smith_hook.c index d5353e9e1..13019953e 100644 --- a/driver/LKM/src/smith_hook.c +++ b/driver/LKM/src/smith_hook.c @@ -46,27 +46,41 @@ SMITH_HOOK(SETSID, 1); SMITH_HOOK(PRCTL, 1); SMITH_HOOK(MEMFD_CREATE, 1); SMITH_HOOK(MOUNT, 1); -SMITH_HOOK(DNS, 1); SMITH_HOOK(USERMODEHELPER, 1); SMITH_HOOK(UDEV, 1); SMITH_HOOK(CHMOD, 1); - -SMITH_HOOK(WRITE, 0); -SMITH_HOOK(ACCEPT, 0); -SMITH_HOOK(OPEN, 0); -SMITH_HOOK(MPROTECT, 0); SMITH_HOOK(NANOSLEEP, 0); -SMITH_HOOK(KILL, 0); -SMITH_HOOK(RM, 0); -SMITH_HOOK(EXIT, 0); -static int FAKE_SLEEP = 0; -static int FAKE_RM = 0; +SMITH_HOOK(WRITE, SANDBOX); +SMITH_HOOK(ACCEPT, SANDBOX); +SMITH_HOOK(OPEN, SANDBOX); +SMITH_HOOK(MPROTECT, SANDBOX); +SMITH_HOOK(KILL, SANDBOX); +SMITH_HOOK(RM, SANDBOX); +SMITH_HOOK(EXIT, SANDBOX); + +/* + * + * raw tracepoint brings severe performance penalty for syscall-intensive ops. + * so disabled by default, and enabled only for SANDBOX or kernels >= 5.4.210 + * + */ +SMITH_HOOK(RAWTP, SANDBOX || (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 210))); +SMITH_HOOK(DNS, SANDBOX || (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 210))); + +static int FAKE_RM = SANDBOX; +#if SANDBOX +static int PID_TREE_LIMIT = 100; +static int PID_TREE_LIMIT_LOW = 100; +static int EXECVE_GET_SOCK_PID_LIMIT = 100; +static int EXECVE_GET_SOCK_FD_LIMIT = 100; +#else static int PID_TREE_LIMIT = 12; static int PID_TREE_LIMIT_LOW = 8; static int EXECVE_GET_SOCK_PID_LIMIT = 4; static int EXECVE_GET_SOCK_FD_LIMIT = 12; /* maximum fd numbers to be queried */ +#endif static char connect_syscall_kprobe_state = 0x0; static char execve_kretprobe_state = 0x0; @@ -2702,6 +2716,10 @@ static int __init smith_sysret_init(void) { int i, rc; + /* skip raw tracepoint registration */ + if (!RAWTP_HOOK) + return 0; + /* check the tracepoints of our interest */ rc = smith_assert_tracepoints(); if (rc) { @@ -2732,6 +2750,10 @@ static void smith_sysret_fini(void) { int i; + /* skip raw tracepoint unregistration */ + if (!RAWTP_HOOK) + return; + /* register callbacks for the tracepoints of our interest */ for (i = NUM_TRACE_POINTS; i > 0; i--) smith_unregister_tracepoint(&g_smith_tracepoints[i - 1]); @@ -4804,28 +4826,6 @@ static void __init install_kprobe(void) { int ret; - if (SANDBOX == 1) { - DNS_HOOK = 1; - USERMODEHELPER_HOOK = 1; - //MPROTECT_HOOK = 1; - ACCEPT_HOOK = 1; - OPEN_HOOK = 1; - MPROTECT_HOOK = 1; - //NANOSLEEP_HOOK = 1; - KILL_HOOK = 1; - RM_HOOK = 1; - EXIT_HOOK = 1; - WRITE_HOOK = 1; - - PID_TREE_LIMIT = 100; - PID_TREE_LIMIT_LOW = 100; - EXECVE_GET_SOCK_PID_LIMIT = 100; - EXECVE_GET_SOCK_FD_LIMIT = 100; - - FAKE_SLEEP = 1; - FAKE_RM = 1; - } - if (UDEV_HOOK == 1) { static void (*smith_usb_register_notify) (struct notifier_block * nb); smith_usb_register_notify = __symbol_get("usb_register_notify"); @@ -5036,7 +5036,7 @@ static void __init install_kprobe(void) module_param(sid_##name, charp, S_IRUSR|S_IRGRP|S_IROTH) /* latest commit id */ -static char *smith_srcid = SMITH_SRCID(6b3b480a0c12ef8c24cb3417a109d2082b72cfd9); +static char *smith_srcid = SMITH_SRCID(d2effe977bb0090128d7bfcfb725d3a997fff2ca); static int __init kprobe_hook_init(void) { diff --git a/driver/LKM/src/trace.c b/driver/LKM/src/trace.c index ed748db6d..c83c4c3a5 100644 --- a/driver/LKM/src/trace.c +++ b/driver/LKM/src/trace.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "../include/util.h" #include "../include/trace.h" @@ -21,8 +22,12 @@ ((1 << (sizeof(((struct print_event_entry *)0)->id) * 8)) - 1) struct print_event_iterator { + struct list_head next; struct mutex mutex; struct tb_ring *ring; + char comm[TASK_COMM_LEN]; + char node[__NEW_UTS_LEN]; + pid_t owner; /* The below is zeroed out in pipe_read */ struct trace_seq seq; @@ -34,6 +39,9 @@ struct print_event_iterator { }; static struct tb_ring *trace_ring; +static LIST_HEAD(trace_list); +static struct mutex trace_lock; +static int trace_n_instances; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) static ssize_t(*trace_seq_to_user_sym) (struct trace_seq * s, @@ -54,6 +62,22 @@ static int trace_lookup_symbols(void) return 0; } +void trace_show_instances(void) +{ + struct print_event_iterator *iter; + int niters = 0; + + mutex_lock(&trace_lock); + printk("trace proc_entry opened %d times:\n", trace_n_instances); + list_for_each_entry(iter, &trace_list, next) { + niters++; + printk("%6d: %u %16s %s\n", niters, iter->owner, iter->comm, iter->node); + } + if (niters != trace_n_instances) + printk("inconsistent values: %d %d\n", niters, trace_n_instances); + mutex_unlock(&trace_lock); +} + static int trace_open_pipe(struct inode *inode, struct file *filp) { struct print_event_iterator *iter; @@ -72,6 +96,17 @@ static int trace_open_pipe(struct inode *inode, struct file *filp) #else iter->ring = PDE(inode)->data; #endif + + /* tracing current task for this opened instance */ + iter->owner = current->pid; + memcpy(iter->comm, current->comm, TASK_COMM_LEN); + memcpy(iter->node, current->nsproxy->uts_ns->name.nodename, __NEW_UTS_LEN), + + mutex_lock(&trace_lock); + trace_n_instances++; + list_add_tail(&iter->next, &trace_list); + mutex_unlock(&trace_lock); + filp->private_data = iter; nonseekable_open(inode, filp); __module_get(THIS_MODULE); @@ -344,6 +379,15 @@ static int trace_release_pipe(struct inode *inode, struct file *file) { struct print_event_iterator *iter = file->private_data; + if (!iter) + return 0; + + /* removing iter from trace_list */ + mutex_lock(&trace_lock); + list_del(&iter->next); + trace_n_instances--; + mutex_unlock(&trace_lock); + mutex_destroy(&iter->mutex); kfree(iter); module_put(THIS_MODULE); @@ -415,6 +459,7 @@ static int __init print_event_init(void) trace_ring = tb_alloc(RB_BUFFER_SIZE, TB_FL_OVERWRITE); if (!trace_ring) return -ENOMEM; + mutex_init(&trace_lock); if (!proc_create_data(PROC_ENDPOINT, S_IRUSR, NULL, &trace_pipe_fops, trace_ring)) @@ -437,6 +482,7 @@ static int __init print_event_init(void) errorout: tb_free(trace_ring); + mutex_destroy(&trace_lock); return -ENOMEM; } @@ -447,6 +493,10 @@ static void print_event_exit(void) if (trace_ring) tb_free(trace_ring); + if (trace_n_instances) + trace_show_instances(); + mutex_destroy(&trace_lock); + pr_info("destroy %d print event class\n", num_print_event_class()); }