Des outils pour afficher les fichiers auxquels un programme accède?

12

Je ne vais pas pour des outils compliqués comme le mode de réclamation AppArmor, j'ai besoin d'outils faciles pour me dire quels fichiers sont accessibles par un programme spécifique.

Boll19
la source
7
Sous quel système d'exploitation?
Jeff Schaller
Cela pourrait également être utile pour expliquer que vous vous attendez à ce que le programme accède aux fichiers de quelle manière - lecture, écriture, ajout, uniquement obtention fstat()ou lstat()info, etc.
Sergiy Kolodyazhnyy
Suse et Ubuntu
Boll19
Peu importe comment je dois savoir, est-ce que fstat () ou lstat () sont en train de programmer?
Boll19
Le commentaire de Sergiy Kolodyazhnyy en d'autres termes: si un programme vérifie la longueur, la date de modification, les autorisations ou d'autres propriétés d'un fichier mais n'accède pas aux données du fichier, le considérez-vous comme "accédant au fichier" ou non?
telcoM

Réponses:

12

Selon Chris Down, vous pouvez utiliser strace -ppour examiner un processus déjà en cours d' exécution, pour voir quels fichiers il ouvre à partir de maintenant jusqu'au moment où vous terminez strace ou le processus lui-même se termine.

Si vous souhaitez voir les fichiers ouverts pendant toute la durée d'un processus, utilisez dès le début stracele nom de l'exécutable. L'ajout -fgarantit que tous les sous-processus bifurqués sont également signalés. Exemple

# 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 +++
#

Utilisation lsofpour voir quels fichiers un processus a actuellement ouverts

# 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
[...]
#

Si vous avez SystemTap, vous pouvez surveiller l'hôte entier pour les fichiers en cours d'ouverture.

[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"
[...]
#
Steve
la source
2
openn'est pas le seul appel système pertinent. Par exemple, il est possible de passer des descripteurs de fichiers entre les processus via un socket Unix, et il y openata l'appel système qui peut également ouvrir un fichier.
kasperd
---- SIGUSR1 {si_signo = SIGUSR1, si_code = SI_TKILL, si_pid = 6026, si_uid = 1002} ---- qu'est-ce que
Boll19
kaspers, dois-je seulement rechercher 'openat' à la commande de sortie strace?
Boll19
Essayer d'ouvrir un fichier (mais le fichier peut ne pas exister) s'affiche également dans les sorties «strace»?
Boll19
Boll19, les fichiers qui ne s'ouvrent pas car ils n'existent pas sont heureusement signalés à l'intérieur strace, voir les lignes ENOENT dans l'exemple.
steve
5

Vous pouvez utiliser opensnoopdepuis BCC, qui utilise eBPF sous le capot:

# ./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
[...]

C'est assez performant car il utilise kprobes au lieu de devoir redémarrer les appels système, comme le stracefait.

Vous pouvez également le faire avec strace(potentiellement avec -fpour suivre les enfants du processus tracé), mais sa façon de fonctionner, impliquant le redémarrage des appels système dans le cadre de ptrace , ralentira quelque peu votre application:

# 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
[...]

Vous pouvez également démarrer votre application de cette façon si vous le souhaitez, en utilisant strace [executable]ou strace -f [executable].

Chris Down
la source
5

Mon outil préféré pour surveiller les fichiers ouverts par une application est le puissant cadre de surveillance sysdig.

Pour surveiller tous les fichiers ouverts ouverts par un programme nommé exe_file:

sudo sysdig -p "proc.name=exe_file %12user.name %6proc.pid %12proc.name %3fd.num %fd.typechar %fd.name" evt.type=open

Surveillance de tous les fichiers ouverts sur le serveur:

sudo sysdig -p "%12user.name %6proc.pid %12proc.name %3fd.num %fd.typechar %fd.name" evt.type=open

Création d'un fichier de trace qui ne contiendra que des événements d'écriture dans les répertoires personnels (que nous pourrons inspecter ultérieurement 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

Tout voir au niveau syscall, un processus nommé exe_file:

sudo sysdig proc.name=exe_file

Sysdig possède de nombreux burins, voir pour des choses plus intéressantes qu'il peut faire:

Vous avez également ce dtracequi n'est pas beaucoup utilisé sous Linux, mais qui est toujours beaucoup utilisé avec les systèmes d'exploitation * BSD:

# Files opened by process,
dtrace -n 'syscall::open*:entry { printf("%s %s",execname,copyinstr(arg0)); }'

De plus sysdig, straceet dtrace, vous avez également ltrace, qui enregistre / intercepte les signaux / bibliothèques dynamiques / appels système qui sont appelés / reçus par un processus:

ltraceest un programme qui exécute simplement la commande spécifiée jusqu'à sa sortie. Il intercepte et enregistre les appels de bibliothèque dynamique qui sont appelés par le processus exécuté et les signaux qui sont reçus par ce processus. Il peut également intercepter et imprimer les appels système exécutés par le programme.

$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) +++  

Si le programme est petit, vous pouvez également envisager de le désassembler objdump -d exe_fileou de le désassembler / décompiler avec Hopper, pour voir tous les fichiers qu'il traite.

Pour plus de détails, voir: Comprendre ce que fait un binaire Linux

Dans un premier temps, je ferais aussi:

strings exe_file

Il s'agit d'une approche à faible coût, et avec de la chance, certains des noms de fichiers peuvent simplement être présents en mode ASCII dans le fichier binaire avec chance.

Voir aussi la réponse connexe Pourquoi le vrai et le faux sont-ils si grands?

S'il s'agit de fichiers binaires / fichiers fournis avec la distribution, vous pouvez également récupérer les sources à partir des référentiels sources de la distribution ou des référentiels officiels de l'utilitaire réel.

Comme dernière ressource, vous pouvez toujours utiliser des outils comme gdb ou rr pour déboguer le binaire en temps réel.

Rui F Ribeiro
la source
aaa43bb66: ~ # sudo proc.name = exe_file sysdig -p "% 12user.name% 6proc.pid% 12proc.name% 3fd.num% fd.typechar% fd.name" evt.type = open Impossible de charger l'erreur du pilote ouverture du périphérique / dev / sysdig0. Assurez-vous que vous disposez des informations d'identification root et que le module sysdig-probe est chargé.
Boll19
/ * <pre> aaa43bb66: ~ # sudo proc.name = exe_file sysdig -p "% 12user.name% 6proc.pid% 12proc.name% 3fd.num% fd.typechar% fd.name" evt.type = open Unable pour charger le pilote en ouvrant le périphérique / dev / sysdig0. Assurez-vous que vous disposez des informations d'identification root et que le module sysdig-probe est chargé. <code> * /
Boll19
@ Boll19 Il y a une erreur, je l'ai corrigée. Ce message semble concerner un sysdigbogue (utilisez-vous ARM?), Veuillez lui poser une nouvelle question.
Rui F Ribeiro