对于 Linux 上的 Mach 内核 API 仿真,我需要在刚刚创建或终止任务时调用我的内核模块。
在我的内核模块中,这可以通过 Linux 安全模块很好地完成,但几年前,它们通过取消导出所需的符号来阻止外部模块充当 LSM。
我能找到的唯一其他方法是让我的模块像一个 rootkit。找到系统调用表并将其挂在那里。
修补内核是不可能的。我需要轻松安装我的应用程序。还有其他方法吗?
对于 Linux 上的 Mach 内核 API 仿真,我需要在刚刚创建或终止任务时调用我的内核模块。
在我的内核模块中,这可以通过 Linux 安全模块很好地完成,但几年前,它们通过取消导出所需的符号来阻止外部模块充当 LSM。
我能找到的唯一其他方法是让我的模块像一个 rootkit。找到系统调用表并将其挂在那里。
修补内核是不可能的。我需要轻松安装我的应用程序。还有其他方法吗?
您可以使用Kprobes,它使您能够动态地挂接到内核中的代码。您将需要在涉及创建和销毁为您提供所需信息的流程的功能中找到正确的功能。例如,对于创建的任务,fork.c 中的 do_fork() 将是一个很好的起点。对于销毁的任务,do_exit。您可能想要编写一个 retprobe,它是一种 kprobe,它还可以在函数执行结束时,在它返回之前让您控制它。您希望在函数返回之前进行控制的原因是通过检查返回值来检查它是否成功创建进程。如果出现错误,则函数将返回负值,或者在某些情况下可能返回 0。
你可以通过创建一个 kretprobe 结构来做到这一点:
static struct kretprobe do_fork_probe = {
.entry_handler = (kprobe_opcode_t *) my_do_fork_entry,
.handler = (kprobe_opcode_t *) my_do_fork_ret,
.maxactive = 20,
.data_size = sizeof(struct do_fork_ctx)
};
当控制进入挂钩函数时,my_do_fork_entry 被执行,而 my_do_fork_ret 在它返回之前被执行。您可以按如下方式连接它:
do_fork_probe.kp.addr =
(kprobe_opcode_t *) kallsyms_lookup_name("do_fork");
if ((ret = register_kretprobe(&do_fork_probe)) <0) {
// handle error
}
在你的钩子的实现中,获取参数和返回值有点笨拙。您可以通过保存的寄存器 pt_regs 数据结构获得这些信息。让我们看一下返回钩子,在 x86 上,您可以通过 regs->ax 获得返回值。
static int my_do_fork_ret(struct kretprobe_instance *ri, struct pt_regs *regs)
{
struct do_fork_ctx *ctx = (struct do_fork_ctx *) ri->data;
int ret = regs->ax; // This is on x86
if (ret > 0) {
// It's not an error, probably a valid process
}
}
在入口点,您可以通过寄存器访问参数。例如,在 x86 上,regs->di 是第一个参数,regs->si 是第二个参数,等等。您可以通过 google 获取完整列表。请注意,您不应依赖这些寄存器作为返回挂钩中的参数,因为这些寄存器可能已被其他计算覆盖。
你肯定要跳很多圈才能让它工作,但希望这篇笔记能让你朝着正确的方向前进。