Insmod:模块问题中的未知符号

时间:2017-04-04 03:01:38

标签: c linux linux-kernel kernel-module

我正在编写一个LKM来查看文件的扩展属性,以确定是否应该打开它。我有一个insmode:模块问题中的未知符号。我找不到导致这种情况的原因。模块如下:

#include <linux/module.h>  /* Needed by all kernel modules */
#include <linux/kernel.h>  /* Needed for loglevels (KERN_WARNING, KERN_EMERG, KERN_INFO, etc.) */
#include <linux/init.h>    /* Needed for __init and __exit macros. */
#include <linux/unistd.h>  /* sys_call_table __NR_* system call function indices */
#include <linux/fs.h>      /* filp_open */
#include <linux/slab.h>    /* kmalloc */
#include <asm/paravirt.h> /* write_cr0 */
#include <asm/uaccess.h>  /* get_fs, set_fs */
#include <linux/xattr.h>
#include <linux/dcache.h> /*dentry*/


#define PROC_V    "/proc/version"
#define BOOT_PATH "/boot/System.map-"
#define MAX_VERSION_LEN   256


//https://tnichols.org/2015/10/19/Hooking-the-Linux-System-Call-Table/



unsigned long *syscall_table = NULL;
//unsigned long *syscall_table = (unsigned long *)0xffffffff81801400;
asmlinkage int (*original_open)(const char*, int flags, mode_t);
static int find_sys_call_table (char *kern_ver) {
    char system_map_entry[MAX_VERSION_LEN];
    int i = 0;
    /*
     * Holds the /boot/System.map-<version> file name as we build it
     */
    char *filename;
    /*
     * Length of the System.map filename, terminating NULL included
     */
    size_t filename_length = strlen(kern_ver) + strlen(BOOT_PATH) + 1;
    /*
     * This will point to our /boot/System.map-<version> file
     */
    struct file *f = NULL;

    mm_segment_t oldfs;

    oldfs = get_fs();
    set_fs (KERNEL_DS);
    printk(KERN_EMERG "Kernel version: %s\n", kern_ver);

    filename = kmalloc(filename_length, GFP_KERNEL);
    if (filename == NULL) {
        printk(KERN_EMERG "kmalloc failed on System.map-<version> filename allocation");
        return -1;
    }

    /*
     * Zero out memory to be safe
     */
    memset(filename, 0, filename_length);

    /*
     * Construct our /boot/System.map-<version> file name
     */
    strncpy(filename, BOOT_PATH, strlen(BOOT_PATH));
    strncat(filename, kern_ver, strlen(kern_ver));

    /*
     * Open the System.map file for reading
     */
    f = filp_open(filename, O_RDONLY, 0);
    if (IS_ERR(f) || (f == NULL)) {
        printk(KERN_EMERG "Error opening System.map-<version> file: %s\n", filename);
        return -1;
    }

    memset(system_map_entry, 0, MAX_VERSION_LEN);

    /*
     * Read one byte at a time from the file until we either max out
     * out our buffer or read an entire line.
     */
    while (vfs_read(f, system_map_entry + i, 1, &f->f_pos) == 1) {
        /*
         * If we've read an entire line or maxed out our buffer,
         * check to see if we've just read the sys_call_table entry.
         */
        if ( system_map_entry[i] == '\n' || i == MAX_VERSION_LEN ) {
            // Reset the "column"/"character" counter for the row
            i = 0;

            if (strstr(system_map_entry, "sys_call_table") != NULL) {
                char *sys_string;
                char *system_map_entry_ptr = system_map_entry;

                sys_string = kmalloc(MAX_VERSION_LEN, GFP_KERNEL);  
                if (sys_string == NULL) {
                    filp_close(f, 0);
                    set_fs(oldfs);
                    kfree(filename);

                    return -1;
                }

                memset(sys_string, 0, MAX_VERSION_LEN);
                strncpy(sys_string, strsep(&system_map_entry_ptr, " "), MAX_VERSION_LEN);

                //syscall_table = (unsigned long long *) kstrtoll(sys_string, NULL, 16);
                //syscall_table = kmalloc(sizeof(unsigned long *), GFP_KERNEL);
                //syscall_table = kmalloc(sizeof(syscall_table), GFP_KERNEL);
                kstrtoul(sys_string, 16, &syscall_table);
                printk(KERN_EMERG "syscall_table retrieved\n");

                kfree(sys_string);

                break;
            }

            memset(system_map_entry, 0, MAX_VERSION_LEN);
            continue;
        }

        i++;
    }

    filp_close(f, 0);
    set_fs(oldfs);

    kfree(filename);

    return 0;
}
/*
 * We have to pass in a pointer to a buffer to store the parsed
 * version information in. If we declare a pointer to the
 * parsed version info on the stack of this function, the
 * pointer will disappear when the function ends and the
 * stack frame is removed.
 */
char *acquire_kernel_version (char *buf) {
    struct file *proc_version;
    char *kernel_version;

    /*
     * We use this to store the userspace perspective of the filesystem
     * so we can switch back to it after we are done reading the file
     * into kernel memory
     */
    mm_segment_t oldfs;

    /*
     * Standard trick for reading a file into kernel space
     * This is very bad practice. We're only doing it here because
     * we're malicious and don't give a damn about best practices.
     */
    oldfs = get_fs();
    set_fs (KERNEL_DS);

    /*
     * Open the version file in the /proc virtual filesystem
     */
    proc_version = filp_open(PROC_V, O_RDONLY, 0);
    if (IS_ERR(proc_version) || (proc_version == NULL)) {
        return NULL;
    }

    /*
     * Zero out memory just to be safe
     */
    memset(buf, 0, MAX_VERSION_LEN);

    /*
     * Read version info from /proc virtual filesystem
     */
    vfs_read(proc_version, buf, MAX_VERSION_LEN, &(proc_version->f_pos));

    /*
     * Extract the third field from the full version string
     */
    kernel_version = strsep(&buf, " ");
    kernel_version = strsep(&buf, " ");
    kernel_version = strsep(&buf, " ");

    filp_close(proc_version, 0);

    /*
     * Switch filesystem context back to user space mode
     */
    set_fs(oldfs);

    return kernel_version;
}
asmlinkage int new_open (const char* path, int flags, mode_t mode) {
    //printk(KERN_EMERG "[+] open() hooked.");

    char *start = "root/home/";

    if(strncmp(start,path,sizeof(char)*10)){

        mm_segment_t oldfs;
        struct file *f;
        int val;
        struct dentry *dentry;

        oldfs = get_fs();
        set_fs (KERNEL_DS);
        f = filp_open(path, O_RDONLY, 0);
        if(f){
            dentry = f->f_path.dentry;
            filp_close(f, 0);
            val = vfs_getxattr(dentry, "user.lock", NULL, 0);
            //printk(KERN_EMERG "%d %s\n", val, path);
            if(val != 0) return -1;
        }

        set_fs(oldfs);
    }
    return original_open(path, flags, mode);
}


static int __init onload(void) {
    char *kernel_version = kmalloc(MAX_VERSION_LEN, GFP_KERNEL);
    printk(KERN_WARNING "Hello world!\n");
    // printk(KERN_EMERG "Version: %s\n", acquire_kernel_version(kernel_version));

    find_sys_call_table(acquire_kernel_version(kernel_version));

    printk(KERN_EMERG "Syscall table address: %p\n", syscall_table);
    printk(KERN_EMERG "sizeof(unsigned long *): %zx\n", sizeof(unsigned long*));
    printk(KERN_EMERG "sizeof(sys_call_table) : %zx\n", sizeof(syscall_table));

    if (syscall_table != NULL) {
        write_cr0 (read_cr0 () & (~ 0x10000));
        original_open = (void *)syscall_table[__NR_open];
        syscall_table[__NR_open] = &new_open;
        write_cr0 (read_cr0 () | 0x10000);
        printk(KERN_EMERG "[+] onload: sys_call_table hooked\n");
    } else {
        printk(KERN_EMERG "[-] onload: syscall_table is NULL\n");
    }

    kfree(kernel_version);

    /*
     * A non 0 return means init_module failed; module can't be loaded.
     */
    return 0;
}
static void __exit onunload(void) {
    if (syscall_table != NULL) {
        write_cr0 (read_cr0 () & (~ 0x10000));
        syscall_table[__NR_open] = original_open;
        write_cr0 (read_cr0 () | 0x10000);
        printk(KERN_EMERG "[+] onunload: sys_call_table unhooked\n");
    } else {
        printk(KERN_EMERG "[-] onunload: syscall_table is NULL\n");
    }
    printk(KERN_INFO "Goodbye world!\n");
}
module_init(onload);
module_exit(onunload);

当我评论val = vfs_getxattr(dentry, "user.lock", NULL, 0);(在new_open()中)时,它会安装。我无法弄清楚是什么导致了这个问题,任何想法都会张开双臂欢迎!

1 个答案:

答案 0 :(得分:2)

符号vfs_getxattr仅针对GPL模块导出 (使用EXPORT_SYMBOL_GPL)。

你需要

MODULE_LICENSE("GPL");

在模块中使用此类符号。

最好在每个驱动程序中都有MODULE_LICENSE();,无论是“普通公共许可证”还是专有驱动程序。