Construya una aplicación Python para analizar volcados de memoria compartida

Use la herramienta struct para extraer datos de sistema para análisis

Aprenda cómo analizar volcados de memoria compartida legibles por máquinas en una plataforma Linux y extraiga su formato de datos esperado usando Python y la herramienta struct . En este artículo primero verá cómo determinar el formato de los datos mediante la lectura del formato de archivo binario del archivo de volcado; usted necesita esto para poder analizar, extraer y analizar los datos. Luego, verá cómo analizar el archivo con base en el formato y luego a hacer que los resultados coincidan con el formato esperado para producir un resultado de validación.

Actualización: En la sección Descargas , encontrará una aplicación Pyhton operacional y un archivo de volcado que puede utilizar tal como está o que puede modificar de acuerdo a sus necesidades. Hemos cambiado el nombre del archivo de volcado a lo largo de este artículo para que coincida con el nombre usado en la descarga. -Ed.

Asha Shivalingaiah, Software Engineer, IBM

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.



18-07-2011 (Primera publicación 18-07-2011)

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.

Antes de comenzar

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.


Definiendo el escenario

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.


Analizando el archivo volcado

Los pasos para comprender los datos en un archivo de volcado de memoria (identificar el formato, analizar y leer el archivo) son relativamente simples:

  1. Abra el archivo.
  2. Lea los bytes con un descriptor de archivos.
  3. Convierta los datos a un formato de cadena de caracteres legible cuando sea necesario.
  4. Verifique si el almacenamiento intermedio está intacto o si está truncado o tiene errores.
  5. Desempaquete los datos del almacenamiento intermedio.
  6. Extraiga la información.
  7. Imprima los datos.
  8. 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.

Abra el archivo

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')

Lea los bytes

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.

Convierta los datos

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

Desempaquete los datos

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])

Extraiga los detalles

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.

Imprima el resultado

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.


Descargar

DescripciónNombretamaño
Python app for parsing memory dumpParseBinaryInPython.zip6KB

Recursos

Aprender

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.

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=Linux
ArticleID=732603
ArticleTitle=Construya una aplicación Python para analizar volcados de memoria compartida
publish-date=07182011