Android 6.0下LD_PRELOAD是否仍可用?调用失效问题求助
首先明确:Android 6.0(Marshmallow)完全支持LD_PRELOAD机制,你的操作没生效主要是因为Android的安全限制、环境变量设置方式和桌面Linux有差异,下面一步步拆解问题和解决办法:
1. 库文件的权限与存放目录问题
你把libpreload.so放到/data/目录,这个目录默认权限仅对root和system用户开放读写,普通APP进程甚至部分系统进程都无法访问该目录下的文件。更合适的调试用存放位置是/data/local/tmp/——这个目录权限宽松,所有root进程都能正常访问。
修正步骤:
# 推送库到正确目录 adb push libpreload.so /data/local/tmp/ # 给库设置可执行权限(必须,否则动态链接器无法加载) adb shell chmod 755 /data/local/tmp/libpreload.so
2. 环境变量设置方式错误
你用setprop com.foo.bar LD_PRELOAD=xxx是完全错误的——setprop用于设置系统属性,而LD_PRELOAD是进程环境变量,Android不会自动将自定义系统属性转换为环境变量。
针对不同场景,正确的设置方式有两种:
方式一:针对单个进程临时生效(推荐调试用)
直接在启动进程时指定LD_PRELOAD,这是最安全的调试方式:
# 示例:启动一个带LD_PRELOAD的shell,该shell内执行的所有命令都会加载你的库 adb shell su -c "LD_PRELOAD=/data/local/tmp/libpreload.so /system/bin/sh" # 之后在这个shell里执行echo,用strace跟踪就能看到你的钩子输出 strace echo "test"
如果要hook某个APP进程,可以用am命令传递环境变量:
adb shell am start -n com.your.app.package/.MainActivity --es LD_PRELOAD "/data/local/tmp/libpreload.so"
注意:部分APP可能会在启动时重置环境变量,这种情况需要root权限下直接attach进程或修改APP的启动脚本。
方式二:全局生效(不推荐,风险高)
如果要让所有进程都加载你的库,需要修改Android的动态链接器配置文件/system/etc/ld.config.txt:
- 先挂载system分区为可写:
adb shell su -c "mount -o rw,remount /system" - 编辑
ld.config.txt,添加一行:PRELOAD /data/local/tmp/libpreload.so - 重启设备生效
这种方式会影响所有进程,可能导致系统不稳定,仅用于特殊测试场景。
3. SELinux 限制
Android 6.0默认开启SELinux强制模式,你的库文件如果没有正确的SELinux上下文,动态链接器会拒绝加载它。可以先临时关闭SELinux测试是否是这个问题:
adb shell su -c setenforce 0
如果此时LD_PRELOAD生效了,说明是SELinux的问题,需要给库文件设置正确的上下文:
# 查看/data/local/tmp目录的SELinux上下文 adb shell ls -Z /data/local/tmp # 给库设置相同的上下文(示例,以实际输出为准) adb shell su -c chcon u:object_r:shell_data_file:s0 /data/local/tmp/libpreload.so
4. Strace 使用注意事项
确保你strace的是正确的进程,并且跟踪了子进程:
- 用
strace -f <command>跟踪所有子进程(很多Android进程会fork子进程执行任务) - 可以先检查进程是否加载了你的库:
adb shell cat /proc/<pid>/maps | grep libpreload,如果有输出,说明库已经被成功加载
总结
Android 6.0确实支持LD_PRELOAD,但不能照搬桌面Linux的操作流程,核心要解决:库的权限与存放位置、环境变量的正确设置、SELinux的限制这三个问题。按照上面的步骤调整后,你应该能看到strace捕获到预期的write调用输出。
内容的提问来源于stack exchange,提问作者kaloon

