背景
在分析 Android 应用/系统的一些问题时,有时可能需要得知应用是否有直接或间接调用了系统的某个 native 函数,或调用某个函数时传入的参数情况。
uprobe_events 实战
uprobe_events 是 Linux 内核提供的一种调试方案,可以跟踪一个用户态函数(或是函数内的某行代码)是否有被调用,以及函数入参等,详细介绍可阅览官方文档 docs.kernel.org/trace/uprob… ,本文主要关注如何快速使用 uprobe_events。
下面以跟踪系统内的进程都打开了什么文件为例,演示如何快速使用 uprobe_events。
step 1
使用 readelf 获取 open64 函数在 libc.so 中的偏移量:
flame:/ # readelf -s /apex/com.android.runtime/lib64/bionic/libc.so | grep open64
786: 000000000008bc10 980 FUNC GLOBAL DEFAULT 15 freopen64
904: 000000000008cb10 172 FUNC GLOBAL DEFAULT 15 funopen64
1154: 000000000007a7f0 404 FUNC GLOBAL DEFAULT 15 open64
1225: 000000000008b878 392 FUNC GLOBAL DEFAULT 15 fopen64
6160: 000000000007a7f0 404 FUNC GLOBAL DEFAULT 15 open64
6652: 000000000008b878 392 FUNC GLOBAL DEFAULT 15 fopen64
6653: 000000000008bc10 980 FUNC GLOBAL DEFAULT 15 freopen64
6677: 000000000008cb10 172 FUNC GLOBAL DEFAULT 15 funopen64
可以看到偏移量为 0x7a7f0。
step 2
在 /sys/kernel/tracing 目录下执行如下命令:
echo 'p:open_probe /apex/com.android.runtime/lib64/bionic/libc.so:0x7a7f0 pathname=+0(%x0):string' > uprobe_events
其中 [open_probe] 可以替换为其它字符串。+0(%x0):string 表示将 open64 函数的第一个参数作为字符串输出。open64 函数的实现如下,其第一个参数代表文件路径。
bionic/libc/bionic/open.cpp
int open(const char* pathname, int flags, ...) {
mode_t mode = 0;
if (needs_mode(flags)) {
va_list args;
va_start(args, flags);
mode = static_cast<mode_t>(va_arg(args, int));
va_end(args);
}
return FDTRACK_CREATE(__openat(AT_FDCWD, pathname, force_O_LARGEFILE(flags), mode));
}
__strong_alias(open64, open);
step 3
在 /sys/kernel/tracing 目录下执行如下命令:
echo 1 > events/uprobes/enable
echo 1 > tracing_on
其中第一条命令表示打开 uprobe_events 跟踪点,第二条命令表示打开 Linux 内核 ftrace 输出开关。
step 4
在 /sys/kernel/tracing 目录下执行如下命令:
cat trace_pipe | grep open_probe
从桌面启动一个 App,得到如下输出结果(节选):
RenderThread-24323 [005] .... 17932.382490: open_probe: (0x76e789a7f0) pathname="/sys/devices/system/cpu/present"
RenderThread-24323 [005] .... 17932.382534: open_probe: (0x76e789a7f0) pathname="/sys/devices/system/cpu/cpu0/cpu_capacity"
RenderThread-24323 [005] .... 17932.382546: open_probe: (0x76e789a7f0) pathname="/sys/devices/system/cpu/cpu1/cpu_capacity"
RenderThread-24323 [005] .... 17932.382556: open_probe: (0x76e789a7f0) pathname="/sys/devices/system/cpu/cpu2/cpu_capacity"
RenderThread-24323 [005] .... 17932.382564: open_probe: (0x76e789a7f0) pathname="/sys/devices/system/cpu/cpu3/cpu_capacity"
RenderThread-24323 [005] .... 17932.382573: open_probe: (0x76e789a7f0) pathname="/sys/devices/system/cpu/cpu4/cpu_capacity"
RenderThread-24323 [005] .... 17932.382581: open_probe: (0x76e789a7f0) pathname="/sys/devices/system/cpu/cpu5/cpu_capacity"
RenderThread-24323 [005] .... 17932.382589: open_probe: (0x76e789a7f0) pathname="/sys/devices/system/cpu/cpu6/cpu_capacity"
RenderThread-24323 [005] .... 17932.382597: open_probe: (0x76e789a7f0) pathname="/sys/devices/system/cpu/cpu7/cpu_capacity"
RenderThread-24323 [007] .... 17932.383114: open_probe: (0x76e789a7f0) pathname="/sys/class/kgsl/kgsl-3d0/gpu_model"
可以看到 RenderThread open 了一些 cpu 和 gpu 有关的节点。