В формате команд фирмы intel имеется поле префикс что такое префикс

Обновлено: 04.07.2024

Программная модель включает восемь регистров общего назначения, шесть регистров сегментов, указатель команд , регистр системных флагов, регистры системных адресов, четыре регистра управления и шесть регистров отладки.

Хотя регистр ESP тоже относится к регистрам общего назначения, он содержит указатель на вершину стека и не используется для других целей.

Следует отметить, что регистры могут быть неравнозначны и при использовании определенных инструкций могут иметь специальное значение :

  • EAX - аккумулятор, операнд-источник или приемник результата (некоторые инструкции могут быть короче на один байт при использовании EAX);
  • EBX - указатель на данные в сегменте DS;
  • ECX - счетчик для цепочечных (например, MOVS) и циклических (с префиксом REP) инструкций;
  • EDX - адрес порта ввода-вывода для инструкций IN/INS, OUT/OUTS;
  • ESI - указатель на операнд-источник в сегменте DS для цепочечных инструкций;
  • EDI - указатель на операнд-приемник в сегменте ES для цепочечных инструкций;
  • EBP - указатель на данные в сегменте SS.

МП включает шесть непосредственно доступных 16-битных регистров сегментов. С каждым сегментным регистром ассоциирован программно-недоступный кэш дескриптора соответствующего сегмента, содержащий базовый адрес сегмента в линейном адресном пространстве, предел сегмента и атрибуты сегмента. Этот кэш заполняется при загрузке значения в сегментный регистр . В реальном режиме предел сегмента всегда 0FFFFh, атрибуты игнорируются, а базовый адрес вычисляется сдвигом значения селектора на 4 бита влево. В защищенном режиме кэш заполняется соответствующими значениями из дескрипторной таблицы .

Не все сегментные регистры равнозначны. Регистр CS хранит селектор сегмента кода. МП извлекает очередную инструкцию для исполнения, формируя логический адрес из селектора в CS и смещения в регистре EIP . Значение этого регистра нельзя изменить непосредственно, оно меняется в командах межсегментного перехода ( FAR JMP), межсегментного вызова ( FAR CALL ), при вызове обработчика прерывания ( INT ) и при возврате из далекой процедуры (RETF) или обработчика прерывания ( IRET ).

Регистр SS хранит селектор сегмента стека. Стек используется для передачи параметров подпрограммам и для сохранения адреса возврата при вызове подпрограммы или обработчика прерывания. Вершиной стека считается байт , логический адрес которого образуется из селектора в регистре SS и смещения в регистре ESP . Программа может непосредственно изменить значение SS, что дает ей возможность переключаться между несколькими стеками. Причем на время выполнения команды MOV SS,xxxx и одной команды, следующей за ней (обычно это MOV ESP ,xxxx), запрещаются маскируемые и блокируются немаскируемые прерывания .

Регистры DS, ES, FS и GS хранят селекторы сегментов данных. Если инструкция обращается к памяти, но содержит только смещение, то считается, что она обращается к данным в сегменте DS. Сегмент ES может использоваться без явного указания в цепочечных командах. Сегменты FS и GS используются при обращении к памяти только при явном использовании в инструкции префиксов этих сегментов.

Указатель команд ( EIP ) является 32-разрядным регистром. Он содержит смещение следующей команды, подлежащей выполнению. Относительный адрес отсчитывается от базового адреса сегмента исполняемой задачи. Указатель команд непосредственно недоступен программисту, но он изменяется явно командами управления потоком, прерываниями и исключениями (JMP, CALL , RET , IRET , команды условного перехода). Получить текущее значение EIP можно, если выполнить команду CALL , а затем прочитать слово на вершине стека.

Регистр системных флагов EFLAGS содержит группу флагов состояния , управления и системных флагов. Младшие 16 бит регистра представляют собой 16-разрядный регистр флагов и состояния МП 8086, называемый FLAGS, который наиболее полезен при исполнении программ для МП 8086 и Intel-286. Некоторые из флагов могут быть изменены специально предназначенными для этой цели инструкциями. Для изменения или проверки группы флагов можно воспользоваться следующими командами:

  • LAHF/SAHF - загрузка/сохранение младших 8 битов регистра флагов в регистре AH;
  • PUSHF/POPF - помещение/извлечение из стека младших 16 битов регистра флагов ;
  • PUSHFD/POPFD - помещение/извлечение из стека 32-битного регистра EFLAGS.

Регистры управления сегментированной памятью, известные также как регистры системных адресов, указывают на структуры данных, которые управляют механизмом сегментированной памяти. Они определены для ссылок на таблицы или сегменты , поддерживаемые моделью защиты МП.

МП имеет четыре 32-разрядных регистра управления CR0-CR4, в которых хранятся флаги состояния МП или глобальные флаги. Вместе с регистрами системных адресов эти регистры хранят информацию о состоянии МП, которая влияет на все задачи в системе. Системным программистам регистры управления доступны только через варианты команды MOV , которые позволяют их загружать или сохранять в регистрах общего назначения.

Шесть доступных регистров отладки (DR0-DR3, DR6, DR7, регистры DR4 и DR5 зарезервированы) расширяют возможности отладки. Они устанавливают точки останова по данным и позволяют устанавливатьзадавать точки останова по командам без модификации сегментов программ. Регистры DR0-DR3 предназначены для хранения четырех линейных адресов точек останова. Регистр DR6 отражает текущее состояние точек останова. Регистр DR7 задает условие для точек останова.

В МП Intel-386 и Intel-486 использовались также 2 регистра страничных проверок (TR6 и TR7), которые позднее были исключены из архитектуры IA-32 .

Формат команды микропроцессора IA-32

Инструкция микропроцессора может содержать следующие поля:

Префикс - необязательная часть инструкции, которая позволяет изменить некоторые особенности ее выполнения. В команде может быть использовано сразу несколько префиксов разного типа. Типы префиксов: командные префиксы (префиксы повторения) REP, REPE/REPZ, REPNE/REPNZ; префикс блокировки шины LOCK; префиксы размера; префиксы замены сегмента.

КОП - код операции .

Байт " Mod R/M" определяет режим адресации , а также иногда дополнительный код операции . Необходимость байта " Mod R/M" зависит от типа инструкции.

Байт SIB (Scale-Index-Base) определяет способ адресации при обращении к памяти в 32-битном режиме. Необходимость байта SIB зависит от режима адресации, задаваемого полем " Mod R/M".

Кроме того, инструкция может содержать непосредственный операнд и/или смещение операнда в сегменте данных.

Если инструкция микропроцессора требует операнды, то они могут задаваться следующими способами: непосредственно в коде инструкции (только операнд -источник); в одном из регистров; через порт ввода-вывода ; в памяти.

Для совместимости с 16-битными процессорами архитектура IA-32 использует одинаковые коды для инструкций, оперирующих как с 16-битными, так и 32-битными операндами. Новая архитектура предусматривает также новые возможности при указании адреса для операнда в памяти. Как процессор будет считать операнд или его адрес , зависит от эффективного размера операнда и эффективного размера адреса для данной команды. Эти значения определяются на основе режима работы, бита D дескриптора используемого сегмента и наличия в инструкции определенных префиксов.

Регистровый режим адресации определят операнд -источник или операнд -приемник в одном из регистров процессора или сопроцессора.

В некоторых случаях, (например, в инструкциях DIV и MUL) могут использоваться пары 32-битных регистров (например, EDX:EAX), образуя 64-битный операнд .

Адресация через порт ввода-вывода подразумевает получение операнда или сохранение операнда через пространство портов ввода-вывода. Адрес порта ввода-вывода либо непосредственно включается в код инструкции, либо берется из регистра DX.

Очень распространенный способ адресации операнда - адресация через память . Таким образом, может быть указан операнд -источник или операнд -приемник. Следует отметить, что процессор не позволяет одновременно задавать оба операнда через память (за исключением некоторых цепочечных команд).

Для получения операнда из памяти процессору необходимо знать селектор сегмента и смещение в сегменте. В некоторых командах селектор может быть указан непосредственно в коде инструкции. В других случаях процессор может явно или неявно использовать значение одного из сегментных регистров. Под неявным использованием сегментных регистров подразумевается то, что в зависимости от предназначения операнда процессор использует определенный сегментный регистр для обращения к памяти: CS -для выборки инструкций; SS - для работы со стеком или обращения к памяти через регистры ESP или EBP ; ES - для получения адреса операнда-приемника в цепочечных командах; DS - при всех остальных обращениях к памяти. Явное использование сегментных регистров возможно, если в код инструкции включается префикс смены сегмента. Указание префикса смены сегмента допустимо не для всех команд: нельзя менять сегмент для команд работы со стеком (всегда используется SS); для цепочечных команд можно менять сегмент только операнда-источника ( операнд -приемник всегда адресуется через ES).

Смещение в сегменте (эффективный или исполнительный адрес - EA ) может быть вычислено на основе значений регистров общего назначения и/или указанного в коде инструкции относительного смещения, при этом любой или даже несколько из указанных компонентов могут отсутствовать:

Такая схема позволяет в языках высокого уровня и на языке Ассемблера легко реализовать работу с массивами.

Сегодня я хочу рассказать вам о префиксах в системе команд Intel IA-32 в 32- и 64-битных вариантах (также именуемых как x86 и x86_64). Но для начала напомню вкратце общую структуру IA-32 инструкции:


  • Префиксы. Могут отсутствовать. Может присутствовать сразу несколько.
  • Опкод. Может состоять из одного, двух или трех байтов.
  • Mod_R/M байт. Используется для адресации операндов. Может отсутствовать в кодировке, если инструкция не имеет явных операндов.
  • SIB (Scale Index Base) байт. Второй байт, использующийся для адресации операндов в памяти. Может отсутствовать.
  • Байт смещения адреса (англ. displacement). 1, 2, 4 или ни одного байта.
  • Константа (англ. immediate). 1, 2, 4 или ни одного байта.

Однобайтные префиксы

Практически с самых первых процессоров Intel в системе команд IA-32 начали использоваться однобайтные префиксы. О них уже было написано на Хабре, по этой причине я про них рассказывать не буду.

Обязательные префиксы

С появлением расширения SSE часть однобайтных префиксов, а именно 0xf2 , 0xf3 , 0x66 в некоторых случаях стали иметь смысл части опкода. Появились так называемые обязательные префиксы (англ. mandatory prefixes). Примеры таких инструкций приведены ниже.

Кодировка Инструкция Обязательный префикс
0x0f 0x10 MOVUPS -
0xf2 0x0f 0x10 MOVSD 0xf2
0xf3 0x0f 0x10 MOVSS 0xf3
0x66 0x0f 0x10 MOVUPD 0x66

Не сложно заметить, что кодировки этих инструкций отличаются только префиксом. Опкод у них совпадает – 0x0f 0x10 . При этом семантика у этих инструкций различна. Например, MOVSD копирует из одного операнда в другой 64 бита, а MOVUPD – 128 бит.

Префикс REX

В определенный момент появилась необходимость в поддержке 64-битного адресного пространства и расширения числа адресуемых регистров. С этой задачей успешно справились разработчики AMD, добавив префикс, названный REX . Данный префикс также является однобайтными, и имеет вид 0x4* . Его биты используются для расширения уже существующих полей, кодируемых в Mod_R/M байте, а также ширины операнда. На рисунке приведен пример использования REX префикса для адресации регистров.


Стоит отметить несколько особенностей, связанных с использованием этого префикса. Кодировка 0x4* соответствует префиксу только в 64-битном режиме, во всех остальных режимах она соответствует вариантам инструкций INC/DEC . Интересным свойством данного префикса является то, что он должен быть расположен непосредственно перед байтом опкода, в противном случае он игнорируется. Если REX префикс используется вместе с инструкцией требующей присутствия другого обязательного префикса, он должен быть расположен между этим префиксом и байтом кода операции.

Префикс VEX

С введением расширения AVX в системе команд IA-32 появился новый префикс, названный VEX . Он уже не однобайтный. Он может состоять либо из двух, либо из трех байт в зависимости от первого байта префикса. 0xc4 и 0xc5 соответственно.


Поля R , X , B , W несут тот же смысл, что и соответствующие поля REX префикса. Поле pp предоставляет функциональность, эквивалентную обязательным SIMD префиксам (например, b01 = 0x66 ). А поле m-mmmm может соответствовать целым двум байтам опкода (например, 0b00011 = 0x0f 0x3a ). Поле L определяет длину вектора: 0 – 128 бит, 1 – 256 бит.

Использование VEX префикса предоставляет следующие преимущества:

Префикс EVEX

Не так давно Intel анонсировал появление нового расширения набора команд с названием AVX3 или AVX512 . С появлением этого расширения также появился и новый префикс, получивший название EVEX . Его описание можно найти в Intel Architecture Instruction Set Extensions Programming Reference.


Он представляет собой усовершенствованный вариант VEX префикса, имеет длину уже в 4 байта и начинается с байта 0x62 , который во всех режимах, кроме 64-битного соответствует инструкции BOUND , редко используемой в современных программах.

Приведу некоторые, на мой взгляд, интересные особенности EVEX префикса:

  • Два бита для длины вектора – LL` – необходимые для поддержки векторов размером 128, 256 и 512 бит.
  • Поддержка адресации новых 512-битных регистров ZMM8 — ZMM31 .
  • Поддержка регистров маски операндов (англ. opmask registers). Поле EVEX.aaa .
  • EVEX.mm – эквивалент поля VEX.m-mmmm , но занимает два бита вместо пяти.

Заключение

В заключение хочется отметить некоторые причины появления настолько сложной и, местами, не логичной системы команд. История развития системы команд Intel IA-32 начинается в 70-х годах прошлого столетия, когда ни о каких 64-битных режимах не было и речи. Кроме Intel существенный вклад в эволюцию IA-32 внесла компания AMD. Много усилий было потрачено на поддержание обратной совместимости между различными моделями процессоров. Множество интересных фактов, связанных с развитием архитектуры IA-32 можно найти в статье A. Fog’a.

Издано: 2003, BHV
Твердый переплет, 560 стр..

Структура команд Intel 80x86

(рабочий фрагмент главы из второго издания "Фундаментальных основ хакерства")

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

Программируя непосредственно в машинных кодах, можно создавать чрезвычайно хитрый код, умело сопротивляющийся всяким попыткам его отладить или "прожевать" дизассемблером. Соответственно и изучить такой код будет можно лишь разобравшись в нем "вручную". Знаний одних лишь опкодов машинных команд, подчеркнутых из технической документации от разработчиков процессоров, может оказаться недостаточно. Документация не раскрывает всех тонкостей интерпретации команд микропроцессором и особенностей его поведения.

Наконец, владение машинными командами как нельзя кстати пригодится для написания собственных дизассемблеров, отладчиков и другого хакерского инструментария.

Структура машинных инструкций микропроцессов серии Intel 80x86 в общем случае выглядит как показано на рис. 1.5. Давайте рассмотрим его повнимательнее.

Рис. 1.5 Структура машинных инструкций микропроцессоров серии Intel 80x86

Префиксы

В начале каждой команды может располагаться до четырех префиксов (от лат. pre – впереди и fix – прикреплений) - особых однобайтовых кодов, уточняющих поведение команды, - а может не быть ни одного - тогда команда ведет себя "по умолчанию".

  1. 1. Префиксы блокировки и повторения:
    1. 0xF0 LOCK
    2. 0хF2 REPNZ (только для строковых инструкций).
    3. 0xF3 REP (только для строковых инструкций).
    1. 0х2E CS:
    2. 0х36 SS:
    3. 0х3E DS:
    4. 0х26 ES:
    5. 0х64 FS:
    6. 0х65 GS:
    1. 0х66
    1. 0х67

    Если используется более одного префикса из той же самой группы, то действие команды не определено и по-своему реализовано на разных типах процессоров.

    Префиксы блокировки и повторения

    Префикс "LOCK" указывает процессору на необходимость монопольного захвата шины на время выполнения следующей машинной команды. Это позволяет синхронизовать обращения к памяти, предотвращая одновременный доступ нескольких процессоров (или устройств - контроллера DMA например) к одной ячейке памяти.

    Замечание: Операционная система Windows 2000 содержит ошибку - при встрече с некорректным LOCK функция GetExceptionCode возвращает не STATUS_ILLEGAL_INSTRUCTION (код C000001D) - как того следовало бы ожидать, а никому не известный код C000001E, определение которого в заголовочных файлах SDK вообще отсутствует! Поэтому, разработчикам защит следует "вручную" обрабатывать такую ситуацию.

    Дополнение: Как вы думаете, что произойдет если указать несколько префиксов LOCK перед одной командой? Документация от Intel об этой тонкости умалчивает, ссылаясь на неопределенное поведение, однако, тривиальный эксперимент убеждает, что несколько префиксов LOCK работают точно так, как один.

    Листинг 44 Использование префикса LOCK для вызова структурного исключения

    Результат дизассемблирования файла, сгенерированного компилятором Microsoft Visual C++ 6.0 в режиме максимальной оптимизации будет выглядеть так (ниже приведен лишь ключевой фрагмент):

    Листинг 45 Фрагмент дизассемблированной защиты, основанной на префиксе LOOK

    Префиксы повторений

    Два префикса повторений REP (синонимы - REPE, REPZ) и REPNE (синоним - REPNZ) повторяют следующую за ними строковую инструкцию ECX [CX] раз, каждый раз уменьшая ECX [CX] на единицу, но не выставляя флаг нуля (ZF) при обращении ECX [CX] в нуль, - вернее, префиксы повторения вообще не воздействуют на флаги.

    Префиксы повторений могут функционировать лишь со следующими командами: INS, MOVS, OUTS, LOADS, STOS, CMPS и SCAS, причем, проверка условия продолжения цикла осуществляется только с двумя командами – CMPS и SCAS. Т. е. "REPNE LOADS" работает аналогично "REPE LOADS" и независимо от рода считываемых данных всегда повторяется ECX [CX] раз.

    Когда же префиксы REPE или REPNE используется не со строковыми инструкциями, они выполняют следующую за ней команду всего лишь раз, не уменьшая при этом содержимого регистра ECX [CX]! Так, например, на первый взгляд следующий код "REP SHL EAX, CL" должен сдвинуть содержимое EAX на бит влево, однако же на самом деле сдвиг равен CL бит! бит влево, однако же на самом деле сдвиг равен CL бит!

    Префиксы переопределения сегмента

    Префиксы переопределения сегмента указывают какой именно сегментной регистр следует использовать для обращения к ячейке памяти. Префиксы переопределения сегмента применимы только к командам явно обращающимся к памяти, - т. е. имеющими адресное поле mod, содержимое которого не равно "11", а в остальных случаях они игнорируются. Так, конструкции "DS:REP" и "CS:PUSH AX" означают то же самое, что и "REP" и "PUSH EAX", хотя начинающие хакеры могут предположить, что префикс "DS" заставляет инструкцию REP искать адрес возврата по указателю "DS:ESP", а "PUSH", соответственно, заталкивать EAX по адресу CS:ESP-4. Такой трюк, действительно, встречается в некоторых защитных механизмах, создатели которых стремились сбить взломщиков с толку.

    Другой примем - использование нескольких префиксов с одной командной, например, "DS:FS:FG:CS:MOV AX,[100]". Микропроцессоры Intel и AMD используют для адресации ячейки последний префикс в цепочке (в данном случае CS), однако их разработчики этого не гарантируют (официально, как сказано выше, использование двух и более префиксов одной и той же категории запрещено). Вполне возможно, что следующие модели процессоров будут при встрече такой конструкции выбрасывать исключение или обрабатывать ее по-иному, поэтому, крайне не рекомендуется использовать этот трюк в собственных защитах.

    Замечание: дизассемблер IDA Pro вплоть до версии 4.17 включительно не "переваривает" множественные префиксы и вряд ли начнет "переваривать" их в последующих версиях.

    В частности, "CS:DS:ES:MOV [EAX], 0x666" IDA Pro дизассемблирует так:

    Листинг 46 Использование множественных префиксов сбивает дизассемблер с толку

    Вообще-то, это не создает никаких проблем, ибо в "высшем смысле" IDA Pro поступает правильно, т.к. действительно процессор "съедает" только последний префикс в цепочке, а остальные - игнорирует. Просто, не надо бояться, увидев в коде странные "DB", - правда убедитесь, что это именно префиксы, а не что-то другое!

    Префикс переопределения размера операндов

    Префикс переопределения размера операндов работает как триггер - если он встречается в 32-разрядном коде, то процессор интерпретирует операнды следующий за ним инструкции как 16-разнядные и, соответственно, наоборот.

    Дело в том, что схема адресации, принятая в микропроцессорах серии Intel 80x86 не позволяет отличать 16- и 32- разрядные регистры: EAX кодируется точно как и его "младший брат" - AX. Не стоит кидать камни в разработчиков и обвинять Intel в "кривизне". Во-первых, такая ситуация вызвана требованием обратной совместимости, а во-вторых, в адресных полях битам и так тесно, как селедкам в бочке и ни одной свободной комбинации нулей и единичек для кодирования разрядности операндов там нет. К тому же, 386+ микропроцессоры оптимизированные именно на работу с 32-разрядными данными и 32-разрядным кодом: 16-разрядные операнды в 32-разрядном режиме или 16-разрядный режим сегодня - почтенное достояние старины. Поэтому, использование префикса для выбора разрядности операндов - вполне оправданно.

    Префикс переопределения размера работает и с командами, имеющие неявные операнды. Рассмотрим, как работает конструкция "66:RETN". Казалось бы, коль инструкция RETN не имеет операндов, то префикс 66 можно игнорировать. На самом деле RETN работает с неявным операндом и, судя, по распечатке микрокода работает она так:

    Листинг 47 Псевдокод, раскрывающий алгоритм работы машинной команды RETN

    Ага, говорим мы голос Тиггера вдруг вышедшего на свет из чащи чертополоха, - если это не ошибка разработчиков процессора, то классная фишка! Правда, в среде 32-разрядного кода она уже не такая и классная, какой была под MS-DOS, но все по порядку.

    В реальном и 16-разрядном режиме указатель команд обрезается до 16 бит, поэтому на первый взгляд 66:RET сработает корректно. Корректно-то, оно корректно, да не совсем - стек ведь оказывается несбалансированным! Из него вместо одного слова RET снимает целых два! Так нетрудно получить и исключение 0xC - исчерпание стека. Если это сделать умышленно, поручив обработку исключения нечто полезное, то такое приложение не удаться отладить ни Soft-Ice, ни даже эмулирующим отладчиком cup386! Остальные отладчики так же либо виснут, либо совершают очень далекий переход по EIP и, попав в "космос", так же виснут, либо снимают одно слово и не вызывают ни исключения ни его обработчика, а если этот обработчик расшифровывает некоторые части программы, то под отладчиком они остаются зашифрованными и все вновь виснет. В общем труба

    В 32-разрядном режиме префикс 66h приводит к переходу по адресу (word ptr SS:[ESP]), со снятием одного слова из стека вместо двух. А в Windows NT первые 64 килобайта адресного пространства закрыты для обращения и при попытке исполнения первой же команды в этом регионе тут же вылетит знаменитое "access violation". В принципе, его можно перехватить и поручить обработчику нечто полезное, но - один нюанс - в Windows 9x закрыты лишь первые 4 Кб адресного пространства, а в остальные можно читать, писать и даже исполнять в них свой код (правда, Microsoft категорически не приветствует это делать), поэтому, необходимо удостовериться, что биты 815 адреса возврата равны нулю. Это ограничивает свободу разработчика защитного механизма, кроме того, слишком заметно, - потому, практически никем не используется.

    Рассмотрим следующий пример:

    Листинг 48 Пример защиты, основанный на некорректной обработке инструкции возврата

    Результат его прогона отладчиком "SED", запущенным под управлением Windows NT должен выглядеть приблизительно так:

    Если префикс переопределения размера стоит перед инструкцией, не имеющей операндов, то он игнорируется. Некоторые дизассемблеры, в том числе и IDA Pro не дизассемблируют такие префиксы, оставляя их в виде "сырого" дампа. Например:"0x66:CLI" будет дизассемблировано так:

    Это не создает серьезных препятствий для анализа, т.к. префикс переопределения размера операндов, в данном случае действительно лишен всякого смысла, но ведь анализирующему требуется еще убедиться, что это именной игнорирующийся префикс переопределения размера, а не нечто иное.

    Префикс переопределения размеров адреса

    Префикс переопределения размеров адреса очень похож на префикс переопределения размеров операнда, за тем исключением, что указывает на длину адресного поля, а не операнда.

    Например, если команду 32-разрядного режима "MOV [0x00000666], AX" записать как "DB 67;MOV [0x0666], AX" ее адресное поле сократится с 4-х до 2-х байт, и, учитывая расход одного байта на префикс, мы выиграем один байт. Правда, значительно проиграем в скорости, ибо код с префиксом исполняется по крайней мере на один такт медленнее - так стоит ли игра свеч?

    Другое дело - 16-разрядный режим. Если войти в protect mode, увеличить лимиты размера сегментов и, "забыв" восстановить их, вернуться обратно в real-mode, мы сможем адресовать все 4 гигабайта оперативной памяти любым из сегментных регистров! Правда, это, так называемый, "unreal mode" особой популярности не сыскал, ибо требовал девственно - чистой MS-DOS без EMS-драйверов, скрытно переводящий процессор в защищенный режим, и запускающих DOS на виртуальной машине под строгим надзором драйвера. Сами же EMS-драйвера требовались многим программам и отказ от их использования был в большинстве случаев невозможен.

    Ограничения количества префиксов

    В документации Intel встречается упоминание об ограничении количества префиксов на команду - официально их должно быть не более четырех. Что ж, это стыкуется с другим ограничением - не более одного префикса из каждой группы. А групп всего четыре - стало быть и префиксов не должно быть более четырех. На самом же деле, как мы уже могли убедиться, использование двух и более префиксов из одной и той же группы вполне допустимо (хотя и не рекомендуется для критически ответственных программ), но если ограничение на это "больше" или количество префиксов может достигать и миллиона?

    Ограничение, действительно есть. Поскольку, размер самой длинной команды со всеми четырьмя префиксами равен 16 байтам, то декодер процессора и "заглатывает" код кускам по 16-байт. Если же команда до того "разжиреет" за счет префиксов, что не сместиться ему в "рот", процессор "подавится" и сгенерирует общее исключение защиты.

    Разумеется, это обстоятельство можно использовать для преднамеренного вызова обработчика исключения.

    Опкод

    О поле "опкод" официальная документация не сообщает практически ничего - говориться лишь что оно занимает от одного до двух байт, а при необходимости "оттяпывает" и три бита поля "Reg/Opcode". И все? Не густо. И это при том, что коды машинных инструкций имеют вполне определенную, регулярную структуру, пускай со сложным сводом правил и исключений но все же Хотя бы из чистого любопытства можно провести сравнительный анализ и выявить общие закономерности.

    Ну, вот например, взгляните на разновидности инструкции ADD (имеются ввиду разновидности опкода, а не полей адресации):

    СТРУКТУРА КОМАНД INTEL 80x86

    Победа в борьбе, как правило, достается тому, кто лучше знает противника. Успешно защититься от хакера сможет лишь тот, кто знает не меньше хакера. Каждый уважающий себя хакер должен понимать, как строятся команды, чтобы с легкостью манипулировать им, занимаясь дизассемблированием. Следовательно и тот, кто хочет защитить свою программу от дизассемблирования, не обойдется без понимания того, как процессор интерпретирует команды.

    Формат инструкции архитектуры Intel приведен на рис.1. Это схематичное представление. Пока без размерностей и пояснений.

    Рис.1. Формат команд процессора х86 фирмы Intel

    Кроме поля кода операции все остальные поля являются необязательными, т.е. в одних командах могут присутствовать, а в других нет.

    Префикс

    На этом же основан один очень любопытный прием противодействия отладчикам, в том числе и знаменитому отладчику-эмулятору Cup386. Рассмотрим, как работает конструкция 0x66:RETN. Казалось бы, раз команда RETN не имеет операндов, то префикс 0x66 можно просто игнорировать. Но, на самом деле, все не так просто. RETN работает с неявным операндом-регистром ip/eip. Именно его и изменяет префикс. Разумеется, в реальном и 16-разрядном режиме указатель команд всегда обрезается до 16 бит, и поэтому, на первый взгляд, возврат сработает корректно. Но стек-то окажется несбалансированным! Из него вместе одного слова взяли целых два! Так нетрудно получить и исключение 0Ch - исчерпание стека.

    Любопытно, какой простой, но какой надежный прием. Впрочем, следует признать, что перехват INT 0Ch под операционной системой Windows бесполезен, и, не смотря на все ухищрения, приложение, породившие такое исключение, будет безжалостно закрыто. Однако, в реальном режиме это работает превосходно.

    Одно плохо - все эти приемы не работают под Windows и другими операционными системами. Это вызвано тем, что обработка исключения типа "Общее нарушение защиты" всецело лежит на ядре операционной системы, что не позволяет приложениям распоряжаться им по своему усмотрению. Забавно, но в режиме эмуляции MS-DOS некоторые EMS-драйверы ведут себя в этом случае совершенно непредсказуемо. Часто при этом они не генерируют ни исключения 0Сh, ни 0Dh. Это следует учитывать при разработке защит, основанных на приведенных выше приемах.

    Обратим внимание так же и на последовательности типа 0x66 0x66 [ххх]. Хотя фирма Intel не гарантирует корректную работу своих процессоров в такой ситуации, но фактически все они правильно интерпретируют такую ситуацию. Иное дело некоторые отладчики и дизассемблеры, которые спотыкаются и начинают некорректно вести себя.

    Есть еще один интересный момент связанный с работой декодера микропроцессора.

    Декодер за один раз считывает только 16 байт и, если команда "не уместится", то он просто не сможет считать "продолжение" и сгенерирует исключение "Общее нарушение защиты". Однако, иначе ведут себя эмуляторы, которые корректно обрабатывают "длинные" инструкции.

    Впрочем, все это очень процессорно-зависимо. Никак не гарантируется сохранение и поддержание этой особенности в будущих моделях, и поэтому злоупотреблять этим не стоит, иначе ваша защита откажется работать.

    Префиксы переопределения сегмента могут встречаться перед любой командой, в том числе и не обращающейся к памяти, например, CS:NOP вполне успешно выполнится. А вот некоторые дизассемблеры сбиться могут. К счастью, IDA Pro к ним не относится. Самое интересное, что комбинация
    DS: FS: FG: CS: MOV AХ,[100]
    работает вполне нормально (хотя это и не гарантируется фирмой Intel). При этом последний префикс в цепочке перекрывает все остальные. Некоторые отладчики, наоборот, ориентируются на первый префикс в цепочке, что дает неверный результат. Этот пример хорош тем, что великолепно выполняется под Windows и другими операционными системами. К сожалению, на декодирование каждого префикса тратится один такт, и все это может медленно работать.

    Код операции

    Само поле кода операции занимает восемь бит и чаще всего имеет следующий формат (рис. 2):

    Рис. 2. Формат поля "Код операции"

    Поле размера указывает на размер операндов. Это поле равно 0, если операнды имеют размер в один байт. Это поле равно 1, если операнды имеют размер в слово (двойное слово в 32-битном режиме или в 16-битном режиме с префиксом 0x66).

    Поле направления обозначает, какой из операндов будет приемником. Если направление равно 0, то приемник - правый операнд, если же направление равно 1, то приемник - левый операнд. На примере инструкции mov bx,dx видно, как можно поменять местами операнды, изменив всего один бит:

    Однако, давайте задумаемся, как поле направления будет вести себя, когда один из операндов непосредственное значение? Разумеется, что оно не может быть приемником и независимо от содержимого этого бита будет только источником. Инженеры Intel учли такую ситуацию и нашли оригинальное применение, часто экономящее в лучшем случае целых три байта. Рассмотрим ситуацию, когда операнду размером слово или двойное слово присваивается непосредственное значение по модулю меньшее 0100h. Ясно, что значащим является только младший байт, а стоящие слева нули по правилам математики можно отбросить. Но попробуйте объяснить это процессору! Потребуется пожертвовать хотя бы одним битом, чтобы указать ему на такую ситуацию. Вот для этого и используется бит направления. Рассмотрим следующую команду:

    Если теперь флаг направления установить в единицу, то произойдет следующие:

    Таким образом, мы экономим один байт в 16-разрядном режиме и целых три - в 32-разрядом. Этот факт следует учитывать при написании самомодифицирующегося кода. Большинство ассемблеров генерируют второй (оптимизированный) вариант, и длина команды оказывается меньше ожидаемой. На этом, кстати, основан очень любопытный прием против отладчиков. Посмотрите на следующий пример:

    После выполнения инструкция в строке 0100h приобретет следующий вид:

    То есть, текущая команда станет на байт короче! И "отрезанный" ноль теперь стал частью другой команды! Но при выполнении на "живом" процессоре такое не произойдет, т.к. следующие значение ip вычисляется еще до выполнения команды на стадии ее декодирования.

    Совсем другое дело отладчики, и особенно отладчики-эмуляторы, которые часто вычисляют значение ip после выполнения команды (это легче запрограммировать). В результате чего наступает крах. Маленькая тонкость - до или после оказалась роковой, и вот вам в подтверждение дамп экрана:

    Он изменит 6-ю строку на XOR SP,SP. Это "завесит" многие отладчики, и, кроме того, не позволит дизассемблерам отслеживать локальные переменные адресуемые через SP. Хотя IDA Pro и позволяет скорректировать стек вручную, для этого надо сначала понять, что SP обнулился. В приведенном примере это очевидно (но в глаза, кстати, не бросается), а если это произойдет в многопоточной системе? Тогда изменение кода очень трудно будет отследить, особенно в листинге дизассемблера. Однако, нужно помнить, что самомодифицирующийся код все же уходит в историю. Сегодня он встречается все реже и реже.

    Кстати, IDA Pro вообще отказывается анализировать весь последующий код. Как это можно использовать? Да очень просто - если эмулировать еще два сегментных регистра в обработчике INT 06h, то очень трудно это будет как отлаживать, так и дизассемблировать программу. Однако, это опять-таки не работает под Win32!

    Управляющие/отладочные регистры кодируются нижеследующим образом:

    Заметим, что коды операций mov, манипулирующих этими регистрами, различны, поэтому-то и возникает кажущееся совпадение имен. С управляющими регистрами связана одна любопытная мелочь. Регистр CR1, как известно большинству, в настоящее время зарезервирован и не используется. Во всяком случае, так написано в русскоязычной документации. На самом деле регистр CR1 просто не существует! И любая попытка обращения к нему вызывает генерацию исключение INT 06h. Например, сир386 в режиме эмуляции процессора этого не учитывает и неверно исполняет программу. А все дизассемблеры, за исключением IDA Pro, неправильно дизассемблируют этот несуществующий регистр:

    Все эти команды на самом деле не существуют и приводят к вызову прерывания INT 06h. He так очевидно, правда? И еще менее очевидно обращение к регистрам DR4-DR5. При обращении к ним исключения не генерируется.

    Между прочим, IDA Pro 3.84 дезассемблирует не все регистры. Зато великолепно их ассемблирует (кстати, ассемблер этот был добавлен другим разработчиком).

    Пользуясь случаем, акцентируем внимание на сложностях, которые подстерегают при написании собственного ассемблера (дизассемблера). Документация Intel местами все же недостаточно ясна (как в приведенном примере), и неаккуратность в обращении с ней приводит к ошибкам, которыми может воспользоваться разработчик защиты против хакеров.

    Теперь перейдем к описанию режимов адресации микропроцессоров Intel. Тема очень интересная и познавательная не только для оптимизации кода, но и для борьбы с отладчиками.

    Первым ключевым элементом является байт modR/M.

    Рис. 3. Формат байта modR/M

    Если mod содержит 11b, то два следующих поля будут представлять собой регистры. (Это так называемая регистровая адресация). Например:

    Как отмечалось выше, по байту modR/M нельзя точно установить регистры. В зависимости от кода операции и префиксов размера операндов, результат может коренным образом меняться.

    Биты 3-5 могут вместо определения регистра уточнять код операции (в случаи, если один из операндов представлен непосредственным значением). Младшие три бита всегда либо регистр, либо способ адресации, что зависит от значения mod. A вот биты 3-5 никак не зависят от выбранного режима адресации и задают всегда либо регистр, либо непосредственный операнд.

    Формат поля R/M, строго говоря, не документирован, однако достаточно очевиден, что позволяет избежать утомительного запоминания совершенно нелогичной на первый взгляд таблицы адресаций (таблица 3).

    Рис. 4. Формат поля R/M.

    Возможно, кому-то эта схема покажется витиеватой и трудной для запоминания, но зубрить все режимы без малейшего понятия механизма их взаимодействия еще труднее, кроме того, нет никакого способа себя проверить и проконтролировать ошибки.

    Действительно, в поле R/M все три бита тесно взаимосвязаны, в отличии от поля mod, которое задает длину следующего элемента в байтах.

    Рис. 5. Формат команды в зависимости от поля mod.

    Разумеется, не может быть смещения Offset 12, (т.к. процессор не оперирует с полуторными словами), а комбинация 11 указывает на регистровую адресацию.

    Все вышесказанное проиллюстрировано в приведенной ниже таблице 3. Обратим внимание на любопытный момент - адресация типа [ВР] отсутствует. Ее ближайшим эквивалентом является [ВР+0]. Отсюда следует, что для экономии следует избегать непосредственного использования ВР в качестве индексного регистра. ВР может быть только базой. И mov ax,[bр] хотя и воспринимается любым ассемблером, но ассемблируется в mov ах,[bр+0], что на байт длиннее.

    Исследовав приведенную ниже таблицу 3, можно прийти к выводу, что адресация в процессоре 8086 была достаточно неудобной. Сильнее всего сказывалось то ограничение, что в качестве индекса могли выступать только три регистра (ВХ, SI, DI), когда гораздо чаще требовалось использовать для этого СХ (например, в цикле) или (как возвращаемое функцией значение).

    Поэтому, начиная с процессора 80386 (для 32-разрядного режима), концепция адресаций была пересмотрена. Поле R/M стало всегда выражать регистр независимо от способа его использования, чем стало управлять поле mod, задающие, кроме регистровой, три вида адресации:

    Видно, что поле mod по-прежнему выражает длину следующего поля - смещения, разве что с учетом 32-битного режима, где все слова расширяются до 32 бит.

    Напомним, что с помощью префикса 0x67 можно и в 16-битном режиме использовать 32-битный режимы адресации, и наоборот. Однако, при этом мы сталкиваемся с интересным моментом - разрядность индексных регистров остается 32-битной и в 16-битном режиме!

    В реальном режиме, где нет понятия границ сегментов, это действительно будет работать так, как выглядит, и мы сможем адресовать первые 4 мегабайта памяти (32 бита), что позволит преодолеть печально известное ограничение размера сегмента 8086 процессоров в 64К. Но такие приложения окажутся нежизнеспособными в защищенном или V86 режиме. Попытка вылезти за границу 64К сегмента вызовет исключение 0Dh, что приведет к автоматическому закрытию приложения, скажем, под управлением Windows. Аналогично поступают и отладчики (в том числе и многие эмуляторы, включая Cup386).

    Сегодня актуальность этого приема, конечно, значительно снизилась, поскольку "голый DOS" практически уже не встречается, а режим его эмуляции Windows крайне неудобен для пользователей.

    Вот она! Но что представляет собой код 0BAh? Попробуем определить это по трем младшим битам. Они принадлежат регистру DL(DX). А 0B4h 09h - это * AH,9. Теперь нетрудно догадаться, что оригинальный код выглядел как:

    И это при том, что не требуется помнить код команды MOV! (Хотя это очень распространенная команда и запомнить ее код все же не помешает).

    Вызов 21-го прерывания 0CDh 21h легко отыскать, если запомнить его символьное представление '=!' в правом окне дампа. Как нетрудно видеть, следующий вызов INT 21h лежит чуть правее по адресу 0Ch. При этом DX указывает на 0156h. Это соответствует смещению 056h в файле. Наверняка эта функция читает пароль. Что ж, уже теплее. Остается выяснить, кто и как к нему обращается. Ждать придется недолго.

    При разборе байта 0Eh не забудьте, что адресации [ВР] не существует в природе. Вместо этого мы получим [offset16]. На размер регистра и приемник результата указывают два младших бита байта 08Ah. Они равны 10b. Следовательно, мы имеем дело с регистром CL, в который записывается содержимое ячейки [0156h].

    Все, знакомые с ассемблером, усмотрят в этом действии загрузку длины пароля (первый байт строки) в счетчик. Неплохо для начала? Мы уже дизассемблировали часть файла и при этом нам не потребовалось знание ни одного кода операции, за исключением, быть может, 0CDh, соответствующего команде INT.

    Вряд ли мы скажем, о чем говорит код 087h. (Впрочем, обращая внимание на его близость к операции NOP, являющейся псевдонимом XCHG AХ,AХ, можно догадаться, что 087h - это код операции XCHG). Обратим внимание на связанный с ним байт 0F2h:

    Эта команда заносит в SI смещение пароля, содержащееся в DX. Такой вывод следует исключительно из смыслового значения регистров (код команды игнорируется). К сожалению, этого нельзя сказать о следующем байте - 0ACh. Это код операции LODSB, и его надо просто запомнить.

    0x02 - код операции ADD, а следующий за ним байт - код AH,AL.

    0хЕ2 - код операции LOOP, а следующий за ним байт - знаковое относительное смещение перехода.

    Чтобы превратить его в знаковое целое, необходимо дополнить его до нуля, (операция NEG, которую большинство калькуляторов не поддерживают). Тот же самый результат мы получим, если отнимем от 0100h указанное значение (в том случае, если разговор идет о байте). В нашем примере это равно пяти. Отсчитаем пять байт влево от начала следующей команды. Если все сделать правильно, то вычисленный переход должен указывать на байт 0ACh (команда LODSB), впрочем, последнее было ясно и без вычислений, ибо других вариантов, по-видимому, не существует.

    Почему? Да просто данная процедура подсчета контрольной суммы (или точнее хеш-суммы) очень типична. Впрочем, не стоит всегда полагаться на свою интуицию и "угадывать" код, хотя это все же сильно ускоряет анализ.

    С другой стороны, хакер без интуиции - это не хакер. Давайте применим нашу интуицию, чтобы "вычислить", что представляет собой код следующей команды. Вспомним, что 0B4h (10110100b) - это MOV AН,imm8.

    0BEh очень близко к этому значению, следовательно, это операция MOV. Осталось определить регистр-приемник. Рассмотрим обе команды в двоичном виде:

    Как уже говорилось выше, младшие три бита - это код регистра. Однако, его невозможно однозначно определить без утончения размера операнда. Обратим внимание на третий (считая от нуля) бит. Он равен нулю для и единице в нашем случае. Рискнем предположить, что это и есть бит размера операнда, хотя этого явно и не уточняет Intel, но вытекает из самой архитектуры команд и устройства декодера микропроцессора.

    Обратим внимание, что это, строго говоря, частный случай, и все могло оказаться иначе. Так, например, четвертый справа бит по аналогии должен быть флагом направления или знакового расширения, но увы - таковым в данном случае не является. Четыре левые бита - это код операции mov reg, imm. Запомнить его легко - это "13" в восьмеричном представлении.

    Итак, 0BEh 0ЗВh 001h - это MOV SI, 013Bh. Скорее всего, 01ЗВh - это смещение, и за этой командой последует расшифровщик очередного фрагмента кода. А может быть и нет - это действительно смелое предположение. Однако, байты 0З0h 024h это подтверждают. Хакеры обычно так часто сталкиваются с функций хоr, что чисто механически запоминают значение ее кода.

    Не трудно будет установить, что эта последовательность дизассемблируется как XOR [SI],AН. Следующий байт 046h уже нетрудно "угадать" - INC SI. Кстати, посмотрим, что же интересного в этом коде:


    Третий бит равен нулю! Выходит команда должна выглядеть как INC AН! (Что кстати, выглядит непротиворечиво смысле дешифровщика). Однако, все же это INC SI. Почему мы решили, что третий бит - флаг размера? Ведь Intel этого никак не гарантировала! А команда 'INC byte' вообще выражается через дополнительный код, что на байт длиннее.

    Выходит, что как ни полезно знать архитектуру инструкций, все же таблицу кодов команд хотя бы местами надо просто выучить. Иначе можно впасть в глубокое заблуждение и совершить грубые ошибки. Хотя с другой стороны, знание архитектуры порой очень и очень помогает.

    Префикс VEX (от «векторных расширений») и VEX схемы кодирования является содержащим расширение для x86 и x86-64 набора инструкций архитектуры для микропроцессоров от Intel , AMD и других.

    Схема кодирования VEX позволяет определять новые инструкции и расширять или изменять ранее существующие коды инструкций . Это служит следующим целям:

    Префикс VEX заменяет наиболее часто используемые байты префикса команд и управляющие коды . Во многих случаях количество байтов префикса и управляющих байтов, которые заменяются, такое же, как количество байтов в префиксе VEX, так что общая длина инструкции, закодированной в VEX, такая же, как длина унаследованного кода инструкции. . В других случаях версия в кодировке VEX длиннее или короче, чем унаследованный код. В 32-битном режиме инструкции, закодированные в VEX, могут обращаться только к первым 8 регистрам YMM / XMM; кодировки для других регистров будут интерпретироваться как устаревшие инструкции LDS и LES, которые не поддерживаются в 64-битном режиме.

    Двухбайтовый префикс VEX содержит следующие компоненты:

    • Бит R̅ похож на бит префикса REX.R, используемый в расширении набора команд x86-64 .
    • Четыре бита с именем v̅, определяющие второй операнд исходного регистра.
    • Бит с именем L, определяющий длину вектора 256 бит.
    • Два бита с именем p для замены префиксов размера операнда и префиксов типа операнда (66, F2, F3).

    Трехбайтовый префикс VEX дополнительно содержит:

    • Три бита, X̅; B̅; и W, также аналогичные соответствующим битам в префиксе REX.
    • Пять бит с именем m. Два из m битов используются для замены существующих escape-кодов и для указания длины инструкции. Оставшиеся три м биты зарезервированы для использования в будущем, например, указать вектор длины> 256 бит, с указанием различных длин команд, или расширение опкода пространства, однако по состоянию на 2013 г. , Intel решила ввести новую схему кодирования, причем префикс EVEX , а чем развернуть оставшиеся m бит.

    В схеме кодирования VEX используется префикс кода, состоящий из двух или трех байтов , который добавляется к существующим или новым кодам команд . [1]

    В архитектуре x86 инструкции с операндом памяти могут использовать байт ModR / M, который определяет режим адресации. Этот байт имеет три битовых поля:

    • mod , биты [7: 6] - в сочетании с полем r / m кодирует либо 8 регистров, либо 24 режима адресации. Также кодирует информацию о коде операции для некоторых инструкций.
    • reg / opcode , биты [5: 3] - в зависимости от байта первичного кода операции, задает либо регистр, либо еще три бита информации кода операции.
    • r / m , биты [2: 0] - могут указывать регистр как операнд или объединяться с полем mod для кодирования режима адресации.

    Формы 32-битной адресации базовый плюс индекс и масштаб плюс индекс (закодированные с помощью r / m = 100 и mod <> 11) требуют другого байта адресации, байта SIB. В нем есть следующие поля:

    • масштабный коэффициент, закодированный битами [7: 6]
    • индексный регистр, биты [5: 3]
    • базовый регистр, биты [2: 0].

    Чтобы использовать 64-битную адресацию и дополнительные регистры, присутствующие в архитектуре x86-64, был введен префикс REX , который обеспечивает дополнительное пространство для кодирования режимов адресации. Битовое поле W изменяет размер операнда на 64 бита, R расширяет reg до 4 бит, B расширяет r / m (или opreg в нескольких кодах операций, таких как "POP reg", которые кодируют номер регистра в своих 3 младших битах кода операции) и X и B расширяют индекс и базу в байте SIB. Однако префикс REX кодируется довольно неэффективно, тратя впустую половину своих 8 бит.

    Кодирование REX и VEX
    REX
    76543210
    Байт 0 0100WрИксB
    3-байтовый VEX
    76543210
    Байт 0 (C4h) 11000100
    Байт 1 РИКСм 4м 3м 2м 1м 0
    Байт 2 W3210Lп 1p 0
    2-байтовый VEX
    76543210
    Байт 0 (C5h) 11000101
    Байт 1 Р3210Lп 1p 0

    Префикс VEX обеспечивает компактное представление префикса REX, а также различных других префиксов для расширения режима адресации, перечисления регистров, размера и ширины операнда:

    Команды, закодированные с префиксом VEX, могут иметь до четырех переменных операндов (в регистрах или в памяти) и один постоянный операнд (непосредственное значение). Команды, которым требуется более трех переменных операндов, используют биты непосредственного операнда для указания операнда 4-го регистра (IS4 выше). Максимум один из операндов может быть операндом памяти; и максимум один из операндов может быть непосредственной константой из 4 или 8 бит. Остальные операнды - регистры.

    AVX набор команд является первым набором команд расширения , чтобы использовать схему кодирования VEX. Набор инструкций AVX использует префикс VEX только для инструкций, использующих регистры SIMD XMM .

    Однако схема кодирования VEX использовалась и для других типов команд при последующем расширении набора команд. Например, BMI представила арифметические инструкции с кодированием VEX и битовые манипуляции, которые работают с регистрами общего назначения. AVX-512 представил 8 регистров маски и добавил инструкции для управления ими. В этих инструкциях используется кодировка VEX. VEX.R, VEX.B или VEX.v3 игнорируются, когда поле используется для кодирования регистра маски.

    Значения начальных байтов префикса VEX, C4h и C5h, такие же, как коды операций инструкций LDS и LES. Эти инструкции не поддерживаются в 64-битном режиме. Чтобы устранить неоднозначность в 32-битном режиме, спецификация VEX использует тот факт, что допустимый байт ModRM LDS или LES не может иметь форму 11xxxxxx (которая будет определять регистровый операнд). Различные битовые поля во втором байте префикса VEX инвертируются, чтобы гарантировать, что байт всегда имеет эту форму в 32-битном режиме.

    Устаревшие инструкции SIMD с добавленным префиксом VEX эквивалентны тем же инструкциям без префикса VEX со следующими отличиями:

    Инструкции, которые используют весь 256-битный регистр YMM, не следует смешивать с инструкциями, не относящимися к VEX, которые оставляют верхнюю половину регистра неизменной по соображениям эффективности.

    Читайте также: