🏷️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);