【问题标题】:Intercepting 64-bit Linux kernel function: Length of a function pointer at 32/64-bit?拦截 64 位 Linux 内核函数:函数指针在 32/64 位的长度?
【发布时间】:2011-07-10 11:37:42
【问题描述】:

我正在尝试重新实现旧的庞然大物内核拦截(在this Phrack issue 中描述)。

替换32位函数调用的代码如下:

#define SYSMAPADDR 0x12345678
#define CODESIZE 7
static char acct_code[7] = "\xb8\x00\x00\x00\x00"/*movl $0, %eax*/
"\xff\xe0";/*jmp *%eax*/
*(long*)&acct_code[1] = (long)my_hijacking_function;
// here, use either set_pages_rw or trick CR0 to do this:
memcpy(SYSMAPADDR, acct_code, CODESIZE);

但是原函数的64位地址是0xffffffff12345678(内核位于低内存)。

那么 (long) 新函数指针是否只适合 movl 指令的 4 个 \x00 字节?

顺便说一句,请把这个链接到Can I replace a Linux kernel function with a module?和Overriding functionality with modules in Linux kernel,上面描述的hacky方法更灵活(可以拦截非外部函数=>不需要重新编译内核)。

【问题讨论】:

  • 这个 Phrack 问题已经过时了。 2.6.3x Linux 内核与 2.4.x 完全不同 - 所以我怀疑这个问题的任何内容都适用于当前内核。此外,系统调用的组织方式也不同,以支持现代 CPU 的快速系统调用功能。
  • @datenwolf,它可以通过一些修改工作 - memset.wordpress.com/2010/12/03/… - 只是试图将其修复为 64 位。
  • 感谢 jmkeyes 和 Random832,将在选择的具有不同 2.4-2.6 内核(最新版本)的 32/64 位发行版上设置一组测试。同时,想要更多的想法.. :) 这是一项消耗代表的赏金。

标签: c assembly linux-kernel x86-64 interception


【解决方案1】:

在任何 x86(32 位或 64 位)上,无法直接且无条件地跳转到位移大于 2GB 的地址。

当我前段时间编写一个迂回库时,我能想出的重定向程序流(对于 x86-64)的最佳选择包括通过 M 字节备份目标函数的序言并用覆盖目标函数的序言两条指令。

我使用 %r11 寄存器而不是累加器。根据AMD64 ABI Draft 0.99.5,%r11 是一个临时寄存器,不会跨函数调用保留。

第一条指令movq $addr, %r11 的作用与它看起来完全一样:它将指定的地址加载到寄存器中。第二条指令jmp *%r11 强制无条件间接跳转到存储在%r11 中的地址。

附加到备份指令的末尾应该是另一个无条件间接跳转回原始目标的函数,跳转到被覆盖指令之后的地址。然后,当你想调用原始函数时,你可以调用备份函数序言的地址,程序流程照常进行。

请记住,要备份的字节数M 必须是存储/跳转指令的大小与被覆盖指令的剩余部分的总和。你不想在完成这个巫术之后留下任何部分指令。

【讨论】:

  • 要进行一些实验.. 顺便说一句,2GB 限制是可以的,因为内核将自己加载到较低的内存中,但仍需要 64 位寻址才能运行。
  • 使用movq 将立即数存储到寄存器中是隐式符号扩展的,详见here。
【解决方案2】:

注意:我假设这是针对 x86_64 的。

函数指针是 64 位的,movl 指令零扩展为 64 位寄存器,因此您必须重写机器代码。你想要的指令可能是48 B8 (imm64)(即movq ..., %rax),我认为跳转指令可以不用管,但我对此了解不多。您可能应该在您的问题中添加“x86-64”和“程序集”标签。

【讨论】:

    【解决方案3】:

    您可以使用JMP rel32 (0xE9) 操作从当前地址执行 32 位相对跳转。这将允许您以 5 个字节跳转到源地址 2GB 范围内的任何位置。它还具有不会破坏 %eax 的优点(这在您的情况下可能很重要,也可能不重要)。

    也就是说,我建议改为查看kprobes API。这将为您处理运行时修补的所有艰苦工作。它还处理多个标记应用于相同的功能和其他类似的问题,并且可以移植到多个平台。特别是,如果您的猴子修补方法正在使用中,如果编译进去,它可能会与标记 API 发生冲突,从而导致崩溃。如果动态可修补代码位于函数的前几个字节(LOCK 前缀等),也会导致崩溃。

    您可能还想了解ftrace works 的方式 - 根据内核配置,挂接到 ftrace 可能会更快。

    【讨论】:

    • 嘿,另一个已弃用(支持 Tracepoints)的 API。 “从那时起,内核标记逐渐从内核源代码中删除,并最终在 2009 年 12 月 3 日发布的 Linux 内核 2.6.32[8][9] 中完全删除。”。
    • 另外,添加新的tracepoint/marker似乎还需要重新编译内核代码。
    • 对不起,我把它和 kprobes 弄混了 >_> 我会更新我的答案以获得正确的答案。
    猜你喜欢
    • 2013-01-11
    • 1970-01-01
    • 1970-01-01
    • 2014-12-11
    • 1970-01-01
    • 2012-08-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-29
    相关资源
    最近更新 更多