# 2021q3 Homework1 (quiz1)
contributed by < [fdgkhdkgh](https://) >
###### tags: `linux2021` `kernel`
[第一週隨堂測驗題](https://hackmd.io/@sysprog/linux2021-summer-quiz1)
## 解釋上述程式碼運作原理,包含 ftrace 的使用
在 kernel module 初始化的地方,呼叫 init_hook
```cpp
static int _hideproc_init(void)
{
.
.
.
init_hook();
return 0;
}
```
---
```cpp
struct ftrace_hook {
const char *name;
void *func, *orig;
unsigned long address;
struct ftrace_ops ops;
};
.
.
.
static void init_hook(void)
{
// 使用 kallsyms_lookup_name 去找到 linux kernel 中 "find_ge_pid" 這個 function 的位址
real_find_ge_pid = (find_ge_pid_func) kallsyms_lookup_name("find_ge_pid");
hook.name = "find_ge_pid";
// 儲存我們想執行的 find_ge_pid 的位址
hook.func = hook_find_ge_pid;
// 儲存真正的 find_ge_pid 的位址
hook.orig = &real_find_ge_pid;
// 開始安裝我們在這裡設定的 ftrace hook
hook_install(&hook);
}
```
---
```cpp
static int hook_install(struct ftrace_hook *hook)
{
// 再抓取一次我們想 hook 的 kernel function 的位址
// 若抓取失敗則回傳錯誤碼
int err = hook_resolve_addr(hook);
if (err)
return err;
// 註冊 ftrace 的 call back function
hook->ops.func = hook_ftrace_thunk;
hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_RECURSION_SAFE |
FTRACE_OPS_FL_IPMODIFY;
// hook->address : 被我們 hook 的 kernel function
// ftrace_set_filter_ip : 哪些 ip address 可以使用這個 ftrace 的 call back function。
// e.g. 這邊使用的是真正的 find_ge_pid 的位址,表示當我們呼叫 find_ge_pid 時,
// 會進入此 ftrace 設定的 call back function ( 也就是 hook_ftrace_thunk )
// 而呼叫其他的 function 則不會進入此 ftrace 的 call back function
err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);
if (err) {
printk("ftrace_set_filter_ip() failed: %d\n", err);
return err;
}
// 註冊這個 ftrace,並開始啟用
err = register_ftrace_function(&hook->ops);
if (err) {
printk("register_ftrace_function() failed: %d\n", err);
// 註冊失敗的話,要把這個 filter 關掉
ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
return err;
}
return 0;
}
```
---
設定好後,每當 linux kernel 裡除了我們的 kernel module 以外的任何地方呼叫 "find_ge_pid" ,就會執行我們所設定的 frace 的 call back function "hook_ftrace_thunk"。
```cpp
static void notrace hook_ftrace_thunk(unsigned long ip,
unsigned long parent_ip,
struct ftrace_ops *ops,
struct pt_regs *regs)
{
struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);
// 假如 caller 的 instruction pointer 並不在我們這個 kernel module 的範疇內,
// 就將其 instruction pointer 指向我們這個 kernel module 版本的 hook_find_ge_pid
if (!within_module(parent_ip, THIS_MODULE))
regs->ip = (unsigned long) hook->func;
}
```
---
從 ftrace 的 call back function "hook_ftrace_thunk" 會跳轉到我們的 kernel module 所實作的 hook_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);
// 只要想要搜尋的 pid_t 位於 hidden_list 內,就讓 "real_find_ge_pid" 去搜尋 pid_t + 1 ,藉此隱藏 pid_t
while (pid && is_hidden_proc(pid->numbers->nr))
pid = real_find_ge_pid(pid->numbers->nr + 1, ns);
return pid;
}
```
---
## 若你用的核心較新,請試著找出替代方案
使用的是 linux-5.4.134 ,剛好不會碰上這個問題。
```
$ uname -a
Linux tommy-X450VP 5.4.134 #1 SMP Sun Jul 25 17:18:47 CST 2021 x86_64 x86_64 x86_64 GNU/Linux
```
---
## 本核心模組只能隱藏單一 PID,請擴充為允許其 PPID 也跟著隱藏
1. 使用 find_vpid 從 pid_t 拿到 struct pid
2. 再用 pid_task 從 struct pid 拿到 struct task_struct
3. 使用 task_ppid_nr 從 struct task_struct 拿到 ppid
4. 把 pid 以及 ppid 都用 hide_process 來隱藏起來
```cpp=
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";
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)) {
kstrtol(message + sizeof(add_message), 10, &pid);
struct task_struct *task = pid_task(find_vpid(pid), PIDTYPE_PID);
struct pid_t *ppid = task_ppid_nr(task);
hide_process(pid);
hide_process(ppid);
} else if (!memcmp(message, del_message, sizeof(del_message) - 1)) {
kstrtol(message + sizeof(del_message), 10, &pid);
unhide_process(pid);
} else {
kfree(message);
return -EAGAIN;
}
```
---
## 允許給定一組 PID 列表,而非僅有單一 PID
使用簡易的 parser 將 PID 列表裡的所有 PID 用 hide_process 隱藏起來
```cpp=
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";
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)) {
hide_processes((char *) message + sizeof(add_message));
} else if (!memcmp(message, del_message, sizeof(del_message) - 1)) {
unhide_processes((char *) message + sizeof(del_message));
} else {
kfree(message);
return -EAGAIN;
}
*offset = len;
kfree(message);
return len;
}
.
.
.
void hide_processes(char *pids) {
char num[32];
memset(num, 0, sizeof(num));
int stat = 0;
int i = 0;
int numi = 0;
long pid = 0;
while(pids[i] != 0) {
if(stat == 0) {
if(pids[i] >= '0' && pids[i] <= '9') {
num[numi++] = pids[i];
stat = 1;
}
} else if(stat == 1) {
if(pids[i] >= '0' && pids[i] <= '9') {
num[numi++] = pids[i];
} else {
kstrtol(num, 10, &pid);
printk("%d\n", pid);
hide_process(pid);
memset(num, 0, sizeof(num));
numi = 0;
stat = 0;
}
}
i++;
}
if(num[0] != 0) {
kstrtol(num, 10, &pid);
printk("%d\n", pid);
hide_process(pid);
}
}
```
---
## 指出程式碼可改進的地方,並動手實作
### 釋放 ftrace 的 hook
在釋放 kernel module 前使用老師寫好的 hook_remove 將 hook 釋放。
```cpp=
static void _hideproc_exit(void)
{
printk(KERN_INFO "@ %s\n", __func__);
hook_remove(&hook);
}
```
---
### 處理重複加兩次相同 pid 的情形
避免同一個 pid 重複出現在 linked list 裡。
```cpp=
static int hide_process(pid_t pid)
{
if(!is_hidden_proc(pid)) {
pid_node_t *proc = kmalloc(sizeof(pid_node_t), GFP_KERNEL);
proc->id = pid;
// TODO
list_add(&(proc->list_node), &hidden_proc);
}
return SUCCESS;
}
```
---
### 使用 rbtree 實作 hideproc
插入節點由 O(1) 變慢成 O(logn)
刪除節點由 O(n) 加快成 O(logn)
查詢節點由 O(n) 加快成 O(logn)
https://github.com/fdgkhdkgh/linux2021-summer-q1/tree/main/main_rb
---