[Перевод] Обнаружены незадокументированные опкоды в системе инструкций процессора x86

Венгерский инженер Кан Бёлюк (Can Bölük) из Verilave нашёл неиспользованные и не задокументированные операционные коды в системе инструкций процессора x86-64.  

Оказывается, такие опкоды действительно существуют! Некоторые команды изначально были мертворожденными, другие больше напоминают пасхалки, а третьи кажутся забытыми багами или частично реализованными командами, которые никогда не увидят свет. Есть и такие опкоды, которые, кажется, открывают целый «Режим бога» и позволяют обойти защиту процессора или даже переписать внутренний микрокод чипа. Бёлюк автоматизировал процесс их обнаружения, причём его подход оказался весьма оригинальным.

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

Кан Беллюк вывел метод обнаружения практически любой инструкции процессора, используя сторонний канал. Способ интересен тем, что он обращается к нетривиальным возможностям процессора.  Полный код доступен тут: haruspex.can.ac / Github.

В своём блоге Кан Беллюк подробно рассказывает о сути своего подхода:

Откуда взялись микроинструкции

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

  • Механизм перевода микро-инструкций (MITE) – переводит простые устаревшие инструкции в четыре или менее микроинструкций.

  • Секвенсор микрокода (MS) – отвечает за перевод более сложных инструкций, от которых CISC в архитектуре Intel просто сходит с ума.

Здесь также стоит упомянуть буфер декодированного потока (DSB), более известный как iCache. Но он не имеет отношения к эксперименту, так как обрабатывать низкоуровневые элементы можно через счётчики производительности, которые нам щедро предоставляет Intel. В основном это:

[Перевод] Обнаружены незадокументированные опкоды в системе инструкций процессора x86

Такой подход имеет преимущества перед другими, такими как Sandsifter. Даже если микрокод инструкции выдаёт #UD (скажем, если пароль не совпадает или не выполняются какие-то условия), это нас не обманет, поскольку его всё ещё нужно декодировать.

Исполнение команд в параллельной вселенной

Теперь проблема заключается в том, чтобы получить эти инструкции. Исполнять все подряд – не вариант, потому что исполнение инструкции может, к примеру, вызвать сброс системы или испортить внутренний кэш. Как этого избежать? Очень просто – не исполнять! И здесь начинается вторая интересная особенность микроархитектуры: спекулятивное и неупорядоченное исполнение (speculative execution). 

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

Проще всего реализовать это через CALL. Рассмотрим следующий фрагмент:

call x
  
x:
  lea        rax,     [rip+z]
  xchg       [rsp],   rax
  ret
z:

Это приведёт к исполнению спекулятивного кода и остановке вызванной подпрограммы, если такое возможно. Команда XCHG может показаться избыточной, но практика показывает, что процессор слишком умен, чтобы начать исполнять другую ветку, если процедура не возвращается. Поэтому нужно дать целевому буферу ветки то, что ему требуется. Здесь больше подходит XCHG, а не MOV, потому что последний подразумевает LOCK, который приведёт к остановке процессора в памяти, а нам важно, чтобы он был видимым.

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

auto ip = ia32::get_ip() & ~63ull;
std::copy_n( ( volatile uint8_t* ) ip, 512, ( volatile uint8_t* ) ip );
ia32::sfence();
for ( size_t n = 0; n != 512; n += 64 )
  ia32::clflush( ip + n );

Точный сбор данных

Переходим к практике. Нам нужны точные результаты, что подразумевает непрерывность работы. Вы можете ошибочно считать, что находитесь в режиме ядра, а значит проблема решается запуском командной строки. Но на самом деле это не так. Во-первых, #SMI (System Management Interrupt или прерывание управления системой), как выяснилось, не так уж хорошо справляется с остановкой PMC (Уязвимости контроллера управления питанием). Во-вторых, это, в любом случае, мешает чистоте эксперимента. Поэтому Кан повторял опыты, пока IA32_MSR_SMI_COUNT не стал постоянным во время выполнения. Другой багер – #NMI. С ним можно справиться с помощью установки прерывания. Также стоило бы принимать во внимание #MC, но на него можно забить.

Повторяя эксперимент множество раз, выбирая Mod(x) и тщательно прописывая код, вы избежите большинства оставшихся проблем. Следующий шаг – написание кода и, собственно, сбор данных. Спекулятивный код будет состоять из 15 копий NOP и одной 0xCE в конце, что позволит вызвать фактическое выполнение #UD и остановить спекулятивное выполнение. Мы будем пробовать практически каждый код операции в диапазоне 0x00-0xFF, и все они будут иметь префикс 0x0F или, возможно, другой префикс из набора { 0x66, 0xF2, 0xF3 } поскольку Intel любит использовать их для создания новых кодов операций. Нам также нужно добавить суффикс для обнаружения вариантов ModR/M, но это занимает считанные секунды.

Сведение результатов

Для начала возьмём два базовых замера: NOP, как он есть, и 0xCE, как первый операционный код, который покажет значения для завершенного исполнения команды и для случая for-real-#UD.

Чтобы убрать основную часть «мусора», сразу удаляем все замеры, соответствующие случаю for-real-#UD. Остаётся избавиться от избыточных префиксов. Шаблон:

┌─────────┬──────────────────────────────────┬──────┬────┐
│ (index) │             decoding             │ mits │ ms │
├─────────┼──────────────────────────────────┼──────┼────┤
│   90    │              'nop'               │  54  │ 80 │ /*Baseline*/
│  6690   │           'data16 nop'           │  53  │ 67 │
│  f290   │              'nop'               │  53  │ 80 │
│ f20f90  │ 'seto byte ptr [rax-0x6f6f6f70]' │  48  │ 80 │
└─────────┴──────────────────────────────────┴──────┴────┘

Каждый nop преобразуется в отдельную микрокоманду, которая будет обрабатываться MITE. Если префикс избыточен, MITS должна всегда отключаться только одной командой, а MS при этом будет оставаться прежней. Кроме того, мы можем отфильтровать часть избыточных проверок, задав условие, что 0x0F никогда не должен быть лишним. Объединив оба этих условия, мы уберём практически весь мусор и даже сможем аккуратно вычислить длину инструкции. Приведённый ниже код позволяет избавиться от 54954 ненужных записей.

const propertyMatch = (i1, i2) => {
  return i2.ms == i1.ms && i2.outOfOrder == i1.outOfOrder && i2.iclass == i1.iclass;
};

// Purge redundant prefixes.
//
for (const k1 of Object.keys(instructions)) {
  // Skip if already deleted.
  //
  const i1 = instructions[k1];
  if (!i1) {
    continue;
  }

  // Iterate each prefix (apart from 0f):
  //
  for (const pfx of prefixList) {
    // If the instruction exists:
    //
    const k2 = pfx + k1;
    if (k2 in instructions) {
      // If the instruction has matching properties as the derived from parent, delete the entry.
      //
      const i2 = instructions[k2];
      if (propertyMatch(i1, i2)) {
        // MITS#1 == MITS#2 can indicate same instruction if instruction halts.
        // Otherwise MITS#1 has to be one more than MITS#2 since it should execute one more NOP.
        //
        if (i1.mits != i2.mits) {
          if (i1.mits != i2.mits + 1) {
            continue;
          }
        } else if (i1.mits > faultBaseline.mits) {
          continue;
        }
        delete instructions[k2];
      }
    }
  }
}

Фильтрация на основе суффиксов делается плюс-минус аналогично и позволяет избавиться ещё от 72869 инструкций. В итоге останется 1699 записей, которых достаточно для начала анализа!

// Purge redundant suffixes.
//
for (const k1 of Object.keys(instructions)) {
  // Skip if already deleted or not relevant.
  //
  const i1 = instructions[k1];
  if (!i1 || k1.length <= 2) {
    continue;
  }

  // Find maching entries:
  //
  for (const k2 of Object.keys(instructions)) {
    // If it is matching except the last byte:
    //
    if (k2.startsWith(k1.substr(0, k1.length - 2)) && k2 != k1) {
      // If it has matching properties ignoring the length, erase it
      //
      const i2 = instructions[k2];
      if (propertyMatch(i1, i2)) {
        delete instructions[k2];
      }
    }
  }
}

Анализ поведения

Теперь рассмотрим тот поразительный объем информации, которую мы собрали только с этих двух счётчиков и некоторых байтов, расположенных в фиксированном месте, но даже не выполняемых. Если MS ниже базовой линии nop, это будет означать, что поток управления прерван, то есть перед нами ветвь или исключение. Если MITS совпадает с базовой линией, это, вероятно, указывает на инструкцию сериализации, которая выполнила несколько микрокоманд (при условии, что она прошла начальный фильтр MS или MITS, не оставаясь прежней), но затем остановила спекулятивный поток (поскольку ни один из кодов операций NOP не был декодирован).

┌──────────┬────────────────────────────────────────────────────────────┬──────┬─────┬─────────────┬──────────────────┐
│ (index)  │                          decoding                          │ mits │ ms  │ serializing │ speculationFence │
├──────────┼────────────────────────────────────────────────────────────┼──────┼─────┼─────────────┼──────────────────┤
│  668690  │            'xchg byte ptr [rax-0x6f6f6f70], dl'            │  47  │ 88  │    true     │      false       │
│    6c    │                          'insb '                           │  39  │ 112 │    true     │       true       │
│    6d    │                          'insd '                           │  39  │ 99  │    true     │       true       │
│    6e    │                          'outsb '                          │  39  │ 98  │    true     │       true       │
│    6f    │                          'outsd '                          │  39  │ 98  │    true     │       true       │
│   8e90   │            'mov ss, word ptr [rax-0x6f6f6f70]'             │  42  │ 86  │    true     │       true       │
│   c290   │                        'ret 0x9090'                        │  43  │ 107 │    true     │       true       │ // <---  Likely errors since
│    c3    │                           'ret '                           │  41  │ 106 │    true     │       true       │ // <-/   CF will be interrupted
│   ca90   │                      'ret far 0x9090'                      │  39  │ 145 │    true     │       true       │ //       but will continue from a valid IP.
│    cb    │                         'ret far '                         │  39  │ 145 │    true     │       true       │
│    cc    │                          'int3 '                           │  39  │ 94  │    true     │       true       │
│   cd90   │                         'int 0x90'                         │  39  │ 91  │    true     │       true       │
│    cf    │                          'iretd '                          │  39  │ 136 │    true     │       true       │
│   e490   │                       'in al, 0x90'                        │  39  │ 110 │    true     │       true       │
│   e590   │                       'in eax, 0x90'                       │  39  │ 110 │    true     │       true       │
│   e690   │                       'out 0x90, al'                       │  39  │ 110 │    true     │       true       │
│   e790   │                      'out 0x90, eax'                       │  39  │ 110 │    true     │       true       │
│    ec    │                        'in al, dx'                         │  39  │ 109 │    true     │       true       │
│    ed    │                        'in eax, dx'                        │  39  │ 109 │    true     │       true       │
│    ee    │                        'out dx, al'                        │  39  │ 109 │    true     │       true       │
│    ef    │                       'out dx, eax'                        │  39  │ 109 │    true     │       true       │
│    f1    │                          'int1 '                           │  39  │ 112 │    true     │       true       │
│    f4    │                           'hlt'                            │  39  │ 124 │    true     │       true       │
│  0f0090  │              'lldt word ptr [rax-0x6f6f6f70]'              │  47  │ 93  │    true     │       true       │
│  0f0098  │              'ltr word ptr [rax-0x6f6f6f70]'               │  39  │ 110 │    true     │       true       │
│  0f0080  │              'sldt word ptr [rax-0x6f6f6f70]'              │  47  │ 87  │    true     │      false       │
│  0f0081  │              'sldt word ptr [rcx-0x6f6f6f70]'              │  47  │ 87  │    true     │      false       │
│  0f0088  │              'str word ptr [rax-0x6f6f6f70]'               │  47  │ 87  │    true     │      false       │
│  0f00a0  │              'verr word ptr [rax-0x6f6f6f70]'              │  47  │ 91  │    true     │       true       │
│  0f00a8  │              'verw word ptr [rax-0x6f6f6f70]'              │  47  │ 91  │    true     │       true       │
│  0f00d8  │                          'ltr ax'                          │  39  │ 108 │    true     │       true       │
│  0f0190  │                'lgdt ptr [rax-0x6f6f6f70]'                 │  47  │ 94  │    true     │       true       │
│  0f0198  │                'lidt ptr [rax-0x6f6f6f70]'                 │  47  │ 94  │    true     │       true       │
│  0f0180  │                'sgdt ptr [rax-0x6f6f6f70]'                 │  47  │ 89  │    true     │      false       │
│  0f0188  │                'sidt ptr [rax-0x6f6f6f70]'                 │  47  │ 88  │    true     │      false       │
│  0f01b0  │              'lmsw word ptr [rax-0x6f6f6f70]'              │  39  │ 103 │    true     │       true       │
│  0f01b8  │             'invlpg byte ptr [rax-0x6f6f6f70]'             │  39  │ 114 │    true     │       true       │
│  0f01a0  │              'smsw word ptr [rax-0x6f6f6f70]'              │  47  │ 85  │    true     │      false       │
│ f20f22a4 │                       'mov cr4, rsp'                       │  39  │ 103 │    true     │       true       │
│ f20f2396 │                       'mov dr2, rsi'                       │  39  │ 110 │    true     │       true       │
│ f20f2380 │                       'mov dr0, rax'                       │  39  │ 109 │    true     │       true       │
│ f20fc788 │           'cmpxchg8b qword ptr [rax-0x6f6f6f70]'           │  46  │ 95  │    true     │      false       │
│ f20fc78a │           'cmpxchg8b qword ptr [rdx-0x6f6f6f70]'           │  46  │ 95  │    true     │      false       │
│  f38690  │       'xrelease xchg byte ptr [rax-0x6f6f6f70], dl'        │  47  │ 88  │    true     │      false       │
│  f38790  │      'xrelease xchg dword ptr [rax-0x6f6f6f70], edx'       │  47  │ 88  │    true     │      false       │
│  f38890  │        'xrelease mov byte ptr [rax-0x6f6f6f70], dl'        │  47  │ 84  │    true     │      false       │
│  f38990  │       'xrelease mov dword ptr [rax-0x6f6f6f70], edx'       │  47  │ 84  │    true     │      false       │
│   f36c   │                        'rep insb '                         │  39  │ 112 │    true     │       true       │
│   f36d   │                        'rep insd '                         │  39  │ 112 │    true     │       true       │
│   f36e   │                        'rep outsb '                        │  39  │ 111 │    true     │       true       │
│   f36f   │                        'rep outsd '                        │  39  │ 111 │    true     │       true       │
│   f3a4   │         'rep movsb byte ptr [rdi], byte ptr [rsi]'         │  43  │ 118 │    true     │       true       │ //
│   f3a6   │         'rep cmpsb byte ptr [rsi], byte ptr [rdi]'         │  43  │ 123 │    true     │       true       │ //
│   f3a7   │        'rep cmpsd dword ptr [rsi], dword ptr [rdi]'        │  43  │ 123 │    true     │       true       │ //
│   f3aa   │                 'rep stosb byte ptr [rdi]'                 │  43  │ 125 │    true     │       true       │ // Likely errors since
│   f3ac   │                 'rep lodsb byte ptr [rsi]'                 │  43  │ 106 │    true     │       true       │ // rcx is undefined.
│   f3ad   │                'rep lodsd dword ptr [rsi]'                 │  43  │ 106 │    true     │       true       │ //
│   f3ae   │                 'rep scasb byte ptr [rdi]'                 │  43  │ 123 │    true     │       true       │ //
│   f3af   │                'rep scasd dword ptr [rdi]'                 │  43  │ 123 │    true     │       true       │ //
│ f30f0082 │              'sldt word ptr [rdx-0x6f6f6f70]'              │  46  │ 87  │    true     │      false       │
│ f30f0088 │              'str word ptr [rax-0x6f6f6f70]'               │  46  │ 87  │    true     │      false       │
│ f30f0180 │                'sgdt ptr [rax-0x6f6f6f70]'                 │  46  │ 89  │    true     │      false       │
│ f30f018a │                'sidt ptr [rdx-0x6f6f6f70]'                 │  46  │ 88  │    true     │      false       │
│ f30f01a1 │              'smsw word ptr [rcx-0x6f6f6f70]'              │  46  │ 85  │    true     │      false       │
│ f30f2190 │                       'mov rax, dr2'                       │  39  │ 107 │    true     │       true       │
│ f30f22a4 │                       'mov cr4, rsp'                       │  39  │ 103 │    true     │       true       │
│ f30f2380 │                       'mov dr0, rax'                       │  39  │ 109 │    true     │       true       │
│ f30f238e │                       'mov dr1, rsi'                       │  39  │ 110 │    true     │       true       │
│ f30f7890 │                             ''                             │  39  │ 87  │    true     │       true       │
│ f30f7990 │                             ''                             │  39  │ 87  │    true     │       true       │
│ f30fc789 │           'cmpxchg8b qword ptr [rcx-0x6f6f6f70]'           │  46  │ 95  │    true     │      false       │
│ f30fc78f │           'cmpxchg8b qword ptr [rdi-0x6f6f6f70]'           │  46  │ 95  │    true     │      false       │
│ f30fc7b0 │             'vmxon qword ptr [rax-0x6f6f6f70]'             │  39  │ 116 │    true     │       true       │
│ f30fc733 │                  'vmxon qword ptr [rbx]'                   │  39  │ 119 │    true     │       true       │
│ f30fc776 │                'vmxon qword ptr [rsi-0x70]'                │  39  │ 120 │    true     │       true       │
└──────────┴────────────────────────────────────────────────────────────┴──────┴─────┴─────────────┴──────────────────┘

Анализ спекулятивного поведения

Обратили внимание на столбец «Speculation Fence» (Заграждение для спекуляций) в предыдущем блоке данных? Чтобы узнать, выполняются ли инструкции спекулятивно или они останавливают очередь ради получения результатов от побочного канала, соберём ещё один вид информации из довольно неожиданного источника:

Что с этим делать? Этот счётчик очень полезен, ведь в предыдущем коде мы не делали никаких разделений. Теперь добавляется разделение сразу после инструкции и проверка отличия циклов от нуля. Это позволят узнать, было ли остановлено спекулятивное выполнение или нет. 

Нам нужно сжать divps xmm4, xmm5 прямо перед #UD'ing, 0xCE и потратить несколько циклов на неспекулятивную копию, чтобы вызвать остановку. Это даст спекулятивному коду больше времени для выполнения. Изменение предыдущего кода на следующий даёт нам идеальную настройку:

vmovups    ymm0,    [temp]
vmovups    ymm1,    [temp]
vmovups    ymm2,    [temp]
vmovups    ymm3,    [temp]
vzeroupper
addps      xmm0,    xmm1
vaddps     ymm2,    ymm0,    ymm3
vaddps     ymm1,    ymm0,    ymm2
vaddps     ymm3,    ymm0,    ymm1
vaddps     ymm0,    ymm0,    [temp]
vaddps     ymm1,    ymm0,    [temp]
vaddps     ymm2,    ymm0,    [temp]
vaddps     ymm3,    ymm0,    [temp]
vaddps     ymm0,    ymm0,    ymm1
vaddps     ymm2,    ymm0,    ymm3
vaddps     ymm1,    ymm0,    ymm2
vaddps     ymm3,    ymm0,    ymm1
vaddps     ymm0,    ymm0,    [temp]
vaddps     ymm1,    ymm0,    [temp]
vaddps     ymm2,    ymm0,    [temp]
vaddps     ymm3,    ymm0,    [temp]
vaddps     ymm0,    ymm0,    ymm1
vaddps     ymm2,    ymm0,    ymm3
vaddps     ymm1,    ymm0,    ymm2
vaddps     ymm3,    ymm0,    ymm1
vaddps     ymm0,    ymm0,    [temp]
vaddps     ymm1,    ymm0,    [temp]
vaddps     ymm2,    ymm0,    [temp]
vaddps     ymm3,    ymm0,    [temp]
vaddps     ymm0,    ymm0,    ymm1
vaddps     ymm2,    ymm0,    ymm3
vaddps     ymm1,    ymm0,    ymm2
vaddps     ymm3,    ymm0,    ymm1
lea        rax,     [rip+z]
xchg       [rsp],   rax
ret

Пометка записей с ненулевым счётом, как «спекулятивных», позволяет узнать, какие инструкции мы можем спекулятивно выполнять, чтобы получить информацию. На следующем примере видно, что это отлично работает:

-- These indeed leak data under speculative execution:
┌──────────┬────────────────────────────────────────────────────────────┬──────┬─────┬─────────────┬──────────────────┐
│ (index)  │                          decoding                          │ mits │ ms  │ serializing │ speculationFence │
├──────────┼────────────────────────────────────────────────────────────┼──────┼─────┼─────────────┼──────────────────┤
│   8690   │            'xchg byte ptr [rax-0x6f6f6f70], dl'            │  48  │ 88  │    false    │      false       │
│   e090   │                'loopne 0xffffffffffffff92'                 │  56  │ 87  │    false    │      false       │
│    fb    │                           'sti '                           │  56  │ 83  │    false    │      false       │
│    fc    │                           'cld '                           │  53  │ 83  │    false    │      false       │
│  0f0080  │              'sldt word ptr [rax-0x6f6f6f70]'              │  47  │ 87  │    true     │      false       │
│  0f0081  │              'sldt word ptr [rcx-0x6f6f6f70]'              │  47  │ 87  │    true     │      false       │
│  0f0088  │              'str word ptr [rax-0x6f6f6f70]'               │  47  │ 87  │    true     │      false       │
│  0f00c0  │                         'sldt eax'                         │  51  │ 85  │    false    │      false       │
│  0f00c8  │                         'str eax'                          │  51  │ 85  │    false    │      false       │
│  0f0009  │                    'str word ptr [rcx]'                    │  51  │ 87  │    false    │      false       │
│  0f0180  │                'sgdt ptr [rax-0x6f6f6f70]'                 │  47  │ 89  │    true     │      false       │
│  0f0188  │                'sidt ptr [rax-0x6f6f6f70]'                 │  47  │ 88  │    true     │      false       │
│  0f01a0  │              'smsw word ptr [rax-0x6f6f6f70]'              │  47  │ 85  │    true     │      false       │
│  0f01a1  │              'smsw word ptr [rcx-0x6f6f6f70]'              │  47  │ 85  │    true     │      false       │
│  0f01d0  │                         'xgetbv '                          │  51  │ 88  │    false    │      false       │
│  0f01d5  │                           'xend'                           │  51  │ 84  │    false    │      false       │
│  0f01e0  │                         'smsw eax'                         │  51  │ 84  │    false    │      false       │
│  0f010f  │                      'sidt ptr [rdi]'                      │  51  │ 88  │    false    │      false       │
│  0f0140  │                   'sgdt ptr [rax-0x70]'                    │  50  │ 89  │    false    │      false       │
│  0f2098  │                       'mov rax, cr3'                       │  51  │ 88  │    false    │      false       │
│  0f2080  │                       'mov rax, cr0'                       │  51  │ 85  │    false    │      false       │
│   0f31   │                          'rdtsc '                          │  52  │ 93  │    false    │      false       │
│   0f77   │                           'emms'                           │  52  │ 111 │    false    │      false       │
│   0fa1   │                          'pop fs'                          │  52  │ 87  │    false    │      false       │
│  0fa390  │            'bt dword ptr [rax-0x6f6f6f70], edx'            │  51  │ 86  │    false    │      false       │
...
│ f30f0082 │              'sldt word ptr [rdx-0x6f6f6f70]'              │  46  │ 87  │    true     │      false       │
│ f30f0088 │              'str word ptr [rax-0x6f6f6f70]'               │  46  │ 87  │    true     │      false       │
│ f30f0006 │                   'sldt word ptr [rsi]'                    │  50  │ 87  │    false    │      false       │
│ f30f000a │                    'str word ptr [rdx]'                    │  50  │ 87  │    false    │      false       │
│ f30f0180 │                'sgdt ptr [rax-0x6f6f6f70]'                 │  46  │ 89  │    true     │      false       │
│ f30f018a │                'sidt ptr [rdx-0x6f6f6f70]'                 │  46  │ 88  │    true     │      false       │
│ f30f01a1 │              'smsw word ptr [rcx-0x6f6f6f70]'              │  46  │ 85  │    true     │      false       │
│ f30f010f │                      'sidt ptr [rdi]'                      │  50  │ 88  │    false    │      false       │
│ f30f0126 │                   'smsw word ptr [rsi]'                    │  50  │ 85  │    false    │      false       │
│ f30f0140 │                   'sgdt ptr [rax-0x70]'                    │  49  │ 89  │    false    │      false       │
│ f30faed0 │                       'wrfsbase eax'                       │  50  │ 87  │    false    │      false       │
│ f30faed8 │                       'wrgsbase eax'                       │  50  │ 87  │    false    │      false       │
│ f30faec0 │                       'rdfsbase eax'                       │  50  │ 86  │    false    │      false       │
│ f30faec8 │                       'rdgsbase eax'                       │  50  │ 86  │    false    │      false       │
│ f30fb391 │           'btr dword ptr [rcx-0x6f6f6f70], edx'            │  50  │ 86  │    false    │      false       │
│ f30fb39f │           'btr dword ptr [rdi-0x6f6f6f70], ebx'            │  50  │ 86  │    false    │      false       │
│ f30fbb92 │           'btc dword ptr [rdx-0x6f6f6f70], edx'            │  50  │ 86  │    false    │      false       │
│ f30fbb94 │        'btc dword ptr [rax+rdx*4-0x6f6f6f70], edx'         │  49  │ 86  │    false    │      false       │
│ f30fc789 │           'cmpxchg8b qword ptr [rcx-0x6f6f6f70]'           │  46  │ 95  │    true     │      false       │
│ f30fc78f │           'cmpxchg8b qword ptr [rdi-0x6f6f6f70]'           │  46  │ 95  │    true     │      false       │
└──────────┴────────────────────────────────────────────────────────────┴──────┴─────┴─────────────┴──────────────────┘
-- Yet these do not:
┌──────────┬─────────────────────────────────────────────────┬──────┬─────┬─────────────┬──────────────────┐
│ (index)  │                    decoding                     │ mits │ ms  │ serializing │ speculationFence │
├──────────┼─────────────────────────────────────────────────┼──────┼─────┼─────────────┼──────────────────┤
│    6c    │                     'insb '                     │  39  │ 112 │    true     │       true       │
│    6d    │                     'insd '                     │  39  │ 99  │    true     │       true       │
│    6e    │                    'outsb '                     │  39  │ 98  │    true     │       true       │
│    6f    │                    'outsd '                     │  39  │ 98  │    true     │       true       │
│   8e90   │       'mov ss, word ptr [rax-0x6f6f6f70]'       │  42  │ 86  │    true     │       true       │
│    9d    │                    'popfq '                     │  55  │ 87  │    false    │       true       │
│   c290   │                  'ret 0x9090'                   │  43  │ 107 │    true     │       true       │
│    c3    │                     'ret '                      │  41  │ 106 │    true     │       true       │
│   c890   │              'enter 0x9090, 0x90'               │  50  │ 93  │    false    │       true       │
│   ca90   │                'ret far 0x9090'                 │  39  │ 145 │    true     │       true       │
│    cb    │                   'ret far '                    │  39  │ 145 │    true     │       true       │
│    cc    │                     'int3 '                     │  39  │ 94  │    true     │       true       │
│   cd90   │                   'int 0x90'                    │  39  │ 91  │    true     │       true       │
│    cf    │                    'iretd '                     │  39  │ 136 │    true     │       true       │
│   e190   │           'loope 0xffffffffffffff92'            │  64  │ 83  │    false    │       true       │
│   e490   │                  'in al, 0x90'                  │  39  │ 110 │    true     │       true       │
│   e590   │                 'in eax, 0x90'                  │  39  │ 110 │    true     │       true       │
│   e690   │                 'out 0x90, al'                  │  39  │ 110 │    true     │       true       │
│   e790   │                 'out 0x90, eax'                 │  39  │ 110 │    true     │       true       │
│    ec    │                   'in al, dx'                   │  39  │ 109 │    true     │       true       │
│    ed    │                  'in eax, dx'                   │  39  │ 109 │    true     │       true       │
│    ee    │                  'out dx, al'                   │  39  │ 109 │    true     │       true       │
│    ef    │                  'out dx, eax'                  │  39  │ 109 │    true     │       true       │
│    f1    │                     'int1 '                     │  39  │ 112 │    true     │       true       │
│   f390   │                     'pause'                     │  52  │ 86  │    false    │       true       │
│    f4    │                      'hlt'                      │  39  │ 124 │    true     │       true       │
│    fd    │                     'std '                      │  53  │ 83  │    false    │       true       │
│  0f0090  │        'lldt word ptr [rax-0x6f6f6f70]'         │  47  │ 93  │    true     │       true       │
│  0f0098  │         'ltr word ptr [rax-0x6f6f6f70]'         │  39  │ 110 │    true     │       true       │
│  0f00a0  │        'verr word ptr [rax-0x6f6f6f70]'         │  47  │ 91  │    true     │       true       │
│  0f00a8  │        'verw word ptr [rax-0x6f6f6f70]'         │  47  │ 91  │    true     │       true       │
│  0f00d0  │                    'lldt ax'                    │  51  │ 91  │    false    │       true       │
│  0f00d8  │                    'ltr ax'                     │  39  │ 108 │    true     │       true       │
...
│  0f00e0  │                    'verr ax'                    │  51  │ 90  │    false    │       true       │
│  0f00e8  │                    'verw ax'                    │  51  │ 90  │    false    │       true       │
│  0f0190  │           'lgdt ptr [rax-0x6f6f6f70]'           │  47  │ 94  │    true     │       true       │
│  0f0198  │           'lidt ptr [rax-0x6f6f6f70]'           │  47  │ 94  │    true     │       true       │
│  0f01b0  │        'lmsw word ptr [rax-0x6f6f6f70]'         │  39  │ 103 │    true     │       true       │
│  0f01b8  │       'invlpg byte ptr [rax-0x6f6f6f70]'        │  39  │ 114 │    true     │       true       │
│  0f01d1  │                    'xsetbv '                    │  51  │ 117 │    false    │       true       │
│  0f01d2  │                       ''                        │  39  │ 87  │    true     │       true       │
│  0f01d4  │                    'vmfunc '                    │  39  │ 83  │    true     │       true       │
│ 0f2193   │                 'mov rbx, dr2'                  │  39  │ 107 │    true     │       true       │
└──────────┴─────────────────────────────────────────────────┴──────┴─────┴─────────────┴──────────────────┘

Некоторые интересные результаты

Вот полный перечень незадокументированных инструкций-пасхалок, которые Кан Бёлюк нашёл на своём i7 6850k:

┌──────────┬──────────┬──────┬─────┬─────────────┬──────────────────┐
│ (index)  │ decoding │ mits │ ms  │ serializing │ speculationFence │
├──────────┼──────────┼──────┼─────┼─────────────┼──────────────────┤
│  0f01d2  │    ''    │  39  │ 87  │    true     │       true       │
│  0f01c6  │    ''    │  39  │ 83  │    true     │       true       │
│  0f01cc  │    ''    │  39  │ 104 │    true     │       true       │
│  0f0c90  │    ''    │  39  │ 138 │    true     │       true       │ /* Recent CRBUS leaking instruction, 90 is the next NOP. */
│   0f0e   │ 'femms'  │  52  │ 101 │    false    │       true       │ /* Recent CRBUS leaking instruction */
│  0faed0  │    ''    │  39  │ 87  │    true     │       true       │
│  0fc790  │    ''    │  39  │ 87  │    true     │       true       │
│ 660f3883 │    ''    │  39  │ 81  │    true     │       true       │
│ 660f3860 │    ''    │  39  │ 87  │    true     │       true       │
│ 660f3a80 │    ''    │  39  │ 87  │    true     │       true       │
│ f30f7890 │    ''    │  39  │ 87  │    true     │       true       │
│ f30f7990 │    ''    │  39  │ 87  │    true     │       true       │
│ f30fe7fc │    ''    │  73  │ 80  │    false    │       true       │
└──────────┴──────────┴──────┴─────┴─────────────┴──────────────────┘

Вопреки всеобщему мнению mov cr2, reg не сериализированы.

┌──────────┬────────────────┬──────┬─────┬─────────────┬──────────────────┐
│ (index)  │    decoding    │ mits │ ms  │ serializing │ speculationFence │
├──────────┼────────────────┼──────┼─────┼─────────────┼──────────────────┤
│  0f2090  │ 'mov rax, cr2' │  51  │ 83  │    false    │      false       │
│  0f2098  │ 'mov rax, cr3' │  51  │ 88  │    false    │      false       │
│  0f2080  │ 'mov rax, cr0' │  51  │ 85  │    false    │      false       │
│  0f2290  │ 'mov cr2, rax' │  51  │ 87  │    false    │       true       │ /* ! */
│  0f2298  │ 'mov cr3, rax' │  39  │ 161 │    true     │       true       │
│  0f2299  │ 'mov cr3, rcx' │  39  │ 151 │    true     │       true       │
│  0f229b  │ 'mov cr3, rbx' │  39  │ 155 │    true     │       true       │
│  0f2280  │ 'mov cr0, rax' │  39  │ 110 │    true     │       true       │
│  0f2281  │ 'mov cr0, rcx' │  39  │ 153 │    true     │       true       │
│  0f22a0  │ 'mov cr4, rax' │  39  │ 103 │    true     │       true       │
│  0f22a1  │ 'mov cr4, rcx' │  39  │ 120 │    true     │       true       │
│  0f22a4  │ 'mov cr4, rsp' │  39  │ 104 │    true     │       true       │
│ 660f22a4 │ 'mov cr4, rsp' │  39  │ 103 │    true     │       true       │
│ f20f22a4 │ 'mov cr4, rsp' │  39  │ 103 │    true     │       true       │
│ f30f22a4 │ 'mov cr4, rsp' │  39  │ 103 │    true     │       true       │
└──────────┴────────────────┴──────┴─────┴─────────────┴──────────────────┘

Несмотря на отсутствие проверки CPL для int imm8int1 имеет больше логики в микрокоде.

┌─────────┬────────────┬──────┬─────┬─────────────┬──────────────────┐
│ (index) │  decoding  │ mits │ ms  │ serializing │ speculationFence │
├─────────┼────────────┼──────┼─────┼─────────────┼──────────────────┤
│   cc    │  'int3 '   │  39  │ 94  │    true     │       true       │
│  cd90   │ 'int 0x90' │  39  │ 91  │    true     │       true       │
│   f1    │  'int1 '   │  39  │ 112 │    true     │       true       │
└─────────┴────────────┴──────┴─────┴─────────────┴──────────────────┘

mov ss – это заграждение для спекуляций, а cli – нет. lss является заграждением для спекуляций, а lgs не является.

┌─────────┬────────────────────────────────────────────────┬──────┬────┬─────────────┬──────────────────┐
│ (index) │                    decoding                    │ mits │ ms │ serializing │ speculationFence │
├─────────┼────────────────────────────────────────────────┼──────┼────┼─────────────┼──────────────────┤
│  8890   │      'mov byte ptr [rax-0x6f6f6f70], dl'       │  49  │ 80 │    false    │      false       │
│  8990   │     'mov dword ptr [rax-0x6f6f6f70], edx'      │  49  │ 80 │    false    │      false       │
│ 668890  │      'mov byte ptr [rax-0x6f6f6f70], dl'       │  48  │ 80 │    false    │      false       │
│  8a90   │      'mov dl, byte ptr [rax-0x6f6f6f70]'       │  49  │ 80 │    false    │      false       │
│  8b90   │     'mov edx, dword ptr [rax-0x6f6f6f70]'      │  49  │ 80 │    false    │      false       │
│  8c90   │      'mov word ptr [rax-0x6f6f6f70], ss'       │  50  │ 80 │    false    │      false       │
│  8e90   │      'mov ss, word ptr [rax-0x6f6f6f70]'       │  42  │ 86 │    true     │       true       │ /* ! */
│   fa    │                     'cli '                     │  56  │ 80 │    false    │      false       │
│ 0fb290  │        'lss edx, ptr [rax-0x6f6f6f70]'         │  39  │ 89 │    true     │       true       │ /* ! */
│ 0fb590  │        'lgs edx, ptr [rax-0x6f6f6f70]'         │  47  │ 89 │    true     │      false       │
└─────────┴────────────────────────────────────────────────┴──────┴────┴─────────────┴──────────────────┘

 


Что ещё интересного есть в блоге Cloud4Y

→ Частые ошибки в настройках Nginx, из-за которых веб-сервер становится уязвимым

→ Пароль как крестраж: ещё один способ защитить свои учётные данные

→ Облачная кухня: готовим данные для мониторинга с помощью vCloud API и скороварки

→ Подготовка шаблона vApp тестовой среды VMware vCenter + ESXi

→ Почему ваш бизнес может быть разрушен

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


 

Источник

x86, операционные коды, пасхалки, процессоры

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