正确从probes中取寄存器

记录一下
正确从 probes 中取寄存器

kprobe 类探针都是可以取到进入内核之前的 userspace regs 的

从结构体的定义中可以发现自带了一个所谓的 pt_regs,然而直接打印这个 pt_regs 可以发现,好像得到的 FF 开头的地址实际上都是内核中的地址。因此,我们不能直接访问这个 pt_regs

取 userspace reg

经过一番查找,我在 stack overflow 上发现了一个帖子,其中提到如果需要访问用户寄存器需要先取到 task

可以使用 helper 实现取当前 task

 let task = bpf_get_current_task() as *const u8;

至于这个结构体定义,可以参照之前使用 btf 取结构体的方法

使用 map 来储存结构体

然后使用 probe read 把这个结构体读到程序中,然而这个结构体非常大,会导致爆栈

所以要创建一个 map,这里直接使用 array map 来储存

#[map]
static mut TASK_STRUCT_BUF_ARR: Array<[u8; 2048]> = Array::with_max_entries(2, 0);

然后取这个 map 元素的指针,使用 probe read 就可以了

读取寄存器

拿到 task_struct 之后,要读取寄存器,还得了解一下用户层进行 syscall 调用之后,内核是如何保存 context 的

根据 kernel 带师 a3 的说法,是在进入内核态之前压到了内核栈底。但是如果要从 task 取到 pt_regs, 需要一些别的操作。

linux 内核提供了一个宏,用于完成这个操作,位于 arch/{ARCH}/include/asm/processor.h 里面,对于 Android,内核 5.10 版本,简单翻一下就能翻到

#define task_pt_regs(p) \
    ((struct pt_regs *)(THREAD_SIZE + task_stack_page(p)) - 1)

这个 task_stack_page 只是一个简单的取结构体字段
主要的是这个 THREAD_SIZE 与 -1
事实上就是兜兜转转拿到了内核栈栈底

这个 THREAD_SIZE 也可以通过简单的搜索搜到,事实上就是 1 << 14 (aarch64)

接下来就能用 rust 来实现了

        if let Some(task_ptr) = tsk_st {
            let tsk_struct_ptr = task_ptr as *mut task_struct;
            let stack_ptr = (*tsk_struct_ptr).stack as u64;
            let pt_regs_ptr = (stack_ptr + PAGE_SIZE - size_of::<pt_regs>() as u64) as *mut pt_regs;
            let pt_regs = bpf_probe_read(pt_regs_ptr).unwrap();
            pc = pt_regs.__bindgen_anon_1.user_regs.pc;
        }
正文完
 0
评论(没有评论)