-
Notifications
You must be signed in to change notification settings - Fork 593
WIP: Introduce Ftrace in the syscall section #180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
nickchen120235
wants to merge
11
commits into
sysprog21:master
Choose a base branch
from
nickchen120235:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+324
−0
Draft
Changes from 1 commit
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
f7cc340
Create Ftrace syscall hooking example
nickchen120235 9b7be18
Update ftrace example
nickchen120235 6626ce8
Complete ftrace part
nickchen120235 7486076
inhibit some sections of code that breaks when rendering
nickchen120235 b44bf10
requested changes
nickchen120235 2df343f
all `ftrace` should use verb
nickchen120235 7f5cf1f
change callback func description
nickchen120235 5a71f73
shorten the code
nickchen120235 3b0cc7d
split lines
nickchen120235 8cf0d6d
provide usage example
nickchen120235 beb9272
fix example
nickchen120235 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Update ftrace example
- uid should be initialized - update comments - add uid check in our_sys_openat - format
- Loading branch information
commit 9b7be185b1c9dab0654bc9932bcb8e2ba871b28a
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,13 @@ | ||
| /** | ||
| * syscall-ftrace.c | ||
| * | ||
| * System call "stealing" with Ftrace | ||
| * System call "stealing" with ftrace | ||
| * | ||
| * We create a callback function that contains | ||
| * an unconditional jump to our spying function, | ||
| * which will then return control to the original one. | ||
| * | ||
| * The callback function is triggered by ftrace. | ||
| */ | ||
|
|
||
| #include <linux/kernel.h> | ||
|
|
@@ -22,30 +28,30 @@ MODULE_LICENSE("GPL"); | |
| #define MAX_FILENAME_SIZE 200 | ||
|
|
||
| /* UID we want to spy on - will be filled from the command line. */ | ||
| static int uid; | ||
| static int uid = 0; | ||
| module_param(uid, int, 0644); | ||
|
|
||
| /** | ||
| * This is a helper structure that housekeeps all information | ||
| * This is a housekeeping structure that saves all information | ||
| * needed for hooking. Usage with `PREPARE_HOOK` is recommended. | ||
| * | ||
| * Example: | ||
| * static ftrace_hook_t sys_clone_hook = PREPARE_HOOK(__NR_openat, my_sys_clone, &orig_sys_clone) | ||
| * static ftrace_hook_t sys_clone_hook = | ||
| * PREPARE_HOOK(__NR_clone, my_sys_clone, &orig_sys_clone) | ||
| */ | ||
| typedef struct ftrace_hook { | ||
| unsigned long nr; // syscall name | ||
| void* new; // hook function | ||
| void* orig; // original function | ||
| unsigned long nr; // syscall name | ||
| void *new; // hook function | ||
| void *orig; // original function | ||
|
|
||
| unsigned long address; // address to the original function | ||
| struct ftrace_ops ops; // ftrace structure | ||
| } ftrace_hook_t; | ||
|
|
||
| #define PREPARE_HOOK(_nr, _hook, _orig) { \ | ||
| .nr = (_nr), \ | ||
| .new = (_hook), \ | ||
| .orig = (_orig) \ | ||
| } | ||
| #define PREPARE_HOOK(_nr, _hook, _orig) \ | ||
| { \ | ||
| .nr = (_nr), .new = (_hook), .orig = (_orig) \ | ||
| } | ||
|
|
||
| unsigned long **sys_call_table; | ||
|
|
||
|
|
@@ -56,48 +62,65 @@ unsigned long **sys_call_table; | |
| */ | ||
| static int resolve_address(ftrace_hook_t *hook) | ||
| { | ||
| static struct kprobe kp = { | ||
| .symbol_name = "kallsyms_lookup_name" | ||
| }; | ||
| static struct kprobe kp = { .symbol_name = "kallsyms_lookup_name" }; | ||
| unsigned long (*kallsyms_lookup_name)(const char *name); | ||
linD026 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| register_kprobe(&kp); | ||
| kallsyms_lookup_name = (unsigned long (*)(const char *))kp.addr; | ||
| unregister_kprobe(&kp); | ||
|
|
||
| if (kallsyms_lookup_name) pr_info("[syscall-ftrace] kallsyms_lookup_name is found at 0x%lx\n", (unsigned long)kallsyms_lookup_name); | ||
| if (kallsyms_lookup_name) | ||
jserv marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| pr_info("[syscall-ftrace] kallsyms_lookup_name is found at 0x%lx\n", | ||
nickchen120235 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| (unsigned long)kallsyms_lookup_name); | ||
| else { | ||
| pr_err("[syscall-ftrace] kallsyms_lookup_name is not found!\n"); | ||
| return -1; | ||
| } | ||
|
|
||
| sys_call_table = (unsigned long **)kallsyms_lookup_name("sys_call_table"); | ||
| if (sys_call_table) pr_info("[syscall-ftrace] sys_call_table is found at 0x%lx\n", (unsigned long)sys_call_table); | ||
| if (sys_call_table) | ||
jserv marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| pr_info("[syscall-ftrace] sys_call_table is found at 0x%lx\n", | ||
| (unsigned long)sys_call_table); | ||
| else { | ||
| pr_err("[syscall-ftrace] sys_call_table is not found!\n"); | ||
| return -1; | ||
| } | ||
|
|
||
| hook->address = (unsigned long)sys_call_table[hook->nr]; | ||
| *((unsigned long*) hook->orig) = hook->address; | ||
| *((unsigned long *)hook->orig) = hook->address; | ||
| return 0; | ||
| } | ||
|
|
||
| /** | ||
| * This is where the magic happens. | ||
| * | ||
| * We check whether this function is called by the kernel or this module | ||
| * by checking whether parent_ip is within this module. | ||
| * | ||
| * During the first call, parent_ip points to somewhere in the kernel | ||
| * that's not in this module, | ||
| * while the second call is in this module | ||
| * since it's called from our_sys_openat. | ||
| * | ||
| * If it is the first call, we modify ip to be our_sys_openat, | ||
| * which will pass control to it after ftrace is done. | ||
| */ | ||
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) | ||
| static void notrace ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct ftrace_regs *fregs) | ||
| static void notrace ftrace_thunk(unsigned long ip, unsigned long parent_ip, | ||
| struct ftrace_ops *ops, | ||
| struct ftrace_regs *fregs) | ||
| { | ||
| ftrace_hook_t *hook = container_of(ops, ftrace_hook_t, ops); | ||
| if (!within_module(parent_ip, THIS_MODULE)) fregs->regs.ip = (unsigned long) hook->new; | ||
| if (!within_module(parent_ip, THIS_MODULE)) | ||
| fregs->regs.ip = (unsigned long)hook->new; | ||
| } | ||
|
|
||
| #else | ||
| static void notrace ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) | ||
| #else /** Version < v5.11 */ | ||
linD026 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| static void notrace ftrace_thunk(unsigned long ip, unsigned long parent_ip, | ||
| struct ftrace_ops *ops, struct pt_regs *regs) | ||
| { | ||
| ftrace_hook_t *hook = container_of(ops, ftrace_hook_t, ops); | ||
| if (!within_module(parent_ip, THIS_MODULE)) regs->ip = (unsigned long) hook->new; | ||
| if (!within_module(parent_ip, THIS_MODULE)) | ||
| regs->ip = (unsigned long)hook->new; | ||
| } | ||
|
|
||
| #endif /** Version >= v5.11 */ | ||
|
|
@@ -106,10 +129,14 @@ int install_hook(ftrace_hook_t *hook) | |
| { | ||
| int err; | ||
| err = resolve_address(hook); | ||
| if (err) return err; | ||
| if (err) | ||
| return err; | ||
|
|
||
| /** The callback function */ | ||
| hook->ops.func = ftrace_thunk; | ||
| /** We need registers and we're modifying ip */ | ||
| hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY; | ||
| /** Only sys_openat should be traced */ | ||
| err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0); | ||
| if (err) { | ||
| pr_err("[syscall-ftrace] ftrace_set_filter_ip() failed: %d\n", err); | ||
|
|
@@ -129,20 +156,34 @@ void remove_hook(ftrace_hook_t *hook) | |
| { | ||
| int err; | ||
| err = unregister_ftrace_function(&hook->ops); | ||
| if (err) pr_err("[syscall-ftrace] unregister_ftrace_function() failed: %d\n", err); | ||
| if (err) | ||
| pr_err("[syscall-ftrace] unregister_ftrace_function() failed: %d\n", | ||
| err); | ||
|
|
||
| /** Disable the trace by setting remove to 1 */ | ||
| err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0); | ||
| if (err) pr_err("[syscall-ftrace] ftrace_set_filter_ip() failed: %d\n", err); | ||
| if (err) | ||
| pr_err("[syscall-ftrace] ftrace_set_filter_ip() failed: %d\n", err); | ||
| } | ||
|
|
||
| /** For some reason the kernel segfaults when the arguments are expanded. */ | ||
| /** For some reason the kernel segfaults when the parameters are expanded. */ | ||
| static asmlinkage long (*original_call)(struct pt_regs *regs); | ||
| static asmlinkage long our_sys_openat(struct pt_regs *regs) | ||
| { | ||
| char *kfilename; | ||
| kfilename = kmalloc(GFP_KERNEL, MAX_FILENAME_SIZE*sizeof(char)); | ||
| if (!kfilename) return original_call(regs); | ||
| if (current->cred->uid.val != uid) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still have the warning. |
||
| return original_call(regs); | ||
| kfilename = kmalloc(GFP_KERNEL, MAX_FILENAME_SIZE * sizeof(char)); | ||
linD026 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (!kfilename) | ||
| return original_call(regs); | ||
|
|
||
| /** | ||
| * This may only work in x86_64 because getting parameters | ||
| * from CPU registers is architecture-dependent. | ||
| * | ||
| * Change regs->si to appropriate registers | ||
| * if you are trying on different architecture. | ||
| */ | ||
| if (copy_from_user(kfilename, (char __user *)regs->si, MAX_FILENAME_SIZE) < 0) { | ||
| kfree(kfilename); | ||
| return original_call(regs); | ||
|
|
@@ -154,13 +195,15 @@ static asmlinkage long our_sys_openat(struct pt_regs *regs) | |
| return original_call(regs); | ||
| } | ||
|
|
||
| static ftrace_hook_t sys_openat_hook = PREPARE_HOOK(__NR_openat, our_sys_openat, &original_call); | ||
| static ftrace_hook_t sys_openat_hook = | ||
| PREPARE_HOOK(__NR_openat, our_sys_openat, &original_call); | ||
|
|
||
| static int __init syscall_ftrace_start(void) | ||
| { | ||
| int err; | ||
| err = install_hook(&sys_openat_hook); | ||
| if (err) return err; | ||
| if (err) | ||
| return err; | ||
| pr_info("[syscall-ftrace] hooked, spying on uid %d\n", uid); | ||
| return 0; | ||
| } | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.