/* * scprint.c v0.2.1 - Print list of system call addresses using various means. * * Based (very) loosely on http://www.securityfocus.com/infocus/1811 * * Creates 3 /proc entries that print to dmesg when read: * user_syscall: Prints the syscall table specified by the table= param * idt_syscall: Finds the syscall table by looking up int 0x80 in IDT * sysenter_syscall: Finds the syscalll by looking up the sysenter MSR * * This module takes 3 optional parameters: * table=0xaddr - manually specify syscall table for /proc/user_syscall * system_call=0xaddr - manually specify the system_call symbol for idt_syscall * se_entry=0xaddr - manually specify the sysenter_entry symbol * * Check /boot/System.map for symbol addresses if needed. * * TODO: * - Properly use /proc files instead of dmesg for output * - Handle keyboard IRQ hook method in * http://www.phrack.org/phrack/59/p59-0x0e.txt * - Provide ways to check for pagefault handler hooks * to guard against shadow walker. */ #ifdef MODVERSIONS #include #endif #include #include #include #include #include #include #include #include static long table = 0; static long se_entry = 0; static long system_call = 0; module_param(table, long, S_IRUGO); module_param(se_entry, long, S_IRUGO); module_param(system_call, long, S_IRUGO); #define READ_ASM 128 /* How far to read into asm */ /* i386 sys_call_table for kernel 2.6.x */ char * callz_2p6[] = { "sys_restart_syscall", /* 0 - old "setup()" system call, used for restarting */ "sys_exit", "sys_fork", "sys_read", "sys_write", "sys_open", /* 5 */ "sys_close", "sys_waitpid", "sys_creat", "sys_link", "sys_unlink", /* 10 */ "sys_execve", "sys_chdir", "sys_time", "sys_mknod", "sys_chmod", /* 15 */ "sys_lchown16", "sys_ni_syscall", /* old break syscall holder */ "sys_stat", "sys_lseek", "sys_getpid", /* 20 */ "sys_mount", "sys_oldumount", "sys_setuid16", "sys_getuid16", "sys_stime", /* 25 */ "sys_ptrace", "sys_alarm", "sys_fstat", "sys_pause", "sys_utime", /* 30 */ "sys_ni_syscall", /* old stty syscall holder */ "sys_ni_syscall", /* old gtty syscall holder */ "sys_access", "sys_nice", "sys_ni_syscall", /* 35 - old ftime syscall holder */ "sys_sync", "sys_kill", "sys_rename", "sys_mkdir", "sys_rmdir", /* 40 */ "sys_dup", "sys_pipe", "sys_times", "sys_ni_syscall", /* old prof syscall holder */ "sys_brk", /* 45 */ "sys_setgid16", "sys_getgid16", "sys_signal", "sys_geteuid16", "sys_getegid16", /* 50 */ "sys_acct", "sys_umount", /* recycled never used phys() */ "sys_ni_syscall", /* old lock syscall holder */ "sys_ioctl", "sys_fcntl", /* 55 */ "sys_ni_syscall", /* old mpx syscall holder */ "sys_setpgid", "sys_ni_syscall", /* old ulimit syscall holder */ "sys_olduname", "sys_umask", /* 60 */ "sys_chroot", "sys_ustat", "sys_dup2", "sys_getppid", "sys_getpgrp", /* 65 */ "sys_setsid", "sys_sigaction", "sys_sgetmask", "sys_ssetmask", "sys_setreuid16", /* 70 */ "sys_setregid16", "sys_sigsuspend", "sys_sigpending", "sys_sethostname", "sys_setrlimit", /* 75 */ "sys_old_getrlimit", "sys_getrusage", "sys_gettimeofday", "sys_settimeofday", "sys_getgroups16", /* 80 */ "sys_setgroups16", "old_select", "sys_symlink", "sys_lstat", "sys_readlink", /* 85 */ "sys_uselib", "sys_swapon", "sys_reboot", "old_readdir", "old_mmap", /* 90 */ "sys_munmap", "sys_truncate", "sys_ftruncate", "sys_fchmod", "sys_fchown16", /* 95 */ "sys_getpriority", "sys_setpriority", "sys_ni_syscall", /* old profil syscall holder */ "sys_statfs", "sys_fstatfs", /* 100 */ "sys_ioperm", "sys_socketcall", "sys_syslog", "sys_setitimer", "sys_getitimer", /* 105 */ "sys_newstat", "sys_newlstat", "sys_newfstat", "sys_uname", "sys_iopl", /* 110 */ "sys_vhangup", "sys_ni_syscall", /* old "idle" system call */ "sys_vm86old", "sys_wait4", "sys_swapoff", /* 115 */ "sys_sysinfo", "sys_ipc", "sys_fsync", "sys_sigreturn", "sys_clone", /* 120 */ "sys_setdomainname", "sys_newuname", "sys_modify_ldt", "sys_adjtimex", "sys_mprotect", /* 125 */ "sys_sigprocmask", "sys_ni_syscall", /* old "create_module" */ "sys_init_module", "sys_delete_module", "sys_ni_syscall", /* 130: old "get_kernel_syms" */ "sys_quotactl", "sys_getpgid", "sys_fchdir", "sys_bdflush", "sys_sysfs", /* 135 */ "sys_personality", "sys_ni_syscall", /* reserved for afs_syscall */ "sys_setfsuid16", "sys_setfsgid16", "sys_llseek", /* 140 */ "sys_getdents", "sys_select", "sys_flock", "sys_msync", "sys_readv", /* 145 */ "sys_writev", "sys_getsid", "sys_fdatasync", "sys_sysctl", "sys_mlock", /* 150 */ "sys_munlock", "sys_mlockall", "sys_munlockall", "sys_sched_setparam", "sys_sched_getparam", /* 155 */ "sys_sched_setscheduler", "sys_sched_getscheduler", "sys_sched_yield", "sys_sched_get_priority_max", "sys_sched_get_priority_min", /* 160 */ "sys_sched_rr_get_interval", "sys_nanosleep", "sys_mremap", "sys_setresuid16", "sys_getresuid16", /* 165 */ "sys_vm86", "sys_ni_syscall", /* Old sys_query_module */ "sys_poll", "sys_nfsservctl", "sys_setresgid16", /* 170 */ "sys_getresgid16", "sys_prctl", "sys_rt_sigreturn", "sys_rt_sigaction", "sys_rt_sigprocmask", /* 175 */ "sys_rt_sigpending", "sys_rt_sigtimedwait", "sys_rt_sigqueueinfo", "sys_rt_sigsuspend", "sys_pread64", /* 180 */ "sys_pwrite64", "sys_chown16", "sys_getcwd", "sys_capget", "sys_capset", /* 185 */ "sys_sigaltstack", "sys_sendfile", "sys_ni_syscall", /* reserved for streams1 */ "sys_ni_syscall", /* reserved for streams2 */ "sys_vfork", /* 190 */ "sys_getrlimit", "sys_mmap2", "sys_truncate64", "sys_ftruncate64", "sys_stat64", /* 195 */ "sys_lstat64", "sys_fstat64", "sys_lchown", "sys_getuid", "sys_getgid", /* 200 */ "sys_geteuid", "sys_getegid", "sys_setreuid", "sys_setregid", "sys_getgroups", /* 205 */ "sys_setgroups", "sys_fchown", "sys_setresuid", "sys_getresuid", "sys_setresgid", /* 210 */ "sys_getresgid", "sys_chown", "sys_setuid", "sys_setgid", "sys_setfsuid", /* 215 */ "sys_setfsgid", "sys_pivot_root", "sys_mincore", "sys_madvise", "sys_getdents64", /* 220 */ "sys_fcntl64", "sys_ni_syscall", /* reserved for TUX */ "sys_ni_syscall", "sys_gettid", "sys_readahead", /* 225 */ "sys_setxattr", "sys_lsetxattr", "sys_fsetxattr", "sys_getxattr", "sys_lgetxattr", /* 230 */ "sys_fgetxattr", "sys_listxattr", "sys_llistxattr", "sys_flistxattr", "sys_removexattr", /* 235 */ "sys_lremovexattr", "sys_fremovexattr", "sys_tkill", "sys_sendfile64", "sys_futex", /* 240 */ "sys_sched_setaffinity", "sys_sched_getaffinity", "sys_set_thread_area", "sys_get_thread_area", "sys_io_setup", /* 245 */ "sys_io_destroy", "sys_io_getevents", "sys_io_submit", "sys_io_cancel", "sys_fadvise64", /* 250 */ "sys_ni_syscall", "sys_exit_group", "sys_lookup_dcookie", "sys_epoll_create", "sys_epoll_ctl", /* 255 */ "sys_epoll_wait", "sys_remap_file_pages", "sys_set_tid_address", "sys_timer_create", "sys_timer_settime", /* 260 */ "sys_timer_gettime", "sys_timer_getoverrun", "sys_timer_delete", "sys_clock_settime", "sys_clock_gettime", /* 265 */ "sys_clock_getres", "sys_clock_nanosleep", "sys_statfs64", "sys_fstatfs64", "sys_tgkill", /* 270 */ "sys_utimes", "sys_fadvise64_64", "sys_ni_syscall", /* sys_vserver */ "sys_mbind", "sys_get_mempolicy", "sys_set_mempolicy", "sys_mq_open", "sys_mq_unlink", "sys_mq_timedsend", "sys_mq_timedreceive", /* 280 */ "sys_mq_notify", "sys_mq_getsetattr", "sys_ni_syscall", /* reserved for kexec */ "sys_waitid", "sys_ni_syscall", /* 285 */ /* available */ "sys_add_key", "sys_request_key", "sys_keyctl", NULL }; struct { unsigned short limit; unsigned int base; } __attribute__ ((packed)) idtr; struct { unsigned short off1; unsigned short sel; unsigned char none,flags; unsigned short off2; } __attribute__ ((packed)) idt; // implementation of memmem since the kernel doesn't provide one for me // used to find the call (,eax,4) after the idt (something) is // the location of the sys_call_table void *memmem(const void* haystack, size_t hl, const void* needle, size_t nl) { int i; if (nl>hl) return 0; for (i=hl-nl+1; i; --i) { if (!memcmp(haystack,needle,nl)) return (char*)haystack; ++haystack; } return 0; } long find_system_call(void) { long sys_call_off = 0; // ask processor for interrupt discriptor table asm ("sidt %0" : "=m" (idtr)); /* read-in IDT for 0x80 vector (syscall) */ memcpy(&idt,(void *)idtr.base+8*0x80,sizeof(idt)); sys_call_off = (idt.off2 << 16) | idt.off1; return sys_call_off; } long find_sysenter_entry(void) { long l = 0, h = 0; rdmsr(MSR_IA32_SYSENTER_EIP, (unsigned long) l, (unsigned long) h); if (h) { printk("Woopsies.. Perhaps we're misusing rdmsr. l: 0x%lx, h: 0x%lx\n", l, h); } return l; /* I think this is correct.. I hope. */ } // Function will set sct to the address of the syscall table unsigned long **find_sys_call_table(long sc_asm) { char *p; /* we have syscall routine address now, look for syscall table dispatch (indirect call) */ p = (char*)memmem ((void *)sc_asm,READ_ASM,"\xff\x14\x85",3); if (p) return (unsigned long **)*(unsigned*)(p+3); else return NULL; } ssize_t user_read(struct file *unused_file, char *buffer, size_t len, loff_t *off) { unsigned long **sctable; int z; if (table == 0) { printk("User did not specify sys_call_table (use table=0xaddr from insmod)\n"); } else { sctable = (unsigned long **)table; printk("\nThe user specified table is: %p\n",&sctable[0]); printk("---------------------------------\n"); for(z=0;callz_2p6[z];z++) printk("%p T %s\n", sctable[z], callz_2p6[z]); } return 0; } ssize_t idt_read(struct file *unused_file, char *buffer, size_t len, loff_t *off) { long found_system_call = find_system_call(); unsigned long **sctable; int z; if (!system_call && found_system_call) { system_call = found_system_call; } else if(found_system_call != system_call) { printk("\nWARNING! The address you specified for system_call (%lx) is different from the one we detected (%lx)\n", system_call, found_system_call); } if (system_call) { sctable = find_sys_call_table(system_call); printk("\nThe IDT specified table is: %p\n", &sctable[0]); printk("-------------------------------------\n\n"); for(z=0;callz_2p6[z];z++) printk("%p T %s\n", sctable[z], callz_2p6[z]); printk("%p T %s\n", (void*)found_system_call, "system_call"); //printk("%p T %s\n", (void*)find_kbd_irq(), "i8042_kbd_irq"); } else { printk("Unable to read IDT. Try sysenter or specify the system_call symbol\n"); printk("using system_call=0xaddr from insmod.\n"); } return 0; } ssize_t sysenter_read(struct file *unused_file, char *buffer, size_t len, loff_t *off) { long found_se_entry = find_sysenter_entry(); unsigned long **sctable; int z; if (!se_entry && found_se_entry) { se_entry = found_se_entry; } else if(found_se_entry != se_entry) { printk("\nWARNING! The address you specified for sysenter_entry (%lx) is different from the one we detected (%lx)\n", se_entry, found_se_entry); } if (se_entry) { sctable = find_sys_call_table(se_entry); printk("\nThe sysenter specified table is: %p\n",&sctable[0]); printk("-------------------------------------\n\n"); for(z=0;callz_2p6[z];z++) printk("%p T %s\n", sctable[z], callz_2p6[z]); printk("%p T %s\n", (void*)found_se_entry, "sysenter_entry"); //printk("%p T %s\n", (void*)find_kbd_irq(), "i8042_kbd_irq"); } else { printk("Unable to read sysenter MSR. Try IDT or specify the sysenter_entry symbol\n"); printk("using se_entry=0xaddr from insmod.\n"); } return 0; } int my_atoi(char *str) { char *beg = str; int tot = 0; int pow = 1; while(*str) str++; if(str == beg) return -1; do { str--; if(*str < '0' || *str > '9') { return -1; } tot += (*str - '0')*pow; pow *= 10; } while(str != beg); return tot; } ssize_t common_write(struct file *file, const char __user *usr, size_t len, loff_t *off, unsigned long **sctable) { char *syscall = kmalloc(len+1, GFP_KERNEL); char *len_str; int call_len = 0; char *sc_asm = NULL; int i; if(copy_from_user(syscall, usr, len)) { printk("\nscprint: Error reading written data\n"); len = -EFAULT; goto out; } if(syscall[len-1] == '\n') { syscall[len-1] = 0; } syscall[len] = 0; if(!(len_str = strchr(syscall, ' '))) { printk("\nscprint: Usage: \n"); goto out; } *len_str = 0; len_str++; if((call_len = my_atoi(len_str)) == -1) { printk("\nscprint: must be base 10\n"); goto out; } for(i = 0; callz_2p6[i]; i++) { if(strcmp(syscall, callz_2p6[i]) == 0) { break; } } if(!callz_2p6[i]) { if(strcmp(syscall, "sysenter_entry") == 0) { sc_asm = (char*)se_entry; } else if(strcmp(syscall, "system_call") == 0) { sc_asm = (char*)system_call; } if(!sc_asm) { printk("scprint: syscall %s not found\n", syscall); goto out; } } else { sc_asm = (char *)sctable[i]; } printk("\nchar %s[] = \n\t\"", syscall); for(i=0; i < call_len; i++) { if(i%13 == 12) printk("\"\n\t\""); printk("\\x%x", sc_asm[i]&0xff); } printk("\";\n\n"); out: kfree(syscall); return len; } ssize_t user_write(struct file *file, const char __user *usr, size_t len, loff_t *off) { if (table == 0) { printk("User did not specify sys_call_table (use table=0xaddr from insmod)\n"); } else { return common_write(file, usr, len, off, (unsigned long **)table); } return -EINVAL; } ssize_t idt_write(struct file *file, const char __user *usr, size_t len, loff_t *off) { if (!system_call) { system_call = find_system_call(); } if (system_call) { return common_write(file, usr, len, off, find_sys_call_table(system_call)); } else { printk("Unable to read IDT. Try sysenter or specify the system_call symbol\n"); printk("using system_call=0xaddr from insmod.\n"); } return -EINVAL; } ssize_t sysenter_write(struct file *file, const char __user *usr, size_t len, loff_t *off) { if (!se_entry) { se_entry = find_sysenter_entry(); } if (se_entry) { return common_write(file, usr, len, off, find_sys_call_table(se_entry)); } else { printk("Unable to read sysenter MSR. Try IDT or specify the sysenter_entry symbol\n"); printk("using se_entry=0xaddr from insmod.\n"); } return -EINVAL; } static struct file_operations user_ops = { read: user_read, write: user_write }; static struct file_operations idt_ops = { read: idt_read, write: idt_write }; static struct file_operations sysent_ops = { read: sysenter_read, write: sysenter_write }; int init_module(void) { struct proc_dir_entry *entry; printk("\nSyscall auditor v0.2\n"); entry = create_proc_entry("user_syscall", S_IRUSR|S_IWUSR, &proc_root); entry->proc_fops = &user_ops; printk("Installed /proc/user_syscall\n"); entry = create_proc_entry("idt_syscall", S_IRUSR|S_IWUSR, &proc_root); entry->proc_fops = &idt_ops; printk("Installed /proc/idt_syscall\n"); entry = create_proc_entry("sysenter_syscall", S_IRUSR|S_IWUSR, &proc_root); entry->proc_fops = &sysent_ops; printk("Installed /proc/sysenter_syscall\n"); return 0; } void cleanup_module(void) { remove_proc_entry("sysenter_syscall", &proc_root); remove_proc_entry("idt_syscall", &proc_root); remove_proc_entry("user_syscall", &proc_root); return; } MODULE_LICENSE("GPL");