星闪夜空 发表于 2015-11-24 11:13

探索Linux内核NULL指针引发的BUG遇到的问题

    看到Godbach大神在博客上的《 Linux内核NULL指针引发的BUG》文章,由于刚学了一点内核皮毛,便想尝试一下,结果出了一点问题,希望能够得到大家的帮助。下面是我的尝试与出现的问题:

1、先编写一个简单的字符设备驱动chr_dev.c,如下所示
static struct cdev chr_dev;
static dev_t ndev;

static int chr_open(struct inode *nd, struct file *filp)
{
    int major = MAJOR(nd->i_rdev);
    int minor = MINOR(nd->i_rdev);

    printk("chr_open, major=%d, minor=%d\n", major, minor);

    return 0;
}

static ssize_t chr_read(struct file *f, char __user *u, size_t sz, loff_t *off)
{
    printk("kernel NULL pointer:%x", *(unsigned char *)0);
    printk("kernel NULL pointer:%x", *(unsigned char *)1);
    printk("kernel NULL pointer:%x", *(unsigned long *)2);   

    void (*p)();            
    p = NULL;
    (*p)();                     //在这里解引用空指针

    return 0;
}

struct file_operations chr_ops=
{
    .owner = THIS_MODULE,
    .open = chr_open,
    .read = chr_read,
};

static int demo_init(void)
{
    int ret;

    cdev_init(&chr_dev, &chr_ops);
    ret = alloc_chrdev_region(&ndev, 0, 1, "chr_dev");
    if(ret < 0)
      return ret;

    printk("demo_init():major=%d, minor=%d\n", MAJOR(ndev), MINOR(ndev));

    ret = cdev_add(&chr_dev, ndev, 1);
    if(ret < 0)
      return ret;

    return 0;
}

static void demo_exit(void)
{
    printk("Removing chr_dev module...\n");
    cdev_del(&chr_dev);
    unregister_chrdev_region(ndev, 1);
}

module_init(demo_init);
module_exit(demo_exit);

2、然后仿照写了一个testhole.c文件,用于映射0地址并指向null_func函数,另外触发调用字符驱动,如下所示:
#define BUFFSIZE 1024

void null_func(void)
{
    printf("aaaaaaaaaaaaaaaaaaaaaaa\n");
}

void test_chrdev()
{
    int fd;
    int n;
    char buf;

    fd = open("/dev/chr_dev", O_RDONLY);
    if(fd == -1)
    {
         perror("open error");
         exit(-1);
    }

    while((n = read(fd, buf, BUFFSIZE)) < 0)
    {
         perror("read error");
         exit(-1);
    }

    close(fd);
}

void map_and_call_null()
{
    char *addr;
   
    if((personality(MMAP_PAGE_ZERO)) != -1)
    {
      if((addr = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0)) == MAP_FAILED)
        {
          perror("mmap");
          return;
        }
    }
    else
    {
      perror("personality error");
      return;
    }
   
    *(unsigned char *)0 = '\x90';
    *(unsigned char *)1 = '\xe9';
    *(unsigned long *)2 = (unsigned long)&null_func - 6;
    printf("%x", *(unsigned long *)2);
   
    //void (*aaa)();
    //aaa = NULL;
    //(*aaa)();

    test_chrdev();
}

int main()
{
    map_and_call_null();

    return 0;
}


3、出现的问题
    运行testhole程序后,驱动程序并没有调用到null_func函数,而是产生了BUG: unable to handle kernel NULL pointer dereference at   (null)问题,我在驱动程序的chr_read中打印出地址0,1,2中的值,发现与map_and_call_null函数中赋予的值是一样的,为什么会出现这样的问题呢?我自己的猜想是不是内核态不能直接执行用户态的函数?希望大家不吝赐教!!!

Godbach 发表于 2015-11-24 14:25

回复 1# 星闪夜空

我当时也是根据已知的 NULL pointer 漏洞做的验证。 我印象中新的内核可能不让再对低地址段的内存 map 之类的操作。


   

星闪夜空 发表于 2015-11-24 14:39

回复 2# Godbach

    首先感谢Godbach大神的回复,我用的是Ubuntu12.04 内核版本是3.2.0的,默认情况下,系统是不能将0地址映射的,我是通过echo "0" > /proc/sys/vm/mmap_min_addr使系统能够将0地址映射。有下面两个问题请教:
1、你当时使用的内核版本是多少?
2、我对下面的三行代码还是搞不懂
   *(char *)0 = '\x90';
    *(char *)1 = '\xe9';
    *(unsigned long *)2 = (unsigned long)&kernel_code - 6;
    我只知道它们是将0地址指向kernel_code函数,但是不清楚为什么要这样做?我想自己这方面的知识有所欠缺,还望指明,谢谢了!!!


   

Godbach 发表于 2015-11-24 15:00

回复 3# 星闪夜空

我当时应该用的版本是 2.6.18 的内核吧。

还有相关的解释,应该帖子中也是有的。关于 NULL pointer 的exploit,九贱兄也有好文,你直接本版块搜一下。


   

星闪夜空 发表于 2015-11-24 15:16

回复 4# Godbach

    恩恩,找到了,谢谢提醒


   

nswcfd 发表于 2015-11-25 14:56

本帖最后由 nswcfd 于 2015-11-25 14:56 编辑

x86下, 0x90是nop,0xe9 xx xx xx xx是jmp

星闪夜空 发表于 2015-11-25 19:55

回复 6# nswcfd
    感谢nswcfd的解答,我也是看完godbach大神的博客然后才明白的

   
页: [1]
查看完整版本: 探索Linux内核NULL指针引发的BUG遇到的问题