在网上看到很多文章是通过驱动内联汇编拿到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。