ilammy / ftrace-hook

Using ftrace for function hooking in Linux kernel
GNU General Public License v2.0
253 stars 70 forks source link

Support newer 4.17+ kernels for real #5

Closed ilammy closed 5 years ago

ilammy commented 5 years ago

Previously mentioned patchset changed not only the naming convention of x86_64 stubs but their ABI as well. Now they receive a pointer to struct pt_regs laid out on the stack. We have to pass this pointer as is to the original handler, and if we wish to examine the arguments of a system call then we have to look into appropriate registers ourselves.

ilammy commented 5 years ago

@StephGbzh @Fabiofont could you please try this out? This patch should fix #4.

StephGbzh commented 5 years ago

I confirm it works for me !

Just curious: How did you find the info ? I cloned the linux repo and used grep mkdir and grep pt_regs (well, ripgrep in fact) but there are way too many occurrences. There are around 14000 commits between tags v4.16 and v4.17 ... Just a quick pointer to where you looked at to find that the signature changed and the struct had a field named "di" ?

ilammy commented 5 years ago

Well, it's not very efficient to look through all commits between versions. As you have noticed, there are quite a few of them. In general you'd be better off looking for specific changes via git log v4.16..v4.17 arch/x86 or git log -S pt_regs v4.16..v4.17.

However, in this particular case I already knew that there is a patch set with pt_regs changes somewhere so it did not take long to discover this one. It changes the ABI of the system call handlers to receive a pointer to struct pt_regs which contains values of registers on system call entry. Then it just takes some ABI knowledge: the system call arguments are located in %rdi, %rsi, %rdx, %r10, %r8, %r9 (in that order). execve() gets executable file name as the first argument, so we need the %rdi register which correspons to the "di" field of pt_regs,

StephGbzh commented 5 years ago

Thanks !

ningyuv commented 4 years ago

I got relative path on kernel v5.3.0 when i execute with relative path. How can I get full path? I ran:

~$ Desktop/main
Hello, World!

then dmesg shows as follows:

[15946.274633] ftrace_hook: execve() before: Desktop/main
[15946.274775] ftrace_hook: execve() after: 0
ilammy commented 4 years ago

@ningyuv,

I got relative path on kernel v5.3.0 when i execute with relative path. How can I get full path?

You’d need to append the relative path the current working directory of the current process to get an absolute path. The current process can be accessed via current global variable, it will give you task_struct, and from there you can get the current working directory. You can take a peek at getcwd(2) implementation for a hint.

https://github.com/torvalds/linux/blob/2ef96a5bb12be62ef75b5828c0aab838ebb29cb8/fs/d_path.c#L425-L471

ningyuv commented 4 years ago

@ilammy

@ningyuv,

I got relative path on kernel v5.3.0 when i execute with relative path. How can I get full path?

You’d need to append the relative path the current working directory of the current process to get an absolute path. The current process can be accessed via current global variable, it will give you task_struct, and from there you can get the current working directory. You can take a peek at getcwd(2) implementation for a hint.

https://github.com/torvalds/linux/blob/2ef96a5bb12be62ef75b5828c0aab838ebb29cb8/fs/d_path.c#L425-L471

thanks! Finally, I appended the relative path the current working directory(such as /path/to/pwd/./main or /path/to/pwd/further/../main), it works correctly with filp_open function! I took a peek at getcwd(2) implementation, but it's diffcult for me. I found a blog that solved this problem. The blog: Module to display the current working directory The code:

path_get(&current->fs->pwd);
buf = kmalloc(4096, GFP_KERNEL);
if (!buf)
    goto out_no_buf_mem;
pwd_path = d_path(&current->fs->pwd, buf, 4096);
pr_info("pwd: %s\n", pwd_path);
kfree(buf);
pandaominggz commented 4 years ago

I tried to hook sys_open(), but it didn't work. Here s a piece of code:

.....

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

/*

define HOOK(_name, _function, _original) \

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

static struct ftrace_hook demo_hooks[] = { HOOK("sys_open", fh_sys_open, &real_sys_open) };

....

If you have any clue why my code didn't work, please tell me. Thanks!