🏷️Convert relative path into absolute path in kernel module
- 5.14.0 커널에서 동작한다. 
 
- 파일명을 바꿀 때 호출되는 
sys_renameat2 시스템 콜을 hooking해서 absolute path를 출력하도록 해보자. 
mv 명령어를 쓸 때 내부적으로 sys_renameat2를 호출한다. 
sys_renameat2의 prototype은 다음과 같다. 
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/socket.h>
#include <linux/moduleparam.h>
#include <linux/kprobes.h>
#include <linux/kallsyms.h>
#include <linux/namei.h>
#include <asm/ptrace.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("mysprtlty");
MODULE_DESCRIPTION("convert relative path into absolute path");
MODULE_VERSION("0.01");
typedef int (*sys_call_wrapper)(struct pt_regs *);
unsigned long sys_call_table;
int get_absolute_path(const char __user *filename) {
	struct path path;
	int dfd = AT_FDCWD;
	char *ret_ptr = NULL;
	int error = -EINVAL;
	int flag = 0;
	unsigned int lookup_flags = 0;
	char *tpath = kmalloc(1024, GFP_KERNEL);
	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT)) != 0) {
		goto out;
	}
	if (!(flag & AT_SYMLINK_NOFOLLOW)) {
		lookup_flags |= LOOKUP_FOLLOW;
	}
	error = user_path_at(dfd, filename, lookup_flags, &path);
	if (error){
		goto out;
	}
	ret_ptr = d_path(&path, tpath, 1024);
	printk(KERN_INFO "[+] absolute path: %s\n", ret_ptr);
	kfree(tpath);
	return 0;
out:
	kfree(tpath);
	return error;
}
static unsigned long int **acquire_sys_call_table(void)
{
	unsigned long int (*kallsyms_lookup_name)(const char *name);
	struct kprobe kp = {
		.symbol_name = "kallsyms_lookup_name",
	};
	if (register_kprobe(&kp) < 0){
		return NULL;
	}
	kallsyms_lookup_name = (unsigned long (*)(const char *name))kp.addr;
	unregister_kprobe(&kp);
	return (unsigned long int **)kallsyms_lookup_name("sys_call_table");
}
static int enable_page_rw(void *ptr)
{
	unsigned int level;
	pte_t *pte = lookup_address((unsigned long) ptr, &level);
	if(pte->pte &~_PAGE_RW){
		pte->pte |=_PAGE_RW;
	}
	return 0;
}
static int disable_page_rw(void *ptr)
{
	unsigned int level;
	pte_t *pte = lookup_address((unsigned long) ptr, &level);
	pte->pte = pte->pte &~_PAGE_RW;
	return 0;
}
sys_call_wrapper orig_sys_renameat2;
int my_sys_renameat2(struct pt_regs *regs)
{
	static unsigned int count = 0;
	char *full_path = (void *) 0;
	if (count < 5) {
		int tmp;		
		tmp = get_absolute_path(regs->si);
		tmp = get_absolute_path(regs->r10);
		count++;
	}
	return (*orig_sys_renameat2)(regs);
}
static int __init path_convert_init(void) 
{
	printk(KERN_INFO "[+] path_convert module inserted successfully!\n");
	sys_call_table = acquire_sys_call_table();
	printk(KERN_INFO "[+] sys_call_table = @%lx\n", sys_call_table);
	enable_page_rw((void *)sys_call_table);
	orig_sys_renameat2 = ((sys_call_wrapper *)sys_call_table)[__NR_renameat2];
	if (!orig_sys_renameat2) {
		return -1;
	}
	((sys_call_wrapper *)sys_call_table)[__NR_renameat2] = my_sys_renameat2;
	disable_page_rw((void *)sys_call_table);
	long int nr =  pid_vnr(task_session(current));
	printk(KERN_INFO "[+] session id = %d\n", nr);
	return 0;
}
static void __exit path_convert_exit(void) 
{
	printk(KERN_INFO "[+] path_convert module has been unloaded\n");
	
	enable_page_rw((void *)sys_call_table);
	((sys_call_wrapper *)sys_call_table)[__NR_renameat2] = orig_sys_renameat2;
	disable_page_rw((void *)sys_call_table);
}
module_init(path_convert_init);
module_exit(path_convert_exit);