Bestimmen Sie, ob ein bestimmter Prozess 32- oder 64-Bit ist

14

Bei einem 2.6.x oder neueren Linux-Kernel und einem vorhandenen Benutzerland, das sowohl ELF32- als auch ELF64-Binärdateien ausführen kann (dh längst nicht mehr). Woher weiß ich, dass meine CPU 64-Bit-Betriebssysteme unter Linux unterstützt? ) Wie kann ich feststellen , ob ein bestimmter Prozess ( von PID) läuft im 32- oder 64-Bit-Modus?

Die naive Lösung wäre:

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

Aber werden diese Informationen direkt /procangezeigt, ohne sich darauf zu verlassen libmagic?

Flexo
quelle

Antworten:

21

Wenn Sie sich auf die ELF-Erkennung beschränken möchten, können Sie den ELF-Header von sich /proc/$PID/exeselbst lesen . Es ist ziemlich trivial: Wenn das 5. Byte in der Datei 1 ist, ist es eine 32-Bit-Binärdatei. Wenn es 2 ist, ist es 64-Bit. Für zusätzliche Überprüfung der geistigen Gesundheit:

  1. Wenn die ersten 5 Bytes sind 0x7f, "ELF", 1: Es ist eine 32-Bit-ELF-Binärdatei.
  2. Wenn die ersten 5 Bytes sind 0x7f, "ELF", 2: Es ist eine 64-Bit-ELF-Binärdatei.
  3. Ansonsten: es ist nicht schlüssig.

Sie könnten auch verwenden objdump, aber das nimmt Ihre libmagicAbhängigkeit und ersetzt sie durch eine libelfEins.

Eine andere Möglichkeit : Sie können die /proc/$PID/auxvDatei auch analysieren . Nach proc(5):

Dies enthält den Inhalt der ELF-Interpreterinformationen, die zur Ausführungszeit an den Prozess übergeben werden. Das Format besteht aus einer vorzeichenlosen langen ID und einem vorzeichenlosen langen Wert für jeden Eintrag. Der letzte Eintrag enthält zwei Nullen.

Die Bedeutungen der unsigned longSchlüssel sind in /usr/include/linux/auxvec.h. Du willst AT_PLATFORM, was ist 0x00000f. Zitieren Sie mich nicht, aber es scheint, dass der Wert als interpretiert werden sollte char *, um die Zeichenfolgenbeschreibung der Plattform zu erhalten.

Diese StackOverflow-Frage ist möglicherweise hilfreich.

Noch eine andere Möglichkeit : Sie können den dynamischen Linker ( man ld) anweisen , Informationen über die ausführbare Datei zu sichern. Es druckt die dekodierte AUXV-Struktur als Standardausgabe aus. Warnung: Dies ist ein Hack, aber es funktioniert.

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

Dies zeigt etwas wie:

AT_PLATFORM:     x86_64

Ich habe es mit einer 32-Bit-Binärdatei versucht und i686stattdessen bekommen.

So funktioniert es: LD_SHOW_AUXV=1Weist den Dynamic Linker an, die dekodierte AUXV-Struktur zu sichern, bevor die ausführbare Datei ausgeführt wird. Wenn Sie Ihr Leben nicht wirklich interessant machen möchten, möchten Sie vermeiden, dass die genannte ausführbare Datei tatsächlich ausgeführt wird . Eine Möglichkeit, es zu laden und dynamisch zu verknüpfen, ohne seine main()Funktion aufzurufen , besteht darin, es auszuführen ldd(1). Der Nachteil: LD_SHOW_AUXVWird von der Shell aktiviert, sodass Sie Speicherauszüge der AUXV-Strukturen erhalten für: die Subshell lddund Ihre Zielbinärdatei . Also wir grepfür AT_PLATFORM, aber nur die letzte Zeile behalten.

Parsing auxv : Wenn Sie die auxvStruktur selbst analysieren (ohne auf den dynamischen Loader angewiesen zu sein), gibt es ein kleines Rätsel: Die auxvStruktur folgt der Regel des beschriebenen Prozesses, also sizeof(unsigned long)4 für 32-Bit-Prozesse und 8 für 64 -Bit-Prozesse. Wir können diese Arbeit für uns erledigen. Damit dies auf 32-Bit-Systemen funktioniert, müssen alle Schlüsselcodes 0xffffffffkleiner oder gleich sein. Auf einem 64-Bit-System sind die höchstwertigen 32 Bit Null. Intel-Maschinen sind kleine Endianer, daher folgen diese 32 Bit den niedrigstwertigen im Speicher.

Alles, was Sie tun müssen, ist:

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.

Analysieren der Kartendatei : Dies wurde von Gilles vorgeschlagen, funktionierte aber nicht ganz. Hier ist eine modifizierte Version, die dies tut. Es beruht auf dem Lesen der /proc/$PID/mapsDatei. Wenn in der Datei 64-Bit-Adressen aufgeführt sind, werden 64-Bit-Adressen verarbeitet. Ansonsten sind es 32 Bits. Das Problem liegt darin, dass der Kernel die Ausgabe vereinfacht, indem führende Nullen von Hexadezimaladressen in 4er-Gruppen entfernt werden, sodass der Längen-Hack nicht ganz funktionieren kann. awkzur Rettung:

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

Dies funktioniert, indem die Startadresse der letzten Speicherzuordnung des Prozesses überprüft wird. Sie sind wie aufgeführt 12345678-deadbeef. Wenn es sich also um einen 32-Bit-Prozess handelt, besteht diese Adresse aus acht Hexadezimalziffern und der neunte aus einem Bindestrich. Wenn es sich um eine 64-Bit-Adresse handelt, ist die höchste Adresse länger. Das neunte Zeichen ist eine hexadezimale Ziffer.

Beachten Sie: Alle außer der ersten und der letzten Methode benötigen den Linux-Kernel 2.6.0 oder neuer, da die auxvDatei vorher nicht vorhanden war.

Alexios
quelle
1
Hmmm, ich frage mich, ob sich der ELF-Header in Folgendem befindet /proc/[pid]/auxv: "Die zum Ausführungszeitpunkt an den Prozess übergebenen ELF-Interpreterinformationen. Das Format besteht aus einer vorzeichenlosen langen ID und einem vorzeichenlosen langen Wert für jeden Eintrag" ( man proc).
Goldlöckchen
1
Der Header selbst ist nicht. Ich habe gerade hdeine herausgegeben und es fehlte die magische Zahl. Es mag einige relevante Informationen geben, aber ich denke, dass diese häufiger geändert werden als der ELF-Header. Es wurde auch in 2.6.0 eingeführt, ist also nicht ganz so allgegenwärtig wie /proc/PID/exe. Aber es hat die Architekturinformationen. Ich werde meine Antwort aktualisieren.
Alexios
auxv erwies sich als kniffliger als ich gehofft hatte - sizeof(unsigned long)8 auf 64-Bit oder 4 auf 32-Bit, was bedeutet, dass Sie, um es direkt richtig zu interpretieren, wissen müssen, ob der Prozess 64-Bit oder 32-Bit ist!
Flexo
Du hast absolut recht. Das ist ziemlich nervig. Schnelle Heuristik: Wenn die Bytes 16x + y (4≤y≤7) in der Datei alle Null sind, sehen Sie sich eine ausführbare 64-Bit-Datei an. Dies ist ein Trick: Ich gehe von einer kleinen Endian-Maschine aus und davon aus, dass alle auxvSchlüsselcodes für eine 32-Bit-Version passen unsigned long, sodass die höchstwertigen 32-Bit-Versionen auf einer 64-Bit-Box Null sind.
Alexios
6

Schau rein /proc/$pid/maps. Die Adressbereiche sind über 32-Bit-Adressen (8 Hexadezimalstellen) oder 64-Bit-Adressen (16 Hexadezimalstellen). Dies funktioniert für jede Art von ausführbarer Datei, egal in welchem ​​Format. Sie können nur Informationen zu Prozessen abrufen, die als derselbe Benutzer ausgeführt werden (es sei denn, Sie sind 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

Wenn Sie keine Berechtigung zum Zugriff auf diese Datei haben, besteht meiner Meinung nach die einzige Möglichkeit darin, die ausführbare Datei zu analysieren. (Sie können zwar immer lesen /proc/$pid/stat, aber keines der Felder, die für Prozesse angezeigt werden, die als unterschiedliche Benutzer ausgeführt werden, zeigt die Bitgröße des Prozesses an.) Sie können die Ausführbarkeit des Prozesses mit genau erraten ps -o comm=und im PATHnachschlagen, aber achten Sie darauf, dass der Prozess ausgeführt wird wurde möglicherweise mit einer anderen PATHVersion gestartet oder hat diese möglicherweise umgeschrieben argv[0]. Anschließend können Sie die ausführbare Datei analysieren. Wenn Sie bereit sind, ELF anzunehmen, sehen Sie sich das 5. Byte an .

Gilles 'SO - hör auf böse zu sein'
quelle
Ich habe dein Rezept getestet und es ist fehlgeschlagen. OpenSuSE 12.2, x86-64, Kernel 3.4.63-2.44-Standard, / bin / bash. Die Zeilen / proc / $ pid / maps für die Binärdatei und den ersten Heap werden im 32-Bit-Stil geschrieben, alle anderen im 64-Bit-Stil. Wahrscheinlich werden sie mit "% 08x" gedruckt, aber dieses Rezept muss trotzdem angepasst werden.
Netch
Ich erhalte eine Mischung aus 8, 12 und 16 Nybble-Werten für alle Boxen, mit denen ich es ausprobiert habe. Ohne die Quelle zu überprüfen, ist es wahrscheinlich, dass der Kernel den Abstand auf das niedrigste Vielfache von 16 Bit anpasst, das größer ist als der Adressbereich für jede gedruckte Zeile. Sie müssten also die längste Folge von Hex-Zeichen finden und dann überprüfen.
Alexios
Aber, da die vsyscallKarte immer die höchste ist, könnten Sie mit nur Ändern weg headzu tail- was leider nicht funktionieren , weil proc nicht implementiert seek(2), so wird es etwas hässlicher sein, wieawk /proc/self/maps -- 'END { print substr($1, 0, 9); }'
Alexios
@Netch In der Tat habe ich mir dummerweise die Zeilen vsyscall und stack angesehen und nicht auf die Zuordnung der ausführbaren Datei geachtet. Danke, ich habe nach einer Nicht-32-Bit-Leitung gesucht. Schade, es ist hässlicher, aber das ist das zuverlässigste (zumindest ist es auf x86 sicher, ich habe es nicht mit anderen dualen Architekturen wie sparc und arm überprüft).
Gilles 'SO- hör auf böse zu sein'