linux2021
目的: 檢驗學員對 Linux 核心行程, List Management API, Linux 核心模組 的認知
(僅在 Linux v5.4 測試) 考慮 hideproc
這個 Linux 核心模組可隱藏特定的 process ID,用法如下: (假設 cron
的 PID 為 644)
$ sudo insmod hideproc.ko
$ pidof cron
$ echo "add 644" | sudo tee /dev/hideproc
$ pidof cron # 無法見到 cron
$ echo "del 644" | sudo tee /dev/hideproc
$ pidof cron # 再次見到 cron
$ sudo rmmod hideproc
你或許會好奇:為何不能採用
sudo echo "add 644" > /dev/hideproc
命令呢?請參見 How do I use sudo to redirect output to a location I don't have permission to write to?
以下是 main.c
的程式碼: (部分遮蔽)
#include <linux/cdev.h>
#include <linux/ftrace.h>
#include <linux/kallsyms.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("National Cheng Kung University, Taiwan");
enum RETURN_CODE { SUCCESS };
struct ftrace_hook {
const char *name;
void *func, *orig;
unsigned long address;
struct ftrace_ops ops;
};
static int hook_resolve_addr(struct ftrace_hook *hook)
{
hook->address = kallsyms_lookup_name(hook->name);
if (!hook->address) {
printk("unresolved symbol: %s\n", hook->name);
return -ENOENT;
}
*((unsigned long *) hook->orig) = hook->address;
return 0;
}
static void notrace hook_ftrace_thunk(unsigned long ip,
unsigned long parent_ip,
struct ftrace_ops *ops,
struct pt_regs *regs)
{
struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);
if (!within_module(parent_ip, THIS_MODULE))
regs->ip = (unsigned long) hook->func;
}
static int hook_install(struct ftrace_hook *hook)
{
int err = hook_resolve_addr(hook);
if (err)
return err;
hook->ops.func = hook_ftrace_thunk;
hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_RECURSION_SAFE |
FTRACE_OPS_FL_IPMODIFY;
err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);
if (err) {
printk("ftrace_set_filter_ip() failed: %d\n", err);
return err;
}
err = register_ftrace_function(&hook->ops);
if (err) {
printk("register_ftrace_function() failed: %d\n", err);
ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
return err;
}
return 0;
}
#if 0
void hook_remove(struct ftrace_hook *hook)
{
int err = unregister_ftrace_function(&hook->ops);
if (err)
printk("unregister_ftrace_function() failed: %d\n", err);
err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
if (err)
printk("ftrace_set_filter_ip() failed: %d\n", err);
}
#endif
typedef struct {
pid_t id;
struct list_head list_node;
} pid_node_t;
LIST_HEAD(hidden_proc);
typedef struct pid *(*find_ge_pid_func)(int nr, struct pid_namespace *ns);
static find_ge_pid_func real_find_ge_pid;
static struct ftrace_hook hook;
static bool is_hidden_proc(pid_t pid)
{
pid_node_t *proc, *tmp_proc;
AAA (proc, tmp_proc, &hidden_proc, list_node) {
if (proc->id == pid)
return true;
}
return false;
}
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;
}
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);
}
static int hide_process(pid_t pid)
{
pid_node_t *proc = kmalloc(sizeof(pid_node_t), GFP_KERNEL);
proc->id = pid;
CCC;
return SUCCESS;
}
static int unhide_process(pid_t pid)
{
pid_node_t *proc, *tmp_proc;
BBB (proc, tmp_proc, &hidden_proc, list_node) {
DDD;
kfree(proc);
}
return SUCCESS;
}
#define OUTPUT_BUFFER_FORMAT "pid: %d\n"
#define MAX_MESSAGE_SIZE (sizeof(OUTPUT_BUFFER_FORMAT) + 4)
static int device_open(struct inode *inode, struct file *file)
{
return SUCCESS;
}
static int device_close(struct inode *inode, struct file *file)
{
return SUCCESS;
}
static ssize_t device_read(struct file *filep,
char *buffer,
size_t len,
loff_t *offset)
{
pid_node_t *proc, *tmp_proc;
char message[MAX_MESSAGE_SIZE];
if (*offset)
return 0;
list_for_each_entry_safe (proc, tmp_proc, &hidden_proc, list_node) {
memset(message, 0, MAX_MESSAGE_SIZE);
sprintf(message, OUTPUT_BUFFER_FORMAT, proc->id);
copy_to_user(buffer + *offset, message, strlen(message));
*offset += strlen(message);
}
return *offset;
}
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;
}
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 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);
對應的 Makefile
:
MODULENAME := hideproc
obj-m += $(MODULENAME).o
$(MODULENAME)-y += main.o
KERNELDIR ?= /lib/modules/`uname -r`/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
請補上以上程式碼,使其運作符合預期。
作答區
AAA = ? (應使用 list_for_each_entry_safe
巨集)
BBB = ? (是 list_for_each_entry 的變形)
CCC = ?
DDD = ?
延伸問題:
2020 年的變更 Unexporting kallsyms_lookup_name()
Access to kallsyms on Linux 5.7+
自 Linux 核心原始程式碼編譯出 User-mode Linux 並運用工具來建構實驗所需的檔案系統
Jul 20, 2025系統的 throughput 是評斷排程器優劣的重要效能
Jul 19, 2025無論是作業系統核心、C 語言函式庫內部、程式開發框架,到應用程式,都不難見到 linked list 的身影,包含多種針對效能和安全議題所做的 linked list 變形,又還要考慮到應用程式的泛用性 (generic programming),是很好的進階題材。
Jul 17, 2025資料整理: jserv
Jul 8, 2025or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up