首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用kprobe获取函数参数

使用kprobe获取函数参数
EN

Stack Overflow用户
提问于 2012-05-12 20:15:32
回答 2查看 5.1K关注 0票数 8

我已经在一个函数上放置了一个kprobe,现在我需要在kprobe的预处理函数中获取它的参数值。

下面是我的函数:

代码语言:javascript
复制
void foobar(int arg, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8)
{
    printk("foobar called\n");
}

在其上放置kprobe并调用函数:

代码语言:javascript
复制
...
kp.addr = (kprobe_opcode_t *) foobar;
register_kprobe(&kp);

foobar(0xdead1, 0xdead2, 0xdead3, 0xdead4, 0xdead5, 0xdead6, 0xdead7, 0xdead8);

最后是预处理函数(取自here):

代码语言:javascript
复制
static int inst_generic_make_request(struct kprobe *p, struct pt_regs *regs)
{
  printk(KERN_INFO "eax: %08lx   ebx: %08lx   ecx: %08lx   edx: %08lx\n",
    regs->ax, regs->bx, regs->cx, regs->dx);
    printk(KERN_INFO "esi: %08lx   edi: %08lx   ebp: %08lx   esp: %08lx\n",
      regs->si, regs->di, regs->bp, regs->sp);
    regs++;
    //...
}

预处理程序函数的输出如下所示(我将regs指针递增了3次)

代码语言:javascript
复制
May 10 22:58:07 kernel: [  402.640994] eax: 000dead1   ebx: f7d80086   ecx: 000dead3   edx: 000dead2
May 10 22:58:07 kernel: [  402.640996] esi: 00000000   edi: b77c8040   ebp: 00000000   esp: f7d8006c

May 10 22:58:07 kernel: [  402.641006] eax: f7d8032c   ebx: 000dead5   ecx: 000dead6   edx: 000dead7
May 10 22:58:07 kernel: [  402.641007] esi: 000dead8   edi: f7d800e0   ebp: f7d80330   esp: 08049674

May 10 22:58:07 kernel: [  402.641014] eax: 00000080   ebx: 0992b018   ecx: 0000108e   edx: 0992b008
May 10 22:58:07 kernel: [  402.641015] esi: 08049674   edi: b77c8040   ebp: bfe23fb8   esp: bfe23f50

现在我可以在不同的寄存器中看到foobar函数的参数(但是0xdead4在哪里呢?),它们不应该在堆栈中吗?如何从预处理器函数访问堆栈?或者,在不知道函数的类型和计数的情况下,如何获取函数的参数?我知道这可能不是一件容易的事情(甚至不可能得到所有的值),但只要是近似值就足够了。我正在计算两个函数的参数之间的相关性,我真的不需要精确值。如果我有调用者函数的汇编代码,其中参数被推送到堆栈上,会有帮助吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-05-14 03:32:53

至少有两种方法。

方法1: Jprobes

可能是最简单的方法:如果Jprobes适合您的任务,您可以试一试。它们是kernel docs的近亲(请参阅它们的详细描述和指向kprobes中示例的链接)。

Jprobes允许在进入被探测函数时使用与被探测函数相同的签名来调用函数。您可以通过这种方式自动获取所有参数。

方法2:寄存器和堆栈

另一种方法是对已有的操作进行扩展,并从寄存器和堆栈中检索参数。根据您问题中的输出日志,我假设您使用的是32位x86系统。

x86,32位

据我所知,在x86上的Linux内核中有两个最常见的参数传递约定(详细信息可以在manual by Agner Fog中找到)。请注意,系统调用遵循其他约定(有关详细信息,请参阅manual ),但我假设您对分析“普通”函数而不是系统调用感兴趣。

公约1

对于标记为asmlinkage的函数以及具有可变参数列表的函数,所有参数都在堆栈中传递。函数的返回地址应该在进入函数时位于堆栈的顶部,第一个参数位于它的正下方。第二个参数在第一个参数的下面,依此类推。

因为您有esp的保存值,所以可以找到它所指向的内容。如果使用此约定,则第一个参数应为*(esp+4),第二个参数为*(esp+8),依此类推。

公约2

它似乎用于大多数内核函数,包括您在问题中提到的那个。

内核是用-mregparm=3编译的,因此前3个参数是以eaxedxecx的形式传递的,按照这个顺序,剩下的参数就会堆栈。*(esp+4)应该是第四个参数,*(esp+8)应该是第五个参数,依此类推。

x86,64位

在x86-64上,事情似乎要简单一些。大多数内核函数(包括那些具有可变参数列表的函数)都会获取rdirsirdxrcxr8r9中的前6个参数,按照这个顺序,其余的参数会堆栈。*(esp+8)应该是第七个参数,*(esp+16)应该是第八个参数,依此类推。

编辑:

请注意,在x86-32上,对于内核模式陷阱(包括KProbes依赖的软件断点),esp的值不会保存在pt_regs中。提供了kernel_stack_pointer()函数来检索esp的正确值,它可以在x86-32和x86-64上运行。有关详细信息,请参阅该头文件中对kernel_stack_pointer()的描述。

此外,regs_get_kernel_stack_nth() (也在标头中定义)提供了一种在处理程序中获取堆栈内容的便捷方法。

票数 14
EN

Stack Overflow用户

发布于 2012-05-13 00:22:29

据我所知,分析汇编代码和找到函数参数将是一项困难的任务,特别是在Linux内核代码的情况下。找出函数参数的方法之一是使用调试信息。让我一步一步地把这个放进去。

1)使用调试信息构建内核或模块(-g选项),例如,假设我使用调试信息构建了一个名为'test.ko‘的模块。

2)使用readelf命令对调试信息进行解码。如下所示:

代码语言:javascript
复制
   $readelf debug-dump=info test.ko > log.info

这里我将readelf输出重定向到log.info文件。

3)现在打开log.info并搜索要查找其函数参数的函数,在本例中为'foobar()‘。对于foobar()函数,将有一个带有标记DW_TAG_subprogram的Dwarf条目。在此标记之后,您将找到其他一些带有函数参数名称的dwarf条目。在此条目中,您将在调用函数时找到这些函数参数的位置。例如,它说明第一个参数'arg‘将在ebx寄存器中,第二个参数将在esp+8中,第三个参数将在ecx寄存器中,依此类推。

4)获得这些信息后,在kprobe预处理器中打印所有寄存器。并打印堆栈数据,这可以打印,因为您知道esp寄存器在预处理程序中。

5)根据您在第三步中获得的信息,搜索参数值。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10563635

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档