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 PythonBPF.get_syscall_fnname(SYSCALLNAME)
andBPF.attach_kprobe()
to associate it.Arguments are specified on the function declaration:syscall__SYSCALLNAME(struct pt_regs *ctx, [, argument1 ...])
.
以
hook
指定uid
的openat
为例,b.get_syscall_fnname("openat")
返回的实际是完整系统调用的内核函数,这里返回__arm64_sys_openat
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F3d9d581b-1cbe-449e-9723-f2162662aa76%2F4f9dc7bb-ab28-4ce1-9b14-fb0e0719cb5a%2FUntitled.png?table=block&id=c866cd72-a4e1-445c-9e6f-ecbbe527e3da&t=c866cd72-a4e1-445c-9e6f-ecbbe527e3da&width=708.0000610351562&cache=v2)
这里需要特别注意,关于
fn_name
必须是特定前缀syscall__
syscall__openat
syscall__hello
syscall__xxxx
都能成功获取到输出,但是如果不以syscall__
开头,虽然无报错并且有输出,但是获取到的参数是错误的。![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F3d9d581b-1cbe-449e-9723-f2162662aa76%2Fe00e28e0-1354-4e94-81c8-cdb9fdf159c2%2FUntitled.png?table=block&id=d89b276a-f79d-41f7-8123-63278cf98334&t=d89b276a-f79d-41f7-8123-63278cf98334&width=708.0000610351562&cache=v2)
通过审BCC源码可以发现,确实限定了
syscall__
的前缀,但是无论如何都会attach
到对应的函数,只不过不能正确的解析参数。原因是在内核4.17后引入了一个包装器CONFIG_ARCH_HAS_SYSCALL_WRAPPER
,ctx
将被包装两次(此补丁通过将系统调用参数包装在第一个参数指向的结构中,改变了系统调用参数从用户空间传递到内核的方式),从而导致无法通过直接访问ctx寄存器的方式访问到调用的参数。以x86架构的为例,系统调用包装函数只有一个
regs
参数对此
bcc
()引入 genParamIndirectAssign
对调用参数进行处理,而这种处理的前提是syscall__
前缀的函数名,那么就很好的解释了上述问题。![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F3d9d581b-1cbe-449e-9723-f2162662aa76%2F26d25e0a-6396-4ea1-8dc5-9518a9e32648%2FUntitled.png?table=block&id=65c1e2e5-c370-405a-b399-e371830f40d8&t=65c1e2e5-c370-405a-b399-e371830f40d8&width=1994&cache=v2)
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F3d9d581b-1cbe-449e-9723-f2162662aa76%2F27eaa223-a3bc-4d51-9cea-f27d63d5b347%2FUntitled.png?table=block&id=8bf5b672-8250-43b0-8876-6461b435d05a&t=8bf5b672-8250-43b0-8876-6461b435d05a&width=1738&cache=v2)
![notion image](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F3d9d581b-1cbe-449e-9723-f2162662aa76%2Fc905d942-6592-4e90-86c1-fbe040d8817a%2FUntitled.png?table=block&id=2a3e3c63-a113-4adb-9ff8-8769a34486c2&t=2a3e3c63-a113-4adb-9ff8-8769a34486c2&width=1791&cache=v2)
查看bcc的解决方案,在BPF构造函数中指定
debug=4
即可查看到处理后的bpfcode
,即先读取真实寄存器结构体指针,然后才读取正确的寄存器。修改代码使其可用,这里需要注意的是如果直接初始化一个结构体并且读取内容到结构体
struct pt_regs regs = {};
bpf_probe_read(®s, 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](https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F3d9d581b-1cbe-449e-9723-f2162662aa76%2F5c3828af-7eec-4c6e-b15b-36d4fd0bd2af%2FUntitled.png?table=block&id=dbbff2f7-294d-4187-9b84-1769288486de&t=dbbff2f7-294d-4187-9b84-1769288486de&width=1873&cache=v2)
- 作者:LLeaves
- 链接:https://lleavesg.top//article/Android%20eBPF%20Syscall%E5%8C%85%E8%A3%85%E5%99%A8%E9%97%AE%E9%A2%98%E5%B0%8F%E8%AE%B0
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章