kprobe
1. kprobe 是什么
Kprobe是一种内核调测手段,它可以动态地跟踪内核的行为、收集debug信息和性能信息。 可以跟踪内核几乎所有的代码地址。这点非常神奇啊,几乎所有的代码,Wow,究竟如何办到这一点呢?我们可以从 debugger 角度出发,探究一下这个问题。
众所周知,debugger 是依赖于 ptrace 这个系统调用,ptrace 赋予了程序调试别的进程的能力,这一点是如何办到的呢?
不得不仔细观察 ptrace 的参数选项,我们发现,ptrace 有一个神奇的选项,叫做 PTRACE_POKETEXT
,文档中说,这个选项可以 Copy the word data to the address addr in the tracee's memory
,既然是修改内存中的内容,也就可以修改指令?
kprobe 是作用于指令级别的探针手段,因此和指令的执行分不开关系,必须要提前明确其中的细节:cpu取指令,执行指令,pc指向下一条指令地址,并且检查标志位(中断,eflags),判断是否会触发异常。而异常处理程序是由操作系统提前设置好的。
2. kprobe 实现原理
有的同学可能很诧异,what?代码段还能修改?不是只有可读可写的数据段之类的,才能修改其中的内容吗?Too young too simple
只要是内存中的东西,内核都有办法将其修改掉,只不过,这种手段并不会轻易暴露给用户,否则可不把系统玩崩了
2.1 register_kprobe
做了下面几件事:
- 根据符号获取 kprobe 的地址,或者直接获取到传入参数中的 kprobe 地址
- 准备一个可执行 mmap 区间中的 slot,并且将被 probe 的指令保存到 slot 中
- 添加这个 kprobe 到全局的 kprobe 哈希表中
- 通过 arm_probe 将对应位置上的指令的第一个字节替换为 0xcc
如此一来,执行到这个 kprobe 之后,就会触发 int3 trap,就会陷入内核设置的 int3 处理函数中,即 do_int3
2.2 kprobe_int3_handler
做了下面几件事:
- 执行 pre_handler
- 设置单步中断标志位
- 将 pt_regs 中的 ip 指向保存的指令地址
1 |
|
2.3 kprobe_debug_handler
重置该 kprobe,并且修正一些由于上一条指令执行导致的问题:
- 函数调用
之所以要修正,主要是因为如果我们刚好将一条 call %rip(xxx)
执行的第一个字节修改为 0xcc
了,在单步执行之后,就会将返回地址压栈,压栈的地址又是通过当前的 EIP 来决定的,但此时的 EIP 已经不是正常执行流中的预期值了,这样,函数调用返回之后,return 直接爆炸
因此,这里有必要修正一下位于栈顶的 return_address
2.4 unregister_kprobe
做了几件事:
- 弹出对应指令地址处,最上方的一个 kprobe 探针
- 对于弹出的探针,将其禁止,也就是通过
disarm_kprobe
将原先指令还原
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!