contributed by < dmefs
>
linux2021
環境: windows 10 使用 multipass ,透過 vscode ssh 連到 vm 寫程式
首先看 Linux 核心模組運作原理
根據文章的思路學習並筆記
利用 strace 追蹤執行 insmod fibdrv.ko 的過程有哪些系統呼叫被執行
執行指令 sudo strace insmod fibdrv.ko
有 103 行,摘錄最後幾行,可以發現 finit_module
在 99 行
$ gcc -E fibdrv.c -I $TREE/include/ -I $TREE/arch/x86/include/ -I $TREE/uapi -I $MODULE_PTAH | less
$TREE
= /usr/src/linux-headers-$(uname -r)
$MODULE_PTAH
=/lib/modules/$(uname -r)/build/arch/x86/include/generated/
使用 / 搜尋 可以找到
如同老師說的使用的是 "沒定義 MODULE 的"
將老師的實驗修改一下
產生錯誤訊息
所以 line 7 的意義應該是 “宣告一個 function __func,__func 是 func 的別名“,但 func 不存在,就產生了錯誤
使用 readelf 觀察 fibdrv.ko
$ readelf -a fibdrv.ko | less
搜尋 modinfo
發現以下資訊
使用 hex editor 開啟 fibdrv.ko 可以確認 offset 是 0x5c8
在 Linux 核心模組運作原理 下方有延伸閱讀
這有一系列文章,學習一下
解釋如何 build kernel module 這已經會了,看過即可
介紹如何使用 ftrace hook system call ,他的例子 hook mkdir。這也是作業一 hook 所使用的方法。
只有包含在 /proc/kallsyms
的 function,才能夠使用 hook 的方法
展示了 hook 了強大之處, hook kill system call,藉以取得 root 權限
看到這寫作業的先備知識應該夠了,開始看作業
首先從進入點_hideproc_init
切入,這個 function 主要的工作是註冊 device 及 class,最後是 hook
_hideproc_init 並沒有做好 error handle,需要改進
line 6:
註冊 device 有兩種方式 alloc_chrdev_region
跟 register_chrdev_region
,除非確定 major number 才使用 register_chrdev_region
,否則用 alloc_chrdev_region
讓系統幫你配置
line 7:
macroMAJOR
,定義在linux/kdev_t.h
,可以得到 major number
line 9:
class_create(THIS_MODULE, DEVICE_NAME);
定義在 linux/device.h
__class_create 建立一個 可以被 device_create 使用的 class 並回傳,不需要時記得呼叫 class_destroy 回收
line 11:
cdev_init
, cdev_add
,init 一個 cdev
line 13:
device_create
,在 sysfs 建立一個 device
line 16:
init_hook
下方介紹
目前沒做任何事,但是_hideproc_init
有呼叫class_create
,這邊要有對應的class_destory
才對,還有 unregister_chrdev
作業希望 hook find_ge_pid
達到我們想做的事情,於是透過kallsyms_lookup_name
可以取得有 export 的 function address ,包含 system call 及 kernel function
定義一個 struct 紀錄 hook 的資訊,並宣告一個 function pointer type,signatrue 跟我們想要 hook 的 function 一樣
下方是 init_hook 的程式碼,line 2 是多餘的,因為 hook_install 會再做一次
定義一個 macro 方便初始化 ftrace_hook
修改 hook variable 的初始化
可分為 4 部份:
hook_resolve_addr
利用kallsyms_lookup_name
尋找並紀錄 function addresshook_ftrace_thunk
,以及 ftrace 的設定,此次使用了三個設定ftrace_set_filter_ip
設定要追蹤的 functionregister_ftrace_function
enables tracedisable trace and clear filter
當 rip 是我們所要追蹤的 function address 時會被呼叫。
將 rip 替換成 hook function,使用 within_module 判斷有無 recursion 的情況
將目前 hide pid 傳回 user
根據 使用者的指令 hide /unhide process
將 hide pid 加入 list
清空 list,但應該是要只清掉指定 pid 的 node
問題在於:如何從 PID 找到 PPID ?
首先要暸解 PID namespace 的架構,便知道尋找的路徑如下
已知 child PID,利用 find_get_pid
找到 struct pid
再從 struct pid 找到 tast_strct ,使用get_pid_task
透過 tast_struct::real_parent 取得 parent task_struct
利用 task_pid_vnr(parent) 取得 parent PID
取得 parent 之後,記得呼叫 put_task
及 put_pid
釋放資源
get_parent_pid
即是上述的實作
hide_process 隱藏 PID 及 parent PID
do_hide_process 將 PID 加入 hide list 中
unhide_process 回復 PID 及 parent PID
do_unhide_process 將 PID 從 hide list 去除
測試:
撰寫一個簡單的測試程式,程式名稱為 forever_sleep.c
功能是 fork 之後就不停 sleep
測試流程
使用 goto 讓程式更簡潔
reference:https://github.com/torvalds/linux/blob/5bfc75d92efd494db37f5c4c173d3639d4772966/drivers/s390/char/tape_class.c
release init 時所註冊/建立的結構
刪除指定的 node,原本的實作會刪除 list 中所有的 node
測試: