LKM linux_dirent实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LKM linux_dirent实现相关的知识,希望对你有一定的参考价值。

作为一项练习(安全研究),我正在尝试实现一个包装系统调用。根据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.
");
    size = (*k_getdents)(fd, dirp, count);
    printk("mylkm: bytes value returned by original getdents() call: %ld
", size);
    printk("mylkm: count value passed into original getdents() call: %i
", count);
    printk("mylkm: dirp pointer value: %p
", dirp);

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

        bpos += dir1->d_reclen;
    }

    printk("mylkm: Completed mylkm_getdents() call
");
    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: xffffffffxffffffffxffffffffxffffffffxffffffffxffffffff
[ 1389.397210] mylkm: offset to next dirent: 34034544
[ 1389.397211] mylkm: Completed mylkm_getdents() call

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

答案

解决方案是:

  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 ", 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 ", dir1_kernel->d_name); # Free kernel structure at the end of the loop kfree(dir1_kernel);

以上是关于LKM linux_dirent实现的主要内容,如果未能解决你的问题,请参考以下文章

如何交叉编译ARM版本的LKM?

centos系统无法编译lkm

如何用LKM挂断中断门

在 OS 中,为啥可加载内核模块 (LKM) 不需要调用消息传递来进行通信?

从 LKM 读取 ARM CPU 寄存器

linux lkm rootkit常用技巧