在我早期的内核编程工作中,我试图替换/挂钩 ioctl 系统调用,目的是记录并最终检查完成的每个 ioctl 调用。
目标系统是内核为3.10的mips(o32)系统。
基于我在基于 x86 的系统上看到的类似项目/示例,我得出了一个我认为可行的基本片段。我无权访问 System.map,但我注意到 sys_call_table 地址,因此我的尝试基于在目标系统上找到的地址
/proc/kallsyms
。我知道这个地址会随着内核版本的不同而改变,但这并不重要;这仅用于实验目的。
整个模块:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
static u32 **sct = (u32**)0x80008660; // `grep sys_call_table /proc/kallsyms`
asmlinkage int (*ioctl_orig)(s32 fd, u32 cmd, void* addr);
asmlinkage int ioctl_new(s32 fd, u32 cmd, void* addr)
{
printk("[IOC] Intercepted ioctl 0x%x to addr 0x%p\n", cmd, addr);
return ioctl_orig(fd, cmd, addr);
}
static int __init _enter(void)
{
ioctl_orig = (void*)sct[__NR_ioctl];
sct[__NR_ioctl] = (u32*)ioctl_new;
printk("[IOC] Original IOCTL addr: %p\n", ioctl_orig);
printk("[IOC] New IOCTL addr: %p\n", sct[__NR_ioctl]);
return 0;
}
static void __exit _exit(void)
{
sct[__NR_ioctl] = (u32 *)ioctl_orig;
printk("[IOC] Unloaded\n");
}
module_init(_enter);
module_exit(_exit);
MODULE_LICENSE("GPL");
显然这是行不通的,否则我就不会在这里刮墙了。该模块加载良好,来自
printk
/_enter
的 _exit
确实出现了,但是当我以任何方式对内核执行 ioctl 时,没有任何反应(我希望看到来自 ioctl_new
的“拦截的 ioctl”消息)
),这让我相信我修改了错误的位置。
问题:
/proc/kallsyms
提供指向系统调用表开头的正确指针吗?sys_ioctl
中与 /proc/kallsyms
相关的值应该与 *sct[__NR_ioctl]
匹配,还是我遗漏了某些内容?查看 arch/mips/kernel/ftrace.c 让我相信您需要使用名为“sys32_call_table”的表
@alexst 提供了真实答案! 根据 linux/unistd.h 对于 MIPS 架构:
#define __NR_Linux 4000
...
#define __NR_ioctl (__NR_Linux + 54)
所以你需要从 __NR_ioctl 中减去 __NR_Linux,例如:
ioctl_orig = (void*)sct[__NR_ioctl-__NR_Linux];
我最近在旧内核上挂接了内核函数,上面的答案帮助我解决了我的问题。 我的系统环境是Linux(无)3.17.2 mips32 Little-Endian
错误用法
__sys_call_table[__NR_kill] = (unsigned long) hacked_kill;
正确使用方法
__sys_call_table[__NR_kill - __NR_O32_Linux] = (unsigned long) hacked_kill;