Reputation: 1
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:
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];
Where's my mistake? Did something change for newer kernels?
You'll notice I left two methods for disabling memory read-only protection, are both suitable for newer 5.X 32-bit kernels?
How would one go about obtaining the sys_call_table dynamically
within the code, what would be used as reference? I haven't been able
to find information on the exported sys_calls for newer versions. I found sys_close
being used often, but the compiler doesn't recognize it and suggests the use of ksys_close
instead.
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
Reputation: 58888
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