1

Consider the following kernel module source, which uses kprobes_register to get the address of a kernel symbol.

It works for symbols like flush_tlb_all, but not for sys_call_table.

A quick lookup using /proc/kallsyms:

sudo cat /proc/kallsyms | grep -E '\sflush_tlb_all$|\ssys_call_table$'
ffffffff86a83e20 T flush_tlb_all
ffffffff87a002e0 D sys_call_table

The difference being (man nm) that flush_tlb_all is in the text section, where as sys_call_table is in the data section. However, both symbols are global (external).

Why can't kprobes_register be used to lookup the address of sys_call_table?

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>

#define MAX_SYMBOL_LEN  64
static char symbol[MAX_SYMBOL_LEN] = "flush_tlb_all";
module_param_string(symbol, symbol, sizeof(symbol), 0644);

/* For each probe you need to allocate a kprobe structure */
static struct kprobe kp = {
   .symbol_name = symbol,
};

static int __init kprobe_init(void)
{
   int ret;

   ret = register_kprobe(&kp);
   if (ret < 0) {
      pr_err("register_kprobe failed, returned %d\n", ret);
      return ret;
   }
   pr_info("Planted kprobe at %p\n", kp.addr);
   return 0;
}

static void __exit kprobe_exit(void)
{
   unregister_kprobe(&kp);
   pr_info("kprobe at %p unregistered\n", kp.addr);
}

module_init(kprobe_init);
module_exit(kprobe_exit);
MODULE_LICENSE("GPL");

1 Answer 1

2

The difference being (man nm) that flush_tlb_all is in the text section, where as sys_call_table is in the data section. However, both symbols are global (external).

This is exactly why register_kprobe fails on sys_call_table: it only allows probes in the kernel text. kprobe_register calls check_kprobe_address_safe once it has the address to probe, and the latter checks that the address is part of the kernel text.

9
  • Thanks @StephenKitt. How is it possible to retrieve address of global symbols in the data section? Normally kallsyms would allow that, however its API is not exported to modules anymore. Maybe I should do a kprobe for the kallsyms functions and see where that leads me. I need this, because I want to experience with dynamically patching syscalls.
    – Shuzheng
    Commented May 3, 2021 at 5:17
  • An approach similar to this one should still work, as long as you change the syscall table to be writable. Commented May 3, 2021 at 13:30
  • Thanks. However, the approach is extremely dependent on the given kernel for the searched kernel-memory range (between two symbols). I did it by using kprobe to get the non-exported kallsyms_lookup_name, and then used that to get the sys_call_table. However, my solution depends on kallsyms and kprobes having been configured for the given kernel.
    – Shuzheng
    Commented May 3, 2021 at 15:18
  • ~@StephenKitt - Regarding your approach, it seems that sys_call_table[__NR_close]==__x64_sys_close and that sys_close is not exported anymore from syscalls.h? Furthermore, ksys_close is not equal to __x64_sys_close, so I have no real way of comparing sys_call_table[__NR_close] to a known symbol? Do you have any ideas on how to solve this issue?
    – Shuzheng
    Commented May 5, 2021 at 8:50
  • That’s a good point, syscalls are no longer exported to modules. I don’t know of a workaround. Commented May 5, 2021 at 11:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .