ESTUDIO COLECTIVO DE DESPROTECCIONES
WKT Tutorialz Site
WKT
Título

La Memoria en W32: intro

W95 / W98 / NT
Dificultad 1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista
Herramientas SoftIce v3.24
Objetivo Suministrar conocimiento que permita comprender c≤mo W32 traduce direcciones virtuales en direcciones fφsicas.
Cracker nuMIT_or
Fecha Octubre de 1999
Fuente PERTZOLD, Ch: Programaci≤n en Windows 95, Madrid, McGraw-Hill, 1996. Capφtulo "Gesti≤n de memoria y entrada/salida de archivos", Apartado: "Finalmente, 32 bits", pp 727-730.

La Memoria en W32: intro

Introducci≤n

La gesti≤n de memoria en W32 se realiza a travΘs de tres mecanismos:

 

1- memoria virtual: cuando se quiere gestionar grandes estructuras de objetos.
2- proyecci≤n de archivos en memoria: para gestionar grandes paquetes de datos en los archivos y compartir datos entre procesos distintos.
3- montφculos (heaps): para gestionar muchos bloques peque±os de datos.

El tema estß suficientemente desarrollado en tres artφculos clßsicos de Randy Katz:

 

1- Managing Virtual Memory in Win32
2- Managing Memory-Mapped Files in Win32
3- Managing Heap Memory in Win32

En este artφculo me limitarΘ a explicar un poco la gesti≤n de la memoria virtual en W32, concetrßndome en la manera c≤mo W32 traduce direcciones virtuales a direcciones reales. Se trata de una adpataci≤n de la secci≤n "Finalmente, 32 bits" del capφtulo "Gesti≤n de memoria y entrada/salida de archivos" del libro Programaci≤n en Windows 95 de Charles Petzold.

 

 

Al Atake

 

┐QU╔ ES MEMORIA VIRTUAL?
==========================
A medida que los programas de aplicaci≤n se han hecho mßs sofisticados y mßs grandes, se ha necesitado de ampliar las posibilidades de la memoria dinßmica de los sistemas. Debido a lo poco econ≤mico que resulta atender la gran exigencia de memoria simplemente implementando ships con elevada capacidad de almacenamiento, este problema se ha abordado vφa software, a travΘs de sistemas operativos que ofrecen gesti≤n de memoria virtual.

Es un mecanismo de gesti≤n de la memoria de un sistema que implementa un espacio de memoria ficticio, llamado espacio de direcciones o de nombres, el cual es mucho mayor que el espacio de memoria real fφsica que posee el hardware del sistema. De esta manera, el programador al desarrollar aplicaciones para un SO con memoria virtual, no tiene que ocuparse de la memoria fφsica, simplemente programa como si tuviera a su disposici≤n una gran espacio de direcciones para cada proceso.

 

┐C╙MO ES POSIBLE LA GESTI╙N DE MEMORIA VIRTUAL?
=================================================
El programador desarrolla sobre un gran espacio de memoria virtual, compila o ensambla sus fuentes para producir los ejecutables de sus aplicaciones. Es todo lo que hace. El SO se encarga de lo demßs: traduce las direcciones virtuales en direcciones fφsicas y carga en la RAM del sistema s≤lo aquellos bloques de la aplicaci≤n que necesi ta, nunca carga todo el ejecutable ya que Θste seguramente ocuparφa todo el espacio de memoria fφsica disponible y el sistema colapsarφa.



┐C╙MO TRADUCE W32 DIRECCIONES VIRTUALES A REALES?
=====================================================
W32 requiere la presencia de un microprocesador Intel 386, 486 o Pentium. Estos procesadores usan direccionamiento de memoria de 32 bits y por tanto son capaces de acceder 2 elevado a la 32, es decir, 4.294.967.296 bytes (4 gigabytes o GB) de memoria fφsica. Por supuesto, la mayorφa de los usuarios de W32 no estßn cerca de ese lφmite.

Las direcciones de 32 bits usadas por los programas W32 para acceder al c≤digo y los datos no son las direcciones fφsicas de 32 bits que el microprocesador usa para direccionar la memoria fφsica. La direcci≤n que utiliza la aplicaci≤n se llama direcci≤n ½virtual╗ la cual es una direcci≤n ficticia que se traduce a una direcci≤n fφsica a travΘs de una ½tabla de pßgina╗.

Para leer una instrucci≤n o dato en la RAM, el CPU necesita colocar en el BUS de direcciones esa direcci≤n donde debe estar ubicada la direcci≤n o dato solicitado. Por ejemplo, si quiere acceder a la memoria de video, que estß en 0A0000h, el CPU debe colocar este valor en el BUS.

Como las direciones de las aplicaciones son direcciones virtuales, el CPU debe traducirlas a direcciones fφsicas antes de acceder a los datos en la RAM.

Las aplicaciones suelen ignorar este proceso. El programa parece almacenarse en un espacio de direcciones de 32 bits y no hay nada extra±o cuando se tiene que acceder a esta memoria. Sin embargo, es conveniente hacerse una idea de lo que significa esto.

W32 pagina la memoria fφsica, la divide en ½pßginas╗ con una longitud de 4096 bytes (4 KB). Una mßquina equipada con 8 megabytes de memoria tiene 2048 pßginas. W95/98 mantiene una colecci≤n de tablas de pßgina (ellas mismas pßginas 4 KB) para traducir direcciones virtuales a direcciones fφsicas.

Cualquier proceso en W32 tiene su propia ½pßgina de directorio╗, que es una colecci≤n de hasta 1024 entradas de 32 bits almacenadas contiguamente, una despuΘs de otra. La direcci≤n fφsica de la pßgina de directorio activa se almacena en un registro del microprocesador llamado CR3 (CONTROL REGISTER 3), que se cambia cuando el SO conmuta el control entre procesos. Los 10 bits altos de una direcci≤n virtual especifican una de las 1024 entradas posibles en esta pßgina de directorio. Los 20 bits altos de la entrada de la pßgina de directorio indica una direcci≤n fφsica de una tabla de pßgina (los 12 bits inferiores de la direcci≤n fφsica se definen a cero). Esto referencia otra pßgina, que tambiΘn tiene hasta 1024 entradas de 32 bits. Los 10 bits del medio de la direcci≤n virtual referencian una de estas entradas. De nuevo, la entrada tiene una direcci≤n fφsica de 20 bits para indicar la posici≤n de comienzo de un marco de pßgina, que es una direcci≤n fφsica. Los 12 bits inferiores de una direcci≤n virtual apuntan a una posici≤n fφsica dentro de este marco de pßgina.

Mostrado simb≤licamente, uno puede representar una direcci≤n virtual de 32 bits (que es con lo que trabaja una aplicaci≤n) como una entrada de pßgina de directorio de 10 bits (d), una entrada de tabla de pßgina de 10 bits (p) y un offset de 12 bits:

 

dddd-dddd-ddpp-pppp-pppp-oooo-oooo-oooo

Para cada proceso, el microprocesador almacena un valor de 20 bits en el registro CR3 (r de registro):

 

rrrr-rrrr-rrrr-rrrr-rrrr

La direcci≤n fφsica de comienzo de la pßgina de directorio activa del proceso es:

 

rrrr-rrrr-rrrr-rrrr-rrrr- 0000-0000-0000

RecuΘrdese que todas las pßginas se almacenan en lφmites de 4 KB, por tanto, cada pßgina comienza en una direcci≤n con los 12 bits inferiores igual a cero. El microprocesador primero accede a la direcci≤n fφsica:

 

rrrr-rrrr-rrrr-rrrr-rrrr-dddd-dddd-dd00

Esta posici≤n contiene otro valor de 20 bits (t de tabla):

 

tttt-tttt-tttt-tttt-tttt

lo cual indica la direcci≤n fφsica de comienzo de una tabla de pßgina:

 

tttt-tttt-tttt-tttt-tttt-0000-0000-0000

El microprocesador accede luego a una direcci≤n fφsica:

 

tttt-tttt-tttt-tttt-tttt-pppp-pppp-pp00

Almacenado en este ßrea hay un valor de 20 bits de un marco de pßgina (c de marco):

 

CCCC-CCCC-CCCC-CCCC-CCCC

La direcci≤n fisφca final de 32 bits es una combinaci≤n de este marco de pßgina con los 12 bits de offset inferiores de la direcci≤n virtual:

 

CCCC-CCCC-CCCC-CCCC-CCCC-0000-0000-0000

Esta es la direcci≤n fφsica. Es todo.

Puede parecer que traducir una direcci≤n virtual en direcci≤n fφsica es un proceso que lleva mucho tiempo, pero realmente no es asφ. Los microprocesadores Intel 386, 486 y Pentium tienen una memoria cache interna que puede guardar estas tablas de pßgina dentro del microprocesador. La traducci≤n se realiza de forma muy rßpida sin ninguna penalizaci≤n de tiempo. Esta doble paginaci≤n (pßginas que se guardan en una pßgina) ofrece a cada aplicaci≤n un lφmite te≤rico de aproximadamente un mill≤n de pßginas de 4 KB.

 

EMPLEANDO SICE
================
Podemos revisar el proceso de traducci≤n de memoria virtual a memoria fφsica con SICE.

Carguemos notepad.exe con el loader de SICE. Cuando se despliega la pantalla de SICE, el depurador estß apuntando a la primera instrucci≤n del programa cuya direcci≤n virtual es 00401000h. A partir de esta direcci≤n podemos calcular la direcci≤n fφsica donde se ubica esta instrucci≤n en la RAM.

Traduzcamos el valor hexadecimal de la direcci≤n virtual a binario:

 

0000 0000 0100 0000 0001 0000 0000 0000

Tenemos:
1. Entrada del directorio de pßginas o primario, diez bytes altos:

 

0000 0000 01 = 1

Es la entrada 1 del directorio primario

2. Entrada de la tabla de pßinas o directorio secundario, diez bytes medios:

 

00 0000 0001 = 1

Es la entrada 1 del directorio secundario

3. Desplazamiento en el marco de pßgina de memoria fφsica, doce bytes bajos restantes:

 

0000 0000 0000 = 0

Comienzo del marco de pßgina o desplazamiento cero. RecuΘrdese que es la primera instrucci≤n del programa.

Para determinar exactamente la direcci≤n fφsica de la direcci≤n debemos:

1. obtener la direcci≤n fφsica donde estß el directorio de pßginas activas o directorio primario. Este valor estß en CR3 y lo obtenemos con el comando 'CPU' de SICE. Cada entrada de este directorio es un puntero al directorio secundario.

2. obtener la direcci≤n de la entrada en el directorio primario donde se encuentra la direcci≤n del comienzo del directorio secundario. Sabemos la direcci≤n del inicio del directorio primario por los 20 bits altos del contenido de CR3. La entrada dentro del directorio primario que dice donde estß la tabla que interesa estß indicada por los diez bits altos de la direcci≤n virtual. Como cada entrada del directorio primario es de 32 bits = 4 bytes, hay que multiplicar el n·mero de entrada por 4 para obtener la direcci≤n de la entrada correspondiente en el directorio primario.

3. obtener la direcci≤n fφsica d≤nde se encuentra la tabla de pßginas o directorio secundario. Con la direcci≤n de la entrada del directorio de pßgina donde estß la direcci≤n de la tabla de pßginas activas, se puede emplear el comando PEEK de SICE para obtener esta ·ltima direcci≤n:

 

PEEK D DIRECCI╙N_DE_ENTRADA_DE_DIRECTORIO_PRIMARIO

4. obtener la direcci≤n de la entrada en la tabla de pßginas donde estß la direcci≤n del inicio o marco de la pßgina donde estß la instrucci≤n apuntada por la direcci≤n virtual. Sabemos la direcci≤n del inicio del directorio secundario por los 20 bits altos del contenido la entrada del directorio primario. La entrada dentro del directorio primario que dice donde estß el mmraco de pßgina que buscamos estß indicada por los diez bits medios de la direcci≤n virtual. Como cada entrada del directorio secundario es de 32 bits=4 bytes, hay que multiplicar el n·mero de entrada por 4 para obtener la direcci≤n de la entrada correspondiente en el directorio secundario.

5. obtener la direcci≤n fφsica d≤nde se encuentra el marco de pßgina donde se ubica la instrucci≤n se±alada por la direcci≤n virtual. Con la direcci≤n de la entrada del directorio secundario donde estß la direcci≤n del marco de pßginas, se puede emplear el comando PEEK de SICE para obtener esta ·ltima direcci≤n, ya que este comando devuelve el contenido de una direcci≤n fφsica:

 

PEEK D DIRECCI╙N_DE_ENTRADA_DE_DIRECTORIO_SECUNDARIO

6. obtener la direcci≤n fφsica de la instrucci≤n en la pßgina especφfica. Este valor se obtiene a partir de los 20 bits altos del contenido en la entrada correspondiente en el directorio secundario y los 12 bits bajos en la direcci≤n virtual. Estos bits bajos de la direcci≤n virtual son un desplazamiento dentro del marco de pßgina donde se ubic≤ el contenido de la aplicaci≤n.

7. obtener el contenido en la direcci≤n fφsica se±alada por el desplazamiento dentro del marco de pßgina donde se encuentra el c≤digo o los datos indicados por la direcci≤n virtual. Finalmente el comando

 

PEEK D DESPLAZAMIENTO_EN_MARCO_DE_P┴GINA

devuelve, invertida, la instrucci≤n en octal en la direcci≤n virtual. Para comprobar esto, si el puntero de SICE apunta a esta direcci≤n virtual el comando

 

D EIP

devuelve el mismo valor en la ventana de datos de SICE. TambiΘn podemos verificar esto revisando el c≤digo en octal de la instrucci≤n correspondiente. Debemos ver lo mismo pero invertido. El comando

 

CODE ON

lo mostrarß.

Continuemos con notepad.exe.

Ya tenemos los valores que corresponden a la direcci≤n virtual de la primera instrucci≤n de notepad.exe

La direcci≤n fφsica de la pßgina de directorio o directorio primario lo obtenemos con el comando CPU de SICE: 008170000h. Traduzcßmoslo a binario:

 

0000 0000 1000 0001 0111 0000 0000 0000

Los primeros 20 bits de este n·mero indican la direcci≤n fφsica del comienzo del directorio primario del proceso activo. Los diez bits altos de la direcci≤n virtual nos dicen que se trata de la primera entrada en este directorio

 

0000 0000 1000 0001 0111 - 0000 0000 01 - 00
                    ^                                    ^
     bytes altos en CR3             -     bytes altos
                                                    en dir virtual

obtenemos una nueva direcci≤n fφsica, la de la entrada 1 de la tabla de pß-
ginas o directorio secundario: 00817004h.

Ejecutemos el comando PEEK D 817004.

 

PEEK D 00817004

SICE nos dß: 01EFE267

Obtenemos ahora la direcci≤n fφsica del directorio secundario. En binario:

 

0000 0001 1110 1111 1110 0010 0110 0111

Los doce bits altos de este valor apuntan al inicio del directorio secundario. Los diez bits medios indican una entrada dentro de este directorio.

 

1100 0001 1011 1011 1111 - 00 0000 0001 - 00
                    ^                                    ^
 bytes altos en 01EFE267      -      bytes medios
                                                   en dir virtual

En hexadecimal: 01EFF004H

Esta entrada contiene la direcci≤n del marco de pßgina en memoria fφsica donde se encuentra instrucci≤n indicada por la direcci≤n virtual.

Ahora obtengamos la direcci≤n del marco de pßgina:

 

PEEK D 01EFF004

Obtenemos un valor como 01DEC225h. Traduzcamos este valor a binario:

0000 0001 1101 1110 1100 0010 0010 1001

Los 20 bits altos de este valor son la direcci≤n fφsica de un marco de pßgina, es decir, del comienzo de una pßgina de memoria fφsica. Si agregamos a este valor el valor indicado por los doce bits bajos de la direcci≤n virtual obtenemos la direcci≤n fφsica donde se haya la instrucci≤n se±alada por la direcci≤n virtual:

 

0000 0001 1101 1110 1100 - 0000 0000 0000
                    ^                                    ^
bytes altos en 01DEC225h     -     bytes bajos
                                                    en dir virtual

PEEK D 01DEC0000

SICE devuelve 83EC8B55

El valor devuelto por este comando corresponde a la instrucci≤n en octal, pero invertido, a la que apunta la direcci≤n virtual. Si esta es la direcci≤n se±alada por el puntero de SICE el siguiente comando nos devolverß el mismo valor regeresado antes:

 

D EIP

Vemos ahora que la ventana de datos de SICE apunta a 83EC8B55, en la direcci≤n EIP, el mismo valor devuelto con el comando 'PEEK D 01DEC0000'

Hagamos "code on" y veamos la correspondencia entre el valor desplegado por el ·ltimo PEEK y el c≤digo en octal desplegado por SICE, que seguramente serß el mismo pero invertido

 

55         push  ebp
8BCE   mov   ebp, esp
83...


┐QU╔ VENTAJAS TIENE LA PAGINACI╙N?
====================================
Las ventajas de la paginaci≤n son:

Primero, las aplicaciones se aislan unas de otras. Ning·n proceso puede inadvertidamente (o maliciosamente) escribir encima del espacio de c≤digo o datos de otro proceso, pues ni siquiera es capaz de direccionar la memoria del otro proceso sin el valor CR3 adecuado, y definir este valor es una tarea que s≤lo puede hacer el n·cleo W32.

Segundo, este mecanismo de paginaci≤n resuelve uno de los problemas mßs bßsicos en un entorno multitarea: la consolidaci≤n de la memoria libre. En un esquema de direccionamiento mßs simple, a medida que se ejecutan m·ltiples programas y se sale de ellos, la memoria se va fragmentando. Si la memoria estß demasiado fragmentada, los programas no se pueden ejecutar porque no tienen suficiente memoria contigua, incluso aunque la cantidad total de memoria libre sea la adecuada. Con la paginaci≤n, no es necesario consolidar la memoria fφsica libre porque las pßginas no tienen que ser contiguas. Cualquier cosa se gestiona manipulando las tablas de pßgina. La ·nica pΘrdida viene realmente por el espacio perdido en las propias tablas de pßgina y la granularidad de las pßginas de 4 KB.

Tercero, hay bits extra en las entradas de tabla de pßgina de 32 bits ademßs de las direcciones de 20 bits. Un bit indica que se ha accedido a una pßgina particular (se denomina bit de ½acceso╗); otro que la pßgina ha sido escrita (el bit ½sucio╗). W32 puede usar estos bits para determinar si una pßgina de memoria se puede intercambiar (swapping) a un archivo de disco para obtener mßs memoria fφsica libre. Otro bit (½presencia╗) indica si la pßgina se ha intercambiado al disco y se tiene que recargar en memoria.

Otro bit (½lectura/escritura╗) indica si la pßgina se puede escribir. Este bit protege el c≤digo de los punteros errantes. Por ejemplo, si se incluye la instrucci≤n en c≤digo C siguiente en un programa Windows:

* (int *) WinMain 0;

se obtendrß un cuadro de mensaje diciendo: ½Este programa ha realizado una operaci≤n ilegal y se cerrarß╗. Este bit no evita que un programa compile el c≤digo fuente del programa y almacene las instrucciones de lenguaje ensamblador en memoria para ser ejecutadas.

Las direcciones virtuales tienen un ancho de 32 bits. El c≤digo y los datos (estßticos, de pila o localizados) de un programa tendrßn direcciones entre 0x00000000 y 0x7FFFFFFF. El propio W32 usa direcciones desde 0x80000000 a 0xFFFFFFFF y aquφ es donde se encontrarßn los puntos de entrada a las librerφas de enlace dinßmico (DLL) de W32.

La cantidad total de memoria libre estß determinada por la cantidad de memoria fφsica libre y el ßrea libre de disco duro disponible para intercambio de pßginas. Como es normal con la gesti≤n de memoria virtual, W325 emplea un algoritmo LRU (Least-Recently Used: usado-menos-reciente
mente) para determinar quΘ pßginas intercambiar a disco. Los bits de ½acceso╗ y ½sucio╗ le ayudan en esta tarea. Las pßginas de c≤digo no tienen que guardarse en disco porque las pßginas de c≤digo no son escribφbles, simplemente se puede recargar desde el archivo .EXE o la librerφa de enlace dinßmico (DLL).

A veces, se verß que se accede al disco cuando se mueve el rat≤n desde el ßrea cliente de un programa al ßrea cliente de otro programa. ┐Por quΘ ocurre esto? Windows tiene que enviar mcnsajes de movimiento de rat≤n a la segunda aplicaci≤n. Si el c≤digo del programa para procesar este mensaje no estß cargado actualmente en memoria, Windows tiene que recargarlo desde el archivo de disco. Si tiene varios programas Windows grandes cargados simultßneamente y no tiene mucha memoria, probablemente verß una actividad inusual en el disco duro a medida que se mueve de programa a programa, pues Windows estß recargando pßginas descargadas previamente.

A veces, los programas individuales se ralentizan (o se detienen completamente) cuando Windows estß realizando el intercambio (conmutaci≤n) de pßginas. Las pßginas de c≤digo se pueden compartir entre aplicaciones.

Esto es particularmente ·til para las librerφas de enlace dinßmico. Varios programas ejecutßn-dose a la vez pueden usar las mismas funciones Windows 95, sin que sea necesario que el mismo c≤digo se cargue en memoria varias veces. S≤lo es necesario una copia del c≤digo.

Excepto la granularidad de las pßginas de 4 KB, la memoria fφsica no se puede fragmentar porque la desfragmentaci≤n implica ·nicamente manipular las tablas de pßgina. Sin embargo, la memoria virtual de una aplicaci≤n se puede fragmentar si una aplicaci≤n localiza, relocaliza y libera varios bloques de memoria. El limite de 2 MB para el c≤digo y los datos de una aplicaci≤n normalmente es suficiente para evitar problemas. Es mucho mßs fßcil que un programa se quede antes sin memoria fφsica que llegue a encontrar un lφmite en la memoria virtual. Pero puede ocurrir, y si tiene un programa donde este problema es concebible, puede que desee considerar la memoria ½movible╗.

 

┐EL PROGRAMADOR PUEDE GESTIONAR LA MEMORIA VIRTUAL?
========================================================
Sφ, por supuesto. Para ello W32 proporciona varias funciones API:

Esta funci≤n se emplea para reservar y comprometer espacio de memoria virtual:

LPVOID VirtualAlloc (LPVOID lpAddress,DWORD cbSize,
                DWORD fdwAllocationType, DWORD fdwProtec);

Para liberar el espacio de memoria comprometido:

BOOL VirtualFree (LPVOID lpAddress,SWORD cbSize,DWORD fdwFreeType);


Bueno. Espero que este breve escrito sobre la gesti≤n de memoria virtual en W32 pueda entenderse y sea de utilidad.

Cualquier error u observaci≤n, por favor notifφquenme:
nuMIT_or@iname.com


Ir a: Descabezando archivos ejecutables portables
 


[ Entrada | Documentoz GenΘricoz | WKT TEAM Main Site ]
[ Todo el ECD | x Tipo de Protecci≤n | x Fecha de Publicaci≤n | x orden AlfabΘtico ]