Недокументированные операционные коды процессора 8086

Original author: Michal Necasek
  • Translation
Недавно, когда я изучал досовые бут-секторы, я обнаружил довольно загадочную вещь. DOS распознает, является ли бут-сектор загрузочным, по нескольким критерям, и один из них (странно, да?) — есть ли в первых двух байтах jump-инструкция, которая указывает, соответственно, куда-нибудь за BPB. В модуле MSDISK.INC из MS-DOS 3.21 OAK как раз и происходят такие проверки. Модуль проверяет, начинается ли BPB с прыжка, при этом за верную jump-инструкцию он принимает опкоды EBh (short JMP), E9h (JMP), или 69h. Стоп. 69? Это же IMUL!
Не-а, не IMUL. Комментарий в коде утверждает, что это «direct jump»:

   cmp   byte ptr cs:[DiskSector],069H  ; Is it a direct jump?
   je    Check_Signature                ; don't need to find a NOP
   cmp   byte ptr cs:[DiskSector],0E9H  ; DOS 2.0 jump?
   je    Check_Signature                ; no need for NOP
   cmp   byte ptr cs:[DiskSector],0EBH  ; How about a short jump.
   jne   BadDisk

Хорошо, вот только про 69h в документации 8086 ничего не сказано! Да, в 8186 и далее — это опкод IMUL, но то, что 8086 умеет IMUL — крайне маловероятно. Тем более, комментарий ясно указывает, что это прыжок.

Может быть, на процессорах 8086 69h ведёт себя как прыжок? Вопрос хороший, вот только информации по этому поводу почти нет. Я подумал, что кто-нибудь из олдовой хакерской тусовки ранних PC наверняка бы точно выяснил, что именно делают незадокументированные инструкции, но я ошибся — довольно глубокий поиск по всему интернету не дал никаких вразумительных результатов, и даже книжка авторства Frank van Gilluwe «Undocumented PC», в которой довольно много сказано про неописанные в документации операционные коды, помогла несильно. Наверняка, когда-нибудь, где-то кто-то что-то, да опубликовал...

Сначала хорошо бы отметить, что начиная с 80186, в отличие от 8086/8088, попытка исполнения неправильного (недокументированного) опкода вызовет соответствующее исключение (сейчас обозначается как #UD). То есть сейчас опкод либо является стандартным и исполняется, либо срабатывает #UD, и, соответственно, нет никакого смысла пытаться исполнять что-либо, кроме описанных в документации инструкций. Ну разумеется тут есть исключения: D6h и F1h — Intel ясно дал понять, что стандарты его не волнуют… но вернёмся опять к 8086.

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

Существует несколько опкодов 8086, которые не описаны в документации Intel, но поведение которых было давно установлено:
  • POP CS (0Fh). Довольно просто угадать, что он делает. Смотрите: 06h (push es) — 07h (pop es), 16h (push ss) — 17h (pop ss), 1Eh (push ds) — 1Fh (pop ds), 0Eh (push cs) — и, соответственно, 0Fh (pop cs). Эта инструкция, хоть и работает, но почти бесполезна. Потому-то она никогда не была документирована, и, начиная с 80286, опкоду 0Fh соответствует другая инструкция.
  • MOV CS (8Eh). То же, что и с POP CS — довольно просто угадать, и опять же — бесполезна.
  • SETALC/SALC (D6h). Делает то же, что и SBB AL, AL, но не трогает флаги, то есть в AL будет или FFh, или 0 — в зависимости от CF. Эта инструкция присутствует в современных центральных процессорах Intel, но она всё равно незадокументирована.

Три инструкции — хорошо, но для 8086 также неопределены 60h — 6Fh (в числе которых наша 69h), C0h — C1h, C8h — C9h, и F1h. Также есть несколько пробелов в opcode extensions (это когда инструкция описывается не только первым байтом, но и частью бит в следующим за ним байтом), особенно — группа GRP4, вот.

Чтобы прояснить это дело, Raúl Gutiérrez Sanz взял свой Siemens 8086-2 (произведён в 1990) и разобрался, что именно происходит при исполнении опкодов, неописанных в официальной документации.



Этот процессор произведён под лицензией Intel, и, без каких-либо подозрений, он функционально не отличается от оригинальных процессоров Intel.

Разгадка


К сожалению, поведение большинства недокументированных опкодов весьма прозаично. Никаких магических секретных инструкций — эти инструкции — всего лишь алиасы к другим, документированным. Другими словами, процессор 8086, при декодировании таких инструкций, просто игнорирует некоторые биты слова, и всё :(

Незадокументированные опкоды 60h — 6Fh соответствуют тем же самым инструкциям, что и документированные 70h — 7Fh — процессор игнорирует четвёртый бит опкода, то есть 60h соответствует 70h, 61h — 71h, ..., 6Eh — 7Eh, 6Fh — 7Fh, и делают они ровно то же самое. Довольно разумно, особенно — для медленного 8086 — никакого потенциально опасного поведения, да и микрокода лишнего писать не нужно. С другой стороны, эти изначально незадокументированные опкоды потом можно назначить для других инструкций в процессорах нового поколения, сохраняя обратную совместимость (ведь предполагается, что неописанные опкоды никто в коде использовать не будет).

Так, в диапазоне C0h — CFh ЦПУ игнорирует первый бит слова: C0h означает C2h, C1h — C3h, C8h — CAh, а C9h — CBh.

Поведение опкода F1h в настоящий момент остаётся тайной. На новых процессорах F1h — недокументированная инструкция ICEBP, или INT1. В документации Intel она не описана, хотя у AMD присутствует.

На 8088, F1h скорее даже не инструкция, а префикс. Мы определили это, пошагово пройдя по последовательности, состоящей из повторяющихся опкодов F1h и ещё какого-нибудь документированного. Микропроцессор перешагивает через всю последовательность, что фактически доказывает, что F1h — некоторого рода префикс.
Думается, что F1h — это алиас к F0h — префикс LOCK. Как-нибудь доказать или опровергнуть это у нас не получилось, потому что требуется какая-нибудь железяка, способная отслеживать сигнал LOCK# на шине.

Вернёмся к бут-сектору. Мог ли бут-сектор начинаться с 69h? На 8088, скорее всего, это могло бы сработать. Если опкод 69h — это алиас к инструкции JNS, то, если знаковый флаг не установлен, то будет выполнен short jump. По крайней мере на IBM PC состояние флагов на начало исполнения бут-сектора предсказуемо, таким образом 69h мог бы сработать.
Но в чём смысл ставить в начало бут-сектора 69h и кому это могло бы понадобиться? Хороший вопрос. На данный момент мне неизвестно, существовали ли какие-нибудь бут-секторы DOS, начинающиеся с опкода 69h. Для чего кому-то надо было использовать недокументированные инструкции совершенно непонятно. Может, неправильная реализация системы защиты от копирования… но факт в том, что DOS отдельно проверяет начало бут-сектора на наличие опкода 69h, и это совершенно точно доказывает, что такие бут-секторы определённо существовали. Идеи?

P. S. Есть ещё несколько недокументированных опкодов в диапазоне расширенных кодов! Подробнее об этом в следующий раз...
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 25

    +11
    Инструкция 0x4e 0x53 0x41 0x20 переводит процессор в привилегированный режим и отключает любые проверки на ring0, ring -1 и т.д.
      +1
      А вы по памяти коды символов писали или в таблицу подсматривали?
        +1
        А вы их по памяти читали? :)
        Что же должна делать инструкция 4Bh 47h 42h 20h…
          +2
          Вроде как опкоды редко когда попадали в область английских букв. Там чаще русские и спецсимволы…
            +2
            Ну заглавную A и пробел в конце я глазами разглядел, например, их коды сложно забыть.
              +1
              HIEW подсказывает, что эта последовательность байтов соответствует этому коду:
              dec       bx
              inc       di
              inc       dx
              and       [bx][si],al
              Что это делает — честно, не в курсе. Ассемблер один раз порывался выучить, но быстро сдулся :(
                +1
                ээээм… KGB? мда… сарказм детекшн фейлид :(
                  +1
                  Да там ничего сложного нет. Любой ассемблер — суть представление инструкций в человекопонятном виде. А процессор умеет делать довольно примитивные вещи: сложить два числа в регистрах, умножить их, считать число из памяти в регистр — в этом духе. Так что изучайте, лишним не будет. Не обязательно углубляться, достаточно научиться писать простенькие циклы и вызывать процедуры — для понимания того, что сгенерил компилятор из С или C++, хватит вполне. Через месяц-два изучения сможете уже прослеживать корелляцию между своей писаниной и выхлопом компилятора, и соответственно понимать, «сколько стоят» те или иные фичи в ЯП.
                0
                echo "...."|hexdump -C
                0
                «NSA „
                sarcasm?
                  –2
                  Похоже на бред.
                  Если вы говорите про i386 — то это пара обычных опкодов которые не делают ничего магического.
                  Если говорите про что-то более старое (8086, итд.) — то это бред тем более, поскольку там нет никаких привелегированных режимов.
                    +10
                    А, вкурил, типа шутка.
                  +18
                  В/О FПРГ Fx2 Fx2 × Fx2 ХПС Сх С/П FАВТ В/О 1 ВП 22 В↑ 1 ВП 50 С/П
                    +1
                    Когда-то были популярны знаменитые Interrupt List: www.cs.cmu.edu/~ralf/files.html
                    Там же где-то были и недокументированные команды.
                      +7
                      Стоит отметить, что Interrup.lst и TechHelp появились до создания интернета.
                      А по сути топика — автор и переводчик модолцы… Но, черт возьми! Закопайте же стюардессу!
                        0
                        хорошее было время, ага :)
                        ну, стюардессу сейчас не закапывает не только ТС)
                      0
                      Мне вспоминается, как я когда-то давно делал таблицу команд процессора 80x86 по книге Зубкова «Ассмеблер для dos windows и unix». Тогда тоже обратил внимание на «ICEPB» 0xF1. Но эта команда была «на виду», ее недокументированность сложно было не заметить. А вообще таких команд может быть неограниченное количество — ведь кроме «основой» таблицы, есть команды начинающиеся с 0x0F — та таблица содержит еще много «дырок», есть команды типа FPU устроенные аналогичным образом и т.п. Наконец, какая-нибудь редкоисползуемая или вообще недокументированная/незадействованная команда может делать что-то не то при определенном сочетании данных в регистрах.
                        +1
                        Начиная с PentiumPro все чудеса сошли на нет. Всё просто: так то, что как современные процессоры реально исполняют имеет мало общего с тем, что пишет программист (даже на ассемблере), то всякие подобные чудеса отсекаются на весьма ранних этапах.
                        +1
                        Я только вот что не пойму. Если 0x69 — недокументированный алиас к 0x79, то почему 0x79 не проверяется? Вот так недокументированные возможности и становятся внезапным стандартом…
                          +3
                          Напомнило передачу о тетеньке, которая расшифровывала таинственные письмена, распечатанные принтером. Но статья любопытная.
                            +1
                            Нашел упоминаемый файл (MSDISK.INC). Он идеально документирован, увлекательное чтение.

                            У меня, кстати, случилось крушение картины мира: оказывается, MS-DOS 3.30 не проверяла сигнатуру 0AA55h в бут-секторе!
                              0
                              Когда-то давно читал, что только на 8086 (насчет 8088 не уверен) можно было выполнить mov cs,ax. И это был бы межсегментный переход.
                              Даже был какой-то вирус, который работал только на 8086 из-за использования этой возможности.
                                0
                                  +5
                                  Странно, что так сложно было :) раньше была такая книга «Ассемблер PC/XT». Она, кстати, и в СССР переводная была. Там коды команд описывались с точки зрения дешифратора команд и внутренних состояний процессора. Там же довольно регулярная система команд была. Отлично было видно поведение всех «недокументированных» команд. Плюс, они перечислялись в конце книги. Было ещё несколько подобных книг. И по Z80 подобное было. Просто потом по мере развития архитектуры большая часть документации была переписана и формализована. Некоторые стали legacy либо просто опущены. А тогда во многой литературе описывались не только недокументированные коды, но и отличия между процессорами разных производителей. 8086 отличался от 8080, а Intel от NEC и AMD. И при разработке всякого рода низкоуровневого софта это учитывалось :)
                                    0
                                    Можно насладится реализацией x86 в исходниках (не Intel конечно)
                                    www.ht-lab.com/freecores/cpu8086/cpu86.html
                                    zet.aluzina.org/index.php/Zet_processor

                                    Еще было на Хабре
                                    habrahabr.ru/post/146160/

                                    Only users with full accounts can post comments. Log in, please.