modes instruction and assembly x86 addressing-mode

assembly - instruction - ¿Cuál es el significado de la instrucción x86 "call dword ptr ds:[00923030h]"?



x86 addressing modes (3)

¿Qué hace la siguiente instrucción de ensamblador x86?

call dword ptr ds:[00923030h]

Es una llamada indirecta, sospecho, pero ¿cómo calcula exactamente la dirección de la llamada?


[EDIT] actualizado

Cada vez que vea un operando de memoria que se parece a ds:0x00923030 , ese es un modo de direccionamiento relativo al segmento . La dirección real referida tp está en la dirección lineal 0x00923030 con respecto a la dirección base del registro del segmento ds .

La segmentación de memoria en la arquitectura x86 es algo confusa, y creo que Wikipedia hace un buen trabajo al explicarla.

Básicamente, x86 tiene una serie de registros de segmentos especiales: cs (segmento de código ), ds (segmento de datos ), es , fs , gs y ss (segmento de pila ). Cada acceso a la memoria está asociado con un cierto registro de segmento. Normalmente, no se especifica el registro de segmento, y dependiendo de cómo se accede a la memoria, se utiliza un registro de segmento predeterminado. Por ejemplo, el registro cs se usa para leer las instrucciones.

Cada registro de segmento tiene una cierta dirección base y un límite . La dirección base determina la dirección física a la que corresponde la dirección lineal 0x00000000, y el límite determina la dirección lineal máxima permitida para ese segmento. Por ejemplo, si la dirección base era 0x00040000 y el límite era 0x0000FFFF, entonces las únicas direcciones lineales válidas serían 0x00000000 a 0x0000FFFF, y las direcciones físicas correspondientes serían 0x00040000 a 0x0004FFFF.

Por lo tanto, la dirección física a la que reside la subrutina que se llama está dada por la dirección base almacenada en el registro del segmento ds , más 0x00923030. Pero aún no hemos terminado, la instrucción tiene la palabra ptr . Esto agrega un nivel adicional de direccionamiento indirecto, por lo que el objetivo real de la subrutina es la dirección almacenada en la ubicación ds:0x00923030 .

En la sintaxis de AT & T (aceptada por el ensamblador de GNU), la instrucción se escribiría de la siguiente manera:

lcall *ds:0x00923030

Para obtener todos los detalles sangrientos de lo que hace la instrucción, consulte el manual de referencia 80386 . Esta variante particular de la instrucción es "CALL r/m16" (llamada cerca de registro indirecto / memoria indirecta).


Este código de operación específico hace una llamada a través de la dirección virtual (32 bits aquí) que reside en la ubicación apuntada por la dirección lógica ds:[00923030h] .
Una dirección lógica está compuesta de dos componentes:

  1. Selector de segmento de 16 bits, ds en este caso, que básicamente es un índice en la tabla de descriptor (global / local) administrada por el sistema operativo. Tal selector también contiene información de derechos de acceso para el segmento dado que se verifica al acceder (nivel de privilegio actual, CPL )
  2. Una compensación de 32 bits
    La dirección final se calcula de la siguiente manera: dirección base extraída del selector + desplazamiento

Tenga en cuenta que el cálculo anterior denota una dirección lineal, no física (ver manuales de inteligencia volumen 3a , figura 2.2), que luego se traduce a través del mecanismo estándar para la paginación de 4KB, es decir, la dirección consta de un directorio índice a página, página tabla y un desplazamiento en la página seleccionada. Sin embargo, tenga en cuenta que todos los sistemas operativos de la corriente principal utilizan el llamado modelo de memoria plana, lo que significa que todos los selectores de segmentos apuntan a la dirección 0x00000000 con el límite establecido en 0xFFFFFFFF, que es la razón por la que puede realizar el reparto entre todos los segmentos. para (fácil) explotación de desbordamientos de búfer.

Es muy probable que la instrucción de ensamblador que haya dado sea una llamada a través de la Tabla de direcciones de importación (consulte este gran artículo para obtener más detalles) de un archivo ejecutable, es decir, es bastante improbable que se trate de una llamada de subrutina ordinal.
Los compiladores emiten este tipo de código porque la dirección virtual final de una función importada desde una dll externa no puede conocerse en general en el momento de la compilación (debido a la rebase de dlls). Al utilizar una construcción de llamada de este tipo, el cargador del sistema operativo puede insertar la dirección virtual correcta en el puntero de dirección por la dirección lógica y al compilador no le debe importar qué dirección de todas formas tenga la función final.


IIRC, toma el valor del registro DS (y lo desplaza a la izquierda 4 bits), agrega al valor inmediato dado, obtiene un valor dword de la ubicación de la memoria resultante, que se convierte en la dirección a llamar. (EDITAR: esto es cierto para el modo real de 16 bits, para el modo protegido ver las otras respuestas).