Что означает инструкция x86 "call dword ptr ds: [00923030h]"? - PullRequest
10 голосов
/ 06 февраля 2009

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

call dword ptr ds:[00923030h]

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

Ответы [ 3 ]

17 голосов
/ 06 февраля 2009

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

Всякий раз, когда вы видите операнд памяти, похожий на 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" (вызов рядом с регистром косвенный / память косвенным).

10 голосов
/ 06 февраля 2009

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

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

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

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

1 голос
/ 06 февраля 2009

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

...