无法通过Linux内核模块中的ftrace挂钩sys_open()

时间:2020-07-16 12:47:19

标签: c linux-kernel hook

我试图从gitub @ilammy/ftrace_hook扩展代码以挂钩另一个系统调用:sys_open()。 Clone()和execve()能够钩住,但是我无法钩住open()。没有发生任何错误,但是甚至没有调用fh_sys_open()函数。内核版本为5.3.0.62-通用,操作系统为ubuntu 18.04。我通过简单地在C中调用open()对其进行了测试。代码:

/*
 * Hooking kernel functions using ftrace framework
 *
 * Copyright (c) 2018 ilammy
 */

#define pr_fmt( fmt ) "ftrace_hook: " fmt

#include <linux/ftrace.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>
#include <linux/linkage.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/version.h>

MODULE_DESCRIPTION( "Example module hooking clone() and execve() via ftrace" );
MODULE_AUTHOR( "ilammy <a.lozovsky@gmail.com>" );
MODULE_LICENSE( "GPL" );


/*
 * There are two ways of preventing vicious recursive loops when hooking:
 * - detect recusion using function return address (USE_FENTRY_OFFSET = 0)
 * - avoid recusion by jumping over the ftrace call (USE_FENTRY_OFFSET = 1)
 */
#define USE_FENTRY_OFFSET 0


/**
 * struct ftrace_hook - describes a single hook to install
 *
 * @name:     name of the function to hook
 *
 * @function: pointer to the function to execute instead
 *
 * @original: pointer to the location where to save a pointer
 *            to the original function
 *
 * @address:  kernel address of the function entry
 *
 * @ops:      ftrace_ops state for this function hook
 *
 * The user should fill in only &name, &hook, &orig fields.
 * Other fields are considered implementation details.
 */
struct ftrace_hook {
    const char  *name;
    void        *function;
    void        *original;

    unsigned long       address;
    struct ftrace_ops   ops;
};

static int fh_resolve_hook_address( struct ftrace_hook *hook )
{
    hook->address = kallsyms_lookup_name( hook->name );

    if ( !hook->address )
    {
        pr_debug( "unresolved symbol: %s\n", hook->name );
        return(-ENOENT);
    }

#if USE_FENTRY_OFFSET
    *( (unsigned long *) hook->original) = hook->address + MCOUNT_INSN_SIZE;
#else
    *( (unsigned long *) hook->original) = hook->address;
#endif

    return(0);
}


static void notrace fh_ftrace_thunk( unsigned long ip, unsigned long parent_ip,
                                     struct ftrace_ops *ops, struct pt_regs *regs )
{
    struct ftrace_hook *hook = container_of( ops, struct ftrace_hook, ops );

#if USE_FENTRY_OFFSET
    regs->ip = (unsigned long) hook->function;
#else
    if ( !within_module( parent_ip, THIS_MODULE ) )
        regs->ip = (unsigned long) hook->function;
#endif
}


/**
 * fh_install_hooks() - register and enable a single hook
 * @hook: a hook to install
 *
 * Returns: zero on success, negative error code otherwise.
 */
int fh_install_hook( struct ftrace_hook *hook )
{
    int err;

    err = fh_resolve_hook_address( hook );
    if ( err )
        return(err);


    /*
     * We're going to modify %rip register so we'll need IPMODIFY flag
     * and SAVE_REGS as its prerequisite. ftrace's anti-recursion guard
     * is useless if we change %rip so disable it with RECURSION_SAFE.
     * We'll perform our own checks for trace function reentry.
     */
    hook->ops.func  = fh_ftrace_thunk;
    hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS
                      | FTRACE_OPS_FL_RECURSION_SAFE
                      | FTRACE_OPS_FL_IPMODIFY;

    err = ftrace_set_filter_ip( &hook->ops, hook->address, 0, 0 );
    if ( err )
    {
        pr_debug( "ftrace_set_filter_ip() failed: %d\n", err );
        return(err);
    }

    err = register_ftrace_function( &hook->ops );
    if ( err )
    {
        pr_debug( "register_ftrace_function() failed: %d\n", err );
        ftrace_set_filter_ip( &hook->ops, hook->address, 1, 0 );
        return(err);
    }

    return(0);
}


/**
 * fh_remove_hooks() - disable and unregister a single hook
 * @hook: a hook to remove
 */
void fh_remove_hook( struct ftrace_hook *hook )
{
    int err;

    err = unregister_ftrace_function( &hook->ops );
    if ( err )
    {
        pr_debug( "unregister_ftrace_function() failed: %d\n", err );
    }

    err = ftrace_set_filter_ip( &hook->ops, hook->address, 1, 0 );
    if ( err )
    {
        pr_debug( "ftrace_set_filter_ip() failed: %d\n", err );
    }
}


/**
 * fh_install_hooks() - register and enable multiple hooks
 * @hooks: array of hooks to install
 * @count: number of hooks to install
 *
 * If some hooks fail to install then all hooks will be removed.
 *
 * Returns: zero on success, negative error code otherwise.
 */
int fh_install_hooks( struct ftrace_hook *hooks, size_t count )
{
    int err;
    size_t  i;

    for ( i = 0; i < count; i++ )
    {
        err = fh_install_hook( &hooks[i] );
        if ( err )
            goto error;
    }

    return(0);

    error:
    while ( i != 0 )
    {
        fh_remove_hook( &hooks[--i] );
    }

    return(err);
}


/**
 * fh_remove_hooks() - disable and unregister multiple hooks
 * @hooks: array of hooks to remove
 * @count: number of hooks to remove
 */
void fh_remove_hooks( struct ftrace_hook *hooks, size_t count )
{
    size_t i;

    for ( i = 0; i < count; i++ )
        fh_remove_hook( &hooks[i] );
}


#ifndef CONFIG_X86_64
#error Currently only x86_64 architecture is supported
#endif

#if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION( 4, 17, 0 ) )
#define PTREGS_SYSCALL_STUBS 1
#endif


/*
 * Tail call optimization can interfere with recursion detection based on
 * return address on the stack. Disable it to avoid machine hangups.
 */
#if !USE_FENTRY_OFFSET
#pragma GCC optimize("-fno-optimize-sibling-calls")
#endif

#ifdef PTREGS_SYSCALL_STUBS
static asmlinkage long (*real_sys_clone)( struct pt_regs *regs );

static asmlinkage long fh_sys_clone( struct pt_regs *regs )
{
    long ret;

    pr_info( "clone() before\n" );

    ret = real_sys_clone( regs );

    pr_info( "clone() after: %ld\n", ret );

    return(ret);
}


#else
static asmlinkage long (*real_sys_clone)( unsigned long clone_flags,
                                          unsigned long newsp, int __user *parent_tidptr,
int __user *child_tidptr, unsigned long tls );

static asmlinkage long fh_sys_clone( unsigned long clone_flags,
                                     unsigned long newsp, int __user *parent_tidptr,
int __user *child_tidptr, unsigned long tls )
{
long ret;

pr_info( "clone() before\n" );

ret = real_sys_clone( clone_flags, newsp, parent_tidptr,
                      child_tidptr, tls );

pr_info( "clone() after: %ld\n", ret );

return(ret);
}


#endif

static char *duplicate_filename( const char __user *filename )
{
char *kernel_filename;

kernel_filename = kmalloc( 4096, GFP_KERNEL );
if ( !kernel_filename )
return(NULL);

if ( strncpy_from_user( kernel_filename, filename, 4096 ) < 0 )
{
kfree( kernel_filename );
return(NULL);
}

return(kernel_filename);
}


#ifdef PTREGS_SYSCALL_STUBS
static asmlinkage long (*real_sys_execve)( struct pt_regs *regs );

static asmlinkage long fh_sys_execve( struct pt_regs *regs )
{
    long    ret;
    char    *kernel_filename;

    kernel_filename = duplicate_filename( (void *) regs->di );

    pr_info( "execve() before: %s\n", kernel_filename );

    kfree( kernel_filename );

    ret = real_sys_execve( regs );

    pr_info( "execve() after: %ld\n", ret );

    return(ret);
}


#else
static asmlinkage long (*real_sys_execve)( const char __user *filename,
const char __user *const __user *argv,
const char __user *const __user *envp );

static asmlinkage long fh_sys_execve( const char __user *filename,
const char __user *const __user *argv,
const char __user *const __user *envp )
{
long    ret;
char    *kernel_filename;

kernel_filename = duplicate_filename( filename );

pr_info( "execve() before: %s\n", kernel_filename );

kfree( kernel_filename );

ret = real_sys_execve( filename, argv, envp );

pr_info( "execve() after: %ld\n", ret );

return(ret);
}


#endif

#ifdef PTREGS_SYSCALL_STUBS
static asmlinkage long (*real_sys_open)( struct pt_regs *regs );

static asmlinkage long fh_sys_open( struct pt_regs *regs )
{
    long    ret;
    char    *kernel_filename;

    /* Copy buffer to kernel space from user space */
    kernel_filename = duplicate_filename( (void *) regs->di );

    /* do something */
    pr_info( "name: %s\n", kernel_filename );

    kfree( kernel_filename );

    /* Call original function */
    ret = real_sys_open( regs );

    return(ret);
}


#else
static asmlinkage long (*real_sys_open)( const char __user *filename, int flags,
        umode_t mode );

static asmlinkage long fh_sys_open( const char __user *filename, int flags,
        umode_t mode )
{
long    ret;
char    *kernel_filename;

/* Copy buffer to kernel space from user space */
kernel_filename = duplicate_filename( filename );

/* do something */
pr_info( "name: %s\n", kernel_filename );

kfree( kernel_filename );

/* Call original function */
ret = real_sys_open( filename, flags, mode );

return(ret);
}


#endif


/*
 * x86_64 kernels have a special naming convention for syscall entry points in newer kernels.
 * That's what you end up with if an architecture has 3 (three) ABIs for system calls.
 */
#ifdef PTREGS_SYSCALL_STUBS
#define SYSCALL_NAME( name ) ("__x64_" name)
#else
#define SYSCALL_NAME( name ) (name)
#endif

#define HOOK( _name, _function, _original )      \
    {                                            \
        .name       = SYSCALL_NAME( _name ),     \
        .function   = (_function),               \
        .original   = (_original),               \
    }

static struct ftrace_hook demo_hooks[] = {
        HOOK( "sys_clone",  fh_sys_clone,  &real_sys_clone ),
        HOOK( "sys_execve", fh_sys_execve, &real_sys_execve ),
        HOOK( "sys_open",   fh_sys_open,   &real_sys_open ),
};

static int fh_init( void )
{
    int err;

    err = fh_install_hooks( demo_hooks, ARRAY_SIZE( demo_hooks ) );
    if ( err )
        return(err);

    pr_info( "module loaded\n" );

    return(0);
}


module_init( fh_init );

static void fh_exit( void )
{
    fh_remove_hooks( demo_hooks, ARRAY_SIZE( demo_hooks ) );

    pr_info( "module unloaded\n" );
}


module_exit( fh_exit );

测试代码:

#include <unistd.h>
#include <fcntl.h>
main()
{
    int fd;
    char s[] = "hello world!\n";
    fd = open("./temp.txt", O_WRONLY|O_CREAT);
    write(fd, s, sizeof(s));
    close(fd);
}

0 个答案:

没有答案