gpt4 book ai didi

linux - 在 linux 内核中包装函数时遇到问题

转载 作者:IT王子 更新时间:2023-10-29 00:00:54 26 4
gpt4 key购买 nike

我已经编写了一个将可信路径执行 (TPE) 实现到您的内核中的 LKM:

https://github.com/cormander/tpe-lkm

当我将 WRAP_SYSCALLS 定义为 1 时,我偶尔会遇到内核 OOPS(在这个问题的末尾描述),并且在我的智慧结束时试图追踪它。

一点背景:

由于 LSM 框架不导出其符号,因此我必须创造性地研究如何将 TPE 检查插入正在运行的内核中。我写了一个 find_symbol_address() 函数,它为我提供了我需要的任何函数的地址,而且它工作得很好。我可以这样调用函数:

int (*my_printk)(const char *fmt, ...);
my_printk = find_symbol_address("printk");
(*my_printk)("Hello, world!\n");

而且效果很好。我使用此方法定位 security_file_mmapsecurity_file_mprotectsecurity_bprm_check 函数。

然后我用 asm 覆盖这些函数,跳转到我的函数以执行 TPE 检查。问题是,当前加载的 LSM 将不再执行它 Hook 该函数的代码,因为它已被完全劫持。

这是我做的一个例子:

int tpe_security_bprm_check(struct linux_binprm *bprm) {

int ret = 0;

if (bprm->file) {
ret = tpe_allow_file(bprm->file);
if (IS_ERR(ret))
goto out;
}

#if WRAP_SYSCALLS
stop_my_code(&cs_security_bprm_check);

ret = cs_security_bprm_check.ptr(bprm);

start_my_code(&cs_security_bprm_check);
#endif

out:

return ret;
}

注意 #if WRAP_SYSCALLS 部分之间的部分(默认情况下定义为 0)。如果设置为 1,则会调用 LSM 的 Hook ,因为我将原始代码写回 asm 跳转并调用该函数,但我偶尔会遇到带有“无效操作码”的内核 OOPS:

invalid opcode: 0000 [#1] SMP 
RIP: 0010:[<ffffffff8117b006>] [<ffffffff8117b006>] security_bprm_check+0x6/0x310

我不知道是什么问题。我尝试了几种不同类型的锁定方法(详见start/stop_my_code 内部)都无济于事。要触发内核 OOPS,请编写一个简单的 bash while 循环,无限地启动后台“ls”命令。大约一分钟后,它就会发生。

我正在 RHEL6 内核上对此进行测试,也适用于 Ubuntu 10.04 LTS (2.6.32 x86_64)。

虽然这种方法到目前为止是最成功的,但我尝试了另一种方法,即简单地将内核函数复制到我用 kmalloc 创建的指针,但是当我尝试执行它时,我得到: 内核试图执行受 NX 保护的页面 - 利用尝试? (uid: 0)。如果有人能告诉我如何 kmalloc 空间并将其标记为可执行文件,那也将帮助我解决上述问题。

感谢任何帮助!

最佳答案

1.看来,security_bprm_check()的开头在调用函数之前没有完全恢复。哎呀发生在 security_bprm_check+0x6 ,即在您放置在那里的跳​​跃之后,看起来,跳跃的某些部分在那一刻仍然存在。我现在不能说为什么会发生这种情况。

看看 Kernel Probes (KProbes) 的实现在 x86 上,它可能会给你一些提示。另见 description of KProbes了解详情。 KProbes 需要以安全的方式修补和恢复几乎任意的内核代码片段来完成他们的工作。

2.现在介绍您提到的另一种方法,即函数的复制。以下是一些 hack 并且会被内核开发人员皱眉,但如果没有其他方法,这可能会有所帮助。

您可以分配内存以将函数复制到与分配内核模块代码的内存相同的区域。默认情况下,该区域应该是可执行的。同样,KProbes 使用这个技巧来分配它们的迂回缓冲区。

内存由 module_alloc() 分配函数并由 module_free() 释放.这些函数当然不会导出,但您可以像查找 security_file_mmap() 一样找到它们的地址。等等。出于好奇,您正在使用 kallsyms_on_each_symbol() ,对吧?

如果您以这种方式分配内存,这也有助于避免另一个不太明显的问题。在 x86-64 上,可用于 kmalloc 和模块代码的内存地址区域彼此相距甚远(请参阅 Documentation/x86/x86_64/mm.txt ),超出任何相对跳转的范围。如果内存被映射到模块的地址区,你可以使用near relative jumps和calls来调用复制的函数。这种方式也避免了 RIP 相对寻址的类似问题。

编辑:请注意,在 x86 上,如果您将某些代码复制到不同的内存区域并希望它在那里运行,则可能需要对该代码进行一些更改。至少您需要修复将控制转移到复制代码之外的相关调用和跳转(例如,对另一个函数的调用等)以及具有 RIP 相对寻址的指令。

除此之外,代码中可能还有其他结构需要修复。例如,编译器可能优化了部分甚至全部 switch。语句通过表跳转。也就是说,每个 case 的代码块地址保存在内存中的表中,开关变量是该表的索引。这样,您的模块将执行类似 jmp <table_start>(%reg, N) 的操作,而不是进行多次比较。 (N 是指针的大小,以字节为单位)。也就是说,只是跳转到表中适当元素中的地址。因为此类表是在您复制代码之前为代码创建的,所以可能需要进行修复,否则此类跳转会将执行带回到原始代码段而不是复制的代码段。

关于linux - 在 linux 内核中包装函数时遇到问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6434701/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com