# 2021q3 Homework1 (quiz1) contributed by < `lekoOwO` > ## 1. 解釋上述程式碼運作原理,包含 ftrace 的使用 這個程式藉由 Hook ftrace 的方式換掉了原本的 `find_ge_pid` (本次用以代換的函式是 `hook->ops.func`,也就是 `hook_ftrace_thunk`)。 在被觸發時,`hook_ftrace_thunk` 內部的操作 (`regs->ip = (unsigned long) hook->func;`) 會把 ip 指到 `hook_find_ge_pid` 上去,藉此綁架原先的 `find_ge_pid`。 ```cpp 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; } ``` 可以看到,如果發現是 hidden proc 的話,pid 就會故意 + 1,讓原本的 `find_ge_pid` 找不到。 ## 2. 本程式僅在 Linux v5.4 測試,若你用的核心較新,請試著找出替代方案 ```shell $ uname -r 5.4.0-80-generic ``` 所以我就沒找了( ## 3. 本核心模組只能隱藏單一 PID,請擴充為允許其 PPID 也跟著隱藏,或允許給定一組 PID 列表,而非僅有單一 PID 修改部分 `device_write` 的 Code: ```c= static ssize_t device_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) { long pid; char *message; char add_message[] = "add", del_message[] = "del"; char delim[] = " "; if (len < sizeof(add_message) - 1 && len < sizeof(del_message) - 1) return -EAGAIN; message = kmalloc(len + 1, GFP_KERNEL); memset(message, 0, len + 1); copy_from_user(message, buffer, len); if (!memcmp(message, add_message, sizeof(add_message) - 1)) { char token = strsep(&message, delim); for(token = strsep(&message, delim); token != NULL; token = strsep(&message, delim)) { kstrtol(token, 10, &pid); hideprocess(pid); } } else if (!memcmp(message, del_message, sizeof(del_message) - 1)) { char* token = strsep(&message, delim); for(token = strsep(&message, delim); token != NULL; token = strsep(&message, delim)) { kstrtol(token, 10, &pid); unhide_process(pid); } } else { kfree(message); return -EAGAIN; } *offset = len; kfree(message); return len; } ``` 注意到 L18 ~ L22, L24 ~ L28 使用了 `strsep` 來對 `message` 以 `delim` 切割 藉此讀入多組 pid 而預設的 `delim` 是 `" "` (L10)。 ## 4.指出程式碼可改進的地方,並動手實作 1. `unhide_process` 沒有驗證 `pid`,直接刪光光 修正: ```c= static int unhide_process(pid_t pid) { pid_node_t *proc, *tmp_proc; list_for_each_entry_safe(proc, tmp_proc, &hidden_proc, list_node) { if (proc->id == pid) { list_del(&proc->list_node); kfree(proc); } } return SUCCESS; } ``` 2. 資源釋放 ```c= static void _hideproc_exit(void) { printk(KERN_INFO "@ %s\n", __func__); hook_remove(&hook); device_destroy(hideproc_class, minor_dev); class_destroy(hideproc_class); cdev_del(&cdev); unregister_chrdev_region(dev, MINOR_VERSION); } ``` L6 的 minor_dev 是 `_hideproc_init` 裡 `MKDEV(dev_major, MINOR_VERSION)` 的 `dev_t` 我覺得一直 call 他很醜,所以就拿出來存比較好看。