Bol*_*l19 12 linux process open-files files
我不会使用像 AppArmor 抱怨模式这样的复杂工具,我需要简单的工具来告诉我特定程序访问了哪些文件。
ste*_*eve 12
根据 Chris Down 的说法,您可以strace -p
用来检查已经运行的进程,以查看从现在开始直到您终止 strace 或进程本身完成时它打开了哪些文件。
如果您想查看在整个进程期间打开的文件,请从一开始就使用strace
可执行文件名称。添加-f
确保任何分叉的子流程也得到报告。例子
# strace -e open -f /bin/id
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open("/proc/thread-self/attr/current", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/proc/self/task/1581/attr/current", O_RDONLY|O_CLOEXEC) = 3
open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
open("/usr/share/locale/en_US.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 3
open("/etc/group", O_RDONLY|O_CLOEXEC) = 3
open("/etc/group", O_RDONLY|O_CLOEXEC) = 3
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
+++ exited with 0 +++
#
Run Code Online (Sandbox Code Playgroud)
使用lsof
看看有哪些文件,进程目前已经打开
# lsof -p $(pidof NetworkManager)
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
NetworkMa 722 root cwd DIR 253,0 224 64 /
NetworkMa 722 root rtd DIR 253,0 224 64 /
NetworkMa 722 root txt REG 253,0 2618520 288243 /usr/sbin/NetworkManager
NetworkMa 722 root mem REG 253,0 27776 34560 /usr/lib64/libnss_dns-2.17.so
[...]
#
Run Code Online (Sandbox Code Playgroud)
如果您有 SystemTap,您可以监控整个主机是否有打开的文件。
[root@localhost tmp]# cat mon
#!/usr/bin/env stap
probe syscall.open { printf ("pid %d program %s opened %s\n", pid(), execname(), filename) }
# ./mon
pid 14813 program touch opened "/etc/ld.so.cache"
pid 14813 program touch opened "/lib64/libc.so.6"
pid 14813 program touch opened 0x7f7a8c6ec8d0
pid 14813 program touch opened "foo2"
[...]
#
Run Code Online (Sandbox Code Playgroud)
你可以opensnoop
从 BCC使用,它在引擎盖下使用 eBPF:
# ./opensnoop -p 1576
PID COMM FD ERR PATH
1576 snmpd 11 0 /proc/sys/net/ipv6/conf/lo/forwarding
1576 snmpd 11 0 /proc/sys/net/ipv6/neigh/lo/base_reachable_time_ms
1576 snmpd 9 0 /proc/diskstats
1576 snmpd 9 0 /proc/stat
1576 snmpd 9 0 /proc/vmstat
[...]
Run Code Online (Sandbox Code Playgroud)
这是非常高效的,因为它使用 kprobes 而不是必须重新启动系统调用,就像strace
那样。
您也可以使用strace
(可能-f
用于跟踪被跟踪进程的子进程)来执行此操作,但它的操作方式(包括重新启动系统调用作为ptrace 的一部分)会在一定程度上减慢您的应用程序的速度:
# strace -e open -p 15735
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/gconv/gconv-modules.cache", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/gconv/gconv-modules", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/python2.7/site-packages", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 4
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open("/etc/localtime", O_RDONLY|O_CLOEXEC) = 8
[...]
Run Code Online (Sandbox Code Playgroud)
如果需要,您也可以通过这种方式启动您的应用程序,使用strace [executable]
、 或strace -f [executable]
。
我最喜欢的监控应用程序打开哪些文件的工具是强大的监控框架sysdig
。
用于监视由名为 的程序打开的所有打开文件exe_file
:
sudo sysdig -p "proc.name=exe_file %12user.name %6proc.pid %12proc.name %3fd.num %fd.typechar %fd.name" evt.type=open
Run Code Online (Sandbox Code Playgroud)
Monitoring all the files opened in the server:
sudo sysdig -p "%12user.name %6proc.pid %12proc.name %3fd.num %fd.typechar %fd.name" evt.type=open
Run Code Online (Sandbox Code Playgroud)
Creating a trace file that will only contain writing events in home directories (which we can inspect later with sysdig -r writetrace.scap.gz
):
sudo sysdig -p "%user.name %proc.name %fd.name" "evt.type=write and fd.name contains /home/" -z -w writetrace.scap.gz
Run Code Online (Sandbox Code Playgroud)
Seeing everything at syscall level a process named exe_file
does:
sudo sysdig proc.name=exe_file
Run Code Online (Sandbox Code Playgroud)
Sysdig has many chisels, see for more interesting things it can do:
You also have got dtrace
that is not much used in Linux, but is still use a lot with *BSD operating systems:
# Files opened by process,
dtrace -n 'syscall::open*:entry { printf("%s %s",execname,copyinstr(arg0)); }'
Run Code Online (Sandbox Code Playgroud)
Besides sysdig
, strace
and dtrace
, you also have got ltrace
, which records/intercepts signals/dynamic libraries/system calls which are called/received by a process:
ltrace
is a program that simply runs the specified command until it exits. It intercepts and records the dynamic library calls which are called by the executed process and the signals which are received by that process. It can also intercept and print the system calls executed by the program.
$ltrace exe_file
_libc_start_main(0x400624, 1, 0x7ffcb7b6d7c8, 0x400710 <unfinished ...>
time(0) = 1508018406
srand(0x59e288e6, 0x7ffcb7b6d7c8, 0x7ffcb7b6d7d8, 0) = 0
sprintf("mkdir -p -- '/opt/sms/AU/mo'", "mkdir -p -- '%s'", "/opt/sms/AU/mo") = 28
system("mkdir -p -- '/opt/sms/AU/mo'" <no return ...>
--- SIGCHLD (Child exited) ---
<... system resumed> ) = 0
rand(2, 0x7ffcb7b6d480, 0, 0x7f9d6d4622b0) = 0x2d8ddbe1
sprintf("/opt/sms/AU/mo/tmp.XXXXXX", "%s/tmp.XXXXXX", "/opt/sms/AU/mo") = 29
mkstemp(0x7ffcb7b6d5c0, 0x40080b, 0x40081a, 0x7ffffff1) = 3
sprintf("/opt/sms/AU/mo/tmp.XXXXXX", "%s/tmp.XXXXXX", "/opt/sms/AU/mo") = 29
mkstemp(0x7ffcb7b6d5c0, 0x40080b, 0x40081a, 0x7ffffff1) = 4
+++ exited (status 0) +++
Run Code Online (Sandbox Code Playgroud)
If the program is small, you might also consider disassembling it with objdump -d exe_file
or disassembling/decompiling it with Hopper
, to see all the files it deals with.
For more details see: Understanding what a Linux binary is doing
As a first approach, I would also do:
strings exe_file
Run Code Online (Sandbox Code Playgroud)
It is a low cost approach, and with luck some of the files names might just be present in ASCII mode in the binary file with luck.
See also related answer Why are true and false so large?
If binaries/files that come with the distribution you can also fetch the sources from the sources repositories of the distribution, or the official repositories of the actual utility.
As a last resource, you can always use tools like gdb or rr to debug the binary in real time.