Pet*_*jci 6 linux debugging assembly kernel
我已经在函数上放了一个kprobe,现在我需要在kprobe的预处理函数中获取它的参数值.
这是我的功能:
void foobar(int arg, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8)
{
printk("foobar called\n");
}
Run Code Online (Sandbox Code Playgroud)
把kprobe放在上面并调用函数:
...
kp.addr = (kprobe_opcode_t *) foobar;
register_kprobe(&kp);
foobar(0xdead1, 0xdead2, 0xdead3, 0xdead4, 0xdead5, 0xdead6, 0xdead7, 0xdead8);
Run Code Online (Sandbox Code Playgroud)
最后是预处理功能(取自此处):
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++;
//...
}
Run Code Online (Sandbox Code Playgroud)
预处理程序函数的输出看起来像这样(我增加regs指针3次)
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
Run Code Online (Sandbox Code Playgroud)
现在我可以foobar在各种寄存器中看到函数的参数(但是在哪里0xdead4?),它们不应该在堆栈中吗?如何从预处理程序功能访问堆栈?或者如何在不知道其类型和数量的情况下获取任何函数的参数?我知道这可能不是一件容易的事(甚至不可能获得所有值),但只有大约值才足够.我正在计算两个函数的参数之间的相关性,我真的不需要精确的值.如果我有调用函数的汇编代码,其中参数被压入堆栈,它会有帮助吗?
Eug*_*ene 12
至少有两种方法.
可能是最简单的一个:如果Jprobes适合你的任务,你可以尝试一下.他们是kprobes的近亲(参见他们的详细描述和内核文档中示例的链接).
Jprobes允许在进入后者时使用与探测到的功能相同的签名来调用您的函数.您可以通过这种方式自动获取所有参数.
另一种方法可能是扩展你已经做过的事情,并从寄存器和堆栈中检索参数.从您的问题中的输出日志,我假设您正在使用32位x86系统.
据我所知,在x86上的Linux内核中有两个最常见的参数传递约定(详细信息可在Agner Fog的手册中找到).请注意,系统调用遵循其他约定(有关详细信息,请参阅手册),但我假设您有兴趣分析"普通"函数而不是系统调用.
对于标记的函数asmlinkage以及具有可变参数列表的函数,所有参数都在堆栈上传递.函数的返回地址应该在函数入口处的堆栈顶部,第一个参数位于它的正下方.第二个参数低于第一个参数,依此类推.
由于您拥有已保存的值esp,您可以找到它指向的内容.*(esp+4)应该是第一个参数,*(esp+8)- 第二个参数,等等,如果使用这个约定.
它似乎用于大多数内核函数,包括您在问题中提到的函数.
内核编译时-mregparm=3,因此前3个参数传递中eax,edx并ecx以该顺序外,其余去堆.*(esp+4)应该是第四个参数,*(esp+8)- 第五个参数,依此类推.
似乎x86-64上的事情有点简单.大多数的内核函数(包括那些带有可变参数列表)获得第6个参数中rdi,rsi,rdx,rcx,r8,r9,按照这个顺序,其余的去堆.*(esp+8)应该是第7个参数,*(esp+16)- 第8 个参数,依此类推.
编辑:
请注意,在x86-32上,内存模式陷阱(包括KProbes所依赖的软件断点)的值esp不会保存pt_regs.<asm/ptrace.h>提供 kernel_stack_pointer()了检索正确值的函数esp,它在x86-32和x86-64上都有效.有关kernel_stack_pointer()详细信息,请参阅该头文件中的说明.
此外,regs_get_kernel_stack_nth()(也在该头文件中定义)提供了一种在处理程序中获取堆栈内容的便捷方法.