🏷️System Call Hooking
📌고려 사항
- 현재 x86_64에서 (kernel version = 5.14.0) System Call Hooking시 고려해야 할 사항이 몇가지 있다.
1. system call wrapper
2. disable cr0 wp bit
- control register의 write protection bit를 먼저 꺼야될 수도 있다.
3. KASLR
- booting시 sys_call_table의 주소를 무작위로 바꾼다.
.config
에서 끌 수 있다.
4. SMEP, SMAP
📌sys_read hooking example (kernel 5.14.0)
- 아래 예시는 kernel 5.14.0 x86_64 아키텍처에서 수행했다.
#define CR0_WP 0
#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 <asm/ptrace.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("mysprtlty");
MODULE_DESCRIPTION("System Call Hooking Test");
MODULE_VERSION("0.01");
typedef int (*sys_call_wrapper)(struct pt_regs *);
unsigned long sys_call_table;
#if CR0_WP
static unsigned long int cr0;
extern unsigned long int __force_order;
static inline void write_forced_cr0(unsigned long value)
{
asm volatile("mov %0,%%cr0":"+r"(value),"+m"(__force_order));
}
static inline void zero_cr0_wp(void)
{
write_forced_cr0(read_cr0() & (~0x10000));
}
static inline void one_cr0_wp(void)
{
write_forced_cr0(read_cr0() | 0x10000);
}
#endif
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_read;
int my_sys_read(struct pt_regs *regs)
{
static unsigned int count = 0;
if(count < 5) {
printk(KERN_INFO "[+] my_sys_read is called %d times.", count + 1);
count++;
}
return (*orig_sys_read)(regs);
}
static int __init hooking_sys_read_init(void) {
printk(KERN_INFO "[+] hooking_sys_read module inserted successfully!\n");
sys_call_table = acquire_sys_call_table();
printk(KERN_INFO "[+] the address of sys_call_table = @%lx\n", sys_call_table);
enable_page_rw((void *)sys_call_table);
orig_sys_read = ((sys_call_wrapper *)sys_call_table)[__NR_read];
if (!orig_sys_read) {
return -1;
}
((sys_call_wrapper *)sys_call_table)[__NR_read] = my_sys_read;
disable_page_rw((void *)sys_call_table);
printk(KERN_INFO "[+] the address of orig_sys_read = %p", orig_sys_read);
return 0;
}
static void __exit hooking_sys_read_exit(void)
{
printk(KERN_INFO "[+] hooking_sys_read module unloaded\n");
enable_page_rw((void *)sys_call_table);
((sys_call_wrapper *)sys_call_table)[__NR_read] = orig_sys_read;
disable_page_rw((void *)sys_call_table);
}
module_init(hooking_sys_read_init);
module_exit(hooking_sys_read_exit);
📌References