# 2021q3 Homework1 (quiz1) ###### contributed by < `Kaminyou` > ### 0x00 Environment 1. Although `Linux v5.4` is recommened to utilized for this test, I used `Linux v4.06` and successfully build this testing kernel module. 2. However, two errors (`implicit declaration of function 'kmalloc'` and `implicit declaration of function 'copy_to_user'`) should be solved by appending two corresponding header files. ```c #include <linux/slab.h> #include <linux/uaccess.h> ``` 3. Fill the AAA to DDD with ``` AAA = list_for_each_entry_safe BBB = list_for_each_entry_safe CCC = list_add_tail(&proc->list_node, &hidden_proc) DDD = list_del(&proc->list_node) ``` 4. Then everything will go smoothly. 5. To solve problem 0x02. A VM of `Linux v5.8` is leveraged. ### 0x01 Explain the source code as well as the utilization of `ftrace` 1. `ftrace_hook` can hook a given function and execute what hacker (developer) wants to do. 2. So the whole concept is simple! First, maintain a list to store the `pid` that are sepcified to be hidden. Second, hook the `find_ge_pid` function. Third, when `find_ge_pid` is called, if the given `pid` is in the maintained list, we return what we want to do (in the provided source code, return a`pid` that is returned by `real_find_ge_pid(pid->numbers->nr + 1, ns)`); otherwise, we just return the `pid`. ```c static struct pid *hook_find_ge_pid(int nr, struct pid_namespace *ns) { struct pid *pid = real_find_ge_pid(nr, ns); while (pid && is_hidden_proc(pid->numbers->nr)) pid = real_find_ge_pid(pid->numbers->nr + 1, ns); return pid; } ``` 3. It can be inferred that `pidof` would call `find_ge_pid` and check the return struct `pid`. If the one is correct, then print the pid to stdout. If not, do nothing. ### 0x02 Provide a solution for newer linux kernel 1. An error of `ERROR: modpost: "kallsyms_lookup_name" [.../hideproc.ko] undefined!` would be generated. 2. The primary problem is that `kallsyms_lookup_name()` can be utilized as a backdoor to exploit any symbol (func. or data structures) in the kernel's symbol table via the returned address. Hence, it was removed. 3. With a github repo of [`kallsyms-mod`](https://github.com/h33p/kallsyms-mod), this limitation can be circumvented! 4. Clone the repo and copy required files: ```bash git clone https://github.com/h33p/kallsyms-mod cp kallsyms-mod{kallsyms_kp.c, kallsyms_lp.c, kallsyms.c, kallsyms.h, ksyms.h} /<path-to-main.c>/ ``` 5. Revise `main.c` referring to the provided sample in the repo: - Append headers ```c #include "kallsyms.h" #include "ksyms.h" ``` - Modify `_hideproc_init()` ```c KSYMDEF(kvm_lock); KSYMDEF(vm_list); static int _hideproc_init(void) { int r; int err, dev_major; dev_t dev; if ((r = init_kallsyms())) return r; KSYMINIT_FAULT(kvm_lock); KSYMINIT_FAULT(vm_list); if (r) return r; printk(KERN_INFO "@ %s\n", __func__); ... } ``` - Append the dependency in `Makefile` ```makefile $(MODULENAME)-y += main.o kallsyms.o ``` 7. Then, everything will go smoothly. ### 0x03 Enable hiding PPID 1. Enable searching for the PPID given a PID ```c static pid_t get_PPID_from_pid(pid_t pid) { struct pid *pid_self; struct task_struct *t; pid_t ppid = 0; pid_self = find_get_pid(pid); if (!pid_self) return ppid; t = get_pid_task(pid_self, PIDTYPE_PID); if (!t){ put_pid(pid_self); return ppid; } ppid = task_pid_vnr(t->real_parent); if (!ppid) { put_task_struct(t); return ppid; } return ppid; } ``` 2. Append this function to the original `hide_process()` ```c static int hide_process(pid_t pid) { pid_t ppid; pid_node_t *proc = kmalloc(sizeof(pid_node_t), GFP_KERNEL); proc->id = pid; list_add_tail(&proc->list_node, &hidden_proc); ppid = get_PPID_from_pid(pid); if (ppid){ pid_node_t *proc = kmalloc(sizeof(pid_node_t), GFP_KERNEL); proc->id = ppid; list_add_tail(&proc->list_node, &hidden_proc); } return SUCCESS; } ``` ### 0x04 Improve the source code 1. Enable the releasing of all allocated resources ```c static void _hideproc_exit(void) { printk(KERN_INFO "@ %s\n", __func__); /* FIXME: ensure the release of all allocated resources */ dev_t dev = (&cdev)->dev; device_destroy(hideproc_class, dev); cdev_del(&cdev); class_destroy(hideproc_class); unregister_chrdev_region(dev, MINOR_VERSION); } ``` 2. Initially, these is no validation for a given `pid` during `unhide_process` but merely expunge everything in the list. Add this validation. ```c static int unhide_process(pid_t pid) { pid_node_t *proc, *tmp_proc; pid_t ppid = get_PPID_from_pid(pid); list_for_each_entry_safe(proc, tmp_proc, &hidden_proc, list_node) { if ((pid == proc->id) || ((ppid) && (ppid == proc->id))){ list_del(&proc->list_node); kfree(proc); } } return SUCCESS; } ```