获取kprobes嗅探的函数参数

获取kprobes嗅探的函数参数

疑问:

我已经用kprobes对一个函数进行嗅探,我需要在pre_handler处理函数里获取被嗅探函数的参数值。

被嗅探函数如下:

void foobar(int arg, int arg2, int arg3,int arg4, int arg5, int arg6, int arg7, int arg8)

{

    printk("foobarcalled\n");

}

设置嗅探:

...
kp.addr = (kprobe_opcode_t *) foobar;
register_kprobe(&kp);
 
foobar(0xdead1, 0xdead2, 0xdead3, 0xdead4, 0xdead5, 0xdead6, 0xdead7, 0xdead8);

pre_handler处理函数如下:

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++;
    //...
}

运行后,pre_handler处理函数输出如下:

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在哪里?),它们不是应该在栈里吗?我怎样才能在pre_handler处理函数里访问这些栈?又或者说我怎样才能获取任何不知道参数类型和个数的函数的参数?我知道这不是一件容易的事(甚至不可能获取全部参数),即使获取约值也行,我已经计算了两个函数间的参数,所以不需要太精确。如果我有参数在栈里的被调用函数的汇编代码,这是否有帮助?

 

回答

至少有两种方法。

1、方法一:Jprobes

估计是最简单的一种方法:如果你的任务可以用Jprobes,你可以试一下。Jprobes是基于kprobes实现的(详细描述和使用例子可以看:http://www.mjmwired.net/kernel/Documentation/kprobes.txt)。

Jprobes允许在被嗅探函数入口处调用与被嗅探函数原型一致的处理函数,你自然就可以通过这种方式获取它的所有参数了。

2、方法二:寄存器和栈

另一种方法就如你已经做得,稍微复杂一点。从你的输出日志看,我猜你是在32位X86系统上做的。

2.1、 32位X86

正如我们所见,在X86的linux内核(详见http://www.agner.org/optimize/calling_conventions.pdf)传递参数时有两个最常见的规律。需要注意的是系统调用可能遵循其他规律(详见man手册http://www.agner.org/optimize/calling_conventions.pdf),但我想你感兴趣的是分析“普通”函数而非系统调用。

规律1:

对于asmlinkage 标志和有变量参数列表的函数,全部参数压入栈内。函数的返回地址在入口函数的栈顶,第一个参数位于它的后面,第二个参数跟在第一个的后面,以此类推。

比如,在这种规律下如果你保存了esp的值,你会发现:*(esp+4)就是第一个参数,*(esp+8)就是第二个参数,以此类推。

规律2:

包括你所列举的例子在内的大多数函数都遵循这种规律。

内核编译时是带参数-mregparm=3的,所以前3个参数保存在eax、ebx和ecx,按照这种顺序,其他参数压入栈内,*(esp+4) 就是第4个参数,*(esp+8) 就是第5个参数。

2.2、64位X86

在X86-64上会简单一些。大部分内核函数(包括有变量参数列表的函数),可以通过寄存器rdi,、rsi,、rdx,、rcx,、r8,、r9获取前6个参数,按照这种顺序,其他参数压入栈内,*(esp+8) 就是第7个参数, *(esp+16) 就是第8个参数。

2.3、注意

值得注意的是:在X86-32系统上,并没有为内核模式陷阱(包括kprobes依赖的断点)把esp的值保存到pt_regs上。 <asm/ptrace.h> 提供kernel_stack_pointer()函数来获取当前esp的值,它在X86-32和X86-64上均有效。详见其头文件。

另外,regs_get_kernel_stack_nth()(也是定义在 <asm/ptrace.h>)能方便地获取处理器堆栈的内容。

 

英语原文:http://stackoverflow.com/questions/10563635/getting-function-arguments-using-kprobes

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值