Что означает инструкция x86 "call dword ptr ds:[00923030h]"?

Что делает следующая инструкция ассемблера x86?

call dword ptr ds:[00923030h]

Я подозреваю, что это косвенный вызов, но как именно он вычисляет адрес для вызова?

3 ответа

[РЕДАКТИРОВАТЬ] Обновлено

Всякий раз, когда вы видите операнд памяти, который выглядит как ds:0x00923030 это режим адресации, относящийся к сегментам. Фактический адрес, на который ссылается tp, находится по линейному адресу 0x00923030 относительно базового адреса ds сегментный регистр.

Сегментация памяти в архитектуре x86 несколько сбивает с толку, и я думаю, что Википедия хорошо объясняет это.

В основном, x86 имеет ряд специальных сегментных регистров: cs (сегмент кода), ds (сегмент данных), es, fs, gs, а также ss (стековый сегмент). Каждый доступ к памяти связан с определенным сегментным регистром. Обычно вы не указываете регистр сегмента, и в зависимости от того, как осуществляется доступ к памяти, используется регистр сегмента по умолчанию. Например, cs регистр используется для чтения инструкций.

Каждый сегментный регистр имеет определенный базовый адрес и ограничение. Базовый адрес определяет физический адрес, которому соответствует линейный адрес 0x00000000, а предел определяет максимально допустимый линейный адрес для этого сегмента. Например, если базовый адрес был 0x00040000, а предел был 0x0000FFFF, то единственными действительными линейными адресами были бы от 0x00000000 до 0x0000FFFF, а соответствующие физические адреса были бы от 0x00040000 до 0x0004FFFF.

Таким образом, физический адрес, по которому находится вызываемая подпрограмма, задается базовым адресом, хранящимся в ds сегментный регистр, плюс 0x00923030. Но мы еще не закончили - в инструкции есть слово ptr в этом. Это добавляет дополнительный уровень косвенности, поэтому фактической целью подпрограммы является адрес, сохраненный в местоположении ds:0x00923030,

В синтаксисе AT&T (принятом ассемблером GNU) инструкция будет записана следующим образом:

lcall *ds:0x00923030

Подробные сведения о том, что делает инструкция, см. В справочном руководстве 80386. Этот конкретный вариант инструкции "CALL r/m16" (вызов около регистра косвенный / память косвенная).

This specific opcode makes a call through the virtual address (32bit here) residing at the location pointed to by the logical address ds:[00923030h],
A logical address is made of two components:

  1. A 16 bit segment selector, ds in this case, which is basically an index into the (global / local) descriptor table managed by the operating system. Such a selector also holds access rights information for the given segment which is checked upon access (current privilege level, CPL)
  2. A 32bit offset
    The final address is then computed as follows: base address fetched from selector + offset

Please note that the above calculation denotes a linear address, not a physical one (see intel manuals volume 3a, figure 2.2), which is then translated via the standard mechanism for 4KB paging, ie the address consists of an index to page directory, page table and an offset into the selected page. Keep in mind though, that all main stream operating system use the so called flat memory modell, which means that all segment selectors point to address 0x00000000 with the limit set to 0xFFFFFFFF, which is the reason why you can cast between all segments and ultimately leads to (easy) exploitation of buffer overflows.

Указанная вами инструкция на ассемблере, скорее всего, будет вызовом из таблицы адресов импорта (более подробную информацию см. В этой замечательной статье) исполняемого файла, то есть весьма маловероятно, что это порядковый вызов подпрограммы.
Подобный код генерируется компиляторами, потому что окончательный виртуальный адрес импортированной функции из внешней библиотеки DLL вообще не может быть известен во время компиляции (из-за перебазирования библиотеки DLL). Используя такую ​​вызывающую конструкцию, загрузчик ОС может вставить правильный виртуальный адрес по указателю адреса по логическому адресу, и компилятору не нужно заботиться о том, какой адрес в конечном итоге имеет функция.

call dword ptr ds:[00923030h], имея в виду call что-то значение в указателе 00923939h в data segment

IIRC, он принимает значение регистра DS (и сдвигает его влево на 4 бита), добавляет к этому непосредственное заданное значение, выбирает значение dword из результирующей ячейки памяти, которая становится адресом для вызова. (РЕДАКТИРОВАТЬ: это верно для 16-битного реального режима, для защищенного режима см. Другие ответы.)

Другие вопросы по тегам