# 中央資工 Linux 作業系統 Project 1(2021) ###### tags: `Linux` > 內容 : 寫一個system call從kernel space找出指定 process(task)的code(text)段的位址 [task_struct](https://elixir.bootlin.com/linux/v4.14/source/include/linux/sched.h#L519) ## **Add a system call & compile Linux Kernel** ### (1) Get the Linux kernel source code 到 [The Linux Kernel Archives](https://www.kernel.org/)查詢版本,這裡下載的版本為5.14.16 ``` $ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.14.16.tar.xz ``` ### (2) decompress file tar指令參數意義 : [tar指令使用](https://blog.gtwang.org/linux/tar-command-examples-in-linux-1/) 解壓縮後**linux-5.14.16**會在你的Home directory ``` $ tar -xvf linux-5.14.16.tar.xz -C ~/ ``` ### (3) Install the Compile Tools 安裝編譯kernel所需的工具 (以下參考不同來源) ``` $ sudo apt install build-essential libncurses-dev libssl-dev libelf-dev bison flex -y $ sudo apt-get install git ccache libssl-dev fakeroot bison gcc flex ``` ### (4) Clean up your installed packages. ``` $ sudo apt clean && sudo apt autoremove -y ``` ### (5) Add a new system call number to kernel's system call table #### Add it to the file **syscall_64.tbl** (你是64bit系統就修改這個,32bit修改下面那個) ``` $ vim ~/linux-5.14.16/arch/x86/entry/syscalls/syscall_64.tbl ``` 新增如下,並記住System Number = 448。 ![](https://i.imgur.com/yjui0Jr.jpg) #### Add it to the file **syscall_32.tbl** ``` $ vim ~/linux-5.14.16/arch/x86/entry/syscalls/syscall_32.tbl ``` ![](https://i.imgur.com/FJyJ9Ac.jpg) ### (6) Define my system call ``` $ cd linux-5.14.16 $ mkdir pass_kernel_data $ cd pass_kernel_data $ vim pass_kernel_data.c ``` 在 **pass_kernel_data.c** Define: - system call 程式 從 [`init_task`](https://elixir.bootlin.com/linux/v4.14/source/init/init_task.c#L20) 開始遍歷 task_list 至指定 task,傳出指定資料至指定記憶體段。 ```c= #include <linux/kernel.h> #include <linux/string.h> #include <linux/uaccess.h> #include <linux/init_task.h> #include <linux/syscalls.h> // you need to pass this data struct to userspace and print on terminal struct data_segment { unsigned long start_code; unsigned long end_code; }; SYSCALL_DEFINE2(pass_kernel_data, pid_t, user_pid, void* __user, user_address) { struct data_segment my_data_segment; struct task_struct *task; for_each_process(task) { if(task->pid == user_pid) { my_data_segment.start_code = task->mm->start_code; my_data_segment.end_code = task->mm->end_code; copy_to_user(user_address, &my_data_segment, sizeof(struct data_segment)); } } return 0; } ``` 建立 pass_kernel_data 資料夾的 makefile `$ vim Makefile` ```c= obj-y := pass_kernel_data.o ``` 返回上一層修改makefile ``` $ cd .. // to linux-5.14.16/ $ vim Makefile ``` 找到 **core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/** 在最後面新增 **pass_kernel_data/** ### (7) Add corresponding function prototype for your system call to the header file of **syscalls.h** ``` $ vim ~/linux-5.14.16/include/linux/syscalls.h ``` 在最下面(#endif前)新增一行 : `asmlinkage int pass_kernel_data(pid_t user_pid, void* __user user_address);` ### (8) Clean all the previous configurations of the kernel 可先使用下列指令查看當前的kernel version ``` $ uname -a ``` 清理上次編譯時產生的目標檔、暫存檔、模組檔 ..,以及核心組態檔和備份檔 ``` $ cd linux-5.14.16 $ sudo make mrproper ``` ### (9) Copy our current kernel configuration to the new kernel 將使用中的核心組態檔複製過來 ``` $ cp /boot/config-`uname -r` .config $ make oldconfig ``` ### (10) Compile Kernel 將使用中的核心組態檔複製過來 ``` $ sudo make menuconfig // 用預設的就好 $ sudo make -j$(nproc) // 包含make bzImage、make modules...等動作 $ sudo make modules_install -j$(nproc) $ sudo make install -j$(nproc) // 安裝kernel $ sudo update-grub $ sudo reboot ``` :warning: 若遇到報錯`No rule to make target 'debian/certs/debian-uefi-certs.pem` 將 **linux-5.14.16/.config** 中下列兩項設為空值 *CONFIG_SYSTEM_TRUSTEDKEYS=""* *CONFIG_SYSTEM_REVOCATIONKEYS=""* :warning: 重新編譯前請先make clean ### (11) Creat a .c file (user process) to call mysyscall ``` $ vim test.c $ gcc test.c -o test $ ./test $ dmesg | tail // see the kernel log info ``` user space程式 ```c= #include <syscall.h> #include <sys/types.h> #include <stdio.h> #include <unistd.h> #include <time.h> struct data_segment { unsigned long start_code; unsigned long end_code; }; int main() { struct data_segment my_data_segment; // no.448的System call int a = syscall(448, getpid(), (void*)&my_data_segment); printf("system call return %d\n",a); printf("Start: %lx\nEnd: %lx\n", my_data_segment.start_code, my_data_segment.end_code); int b; scanf("%d",&b); // 卡住程式 return 0; } ``` --- ## **<font color="#f00">tmux</font>** 參考 [https://hackmd.io/@ADLlearning/BJp1I6RLK](https://hackmd.io/@ADLlearning/BJp1I6RLK) [tmux使用](https://blog.gtwang.org/linux/linux-tmux-terminal-multiplexer-tutorial/) [ps指令](http://www.happystreet.com.tw/index.php/system-dynamic-teaching/unix-linux/410-a-day-of-school-a-unix-command-ps) ### <font color="blue">安裝tmux</font> ``` $ sudo apt install tmux ``` 下圖為pane的操作常用鍵: ![](https://i.imgur.com/orLCU0A.jpg) 下圖為window的操作常用鍵: ![](https://i.imgur.com/0v9tbqn.jpg) ### <font color="blue">測試使用tmux</font> 先ctrl+b %開啟兩個pane 一邊使用 test.c (先修改成可讓程式卡著執行) 一邊使用 `$ ps -a` 指令可觀看目前同個終端下的執行程式 ![](https://i.imgur.com/LkAL0NU.jpg) 可以看到正在執行的process pid ``` $ cd /proc/5588 //每次執行分配的pid不同,如下圖pid=5603 $ cat maps ``` 這裡可以看到process的位址分布 ![](https://i.imgur.com/w80iBx4.jpg) 第一列就是目標text段的開頭位置 ## Q&A ### 1. what is “asmlinkage” 當 system call handler 要呼叫相對應的 system call routine 時,便將一般用途暫存器的值 push 到 stack 裡,因此 system call routine 就要由 stack 來讀取 system call handler 傳遞的參數。這就是 asmlinkage 標籤的用意。 加上 “asmlinkage” 後,C function 就會由 stack 取參數,而不是從 register 取參數(可能發生在程式碼最佳化後)。 [https://www.twblogs.net/a/5e4fc053bd9eee101df86de7](https://www.twblogs.net/a/5e4fc053bd9eee101df86de7) ### 2. memcpy與copy_to_user差別 [https://blog.csdn.net/lhl_blog/article/details/107030517](https://blog.csdn.net/lhl_blog/article/details/107030517) [https://stackoverflow.com/questions/14970698/copy-to-user-vs-memcpy](https://stackoverflow.com/questions/14970698/copy-to-user-vs-memcpy) ### 3. vm_area_struct與mm_struct差別 [https://blog.csdn.net/weixin_41028621/article/details/104455327](https://blog.csdn.net/weixin_41028621/article/details/104455327) ### 4. sys_前綴問題(3.X版kernel不需要,為何4.X 5.X版後需要加) syscalls.h定義 [https://elixir.bootlin.com/linux/v5.14.16/source/include/linux/syscalls.h#L211](https://elixir.bootlin.com/linux/v5.14.16/source/include/linux/syscalls.h#L211) [http://gityuan.com/2016/05/21/syscall/](http://gityuan.com/2016/05/21/syscall/) ### 5. code_start跟code_end範圍這麼大為啥沒用這麼多 分配空間大小時,OS不知道程式會用多少空間,分配以PAGE為單位,所以通常會超出範圍,其實沒用那麼多。 ### 6. for_each_process要遍歷所有process不優,有沒有別的替代方式? 可以使用get_current(asm/current.h)來得知目前正在執行的程序(可直接include`asm/current.h`使用"current" keyword。 這裡的current process指的是呼叫這個system call的process [https://stackoverflow.com/questions/12434651/what-is-the-current-in-linux-kernel-source](https://stackoverflow.com/questions/12434651/what-is-the-current-in-linux-kernel-source) ![](https://i.imgur.com/SXcFzty.jpg)