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

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

call dword ptr ds:[00923030h]

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


person Community    schedule 05.02.2009    source источник


Ответы (4)


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

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

person Adam Rosenfield    schedule 05.02.2009
comment
Не совсем так, я думаю, здесь присутствует косвенность. Таким образом, это должно быть: Физический адрес, по которому находится вызываемая подпрограмма, определяется значением базового адреса, хранящегося в сегментном регистре ds, плюс 0x00923030. - person Knut Arne Vedaa; 06.02.2009
comment
Селектор сегментов указывает не на физический адрес, как вы говорите, а на линейный адрес. Вместо этого физический адрес получается на последнем этапе, когда логический адрес уже преобразован в линейный адрес. - person newgre; 06.02.2009
comment
Ага, сегментированный -> линейный, затем виртуальный -> физический. - person Peter Cordes; 28.12.2019
comment
Кроме того, весь доступ к памяти в x86 относится к некоторому сегменту. ds: используется по умолчанию для режимов адресации данных, отличных от ebp или esp в качестве базовых регистров. Этот ответ также подразумевает, что call ds:1234 будет иметь меньше косвенных уровней, т. е. может выполнять EIP=1234 вместо загрузки нового значения EIP из адреса памяти ds:1234. Но это не имеет смысла; ds: в ds:1234 уже означает, что это операнд источника памяти (данных). - person Peter Cordes; 28.12.2019
comment
И нет, это call r/m32, а не 16. dслово — 32 бита, слово — 16. Большинство этих ошибок незначительны, и под ними скрывается несколько хороший ответ, но ответ на связанный дубликат довольно хорош. - person Peter Cordes; 28.12.2019

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

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

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

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

person newgre    schedule 06.02.2009

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

person VIII    schedule 28.12.2019

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

person Knut Arne Vedaa    schedule 05.02.2009
comment
это неправильно, смещение не добавляется к значению регистра ds. На самом деле это логический адрес. - person newgre; 06.02.2009
comment
Это не настоящий режим, потому что он использует размер операнда DWORD для нового адреса (и это недалеко). В реальном режиме это будет только указатель функции word ptr. - person Peter Cordes; 28.12.2019