Déterminer si un processus spécifique est 32 ou 64 bits

14

Étant donné un noyau Linux 2.6.x ou plus récent et un espace utilisateur existant capable d'exécuter à la fois des fichiers binaires ELF32 et ELF64 (c'est-à-dire bien passés Comment puis-je savoir que mon processeur prend en charge les systèmes d'exploitation 64 bits sous Linux? ), Comment puis-je déterminer si un processus donné ( by PID) fonctionne en mode 32 ou 64 bits?

La solution naïve serait d'exécuter:

file -L /proc/pid/exe | grep -o 'ELF ..-bit [LM]SB'

mais cette information est-elle exposée directement /procsans s'appuyer sur libmagic?

Flexo
la source

Réponses:

21

Si vous souhaitez vous limiter à la détection ELF, vous pouvez lire l'en- tête ELF de /proc/$PID/exevous - même. C'est assez banal: si le 5ème octet du fichier est 1, c'est un binaire 32 bits. Si c'est 2, c'est 64 bits. Pour une vérification supplémentaire de la santé mentale:

  1. Si les 5 premiers octets sont 0x7f, "ELF", 1: c'est un binaire ELF 32 bits.
  2. Si les 5 premiers octets sont 0x7f, "ELF", 2: c'est un binaire ELF 64 bits.
  3. Sinon: ce n'est pas concluant.

Vous pouvez également utiliser objdump, mais cela supprime votre libmagicdépendance et la remplace par une libelfautre.

Une autre façon : vous pouvez également analyser le /proc/$PID/auxvfichier. Selon proc(5):

Il contient le contenu des informations d'interpréteur ELF transmises au processus au moment de l'exécution. Le format est un ID long non signé plus une valeur longue non signée pour chaque entrée. La dernière entrée contient deux zéros.

Les significations des unsigned longtouches sont là /usr/include/linux/auxvec.h. Vous voulez AT_PLATFORM, ce qui est 0x00000f. Ne me citez pas à ce sujet, mais il semble que la valeur devrait être interprétée comme un char *pour obtenir la description de la chaîne de la plate-forme.

Vous pouvez trouver cette question StackOverflow utile.

Encore une autre façon : vous pouvez demander au linker dynamique ( man ld) de vider les informations sur l'exécutable. Il imprime sur la sortie standard la structure AUXV décodée. Attention: c'est un hack, mais ça marche.

LD_SHOW_AUXV=1 ldd /proc/$SOME_PID/exe | grep AT_PLATFORM | tail -1

Cela montrera quelque chose comme:

AT_PLATFORM:     x86_64

Je l'ai essayé sur un binaire 32 bits et l'ai obtenu à la i686place.

Comment cela fonctionne: LD_SHOW_AUXV=1demande au Dynamic Linker de vider la structure AUXV décodée avant d'exécuter l'exécutable. À moins que vous n'aimiez vraiment rendre votre vie intéressante, vous voulez éviter d' exécuter réellement ledit exécutable. Une façon de le charger et de le lier dynamiquement sans réellement appeler sa main()fonction est de l'exécuter ldd(1). L'inconvénient: LD_SHOW_AUXVest activé par le shell, vous obtiendrez donc des vidages des structures AUXV pour: le sous-shell ldd, et votre binaire cible. Nous avons donc greppour AT_PLATFORM, mais ne gardons que la dernière ligne.

Analyse Auxv : si vous analysez la auxvstructure vous-même (sans compter sur le chargeur dynamique), il y a un peu d'énigme: la auxvstructure suit la règle du processus qu'elle décrit, elle sizeof(unsigned long)sera donc 4 pour les processus 32 bits et 8 pour 64 processus de bits. Nous pouvons faire en sorte que cela fonctionne pour nous. Pour que cela fonctionne sur les systèmes 32 bits, tous les codes clés doivent être 0xffffffffou moins. Sur un système 64 bits, les 32 bits les plus significatifs seront nuls. Les machines Intel sont peu endians, donc ces 32 bits suivent les moins significatifs en mémoire.

À ce titre, il vous suffit de:

1. Read 16 bytes from the `auxv` file.
2. Is this the end of the file?
3.     Then it's a 64-bit process.
4.     Done.
5. Is buf[4], buf[5], buf[6] or buf[7] non-zero?
6.     Then it's a 32-bit process.
7.     Done.
8. Go to 1.

Analyser le fichier de cartes : cela a été suggéré par Gilles, mais n'a pas vraiment fonctionné. Voici une version modifiée qui le fait. Il repose sur la lecture du /proc/$PID/mapsfichier. Si le fichier répertorie les adresses 64 bits, le processus est de 64 bits. Sinon, c'est 32 bits. Le problème réside dans le fait que le noyau simplifiera la sortie en supprimant les zéros de tête des adresses hexadécimales en groupes de 4, de sorte que le hack de longueur ne peut pas tout à fait fonctionner. awkà la rescousse:

if ! [ -e /proc/$pid/maps ]; then
    echo "No such process"
else
    case $(awk </proc/$pid/maps -- 'END { print substr($1, 0, 9); }') in
    *-) echo "32 bit process";;
    *[0-9A-Fa-f]) echo "64 bit process";;
    *) echo "Insufficient permissions.";;
    esac
 fi

Cela fonctionne en vérifiant l'adresse de départ de la dernière carte mémoire du processus. Ils sont listés comme 12345678-deadbeef. Donc, si le processus est de 32 bits, cette adresse comportera huit chiffres hexadécimaux et le neuvième sera un tiret. S'il s'agit d'un 64 bits, l'adresse la plus élevée sera plus longue que cela. Le neuvième caractère sera un chiffre hexadécimal.

Attention: toutes les méthodes sauf la première et la dernière ont besoin du noyau Linux 2.6.0 ou plus récent, car le auxvfichier n'était pas là auparavant.

Alexios
la source
1
Hmmm, je me demande si l'en-tête ELF est dans /proc/[pid]/auxv: "les informations d'interpréteur ELF transmises au processus au moment de l'exécution. Le format est un ID long non signé plus une valeur longue non signée pour chaque entrée" ( man proc).
goldilocks
1
L'en-tête lui-même ne l'est pas. Je viens d'en hdéditer un et il lui manquait le chiffre magique. Il peut y avoir des informations pertinentes ici, mais je pense qu'elles seraient sujettes à des changements plus fréquents que l'en-tête ELF lui-même. Il a également été introduit dans 2.6.0, il n'est donc pas aussi omniprésent que /proc/PID/exe. Mais il contient les informations sur l'architecture. Je mettrai à jour ma réponse.
Alexios
auxv s'est avéré être plus délicat que je l'espérais - sizeof(unsigned long)est 8 sur 64 bits ou 4 sur 32 bits, ce qui signifie que pour l'interpréter correctement directement, vous devez savoir si le processus est 64 bits ou 32 bits pour commencer!
Flexo
Vous avez absolument raison. C'est assez ennuyeux. Heuristique rapide: si les octets 16x + y (4≤y≤7) sont tous nuls dans le fichier, vous regardez un exécutable 64 bits. C'est un kludge: je suppose une petite machine endian, et que tous les auxvcodes clés correspondent à un 32 bits unsigned long, donc le 32 bits le plus significatif sur une boîte de 64 bits serait zéro.
Alexios
6

Regardez /proc/$pid/maps. Les plages d'adresses sont sur des adresses 32 bits (8 chiffres hexadécimaux) ou des adresses 64 bits (16 chiffres hexadécimaux). Cela fonctionne pour tout type d'exécutable, quel que soit le format. Vous pouvez uniquement obtenir des informations sur les processus exécutés en tant que même utilisateur (sauf si vous êtes root).

if ! [ -e /proc/$pid/maps ]; then
  echo No such process
elif grep -q '^........[^-]' /proc/$pid/maps; then
  echo 64-bit
elif grep -q . /proc/$pid/maps; then
  echo 32-bit
else
  echo Insufficient permissions
fi

Si vous n'avez pas la permission d'accéder à ce fichier, alors je pense que la seule façon est d'essayer d'analyser l'exécutable. (Bien que vous puissiez toujours lire /proc/$pid/stat, aucun des champs affichés pour les processus exécutés en tant qu'utilisateurs différents ne révèle la taille en bits du processus.) Vous pouvez faire une bonne estimation de l'exécutable du processus avec ps -o comm=, et le rechercher dans le PATH- mais attention que le processus peut avoir été lancé avec un autre PATH, ou peut avoir été réécrit argv[0]. Vous pouvez ensuite analyser l'exécutable - si vous êtes prêt à assumer ELF, regardez le 5ème octet .

Gilles 'SO- arrête d'être méchant'
la source
J'ai testé votre recette et elle a échoué. OpenSuSE 12.2, x86-64, noyau 3.4.63-2.44 par défaut, / bin / bash. Les lignes / proc / $ pid / maps pour le binaire et le premier segment sont écrites en style 32 bits, mais toutes les autres sont en style 64 bits. Ils sont probablement imprimés en utilisant "% 08x", mais de toute façon cette recette doit être ajustée.
Netch
J'obtiens un mélange de valeurs de 8, 12 et 16 nybbles sur toutes les boîtes avec lesquelles j'ai essayé. Sans vérifier la source, je suppose que le noyau ajuste le remplissage au plus petit multiple de 16 bits supérieur à la plage d'adresses pour chaque ligne imprimée, vous devez donc trouver la plus longue séquence de caractères hexadécimaux, puis vérifiez.
Alexios
Mais, puisque la vsyscallcarte est toujours le plus haut, vous pouvez vous contenter de changer juste headpour tail- qui, malheureusement, ne fonctionnera pas parce que proc n'implémente pas seek(2), donc ça va être quelque chose plus laid, commeawk /proc/self/maps -- 'END { print substr($1, 0, 9); }'
Alexios
@Netch En effet, j'ai stupidement regardé les lignes vsyscall et stack et je n'ai pas fait attention au mappage de l'exécutable. Merci, j'ai mis à jour pour rechercher toute ligne non 32 bits. Dommage, c'est plus laid, mais c'est le plus fiable (au moins c'est sûr-feu sur x86, je n'ai pas vérifié avec d'autres architectures doubles telles que sparc et arm).
Gilles 'SO- arrête d'être méchant'