Tentukan apakah proses tertentu adalah 32- atau 64-Bit

14

Diberikan kernel Linux 2.6.x atau yang lebih baru dan userland yang ada yang mampu menjalankan binari ELF32 dan ELF64 (yaitu, sudah lewat Bagaimana saya tahu bahwa CPU saya mendukung sistem operasi 64bit di Linux? ) Bagaimana saya bisa menentukan apakah suatu proses diberikan ( oleh PID) berjalan dalam mode 32- atau 64-bit?

Solusi naif adalah menjalankan:

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

tetapi apakah informasi itu disingkapkan secara langsung /proctanpa mengandalkan libmagic?

Flexo
sumber

Jawaban:

21

Jika Anda ingin membatasi diri Anda untuk deteksi ELF, Anda dapat membaca judul ELF dari /proc/$PID/exediri Anda sendiri. Ini cukup sepele: jika byte ke-5 dalam file adalah 1, itu adalah biner 32-bit. Jika 2, 64-bit. Untuk pemeriksaan kewarasan tambahan:

  1. Jika 5 byte pertama adalah 0x7f, "ELF", 1: ini adalah biner ELF 32 bit.
  2. Jika 5 byte pertama adalah 0x7f, "ELF", 2: itu adalah biner ELF 64 bit.
  3. Jika tidak: tidak dapat disimpulkan.

Anda juga bisa menggunakan objdump, tetapi itu menghilangkan libmagicketergantungan Anda dan menggantinya dengan yang lain libelf.

Cara lain : Anda juga dapat mem-parsing /proc/$PID/auxvfile. Menurut proc(5):

Ini berisi isi informasi penerjemah ELF yang diteruskan ke proses pada waktu exec. Formatnya adalah satu ID panjang yang tidak ditandatangani ditambah satu nilai panjang yang tidak ditandatangani untuk setiap entri. Entri terakhir berisi dua nol.

Arti unsigned longkunci ada di /usr/include/linux/auxvec.h. Anda ingin AT_PLATFORM, yang 0x00000f. Jangan mengutip saya tentang itu, tetapi tampaknya nilai harus ditafsirkan sebagai char *untuk mendapatkan deskripsi string dari platform.

Anda mungkin menemukan pertanyaan StackOverflow ini bermanfaat.

Namun cara lain : Anda dapat menginstruksikan dynamic linker ( man ld) untuk membuang informasi tentang executable. Ini mencetak ke output standar struktur AUXV yang di-decode. Peringatan: ini peretasan, tetapi berhasil.

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

Ini akan menunjukkan sesuatu seperti:

AT_PLATFORM:     x86_64

Saya mencobanya pada biner 32-bit dan i686sebagai gantinya.

Bagaimana ini bekerja: LD_SHOW_AUXV=1menginstruksikan Dynamic Linker untuk membuang struktur AUXV yang didekodekan sebelum menjalankan executable. Kecuali Anda benar - benar ingin membuat hidup Anda menarik, Anda ingin benar-benar menjalankan kata executable. Salah satu cara untuk memuat dan menautkannya secara dinamis tanpa benar-benar memanggil main()fungsinya adalah dengan menjalankannya ldd(1). The downside: LD_SHOW_AUXVdiaktifkan oleh shell, sehingga Anda akan mendapatkan kesedihan dari struktur AUXV untuk: subkulit ldd,, dan biner target Anda. Jadi kami grepuntuk AT_PLATFORM, tetapi hanya menjaga baris terakhir.

Parsing auxv : jika Anda menguraikan auxvstruktur sendiri (tidak mengandalkan loader dinamis), maka ada sedikit teka-teki: auxvstruktur mengikuti aturan proses yang dijelaskan, sehingga sizeof(unsigned long)akan menjadi 4 untuk proses 32-bit dan 8 untuk 64 proses-bit. Kita dapat membuat ini bekerja untuk kita. Agar ini bekerja pada sistem 32-bit, semua kode kunci harus 0xffffffffatau kurang. Pada sistem 64-bit, 32 bit yang paling signifikan adalah nol. Mesin Intel adalah endian kecil, jadi 32 bit ini mengikuti yang paling tidak signifikan dalam memori.

Dengan demikian, yang perlu Anda lakukan adalah:

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.

Mem-parsing file peta : ini disarankan oleh Gilles, tetapi tidak berhasil. Ini versi modifikasi yang bisa. Itu bergantung pada membaca /proc/$PID/mapsfile. Jika file mencantumkan alamat 64-bit, prosesnya adalah 64 bit. Kalau tidak, itu 32 bit. Masalahnya terletak pada kernel yang akan menyederhanakan output dengan melepaskan nol terkemuka dari alamat hex dalam kelompok 4, sehingga panjang hack tidak bisa bekerja. awkuntuk menyelamatkan:

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

Ini berfungsi dengan memeriksa alamat awal dari peta memori terakhir dari proses. Mereka terdaftar seperti 12345678-deadbeef. Jadi, jika prosesnya 32-bit, alamat itu akan menjadi delapan digit hex, dan kesembilan akan menjadi tanda hubung. Jika itu 64-bit, alamat tertinggi akan lebih lama dari itu. Karakter kesembilan akan menjadi digit hex.

Waspadalah: semua kecuali metode pertama dan terakhir membutuhkan kernel Linux 2.6.0 atau yang lebih baru, karena auxvfile tersebut tidak ada sebelumnya.

Alexios
sumber
1
Hmmm, saya bertanya-tanya apakah header ELF di /proc/[pid]/auxv: "informasi penerjemah ELF diteruskan ke proses pada waktu exec. Formatnya adalah satu ID panjang yang tidak ditandatangani ditambah satu nilai panjang yang tidak ditandatangani untuk setiap entri" ( man proc).
goldilocks
1
Header itu sendiri tidak. Saya baru saja hdmengedit satu dan tidak memiliki nomor ajaib. Mungkin ada beberapa informasi yang relevan di sana, tapi saya pikir itu akan lebih sering berubah daripada header ELF itu sendiri. Itu juga diperkenalkan di 2.6.0, jadi itu tidak cukup di mana-mana /proc/PID/exe. Tetapi memang memiliki informasi arsitektur. Saya akan memperbarui jawaban saya.
Alexios
auxv ternyata lebih rumit dari yang saya harapkan - sizeof(unsigned long)adalah 8 pada 64 bit atau 4 pada 32 bit yang berarti bahwa untuk menafsirkannya secara langsung Anda harus tahu apakah prosesnya 64 bit atau 32 bit untuk memulainya!
Flexo
Anda benar sekali. Itu sangat menjengkelkan. Heuristik cepat: jika byte 16x + y (4≤y≤7) semuanya nol dalam file, Anda melihat 64-bit yang dapat dieksekusi. Ini adalah kludge: Saya mengasumsikan mesin endian kecil, dan bahwa semua auxvkode kunci sesuai dengan 32-bit unsigned long, sehingga 32-bit paling signifikan pada kotak 64-bit akan menjadi nol.
Alexios
6

Lihat /proc/$pid/maps. Rentang alamat lebih dari alamat 32-bit (8 digit heksadesimal) atau alamat 64-bit (16 digit heksadesimal). Ini berfungsi untuk semua jenis yang dapat dieksekusi, apa pun formatnya. Anda hanya bisa mendapatkan informasi tentang proses yang berjalan sebagai pengguna yang sama (kecuali jika Anda 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

Jika Anda tidak memiliki izin untuk mengakses file ini, maka saya pikir satu-satunya cara adalah mencoba menganalisis yang dapat dieksekusi. (Meskipun Anda selalu dapat membaca /proc/$pid/stat, tidak ada bidang yang ditampilkan untuk proses yang berjalan karena pengguna yang berbeda mengungkapkan ukuran bit proses.) Anda dapat menebak dengan baik proses yang dapat dieksekusi dengan ps -o comm=, dan mencari hal itu di PATH- tetapi berhati-hatilah karena prosesnya mungkin telah diluncurkan dengan berbedaPATH , atau mungkin telah ditulis ulang argv[0]. Anda kemudian dapat menganalisis executable - jika Anda mau mengasumsikan ELF, lihat byte ke-5 .

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Saya sudah menguji resep Anda dan gagal. OpenSuSE 12.2, x86-64, kernel 3.4.63-2.44-default, / bin / bash. Baris / proc / $ pid / maps untuk biner dan heap pertama ditulis dalam gaya 32-bit, tetapi semua yang lain dalam gaya 64-bit. Kemungkinan mereka dicetak menggunakan "% 08x", tetapi bagaimanapun resep ini harus disesuaikan.
Netch
Saya mendapatkan campuran nilai 8, 12 dan 16-nybble pada semua kotak yang saya coba. Tanpa memeriksa sumbernya, tebakan saya adalah kernel menyesuaikan padding ke kelipatan terendah 16-bit lebih besar dari kisaran alamat untuk setiap baris yang dicetak, jadi Anda harus menemukan urutan terpanjang dari karakter hex, kemudian periksa.
Alexios
NAMUN, karena vsyscallpeta selalu yang tertinggi, Anda bisa lolos dengan hanya mengubah headke tail- yang, sayangnya, tidak akan bekerja karena proc tidak melaksanakan seek(2), sehingga harus menjadi sesuatu jelek, sepertiawk /proc/self/maps -- 'END { print substr($1, 0, 9); }'
Alexios
@Netch Memang, saya bodoh melihat vsyscall dan garis stack dan tidak memperhatikan pemetaan executable. Terima kasih, saya telah memperbarui untuk mencari garis non-32-bit. Sayang sekali, ini lebih jelek, tapi ini yang paling bisa diandalkan (paling tidak pasti pada x86, saya belum memeriksa dengan arsitektur ganda lainnya seperti sparc dan arm).
Gilles 'SANGAT berhenti menjadi jahat'