0
votes

In my early endeavours into kernel programming I'm trying to replace/hook into the ioctl syscall, with the purpose of logging and eventually inspecting every ioctl call done.

The target system is a mips (o32) system with kernel 3.10.

Based on similar projects/examples I've seen for x86 based systems I've arrived at a basic snippet I thought would work. I don't have access to a System.map but I noticed the sys_call_table address, so I based my attempts on the address found /proc/kallsyms on the target system. I know this address will change from kernel build to build but that doesn't matter at this point; this is for experimental purposes only.

The module in its entirety:

#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");

Obviously this doesn't work or I wouldn't be here scraping the walls. The module loads fine and the printks from _enter/_exit do indeed appear, but nothing happens when I do ioctls towards the kernel in any way (I would expect to see the "Intercepted ioctl" message from ioctl_new), which leads me to believe I'm modifying the wrong spot.

Questions:

  • Obviously: What am I doing wrong?
  • Can I rely on /proc/kallsyms providing the correct pointer to the beginning of the syscall table?
  • Am I right in my assumption that the value associated with sys_ioctl in /proc/kallsyms should match *sct[__NR_ioctl] or am I missing something?
  • Am I casting correctly?
  • Is this method of modifying the sctable even applicable on mips?
3
"What am I doing wrong?" On x86, to do the same, you must disable page protection by some asm line. I think there is some equivalent on mips. - Mathieu
"Can I rely on /proc/kallsyms...?" Did the function kallsyms_lookup_name("sys_call_table") exists on your system? It should return you what you want. (stackoverflow.com/a/39202118/1212012) - Mathieu
Am aware of the cr0 page protection bit on x86, although I have not found any evidence of a similar mechanism on MIPS. - bjorn
Also, kallsyms_lookup_name is not exported in the kernel I'm working with unfortunately, so no go there. - bjorn

3 Answers

0
votes

Looking at arch/mips/kernel/ftrace.c leads me to believe that you need to use the table called "sys32_call_table"

0
votes

What am I doing wrong?

You are trying to modify the system call table from a kernel module. This is unsafe and unsupported. Don't do it.

If you want to inspect system calls, there are a number of better tools available in the kernel, such as ftrace, perf, and SystemTap. Which one is most appropriate for you will depend on your specific requirements.

0
votes

@alexst provided true answer! According to linux/unistd.h for MIPS architecture:

#define __NR_Linux 4000
...
#define __NR_ioctl (__NR_Linux + 54)

So you need substract __NR_Linux from __NR_ioctl, e.g.:

ioctl_orig = (void*)sct[__NR_ioctl-__NR_Linux];