Если вы хотите ограничить себя обнаружение ELF, вы можете прочитать ELF заголовок из /proc/$PID/exe
себя. Это довольно тривиально: если 5-й байт в файле равен 1, это 32-разрядный двоичный файл. Если это 2, это 64-битный. Для дополнительной проверки работоспособности:
- Если первые 5 байтов
0x7f, "ELF", 1
: это 32-битный двоичный файл ELF.
- Если первые 5 байтов
0x7f, "ELF", 2
: это 64-битный двоичный файл ELF.
- В противном случае: это неокончательно.
Вы также можете использовать 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
файла там не было раньше.
/proc/[pid]/auxv
: «Информация интерпретатора ELF, переданная процессу во время выполнения. Формат - один длинный беззнаковый идентификатор и одно длинное значение без знака для каждой записи» (man proc
).hd
издал один, и в нем не было магического числа. Там может быть некоторая соответствующая информация, но я думаю, что она будет подвергаться более частым изменениям, чем сам заголовок ELF. Он также был представлен в 2.6.0, так что он не такой повсеместный, как/proc/PID/exe
. Но это действительно есть информация о архитектуре. Я обновлю свой ответ.sizeof(unsigned long)
это 8 на 64 бит или 4 на 32 бит, что означает, что для правильной интерпретации напрямую вы должны знать, является ли процесс 64-битным или 32-битным для начала!auxv
коды клавиш соответствуют 32-битнымunsigned long
, поэтому наиболее значимые 32-битные в 64-битном боксе будут равны нулю.Посмотри
/proc/$pid/maps
. Диапазоны адресов - это 32-битные адреса (8 шестнадцатеричных цифр) или 64-битные адреса (16 шестнадцатеричных цифр). Это работает для любого вида исполняемого файла, независимо от формата. Вы можете получить информацию только о процессах, запущенных от имени того же пользователя (если вы не являетесь пользователем root).Если у вас нет разрешения на доступ к этому файлу, то я думаю, что единственный способ - попытаться проанализировать исполняемый файл. (Хотя вы всегда можете прочитать
/proc/$pid/stat
, ни одно из полей, отображаемых для процессов, запущенных от имени разных пользователей, не показывает размер битов процесса.) Вы можете точно угадать исполняемый файл процессаps -o comm=
и посмотреть его вPATH
- но остерегайтесь того, что процесс возможно, был запущен с другимPATH
, или, возможно, переписал егоargv[0]
. Затем вы можете проанализировать исполняемый файл - если вы хотите принять ELF, посмотрите на 5-й байт .источник
vsyscall
карта всегда самая высокая, вы можете просто изменить ееhead
наtail
- что, к сожалению, не сработает, потому что proc не реализуетseek(2)
, поэтому должно быть что-то более уродливое, например,awk /proc/self/maps -- 'END { print substr($1, 0, 9); }'