---
###### tags: `sysprog2021q1`
---
# 2021q3 Homework1 (quiz1)
contributed by <`93i7xo2`>
> Source: [quiz1](https://hackmd.io/@sysprog/linux2021-summer-quiz1)
## 解釋上述程式碼運作原理,包含 ftrace 的使用
ftrace 的功用是在目標函式前附加上 callback 來追蹤在 kernel 內的流程,使用這項功能必須用 `struct ftrace_ops` 來告知 ftrace 何者為 callback。
```cpp
struct ftrace_hook {
...
struct ftrace_ops ops;
};
```
啟用/停用方法如下:
```cpp
register_ftrace_function(&ops);
unregister_ftrace_function(&ops);
```
以 `ftrace_set_filter()` 對特定名稱的函式進行 hook,若函式名稱重複可以指定 `ip` 的方式進行,`hook_install()` 即採用後者:
> [ftrace.c](https://elixir.bootlin.com/linux/latest/source/kernel/trace/ftrace.c#L5401)
```cpp
/**
* ftrace_set_filter_ip - set a function to filter on in ftrace by address
* @ops - the ops to set the filter with
* @ip - the address to add to or remove from the filter.
* @remove - non zero to remove the ip from the filter
* @reset - non zero to reset all filters before applying this filter.
*
* Filters denote which functions should be enabled when tracing is enabled
* If @ip is NULL, it fails to update filter.
*/
int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip,
int remove, int reset)
```
```cpp
ret = ftrace_set_filter(&ops, "schedule", strlen("schedule"), 0);
ret = ftrace_set_filter_ip(&ops, ip, 0, 0);
```
在 callback 換掉 `regs->ip` 達到劫持的作用:
```cpp
static void notrace hook_ftrace_thunk(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *ops,
struct pt_regs *regs) {
...
regs->ip = (unsigned long)hook->func;
}
```
### `hook_install()` 行為
1. 以 `kallsyms_lookup_name` 找尋名稱為 `find_ge_pid` 的 address
2. 設定 func & flags,呼叫 ftrace API 進行 hook
:::warning
Problem:
1. `hook_find_ge_pid` 行為不明,明明有簡單的 `find_pid_ns` 可用。
```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;
}
```
2. 使用`hook.func = hook_find_ge_pid;` 而非 `hook.func = &hook_find_ge_pid;`
:::
:::info
Reference
- [Using ftrace to hook to functions](https://www.kernel.org/doc/html/latest/trace/ftrace-uses.html)
- [The Linux Kernel API](https://www.kernel.org/doc/html/latest/core-api/kernel-api.html)
- [LABS - Character device drivers](https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html)
:::
## 本程式僅在 Linux v5.4 測試,若你用的核心較新,請試著找出替代方案
```
$ uname -r
5.4.0-80-generic
```
## 本核心模組只能隱藏單一 PID,請擴充為允許其 PPID 也跟著隱藏,或允許給定一組 PID 列表,而非僅有單一 PID
#### 隱藏 ppid
To-do
#### 多重 pid 輸入
在 `device_write()` 內以 `strsep` 分離字串(核心內無法使用 `strtok`),達到多個 pid 同時輸入的可能,最後去除輸入時重複 pid 的 entry。
```cpp
static ssize_t device_write(struct file *filep, const char *buffer, size_t len,
loff_t *offset) {
...
while ((found = strsep(&start, " ")) != NULL) {
if ((ret = kstrtol(found, 10, &pid)) == 0) {
switch (input_mode) {
case _ADD:
hide_process(pid);
break;
case _DEL:
unhide_process(pid);
break;
}
}
}
/* remove duplicate entries */
list_sort(priv, &hidden_proc, &list_cmp);
list_for_each_entry_safe(proc, tmp_proc, &hidden_proc, list_node) {
if (&tmp_proc->list_node != (&hidden_proc) && tmp_proc->id == proc->id) {
list_del(&proc->list_node);
kfree(proc);
}
}
...
}
```
假設 `pid = {1, 2, 7, 8, 6}` 的 process 存在,批次隱藏/復原 pid 的步驟如下:
```bash
$ sudo insmod hideproc.ko
$ echo "add 8 7 6" | sudo tee /dev/hideproc
add 8 7 6
$ echo "del 1 2 8" | sudo tee /dev/hideproc
del 1 2 8
$ sudo cat /dev/hideproc
pid 6
pid 7
$ echo "del 6" | sudo tee /dev/hideproc
del 6
$ sudo cat /dev/hideproc
pid 7
$ sudo rmmod hideproc
```
## 指出程式碼可改進的地方,並動手實作
#### a. module init/exit
適當的初始化及釋放資源
```cpp
static int _hideproc_init(void) {
int dev_major;
printk(KERN_INFO "@ %s\n", __func__);
if (alloc_chrdev_region(&dev, 0, MINOR_VERSION, DEVICE_NAME) < 0) {
return -1;
}
dev_major = MAJOR(dev);
cdev_init(&cdev, &fops);
if (cdev_add(&cdev, MKDEV(dev_major, MINOR_VERSION), 1) == -1) {
unregister_chrdev_region(dev, 1);
return -1;
}
if (IS_ERR(hideproc_class = class_create(THIS_MODULE, DEVICE_NAME))) {
cdev_del(&cdev);
unregister_chrdev_region(dev, 1);
return -1;
}
if (IS_ERR(device_create(hideproc_class, NULL,
MKDEV(dev_major, MINOR_VERSION), NULL,
DEVICE_NAME))) {
class_destroy(hideproc_class);
cdev_del(&cdev);
unregister_chrdev_region(dev, 1);
return -1;
}
init_hook();
return 0;
}
```
```cpp
static void _hideproc_exit(void) {
hook_remove(&hook);
device_destroy(hideproc_class, MKDEV(MAJOR(dev), MINOR_VERSION));
class_destroy(hideproc_class);
cdev_del(&cdev);
unregister_chrdev_region(dev, 1);
printk(KERN_INFO "@ %s\n", __func__);
}
```
卸載模組前告知 ftrace 並釋放 list 上所有元素
```cpp
void hook_remove(struct ftrace_hook *hook) {
pid_node_t *proc, *tmp_proc;
list_for_each_entry_safe(proc, tmp_proc, &hidden_proc, list_node) {
list_del(&proc->list_node);
kfree(proc);
}
int err = unregister_ftrace_function(&hook->ops);
if (err)
printk("unregister_ftrace_function() failed: %d\n", err);
err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
if (err)
printk("ftrace_set_filter_ip() failed: %d\n", err);
}
```