我正在尝试开发一个挂接read()
系统调用的内核模块。由于某些原因,set_memory_rw()
功能似乎不起作用。
我看到了另一个此类问题,但我并不十分了解该怎么做。
我正在使用Raspberry-pi 4开发Kali 4.19.93
我的代码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/syscalls.h>
#include <linux/kallsyms.h>
#include <linux/slab.h>
#include <linux/kern_levels.h>
#include <asm/unistd.h>
#include <asm/cacheflush.h>
#include <linux/semaphore.h>
#include <asm/set_memory.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Omri Ben David");
MODULE_DESCRIPTION("Hooking Linux System calls");
MODULE_VERSION("1.0");
unsigned long** SYS_CALL_TABLE = (unsigned long**) 0xc02011c4;
asmlinkage ssize_t (*original_read) (int fd, char *buf, size_t count);
asmlinkage ssize_t HookRead(unsigned int fd, char * buf, size_t count)
{
printk(KERN_INFO "Rootkit_Debug: Yay you entered my function!!\n Now you can read\n");
return (*original_read)(fd,buf,count);
}
void (*seek)(unsigned long, int);
void (*hide)(unsigned long, int);
static int __init SetHooks(void)
{
printk(KERN_INFO "Hooks Will now be set, hold on tight\n");
printk(KERN_INFO "System calls table is at address %p\n",SYS_CALL_TABLE);
original_read = (void*) SYS_CALL_TABLE[__NR_read];
seek = (void*) kallsyms_lookup_name("set_memory_rw");
hide = (void*) kallsyms_lookup_name("set_memory_ro");
(*seek)((unsigned long)SYS_CALL_TABLE, 1);
SYS_CALL_TABLE[__NR_read] = (unsigned long*)HookRead;
(*hide)((unsigned long)SYS_CALL_TABLE, 1);
printk(KERN_INFO "System calls hooked successfully\n");
return 0;
}
static void __exit HookCleanup(void)
{
printk(KERN_INFO "System calls restore innitiated\n");
(*seek)((unsigned long)SYS_CALL_TABLE, 1);
SYS_CALL_TABLE[__NR_read] = (unsigned long*) original_read;
(*hide)((unsigned long)SYS_CALL_TABLE, 1);
printk(KERN_INFO "System successfully restored. hope you had fun");
}
module_init(SetHooks);
module_exit(HookCleanup);
如何使set_memory_rw()
函数起作用以覆盖syscall表?还是应该使用其他方法?
因此,正如我在上面的评论中所说,似乎函数change_memory_common()
(由change_memory_common()
使用)在应用请求的权限之前进行了检查。这是带有注释的文档:
set_memory_ro/rw()
该函数似乎仅适用于通过/*
* Kernel VA mappings are always live, and splitting live section
* mappings into page mappings may cause TLB conflicts. This means
* we have to ensure that changing the permission bits of the range
* we are operating on does not result in such splitting.
*
* Let's restrict ourselves to mappings created by vmalloc (or vmap).
* Those are guaranteed to consist entirely of page mappings, and
* splitting is never needed.
*
* So check whether the [addr, addr + size) interval is entirely
* covered by precisely one VM area that has the VM_ALLOC flag set.
*/
area = find_vm_area((void *)addr);
if (!area ||
end > (unsigned long)area->addr + area->size ||
!(area->flags & VM_ALLOC))
return -EINVAL;
或vmalloc()
创建的映射,并且vmap()
并不驻留在此类映射中。
关注点似乎与TLB冲突有关。我真的不确定为什么自功能sys_call_table
以来,这会导致TLB冲突,但是我不是ARM专家,所以我可能会丢失一些东西。我想可以打电话给seems to properly call flush_tlb_kernel_range()
,但似乎是不必要的。欢迎任何其他见解!
[在任何情况下,出于练习syscall劫持的目的,您都可以重写自己的flush_tlb_kernel_range()
和flush_tlb_all()
版本,避免进行此检查。一种更简单的方法是只为所需的地址获取适当的PTE,然后更改权限,但是我并没有浏览所有不计其数的宏。
最后,但并非最不重要,因为flush_tlb_all()
最终可能会跨越页面边界,所以在对页面应用更改时最好使用set_memory_common()
而不是仅使用set_memory_rw/ro()
。
这是一个有效的示例:
sys_call_table