Подтвердить что ты не робот

В чем смысл инструкции x86 "call dword ptr ds: [00923030h]"?

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

call dword ptr ds:[00923030h]

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

4b9b3361

Ответ 1

[EDIT] Обновлено

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

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

В основном, 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" (косвенный косвенный косвенный косвенный косвенный косвенный космос).

Ответ 2

Этот конкретный код операции совершает вызов через виртуальный адрес (32 бит здесь), находящийся в месте, на которое указывает логический адрес ds:[00923030h].
Логический адрес состоит из двух компонентов:

  • Сегмент сегмента 16 бит, ds в этом случае, который в основном является индексом в таблице дескрипторов (глобальная/локальная), управляемой операционной системой. Такой селектор также содержит информацию о правах доступа для данного сегмента, которая проверяется при доступе (текущий уровень привилегий, CPL)
  • 32-разрядное смещение
    Окончательный адрес затем вычисляется следующим образом: базовый адрес выбирается из селектора + смещения

Обратите внимание, что приведенный выше расчет обозначает линейный адрес, а не физический (см. руководство intel том 3a, рисунок 2.2), который затем переводится через стандартный механизм для подкачки 4 Кбайт, т.е. адрес состоит из индекса в каталог страницы, таблицу страниц и смещение на выбранную страницу. Имейте в виду, что все операционные системы основного потока используют так называемый модуль памяти с плоской памятью, что означает, что все сегментные селектора указывают на адрес 0x00000000 с пределом, установленным на 0xFFFFFFFF, что является причиной того, что вы можете использовать между всеми сегментами и в конечном итоге привести к (легкой) эксплуатации переполнения буфера.

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

Ответ 3

IIRC, он принимает значение регистра DS (и сдвигает его влево на 4 бита), добавляет к тому, что указанное немедленное значение извлекает значение dword из результирующей ячейки памяти, которая становится адресом для вызова. (EDIT: это справедливо для 16-битного реального режима, для защищенного режима см. Другие ответы.)