Determina se un processo specifico è a 32 o 64 bit


14

Dato un kernel Linux 2.6.xo più recente e un'area utente esistente in grado di eseguire sia i binari ELF32 che ELF64 (vale a dire ben oltre Come faccio a sapere che la mia CPU supporta i sistemi operativi a 64 bit su Linux? ) Come posso determinare se un determinato processo ( tramite PID) funziona in modalità a 32 o 64 bit?

La soluzione ingenua sarebbe quella di eseguire:

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

ma queste informazioni sono esposte direttamente /procsenza fare affidamento libmagic?

Risposte:


21

Se vuoi limitarti al rilevamento ELF, puoi leggere l' intestazione ELF di /proc/$PID/exete stesso. È abbastanza banale: se il 5 ° byte nel file è 1, è un file binario a 32 bit. Se è 2, è a 64 bit. Per un ulteriore controllo di integrità:

  1. Se i primi 5 byte sono 0x7f, "ELF", 1: è un binario ELF a 32 bit.
  2. Se i primi 5 byte sono 0x7f, "ELF", 2: è un binario ELF a 64 bit.
  3. Altrimenti: è inconcludente.

Potresti anche usare objdump, ma questo toglie la tua libmagicdipendenza e la sostituisce con una libelf.

Un altro modo : puoi anche analizzare il /proc/$PID/auxvfile. Secondo proc(5):

Questo contiene il contenuto delle informazioni sull'interprete ELF trasmesse al processo al momento dell'esecuzione. Il formato è un ID lungo senza segno più un valore lungo senza segno per ogni voce. L'ultima voce contiene due zeri.

I significati delle unsigned longchiavi sono dentro /usr/include/linux/auxvec.h. Tu vuoi AT_PLATFORM, che è 0x00000f. Non citarmi su questo, ma sembra che il valore debba essere interpretato come a char *per ottenere la descrizione della stringa della piattaforma.

Questa domanda StackOverflow può essere utile.

Ancora un altro modo : puoi istruire il linker dinamico ( man ld) a scaricare informazioni sull'eseguibile. Stampa sull'output standard la struttura AUXV decodificata. Attenzione: questo è un trucco, ma funziona.

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

Questo mostrerà qualcosa di simile:

AT_PLATFORM:     x86_64

L'ho provato su un binario a 32 bit e ho ottenuto i686invece.

Come funziona: LD_SHOW_AUXV=1indica a Dynamic Linker di scaricare la struttura AUXV decodificata prima di eseguire l'eseguibile. A meno che non ti piaccia davvero rendere la tua vita interessante, vuoi evitare di eseguire effettivamente detto eseguibile. Un modo per caricarlo e collegarlo dinamicamente senza effettivamente chiamarne la main()funzione è eseguirlo ldd(1). Il rovescio della medaglia: LD_SHOW_AUXVè abilitato dalla shell, quindi otterrai dump delle strutture AUXV per: la subshell ldde il tuo binario di destinazione. Quindi siamo grepper AT_PLATFORM, ma manteniamo solo l'ultima riga.

Analisi ausiliaria : se analizzi tu stesso la auxvstruttura (non basandoti sul caricatore dinamico), allora c'è un po 'di enigma: la auxvstruttura segue la regola del processo che descrive, quindi sizeof(unsigned long)saranno 4 per i processi a 32 bit e 8 per 64 processi a bit. Possiamo farlo funzionare per noi. Affinché ciò funzioni su sistemi a 32 bit, tutti i codici chiave devono essere 0xffffffffo meno. Su un sistema a 64 bit, i 32 bit più significativi saranno zero. I computer Intel sono piccoli endian, quindi questi 32 bit seguono quelli meno significativi in ​​memoria.

Pertanto, tutto ciò che devi fare è:

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.

Analizzare il file delle mappe : questo è stato suggerito da Gilles, ma non ha funzionato del tutto. Ecco una versione modificata che lo fa. Si basa sulla lettura del /proc/$PID/mapsfile. Se il file elenca gli indirizzi a 64 bit, il processo è a 64 bit. Altrimenti, sono 32 bit. Il problema sta nel fatto che il kernel semplifica l'output rimuovendo gli zero iniziali dagli indirizzi esadecimali in gruppi di 4, quindi l'hacking di lunghezza non può funzionare del tutto. awkAl salvataggio:

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

Funziona controllando l'indirizzo iniziale dell'ultima mappa di memoria del processo. Sono elencati come 12345678-deadbeef. Quindi, se il processo è a 32 bit, quell'indirizzo avrà una lunghezza di otto cifre esadecimali e il nono sarà un trattino. Se è a 64 bit, l'indirizzo più alto sarà più lungo di quello. Il nono carattere sarà una cifra esadecimale.

Attenzione: tutti tranne il primo e l'ultimo metodo richiedono il kernel Linux 2.6.0 o successivo, poiché il auxvfile non era presente prima.


1
Hmmm, mi chiedo se l'intestazione ELF sia presente /proc/[pid]/auxv: "le informazioni sull'interprete ELF sono passate al processo al momento dell'esecuzione. Il formato è un ID lungo senza segno più un valore lungo senza segno per ogni voce" ( man proc).
Riccioli d'oro

1
L'intestazione stessa non lo è. Ne ho appena hdeditato uno e mancava il numero magico. Ci possono essere alcune informazioni rilevanti lì, ma penso che sarebbero soggette a cambiamenti più frequenti rispetto all'intestazione ELF stessa. È stato anche introdotto in 2.6.0, quindi non è così onnipresente come /proc/PID/exe. Ma fa avere le informazioni di architettura. Aggiornerò la mia risposta.
Alexios,

auxv si è rivelato più complicato di quanto sperassi - sizeof(unsigned long)è 8 su 64 bit o 4 su 32 bit, il che significa che per interpretarlo correttamente direttamente devi sapere se il processo è 64 bit o 32 bit per cominciare!
Flexo,

Hai assolutamente ragione. È piuttosto fastidioso. Euristica rapida: se i byte 16x + y (4≤y≤7) sono tutti zero nel file, stai osservando un eseguibile a 64 bit. Questo è un kludge: sto assumendo una piccola macchina endian e che tutti i auxvcodici chiave si adattano a 32 bit unsigned long, quindi i 32 bit più significativi su una scatola a 64 bit sarebbero zero.
Alexios,

6

Guarda dentro /proc/$pid/maps . Gli intervalli di indirizzi sono indirizzi a 32 bit (8 cifre esadecimali) o indirizzi a 64 bit (16 cifre esadecimali). Funziona con qualsiasi tipo di eseguibile, indipendentemente dal formato. Puoi ottenere solo informazioni sui processi in esecuzione come lo stesso utente (a meno che tu non sia 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

Se non hai i permessi per accedere a questo file, penso che l'unico modo sia provare ad analizzare l'eseguibile. (Sebbene sia sempre possibile leggere /proc/$pid/stat, nessuno dei campi mostrati per i processi in esecuzione come utenti diversi rivela la dimensione dei bit del processo.) È possibile fare una buona idea dell'eseguibile del processo ps -o comm=e osservarlo in alto PATH, ma attenzione che il processo potrebbe essere stato lanciato con un altro PATHo potrebbe aver riscritto il suo argv[0]. È quindi possibile analizzare l'eseguibile: se si è disposti ad assumere ELF, guardare il 5 ° byte .


Ho testato la tua ricetta e non è riuscito. OpenSuSE 12.2, x86-64, kernel 3.4.63-2.44-default, / bin / bash. Le righe / proc / $ pid / maps per il binario e il primo heap sono scritte in stile a 32 bit, ma tutte le altre sono in stile a 64 bit. Probabilmente sono stampati usando "% 08x", ma questa ricetta deve essere modificata.
Netch

Sto ottenendo un mix di valori di 8, 12 e 16-nybble su tutte le scatole con cui l'ho provato. Senza controllare la fonte, la mia ipotesi è che il kernel regola il riempimento al multiplo più basso di 16 bit maggiore dell'intervallo di indirizzi per ogni riga stampata, quindi dovresti trovare la sequenza più lunga di caratteri esadecimali, quindi controlla.
Alessio,

MA, poiché la vsyscallmappa è sempre la più alta, potresti cavartela semplicemente cambiando headin tail- che, purtroppo, non funzionerà perché proc non si implementa seek(2), quindi dovrà essere qualcosa di più brutto, comeawk /proc/self/maps -- 'END { print substr($1, 0, 9); }'
Alexios,

@Netch In effetti, ho guardato stupidamente la vsyscall e le linee dello stack e non ho prestato attenzione alla mappatura dell'eseguibile. Grazie, ho aggiornato per cercare qualsiasi linea non a 32 bit. Peccato, è più brutto, ma questo è il più affidabile (almeno è sicuro su x86, non ho verificato con altre architetture doppie come sparc e arm).
Gilles 'SO- smetti di essere malvagio' il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.