ExtendedAndroidTools 是 Meta(原 Facebook)实验性质的一个开源项目,用来在 Android NDK 环境下交叉编译常见的 Linux 命令行工具,并通过 Docker 提供统一的构建环境。
在这个项目的 action 页面:
https://github.com/facebookexperimental/ExtendedAndroidTools/actions
可以找到编译好的产物(Artifacts):
| Name |
Size |
Digest |
| bpftools-android-arm64.tar.gz |
119 MB |
sha256:e39dbe2f16b7d0e4cfbff3061d5f6d7e5db74e7414f823786181c230bd62d1fa |
将这个压缩包推送到手机上(例如 /data/local/tmp 目录),然后解压即可。
opensnoop
https://github.com/iovisor/bcc/issues/2670
尝试运行 opensnoop 工具:
oriole:/data/local/tmp/bpftools # ./python3 share/bcc/tools/opensnoop
cannot attach kprobe, probe entry may not exist
Traceback (most recent call last):
File "/data/local/tmp/bpftools/share/bcc/tools/opensnoop", line 389, in <module>
b.attach_kprobe(event=fnname_open, fn_name="syscall__trace_entry_open")
File "/data/local/tmp/bpftools/lib/python3.10/site-packages/bcc-0.29.1+eb8ede2d-py3.10.egg/bcc/__init__.py", line 855, in attach_kprobe
Exception: Failed to attach BPF program b'syscall__trace_entry_open' to kprobe b'sys_open', it's not traceable (either non-existing, inlined, or marked as "notrace")
错误提示无法附加 kprobe,我们来看一下出错位置附近的代码。第 389 行内容如下:
b = BPF(text=bpf_text)
if not is_support_kfunc:
b.attach_kprobe(event=fnname_open, fn_name="syscall__trace_entry_open") //389
b.attach_kretprobe(event=fnname_open, fn_name="trace_return")
其中 fnname_open 变量是通过以下方式获取的:
# open and openat are always in place since 2.6.16
fnname_open = b.get_syscall_prefix().decode() + 'open'
fnname_openat = b.get_syscall_prefix().decode() + 'openat'
fnname_openat2 = b.get_syscall_prefix().decode() + 'openat2'
if b.ksymname(fnname_openat2) == -1:
fnname_openat2 = None
根据错误信息判断,问题很可能出在 get_syscall_prefix() 这个方法上。它在当前环境下返回了 sys_ 前缀,导致程序尝试查找 sys_open 这个不存在的符号。在 ARM64 架构的 Android 内核上,理论上应该返回 __arm64_sys_ 才对。
一个常见的解决方法是调整内核的 kptr_restrict 设置。修复并运行的步骤如下:
oriole:/data/local/tmp/bpftools # echo 1 > /proc/sys/kernel/kptr_restrict
oriole:/data/local/tmp/bpftools # ./python3 share/bcc/tools/opensnoop
PID COMM FD ERR PATH
1303 android.hardwar 9 0 /sys/class/power_supply/battery/present
1303 android.hardwar 9 0 /sys/class/power_supply/battery/capacity
1303 android.hardwar 9 0 /sys/class/power_supply/battery/voltage_now
1303 android.hardwar 9 0 /sys/class/power_supply/battery/current_now
1303 android.hardwar 9 0 /sys/class/power_supply/battery/charge_full
关于 kptr_restrict
kptr_restrict 是一个 内核安全开关,它控制着“内核指针(kernel pointers)”能否在用户态被直接读取。许多 eBPF 工具(包括 bcc 和 bpftrace)都需要通过 /proc/kallsyms、ftrace 等接口来解析内核符号名和地址。如果这个开关设置得过于严格,就会影响以下操作:
- 查找系统调用对应的符号(例如
__x64_sys_open)
- 解析内核栈回溯信息
- 获取内核地址以进行调试分析
该参数的值含义如下:
0:不做限制(最宽松)
1:只对 root 用户或拥有 CAP_SYSLOG/CAP_SYS_ADMIN 权限的进程展示符号和地址,其他用户看不到
2:所有用户态进程都看不到内核指针(包括 root),这是最严格的设置
execsnoop
另一个工具 execsnoop 也可以正常运行。执行该程序,然后在手机上打开一个应用,就能看到类似下面的进程执行记录:
oriole:/data/local/tmp/bpftools # ./python3 share/bcc/tools/execsnoop
PCOMM PID PPID RET ARGS
sh 9633 9484 0 /system/bin/sh -c wm density
wm 9633 9484 0
cmd 9640 9633 0
getprop 9655 9484 0 /system/bin/getprop ro.build.version.emui
sh 9660 9484 0 /system/bin/sh -c pm list features
头文件
部分 BCC 程序在编译或运行时可能需要 Linux 内核头文件。你可以从 Android 官方文档提供的链接中,找到对应内核版本预编译好的头文件:
https://source.android.com/docs/core/architecture/kernel/gki-android14-6_1-release-builds?hl=zh-cn
当然,你也可以选择自行编译所需的内核头文件。
trace
我们来测试一个最简单的 BCC 示例程序 hello_world.py:
https://github.com/iovisor/bcc/blob/master/examples/hello_world.py
将这个程序下载下来,推送到手机的 bpftools 目录中并运行:
oriole:/data/local/tmp/bpftools # ./python3 hello_world.py
此时可能没有输出。我们需要检查系统是否开启了内核追踪(tracing)功能:
oriole:/data/local/tmp/bpftools # cat /sys/kernel/tracing/tracing_on
0
oriole:/data/local/tmp/bpftools # echo 1 > /sys/kernel/tracing/tracing_on
开启追踪功能后,再次运行 hello_world.py 程序,就能看到内核通过 bpf_trace_printk 输出的调试信息了:
oriole:/data/local/tmp/bpftools # ./python3 hello_world.py
b' BlockingExecuto-27576 [001] ...1 163059.827337: bpf_trace_printk: LOOKUP_PREFILTER: b4000076a578aed0 com.android.vending'
b' BlockingExecuto-27576 [002] ...1 163059.827938: bpf_trace_printk: LOOKUP_PREFILTER: b4000076a578aed0 com.android.vending'
b' BlockingExecuto-27576 [002] ...1 163059.828007: bpf_trace_printk: LOOKUP_PREFILTER: b4000076a578aed0 com.android.vending'
b' BlockingExecuto-27576 [003] ...1 163059.836466: bpf_trace_printk: LOOKUP_PREFILTER: b4000076a578aed0 com.android.vending'
b' BlockingExecuto-27576 [003] ...1 163059.847786: bpf_trace_printk: LOOKUP_PREFILTER: b4000076a578aed0 com.android.vending'
b' binder:1968_1-1989 [003] ...1 163064.123537: bpf_trace_printk: LOOKUP_PREFILTER: b4000076a578aed0 su'
b' DG-27742 [002] ...1 163066.051667: bpf_trace_printk: LOOKUP_PREFILTER: b4000076a578aed0 su'