Определите, является ли конкретный процесс 32- или 64-битным

14

Учитывая ядро ​​Linux версии 2.6.x или новее и существующее пользовательское пространство, которое способно работать с двоичными файлами ELF32 и ELF64 (т. Е. Прошло много времени. Как узнать, что мой ЦП поддерживает 64-битные операционные системы под Linux? ), Как я могу определить, является ли данный процесс ( по PID) работает в 32- или 64-битном режиме?

Наивным решением было бы запустить:

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

но та информация выставлена ​​непосредственно, /procне полагаясь на libmagic?

Flexo
источник

Ответы:

21

Если вы хотите ограничить себя обнаружение ELF, вы можете прочитать ELF заголовок из /proc/$PID/exeсебя. Это довольно тривиально: если 5-й байт в файле равен 1, это 32-разрядный двоичный файл. Если это 2, это 64-битный. Для дополнительной проверки работоспособности:

  1. Если первые 5 байтов 0x7f, "ELF", 1: это 32-битный двоичный файл ELF.
  2. Если первые 5 байтов 0x7f, "ELF", 2: это 64-битный двоичный файл ELF.
  3. В противном случае: это неокончательно.

Вы также можете использовать objdump, но это снимает вашу libmagicзависимость и заменяет ее на libelfединицу.

Другой способ : вы также можете разобрать /proc/$PID/auxvфайл. По словам proc(5):

Содержит содержимое информации интерпретатора ELF, переданной процессу во время исполнения. Формат - один длинный беззнаковый идентификатор плюс одно длинное без знака для каждой записи. Последняя запись содержит два нуля.

Значения unsigned longключей в /usr/include/linux/auxvec.h. Вы хотите AT_PLATFORM, что есть 0x00000f. Не цитируйте меня по этому поводу, но, похоже, значение следует интерпретировать как a, char *чтобы получить строковое описание платформы.

Этот вопрос StackOverflow может оказаться полезным.

Еще один способ : вы можете указать динамическому компоновщику ( man ld) выводить информацию об исполняемом файле. Он выводит на стандартный вывод декодированную структуру AUXV. Предупреждение: это взлом, но это работает.

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

Это покажет что-то вроде:

AT_PLATFORM:     x86_64

Я попробовал это на 32-разрядном двоичном файле и получил i686вместо этого.

Как это работает: LD_SHOW_AUXV=1инструктирует Dynamic Linker сбросить декодированную структуру AUXV перед запуском исполняемого файла. Если вы действительно не хотите сделать свою жизнь интересной, вы не должны запускать указанный исполняемый файл. Один из способов загрузить и динамически связать его без фактического вызова его main()функции - запустить ldd(1)его. Оборотная сторона: LD_SHOW_AUXVвключена оболочкой, поэтому вы получите дампы структур AUXV для: subshell lddи вашего целевого двоичного файла . Так что мы grepза AT_PLATFORM, но сохраняем только последнюю строчку.

Разбор auxv : если вы анализируете auxvструктуру самостоятельно (не полагаясь на динамический загрузчик), то возникает небольшая загадка: auxvструктура следует правилу процесса, который она описывает, поэтому sizeof(unsigned long)будет 4 для 32-битных процессов и 8 для 64 процессы. Мы можем сделать эту работу для нас. Чтобы это работало в 32-битных системах, все коды клавиш должны быть 0xffffffffили меньше. В 64-битной системе наиболее значимые 32 бита будут равны нулю. Машины Intel имеют младший порядок, поэтому эти 32 бита следуют за наименее значимыми в памяти.

Таким образом, все, что вам нужно сделать, это:

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.

Разбор файла карт : это было предложено Жилем, но не совсем сработало. Вот модифицированная версия, которая делает. Это полагается на чтение /proc/$PID/mapsфайла. Если файл содержит 64-битные адреса, процесс будет 64-битным. В противном случае это 32 бита. Проблема заключается в том, что ядро ​​упростит вывод, удалив начальные нули из шестнадцатеричных адресов в группах по 4, поэтому взлом длины не может работать. awkдля спасения:

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

Это работает путем проверки начального адреса последней карты памяти процесса. Они перечислены как 12345678-deadbeef. Итак, если процесс 32-битный, этот адрес будет иметь длину восемь шестнадцатеричных цифр, а девятый будет дефисом. Если это 64-битный, самый высокий адрес будет длиннее. Девятый символ будет шестнадцатеричной цифрой.

Имейте в виду: для всех, кроме первого и последнего методов, требуется ядро ​​Linux 2.6.0 или новее, поскольку auxvфайла там не было раньше.

Алексиос
источник
1
Хммм, мне интересно, находится ли в заголовке ELF /proc/[pid]/auxv: «Информация интерпретатора ELF, переданная процессу во время выполнения. Формат - один длинный беззнаковый идентификатор и одно длинное значение без знака для каждой записи» ( man proc).
Златовласка
1
Сам заголовок нет. Я только что hdиздал один, и в нем не было магического числа. Там может быть некоторая соответствующая информация, но я думаю, что она будет подвергаться более частым изменениям, чем сам заголовок ELF. Он также был представлен в 2.6.0, так что он не такой повсеместный, как /proc/PID/exe. Но это действительно есть информация о архитектуре. Я обновлю свой ответ.
Алексиос
auxv оказался хитрее, чем я надеялся - sizeof(unsigned long)это 8 на 64 бит или 4 на 32 бит, что означает, что для правильной интерпретации напрямую вы должны знать, является ли процесс 64-битным или 32-битным для начала!
Флекс
Ты абсолютно прав. Это довольно раздражает. Быстрая эвристика: если байты 16x + y (4≤y≤7) равны нулю в файле, вы смотрите на 64-битный исполняемый файл. Это клочок: я предполагаю, что машина с прямым порядком байтов немного и что все auxvкоды клавиш соответствуют 32-битным unsigned long, поэтому наиболее значимые 32-битные в 64-битном боксе будут равны нулю.
Алексиос
6

Посмотри /proc/$pid/maps. Диапазоны адресов - это 32-битные адреса (8 шестнадцатеричных цифр) или 64-битные адреса (16 шестнадцатеричных цифр). Это работает для любого вида исполняемого файла, независимо от формата. Вы можете получить информацию только о процессах, запущенных от имени того же пользователя (если вы не являетесь пользователем 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

Если у вас нет разрешения на доступ к этому файлу, то я думаю, что единственный способ - попытаться проанализировать исполняемый файл. (Хотя вы всегда можете прочитать /proc/$pid/stat, ни одно из полей, отображаемых для процессов, запущенных от имени разных пользователей, не показывает размер битов процесса.) Вы можете точно угадать исполняемый файл процесса ps -o comm=и посмотреть его в PATH- но остерегайтесь того, что процесс возможно, был запущен с другим PATH, или, возможно, переписал его argv[0]. Затем вы можете проанализировать исполняемый файл - если вы хотите принять ELF, посмотрите на 5-й байт .

Жиль "ТАК - перестань быть злым"
источник
Я проверил ваш рецепт, и он не прошел. OpenSuSE 12.2, x86-64, ядро ​​3.4.63-2.44-default, / bin / bash. Строки / proc / $ pid / maps для двоичного файла и первой кучи написаны в 32-битном стиле, но все остальные в 64-битном стиле. Скорее всего, они напечатаны с использованием «% 08x», но в любом случае этот рецепт должен быть скорректирован.
Нетч
Я получаю смесь значений 8, 12 и 16 nybble на всех коробках, с которыми я пробовал. Не проверяя источник, я предполагаю, что ядро ​​корректирует отступ до минимального кратного 16-битного значения, превышающего диапазон адресов для каждой напечатанной строки, поэтому вам нужно найти самую длинную последовательность шестнадцатеричных символов, а затем проверить.
Алексиос
НО, поскольку vsyscallкарта всегда самая высокая, вы можете просто изменить ее headна tail- что, к сожалению, не сработает, потому что proc не реализует seek(2), поэтому должно быть что-то более уродливое, например,awk /proc/self/maps -- 'END { print substr($1, 0, 9); }'
Alexios
@Netch Действительно, я тупо смотрел на строки vsyscall и stack и не обращал внимания на отображение исполняемого файла. Спасибо, я обновился, чтобы найти любую не 32-битную строку. Жаль, что это уродливее, но это самое надежное (по крайней мере, это надежный запуск на x86, я не проверял другие двойные архитектуры, такие как sparc и arm).
Жиль "ТАК - перестань быть злым"