Sali
Sali

Reputation: 1

lvalue required as left operand of assignment - function pointer

I'm working on homework for kernel programming and memory management classes. I need to dynamically obtain the sys_call_table address and install a simple hook, which calls the original sys_call (in this case sys_read) right after printing some text.

To begin with, I hard-coded the sys_call_table address I got using grep and /boot/System.map and followed some code examples I found online:

  1. https://gajastechnologies.blogspot.com/2016/12/basics-of-making-rootkit-from-syscall.html
  2. https://syprog.blogspot.com/2011/10/hijack-linux-system-calls-part-iii.html

The posts offer different takes on the sys_call_table addresses, namely one uses a single pointer, while the other uses a double. However, in both cases I'm getting the error lvalue required as left operand of assignment for the assignment original_read = table[__NR_read];

Code:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/syscalls.h>
#include <linux/kallsyms.h>
#include <linux/compat.h>
#include <asm/uaccess.h>
#include <uapi/asm/unistd.h>

#include <asm/cacheflush.h>
#include <linux/highmem.h>
#include <asm/pgtable_types.h>

#define DISABLE_W_PROTECTED_MEMORY \
    preempt_disable(); \
    barrier(); \
    write_cr0(read_cr0() & (~ 0x00010000));

#define ENABLE_W_PROTECTED_MEMORY \
    write_cr0(read_cr0() | 0x00010000); \
    barrier(); \
    preempt_enable();

MODULE_LICENSE("GPL");

asmlinkage long *original_read(unsigned int fd, char __user *buf, size_t count);
asmlinkage long modified_read(unsigned int fd, char __user *buf, size_t count) {
    pr_info("sys_read\n");
    return original_read(fd, buf, count);
}

void **table = (void *)0xc16e7180;

/*Make page writeable*/

int make_rw(unsigned long address){
    unsigned int level;
    pte_t *pte = lookup_address(address, &level);
    if(pte->pte &~_PAGE_RW){
        pte->pte |=_PAGE_RW;
    }
    return 0;
}

/* Make the page write protected */

int make_ro(unsigned long address){
    unsigned int level;
    pte_t *pte = lookup_address(address, &level);
    pte->pte = pte->pte &~_PAGE_RW;
    return 0;
}

static int __init mod_init(void)
{
    make_rw((unsigned long)table);
    original_read = table[__NR_read];
    table[__NR_read] = modified_read;
    return 0;
}
static void __exit mod_exit(void)
{
    table[__NR_read] = original_read;
    make_ro((unsigned long)table);
}

module_init(mod_init);
module_exit(mod_exit);

EDIT:

I applied the fix to the function pointer and edited the code to match the one presented in the second link. The print from within modified_read never comes up in dmesg despite running some example C code with read(). Also, if I remove the macros for write protected memory (and leave the make_ro, make_rw), the code crashes on insmod. I left both there, for reference. Any suggestions?

#define DISABLE_W_PROTECTED_MEMORY \
    preempt_disable(); \
    barrier(); \
    write_cr0(read_cr0() & (~ 0x00010000));

#define ENABLE_W_PROTECTED_MEMORY \
    write_cr0(read_cr0() | 0x00010000); \
    barrier(); \
    preempt_enable();

asmlinkage long (*original_read)(unsigned int fd, char __user *buf, size_t count);
asmlinkage long modified_read(unsigned int fd, char __user *buf, size_t count) {
    pr_info("sys_read\n");
    return original_read(fd, buf, count);
}

unsigned long *table = (unsigned long*)0xc16e7180;

int make_rw(unsigned long address){
    unsigned int level;
    pte_t *pte = lookup_address(address, &level);
    if(pte->pte &~_PAGE_RW){
        pte->pte |=_PAGE_RW;
    }
    return 0;
}

int make_ro(unsigned long address){
    unsigned int level;
    pte_t *pte = lookup_address(address, &level);
    pte->pte = pte->pte &~_PAGE_RW;
    return 0;
}

static int __init mod_init(void)
{
    DISABLE_W_PROTECTED_MEMORY
    make_rw((unsigned long)table);
    original_read = (void*)*(table + __NR_read);
    *(table + __NR_read) = (unsigned long) modified_read;
    make_ro((unsigned long)table);
    ENABLE_W_PROTECTED_MEMORY
    return 0;
}
static void __exit mod_exit(void)
{
    DISABLE_W_PROTECTED_MEMORY
    make_rw((unsigned long)table);
    *(table + __NR_read) = (unsigned long) original_read;
    make_ro((unsigned long)table);
    ENABLE_W_PROTECTED_MEMORY
}

module_init(mod_init);
module_exit(mod_exit);

Upvotes: 0

Views: 201

Answers (1)

original_read is a function. You cannot assign to a function. For example, you couldn't do printf = something;

It seems that you meant for original_read to be a function pointer. To declare a function pointer, you need an extra set of parentheses:

    asmlinkage long (*original_read)(unsigned int fd, char __user *buf, size_t count);
                    ^              ^

Upvotes: 3

Related Questions