Как определить размер операнда по умолчанию для декодирования инструкций x86-64

В настоящее время я пытаюсь написать программу, которая декодирует инструкции x86-64 в ассемблер, но я застрял в определении размера операнда памяти/регистра по умолчанию при работе с инструкциями, операнды которых не являются явными (т. е. они меняют размер в зависимости от текущего режим работы).

Я осознаю тот факт, что каждый сегмент памяти имеет свою собственную таблицу дескрипторов, которая описывает, среди прочего, поля EFER.LMA, CS.L и CS.D. Различные их комбинации сигнализируют, в каком режиме работы находится конкретный сегмент.

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

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

Я приведу пример:

Как определить размер операнда по умолчанию для декодирования инструкций x86-64

Вариант инструкции add с opcode = 0x03 имеет символы Gv, Ev.

Согласно третьему тому руководства AMD x64, G/E означает регистр общего назначения, а символ v означает «слово, двойное или четверное слово (в 64-битном режиме), в зависимости от эффективного размера операнда».

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

Вот что я собрал воедино, чтобы понять, как к этому подойти:

  1. Предположим, что инструкция нацелена на сегмент по умолчанию. Возможно, это DS для операндов регистров и SS для операндов памяти. Если существует префикс переопределения сегмента, используйте вместо него этот сегмент.

  2. Получите запись дескриптора этого сегмента.

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

  4. Это будет размер, используемый для операнда.

Правильный ли такой подход? Не стесняйтесь исправлять что-либо в моем подходе/понимании. Большое спасибо.

Префиксы (REX.W или 66h) могут отличать эффективный размер операнда от значения по умолчанию для режима. Вы пишете отладчик? Если вы просто пишете дизассемблер, обычным способом является определение режима на основе метаданных объекта/исполняемого файла и/или предоставление пользователю возможности указать его в командной строке (единственный вариант для плоских двоичных файлов). например ndisasm -b 64 foo.o

Peter Cordes 10.08.2024 13:16
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Только сегмент CS влияет на размеры операнда и адреса. DS, SS, ES, FS и GS ни на что не влияют.

Существует 3 различных режима: 16-битный, 32-битный и 64-битный. Они выбираются по битам D и L в дескрипторе, загруженном в CS:

  • 16-битный режим (D=0, L=0): размер операнда и адреса составляет 16 бит. Префикс 66 перед тем, как инструкция меняет размер операнда на 32-битный, префикс 67 изменяет размер адреса на 32-битный. 64-битные размеры недоступны.
  • 32-битный режим (D=1, L=0): размер операнда и адреса 32-битный. Префикс 66 изменяет размер операнда на 16-битный, префикс 67 изменяет размер адреса на 16-битный. 64-битные размеры недоступны.
  • 64-битный режим (D=0, L=1): размер операнда 32 бита, размер адреса 64 бита. Префикс 66 изменяет размер операнда на 16-битный, префикс REX.W изменяет размер операнда на 64-битный, префикс 67 изменяет размер адреса на 32-битный. 16-битный размер адреса недоступен.

EFER.LMA — это глобальное значение (не для каждого сегмента), оно влияет на то, обращает ли ЦП внимание на бит L. Если EFER.LMA=0, бит L игнорируется (предполагается равным 0) и 64-битный режим недоступен.

Бит D в сегментах данных не влияет на размер операнда по умолчанию. Это влияет только на максимальный адрес в сегментах роста (0xFFFF или 0xFFFFFFFF), а для сегмента SS влияет на то, будут ли push и pop использовать регистр SP или ESP. В 64-битном режиме этим регистром всегда является RSP.

Обратите внимание: если код работает под ОС, таблица дескрипторов обычно недоступна для пользовательских программ. Вам нужно использовать какой-то специфичный для ОС способ узнать, в каком режиме работает другой процесс - если такой способ вообще предусмотрен ОС. Однако большинство программ запускаются в одном режиме и никогда его не меняют. Режим запуска обычно указывается в заголовке исполняемого файла.

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

nick17 10.08.2024 21:19

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