#include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <asm/uaccess.h> #include <asm/fcntl.h> #include <asm/unistd.h> #include <asm/ia32_unistd.h> #include <asm/msr.h>
#define THIS_DESCRIPTION "Dynamically get sys_call_table/ia32_sys_call_table in kernel module.\n\t\tI don't think there would be anyone who won't enable CONFIG_IA32_EMULATION these days.\n\t\tso you can add the unessary check if you really want to." MODULE_DESCRIPTION( THIS_DESCRIPTION ); MODULE_AUTHOR("albcamus "); MODULE_LICENSE("GPL");
/** * TODO: if you want shut this up, comment the following line. */ #define BY_IDT_DEBUG
#ifdef BY_IDT_DEBUG #define dbgprint(format,args...) \ printk("get_syscall: function:%s-L%d: "format, __FUNCTION__, __LINE__, ## args); #else #define dbgprint(format,args...) do {} while(0); #endif
/** * sys call table */ void ** my_ia32_syscall_table; void ** my_syscall_table;
/** * 10 bytes -- please refer to AMD64 Architecture Programmer's * Manuals for more information. */ struct idtr { unsigned short limit; unsigned long base; //in 64bit mode, base address is 8 bytes
} __attribute__ ((packed));
/** * in long mode -- 64bit mode and compatity mode, * every IDT entry has a 16-byte size */ struct idt { u16 offset_low; u16 segment; unsigned ist : 3, zero0 : 5, type : 5, dpl :2, p : 1; u16 offset_middle; u32 offset_high; u32 zero1; } __attribute__ ((packed));
/** * Return the first appearence of NEEDLE in HAYSTACK. -- copied from PHRACK * */ static void *memmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len) {/*{{{*/ const char *begin; const char *const last_possible = (const char *) haystack + haystack_len - needle_len;
if (needle_len == 0) /* The first occurrence of the empty string is deemed to occur at the beginning of the string. */ return (void *) haystack;
/* Sanity check, otherwise the loop might search through the whole memory. */ if (__builtin_expect(haystack_len < needle_len, 0)) return NULL;
for (begin = (const char *) haystack; begin <= last_possible; ++begin) if (begin[0] == ((const char *) needle)[0] && !memcmp((const void *) &begin[1], (const void *) ((const char *) needle + 1), needle_len - 1)) return (void *) begin;
return NULL; }/*}}}*/
/** * Find the location of ia32_sys_call_table */ static unsigned long get_syscall_table_ia32(void) {/*{{{*/ #define OFFSET_SYSCALL 100 /* from system_call/ia32_syscall, we'll read first 150 bytes */
struct idtr idtr; struct idt idt;
unsigned long sys_call_off; unsigned long retval;
char sc_asm[OFFSET_SYSCALL], *p;
/* well, let's read IDTR */ asm("sidt %0" :"=m"(idtr) : );
dbgprint("idtr base at %p\n", (void *)idtr.base);
/** * Read in IDT for vector 0x80 (syscall) */ memcpy(&idt, (char *) idtr.base + 16 * 0x80, sizeof(idt)); sys_call_off = ( ( (unsigned long)idt.offset_high ) << 32 ) | ( ((idt.offset_middle << 16 ) | idt.offset_low) & 0x00000000ffffffff ); dbgprint("sys_call_off at %p\n", (void *)sys_call_off);
/* we have syscall routine address now, look for syscall table dispatch (indirect call) */ memcpy(sc_asm, (void *)sys_call_off, OFFSET_SYSCALL); /** * ia32_call > ia32_tracesys > ia32_do_syscall > 'call *ia32_sys_call_table(,%rax,8)' * Find callq *ia32_sys_call_table(,%rax,8) * * (gdb) disassemble ia32_syscall * Dump of assembler code for function ia32_syscall: * 0xffffffff81066b98 : swapgs * 0xffffffff81066b9b : sti * 0xffffffff81066b9c : mov %eax,%eax * 0xffffffff81066b9e : push %rax * 0xffffffff81066b9f : cld * 0xffffffff81066ba0 : sub $0x48,%rsp * 0xffffffff81066ba4 : mov %rdi,0x40(%rsp) * 0xffffffff81066ba9 : mov %rsi,0x38(%rsp) * 0xffffffff81066bae : mov %rdx,0x30(%rsp) * 0xffffffff81066bb3 : mov %rcx,0x28(%rsp) * 0xffffffff81066bb8 : mov %rax,0x20(%rsp) * 0xffffffff81066bbd : mov %gs:0x10,%r10 * 0xffffffff81066bc6 : sub $0x1fd8,%r10 * 0xffffffff81066bcd : orl $0x2,0x14(%r10) * 0xffffffff81066bd2 : testl $0x181,0x10(%r10) * 0xffffffff81066bda : jne 0xffffffff81066c04 * End of assembler dump. * (gdb) disassemble ia32_tracesys * Dump of assembler code for function ia32_tracesys: * 0xffffffff81066c04 : sub $0x30,%rsp * 0xffffffff81066c08 : mov %rbx,0x28(%rsp) * 0xffffffff81066c0d : mov %rbp,0x20(%rsp) * 0xffffffff81066c12 : mov %r12,0x18(%rsp) * 0xffffffff81066c17 : mov %r13,0x10(%rsp) * 0xffffffff81066c1c : mov %r14,0x8(%rsp) * 0xffffffff81066c21 : mov %r15,(%rsp) * 0xffffffff81066c25 : movq $0xffffffffffffffda,0x50(%rsp) * 0xffffffff81066c2e : mov %rsp,%rdi * 0xffffffff81066c31 : callq 0xffffffff81073a02 * 0xffffffff81066c36 : mov 0x30(%rsp),%r11 * 0xffffffff81066c3b : mov 0x38(%rsp),%r10 * 0xffffffff81066c40 : mov 0x40(%rsp),%r9 * 0xffffffff81066c45 : mov 0x48(%rsp),%r8 * 0xffffffff81066c4a : mov 0x58(%rsp),%rcx * 0xffffffff81066c4f : mov 0x60(%rsp),%rdx * 0xffffffff81066c54 : mov 0x68(%rsp),%rsi * 0xffffffff81066c59 : mov 0x70(%rsp),%rdi * 0xffffffff81066c5e : mov 0x78(%rsp),%rax * 0xffffffff81066c63 : mov (%rsp),%r15 * 0xffffffff81066c67 : mov 0x8(%rsp),%r14 * 0xffffffff81066c6c : mov 0x10(%rsp),%r13 * 0xffffffff81066c71 : mov 0x18(%rsp),%r12 * 0xffffffff81066c76 : mov 0x20(%rsp),%rbp * 0xffffffff81066c7b : mov 0x28(%rsp),%rbx * 0xffffffff81066c80 : add $0x30,%rsp * 0xffffffff81066c84 : jmpq 0xffffffff81066bdc * End of assembler dump. * (gdb) disassemble ia32_do_syscall * Dump of assembler code for function ia32_do_syscall: * 0xffffffff81066bdc : cmp $0x13d,%eax * 0xffffffff81066be1 : ja 0xffffffff81066c89 * 0xffffffff81066be7 : mov %edi,%r8d * 0xffffffff81066bea : mov %ebp,%r9d * 0xffffffff81066bed : xchg %ecx,%esi * 0xffffffff81066bef : mov %ebx,%edi * 0xffffffff81066bf1 : mov %edx,%edx * 0xffffffff81066bf3 : callq *0xffffffff812e7c70(,%rax,8) * End of assembler dump. * (gdb) x/xw ia32_do_syscall+23 * 0xffffffff81066bf3 : 0x70c514ff * (gdb) * */ p = (char *) memmem(sc_asm, OFFSET_SYSCALL, "\xff\x14\xc5", 3); if (p == NULL) { printk("opcode not found, meats that we cannot find sys_call_table.\n"); return 0; }
retval = *(unsigned long *) (p + 3); return retval; #undef OFFSET_SYSCALL }/*}}}*/
/** * Find the location of long-mode sys_call_table */ static unsigned long get_syscall_table_long(void) {/*{{{*/ #define OFFSET_SYSCALL 150 unsigned long syscall_long, retval; char sc_asm[OFFSET_SYSCALL];
rdmsrl(MSR_LSTAR, syscall_long); dbgprint("long mode: system_call is at %p\n", (void *)syscall_long);
memcpy(sc_asm, (char *)syscall_long, OFFSET_SYSCALL);
/** * Find callq *sys_call_table(,%rax,8) * ------------------------------------- * (gdb) disassemble system_call * Dump of assembler code for function system_call: * 0xffffffff81063750 : swapgs * 0xffffffff81063753 : mov %rsp,%gs:0x18 * 0xffffffff8106375c : mov %gs:0x10,%rsp * 0xffffffff81063765 : sti * 0xffffffff81063766 : sub $0x50,%rsp * 0xffffffff8106376a : mov %rdi,0x40(%rsp) * 0xffffffff8106376f : mov %rsi,0x38(%rsp) * 0xffffffff81063774 : mov %rdx,0x30(%rsp) * 0xffffffff81063779 : mov %rax,0x20(%rsp) * 0xffffffff8106377e : mov %r8,0x18(%rsp) * 0xffffffff81063783 : mov %r9,0x10(%rsp) * 0xffffffff81063788 : mov %r10,0x8(%rsp) * 0xffffffff8106378d : mov %r11,(%rsp) * 0xffffffff81063791 : mov %rax,0x48(%rsp) * 0xffffffff81063796 : mov %rcx,0x50(%rsp) * 0xffffffff8106379b : mov %gs:0x10,%rcx * 0xffffffff810637a4 : sub $0x1fd8,%rcx * 0xffffffff810637ab : testl $0x181,0x10(%rcx) * 0xffffffff810637b2 : jne 0xffffffff81063889 * 0xffffffff810637b8 : cmp $0x117,%rax * 0xffffffff810637be : ja 0xffffffff8106387b * 0xffffffff810637c4 : mov %r10,%rcx * 0xffffffff810637c7 : callq *0xffffffff812e6d60(,%rax,8) * 0xffffffff810637ce : mov %rax,0x20(%rsp) * End of assembler dump. * (gdb) x/xw system_call+119 * 0xffffffff810637c7 : 0x60c514ff * (gdb) */ retval = (unsigned long) memmem(sc_asm, OFFSET_SYSCALL, "\xff\x14\xc5", 3); if ( retval != 0 ) { dbgprint("long mode : sys_call_table is at %p\n", (void *) (* (unsigned long *)(retval+3)) ) ; retval = (unsigned long) ( * (unsigned long *)(retval+3) ); } else { printk("long mode : memmem found nothing, returning NULL:( \n"); retval = 0; }
#undef OFFSET_SYSCALL return retval; }/*}}}*/
static int get_syscall_init_module(void) {/*{{{*/ printk(KERN_DEBUG "get_syscall: Hi, you fucking linux!\n");
my_ia32_syscall_table = (void **) get_syscall_table_ia32(); if (my_ia32_syscall_table == 0) return -1;
my_syscall_table = (void **) get_syscall_table_long(); if (my_syscall_table == 0) return -1;
dbgprint("ia32_sys_call_table address : %p\n", (void *) my_ia32_syscall_table); dbgprint("long mode : sys_call_table address : %p\n", (void *) my_syscall_table);
#define REPLACE_IA32(x) o_sys_##x = my_ia32_syscall_table[__NR_ia32_##x];\ my_ia32_syscall_table[__NR_ia32_##x] = my_sys_##x
#define GET_OLD_IA32_SYSCALL_ADDR(x) my_ia32_syscall_table[__NR_ia32_##x] // dbgprint("sys_read at %p, my_read at %p\n", (void *)GET_OLD_IA32_SYSCALL_ADDR(read), (void *)my_sys_read);
// REPLACE_IA32(read);
#undef REPLACE_IA32
return 0; }/*}}}*/
static void get_syscall_exit_module(void) {/*{{{*/ printk(KERN_DEBUG "get_syscall: bye, you fucking linux!\n");
#define RESTORE_IA32(x) my_ia32_syscall_table[__NR_ia32_##x] = o_sys_##x
// RESTORE_IA32(read);
#undef RESTORE_IA32 }/*}}}*/
module_init(get_syscall_init_module); module_exit(get_syscall_exit_module);
|