排查LD_PRELOAD拦截需四步:一查环境变量是否设置;二溯来源(用户/系统/容器/systemd);三析so库导出函数及依赖;四验生效性(区分setuid限制、检查/proc/pid/environ和/etc/ld.so.preload)。
排查 LD_PRELOAD 植入的系统调用拦截,关键不是“有没有设”,而是“谁设的、设给谁、设了什么、是否生效”。它不留下明显进程或文件痕迹,但会在动态链接阶段悄然介入。下面分四步直击核心。
一、确认当前环境是否存在 LD_PRELOAD 设置
这是最快速的第一筛。很多报错(如
)就源于此变量残留:
运行
查看是否非空;若输出路径,记下该 .so 文件名和路径
检查是否被子 shell 或服务继承:在新终端中执行同一命令,对比结果
注意空格和引号干扰——有时变量值含多余空格或未闭合引号,也会导致加载失败但不报错
二、追溯 LD_PRELOAD 的来源位置
变量可能来自多个层级,需逐层排查:
Docker Desktop(linux)
当前 Docker 最新稳定版本之一,主要针对稳定性和兼容性进行了修复优化,适合生产环境与日常开发使用。该版本继续强化 AI 开发支持、容器日志管理以及 Docker Engine 的安全能力,对 Windows/macOS/Linux 平台兼容性进行了进一步优化。
下载
用户级配置:搜索
、
、
、
中的
或
后又重新赋值的情况
系统级配置:检查
、
、
,尤其留意由 CUDA、NVIDIA 驱动、安全审计工具或监控 agent 自动写入的脚本
容器与编排环境:Docker 启动时用
;Kubernetes Pod 的
字段或 initContainer 中可能硬编码该变量
systemd 服务:对可疑服务运行
,查看是否注入了 LD_PRELOAD
三、分析预加载库的行为特征
即使找到 .so 文件,也不能只看文件名。要验证它是否真在拦截系统调用:
用
和
确认架构匹配(如 x86_64 程序不能加载 i386 so)
用
查看是否导出常见被 hook 函数
用
看它自身是否依赖异常库(如链接了
这类已知隐藏工具)
临时启用 strace 观察:启动一个简单程序并加 LD_PRELOAD,再运行
,比对函数调用前后行为变化
四、检测运行时是否实际生效
LD_PRELOAD 对 setuid 程序(如
、
)默认无效,glibc 会主动清空该变量。因此需区分场景:
对普通用户程序:用
确认进程内变量真实值
检查是否被绕过:运行
,若权限含
(如
),说明 LD_PRELOAD 在该进程里必然失效
全局生效的真正后门是
—— 它无需环境变量,所有动态链接程序都会加载。用
和
检查是否被篡改或锁定
若怀疑隐蔽 hook,可对比
与
的 PID 差异,缺失的 PID 可能被 LD_PRELOAD + readdir hook 隐藏
LD_PRELOAD cannot be preloadedecho $LD_PRELOAD~/.bashrc~/.bash_profile~/.zshrc~/.profileexport LD_PRELOAD=unset LD_PRELOAD/etc/environment/etc/profile/etc/profile.d/*.sh--env LD_PRELOAD=...env:systemctl show --property=Environment filereadelf -hnm -D /path/to/libxxx.so | grep -E "(open|read|write|exec|fork|malloc)"ldd /path/to/libxxx.solibprocesshider.sostrace -e trace=openat,open,execve -f ./your_program 2>&1 | grep -E "(open|exec)"sudopasswdcat /proc/$(pidof your_program)/environ | tr '\0' '\n' | grep LD_PRELOADls -l /usr/bin/sudos-rwsr-xr-x/etc/ld.so.preloadlsattr /etc/ld.so.preloadstat /etc/ld.so.preloadps auxls /proc/[0-9]* 2>/dev/null | cut -d'/' -f3 | sort -u