# 2021q3 Homework1 (quiz1)
contributed by < `mfinmuch` >
:::danger
注意書寫規範: 中英文間用一個空白字元區隔
:notes: jserv
-->以修改
:::
## 解釋程式碼運作原理,包含 ftrace 的使用
```shell
ls -la /dev/hideproc
crw------- 1 root root 241, 1 Jul 24 21:28 /dev/hideproc
```
c->character driver
r->read
w->write
x->excute
`echo "add 344" | sudo tee /dev/hideproc `
藉由 echo 輸出 "add 344"
`echo "add 344" | sudo tee /dev/hideproc `
`|`->pipe 把前面的輸出 轉到後面
tee
> TEE(1)
> NAME
> tee - read from standard input and write to standard output and files
所以
```shell
$ echo "add 340" | sudo tee /dev/hideproc
add 340
```
第1行的 `echo "add 344"` 相較於 tee 的標準輸入
第2行的 show的add340 是 tee 的 standard output
`sudo echo "add 644" > /dev/hideproc`
因為 echo print 出來的東西是在 bash 上執行的
他的 output 並沒有 root 的身份
```c=216
static int _hideproc_init(void)
{
int err, dev_major;
dev_t dev;
printk(KERN_INFO "@ %s\n", __func__);
err = alloc_chrdev_region(&dev, 0, MINOR_VERSION, DEVICE_NAME);
dev_major = MAJOR(dev);
hideproc_class = class_create(THIS_MODULE, DEVICE_NAME);
cdev_init(&cdev, &fops);
cdev_add(&cdev, MKDEV(dev_major, MINOR_VERSION), 1);
device_create(hideproc_class, NULL, MKDEV(dev_major, MINOR_VERSION), NULL,
DEVICE_NAME);
init_hook();
return 0;
}
static void _hideproc_exit(void)
{
printk(KERN_INFO "@ %s\n", __func__);
/* FIXME: ensure the release of all allocated resources */
}
module_init(_hideproc_init);
module_exit(_hideproc_exit);
```
一開始會從 `modulle_init` 到 `_hideproc_init(void)` 去做執行
第221行註冊空間 [alloc_chrdev_region — register a range of char device numbers](https://www.kernel.org/doc/htmldocs/kernel-api/API-alloc-chrdev-region.html)
第222行給 major number (或minor number)
第226行 cdev_init — initialize a cdev structure
`file_operations `[程式呼叫到 kernel moduel 的動作時,實際上都是引用透過這個資料結構所定義的內容。](https://ithelp.ithome.com.tw/articles/10160301)
- [struct fileoperations, struct file and struct inode.]
`MKDEV` 將主、次設備號轉換成 `dev_t` 類型
```c=224
hideproc_class = class_create(THIS_MODULE, DEVICE_NAME);
cdev_init(&cdev, &fops);
cdev_add(&cdev, MKDEV(dev_major, MINOR_VERSION), 1);
device_create(hideproc_class, NULL, MKDEV(dev_major, MINOR_VERSION), NULL,
DEVICE_NAME);
```
[建立device](https://www.itread01.com/p/1374721.html)
> 函式 device_create() 用於動態的建立邏輯裝置,並對新的邏輯裝置類進行相應初始化,將其與函式的第一個引數所代表的邏輯類關聯起來,然後將此邏輯裝置加到 linux 核心系統的裝置驅動程式模型中。函式能夠自動在 /sys/devices/virtual 目錄下建立新的邏輯裝置目錄,在 /dev 目錄下創建於邏輯類對應的裝置檔案
> class_create(…) 函式,可以用它來建立一個類,這個類存放於 sysfs 下面,一旦建立好了這個類,再呼叫 device_create(…) 函式來在 /dev 目錄下建立相應的裝置節點。這樣,載入模組的時候,使用者空間中的udev會自動響應 device_create(…) 函式,去 /sysfs 下尋找對應的類從而建立裝置節點。
[`cdev_add`](https://www.kernel.org/doc/htmldocs/kernel-api/API-cdev-add.html)->[將之前獲得設備號和設備號長度填充到cdev結構中](https://kknews.cc/code/89zma64.html)
```c=
int cdev_add ( struct cdev * p,
dev_t dev,
unsigned count //the number of consecutive minor numbers corresponding to this device
);
```
:::info
不知道這個unsigned count是否跟一次執行幾個module有關係??
> 何不做實驗確認?
> :notes: jserv
:::
```c=202
static struct cdev cdev;
static struct class *hideproc_class = NULL;
static const struct file_operations fops = {
.owner = THIS_MODULE,
.open = device_open,
.release = device_close,
.read = device_read,
.write = device_write,
};
#define MINOR_VERSION 1
#define DEVICE_NAME "hideproc"
```
`static const struct file_operations fops` 設定該module有哪些功能
```c=171
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);
hide_process(pid);
} 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;
}
*offset = len;
kfree(message);
return len;
}
```
buffer 是 echo 輸入進來的值 之後會 copy 進 `message` 裏頭 (185)
比較要做哪些事情
[kstrtol — convert a string to a long](https://www.kernel.org/doc/htmldocs/kernel-api/API-kstrtol.html)
`kstrtoint() - string to int。
kstrtol() - string to long。`
`char *message` 通常只能讀,如果更改有可能會當基
> 改進漢語表達:「有可能會當基」到底指什麼?
`kstrtol(message + sizeof(add_message), 10, &pid);`
`message + sizeof(add_message)` ->取後面的數值,本code為pid
`10` ->以10進位來看
`&pid` 輸出的結果會放於此
```c=118
static int hide_process(pid_t pid)
{
pid_node_t *proc = kmalloc(sizeof(pid_node_t), GFP_KERNEL);
proc->id = pid;
//CCC
list_add_tail(&proc->list_node, &hidden_proc);
return SUCCESS;
}
static int unhide_process(pid_t pid)
{
pid_node_t *proc, *tmp_proc;
//BBB
list_for_each_entry_safe (proc, tmp_proc, &hidden_proc, list_node) {
//DDD
list_del(&proc->list_node);
kfree(proc);
}
return SUCCESS;
}
```
`hide_process` 把要隱藏的pid加入進 `hidden_proc` 的list中
`unhide_process` 把咬顯示的移除隱藏的list
`list_add_tail` 是將 `list_add` 重新包裝,讓新的node加入list的尾端
`list_del` 刪除指定的node
[參考Linux鏈結串列struct list_head 研究](https://myao0730.blogspot.com/2016/12/linux.html)
[`list_for_each_entry_safe` ](https://www.twblogs.net/a/5d70c78cbd9eee541c33fda4)用list_for_each_entry_safe 可以安全的刪除,而不會影響接下來的節點逐一走訪(用n 指標可以繼續完成接下來的遍歷,
而 list_for_each_entry 則無法繼續遍歷,刪除後會導致無法繼續遍歷)
:::info
list_for_each_entry_safe (proc, tmp_proc, &hidden_proc, list_node)
->list_node哪邊來的?
->這樣刪除跟 tmp_proc 是有跟 proc 做比較嘛?不然怎摸直接刪除的
:::
[LIST_HEAD](https://www.cnblogs.com/hwy89289709/p/6754300.html)
`LIST_HEAD(hidden_proc);` 宣告隱藏list
接下來是 `init_hook()` 宣告隱藏pid功能的func和ftrace
在 `_hideproc_init` 中把module宣告好,把功能 `init_hook()` 插上來
有點像我組好一台電腦(class_create, device_create),要使用GPU就把GPU插上來( `init_hook()` )
```c=110
static void init_hook(void)
{
real_find_ge_pid = (find_ge_pid_func) kallsyms_lookup_name("find_ge_pid");
hook.name = "find_ge_pid";
hook.func = hook_find_ge_pid;
hook.orig = &real_find_ge_pid;
hook_install(&hook);
}
```
宣告hook的功能
其中 `static struct ftrace_hook hook;`
```c=
struct ftrace_hook {
const char *name;
void *func, *orig;
unsigned long address;
struct ftrace_ops ops;
};
```
[`ftrace_ops`](https://www.kernel.org/doc/html/latest/trace/ftrace-uses.html)
```c=
struct ftrace_ops ops = {
.func = my_callback_func,
.flags = MY_FTRACE_FLAGS
.private = any_private_data_structure,
};
```
>To register a function callback, a ftrace_ops is required. This structure is used to tell ftrace what function should be called as the callback as well as what protections the callback will perform and not require ftrace to handle.
註冊哪個func需要用到ftrace的功能
`hook.func = hook_find_ge_pid;` ->
```c=102
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;
}
```
[`struct pid`](https://www.twblogs.net/a/5f03a265d496dddbb542779e)
pid
```c=
struct pid
{
atomic_t count; // 該pid被不同task_struct引用的次數(不同的命名空間,可以存在相同的pid)
unsigned int level; // 這個pid結構體的深度, root level = 0, root的子空間爲1
/* lists of tasks that use this pid */
struct hlist_head tasks[PIDTYPE_MAX]; // 指向與該pid相連的task
struct rcu_head rcu;
struct upid numbers[1]; /* 變長數組, 存儲upid結構, 數組大小爲 level+1
* numbers[0], numbers[1],..., numbers[level]
* 由於同一個pid從下往上會被映射到多個 namespace 中
* 這裏會保存每一個 namespace 下的 upid 信息
*/
};
```
upid
```c=
/*
* struct upid is used to get the id of the struct pid, as it is
* seen in particular namespace. Later the struct pid is found with
* find_pid_ns() using the int nr and struct pid_namespace *ns.
*/
struct upid {
/* Try to keep pid_chain in the same cacheline as nr for find_vpid */
int nr; //PID具體的值
struct pid_namespace *ns; //指向命名空間的指針
struct hlist_node pid_chain; /* pid哈希列表(pid_hash)中的節點,用於快速通過nr和ns查找到upid
* 在alloc_pid 時將該節點添加到哈希列表中
*/
};
```
透過 `is_hidden_proc` 來查找 `hidden_proc` 中是否有要隱藏的pid
```c=91
static bool is_hidden_proc(pid_t pid)
{
pid_node_t *proc, *tmp_proc;
//AAA
list_for_each_entry_safe (proc, tmp_proc, &hidden_proc, list_node) {
if (proc->id == pid)
return true;
}
return false;
}
```
:::info
如果有回傳false就是要刪除的??
real_find_ge_pid->???
`hook_find_ge_pid(int nr, struct pid_namespace *ns)`
儘管了解了此func是在做隱藏,但他的變數是從那傳遞進來的?
:::
`hook_install(&hook);`
##
[ftrace 這也有一個命令列工具 -- trace-cmd。要安裝 trace-cmd,就跟安裝其他套件是一模一樣的:](https://ithelp.ithome.com.tw/articles/10242035)
`sudo apt install trace-cmd`