martes, 8 de diciembre de 2009

Automatizando los volcados de memoria

Pues resulta que andaba yo revisando uno de los PCs traídos de un cliente para ser reparado en taller, y tal como tengo por costumbre, y por eso de ir aprendiendo, me disponía a utilizar windd para volcar la memoria del sistema en un fichero. En esas me encontraba cuando apareció por allí mi compañero, una grandísima persona y aún mejor administrador de sistemas Linux, interesándose por mis maniobras. Después de contarle un poco por encima lo que aquí acabo de describir y verme consultar la ayuda del comando en un par de ocasiones me dijo - yo personalmente, cuando tengo que repetir una tarea al menos dos veces, siempre me genero un script.

Análisis de objetivos

Como suele pasarme con las palabras de mi compañero, no suelo darles importancia en el momento, pero siempre acaban influenciándome. Dándole vueltas al tema me planteé los siguientes requerimientos para el script:

  • Recibirá como argumento la letra de unidad en la que almacenar el volcado resultante.

  • Debería poder detectar si en la unidad indicada hay sufiente espacio disponible para almacenar el volcado, por lo tanto tiene que ser capaz de averiguar la cantidad de memoria física de que dispone el equipo.

  • Tiene que poder detectar el tipo de sistema operativo, 32 ó 64 bits, para lanzar la versión de windd adecuada.

  • El resultado debería almacenarse en un directorio con un nombre significativo y el nombre del fichero debería ser lo suficientemente particular como para no sobreescribir ninguno existente, o ser susceptible de ser sobreescrito.

Si a todo lo anterior le sumamos la posibilidad de ejecutarlo con el menor número de modificaciones en las diferentes versiones NT del sistema operativo Windows, el primer "lenguaje" que me vino a la mente fue "batch scripting", así que lo primero tener a mano los "Assorted NT/2000/XP CMD.EXE Script Tricks", mantenidos por el profesor Timo Salmi.

Descomponiendo el problema en partes mas pequeñas

Empezaré primero tratando de determinar el número de bits del sistema operativo en el que se ejecuta el script. Tal y como sugieren en el siguiente enlace, quedaría algo así:
@echo off & setlocal enableextensions

 
if "%PROCESSOR_ARCHITECTURE%" == "x86" (
if NOT DEFINED "%PROCESSOR_ARCHITEW6432%" (
echo Sistema de 32 bits
) else (
echo Sistema de 64 bits
)
) else (
echo Sistema de 64 bits
)

Otra alternativa sería la mencionada en el siguiente artículo de la base de conocmientos de Microsoft, How To Check If Computer Is Running A 32 Bit or 64 Bit Operating System, el cual se basa en la comprobación de la siguiente clave del registro:
HKLM\HARDWARE\DESCRIPTION\System\CentralProcessor\0

y más concretamente en el valor asociado al parametro Platform ID. Sin embargo descarto de inmediato éste método debido a su limitada universalidad (sólo sirve para sistemas W2k y W2k3).

Una vez salvado este punto continúo, por ejemplo, obteniendo la cantidad de memoria física de que dispone el sistema, utilizando para ello el comando integrado systeminfo y algo de parseo de los resultados. Así que añado las siguientes líneas al script:
for /f "tokens=6,7" %%a in (

'systeminfo^|find "Cantidad total de memoria"') do (
set memory_=%%a
set units_=%%b)
echo Memoria total = %memory_% %units_%

Y ya de seguido procedo utilizando el comando fsutil para obtener el espacio libre de una unidad concreta indicada directamente en el script para simplificar así el proceso:
set drive_=C:

for /f "tokens=2 delims=:" %%a in (
'fsutil volume diskfree %drive_%^|
findstr /i /c:"bytes libres"') do (
set totalSizeBytes_=%%a)
if defined totalSizeBytes_ echo Espacio libre en %drive_% %totalSizeBytes_% bytes
if not defined totalSizeBytes_ echo %drive_% The size could not be resolved

El resultado obtenido ejecutando el "programa" en mi portatil sería:
C:\Users\javi\Desktop\pruebas>testsystem.bat

Sistema de 32 bits
Memoria total = 2.047 MB
Espacio libre en C: 17223503872 bytes

La solución encontrada sería "casi perfecta", pero el problema es que me gustaría poder ejecutar el script también en sistemas Windows 2000 y éstos no disponen ni del comando systeminfo ni del comando fsutil. Si además tenemos en cuenta que no es posible realizar
operaciones aritméticas desde la línea de comandos de Windows sin utilizar herramientas de terceros decido currarme yo mismo un programa para suplir estas carencias.

El API de Windows y yo

Si partimos de la base de que no soy programador ni de lejos, pero que me encanta probar y aprender además de ser un experto en cortar, pegar y encajar, seguro que el código es infinitamente mejorable. De hecho os invito a descargarlo, modificarlo y arreglarlo, pero eso sí, compartiendo aquí vuestras mejoras para que aprendamos todos.

Para generar el ejecutable yo he utilizado MS Visual C++ 2008 Express Edition, entorno que puede descargarse de forma gratuita desde la web de Microsoft; el tipo de proyecto es una aplicación de consola Win32. Para los más impacientes he incluido una versión ya compilada junto con el resto de los ficheros que acompañan esta entrada.

Su funcionamiento es muy simple, tal y como puede apreciarse por la escueta ayuda que muestra al invocarlo sin indicar ningún parametro:
C:\Users\javi\Desktop\mkmemdmp>mkmemdmp.exe

Usage: mkmemdmp.exe <drive>

Example: mkmemdmp.exe C:

Básicamente su función es la de comprobar si el volcado de memoria a realizar cabe en la unidad indicada y lanzar el batch script adecuado en función del sistema operativo en que se ejecute.

Como fuentes de documentación he utilizado los siguientes enlaces:

MEMORYSTATUSEX Structure
GlobalMemoryStatusEx Function
Driver Installation for 32-bit and 64-bit Platforms
GetSystemWow64Directory Function
SYSTEM_INFO Structure
GetSystemInfo Function
PROCESS_INFORMATION Structure
STARTUPINFO Structure
Processes: How can I start a process?

Otro poquito de batch scripting

Ya tengo el programa, el cual se encarga de hacer las comprobaciones oportunas y lanzar en consecuencia el script adecuado, dump32.bat para sistemas de 32 bits o dump64.bat para sistemas de 64 bits, indicándole la unidad a utilizar para almacenar los resultados.

El script en primer lugar comprueba si ya existe el directorio almacén, creándolo en caso contrario utilizando para ello el nombre de máquina:
set dir_=%1

set dir_=%dir_%memdmp_%COMPUTERNAME%
 
if not exist %dir_% (
mkdir %dir_%
)

A continuación genera de forma automática los nombres para los ficheros, tanto para el de volcado como para el de log, utilizando una combinación de la fecha y hora en que se ejecuta:
for /f "tokens=1-3 delims=./-" %%f in ("%date%") do (

set filename_=%%h%%g%%f
)
for /f "tokens=1-3 delims=:, " %%i in ("%time%") do (
set filenamedmp_=%filename_%_%%i%%j%%k.dmp
set filenamelog_=%filename_%_%%i%%j%%k.log
)

Ya por último lanza la herramienta windd, la verdadera estrella de la función, almacenando sus mensajes de salida en el fichero de log y mostrando los parámetros utilizados para ejecutarla. Por defecto el volcado se obtiene en formato raw y se genera el hash SHA1 del mismo.

Juntándolo todo

Para que todo funcione correctamente crearemos primero el directorio mkmemdmp y moveremos allí el ejecutable mkmemdmp.exe y los scripts dump32.bat y dump64.bat. Ahora descargaremos el paquete windd y extraeremos en el directorio anterior los binarios de 32 y 64 bits incluyendo sus drivers correspondientes:
C:\mkmemdmp>dir

El volumen de la unidad C es SYSTEM
El número de serie del volumen es: B00D-25C9
 
Directorio de C:\mkmemdmp
 
08/12/2009 22:03 <DIR> .
08/12/2009 22:03 <DIR> ..
08/12/2009 21:46 1.379 dump32.bat
08/12/2009 19:03 1.375 dump64.bat
07/12/2009 21:19 9.216 mkmemdmp.exe
13/11/2009 13:02 92.088 win32dd.exe
13/11/2009 13:02 50.872 win32dd.sys
13/11/2009 13:02 101.304 win64dd.exe
13/11/2009 13:02 58.296 win64dd.sys
7 archivos 314.530 bytes
2 dirs 17.214.128.128 bytes libres

Suponiendo ahora que pretendemos almacenar el volcado y los ficheros resultantes en la unidad C: el comando a ejecutar sería:
C:\mkmemdmp>mkmemdmp.exe C:

 
+---------------------------------------------------------------+
| - Neo System Forensics - |
| http://neosysforensics.blogspot.com |
+---------------------------------------------------------------+
 
Script para volcado automatizado de la memoria fisica del sistema
Detecta y utiliza la version adecuada de la herramienta windd (*)
 
Fichero de dmp generado:
C:\memdmp_OSIRIS\20091208_220938.dmp
 
Fichero de log generado:
C:\memdmp_OSIRIS\20091208_220938.log
 
Comando windd utilizado:
win32dd.exe /a /s 1 /f C:\memdmp_OSIRIS\20091208_220938.dmp
 
(*) windd - Kernel land physical memory acquisition
Copyright (c) 2007 - 2009, Matthieu Suiche <http://www.msuiche.net>
Copyright (c) 2008 - 2009, MoonSols <http://www.moonsols.com>

Y el contenido del directorio con los resultados sería:
C:\mkmemdmp>dir ..\memdmp_OSIRIS

El volumen de la unidad C es SYSTEM
El número de serie del volumen es: B00D-25C9
 
Directorio de C:\memdmp_OSIRIS
 
08/12/2009 22:09 <DIR> .
08/12/2009 22:09 <DIR> ..
08/12/2009 22:10 2.147.155.968 20091208_220938.dmp
08/12/2009 22:10 2.589 20091208_220938.log
2 archivos 2.147.158.557 bytes
2 dirs 12.927.860.736 bytes libres


Correción 09/12/2009:

Para que el binario compilado incluido en el paquete comprimido funcione, es necesario tener instalado el Microsoft Visual C++ 2008 Redistributable Package (x86).

Código, scripts y ejecutable: mkmemdmp.zip

11 comentarios:

novlucker dijo...

Excelente! ... Al ver tu script he visto que se podría automatizar de igual modo en vbs y evitar las tools de 3eros, PERO, para algunas cosas (más bien varias) se necesita hacer uso de WMI, y eso esta disponible a partir de W2K, así que otra vez se pierde "universalidad" ¬¬ , por lo tanto ... me quedo con tu alternativa :)

Hasta la próxima entrada!

Saludos

neofito dijo...

Creo que WMI esta disponible "de serie" a partir de Windows 2000 SP2, pero teniendo en cuenta la necesidad de instalar el paquete redistributable de MS Visual C++ para que funcione mi programa podemos considerarlo bastante universal.

Si tienes tiempo y estas dispuesto estaria encantado de echarle un vistazo a tu script.

Saludos y gracias por el comentario

silverhack dijo...

#neofito
puedes utilizar WSH que viene de serie con W2K.
Para saber por batch en qué unidad estás, tienes las variables de entorno %HOMEDRIVE% y %SYSTEMDRIVE%. Y para saber si estamos sobre 32 o 64 bits, también tienes la variable %PROCESSOR_ARCHITECTURE%. Espero que te valgan!
Hablando de otros temas, estate pendiente del blog porque voy a publicar algo dentro de poco y me viene de perlas este post tuyo.
Hablamos!

sch3m4 dijo...

silverhack, ^_^ ¿que vas a publicar el qué? ^_^ Responde a los mails maricona!

neofito dijo...

#silverhack

Bueno es saberlo, intentare sacar un rato para ponerme con ello :)

Saludos

PD: Por cierto, supongo que no faltaras a la rootedcon, ¿verdad?

silverhack dijo...

#neofito
Allí nos veremos!

neofito dijo...

Me temo que no asistire a los labs :(

Saludos

MovBiT dijo...

Hola Neofito, estoy haciendo tu idea en asm. Quería preguntarte porque lo haces con scripts, te parece mejor solución?. Si lo termino, si quieres te lo puedo enviar y lo pruebas y me comentas si funciona bien y eso.
Venga, saludos

neofito dijo...

Hola MovBiT, y bienvenido

Pues lo he hecho con scripts para simplificar el programa y que quedara muy patente que el trabajo de verdad lo hace windd. Por otra parte de esa forma me parece mucho mas facil modificar los parametros de configuracion e incluso poder llamar a otros scripts (vease MIR-ROR, p.e.).

Estare encantado de echarle un vistazo a tu alternativa cuando la tengas terminada, ademas seguro que aprendo algo de asm que al final es mi asignatura pendiente :(

Saludos

MovBiT dijo...

Buenas, ya tengo el programita este, a que dirección te lo puedo mandar.
Un saludo.

neofito dijo...

neofito en gmail.

Saludos