【问题标题】:LKM linux_dirent implementationLKM linux_dirent 实现
【发布时间】:2019-02-07 08:24:06
【问题描述】:

作为一个练习(安全研究),我正在尝试实现一个封装的系统调用。根据 getdents 文档 - 您必须实现自己的 linux_dirent 结构(https://linux.die.net/man/2/getdents64)。这一切似乎都有效,但我无法正确读取 linux_dirent 名称。

linux_dirent的实现:

struct mylkm_linux_dirent {
    u64            d_ino;
    s64            d_off;
    unsigned short d_reclen;
    char           d_name[];
};

包装的 getdents() 调用:

/*
This method will call the original and then sift through results.
Method will remove results that contain the specified keyword.
*/
asmlinkage long mylkm_getdents(unsigned int fd, struct mylkm_linux_dirent * dirp, unsigned int count)
{
    unsigned int size = 0;
    unsigned int bpos = 0;
    struct mylkm_linux_dirent* dir1 = NULL;
    char * buffer_ptr = NULL;
    char hide[] = "mylkm";

    printk("mylkm: getdents() called - redirecting to original sys call.\n");
    size = (*k_getdents)(fd, dirp, count);
    printk("mylkm: bytes value returned by original getdents() call: %ld\n", size);
    printk("mylkm: count value passed into original getdents() call: %i\n", count);
    printk("mylkm: dirp pointer value: %p\n", dirp);

    for (bpos = 0; bpos<size;)
    {
        buffer_ptr = (char*)(&dirp + bpos);
        dir1 = (struct mylkm_linux_dirent*)buffer_ptr;
        printk("mylkm: record length: %d\n", dir1->d_reclen);
        printk("mylkm: record name: %s\n", dir1->d_name);
        printk("mylkm: offset to next dirent: %lld\n", (long long)dir1->d_off);

        bpos += dir1->d_reclen;
    }

    printk("mylkm: Completed mylkm_getdents() call\n");
    return 0;
}

日志输出:

[ 1383.082213] mylkm: LKM v0.1 has been loaded.
[ 1389.397196] mylkm: getdents() called - redirecting to original sys call.
[ 1389.397207] mylkm: bytes value returned by original getdents() call: 496
[ 1389.397208] mylkm: count value passed into original getdents() call: 32768
[ 1389.397209] mylkm: dirp pointer value: 0000000002075370
[ 1389.397209] mylkm: record length: 65152
[ 1389.397210] mylkm: record name: \xffffffff\xffffffff\xffffffff\xffffffff\xffffffff\xffffffff
[ 1389.397210] mylkm: offset to next dirent: 34034544
[ 1389.397211] mylkm: Completed mylkm_getdents() call

我已尝试将 dir1->d_name 显式转换为 char 指针。将 char 指针传入原始调用会崩溃。如何获得正确的列表?

【问题讨论】:

  • buffer_ptr = (char*)(&amp;dirp + bpos); - 这是否曾经以dirp 作为指向结构的指针 进行编译,所以在结构中添加了一个数字?在右边你可能想要((char*)dirp) + bpos,它首先将指针转换为指向字符的指针,然后用它进行算术运算。
  • for 循环中添加dir1 指针的输出。并检查它是否与系统调用的dirp 参数一致(即,在第一次迭代时dir1 应等于dirp;在每次下一次迭代中dir1 应等于其先前的值加上先前的记录长度)。
  • @Tsyvarev 我同意...程序因 ((char*)dirp) + bpos 而崩溃。尝试将指针转换为 (struct mylkm_linux_dirent *) 时也是如此。
  • @Tsyvarev 我会检查的。我还将手动增加指针位置并尝试获取 reclen 和 name 变量(u long 为 4 个字节,u short 为 2 个字节)。
  • reclen = dirp + bpos + 8;名称 = dirp + bpos + 10; printk("mylkm: 记录长度: %d\n", reclen); printk("mylkm: 记录名称: %s\n", name); char * name 变量的打印输出崩溃。 reclen 成功了。

标签: c linux module kernel


【解决方案1】:

解决办法是:

  1. 使用正确的指针算法 获取dirp 数组的元素。例如

    buffer_ptr = ((char*)dirp) + bpos;
    dir1 = (struct mylkm_linux_dirent*)buffer_ptr;
    
  2. 通过copy_from_user 函数访问数组的元素,而不是直接访问。例如

    u64 reclen; # Record length will be stored here
    get_user(&reclen, &dir1->d_reclen); # TODO: check possible errors
    printk("mylkm: record length: %d\n", reclen);
    
    # Allocate a structure in the kernel
    struct mylkm_linux_dirent* dir1_kernel = kmalloc(reclen, GFP_KERNEL); # TODO: check possible errors
    # and copy user-space structure into it.
    copy_from_user(dir1_kernel, dir1, reclen); # TODO: check possible errors
    
    # Use dir1_kernel instead of dir
    printk("mylkm: record name: %s\n", dir1_kernel->d_name);
    
    # Free kernel structure at the end of the loop
    kfree(dir1_kernel);
    

【讨论】:

    猜你喜欢
    • 2012-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-23
    • 2015-11-05
    • 2012-02-27
    相关资源
    最近更新 更多