Try   HackMD

2021q3 Homework1 (quiz1)

contributed by < WayneLin1992 >

tags: linux2021

延伸問題

  • 解釋程式碼運作原理,包含 ftrace 的使用
  • 本程式僅在 Linux v5.4 測試,若你用的核心較新,請試著找出替代方案
  • 本核心模組只能隱藏單一 PID,請擴充為允許其 PPID 也跟著隱藏,或允許給定一組 PID 列表,而非僅有單一 PID
  • 指出程式碼可改進的地方,並動手實作

開發環境

$ uname -r
5.8.0-63-generic

make 後的結果

make -C /lib/modules/`uname -r`/build M=/home/wayne/linux/2021q3-quiz1 modules
make[1]: 進入目錄「/usr/src/linux-headers-5.8.0-63-generic」
  CC [M]  /home/wayne/linux/2021q3-quiz1/main.o
  LD [M]  /home/wayne/linux/2021q3-quiz1/hideproc.o
  MODPOST /home/wayne/linux/2021q3-quiz1/Module.symvers
ERROR: modpost: "kallsyms_lookup_name" [/home/wayne/linux/2021q3-quiz1/hideproc.ko] undefined!
make[2]: *** [scripts/Makefile.modpost:124:/home/wayne/linux/2021q3-quiz1/Module.symvers] 錯誤 1
make[2]: *** 正在刪除檔案「/home/wayne/linux/2021q3-quiz1/Module.symvers」
make[1]: *** [Makefile:1698:modules] 錯誤 2
make[1]: 離開目錄「/usr/src/linux-headers-5.8.0-63-generic」
make: *** [Makefile:9:all] 錯誤 2

Unexporting kallsyms_lookup_name() 可以知道在v5.7 之後被遺除了,因為會有後門問題。

The backdoor in question is kallsyms_lookup_name(), which will return the address associated with any symbol in the kernel's symbol table.

可以透過 Access to kallsyms on Linux 5.7+ 加入 module 後可以運行。

其他方案

因為 kallsyms 是相當常用 function ,他會 return address 可以從 github 搜尋 kallsyms_lookup_name() 可以找到大家的解決方式,看了一些,大部分向下由 kprobes 來取代。

#define KPROBE_LOOKUP 1
#include <linux/kprobes.h>
static struct kprobe kp = {
    .symbol_name = "kallsyms_lookup_name"
};
typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
kallsyms_lookup_name_t kallsyms_lookup_name;
register_kprobe(&kp);
kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr;
unregister_kprobe(&kp);

程式碼運行說明

可以先從看起 module_init(_hideproc_init); _hideproc_init 主要為掛載 device 並註冊
printk(KERN_INFO "@ %s\n", __func__); dmesg 會顯示出信息 __func__ 會讓 printk 出所在的 function。
alloc_chrdev_region 取得動態配置 device 範圍空間
MAJOR 將會得到 device 的 major ID
class_create 將會創立 class , return pointer to struct class
cdev_init 初始化 char device , fops 為 file operation
cdev_add 新增 char device to system
device_create 其中 ID 由 major + minor 組成 MKDEV 得到

alloc_chrdev_region - register a range of char device numbers

Allocates a range of char device numbers. The major number will be chosen dynamically, and returned (along with the first minor number) in dev. Returns zero or a negative error code.

MAJOR - manage a device number

A device ID consists of two parts: a major ID, identifying the class of the device, and a minor ID, identifying a specific instance of a device in that class.

class_create - create a struct class structure

This is used to create a struct class pointer that can then be used in calls to device_create.
Returns struct class pointer on success, or ERR_PTR on error.

cdev_add - add a char device to the system

adds the device represented by p to the system, making it live immediately. A negative error code is returned on failure.

device_create - creates a device and registers it with sysfs

This function can be used by char device classes. A struct device will be created in sysfs, registered to the specified class.

init_hook 主要 struct ftrace_hook 的初始化
kallsyms_lookup_name lookup the address for a symbol。

ftrace_hook

struct ftrace_hook {
    const char *name;
    void *func, *orig;
    unsigned long address;
    struct ftrace_ops ops;
};

hook_install 主要設定 hook->ops 並 register ftrace
hook_resolve_addr 透過 kallsyms_lookup_name 得到 find_ge_pid 的 address

struct ftrace_ops

struct ftrace_ops {
	ftrace_func_t			func;
	struct ftrace_ops		*next;
	unsigned long			flags;
	void				*private;
	ftrace_func_t			saved_func;
	int __percpu			*disabled;
};

ftrace - function trace

可以透過 events or break point(function) triggle

參考資料:
ftrace
ftrace Wikipedia
Tracing with Ftrace: Critical Tooling for Linux Development by Steven Rostedt

ftrace_set_filter_ip 可以 filter 掉其他相同名子的相關 function 追蹤固定 address , 這裡為 find_ge_pid
register_ftrace_function 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.

ftrace_set_filter_ip - set a function to filter on in ftrace by address

Sometimes more than one function has the same name. To trace just a specific function in this case.

register_ftrace_function - register a function for profiling

The registered callback will start being called some time after the register_ftrace_function() is called and before it returns. The exact time that callbacks start being called is dependent upon architecture and scheduling of services. The callback itself will have to handle any synchronization if it must begin at an exact moment.

static void _hideproc_exit(void)
{
    printk(KERN_INFO "@ %s\n", __func__);
    /* FIXME: ensure the release of all allocated resources */
    cdev_del(&cdev);
    unregister_chrdev_region(&dev);
    class_destroy(hideproc_class);
    
}