当前位置: 首页 > 工具软件 > Stupid Table > 使用案例 >

抓取sys_call_table 地址 x86_64

端木权
2023-12-01

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

 类似资料: