[ SYSTEM ]: Linux srv.persadacompanies.com 4.18.0-553.56.1.el8_10.x86_64 #1 SMP Tue Jun 10 05:00:59 EDT 2025 x86_64
[ SERVER ]: Apache | PHP: 8.4.19
[ USER ]: persadamedika | IP: 45.64.1.108
GEFORCE FILE MANAGER
/
usr
/
src
/
file_protector-1.1-1589
/
syscall_hooks
/
UPLOAD:
NAME
SIZE
QUICK PERMS
ACTIONS
📄 fs_syscall_hooks.c
49,240 B
SET
[ EDIT ]
|
[ DEL ]
📄 fs_syscall_hooks.h
10,360 B
SET
[ EDIT ]
|
[ DEL ]
📄 syscall_common.c
22,845 B
SET
[ EDIT ]
|
[ DEL ]
📄 syscall_common.h
14,075 B
SET
[ EDIT ]
|
[ DEL ]
📄 syscall_utils.h
9,230 B
SET
[ EDIT ]
|
[ DEL ]
DELETE SELECTED
[ CLOSE ]
EDIT: syscall_common.c
/** @file @brief Common functions and variables for system call hooks @details Copyright (c) 2017-2022 Acronis International GmbH @author Mikhail Molchanov (Mikhail.Molchanov@acronis.com) @since $Id: $ */ #include <linux/version.h> #include "syscall_common.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0) #include "compat.h" #include "debug.h" #include "fs_syscall_hooks.h" #include "lsm_common.h" #include "write_protection.h" #include <asm/ia32_unistd.h> // for ia32_sys_call_table '__NR_ia32_*' system call function indices #include <linux/compiler.h> #include <linux/delay.h> // loops_per_jiffy #include <linux/kallsyms.h> #include <linux/module.h> #include <linux/syscalls.h> // sys_close #include <linux/types.h> // bool, size_t, [u]int(8|16|32|64)_t #include <linux/unistd.h> // for sys_call_table '__NR_*' system call function indices static bool syscall_fallback_hooks_attached = false; // Linux Kernel 5.4 may have backports of "security" features that prevent // syscall hooking via direct sys_call_table modification. As a workaround, // we can use ftrace-based hooking for specific subset of "safe" syscalls. // Known 5.4 kernel is Oracle 7.9 UEK that provides only "security" // features backport, but does not provide CONFIG_SECURITY_PATH hooks that // we normally expect on modern kernels. #if defined(CONFIG_ARCH_HAS_SYSCALL_WRAPPER) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) #define SYSCALL_HOOKS_USE_FTRACE_FALLBACK #endif #ifdef SYSCALL_HOOKS_USE_FTRACE_FALLBACK // ftrace based solution does not support tails calls (look at method below) we only use it for 'rename' and 'unlink' hooks. // Those hooks are fine to be used because it is rare to have long processing operations in them. // This is, of course, worse than just using regular syscall hooks with tail calls which // allows to unload the driver in most of the cases. #include <linux/ftrace.h> #include <ftrace_hooks/ftrace_events.h> #include <ftrace_hooks/reg_tools.h> #define SYSCALL_HOOK_FTRACE(abi, tag) abi##_##tag##_ftrace #define HOOK_TRAMPOLINE_PRE_ONLY(abi, tag) \ static void SYSCALL_HOOK_FTRACE(abi, tag) (unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs) \ { \ struct pt_regs* real_regs = (struct pt_regs *)GET_KERNEL_ARGUMENT(regs, 0); \ (void) ip; (void) parent_ip; (void) op; \ (void) SYSCALL_HOOK_NAME(abi, tag)(real_regs); \ } /* ftrace by default only provide pre hooks but it is actually possible to upgrade pre hooks to pre+post hooks with IPMODIFY function override. Unfortunately to guarantee safety, we have to invoke original functions under the rundown protection to avoid module unload during the call outside of ftrace facilities which will not guarantee safety. The way this trick works is as follows: 1) In ftrace prehook we check whether the caller is from outside the module (to avoid recursion) and if so we modify instruction pointer to point to our override function. Module itself will never call normally syscalls so calls from THIS_MODULE are guaranteed to be from our hooks. 2) In the override function we call our syscall hook. If the hook returns a non-zero function pointer in 'fn' field of hook_ret_t structure, we call that function pointer as the original syscall implementation. Otherwise we just return the 'ret' field as the syscall return value. */ #define SYSCALL_HOOK_OVERRIDE(abi, tag) abi##_##tag##_override #define HOOK_TRAMPOLINE_PREPOST(abi, tag) \ static long SYSCALL_HOOK_OVERRIDE(abi, tag)(struct pt_regs* regs); \ static void SYSCALL_HOOK_FTRACE(abi, tag) (unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs) \ { \ (void) ip; (void) op; \ if (!within_module(parent_ip, THIS_MODULE)) { \ regs->ip = (unsigned long) &SYSCALL_HOOK_OVERRIDE(abi, tag); \ } \ } \ static long SYSCALL_HOOK_OVERRIDE(abi, tag)(struct pt_regs* regs) \ { \ hook_ret_t ret; \ if (!HOOK_PROLOG()) return SYSCALL_ORIG(abi, tag)(regs); \ ret = SYSCALL_HOOK_NAME(abi, tag)(regs); \ if (ret.fn) { \ ret.ret = ((long (*)(struct pt_regs*))ret.fn)(regs); \ } \ HOOK_EPILOG(); \ return ret.ret; \ } // Enforce that tail call optimization is disabled for these functions - it is needed to have a proper parent_ip #pragma GCC push_options #pragma GCC optimize ("no-optimize-sibling-calls") HOOK_TRAMPOLINE_PREPOST(sys, rename) HOOK_TRAMPOLINE_PREPOST(sys, renameat) HOOK_TRAMPOLINE_PREPOST(sys, renameat2) HOOK_TRAMPOLINE_PRE_ONLY(sys, unlink) HOOK_TRAMPOLINE_PRE_ONLY(sys, unlinkat) HOOK_TRAMPOLINE_PREPOST(ia32_sys, rename) HOOK_TRAMPOLINE_PREPOST(ia32_sys, renameat) HOOK_TRAMPOLINE_PREPOST(ia32_sys, renameat2) HOOK_TRAMPOLINE_PRE_ONLY(ia32_sys, unlink) HOOK_TRAMPOLINE_PRE_ONLY(ia32_sys, unlinkat) #pragma GCC pop_options typedef struct { struct ftrace_ops ops; const char* name; int num; bool ia32; bool registered; } syscall_probe_t; #define PROBE_HOOK_TRAMPOLINE_X64(tag, fl_flags) \ { \ .ops = { \ .func = (ftrace_func_t) SYSCALL_HOOK_FTRACE(sys, tag), \ .flags = fl_flags, \ }, \ .name = "__x64_sys_" #tag, \ .num = SYSCALL_HOOK_ID(sys, tag), .ia32 = false, \ .registered = false, \ }, #define PROBE_HOOK_TRAMPOLINE_IA32(tag, fl_flags) \ { \ .ops = { \ .func = (ftrace_func_t) SYSCALL_HOOK_FTRACE(ia32_sys, tag), \ .flags = fl_flags, \ }, \ .name = "__ia32_sys_" #tag, .ia32 = true, \ .num = SYSCALL_HOOK_ID(ia32_sys, tag), .registered = false, \ }, static syscall_probe_t g_probes[] = { PROBE_HOOK_TRAMPOLINE_X64(rename, FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY) PROBE_HOOK_TRAMPOLINE_X64(renameat, FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY) PROBE_HOOK_TRAMPOLINE_X64(renameat2, FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY) PROBE_HOOK_TRAMPOLINE_X64(unlink, FTRACE_OPS_FL_SAVE_REGS) PROBE_HOOK_TRAMPOLINE_X64(unlinkat, FTRACE_OPS_FL_SAVE_REGS) PROBE_HOOK_TRAMPOLINE_IA32(rename, FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY) PROBE_HOOK_TRAMPOLINE_IA32(renameat, FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY) PROBE_HOOK_TRAMPOLINE_IA32(renameat2, FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY) PROBE_HOOK_TRAMPOLINE_IA32(unlink, FTRACE_OPS_FL_SAVE_REGS) PROBE_HOOK_TRAMPOLINE_IA32(unlinkat, FTRACE_OPS_FL_SAVE_REGS) }; static void unregister_ftrace_syscall_events(void); static int register_ftrace_syscall_events(void) { int ret; int i = 0; for (; i < (int) ARRAY_SIZE(g_probes); i++) { syscall_probe_t* probe = &g_probes[i]; struct ftrace_ops *ops = &probe->ops; struct hook_desc* desc = probe->ia32 ? &ia32_hook_table[probe->num] : &hook_table[probe->num]; { desc->syscall_orig = (void*) compat_kallsyms_lookup_name(probe->name); if (!desc->syscall_orig) { EPRINTF("Failed to find %s", probe->name); continue; } DPRINTF("%s -> %lu", probe->name, desc->syscall_orig); } ret = ftrace_set_filter_ip(ops, (unsigned long) desc->syscall_orig, 0, 0); if (ret < 0) { EPRINTF("Failed to set filter ip"); goto fail; } // Note that 'rename' is using IPMODIFY so this failure may actually happen ret = register_ftrace_function(ops); if (ret < 0) { EPRINTF("Failed to register ftrace"); goto fail; } IPRINTF("Planted ftrace %ps", desc->syscall_orig); g_probes[i].registered = true; } return 0; fail: unregister_ftrace_syscall_events(); return ret; } static void unregister_ftrace_syscall_events(void) { int i = 0; for (; i < (int) ARRAY_SIZE(g_probes); i++) { if (g_probes[i].registered) { syscall_probe_t* probe = &g_probes[i]; struct ftrace_ops *ops = &probe->ops; struct hook_desc* desc = probe->ia32 ? &ia32_hook_table[probe->num] : &hook_table[probe->num]; IPRINTF("Remove ftrace for %ps", desc->syscall_orig); unregister_ftrace_function(ops); ftrace_set_filter_ip(ops, (unsigned long) desc->syscall_orig, 1, 0); g_probes[i].registered = false; } } } static int syscall_hooks_attach_fallback(void) { if (lsm_has_security_path()) { // There is no need in fallback syscall hooks if LSM security_path hooks are available return 0; } if (!lsm_has_open()) { EPRINTF("LSM authorizing open hooks are not available, cannot use ftrace syscall hooks"); return -EFAULT; } if (!ftrace_post_event_have(FTRACE_POST_EVENT_FSNOTIFY)) { EPRINTF("fsnotify is missing, cannot use ftrace syscall hooks"); return -EFAULT; } if (!ftrace_post_event_have(FTRACE_PRE_EVENT_RW_VERIFY_AREA)) { EPRINTF("rw_verify_area is missing, cannot use ftrace syscall hooks"); return -EFAULT; } register_ftrace_syscall_events(); syscall_fallback_hooks_attached = true; return 0; } static void syscall_hooks_detach_fallback(void) { unregister_ftrace_syscall_events(); syscall_fallback_hooks_attached = false; } #else static int syscall_hooks_attach_fallback(void){ return -EFAULT; } static void syscall_hooks_detach_fallback(void){} #endif static int syscall_hooks_resolve_symbols(void); static void **p_sys_call_table = NULL; static void **p_ia32_sys_call_table = NULL; static bool syscall_hooks_attached = false; #define HOOK_TRAMPOLINE(abi, tag) HOOK_TRAMPOLINE_ASM(SYSCALL_HOOK_TRAMPOLINE(abi, tag), SYSCALL_HOOK_NAME(abi, tag)) \ void SYSCALL_HOOK_TRAMPOLINE(abi, tag) (void); HOOK_TRAMPOLINE(sys, creat) HOOK_TRAMPOLINE(sys, open) HOOK_TRAMPOLINE(sys, openat) HOOK_TRAMPOLINE(sys, close) HOOK_TRAMPOLINE(sys, read) HOOK_TRAMPOLINE(sys, pread64) HOOK_TRAMPOLINE(sys, readv) HOOK_TRAMPOLINE(sys, preadv) HOOK_TRAMPOLINE(sys, preadv2) HOOK_TRAMPOLINE(sys, write) HOOK_TRAMPOLINE(sys, pwrite64) HOOK_TRAMPOLINE(sys, writev) HOOK_TRAMPOLINE(sys, pwritev) HOOK_TRAMPOLINE(sys, pwritev2) HOOK_TRAMPOLINE(sys, rename) HOOK_TRAMPOLINE(sys, renameat) HOOK_TRAMPOLINE(sys, renameat2) HOOK_TRAMPOLINE(sys, unlink) HOOK_TRAMPOLINE(sys, unlinkat) HOOK_TRAMPOLINE(ia32_sys, creat) HOOK_TRAMPOLINE(ia32_sys, open) HOOK_TRAMPOLINE(ia32_sys, openat) HOOK_TRAMPOLINE(ia32_sys, close) HOOK_TRAMPOLINE(ia32_sys, read) HOOK_TRAMPOLINE(compat_sys, pread64) HOOK_TRAMPOLINE(compat_sys, readv) HOOK_TRAMPOLINE(compat_sys, preadv) HOOK_TRAMPOLINE(compat_sys, preadv2) HOOK_TRAMPOLINE(ia32_sys, write) HOOK_TRAMPOLINE(compat_sys, pwrite64) HOOK_TRAMPOLINE(compat_sys, writev) HOOK_TRAMPOLINE(compat_sys, pwritev) HOOK_TRAMPOLINE(compat_sys, pwritev2) HOOK_TRAMPOLINE(ia32_sys, rename) HOOK_TRAMPOLINE(ia32_sys, renameat) HOOK_TRAMPOLINE(ia32_sys, renameat2) HOOK_TRAMPOLINE(ia32_sys, unlink) HOOK_TRAMPOLINE(ia32_sys, unlinkat) struct hook_desc hook_table[] = { DEFINE_HOOK_DESC(sys, creat, true), DEFINE_HOOK_DESC(sys, open, true), DEFINE_HOOK_DESC(sys, openat, true), DEFINE_HOOK_DESC(sys, close, true), DEFINE_HOOK_DESC(sys, read, true), DEFINE_HOOK_DESC(sys, pread64, true), DEFINE_HOOK_DESC(sys, readv, true), DEFINE_HOOK_DESC(sys, preadv, true), DEFINE_HOOK_DESC(sys, preadv2, PREADV2_ENABLED), DEFINE_HOOK_DESC(sys, write, true), DEFINE_HOOK_DESC(sys, pwrite64, true), DEFINE_HOOK_DESC(sys, writev, true), DEFINE_HOOK_DESC(sys, pwritev, true), DEFINE_HOOK_DESC(sys, pwritev2, PWRITEV2_ENABLED), DEFINE_HOOK_DESC(sys, rename, true), DEFINE_HOOK_DESC(sys, renameat, true), DEFINE_HOOK_DESC(sys, renameat2, RENAMEAT2_ENABLED), DEFINE_HOOK_DESC(sys, unlink, true), DEFINE_HOOK_DESC(sys, unlinkat, true) }; struct hook_desc ia32_hook_table[] = { DEFINE_IA32_HOOK_DESC(ia32_sys, creat, true), DEFINE_IA32_HOOK_DESC(ia32_sys, open, true), DEFINE_IA32_HOOK_DESC(ia32_sys, openat, true), DEFINE_IA32_HOOK_DESC(ia32_sys, close, true), DEFINE_IA32_HOOK_DESC(ia32_sys, read, true), DEFINE_COMPAT_HOOK_DESC(compat_sys, pread64, true), DEFINE_COMPAT_HOOK_DESC(compat_sys, readv, true), DEFINE_COMPAT_HOOK_DESC(compat_sys, preadv, true), DEFINE_COMPAT_HOOK_DESC(compat_sys, preadv2, PREADV2_ENABLED), DEFINE_IA32_HOOK_DESC(ia32_sys, write, true), DEFINE_COMPAT_HOOK_DESC(compat_sys, pwrite64, true), DEFINE_COMPAT_HOOK_DESC(compat_sys, writev, true), DEFINE_COMPAT_HOOK_DESC(compat_sys, pwritev, true), DEFINE_COMPAT_HOOK_DESC(compat_sys, pwritev2, PWRITEV2_ENABLED), DEFINE_IA32_HOOK_DESC(ia32_sys, rename, true), DEFINE_IA32_HOOK_DESC(ia32_sys, renameat, true), DEFINE_IA32_HOOK_DESC(ia32_sys, renameat2, RENAMEAT2_ENABLED), DEFINE_IA32_HOOK_DESC(ia32_sys, unlink, true), DEFINE_IA32_HOOK_DESC(ia32_sys, unlinkat, true) }; static bool need_syscall_hooks(void) { size_t i; for (i = 0; i < TOTAL_HOOKS_COUNT; i++) { if (syscall_replaced_by_lsm(hook_table[i].syscall_nr, false /*ia32*/)) { continue; } if (hook_table[i].enabled) { return true; } } for (i = 0; i < TOTAL_IA32_HOOKS_COUNT; i++) { if (syscall_replaced_by_lsm(ia32_hook_table[i].syscall_nr, true /*ia32*/)) { continue; } if (ia32_hook_table[i].enabled) { return true; } } return false; } static int attach_hooks(bool safe_mode) { unsigned long flags; cr0_write_protect_t wp; size_t i; int err; bool need_hooks = need_syscall_hooks(); if (!need_hooks) return 0; if (safe_mode) { IPRINTF("safe mode is enabled, failing syscall hooks attachment"); return -EPERM; } IPRINTF("attaching syscall hooks..."); err = syscall_hooks_resolve_symbols(); if (err) { return syscall_hooks_attach_fallback(); } local_irq_save(flags); wp = disable_write_protect(); for (i = 0; i < TOTAL_HOOKS_COUNT; i++) { if (syscall_replaced_by_lsm(hook_table[i].syscall_nr, false /*ia32*/)) { continue; } if (hook_table[i].enabled) { hook_table[i].syscall_orig = (void *)p_sys_call_table[hook_table[i].syscall_nr]; DPRINTF("'%s_orig()' = %p", hook_table[i].syscall_name, hook_table[i].syscall_orig); p_sys_call_table[hook_table[i].syscall_nr] = hook_table[i].syscall_hook; IPRINTF("'%s_hook()' = %p was planted!", hook_table[i].syscall_name, hook_table[i].syscall_hook); } else { DPRINTF("planting '%s_hook()' was skipped because it isn't enabled on current distro", hook_table[i].syscall_name); } } for (i = 0; i < TOTAL_IA32_HOOKS_COUNT; i++) { if (syscall_replaced_by_lsm(ia32_hook_table[i].syscall_nr, true /*ia32*/)) { continue; } if (ia32_hook_table[i].enabled) { ia32_hook_table[i].syscall_orig = (void *)p_ia32_sys_call_table[ia32_hook_table[i].syscall_nr]; DPRINTF("'%s_orig()' = %p", ia32_hook_table[i].syscall_name, ia32_hook_table[i].syscall_orig); p_ia32_sys_call_table[ia32_hook_table[i].syscall_nr] = ia32_hook_table[i].syscall_hook; IPRINTF("'%s_hook()' = %p was planted!", ia32_hook_table[i].syscall_name, ia32_hook_table[i].syscall_hook); } else { DPRINTF("planting '%s_hook()' was skipped because it isn't enabled on current distro", ia32_hook_table[i].syscall_name); } } restore_write_protect(wp); syscall_hooks_attached = true; local_irq_restore(flags); IPRINTF("syscall hooks attached"); return 0; } // FIXME: 'detach' may fail if pointers in syscall tables pointing to our // hooks were overwritten by someone else static int detach_hooks(void) { unsigned long flags; cr0_write_protect_t wp; size_t i; IPRINTF("detaching syscall hooks..."); local_irq_save(flags); wp = disable_write_protect(); for (i = 0; i < TOTAL_HOOKS_COUNT; i++) { if (syscall_replaced_by_lsm(hook_table[i].syscall_nr, false /*ia32*/)) { continue; } if (hook_table[i].enabled) { if (likely(hook_table[i].syscall_hook == (void *)p_sys_call_table[hook_table[i].syscall_nr])) { p_sys_call_table[hook_table[i].syscall_nr] = hook_table[i].syscall_orig; IPRINTF("%s_orig = %p was removed!", hook_table[i].syscall_name, hook_table[i].syscall_orig); } else { EPRINTF("sys_call_table pointer to '%s()' differs from expected, " "not replacing it with the original one", hook_table[i].syscall_name); } } else { DPRINTF("planting '%s_orig()' was skipped because corresponding hook isn't enabled on current distro", hook_table[i].syscall_name); } } for (i = 0; i < TOTAL_IA32_HOOKS_COUNT; i++) { if (syscall_replaced_by_lsm(ia32_hook_table[i].syscall_nr, true /*ia32*/)) { continue; } if (ia32_hook_table[i].enabled) { if (likely(ia32_hook_table[i].syscall_hook == (void *)p_ia32_sys_call_table[ia32_hook_table[i].syscall_nr])) { p_ia32_sys_call_table[ia32_hook_table[i].syscall_nr] = ia32_hook_table[i].syscall_orig; IPRINTF("%s_orig = %p was removed!", ia32_hook_table[i].syscall_name, ia32_hook_table[i].syscall_orig); } else { EPRINTF("ia32_sys_call_table pointer to '%s()' differs from expected, " "not replacing it with the original one", ia32_hook_table[i].syscall_name); } } else { DPRINTF("planting '%s_orig()' was skipped because corresponding hook isn't enabled on current distro", ia32_hook_table[i].syscall_name); } } restore_write_protect(wp); syscall_hooks_attached = false; local_irq_restore(flags); IPRINTF("syscall hooks detached"); return 0; } int syscall_hooks_attach(bool safe_mode) { if (syscall_hooks_attached || syscall_fallback_hooks_attached) { EPRINTF("syscall hooks are already attached"); return -EINVAL; } IPRINTF("module_refcount()=%lu", module_refcount_compat(THIS_MODULE)); return attach_hooks(safe_mode); } int syscall_hooks_detach(void) { int r = 0; if (syscall_fallback_hooks_attached) { syscall_hooks_detach_fallback(); } if (syscall_hooks_attached) { r = detach_hooks(); } IPRINTF("module_refcount()=%lu", module_refcount_compat(THIS_MODULE)); return r; } /* In the easyest case 'syscall table' is 'exported' and is directly accessible by its name. Debian (since at least Debian 8, kernel v3.16) does not export 'sys_call_table' nor 'ia32_sys_call_table'. Probably 'sys_call_table' symbol was unexported in 2.5.41 In intermediate case 'syscall table' is 'not exported' but can be found by 'kallsyms_lookup_name()' or via 'kallsyms_on_each_symbol()' if 'kallsyms_lookup_name()' itself is not exported (for example on CentOS 6.x). 'kallsyms_lookup_name()' was exported since 'stable/v2.6.33' but unexported since 'stable/v5.7'. In worst case 'syscall table' can be found by some 'heuristic'. Heuristic must check if (while searching for 'sys_call_table') we just found our variable 'p_sys_call_table' instead of real 'sys_call_table' (it was actually the case, when our variable was named 'sys_call_table'). Some collected facts: CentOS 6.0 2.6.32-71.el6.x86_64 LINUX_VERSION_CODE=2.6.32 RHEL_RELEASE_CODE=6.0 CentOS 6.5 2.6.32-431.el6.x86_64 LINUX_VERSION_CODE=2.6.32 RHEL_RELEASE_CODE=6.5 CentOS 6.10 2.6.32-754.el6.x86_64 LINUX_VERSION_CODE=2.6.32 RHEL_RELEASE_CODE=6.9 'kallsyms_lookup_name' is not exported 'kallsyms_on_each_symbol' is exported CentOS 7.0 3.10.0-123.el7.x86_64 LINUX_VERSION_CODE=3.10.0 RHEL_RELEASE_CODE=7.0 CentOS 7.9 3.10.0-1160.el7.x86_64 LINUX_VERSION_CODE=3.10.0 RHEL_RELEASE_CODE=7.9 Debian 9.5 4.9.0-7-amd64 LINUX_VERSION_CODE=4.9.110 Debian 10.4 4.19.0-9-amd64 LINUX_VERSION_CODE=4.19.118 Ubuntu 16.04 4.4.0-31-generic LINUX_VERSION_CODE=4.4.13 Both 'kallsyms_lookup_name' and 'compat_kallsyms_lookup_name' return address of 'sys_call_table' and 'ia32_sys_call_table' Debian 8.0 3.16.0-4-amd64 LINUX_VERSION_CODE=3.16.7 Both 'kallsyms_lookup_name' and 'kallsyms_on_each_symbol' do not return address of 'sys_call_table' nor 'ia32_sys_call_table'. It is the case when syscall table search heuristic is necessary. */ #if defined USE_FIND_SYSCALL_TABLE_HEURISTIC #define LOOKUP(nr, name) \ name = (unsigned long) compat_kallsyms_lookup_name(#name); \ if (!name) { \ EPRINTF("%s(%s) failure", "kallsyms_lookup_name", #name); \ return 0; \ } else { \ DPRINTF("%s=0x%X %s=0x%p", #nr, nr, #name, (void *)name); \ } static unsigned long * __init heuristic_find_sys_call_table(void) { unsigned long *p; unsigned long sys_write; unsigned long sys_writev; LOOKUP(__NR_write , sys_write) LOOKUP(__NR_writev, sys_writev) for (p = (unsigned long *) sys_close; p < (unsigned long *) &loops_per_jiffy; ++p) { if ((unsigned long) sys_close == p[__NR_close] && sys_write == p[__NR_write] && sys_writev == p[__NR_writev]) { if ((unsigned long *)&p_sys_call_table == p) { WPRINTF("p==&%s", "p_sys_call_table"); } else { IPRINTF("heuristic: %s=0x%p", "sys_call_table", p); return p; } } } EPRINTF("%s not found", "sys_call_table"); return 0; } static unsigned long * __init heuristic_find_ia32_sys_call_table(void) { unsigned long *p; unsigned long compat_sys_readv; unsigned long compat_sys_pwritev; unsigned long compat_sys_writev; LOOKUP(__NR_ia32_readv , compat_sys_readv) LOOKUP(__NR_ia32_pwritev, compat_sys_pwritev) LOOKUP(__NR_ia32_writev , compat_sys_writev) for (p = (unsigned long *) sys_close; p < (unsigned long *) &loops_per_jiffy; ++p) { if (compat_sys_readv == p[__NR_ia32_readv] && compat_sys_pwritev == p[__NR_ia32_pwritev] && compat_sys_writev == p[__NR_ia32_writev]) { if ((unsigned long *)&p_ia32_sys_call_table == p) { WPRINTF("p==&%s", "p_ia32_sys_call_table"); } else { IPRINTF("heuristic: %s=0x%p", "ia32_sys_call_table", p); return p; } } } EPRINTF("%s not found", "ia32_sys_call_table"); return 0; } #undef LOOKUP #endif static unsigned long * __init find_sys_call_table(void) { unsigned long *p = (unsigned long *) compat_kallsyms_lookup_name("sys_call_table"); #if defined USE_FIND_SYSCALL_TABLE_HEURISTIC if (!p) { p = heuristic_find_sys_call_table(); } #endif return p; } static unsigned long * __init find_ia32_sys_call_table(void) { unsigned long *p = (unsigned long *) compat_kallsyms_lookup_name("ia32_sys_call_table"); #if defined USE_FIND_SYSCALL_TABLE_HEURISTIC if (!p) { p = heuristic_find_ia32_sys_call_table(); } #endif return p; } static int syscall_hooks_resolve_symbols(void) { // !!! This will fail on modern kernels !!! // https://stackoverflow.com/questions/78599971/hooking-syscall-by-modifying-sys-call-table-does-not-work BUILD_BUG_ON(TOTAL_HOOKS_COUNT != ARRAY_SIZE(hook_table)); BUILD_BUG_ON(TOTAL_IA32_HOOKS_COUNT != ARRAY_SIZE(ia32_hook_table)); if (p_sys_call_table && p_ia32_sys_call_table) { return 0; } p_sys_call_table = (void **)find_sys_call_table(); if (!p_sys_call_table) { WPRINTF("'%s' syscall table is unavailable", "x86_64"); return -ENOSYS; } IPRINTF("'%s' syscall table is at %p", "x86_64", p_sys_call_table); p_ia32_sys_call_table = (void **)find_ia32_sys_call_table(); if (!p_ia32_sys_call_table) { WPRINTF("'%s' syscall table is unavailable", "ia32"); return -ENOSYS; } IPRINTF("'%s' syscall table is at %p", "ia32", p_ia32_sys_call_table); return 0; } #else int syscall_hooks_attach(bool safe_mode) { return 0; } int syscall_hooks_detach(void) { return 0; } #endif