赞
踩
在网上看到很多文章是通过驱动内联汇编拿到idt地址,但是在x86_64上确跑不起来,还宕机,来一起唠唠。
先说说网上同行说自己使用以下代码去解析sys_call_table 地址
lkm.c
//lkm.c #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/unistd.h> #include <linux/sched.h> #include <linux/kallsyms.h> #include <asm/cacheflush.h> #include <asm/page.h> #include <asm/current.h> unsigned long *sys_call_table; struct { unsigned short size; unsigned int addr; }__attribute__((packed)) idtr; struct { unsigned short offset_1; unsigned short selector; unsigned char zero; unsigned char type_attr; unsigned short offset_2; }__attribute__((packed)) idt; unsigned long *find_sys_call_table(void) { unsigned int sys_call_off; char *p; int i; unsigned int ret; asm("sidt %0":"=m"(idtr)); //通过idtr获取IDT表的地址 printk("Arciryas:idt table-0x%x\n", idtr.addr); //将0x80处sys_call_table的地址存放到idt中 memcpy(&idt, idtr.addr+8*0x80, sizeof(idt));//IDT表一行占8个字节,所以8*0x80 sys_call_off = ((idt.offset_2<<16) | idt.offset_1); p = sys_call_off; for(i=0; i<100; i++) { if(p[i]=='\xff' && p[i+1]=='\x14' && p[i+2]=='\x85') //sys_call_table的call硬编码 ret = *(unsigned int *)(p+i+3); } printk("Arciryas:sys_call_table-0x%x\n", ret); return (unsigned long**)ret; } asmlinkage long (*real_mkdir)(const char __user *pathname,umode_t mode); asmlinkage long fake_mkdir(const char __user *pathname, umode_t mode) { printk("Arciryas:mkdir-%s\n", pathname); return (*real_mkdir)(pathname, mode); } static int lkm_init(void) { sys_call_table = find_sys_call_table(); write_cr0(read_cr0() & (~0x10000)); real_mkdir = (void *)sys_call_table[__NR_mkdir]; sys_call_table[__NR_mkdir] = fake_mkdir; write_cr0(read_cr0() | 0x10000); printk("Arciryas:module loaded\n"); return 0; } static void lkm_exit(void) { write_cr0(read_cr0() & (~0x10000)); sys_call_table[__NR_mkdir] = real_mkdir; write_cr0(read_cr0() | 0x10000); printk("Arciryas:module removed\n"); } module_init(lkm_init); module_exit(lkm_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("xxxxxx"); MODULE_DESCRIPTION("hook mkdir");
这个程序会直接让系统宕机的原因是因为这个结构体对addr的赋int,在64位系统应该改成long,这里仅仅是解决代码执行内联汇编asm宕机问题,除了此处修改,相应的变量也应该修改成Long型,改完之后代码可以跑,但是得到的结果确是错误!
struct
{
unsigned short size;
unsigned int addr; // 高32位为idt表地址
}__attribute__((packed)) idtr; // idtr是48位6字节寄存器
//lkm.c #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/unistd.h> #include <linux/sched.h> #include <linux/kallsyms.h> #include <asm/cacheflush.h> #include <asm/page.h> #include <asm/current.h> unsigned long *sys_call_table; struct { unsigned short size; unsigned long addr; }__attribute__((packed)) idtr; struct { unsigned short offset_1; unsigned short selector; unsigned char zero; unsigned char type_attr; unsigned short offset_2; }__attribute__((packed)) idt; unsigned long *find_sys_call_table(void) { unsigned long sys_call_off; long *p; int i; unsigned long ret; asm("sidt %0":"=m"(idtr)); //通过idtr获取IDT表的地址 printk("Arciryas:idt table-0x%lx\n", idtr.addr); //将0x80处sys_call_table的地址存放到idt中 memcpy(&idt, idtr.addr+8*0x80, sizeof(idt));//IDT表一行占8个字节,所以8*0x80 sys_call_off = ((idt.offset_2<<16) | idt.offset_1); p = sys_call_off; for(i=0; i<100; i++) { if(p[i]=='\xff' && p[i+1]=='\x14' && p[i+2]=='\x85') //sys_call_table的call硬编码 ret = *(unsigned long *)(p+i+3); } printk("Arciryas:sys_call_table-0x%lx\n", ret); return (unsigned long**)ret; } asmlinkage long (*real_mkdir)(const char __user *pathname,umode_t mode); asmlinkage long fake_mkdir(const char __user *pathname, umode_t mode) { printk("Arciryas:mkdir-%s\n", pathname); return (*real_mkdir)(pathname, mode); } static int lkm_init(void) { sys_call_table = find_sys_call_table(); write_cr0(read_cr0() & (~0x10000)); real_mkdir = (void *)sys_call_table[__NR_mkdir]; sys_call_table[__NR_mkdir] = fake_mkdir; write_cr0(read_cr0() | 0x10000); printk("Arciryas:module loaded\n"); return 0; } static void lkm_exit(void) { write_cr0(read_cr0() & (~0x10000)); sys_call_table[__NR_mkdir] = real_mkdir; write_cr0(read_cr0() | 0x10000); printk("Arciryas:module removed\n"); } module_init(lkm_init); module_exit(lkm_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("xxxxxx"); MODULE_DESCRIPTION("hook mkdir");
其实也没有改什么,只是把所有变量类型改为long型。
看下面结果还是错误!!!!
curtis@curtis-virtual-machine:~/Desktop/fail$ uname -a
Linux curtis-virtual-machine 4.2.0-42-generic #49~14.04.1-Ubuntu SMP Wed Jun 29 20:22:11 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
dmesg查看打印信息和从/proc/kallsyms抓出的数据进行比对,验证数据的正确性。
curtis@curtis-virtual-machine:~/Desktop/fail$ sudo cat /proc/kallsyms | grep idt_table
ffffffff81eb5000 B idt_table
ffffffff81eb6000 B debug_idt_table
ffffffff81eb8000 B trace_idt_table
curtis@curtis-virtual-machine:~/Desktop/fail$ sudo cat /proc/kallsyms | grep sys_call_table
ffffffff818001c0 R sys_call_table
ffffffff81801580 R ia32_sys_call_table
curtis@curtis-virtual-machine:~/Desktop/fail$
驱动安装成功,也不会宕机,但是抓到的数据是有问题的!!!!!
curtis@curtis-virtual-machine:~/Desktop/fail$ sudo insmod lkm.ko
curtis@curtis-virtual-machine:~/Desktop/fail$ dmesg | tail -n 3
[ 3749.769517] Arciryas:idt table-0xffffffffff578000
[ 3749.769520] Arciryas:sys_call_table-0xffffffff81c15080
[ 3749.769523] Arciryas:module loaded
curtis@curtis-virtual-machine:~/Desktop/fail$
主要问题在以下两点:
1.x86_64系统获得idtr是通过读寄存器的值得来的;
2.x86_64系统对call的硬编码也是不一样的"\xff\x14\xc5"
修改之后代码以及makefile:
get_sct.c
#include <linux/module.h> #include <linux/kernel.h> unsigned long *sys_call_table; struct { unsigned short limit; unsigned long base; } __attribute__ ((packed))idtr; struct { unsigned short off1; unsigned short sel; unsigned char none, flags; unsigned short off2; } __attribute__ ((packed))idt; void *memmem ( const void *haystack, size_t haystack_size, const void *needle, size_t needle_size ) { char *p; for ( p = (char *)haystack; p <= ((char *)haystack - needle_size + haystack_size); p++ ) if ( memcmp(p, needle, needle_size) == 0 ) return (void *)p; return NULL; } // http://bbs.chinaunix.net/thread-2143235-1-1.html unsigned long *find_sys_call_table ( void ) { char **p; unsigned long sct_off = 0; unsigned char code[512]; rdmsrl(MSR_LSTAR, sct_off); memcpy(code, (void *)sct_off, sizeof(code)); p = (char **)memmem(code, sizeof(code), "\xff\x14\xc5", 3); if ( p ) { unsigned long *sct = *(unsigned long **)((char *)p + 3); // Stupid compiler doesn't want to do bitwise math on pointers sct = (unsigned long *)(((unsigned long)sct & 0xffffffff) | 0xffffffff00000000); return sct; } else return NULL; } // 模块载入时被调用 static int __init init_get_sys_call_table(void) { sys_call_table = find_sys_call_table(); printk("The sys_call_table address is:%lx\n",(unsigned long)sys_call_table); return 0; } // 模块卸载时被调用 static void __exit exit_get_sys_call_table(void) { printk("Get sys_call_table finish!\n"); } module_init(init_get_sys_call_table); module_exit(exit_get_sys_call_table); // 模块信息 MODULE_LICENSE("GPL2.0"); MODULE_AUTHOR("curits");
Makefile:
obj-m += get_sct.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
输出结果:
curtis@curtis-virtual-machine:~/Desktop/get_system_call_x86_64$ ./auto.sh #########make file########## make -C /lib/modules/4.2.0-42-generic/build M=/home/curtis/Desktop/get_system_call_x86_64 modules make[1]: Entering directory `/usr/src/linux-headers-4.2.0-42-generic' Building modules, stage 2. MODPOST 1 modules make[1]: Leaving directory `/usr/src/linux-headers-4.2.0-42-generic' #########insmod ko file######## #########rmmod ko file######### ##########make clean########### make -C /lib/modules/4.2.0-42-generic/build M=/home/curtis/Desktop/get_system_call_x86_64 clean make[1]: Entering directory `/usr/src/linux-headers-4.2.0-42-generic' CLEAN /home/curtis/Desktop/get_system_call_x86_64/.tmp_versions CLEAN /home/curtis/Desktop/get_system_call_x86_64/Module.symvers make[1]: Leaving directory `/usr/src/linux-headers-4.2.0-42-generic' ##########dmesg info########### [ 4033.613719] Arciryas:module removed [ 4044.225868] get_sct: module license 'GPL2.0' taints kernel. [ 4044.225871] Disabling lock debugging due to kernel taint [ 4044.226165] The sys_call_table address is:ffffffff818001c0 [ 4045.244574] Get sys_call_table finish! curtis@curtis-virtual-machine:~/Desktop/get_system_call_x86_64$ sudo cat /proc/kallsyms | grep sys_call_table ffffffff818001c0 R sys_call_table ffffffff81801580 R ia32_sys_call_table curtis@curtis-virtual-machine:~/Desktop/get_system_call_x86_64$
成功拿到x86_64系统sys_call_table!!!!!!!!!!!!
这是因为在高版本内核中系统调用的方式发生了改变,如果要进行系统调用,则必须将所需的系统调用号存储到rax寄存器中,然后使用软件中断调用内核syscall。在使用中断之前,系统调用所需的所有参数都必须加载到某些寄存器中,并且返回值几乎总是放在rax。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。