# System call hooking ###### tags: `learning` ## Things to do :::info 1. 找到system call table的位置 2. 把system call table所在的segment標為可寫 - 原本是read only 3. 找到要hook的system call在syscall table中的offset 4. 把該offset中的ptr改成自己寫的function ptr 5. 把syscall table改回read only ::: ## Kernel Module Programming [ref](https://ithelp.ithome.com.tw/articles/10157922) ### Hello world - `hook.c` ```c= #include <linux/kernel.h> /* for KERN_INFO */ #include <linux/module.h> /* for all modules*/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Minyeon"); MODULE_DESCRIPTION("test"); int init_module(void) { pr_info("Hello\n"); return 0; } void exit_module(void) { pr_info("Goodbye\n"); } ``` - `Makefile` ```c= obj-m += hook.o //obj-m : 將module編成.ko檔,以便之後可以進行載入 all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean ``` - `modinfo hook.ko`可查看module資訊 ![](https://i.imgur.com/70hGiar.png =500x) `insmod hook.ko`把module載入kernel ![](https://i.imgur.com/7FqgIi3.png =500x) `lsmod`可以查看已載入的module ![](https://i.imgur.com/w2PmO0V.png =500x) `rmmod hook.ko`把module從kernel中移除 ![](https://i.imgur.com/SlJxxBF.png =500x) ### 從command line傳參數 - 借助`module_param`這個macro傳參數(定義在`linux/moduleparam.h` 中) ```c= module_param(param_name, param_type, permission) //param_name: 變數名稱 //param_type: 變數的資料型態(int, bool, charp...) //permission: 變數對sysfs的存取權限 ``` 在Command line利用`param_name = our_name`傳入參數 ![](https://i.imgur.com/lsZ0Wp1.png) ### 呼叫`call_usermodehelper` - 執行一個新process(ps),並把結果導至一個檔案(ps_out)中 ```c= #include <linux/kernel.h> /* for KERN_INFO */ #include <linux/module.h> /* for all modules*/ #include <linux/sched.h> /* for task struct*/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Minyeon"); MODULE_DESCRIPTION("test"); int init_hook(void) { int ret = -1; char path[] = "/bin/bash"; char *argv[] = {path, "-c", "/bin/ps -ef >> /home/minyeon/my_sys_hook/ps_out", NULL}; char *envp[] = {"HOME=/", "PATH=/sbin:/bin:/usr/bin", NULL}; pr_info("[*] Start call_usermodehelper...\n"); ret = call_usermodehelper(path, argv, envp, UMH_WAIT_PROC); pr_info("ret=%i\n", ret); pr_info("Process: %s, Pid: %d\n", current->comm, current->pid); //pr_info("Hello %s\n", name); return 0; } void exit_hook(void) { pr_info("Goodbye\n"); } module_init(init_hook); module_exit(exit_hook); ``` ### 執行自己的程式 ```c= int init_hook(void) { int ret = -1; char path[] = "/bin/bash"; char *argv[] = {path, "-c", "/home/minyeon/my_sys_hook/test >> /home/minyeon/my_sys_hook/test_out", NULL}; char *envp[] = {"HOME=/", "PATH=/sbin:/bin:/usr/bin", NULL}; pr_info("[*] Start call_usermodehelper...\n"); ret = call_usermodehelper(path, argv, envp, UMH_WAIT_PROC); pr_info("ret=%i\n", ret); pr_info("Process: %s, Pid: %d\n", current->comm, current->pid); //pr_info("Hello %s\n", name); return 0; } ``` ### Get sys_call_table ```c #include <linux/kernel.h> /* for KERN_INFO */ #include <linux/module.h> /* for all modules*/ #include <linux/sched.h> /* for task struct*/ #include <linux/kprobes.h> /* for kprobe*/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Minyeon"); MODULE_DESCRIPTION("test"); static struct kprobe kp = { .symbol_name = "kallsyms_lookup_name" }; int init_hook(void) { /* find syscall_table*/ register_kprobe(&kp); pr_alert("Syscall table found at 0x%px \n", kp.addr); return 0; } void exit_hook(void) { unregister_kprobe(&kp); pr_info("Goodbye\n"); } module_init(init_hook); module_exit(exit_hook); ``` ## 問題 - Linux kernel 5.7.0之後,`kallsyms_lookup_name`不再export - 編譯會跳出`ERROR: modpost: "kallsyms_lookup_name" undefined!` ![](https://i.imgur.com/qih9MjE.png) - `/proc/kallsyms`每次開機就會random一次,所以無法直接hard coded - [solve by](https://github.com/zizzu0/LinuxKernelModules/blob/main/FindKallsymsLookupName.c)