我正在为内核编程和内存管理类编写家庭作业。我需要动态获取sys_call_table
地址并安装一个简单的钩子,该钩子在打印一些文本后立即调用原始的sys_call(在这种情况下为sys_read
)。
首先,我使用grep和/boot/System.map对获得的sys_call_table
地址进行了硬编码,并遵循了一些我在网上找到的代码示例:
这些帖子对sys_call_table地址有不同的看法,即一个使用单个指针,而另一个使用双指针。但是,在两种情况下,我都得到分配lvalue required as left operand of assignment
的错误original_read = table[__NR_read];
我的错误在哪里?较新的内核有什么变化吗?
您会注意到我留下了两种禁用只读存储器的方法保护,都适合较新的5.X 32位内核?
如何动态获取sys_call_table在代码中,什么将用作参考?我没能力在较新版本的导出的sys_calls上查找信息。我发现经常使用sys_close
,但是编译器无法识别它,建议改用ksys_close
。
代码:
#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);
编辑:
我将修复程序应用于函数指针,并编辑了代码以匹配第二个链接中显示的代码。尽管在modified_read
中运行了一些示例C代码,但dmesg
中的打印从未出现在read()
中。另外,如果删除用于写保护内存的宏(并保留make_ro
,make_rw
),则代码在insmod上崩溃。我把两者都留在那里,以供参考。有什么建议吗?
#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);
original_read
是一个函数。您不能分配给功能。例如,您无法执行printf = something;
[您似乎想让original_read
成为函数指针。要声明函数指针,您需要额外的一组括号:
asmlinkage long (*original_read)(unsigned int fd, char __user *buf, size_t count);
^ ^