type
status
date
slug
summary
tags
category
icon
password
😀
在使用BCC与python结合对Android的openat syscall进行追踪时发现一个很有趣的问题,在此记录
首先是BCC文档中给出如何trace syscall 的教程,这里提到必须要syscall__为前缀,很好奇为什么必须要这个前缀,于是开始进行测试。
Syntax: syscall__SYSCALLNAME
syscall__ is a special prefix that creates a kprobe for the system call name provided as the remainder. You can use it by declaring a normal C function, then using the Python BPF.get_syscall_fnname(SYSCALLNAME) and BPF.attach_kprobe() to associate it.
Arguments are specified on the function declaration: syscall__SYSCALLNAME(struct pt_regs *ctx, [, argument1 ...]).
hook指定uidopenat为例,b.get_syscall_fnname("openat") 返回的实际是完整系统调用的内核函数,这里返回__arm64_sys_openat
notion image
这里需要特别注意,关于fn_name 必须是特定前缀syscall__
syscall__openat syscall__hello syscall__xxxx 都能成功获取到输出,但是如果不以syscall__ 开头,虽然无报错并且有输出,但是获取到的参数是错误的。
notion image
通过审BCC源码可以发现,确实限定了syscall__ 的前缀,但是无论如何都会attach到对应的函数,只不过不能正确的解析参数。原因是在内核4.17后引入了一个包装器CONFIG_ARCH_HAS_SYSCALL_WRAPPERctx将被包装两次(此补丁通过将系统调用参数包装在第一个参数指向的结构中,改变了系统调用参数从用户空间传递到内核的方式),从而导致无法通过直接访问ctx寄存器的方式访问到调用的参数。
以x86架构的为例,系统调用包装函数只有一个regs参数
对此bcc ()引入 genParamIndirectAssign 对调用参数进行处理,而这种处理的前提是syscall__ 前缀的函数名,那么就很好的解释了上述问题。
notion image
notion image
查看tracee https://github.com/aquasecurity/tracee 的解决方案,先通过ctx读第一个参数到pt_regs结构体,然后再从其中读取参数。
notion image
查看bcc的解决方案,在BPF构造函数中指定debug=4 即可查看到处理后的bpfcode ,即先读取真实寄存器结构体指针,然后才读取正确的寄存器。
修改代码使其可用,这里需要注意的是如果直接初始化一个结构体并且读取内容到结构体
struct pt_regs regs = {};
bpf_probe_read(&regs, sizeof(struct pt_regs), (const struct user_pt_regs *)ctx->regs[0]);
可能会导致Looks like the BPF stack limit of 512 bytes is exceeded. 错误,即超过512字节的栈大小限制,所以最好使用指针方案。
notion image
eBPF实践之修改bpf_probe_write_user以对抗某加固Frida检测ByteDance-AppShark静态分析工具
  • Twikoo