我正在编写一个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()
中)时,它会安装。我无法弄清楚是什么导致了这个问题,任何想法都会张开双臂欢迎!
答案 0 :(得分:2)
符号vfs_getxattr
仅针对GPL模块导出 (使用EXPORT_SYMBOL_GPL
)。
你需要
MODULE_LICENSE("GPL");
在模块中使用此类符号。
最好在每个驱动程序中都有MODULE_LICENSE();
,无论是“普通公共许可证”还是专有驱动程序。