--- orig/configure +++ mod/configure @@ -508,6 +508,22 @@ echo "ok (0x$ADDRESS)" LINUX26_SYSCALL_CFLAGS="$LINUX26_SYSCALL_CFLAGS -DSYS_CALL_TABLE_ADDR=0x$ADDRESS" + if [ $CONFIG_X86_64 -eq 1 ] + then + echo -n "locating ia32_sys_call_table... " + ADDRESS=`awk '$3 == "ia32_sys_call_table" { print $1 }' $MAPFILE` + if [ -z "$ADDRESS" ] + then + echo "failed" + echo "error: unable to locate kernel symbol 'ia32_sys_call_table'" + echo " unable to support syscall hooking" + exit 1 + fi + + echo "ok (0x$ADDRESS)" + LINUX26_SYSCALL_CFLAGS="$LINUX26_SYSCALL_CFLAGS -DIA32_SYS_CALL_TABLE_ADDR=0x$ADDRESS" + fi + echo -n "checking sys_call_table status... " if [ $LINUX26_SYSCALLTABLE_READONLY -ne 0 ] @@ -545,6 +561,34 @@ echo "ok (0x$ADDRESS)" LINUX26_SYSCALL_CFLAGS="$LINUX26_SYSCALL_CFLAGS -DDO_EXECVE_ADDR=0x$ADDRESS" + + if [ $CONFIG_X86_64 -eq 1 ] + then + echo -n "locating compat_do_execve... " + + ADDRESS=`awk '$3 == "compat_do_execve" { print $1 }' $MAPFILE` + if [ -z "$ADDRESS" ] + then + echo "failed (probably not a problem)" + else + echo "ok (0x$ADDRESS)" + LINUX26_SYSCALL_CFLAGS="$LINUX26_SYSCALL_CFLAGS -DCOMPAT_DO_EXECVE_ADDR=0x$ADDRESS" + fi + + echo -n "locating sys_execve... " + + ADDRESS=`awk '$3 == "sys_execve" { print $1 }' $MAPFILE` + if [ -z "$ADDRESS" ] + then + echo "failed" + echo "error: unable to locate kernel symbol 'sys_execve'" + echo " unable to support 64-bit exec events" + exit 1 + fi + + echo "ok (0x$ADDRESS)" + LINUX26_SYSCALL_CFLAGS="$LINUX26_SYSCALL_CFLAGS -DSYS_EXECVE_ADDR=0x$ADDRESS" + fi fi fi @@ -664,9 +708,9 @@ then # we are using LSM echo "NOOP: ON_EXEC allowed" > /dev/null - elif [ $CONFIG_X86 -ne 0 -a $CONFIG_X86_64 -eq 0 ] + elif [ $CONFIG_X86 -eq 1 -o $CONFIG_X86_64 -eq 1 ] then - # we are using syscalls with x86 (32-bit) + # we are using syscalls with x86 (32-bit) and x86_64 (64-bit) echo "NOOP: ON_EXEC allowed" > /dev/null elif [ $ON_EXEC -eq 0 ] then @@ -674,7 +718,7 @@ echo "NOOP: ON_EXEC not wanted" > /dev/null else # we have ON_EXEC activated with syscalls on a non-x86 (32-bit) platform - echo "disabling ON_EXEC events (only available on x86 platforms)" + echo "disabling ON_EXEC events (only available on x86 + x86_64 platforms)" ON_EXEC=0 fi --- orig/dazuko_linux.c +++ mod/dazuko_linux.c @@ -91,9 +91,42 @@ #endif #endif +#ifdef CONFIG_X86_64 + +#include +#include + +#ifndef __NR_ia32_open +#define __NR_ia32_open 5 +#endif + +#ifndef __NR_ia32_close +#define __NR_ia32_close 6 +#endif + +#ifndef __NR_ia32_unlink +#define __NR_ia32_unlink 10 +#endif + +#ifndef __NR_ia32_execve +#define __NR_ia32_execve 11 +#endif + +#ifndef __NR_ia32_dup +#define __NR_ia32_dup 41 +#endif + +#ifndef __NR_ia32_dup2 +#define __NR_ia32_dup2 63 +#endif + +#endif #ifdef HIDDEN_SCT void **sys_call_table; +#ifdef CONFIG_X86_64 +void **ia32_sys_call_table; +#endif extern asmlinkage long sys_exit(int error_code); #else extern void *sys_call_table[]; @@ -103,6 +136,8 @@ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#define USERPTR __user + ssize_t linux_dazuko_device_read(struct file *, char __user *, size_t, loff_t *); ssize_t linux_dazuko_device_write(struct file *, const char __user *, size_t, loff_t *); int linux_dazuko_device_open(struct inode *, struct file *); @@ -135,6 +170,11 @@ extern struct xp_atomic active; +#if defined(ON_EXEC_SUPPORT) && defined(CONFIG_X86_64) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +static unsigned char original_ia32_exec_code[7]; +static int32_t original_sys_exec_displacement; +static int32_t *original_sys_exec_where = NULL; +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) static struct vfsmount *orig_rootmnt = NULL; @@ -145,40 +185,44 @@ #if defined(ON_OPEN_SUPPORT) - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) - static asmlinkage long (*original_sys_open)(const char __user *filename, int flags, int mode); - #else - static asmlinkage long (*original_sys_open)(const char *filename, int flags, int mode); - #endif + static asmlinkage long (*original_sys_open)(const char USERPTR *filename, int flags, int mode); static asmlinkage long (*original_sys_dup)(unsigned int filedes); +#ifdef CONFIG_X86_64 + static asmlinkage long (*original_ia32_sys_open)(const char USERPTR *filename, int flags, int mode); + static asmlinkage long (*original_ia32_sys_dup)(unsigned int filedes); +#endif #endif #if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT) static asmlinkage long (*original_sys_dup2)(unsigned int oldfd, unsigned int newfd); +#ifdef CONFIG_X86_64 + static asmlinkage long (*original_ia32_sys_dup2)(unsigned int oldfd, unsigned int newfd); +#endif #endif #if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT) static asmlinkage long (*original_sys_close)(unsigned int fd); +#ifdef CONFIG_X86_64 + static asmlinkage long (*original_ia32_sys_close)(unsigned int fd); +#endif #endif -#ifdef ON_EXEC_SUPPORT +#if defined(ON_EXEC_SUPPORT) && !defined(CONFIG_X86_64) static asmlinkage int (*original_sys_execve)(struct pt_regs regs); #endif #ifdef ON_UNLINK_SUPPORT - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) - static asmlinkage long (*original_sys_unlink)(const char __user *pathname); - #else - static asmlinkage long (*original_sys_unlink)(const char *pathname); - #endif + static asmlinkage long (*original_sys_unlink)(const char USERPTR *pathname); +#ifdef CONFIG_X86_64 + static asmlinkage long (*original_ia32_sys_unlink)(const char USERPTR *pathname); +#endif #endif #ifdef ON_RMDIR_SUPPORT - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) - static asmlinkage long (*original_sys_rmdir)(const char __user *pathname); - #else - static asmlinkage long (*original_sys_rmdir)(const char *pathname); - #endif + static asmlinkage long (*original_sys_rmdir)(const char USERPTR *pathname); +#ifdef CONFIG_X86_64 + static asmlinkage long (*original_ia32_sys_rmdir)(const char USERPTR *pathname); +#endif #endif @@ -248,6 +292,50 @@ #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + +static inline pte_t *daz_lookup_address(unsigned long address) +{ + pgd_t *pgd = pgd_offset_k(address); +#ifdef PUD_SIZE + pud_t *pud; +#endif + pmd_t *pmd; + pte_t *pte; + if (pgd_none(*pgd)) + return NULL; + +#ifdef PUD_SIZE + pud = pud_offset(pgd, address); + if (!pud_present(*pud)) + return NULL; + pmd = pmd_offset(pud, address); +#else + pmd = pmd_offset(pgd, address); +#endif + if (!pmd_present(*pmd)) + return NULL; + if (pmd_large(*pmd)) + return (pte_t *)pmd; + pte = pte_offset_kernel(pmd, address); + if (pte && !pte_present(*pte)) + pte = NULL; + return pte; +} + +void dazuko_change_page_attr(void *p, int n, pgprot_t pr) +{ + pte_t *kpte; + unsigned long q = (unsigned long) p; + + kpte = daz_lookup_address(q); + if (kpte) + change_page_attr(pte_page(*kpte), n, pr); + + global_flush_tlb(); +} + +#endif /* mutex */ @@ -1434,15 +1522,47 @@ return -1; } +#ifdef SYSCALL_TABLE_READONLY -/* system calls */ +static int dazuko_is_address_writable(unsigned long address) +{ + pgd_t *pgd = pgd_offset_k(address); +#ifdef PUD_SIZE + pud_t *pud; +#endif + pmd_t *pmd; + pte_t *pte; -#if defined(ON_OPEN_SUPPORT) -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -asmlinkage long linux_dazuko_sys_open(const char __user *filename, int flags, int mode) + if (pgd_none(*pgd)) + return -1; +#ifdef PUD_SIZE + pud = pud_offset(pgd, address); + if (pud_none(*pud)) + return -1; + pmd = pmd_offset(pud, address); #else -asmlinkage long linux_dazuko_sys_open(const char *filename, int flags, int mode) + pmd = pmd_offset(pgd, address); #endif + if (pmd_none(*pmd)) + return -1; + + if (pmd_large(*pmd)) + pte = (pte_t *)pmd; + else + pte = pte_offset_kernel(pmd, address); + + if (!pte || !pte_present(*pte)) { + return -1; + } + + return pte_write(*pte) ? 1 : 0; +} +#endif + +/* system calls */ + +#if defined(ON_OPEN_SUPPORT) +asmlinkage long do_linux_dazuko_sys_open(const char USERPTR *filename, int flags, int mode, asmlinkage long (*original)(const char USERPTR *, int, int)) { /* The kernel wants to open the given filename * with the given flags and mode. The dazuko_file_struct @@ -1515,13 +1635,25 @@ else { /* call the standard open function */ - fd = original_sys_open(filename, flags, mode); + fd = original(filename, flags, mode); } return fd; } -asmlinkage long linux_dazuko_sys_dup(unsigned int filedes) +asmlinkage long linux_dazuko_sys_open(const char USERPTR *filename, int flags, int mode) +{ + return do_linux_dazuko_sys_open(filename, flags, mode, original_sys_open); +} + +#ifdef CONFIG_X86_64 +asmlinkage long linux_dazuko_ia32_sys_open(const char USERPTR *filename, int flags, int mode) +{ + return do_linux_dazuko_sys_open(filename, flags, mode, original_ia32_sys_open); +} +#endif + +asmlinkage long do_linux_dazuko_sys_dup(unsigned int filedes, asmlinkage long (*original)(unsigned int)) { struct dazuko_file_struct *dfs = NULL; struct event_properties event_p; @@ -1591,15 +1723,28 @@ else { /* call the standard open function */ - error = original_sys_dup(filedes); + error = original(filedes); } return error; } + +asmlinkage long linux_dazuko_sys_dup(unsigned int filedes) +{ + return do_linux_dazuko_sys_dup(filedes, original_sys_dup); +} + +#if defined(CONFIG_X86_64) +asmlinkage long linux_dazuko_ia32_sys_dup(unsigned int filedes) +{ + DPRINT(("ia_sys_dup\n")); + return do_linux_dazuko_sys_dup(filedes, original_ia32_sys_dup); +} +#endif #endif #if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT) -asmlinkage long linux_dazuko_sys_dup2(unsigned int oldfd, unsigned int newfd) +asmlinkage long do_linux_dazuko_sys_dup2(unsigned int oldfd, unsigned int newfd, asmlinkage long (*original)(unsigned int, unsigned int)) { struct dazuko_file_struct *dfs = NULL; struct event_properties open_event_p; @@ -1677,7 +1822,7 @@ #endif /* call the standard open function */ - error = original_sys_dup2(oldfd, newfd); + error = original(oldfd, newfd); #if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT) { @@ -1725,10 +1870,23 @@ return error; } + +asmlinkage long linux_dazuko_sys_dup2(unsigned int oldfd, unsigned int newfd) +{ + return do_linux_dazuko_sys_dup2(oldfd, newfd, original_sys_dup2); +} + +#ifdef CONFIG_X86_64 +asmlinkage long linux_dazuko_ia32_sys_dup2(unsigned int oldfd, unsigned int newfd) +{ + return do_linux_dazuko_sys_dup2(oldfd, newfd, original_ia32_sys_dup2); +} +#endif + #endif #if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT) -asmlinkage long linux_dazuko_sys_close(unsigned int fd) +asmlinkage long do_linux_dazuko_sys_close(unsigned int fd, asmlinkage long (*original)(unsigned int)) { /* The kernel wants to close the given file * descriptor. */ @@ -1783,7 +1941,7 @@ if (xp_id.files != NULL) dazuko_put_files_struct(xp_id.files); - error = original_sys_close(fd); + error = original(fd); if (dfs != NULL) { @@ -1797,27 +1955,26 @@ return error; } -#endif -#ifdef ON_EXEC_SUPPORT +asmlinkage long linux_dazuko_sys_close(unsigned int fd) +{ + return do_linux_dazuko_sys_close(fd, original_sys_close); +} -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -typedef int (*do_execve_call_t)(char *filename, char __user *__user *argv, char __user *__user *envp, struct pt_regs *regs); +#ifdef CONFIG_X86_64 +asmlinkage long linux_dazuko_ia32_sys_close(unsigned int fd) +{ + return do_linux_dazuko_sys_close(fd, original_ia32_sys_close); +} +#endif -static do_execve_call_t XXX_do_execve = (do_execve_call_t) DO_EXECVE_ADDR; #endif -asmlinkage int linux_dazuko_sys_execve(struct pt_regs regs) -{ - /* The kernel wants to execute the given file. - * Because the given structure contains stack - * address information, we can't simply call - * the default standard execve. Instead we - * have to manually inline the standard execve - * call. */ +#ifdef ON_EXEC_SUPPORT +static inline int linux_dazuko_do_exec_check(char USERPTR *filename) +{ struct dazuko_file_struct *dfs = NULL; - char *filename; int error = 0; int check_error = 0; struct event_properties event_p; @@ -1849,7 +2006,7 @@ { dazuko_bzero(dfs->extra_data, sizeof(struct xp_file_struct)); - dfs->extra_data->user_filename = (char *)regs.ebx; + dfs->extra_data->user_filename = filename; dazuko_bzero(&event_p, sizeof(event_p)); event_p.pid = current->pid; @@ -1869,10 +2026,33 @@ } } - if (error) - { - return error; - } + return error; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +typedef int (*do_execve_call_t)(char *filename, char __user *__user *argv, char __user *__user *envp, struct pt_regs *regs); + +static do_execve_call_t XXX_do_execve = (do_execve_call_t) DO_EXECVE_ADDR; +#endif + +#if defined(CONFIG_X86) && !defined(CONFIG_X86_64) + +asmlinkage int linux_dazuko_sys_execve(struct pt_regs regs) +{ + /* The kernel wants to execute the given file. + * Because the given structure contains stack + * address information, we can't simply call + * the default standard execve. Instead we + * have to manually inline the standard execve + * call. */ + char *filename; + int error = 0; + + error = linux_dazuko_do_exec_check((char USERPTR *) regs.ebx); + + if (error) { + return error; + } /* call the standard execve function */ @@ -1968,6 +2148,376 @@ } #endif } + +#elif defined(CONFIG_X86_64) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + + +asmlinkage +long linux_dazuko_64bit_sys_execve(char __user *name, char __user * __user *argv, char __user * __user *envp, struct pt_regs regs) +{ + long error; + char * filename; + + error = linux_dazuko_do_exec_check(name); + + if (error) { + return error; + } + + filename = getname(name); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + return error; + error = XXX_do_execve(filename, argv, envp, ®s); + if (error == 0) { +#ifndef CONFIG_UTRACE + task_lock(current); + current->ptrace &= ~PT_DTRACE; + task_unlock(current); +#endif + } + putname(filename); + return error; +} +#else + +#include +#include + +static int nargs(u32 src, char **dst) +{ + int cnt; + u32 val; + + cnt = 0; + do { + int ret = get_user(val, (__u32 *)(u64)src); + if (ret) + return ret; + if (dst) + dst[cnt] = (char *)(u64)val; + cnt++; + src += 4; + if (cnt >= (MAX_ARG_PAGES*PAGE_SIZE)/sizeof(void*)) + return -E2BIG; + } while(val); + if (dst) + dst[cnt-1] = 0; + return cnt; +} + + +asmlinkage long linux_dazuko_ia32_sys_execve(char *name, u32 argv, u32 envp, struct pt_regs regs) +{ + mm_segment_t oldseg; + char **buf = NULL; + int na = 0,ne = 0; + int ret; + unsigned sz = 0; + + if (argv) { + na = nargs(argv, NULL); + if (na < 0) + return -EFAULT; + } + if (envp) { + ne = nargs(envp, NULL); + if (ne < 0) + return -EFAULT; + } + + if (argv || envp) { + sz = (na+ne)*sizeof(void *); + if (sz > PAGE_SIZE) + buf = vmalloc(sz); + else + buf = kmalloc(sz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + + if (argv) { + ret = nargs(argv, buf); + if (ret < 0) + goto free; + } + + if (envp) { + ret = nargs(envp, buf + na); + if (ret < 0) + goto free; + } + + name = getname(name); + ret = PTR_ERR(name); + if (IS_ERR(name)) + goto free; + + oldseg = get_fs(); + set_fs(KERNEL_DS); + ret = XXX_do_execve(name, argv ? buf : NULL, envp ? buf+na : NULL, ®s); + set_fs(oldseg); + + if (ret == 0) + current->ptrace &= ~PT_DTRACE; + + putname(name); + +free: + if (argv || envp) { + if (sz > PAGE_SIZE) + vfree(buf); + else + kfree(buf); + } + return ret; +} + +#endif + +typedef asmlinkage long (*compat_do_execve_call_t)(char __user *name, + compat_uptr_t __user *argv, + compat_uptr_t __user *envp, + struct pt_regs *regs); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,7) +static compat_do_execve_call_t XXX_compat_do_execve = (compat_do_execve_call_t) COMPAT_DO_EXECVE_ADDR; + +asmlinkage long linux_dazuko_ia32_sys_execve(char __user *name, compat_uptr_t __user *argv, + compat_uptr_t __user *envp, struct pt_regs *regs) +{ + long error; + char * filename; + + + error = linux_dazuko_do_exec_check(name); + + if (error) { + return error; + } + + filename = getname(name); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + return error; + error = XXX_compat_do_execve(filename, argv, envp, regs); + if (error == 0) { +#ifndef CONFIG_UTRACE + task_lock(current); + current->ptrace &= ~PT_DTRACE; + task_unlock(current); +#endif + } + putname(filename); + return error; +} +#else + +#include +#include + +static int nargs(u32 src, char **dst) +{ + int cnt; + u32 val; + + cnt = 0; + do { + int ret = get_user(val, (__u32 *)(u64)src); + if (ret) + return ret; + if (dst) + dst[cnt] = (char *)(u64)val; + cnt++; + src += 4; + if (cnt >= (MAX_ARG_PAGES*PAGE_SIZE)/sizeof(void*)) + return -E2BIG; + } while(val); + if (dst) + dst[cnt-1] = 0; + return cnt; +} + + +asmlinkage long linux_dazuko_ia32_sys_execve(char *name, u32 argv, u32 envp, struct pt_regs regs) +{ + mm_segment_t oldseg; + char **buf = NULL; + int na = 0,ne = 0; + int ret; + unsigned sz = 0; + + if (argv) { + na = nargs(argv, NULL); + if (na < 0) + return -EFAULT; + } + if (envp) { + ne = nargs(envp, NULL); + if (ne < 0) + return -EFAULT; + } + + if (argv || envp) { + sz = (na+ne)*sizeof(void *); + if (sz > PAGE_SIZE) + buf = vmalloc(sz); + else + buf = kmalloc(sz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + + if (argv) { + ret = nargs(argv, buf); + if (ret < 0) + goto free; + } + + if (envp) { + ret = nargs(envp, buf + na); + if (ret < 0) + goto free; + } + + name = getname(name); + ret = PTR_ERR(name); + if (IS_ERR(name)) + goto free; + + oldseg = get_fs(); + set_fs(KERNEL_DS); + ret = XXX_do_execve(name, argv ? buf : NULL, envp ? buf+na : NULL, ®s); + set_fs(oldseg); + +#ifndef CONFIG_UTRACE + if (ret == 0) + current->ptrace &= ~PT_DTRACE; +#endif + + putname(name); + +free: + if (argv || envp) { + if (sz > PAGE_SIZE) + vfree(buf); + else + kfree(buf); + } + return ret; +} + +#endif + +static int dazuko_is_address_writable(unsigned long address); + +static void relocate_32bit_sys_execve_call(void *cur_code_vp) +{ + do { + /* 32-bit exec: we replace original + * LEAQ &sys_execve, rax (48 8D 05
) with + * MOV &linux_dazuko_ia32_sys_execve, rax (48 C7 C0
) + * - both are exactly 7 bytes (when address is 4 bytes). + */ + + unsigned char movstruction[7] = { 0x48, 0xc7, 0xc0, 0 }; + unsigned char *p = (unsigned char *) linux_dazuko_ia32_sys_execve; + unsigned char *cur_code = (unsigned char *) cur_code_vp; + int writable; + + memcpy(original_ia32_exec_code, cur_code, 7); + + if (cur_code[0] != 0x48 || cur_code[1] != 0x8d || cur_code[2] != 5){ + printk(KERN_ERR "whoa, existing execve code not what expected (%02x %02x %02x)\n", + cur_code[0], cur_code[1], cur_code[2]); + break; + } + + if (p < (unsigned char *) 0xffffffff00000000UL) { + printk(KERN_ERR "whoa, our pointer not starting with 0xffffff (%p)\n", p); + break; + } + + memcpy(&movstruction[3], &p, 4); + + writable = dazuko_is_address_writable((unsigned long) cur_code); + if (writable <= 0) { + dazuko_change_page_attr(cur_code, 1, PAGE_KERNEL_NOCACHE); + } + + writable = dazuko_is_address_writable((unsigned long) cur_code); + if (writable > 0) { + memcpy(cur_code, movstruction, 7); + } + else { + printk(KERN_ERR "unable to hook r/o sct\n"); + } + if (writable <= 0) { + dazuko_change_page_attr(cur_code, 1, PAGE_KERNEL_RO); + } + } while (0); +} + +static void restore_32bit_sys_execve_call(void *cur_code_vp) +{ + unsigned char *cur_code = (unsigned char *) cur_code_vp; + int writable = dazuko_is_address_writable((unsigned long) cur_code); + if (writable <= 0) { + dazuko_change_page_attr(cur_code, 1, PAGE_KERNEL_NOCACHE); + } + + writable = dazuko_is_address_writable((unsigned long) cur_code); + + if (writable > 0) { + memcpy(cur_code, original_ia32_exec_code, 7); + } + else { + printk(KERN_ERR "unable to unhook 32-bit execve\n"); + } + if (writable <= 0) { + dazuko_change_page_attr(cur_code, 1, PAGE_KERNEL_RO); + } +} + +static void relocate_64bit_sys_execve_call(void *stub_execve_addr) +{ + size_t n = 200; + void *p = NULL, *q = stub_execve_addr, *sys_execve_addr = (void *) SYS_EXECVE_ADDR; + int32_t displacement; + + + /* Search for CALL (E8H) opcode */ + + while ((p = memchr(q, 0xe8, n))) { + n -= p - q + 1; + + ++p; + q = p; + displacement = *((int32_t *) p); + + if (p+4+displacement == sys_execve_addr) { + original_sys_exec_where = p; + original_sys_exec_displacement = displacement; + + displacement = ((void *) linux_dazuko_64bit_sys_execve) - ((void *)(p+4)); + + *original_sys_exec_where = displacement; + break; + } + } +} + +static void restore_64bit_sys_execve_call(void) +{ + if (original_sys_exec_where) { + *original_sys_exec_where = original_sys_exec_displacement; + } +} + +#endif +#endif #endif static inline int linux_dazuko_sys_generic(int event, const char *user_pathname, int daemon_is_allowed) @@ -2028,7 +2578,7 @@ } #ifdef ON_UNLINK_SUPPORT -asmlinkage long linux_dazuko_sys_unlink(const char *pathname) +asmlinkage long do_linux_dazuko_sys_unlink(const char *pathname, asmlinkage long (*original)(const char *)) { int error; @@ -2037,12 +2587,24 @@ if (error) return error; - return original_sys_unlink(pathname); + return original(pathname); +} + +asmlinkage long linux_dazuko_sys_unlink(const char *pathname) +{ + return do_linux_dazuko_sys_unlink(pathname, original_sys_unlink); +} + +#ifdef CONFIG_X86_64 +asmlinkage long linux_dazuko_ia32_sys_unlink(const char *pathname) +{ + return do_linux_dazuko_sys_unlink(pathname, original_ia32_sys_unlink); } #endif +#endif #ifdef ON_RMDIR_SUPPORT -asmlinkage long linux_dazuko_sys_rmdir(const char *pathname) +asmlinkage long do_linux_dazuko_sys_rmdir(const char *pathname, asmlinkage long (*original)(const char *)) { int error; @@ -2053,6 +2615,18 @@ return original_sys_rmdir(pathname); } + +asmlinkage long linux_dazuko_sys_rmdir(const char *pathname) +{ + return do_linux_dazuko_sys_rmdir(pathname, original_sys_rmdir); +} + +#ifdef CONFIG_X86_64 +asmlinkage long linux_dazuko_ia32_sys_rmdir(const char *pathname) +{ + return do_linux_dazuko_sys_rmdir(pathname, original_ia32_sys_rmdir); +} +#endif #endif @@ -2091,6 +2665,13 @@ } #endif +#ifdef CONFIG_X86_64 +static void** dazuko_get_ia32sct(void) +{ + return (void **)IA32_SYS_CALL_TABLE_ADDR; +} +#endif + static inline void dazuko_lock_current(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) @@ -2108,30 +2689,7 @@ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) static int dazuko_is_address_writable(unsigned long address) { - pgd_t *pgd = pgd_offset_k(address); -#ifdef PUD_SIZE - pud_t *pud; -#endif - pmd_t *pmd; - pte_t *pte; - - if (pgd_none(*pgd)) - return -1; -#ifdef PUD_SIZE - pud = pud_offset(pgd, address); - if (pud_none(*pud)) - return -1; - pmd = pmd_offset(pud, address); -#else - pmd = pmd_offset(pgd, address); -#endif - if (pmd_none(*pmd)) - return -1; - - if (pmd_large(*pmd)) - pte = (pte_t *)pmd; - else - pte = pte_offset_kernel(pmd, address); + pte_t *pte = daz_lookup_address(address); if (!pte || !pte_present(*pte)) return -1; @@ -2140,20 +2698,72 @@ } #endif -#define DAZUKO_HOOK(syscall_func) do \ -{ \ - original_sys_##syscall_func = sys_call_table[__NR_##syscall_func]; \ - sys_call_table[__NR_##syscall_func] = linux_dazuko_sys_##syscall_func; \ - DPRINT(("dazuko: hooked sys_" #syscall_func "\n")); \ -} \ -while (0) -inline int xp_sys_hook(void) +void do_dazuko_hook(const char *funcname, void **sct, int num, void **ptr_to_orig_ptr, void *ptr_to_new_ptr) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) - int syscall_writable; + +#ifdef SYSCALL_TABLE_READONLY + int sct_writable = dazuko_is_address_writable((unsigned long) &sct[num]); + + if (sct_writable < 0) { + xp_print("dazuko: unable to determine if syscall table[%d] is readonly, assuming it is\n", num); + } + if (sct_writable <= 0) { + /* unprotect the syscall table */ + dazuko_change_page_attr(&sct[num], 1, + PAGE_KERNEL_NOCACHE); + } + + sct_writable = dazuko_is_address_writable((unsigned long) &sct[num]); + + if (sct_writable > 0) { #endif + *ptr_to_orig_ptr = sct[num]; + sct[num] = ptr_to_new_ptr; +#ifdef SYSCALL_TABLE_READONLY + } + else { + printk(KERN_ERR "unable to hook syscall %s (%d) in r/o sct\n", + funcname ? funcname : "[unknown]", num); + } + if (sct_writable <= 0) { + dazuko_change_page_attr(&sct[num], 1, + PAGE_KERNEL_RO); + } +#endif +} +#define DAZUKO_HOOK(syscall_func) do_dazuko_hook(#syscall_func, sys_call_table, __NR_##syscall_func, (void **) &original_sys_##syscall_func, linux_dazuko_sys_##syscall_func) + +#ifdef CONFIG_X86_64 +#ifndef __NR_ia32_open +#define __NR_ia32_open 5 +#endif +#ifndef __NR_ia32_dup +#define __NR_ia32_dup 41 +#endif +#ifndef __NR_ia32_dup2 +#define __NR_ia32_dup2 63 +#endif +#ifndef __NR_ia32_close +#define __NR_ia32_close 6 +#endif +#ifndef __NR_ia32_execve +#define __NR_ia32_execve 11 +#endif +#ifndef __NR_ia32_unlink +#define __NR_ia32_unlink 10 +#endif + + +#define DAZUKO_HOOK_IA32(syscall_func) do_dazuko_hook(#syscall_func, ia32_sys_call_table, __NR_ia32_##syscall_func, (void **) &original_ia32_sys_##syscall_func, linux_dazuko_ia32_sys_##syscall_func) + +#else +#define DAZUKO_HOOK_IA32(a) +#endif + +inline int xp_sys_hook(void) +{ /* Called insmod when inserting the module. */ #ifdef HIDDEN_SCT @@ -2165,6 +2775,15 @@ } #endif +#ifdef CONFIG_X86_64 + ia32_sys_call_table = dazuko_get_ia32sct(); + if (ia32_sys_call_table == NULL) + { + xp_print("dazuko: panic (ia32_sys_call_table == NULL)\n"); + return -1; + } +#endif + /* Make sure we have a valid task_struct. */ if (current == NULL) @@ -2278,50 +2897,42 @@ fsync_dev(0); #endif -#ifdef SYSCALL_TABLE_READONLY - syscall_writable = dazuko_is_address_writable((unsigned long) sys_call_table); - if (syscall_writable < 0) - { - xp_print("dazuko: unable to determine if syscall table is readonly, assuming it is\n"); - } - if (syscall_writable <= 0) - { - /* unprotect the syscall table */ - change_page_attr(virt_to_page(sys_call_table), 1, PAGE_KERNEL); - global_flush_tlb(); - } -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) - syscall_writable = dazuko_is_address_writable((unsigned long) sys_call_table); - if (syscall_writable != 1) - { - xp_print("dazuko: warning: assuming syscall table can be modified\n"); - } -#endif - #if defined(ON_OPEN_SUPPORT) DAZUKO_HOOK(open); DAZUKO_HOOK(dup); + DAZUKO_HOOK_IA32(open); + DAZUKO_HOOK_IA32(dup); #endif #if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT) DAZUKO_HOOK(dup2); + DAZUKO_HOOK_IA32(dup2); #endif #if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT) DAZUKO_HOOK(close); + DAZUKO_HOOK_IA32(close); #endif -#ifdef ON_EXEC_SUPPORT +#if defined(ON_EXEC_SUPPORT) && !defined(CONFIG_X86_64) DAZUKO_HOOK(execve); #endif +#if defined(ON_EXEC_SUPPORT) && defined(CONFIG_X86_64) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + + // minor binary patching + + relocate_64bit_sys_execve_call(sys_call_table[__NR_execve]); + relocate_32bit_sys_execve_call(ia32_sys_call_table[__NR_ia32_execve]); +#endif + #ifdef ON_UNLINK_SUPPORT DAZUKO_HOOK(unlink); + DAZUKO_HOOK_IA32(unlink); #endif #ifdef ON_RMDIR_SUPPORT DAZUKO_HOOK(rmdir); + DAZUKO_HOOK_IA32(rmdir); #endif #ifdef SYSCALL_TABLE_READONLY @@ -2343,21 +2954,53 @@ return 0; } -#define DAZUKO_UNHOOK(syscall_func) do \ -{ \ - if (sys_call_table[__NR_##syscall_func] != linux_dazuko_sys_##syscall_func) \ - xp_print("dazuko: " #syscall_func " system call has been changed (system may be left in an unstable state!)\n"); \ - sys_call_table[__NR_##syscall_func] = original_sys_##syscall_func; \ - DPRINT(("dazuko: unhooked sys_" #syscall_func "\n")); \ -} \ -while (0) -inline int xp_sys_unhook(void) +void do_dazuko_unhook(void **sct, int num, void *ptr_to_orig_ptr, void *ptr_to_new_ptr) { + +#ifdef SYSCALL_TABLE_READONLY + int sct_writable = dazuko_is_address_writable((unsigned long) &sct[num]); + + if (sct_writable < 0) { + xp_print("dazuko: unable to determine if syscall table[%d] is readonly, assuming it is\n", num); + } + if (sct_writable <= 0) { + /* unprotect the syscall table */ + dazuko_change_page_attr(&sct[num], 1, + PAGE_KERNEL_NOCACHE); + } + + sct_writable = dazuko_is_address_writable((unsigned long) &sct[num]); + if (sct_writable > 0) { +#endif + if (sct[num] != ptr_to_new_ptr) { + xp_print("dazuko: system call %d has been changed (system may be left in an unstable state!)\n", num); + } + sct[num] = ptr_to_orig_ptr; #ifdef SYSCALL_TABLE_READONLY - int syscall_writable; + } + else { + printk(KERN_ERR "unable to unhook %d from r/o sct", num); + } + if (sct_writable <= 0) { + dazuko_change_page_attr(&sct[num], 1, + PAGE_KERNEL_RO); + } +#endif +} + + +#define DAZUKO_UNHOOK(syscall_func) do_dazuko_unhook(sys_call_table, __NR_##syscall_func, original_sys_##syscall_func, linux_dazuko_sys_##syscall_func) + +#ifdef CONFIG_X86_64 +#define DAZUKO_UNHOOK_IA32(syscall_func) do_dazuko_unhook(ia32_sys_call_table, __NR_ia32_##syscall_func, original_ia32_sys_##syscall_func, linux_dazuko_ia32_sys_##syscall_func) + +#else +#define DAZUKO_UNHOOK_IA32(a) #endif +inline int xp_sys_unhook(void) +{ /* Called by rmmod when removing the module. */ int error; @@ -2371,39 +3014,40 @@ fsync_dev(0); #endif -#ifdef SYSCALL_TABLE_READONLY - syscall_writable = dazuko_is_address_writable((unsigned long) sys_call_table); - - if (syscall_writable <= 0) - { - /* unprotect the syscall table */ - change_page_attr(virt_to_page(sys_call_table), 1, PAGE_KERNEL); - global_flush_tlb(); - } -#endif - #if defined(ON_OPEN_SUPPORT) DAZUKO_UNHOOK(open); DAZUKO_UNHOOK(dup); + DAZUKO_UNHOOK_IA32(open); + DAZUKO_UNHOOK_IA32(dup); #endif #if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT) DAZUKO_UNHOOK(dup2); + DAZUKO_UNHOOK_IA32(dup2); #endif #if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT) DAZUKO_UNHOOK(close); + DAZUKO_UNHOOK_IA32(close); #endif -#ifdef ON_EXEC_SUPPORT +#if defined(ON_EXEC_SUPPORT) && !defined(CONFIG_X86_64) DAZUKO_UNHOOK(execve); #endif +#if defined(ON_EXEC_SUPPORT) && defined(CONFIG_X86_64) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + + restore_32bit_sys_execve_call(ia32_sys_call_table[__NR_ia32_execve]); + restore_64bit_sys_execve_call(); +#endif + #ifdef ON_UNLINK_SUPPORT DAZUKO_UNHOOK(unlink); + DAZUKO_UNHOOK_IA32(unlink); #endif #ifdef ON_RMDIR_SUPPORT DAZUKO_UNHOOK(rmdir); + DAZUKO_UNHOOK_IA32(rmdir); #endif #ifdef SYSCALL_TABLE_READONLY @@ -2671,14 +3315,14 @@ #ifdef MODULE - + MODULE_AUTHOR("Avira GmbH "); MODULE_DESCRIPTION("allow 3rd-party file access control"); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #else static const char __module_license[] __attribute__((section(".modinfo"))) = "license=GPL"; -#endif +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)