从 ftrace 处理程序返回原始函数时恢复任务 pt_regs
restoring task pt_regs when returning to original function from ftrace handler
使用内核模块 (LKM),linux 内核 ftrace
函数允许您设置 FTRACE_OPS_FL_SAVE_REGS
和 FTRACE_OPS_FL_IPMODIFY
标志,基本上允许您完全重定向您可以找到符号地址的任何内核函数,如下所示:
static void notrace my_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *fops, struct pt_regs *regs) {
regs->ip = new_addr;
}
其中new_addr
是新函数的地址。 kpatch
工具使用它,尽管从未 return 到原始函数。
如果在 new_addr
指向的函数末尾,我试试这个:
task_pt_regs(current)->ip = orig_addr + MCOUNT_INSN_SIZE;
有些函数没有问题,但大多数会导致调用进程出现段错误。
ftrace
函数有内置代码可以将当前任务的 pt_regs
在 return 上恢复到原始函数,这就是为什么我可以自己去函数并且参数没有问题。然而,在代码的这一点上,ftrace
不再涉及。我如何告诉内核不要重置当前寄存器,以便新 return 地址的函数可以使用它们?
发布这篇文章后,我想到也许我可以直接从 ftrace 处理程序中的 pt_regs *regs
指针读取参数。事实证明,你可以。通过不重定向到另一个函数,您可以保留寄存器和 return 地址,同时决定您 return 那里还是处理程序本身的其他地方:
int donotexec(void) {
return -EACCES;
}
static void notrace my_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *fops, struct pt_regs *regs) {
struct linux_binprm *bprm = (struct linux_binprm *)regs->di;
if (bprm->file)
if (allowed_to_exec(bprm->file))
regs->ip = (unsigned long)donotexec;
}
此函数挂钩 security_bprm_check
,其中 allowed_to_exec
是另一个检查从 regs->di
寄存器读取的 bprm->file
的函数。
这取决于 arch(请参阅 arch/x86/include/asm/ptrace.h
中内核的 pt_regs
结构)并且仅限于 5 个函数参数。
使用内核模块 (LKM),linux 内核 ftrace
函数允许您设置 FTRACE_OPS_FL_SAVE_REGS
和 FTRACE_OPS_FL_IPMODIFY
标志,基本上允许您完全重定向您可以找到符号地址的任何内核函数,如下所示:
static void notrace my_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *fops, struct pt_regs *regs) {
regs->ip = new_addr;
}
其中new_addr
是新函数的地址。 kpatch
工具使用它,尽管从未 return 到原始函数。
如果在 new_addr
指向的函数末尾,我试试这个:
task_pt_regs(current)->ip = orig_addr + MCOUNT_INSN_SIZE;
有些函数没有问题,但大多数会导致调用进程出现段错误。
ftrace
函数有内置代码可以将当前任务的 pt_regs
在 return 上恢复到原始函数,这就是为什么我可以自己去函数并且参数没有问题。然而,在代码的这一点上,ftrace
不再涉及。我如何告诉内核不要重置当前寄存器,以便新 return 地址的函数可以使用它们?
发布这篇文章后,我想到也许我可以直接从 ftrace 处理程序中的 pt_regs *regs
指针读取参数。事实证明,你可以。通过不重定向到另一个函数,您可以保留寄存器和 return 地址,同时决定您 return 那里还是处理程序本身的其他地方:
int donotexec(void) {
return -EACCES;
}
static void notrace my_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *fops, struct pt_regs *regs) {
struct linux_binprm *bprm = (struct linux_binprm *)regs->di;
if (bprm->file)
if (allowed_to_exec(bprm->file))
regs->ip = (unsigned long)donotexec;
}
此函数挂钩 security_bprm_check
,其中 allowed_to_exec
是另一个检查从 regs->di
寄存器读取的 bprm->file
的函数。
这取决于 arch(请参阅 arch/x86/include/asm/ptrace.h
中内核的 pt_regs
结构)并且仅限于 5 个函数参数。