Los volcados de memoria revelan un estado de memoria en funcionamiento en un punto específico en operación. Son una herramienta importante en la administración de sistemas porque proporcionan evidencia "forense" sobre las condiciones del sistema.
Para las instrucciones, muestras de código y descarga de código (vea Descargas, más adelante) en este artículo, utilicé Python versión 2.4, que usted puede descargar del sitio Python (vea Recursos a continuación). Es posible que usted obtenga resultados diferentes con otras versiones.
Antes de comenzar, asegúrese de estar familiarizado(a) con lo siguiente:
- La implementación /dev/shm de memoria compartida tradicional
- La visualización manual de volcados de datos de memoria compartida en un sistema Linux
- Ciertas dependencias (conceptos de apertura, lectura, escritura y cierre de archivos Linux; el uso del descriptor de archivos y los modos en que se puede abrir el archivo; los conceptos básicos de la estructura Python)
- GNU/Linux, en general
Entendiendo los volcados de memoria compartida en Linux
/dev/shm es una implementación del concepto de memoria compartida tradicional. Es un medio ampliamente utilizado y aceptado para transferir datos entre programas. En /dev/shm, un programa o daemon crea una porción de memoria a la que otros procesos pueden accesar (a los niveles de permiso relevantes). Este es un método rápido y fácil de compartir datos entre procesos.
Cada programa crea su propio archivo en mis ejemplos, uso el archivo de nombre devmem ubicado en /dev/shm/devmem.
Visualizando manualmente el volcado de memoria compartida en Linux
Usted no puede ver los archivos de memoria compartida (conocidos comúnmente como archivos shm) utilizando la herramienta cat utilizada generalmente para mostrar archivos en Linux, porque estos archivos shm están en formato binario. Estos aparecerán como una porción de caracteres confusos si usted intenta visualizarlos con métodos genéricos de visualización de archivos. Yo utilizo la herramienta hexdump) para leer los archivos mem y visualizarlos en un formato legible; también hay otras herramientas disponibles para este propósito.
Para este artículo, el patrón de uso para hexdump
se ve así:
hexdump <optional switches> /dev/shm/devmem for <switches> supported
Consulte Recursos donde hallará un enlace hacia más información sobre
hexdump.
El escenario en el que trabajaremos es un rastreador de red que analiza los paquetes recibidos por el host y almacena los datos en un archivo mem compartido, /dev/shm/devmem. Estos datos contienen información sobre el paquete recibido.
Generalmente el archivo se ve así:
- El almacenamiento del archivo de memoria es /dev/shm/devmem
- El formato de archivo devmem contiene:
- 4 bytes de dirección de fuente para notificar quién lo envió
- 4 bytes de dirección de destino para notificar hacia quién va
- 2 bytes de puerto fuente (en otras palabras, el puerto en la fuente que utilizó el paquete)
- 2 bytes de puerto de destino (de forma similar, el puerto de destino que utilizará el paquete)
- 2 bytes de protocolo (el protocolo del cual es parte el paquete)
- 4 bytes de tiempo para indicar la indicación de fecha con la que el paquete fue visto por el fragmento de red
- 1 registro de longitud = la suma de las especificaciones devmem (esto es, 18 bytes)
- El tamaño máximo del archivo de memoria es 1KByte, así que puede contener 1024 bytes (1024 / 18 = 56 registros)
Si usted hace hexdump y presenta en pantalla manualmente el archivo en una terminal Linux, este se verá más o menos así:
Listado 1. Mostrando un archivo de volcado
# hexdump /dev/shm/devmem 0000000 0004 0000 0400 0000 fc64 0a00 00fb e000 0000010 14e9 14e9 0011 0000 0000 0000 0000 0000 0000020 0000 0000 0000 0000 0000 0000 0000 0800 0000030 1668 0000 0000 0000 0032 0000 0000 0000 0000040 0000 0000 0001 e000 0000 0000 0002 0000 0000050 0000 0000 0000 0000 0000 0000 0000 0000 0000060 0000 0000 0000 0800 0100 0000 0000 0000 0000070 0008 0000 0000 0000 fc64 0a00 fd64 0a00 0000080 2328 03ea 0006 0000 0000 0000 0000 0000 0000090 0000 0000 0000 0000 0000 0000 0000 0800 00000a0 7700 0001 0000 0000 0040 0000 0000 0000 00000b0 fd64 0a00 fc64 0a00 03ea 2328 0006 0000 00000c0 0000 0000 0000 0000 0000 0000 0000 0000 00000d0 0000 0000 0000 0800 0a00 0000 0000 0000 00000e0 0040 0000 0000 0000 fc64 0a00 fd64 0a00 00000f0 2328 03ec 0006 0000 0000 0000 0000 0000 0000100 0000 0000 0000 0000 0000 0000 0000 0800 0000110 7700 0001 0000 0000 0040 0000 0000 0000 |
Vamos a ver los pasos necesarios para analizar el archivo.
Los pasos para comprender los datos en un archivo de volcado de memoria (identificar el formato, analizar y leer el archivo) son relativamente simples:
- Abra el archivo.
- Lea los bytes con un descriptor de archivos.
- Convierta los datos a un formato de cadena de caracteres legible cuando sea necesario.
- Verifique si el almacenamiento intermedio está intacto o si está truncado o tiene errores.
- Desempaquete los datos del almacenamiento intermedio.
- Extraiga la información.
- Imprima los datos.
- Construya un bucle para efectuar los pasos del 1 al 7 en cada registro de un volcado de datos compartidos. (Usted no querría hacerlo manualmente, ¿cierto?)
Vamos a ver el flujo del proceso más detalladamente.
Para abrir el archivo de memoria compartida, use la forma general
fd = open(fileName, mode).
fd es el descriptor del archivo, un apuntador hacia el archivo. Para este ejemplo, use lo siguiente:
- fileName: /dev/shm/devmem
- mode: rb (leer sólo en modo binario)
Listado 2. Abriendo un archivo de memoria compartida
fd = open('/dev/shm/devmem ','rb')
|
Para leer los bytes usando el descriptor de archivo obtenido en la función de llamado anterior, utilizo el siguiente código. Este lee el número indicado de bytes del parámetro de archivo que se pasó:
Listado 3. Abriendo un archivo de memoria compartida
def ReadBytes(self, fd, noBytes):
'''
Read file and return the number of bytes as specified
'''
data = fd.read(noBytes)
return data
buffer = ReadBytes('/dev/shm/devmem ', 18)
# Pass the file name and pass the number of bytes
# Number of bytes is 18 since in the example scenario each record
# is of length 18
|
Aquí, leer los bytes no es suficiente para extraer la información necesaria; retorna un almacenamiento intermedio si se lee la cadena de caracteres. Necesita analizarse y convertirse en un formato de cadena de caracteres comprensible.
Los struct de Python pueden usarse para manejar los datos binarios almacenados en los archivos o de conexiones de red, entre otras fuentes. El struct de Python también tiene dos amplias funcionalidades:
pack y unpack.
El trabajo de pack es retornar una cadena de caracteres que contiene los valores v1,
v2, ... empaquetados, de acuerdo al formato dado. Los argumentos deben coincidir exactamente con los valores requeridos por el formato.
El rol de unpack es desempaquetar la cadena de caracteres (presumiblemente empaquetada por pack(fmt, ...)) de acuerdo al formato dado. El resultado es una tupla incluso si contiene exactamente un elemento. La cadena de caracteres debe contener exactamente la cantidad de datos requeridos por el formato: len(string) debe ser igual a
calcsize(fmt).
Formatos aceptables:
- Formatos de 1-byte:
- b para caracter firmado
- B para caracter no firmado
- Formatos de 2-bytes:
- h para entero corto
- H para corto no firmado
- Formatos de 4-bytes:
- l para largo
- L para largo no firmado
- Formatos de 8-bytes:
- q para largo largo
- Q para largo largo no firmad
Para otros formatos soportados para empaquetar y desempaquetar los bytes del almacenamiento intermedio, consulte la literatura Python listada en Recursos.
Verifique el almacenamiento intermedio
Para verificar que el almacenamiento intermedio de 18 bytes que se ha leído esté intacto y que no esté truncado ni tenga errores, puede usar la función
calcsize para verificar si el tamaño de bytes todavía es 18 como se espera cuando se lea. Puede usar la función assert de Python para este propósito.
Listado 4. Verificando que el tamaño del almacenamiento intermedio sea correcto
self.assertEqual(len(buffer), struct.calcsize('llllh'))
# 4 l's is 4*4 bytes = 16 bytes and h is 2 bytes so that is 18 bytes
# we could use QQh which is 2*8 + 2 = 18 bytes as well
|
Ahora que ha verificado que el almacenamiento intermedio realmente tiene 18 bytes,
puede desempaquetar sus datos de este. struct
proporciona una función útil, unpack_from , que proporciona el número de bytes, el nombre del almacenamiento intermedio y el desplazamiento en el cual necesita leerse:
struct.unpack_from(fmt, buffer[, offset=0])
En nuestro escenario, estos son los detalles que deseamos extraer:
Listado 5. Detalles a extraer
sourceAddress = (struct.unpack_from('B', buffer,0),
struct.unpack_from('B', buffer,1),
struct.unpack_from('B', buffer,2),
struct.unpack_from('B', buffer,3))
destinationAddress = (struct.unpack_from('B', buffer,4),
struct.unpack_from('B', buffer,5),
struct.unpack_from('B', buffer,6),
struct.unpack_from('B', buffer,7))
sourcePort = (struct.unpack_from('B', buffer,8),
struct.unpack_from('B', buffer,9))
destinationPort = (struct.unpack_from('B', buffer,10),
struct.unpack_from('B', buffer,11))
protocolUsed = (struct.unpack_from('B', buffer,12),
struct.unpack_from('B', buffer,13))
timeStamp = (struct.unpack_from('B', buffer,14),
struct.unpack_from('B', buffer,15),
struct.unpack_from('B', buffer,16),
struct.unpack_from('B', buffer,17))
|
Nota: Dependiendo de la plataforma y de si la estructura mem está en formato big endian o little endian, usted podrá necesitar intercambiar el orden en que los bytes se leen.
Ahora que ha desempaquetado los valores del almacenamiento intermedio binario que leyó, puede usar los comandos estándar print para obtener el resultado necesario.
Listado 6. Imprimiendo los detalles
print "sourceAddress =" ,
(struct.unpack_from('B', buffer,0),struct.unpack_from('B', buffer,1),
struct.unpack_from('B', buffer,2),struct.unpack_from('B', buffer,3))
print "destinationAddress = " ,
(struct.unpack_from('B', buffer,4),struct.unpack_from('B', buffer,5),
struct.unpack_from('B', buffer,6),struct.unpack_from('B', buffer,7))
print "sourcePort = " , (struct.unpack_from('H',buffer,8))
print "destinationPort = " , (struct.unpack_from('H',buffer,10))
print "protocolUsed = " , (struct.unpack_from('H',buffer,12))
print "timeStamp = " ,
(struct.unpack_from('B', buffer,14),struct.unpack_from('B', buffer,15),
struct.unpack_from('B', buffer,16),struct.unpack_from('B', buffer,17))
|
El resultado esperado del Listado 6 deberá estar en este formato:
Listado 7. Resultado de la impresión
sourceAddress = ((192,), (168,), (10,), (102,)) destinationAddress = ((207,), (168,), (1,), (103,)) sourcePort = (11299,) destinationPort = (11555,) protocolUsed = (256,) timeStamp = ((1,), (12,), (0,), (1,)) |
Automatice el proceso para todos los registros
Ahora, para leer e imprimir todos los registros de todo el archivo de memoria compartida, cree un bucle:
Listado 8. Creando un bucle para leer e imprimir todos los registros
for element in range (0,56):
#loop 18 since we know the file size and
#the record length: 1024/18 = 56 records
buffer = ReadBytes('/dev/shm/devmem ', 18)
self.assertEqual(len(buffer), struct.calcsize('llllh'))
sourceAddress = struct.unpack_from('B', buffer,0),
struct.unpack_from('B', buffer,1),
struct.unpack_from('B', buffer,2),
struct.unpack_from('B', buffer,3))
destinationAddress = struct.unpack_from('B', buffer,4),
struct.unpack_from('B', buffer,5),
struct.unpack_from('B', buffer,6),
struct.unpack_from('B', buffer,7))
sourcePort = struct.unpack_from('B', buffer,8),
struct.unpack_from('B', buffer,9)
destinationPort = struct.unpack_from('B', buffer,10),
struct.unpack_from('B', buffer,11))
protocolUsed = ,struct.unpack_from('B', buffer,12),
struct.unpack_from('B', buffer,13))
timeStamp = struct.unpack_from('B', buffer,14),
struct.unpack_from('B', buffer,15),
struct.unpack_from('B', buffer,16),
struct.unpack_from('B', buffer,17))
print "sourceAddress = " ,
struct.unpack_from('B', buffer,0),
struct.unpack_from('B', buffer,1),
struct.unpack_from('B', buffer,2),
struct.unpack_from('B', buffer,3))
print "destinationAddress = " ,
struct.unpack_from('B', buffer,4),
struct.unpack_from('B', buffer,5),
struct.unpack_from('B', buffer,6),
struct.unpack_from('B', buffer,7))
print "sourcePort = " ,
struct.unpack_from('H',buffer,8))
print "destinationPort = " ,
struct.unpack_from('H',buffer,10))
print "protocolUsed = " ,
struct.unpack_from('H',buffer,12))
print "timeStamp = " ,
struct.unpack_from('B', buffer,14),
struct.unpack_from('B', buffer,15),
struct.unpack_from('B', buffer,16),
struct.unpack_from('B', buffer,17))
|
¡Eso es todo lo que se necesita! Hemos analizado un formato conocido de volcado de memoria binaria en Linux, y usamos structs de Python para leer el volcado de datos binarios y los hemos presentado en un formato legible.
| Descripción | Nombre | tamaño | Metodo de descarga |
|---|---|---|---|
| Python app for parsing memory dump | ParseBinaryInPython.zip | 6KB | HTTP |
Información sobre métodos de descarga
Aprender
- Aprenda más sobre Python en la Python Software
Foundation.
- Aprenda cómo utilizar la herramienta hexdump para mostrar archivos especificados en un formato especificado por el usuario.
- Navegue más artículos developerWorks sobre Python.
- En la zona Linux de developerWorks,
encontrará cientos de artículos "cómo hacer" y tutoriales, así como descargas, foros de discusión, y gran cantidad de otros recursos para desarrolladores y administradores Linux.
- Permanezca actualizado(a) con los Eventos técnicos y webcasts de developerWorks enfocados en una variedad de productos IBM y de temas de la industria TI.
- Asista a una charla informativa developerWorks Live! sin costo para actualizarse rápidamente sobre productos y herramientas IBM y sobre las tendencias de la industria.
- Vea Demostraciones on demand de developerWorks que van desde demostraciones sobre instalación y configuración de productos para principiantes, hasta funcionalidades avanzadas pera desarrolladores experimentados.
- Siga a DeveloperWorks en Twitter, o suscríbase a un feed de tweets Linux en developerWorks.
Obtener los productos y tecnologías
-
Descargue Python del sitio web Python.
-
Evalúe productos de IBM de la forma que mejor se ajuste a usted: Descargue una prueba de producto, ensaye un producto en línea, use un producto en un entorno en nube, o pase algunas horas en el
SOA Sandbox aprendiendo a implementar la Arquitectura Orientada a Servicios con eficiencia.
Comentar
- Participe en la Comunidad My developerWorks.
Conéctese con otros usuarios developerWorks mientras explora los blogs, foros, grupos y wikis dirigidos a desarrolladores.
Asha Shivalingaiah es Software Engineer y trabaja en pruebas en el Australia Development Lab para la Solución de Seguridad de IBM, Tivoli. Desde que se unió al equipo IBM Rational en el 2008, ha trabajado con Rational Rhapsody y con otras herramientas del portafolio Rational para administrar el ciclo de vida de desarrollo de software. Cuenta con bastante experiencia en lenguajes de modelado como UML 2 y SysML, y en el uso de Rational Rhapsody para desarrollo orientado a modelos.