Convert relative path into absolute path in kernel module

MySprtlty·2024년 5월 23일
0

Kernel

목록 보기
9/10

🏷️Convert relative path into absolute path in kernel module

  • 5.14.0 커널에서 동작한다.
  • 파일명을 바꿀 때 호출되는 sys_renameat2 시스템 콜을 hooking해서 absolute path를 출력하도록 해보자.
  • mv 명령어를 쓸 때 내부적으로 sys_renameat2를 호출한다.
  • sys_renameat2의 prototype은 다음과 같다.
/*
 * convert relative path to absolute path 
 * %rdi, rsi, %rdx, %r10, %r8, %r9
 */

#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;
}

/* get the address of sys_call_table */
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;
}

/* original sys_renameat2 & my_sys_renameat2 */
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);

/* sys_renameat2 */
	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");

	/* recover the sys_call_table */
	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);
  • dmesg -wT &로 확인해보자.
profile
2Co 4:7

0개의 댓글