Determine si un proceso específico es de 32 o 64 bits

14

Dado un kernel de Linux 2.6.xo más reciente y un área de usuario existente que es capaz de ejecutar binarios ELF32 y ELF64 (es decir, mucho más allá, ¿cómo sé que mi CPU admite sistemas operativos de 64 bits en Linux? ) ¿Cómo puedo determinar si un proceso dado ( por PID) se ejecuta en modo de 32 o 64 bits?

La solución ingenua sería ejecutar:

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

pero ¿se expone esa información directamente /procsin depender de ella libmagic?

Flexografía
fuente

Respuestas:

21

Si desea limitarse a la detección ELF, puede leer el encabezado ELF de /proc/$PID/exeusted mismo. Es bastante trivial: si el quinto byte del archivo es 1, es un binario de 32 bits. Si es 2, es de 64 bits. Para mayor control de cordura:

  1. Si los primeros 5 bytes son 0x7f, "ELF", 1: es un binario ELF de 32 bits.
  2. Si los primeros 5 bytes son 0x7f, "ELF", 2: es un binario ELF de 64 bits.
  3. De lo contrario: no es concluyente.

También podría usar objdump, pero eso le quita su libmagicdependencia y la reemplaza por una libelf.

Otra forma : también puede analizar el /proc/$PID/auxvarchivo. De acuerdo a proc(5):

Contiene el contenido de la información del intérprete ELF que se pasó al proceso en el momento de ejecución. El formato es un ID largo sin signo más un valor largo sin signo para cada entrada. La última entrada contiene dos ceros.

Los significados de las unsigned longteclas están en /usr/include/linux/auxvec.h. Tú quieres AT_PLATFORM, que es 0x00000f. No me cite sobre eso, pero parece que el valor debe interpretarse como a char *para obtener la descripción de la cadena de la plataforma.

Puede encontrar útil esta pregunta de StackOverflow .

Otra forma más : puede indicarle al vinculador dinámico ( man ld) que descargue información sobre el ejecutable. Imprime en salida estándar la estructura decodificada de AUXV. Advertencia: este es un truco, pero funciona.

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

Esto mostrará algo como:

AT_PLATFORM:     x86_64

Lo probé en un binario de 32 bits y obtuve en su i686lugar.

Cómo funciona esto: LD_SHOW_AUXV=1indica al Dynamic Linker que descargue la estructura decodificada de AUXV antes de ejecutar el ejecutable. A menos que realmente le guste hacer que su vida sea interesante, desea evitar ejecutar dicho ejecutable. Una forma de cargarlo y vincularlo dinámicamente sin llamar realmente a su main()función es ejecutarlo ldd(1). La desventaja: LD_SHOW_AUXVestá habilitada por el shell, por lo que obtendrá volcados de las estructuras AUXV para: el subshell lddy su binario de destino. Entonces, greppara AT_PLATFORM, pero solo mantenemos la última línea.

Analizando auxv : si analiza la auxvestructura usted mismo (sin depender del cargador dinámico), entonces hay un poco de enigma: la auxvestructura sigue la regla del proceso que describe, por sizeof(unsigned long)lo que será 4 para procesos de 32 bits y 8 para 64 -bit procesos. Podemos hacer que esto funcione para nosotros. Para que esto funcione en sistemas de 32 bits, todos los códigos clave deben ser 0xffffffffo menos. En un sistema de 64 bits, los 32 bits más significativos serán cero. Las máquinas Intel son pequeños endianes, por lo que estos 32 bits siguen a los menos significativos en la memoria.

Como tal, todo lo que necesita hacer es:

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.

Analizando el archivo de mapas : esto fue sugerido por Gilles, pero no funcionó. Aquí hay una versión modificada que sí. Se basa en leer el /proc/$PID/mapsarchivo. Si el archivo enumera direcciones de 64 bits, el proceso es de 64 bits. De lo contrario, son 32 bits. El problema radica en que el núcleo simplificará la salida eliminando los ceros iniciales de las direcciones hexadecimales en grupos de 4, por lo que el corte de longitud no puede funcionar. awkal rescate:

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

Esto funciona comprobando la dirección de inicio del último mapa de memoria del proceso. Están listados como 12345678-deadbeef. Entonces, si el proceso es de 32 bits, esa dirección tendrá ocho dígitos hexadecimales y la novena será un guión. Si es de 64 bits, la dirección más alta será más larga que eso. El noveno personaje será un dígito hexadecimal.

Tenga en cuenta: todos los métodos, excepto el primero y el último, necesitan el kernel de Linux 2.6.0 o posterior, ya que el auxvarchivo no estaba allí antes.

Alexios
fuente
1
Hmmm, me pregunto si el encabezado ELF está en /proc/[pid]/auxv: "la información del intérprete ELF pasó al proceso en el momento de la ejecución. El formato es una ID larga sin signo más un valor largo sin signo para cada entrada" ( man proc).
Ricitos de oro
1
El encabezado en sí no lo es. Acabo de hdeditar uno y le faltaba el número mágico. Puede haber información relevante allí, pero creo que estaría sujeta a cambios más frecuentes que el encabezado ELF en sí. También se introdujo en 2.6.0, por lo que no es tan omnipresente como /proc/PID/exe. Pero lo hace tener la arquitectura de la información. Actualizaré mi respuesta.
Alexios
auxv resultó ser más complicado de lo que esperaba: sizeof(unsigned long)es 8 en 64 bits o 4 en 32 bits, lo que significa que para interpretarlo directamente directamente, ¡para empezar, debe saber si el proceso es de 64 bits o 32 bits!
Flexo
Estás absolutamente en lo correcto. Eso es bastante molesto. Heurística rápida: si los bytes 16x + y (4≤y≤7) son todos cero en el archivo, está viendo un ejecutable de 64 bits. Esto es un error: supongo que una pequeña máquina endian, y que todos los auxvcódigos clave se ajustan a 32 bits unsigned long, por lo que los 32 bits más significativos en una caja de 64 bits serían cero.
Alexios
6

Busque en /proc/$pid/maps. Los rangos de direcciones son más de direcciones de 32 bits (8 dígitos hexadecimales) o direcciones de 64 bits (16 dígitos hexadecimales). Esto funciona para cualquier tipo de ejecutable, sin importar el formato. Solo puede obtener información sobre los procesos que se ejecutan como el mismo usuario (a menos que sea 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

Si no tiene permiso para acceder a este archivo, creo que la única forma es tratar de analizar el ejecutable. (Si bien siempre puede leer /proc/$pid/stat, ninguno de los campos que se muestran para los procesos que se ejecutan a medida que diferentes usuarios revelan el tamaño de bits del proceso). Puede adivinar el ejecutable del proceso ps -o comm=y buscarlo en el PATH, pero tenga cuidado de que el proceso puede haber sido lanzado con un diferente PATH, o puede haber reescrito su argv[0]. Luego puede analizar el ejecutable; si está dispuesto a asumir ELF, mire el quinto byte .

Gilles 'SO- deja de ser malvado'
fuente
He probado tu receta y falló. OpenSuSE 12.2, x86-64, kernel 3.4.63-2.44-default, / bin / bash. Las líneas / proc / $ pid / maps para el binario y el primer montón están escritas en estilo de 32 bits, pero todas las demás están en estilo de 64 bits. Probablemente se imprimen con "% 08x", pero de todos modos esta receta se ajustará.
Netch
Estoy obteniendo una mezcla de 8, 12 y 16 valores de nybble en todos los cuadros con los que lo probé. Sin comprobar la fuente, supongo que el núcleo ajusta el relleno al múltiplo más bajo de 16 bits mayor que el rango de direcciones para cada línea impresa, por lo que tendría que encontrar la secuencia más larga de caracteres hexadecimales, luego verifique.
Alexios
Pero, puesto que el vsyscallmapa es siempre el más alto, usted podría conseguir lejos con sólo cambiar heada tail- los que, por desgracia, no va a funcionar porque proc no poner en práctica seek(2), por lo que tendrá que ser algo más feo, comoawk /proc/self/maps -- 'END { print substr($1, 0, 9); }'
Alejo
@Netch De hecho, miré estúpidamente las líneas vsyscall y stack y no presté atención a la asignación del ejecutable. Gracias, he actualizado para buscar cualquier línea que no sea de 32 bits. Lástima, es más feo, pero este es el más confiable (al menos es seguro en x86, no lo he comprobado con otras arquitecturas duales como sparc y arm).
Gilles 'SO- deja de ser malvado'