0

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?
bjorn
  • 46
  • 5
  • "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 Jun 19 '17 at 06:55
  • "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. (https://stackoverflow.com/a/39202118/1212012) – Mathieu Jun 19 '17 at 06:57
  • Am aware of the cr0 page protection bit on x86, although I have not found any evidence of a similar mechanism on MIPS. – bjorn Jun 22 '17 at 11:23
  • Also, kallsyms_lookup_name is not exported in the kernel I'm working with unfortunately, so no go there. – bjorn Jun 22 '17 at 11:38

3 Answers3

0

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

alexst
  • 591
  • 2
  • 6
  • No mention of sys32_call_table in the 3.10 source tree, this seems to be a more recent thing? – bjorn Jun 22 '17 at 11:20
  • Indeed there's none in 3.10 but I think you need to check the syscall argument. Looks like for O32 you need to subtract __NR_O32_Linux from syscall number? I'm not really sure since I've not used MIPS. https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable/+/linux-3.10.y/arch/mips/include/uapi/asm/unistd.h#380 Either way, you can implement your hooks directly in "fs/ioctl.c" and "fs/compat_ioctl.c". If you want a module, you can implement a Loadable Security Module like SELinux or Tomoyo via security_file_ioctl – alexst Jun 22 '17 at 12:36
0

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.

  • Fully aware that this is shady practice. Will look further into ftrace to see if that can help us. The two other options are unavailble on the target system (hardened embedded kernel). – bjorn Jun 22 '17 at 11:20
0

@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];
HiHat
  • 3
  • 3