Sprawdź, czy określony proces jest 32- czy 64-bitowy


14

Biorąc pod uwagę jądro Linuksa w wersji 2.6.x lub nowszej i istniejącą przestrzeń użytkownika, która jest zdolna do uruchamiania zarówno plików binarnych ELF32, jak i ELF64 (tj. Dawno temu Skąd mam wiedzieć, że mój procesor obsługuje 64-bitowe systemy operacyjne pod Linuksem? ) Jak mogę ustalić, czy dany proces ( przez PID) działa w trybie 32- lub 64-bitowym?

Naiwnym rozwiązaniem byłoby uruchomienie:

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

ale czy te informacje są ujawniane bezpośrednio, /procbez polegania na nich libmagic?

Odpowiedzi:


21

Jeśli chcesz ograniczyć się do wykrywania ELF można przeczytać nagłówek ELF o /proc/$PID/exesiebie. Jest to dość trywialne: jeśli 5. bajt w pliku ma wartość 1, jest to 32-bitowy plik binarny. Jeśli to 2, to jest 64-bit. W celu dodatkowego sprawdzenia poczytalności:

  1. Jeśli pierwsze 5 bajtów to 0x7f, "ELF", 1: 32-bitowy plik binarny ELF.
  2. Jeśli pierwsze 5 bajtów to 0x7f, "ELF", 2: 64-bitowy plik binarny ELF.
  3. W przeciwnym razie: jest to niejednoznaczne.

Możesz także użyć objdump, ale to usuwa twoją libmagiczależność i zastępuje ją libelfjedną.

Inny sposób : możesz także parsować /proc/$PID/auxvplik. Według proc(5):

Zawiera zawartość informacji interpretera ELF przekazywanych do procesu w czasie wykonywania. Format to jeden bez znaku długi identyfikator plus jedna bez znaku długa wartość dla każdego wpisu. Ostatni wpis zawiera dwa zera.

Znaczenie unsigned longkluczy jest w /usr/include/linux/auxvec.h. ChceszAT_PLATFORM , co jest 0x00000f. Nie cytuj mnie, ale wygląda na to, że wartość należy interpretować jako char *opis łańcucha dla platformy.

Możesz znaleźć przydać to pytanie StackOverflow .

Jeszcze inny sposób : możesz poinstruować linker dynamiczny (man ld ), aby zrzucił informacje o pliku wykonywalnym. Drukuje na standardowe wyjście zdekodowaną strukturę AUXV. Ostrzeżenie: jest to włamanie, ale działa.

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

To pokaże coś takiego:

AT_PLATFORM:     x86_64

Próbowałem na 32-bitowym pliku binarnym i dostałem i686 zamiast tego .

Jak to działa: LD_SHOW_AUXV=1instruuje Dynamic Linker, aby zrzucił zdekodowaną strukturę AUXV przed uruchomieniem pliku wykonywalnego. Jeśli naprawdę nie chcesz, aby twoje życie było interesujące, chcesz uniknąć uruchamiania wspomnianego pliku wykonywalnego. Jednym ze sposobów ładowania i dynamicznego łączenia go bez wywoływania jego main()funkcji jest uruchomienie ldd(1)go. Wada: LD_SHOW_AUXVjest włączona przez powłokę, więc otrzymasz zrzuty struktur AUXV dla: podpowłoki lddi docelowego pliku binarnego. Więc mamy grepAT_PLATFORM, ale zachowujemy tylko ostatnią linię.

Parsowanie Auxv : jeśli parsujeszauxv strukturę samodzielnie (nie polegając na dynamicznym module ładującym), to jest trochę zagadki: auxvstruktura postępuje zgodnie z zasadą opisanego procesu, więc sizeof(unsigned long)będzie 4 dla procesów 32-bitowych i 8 dla 64 -bitowe procesy. Możemy sprawić, aby to działało dla nas. Aby działało to w systemach 32-bitowych, wszystkie kody kluczy muszą być 0xffffffffmniejsze lub mniejsze. W systemie 64-bitowym najbardziej znaczące 32 bity będą wynosić zero. Maszyny Intela są małymi endianami, więc te 32 bity podążają za najmniej znaczącymi w pamięci.

W związku z tym wszystko, co musisz zrobić, to:

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.

Analiza pliku map : Gilles zasugerował, ale nie całkiem działał. Oto zmodyfikowana wersja, która działa. Polega na odczytaniu /proc/$PID/mapspliku. Jeśli plik zawiera adresy 64-bitowe, proces ma 64 bity. W przeciwnym razie jest to 32 bity. Problem polega na tym, że jądro uprości dane wyjściowe, usuwając wiodące zera z adresów szesnastkowych w grupach po 4, więc hack długości nie może do końca działać. awkna pomoc:

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

Działa to poprzez sprawdzenie adresu początkowego ostatniej mapy pamięci procesu. Są wymienione jak 12345678-deadbeef. Tak więc, jeśli proces jest 32-bitowy, adres będzie miał osiem cyfr szesnastkowych, a dziewiąty będzie łącznikiem. Jeśli jest to wersja 64-bitowa, najwyższy adres będzie dłuższy. Dziewiąty znak będzie cyfrą szesnastkową.

Pamiętaj: wszystkie oprócz pierwszej i ostatniej metody wymagają jądra Linuksa 2.6.0 lub nowszego, ponieważ auxvpliku nie było wcześniej.


1
Hmmm, zastanawiam się, czy nagłówek ELF jest w /proc/[pid]/auxv: „informacja interpretera ELF przekazywana do procesu w czasie wykonywania. Format to jeden długi identyfikator bez znaku plus jedna długa wartość bez znaku dla każdego wpisu” ( man proc).
goldilocks,

1
Sam nagłówek nie jest. Właśnie hdzredagowałem jedną i brakowało jej magicznej liczby. Mogą tam być istotne informacje, ale myślę, że będą podlegać częstszym zmianom niż sam nagłówek ELF. Został również wprowadzony w wersji 2.6.0, więc nie jest tak wszechobecny jak /proc/PID/exe. Ale ma informacje o architekturze. Zaktualizuję moją odpowiedź.
Alexios,

Auxv okazało się trudniejsze, niż się spodziewałem - sizeof(unsigned long)ma 8 na 64 bity lub 4 na 32 bity, co oznacza, że ​​aby poprawnie zinterpretować go bezpośrednio, musisz wiedzieć, czy proces jest 64-bitowy, czy 32-bitowy!
Flexo,

Masz całkowitą rację. To dość denerwujące. Szybka heurystyka: jeśli bajty 16x + y (4≤y≤7) mają zero w pliku, patrzysz na 64-bitowy plik wykonywalny. To kludge: zakładam małą maszynę endian i że wszystkie auxvkody kluczy pasują do 32-bitów unsigned long, więc najbardziej znaczące 32-bity na 64-bitowym pudełku będą wynosić zero.
Alexios

6

Zajrzyj do środka /proc/$pid/maps. Zakresy adresów obejmują ponad 32-bitowe adresy (8 cyfr szesnastkowych) lub adresy 64-bitowe (16 cyfr szesnastkowych). Działa to dla każdego rodzaju pliku wykonywalnego, bez względu na format. Możesz uzyskać informacje tylko o procesach działających jako ten sam użytkownik (chyba że jesteś rootem).

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

Jeśli nie masz uprawnień dostępu do tego pliku, myślę, że jedynym sposobem jest próba analizy pliku wykonywalnego. (Chociaż zawsze możesz czytać /proc/$pid/stat, żadne z pól pokazanych dla procesów działających jako różni użytkownicy nie ujawniają wielkości bitów procesu). Możesz dobrze zgadnąć, jaki jest wykonywalny proces ps -o comm=i patrząc na to PATH- ale uważaj na ten proces mógł zostać uruchomiony z innym PATHkodem lub mógł go przepisać argv[0]. Następnie możesz przeanalizować plik wykonywalny - jeśli chcesz założyć ELF, spójrz na 5 bajt .


Przetestowałem twój przepis i nie udało się. OpenSuSE 12.2, x86-64, jądro 3.4.63-2.44-default, / bin / bash. Linie / proc / $ pid / maps dla pliku binarnego i pierwszej sterty są zapisane w stylu 32-bitowym, ale wszystkie pozostałe są w stylu 64-bitowym. Prawdopodobnie są drukowane przy użyciu „% 08x”, ale w każdym razie ten przepis należy dostosować.
Netch

Dostaję mieszankę wartości 8, 12 i 16-częściowych na wszystkich urządzeniach, z którymi próbowałem. Bez sprawdzania źródła, domyślam się, że jądro dostosowuje dopełnianie do najniższej wielokrotności 16 bitów większej niż zakres adresów dla każdej drukowanej linii, więc będziesz musiał znaleźć najdłuższą sekwencję znaków szesnastkowych, a następnie sprawdź.
Alexios,

ALE, ponieważ vsyscallmapa jest zawsze najwyższa, możesz uniknąć zmiany po prostu headna tail- co, niestety, nie zadziała, ponieważ proc nie działa seek(2), więc będzie musiało być coś brzydszego, jakawk /proc/self/maps -- 'END { print substr($1, 0, 9); }'
Alexios

@Netch Rzeczywiście, głupio spojrzałem na linie vsyscall i stos i nie zwróciłem uwagi na mapowanie pliku wykonywalnego. Dzięki, zaktualizowałem się, by szukać dowolnej linii nie 32-bitowej. Szkoda, jest brzydszy, ale jest to najbardziej niezawodny (przynajmniej pewnie strzela na x86, nie sprawdziłem z innymi podwójnymi architekturami, takimi jak sparc i arm).
Gilles „SO- przestań być zły”
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.