Дневники чайника. Чтива 0, виток1

Завели хмыри в засаду
И пытают столько лет
(Юрий Шевчук)

Вступление в суть

Если вы открыли эту главу сознательно, я очень рад. Значит, уроки действительно пошли вам на пользу. Ну а если вы случайно сюда заглянули и до этого с практикой языка Ассемблер не сталкивались - немедленно закройте эту бяку! И отправляйтесь на виток0.

- Матрос, поздравляю, экзамен сдан!

...

- Скоро, наверное, придёт назначение на нового офицера ;).

...

- Да, тут прибывает спец-бот. Они передали, что хотят с тобой поговорить.

...

- Иди к первому шлюзу.

...

Шлюз1. Сквозь клубящийся дым вперёд выступил невысокий гуманоид. Впрочем, невысокий он только по росту, а по званию он Адмирал Военно-Космического Флота. "Выше рангом только Бог", - послышалось из тёмного угла, а перед этим кто-то чуть слышно присвистнул за спиной.

- Молодой человек, на корабль пройдите. Вас там ждут.

...

Сознание отстало от шагов, в голове закружились колючие звёзды, всё куда-то поплыло. Последний шаг на корабль - и бездна.

- Юноша, приходите в себя, нам есть о чём поговорить.

...

- Я представляю Школу, этого пока достаточно.

...

- Вам делают предложение, но вы вправе отказаться.

...

- Возможно, вы подойдёте в ученики... Возможно.

...

- Школа заинтересована в ваших способностях.

...

- Мы давно следим за вами.

...

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

...

- Х-м, некоторые сгорают.

...

- Ученики Школы получают открытый доступ на все уровни информации, по мере обучения.

...

- Все - это значит и на те, о которых запрещено даже говорить простым смертным.

...

- Нужно сделать выбор сейчас.

...

- Вы согласны, юноша?

И опять туман, и опять дорога.

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

Занятия начались практически сразу, то есть ещё на корабле.

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



Мне известны только два способа обучения - интерес и заинтересованность.

С первым всё понятно - человек хочет разобраться, ему интересно, как устроен мир.

Но вот второй способ всегда удивляет.

- А ну быстро выучи таблицу!

- Как, ты ещё не выучил?!

- Тогда 10, нет 20 розг!

И тут уж возникнет заинтересованность в этой таблице. Ну а потом, возможно, тебе объяснят, зачем ты её учил.

И если вы думаете, что силовые методы остались в прошлом, вынужден вас огорчить.

Человечество ещё очень долго (а может быть, всегда) будет больше использовать кнут, чем пряник.

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

Всё это не имеет никакого отношения к жажде познания и великим тайнам бытия.

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

Однако сегодня я отступлю от своего правила "практика > теория". Нам придётся пройти одну главу из "убойной" теории.

Эта глава была полностью переписана 5 раз... Уже 6... Нет 7... Фу-ф, кажется, получилось!

Всё дело в том, что я захотел приблизить свои уроки к научному труду.

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

Я встречал людей, которые делают эту работу на уровне интуиции, не особо задумываясь над сортировкой.

Видел и тех, кто сортирует книги по алфавиту или по цвету корешка :), но мы-то с вами - хакеры и так делать не будем.

Попытка найти систему

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

В первый же день поисков полезных определений я столкнулся с неразрешимой проблемой перевода (английский - русский, Ассемблер - машинный язык, и даже глубже). За год я так и не смог найти учебника или доки, в которой всё было бы структурировано от корней и до макушки. Самый определённый источник среди русскоязычных, который я нашёл, - "Специальный справочник" Юрова. Его посоветовал мне Седой, и я поначалу, мягко выражаясь, не восхитился им.

В нём тоже есть исключения, ошибки и, главное, он очень сложный для новичка. Однако лучше пока нет.

На английском основной источник информации - документация Интел (ссылка ниже). Она самым оперативным способом представляет нам сведения о командах процессора. Но там нет правильных определений, только "конкретная" инфа, по "конкретной" команде.

Всё время мы используем слова "команда", "машинная команда", "команда процессора", "команда Асма", "данные" и, наконец, сам Ассемблер, но вы, возможно, даже не представляете, насколько сложно их определить.

Большинство из этих понятий имеет смысл объяснять интуитивно.

Давайте попробуем определить понятие данные.

Мы с этого начали наши уроки, но теперь вернёмся на новом уровне (как я и обещал - дежа вю :).

Данные в Асме для x86

Интуитивно вся программа делится на "данные" и "машинные команды".

Но дело в том, что существует понятие машина фон Неймана. Один из её принципов - "неразличимость команд и данных в памяти".

То есть мы здесь будем говорить лишь о представлении.

Попытайтесь выделить команды и данные в этом примере:

prax10a.com

00000000: 90         nop
00000001: FE060000   inc  b,[0000]
00000005: FE060500   inc  b,[0005]
00000009: CD20       int  020

Вроде бы, на первый взгляд, это всё команды. Но давайте разбирать момент их выполнения.


00000000: 90         nop

Ну, тут вопросов пока нет. Результативное действие - ничего.


00000001: FE060000   inc  b,[0000]

Однако эта команда превращает предыдущую в данные.

Действие: увеличить на 1, байт по адресу ноль.

То есть на момент выполнения байт №0 был командой. Но уже в момент выполнения следующей команды байт №0 стал данными.


00000005: FE060500   inc  b,[0005]

Ну а здесь наступает полный финиш. Кажется что такая команда выполняет "самоизменение". Однако это не совсем так.

Действие: увеличить на 1 первый байт из этой команды в памяти.

И она прекрасно справляется с этой задачей.

Подобная команда не вызовет ошибки ни в DOS, ни в Win (при условии нужных атрибутов у секции кода в файле).


00000009: CD20       int  020

Ну и после всего этого кошмара пример удачно завершится.

Для того чтобы объяснить понятие данные, нам нужно взять конкретный момент конкретной команды. И только тогда появляется определённость - что есть данные и что есть команда.

Когда же мы говорим обобщённо о данных программы или о машинном коде, мы делаем это интуитивно и возможно ошибочно!

Так что отладчик ещё раз доказывает свою незаменимость. Ведь динамику можно проследить только так. Хотя пример Мюнхгаузена (строка 05) понять не поможет даже отладчик. Тут нужно залезть в шкуру процессора. Он забирает команду, в которой есть указание на адрес в памяти, и ему всё равно, какой это будет адрес, лишь бы он был доступен для чтения/изменения.

Однако в целом, грубо, мы можем сказать, что данные - это всё за вычетом машинных команд (машинного кода).

И ещё очень важно понять, что в рамках наших уроков часть маш.команды - не данные.

Пример:

prax01.com

00000000: B409       mov ah,009
...

В этой строке нет данных, так как значение 09 является частью этой же команды.

То есть в процессор поступит целиком B409 - и всё это машинная команда.

Далее, через несколько строк, мы увидим:

...
0000000D: 47         inc   di
0000000E: 6F         outsw
0000000F: 6F         outsw
00000010: 64204461   and   fs:[si][61],al
00000014: 7921       jns   000000037
00000016: 24         and   al,000

Дизассемблер Hiew'a не способен проанализировать байты настолько, чтобы понять, где здесь данные.

Но мы-то с вами знаем, что по ходу выполнения программы ни один из этих байтов не поступит в процессор как команда, значит, мы выделяем эти байты как данные. При условии, что не было сбоя :).

Я надеюсь, вы поняли сложность процесса анализа данных и кода. Однозначно сказать заранее: "это - данные, а это - код", - нельзя.

Но, как правило, в реальных программах код и изменяемые данные разнесены подальше друг от друга (в разные секции, например). А "кашу" из кода и данных мы чаще всего видим в защищённых программах.

Пару слов о машинном коде

Совокупность машинных команд. :)

Машинная команда

Команда процессора, она же машинная команда (machine command, machine level command), имеет много определений, но все они на практике малополезны. Вот одно из них:

Закодированное по определённым правилам указание процессору на выполнение некоторых действий (В.И. Юров. Спец. Справ.).

И это определение ещё довольно конкретное по сравнению с другими.

Что оно нам даёт?

Во-первых, мы узнаём, что правила кодирования маш.команд определены. Уже хорошо. Хотя надо сказать, что правила эти очень сложные.

Во-вторых, понятно что маш.команда может просить выполнить целую последовательность действий.

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

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

Но всё это неконкретно.

А вот что конкретно.

Одна команда процессора отвечает на три вопроса:

Так пишут Юров и The Svin.

Вывод: лучше всего команда процессора соотносится с понятием предложение.

Маш.команды IA-32 могут быть разного размера (от 1 до 15 байт) - как, например, обычное предложение в человеческом языке может состоять из одного слова и более.

Команда процессора может состоять из следующих полей:
Префиксыопкодбайт mode r/mбайт sibсмещение в команденепосредственный операнд

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

Такая таблица не совсем корректна. Во-первых, код операции может занимать и часть постбайта (он же mode r/m). Во-вторых, префиксы считать таким полем тоже неправильно. Да и вообще, толку от этой таблицы не слишком много, особенно для чайника. :)

Более подробную информацию об устройстве машинных команд смотри в документации на IA-32 от Intel, том второй. Номера для поиска:
253665?? - том 1-й,
253666?? и 253667?? - том 2-й, глава "Instruction Format"
253668?? - том 3-й.

Интел в своей документации использует термин instruction, но что это такое, точно определить тоже невозможно.

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

Я советую приобрести учебник Юрова и его "Специальный справочник".

Ещё очень рекомендую заглянуть на Wasm, в раздел "Образовательные программы". Там вы найдёте замечательные обучалки и некоторую информацию, за что нужно благодарить The Svin'a. Он помог многим быстро и наглядно уловить содержание машинных команд.

Трудности перевода

Кто-то спросит, зачем нам вообще разбираться в этих машинных командах, когда есть Ассемблер и его команды.

Ответ прост. На каждом этапе перевода из языка в язык накапливаются искажения. В некоторых случаях эти искажения приводят к ошибкам. И таких случаев очень много.

С моей точки зрения, программист - это учёный, и у него есть наука.

Любая точная наука начинается с определения её рамок (общего множества) и заканчивается описанием простейшей частицы (элемента этого множества).

Допустим, мы приняли за элемент команды Асма. Тогда нужно однозначно переводить их в команды процессора.

Пример:

Команда Асма      Машинная команда

Режим 32 бита
inc  EAX          40h
push EAX          50h
int 3             CCh

Режим 16 бит
inc  AX           40h
push AX           50h
int 3             CCh

Режим процессора мы сейчас обсуждать не будем, а вот команды...

Здесь всё правильно, логика железная. До тех пор пока мы не встретили такие байты:

FFC0 
FFF0
CD03

Посмотрим на них в Hiew'e:

Адреса    маш.команды     Команды Асма
00000000: FFC0            inc         ax
00000002: FFF0            push        ax
00000004: CD03            int         003

Что же получается? Разные машинные команды могут переводиться в одну конкретную команду Асма.

Казалось бы: ну и фиг с ним, действие-то одно. А вот и нет! Из-за того, что Intel и MicroSoft иногда считают за элемент команду Ассемблера, появляются всякие бяки. И тут могут споткнуться даже самые "крутые" программисты.

Команда Ассемблера "int 3" теоретически могла быть записана не только байтом CCh, но и двумя байтами CD03.

Однако int 3, переведённая в CD03, теперь ведёт себя не так, как CCh. Вот вам и дырка.

Посмотрите в поисковиках строку вроде "int 03 CD03 CC", много интересного можно увидеть.

Во всех нормальных документациях подчёркивается, что int 3 нужно переводить в CCh, так как все служебные программы ждут именно этого, и даже сам процессор ждёт CCh. А двухбайтная маш.команда является уже как бы "неправильной". Но она есть, и её можно использовать.

Из-за ошибки систематизации практически появилась новая команда - "int 03".

Единого написания этих команд пока нет. В FASM'e - "int3" и "int 3", в Hiew'e - "int 3" и "int 03". А в MASM'e - только int 3 (инструкция ССh), второй команды по имени нет, как и нескольких других :).

Ну а первые две пары маш.команд из этого примера совпадают и в действиях, и в переводе на Ассемблер... До следующей ошибки систематизации. :)

Это всё замечательно, но хорошо бы ещё понять, что такое Ассемблер.

Ассемблер

В моих уроках Асм - это язык.

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

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

Но вернёмся к Асму. Существует ещё такая вещь как программа-ассемблер. Такие программы выполняют преобразования символьных команд и данных в бинарные файлы.

Я буду писать слово Ассемблер с большой буквы не столько из уважения, сколько от подхода. И уж вовсе не из-за англоподобности. Ведь в данном тексте Ассемблер - вполне конкретный объект и у него есть имя... Но это уже семантика :). В русском языке по правилам, наверное, надо было бы писать ассемблер с маленькой буквы - так же, как "русский, английский, немецкий...".

Короче, договорились: мы пишем слово Ассемблер с большой буквы и однозначно подразумеваем язык Ассемблер, а не программу-ассемблер.



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

Также и у программ-ассемблеров и дизассемблеров есть свои начертания.

Пример:

4 команды процессора

Адреса    маш.команды   Команды Асма
          
00000000: C3            retn
00000001: CB            retf
00000002: E94B00        jmp   000000050
00000005: EB49          jmps  000000050

Да-да, это всего лишь 2 разные команды Асма!

Пример взят из Hiew'a, этот hex-редактор заточен под нужды самых низкоуровневых программистов (не считая создателей проца :), поэтому мнемоническое представление здесь подчёркивает различия в маш.командах, но это всего лишь диалект, и он не может быть базой языка. В другом дизассемблере мнемоники будут чуть другие, а синтаксис MASM'a вообще далёк от этого написания - как сегодняшний русский от языка XVII века.

Так что jmps в справочниках вы не найдёте, а команда с именем jmp будет включать в себя все маш.команды с опкодами E9h,EBh и ещё пару инструкций, действие которых - "безусловный переход". И опять: есть команда Ассемблера ret, а разными будут машинные команды. Хотя это вопрос систематизации и лингвистики.

Тогда про команду Ассемблера поподробнее

(в тексте просто команда)

Команда Ассемблера объединяет несколько команд процессора общим именем (именами).

К сожалению, это так и есть. Бывают случаи, когда у одной команды Асма есть несколько написаний (и это не диалекты).

Пример я прочёл у The Svin'a:

Если в пустом файле при помощи Hiew'а мы введём строчку "xchg ax,ax", на экране появится одинокий NOP.

Так получилось, потому что изначально коды от 90h до 97h переводились в команду Асма "XCHG" (обмен). Но поскольку обмен содержимого регистра с самим собой - это пустое действие, код 90h стали переводить в NOP.

То есть для нас - людей важно знать, какое действие будет совершаться, что и даёт команда Асма.

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

Ну, а процессору совершенно по барабану: будет его команда входить в группу mov или xor. Ему важнее: что, куда, и как moov'ать. И тут машинные команды отражают суть лучше, чем команда Ассемблера.

Теперь узнаем, из чего может состоять команда Асма.

имя операнд1, операнд2, операнд3

Где имя - мнемоника команды.

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

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




Что такое Ассемблер?

согласно ГОСТ 19781-90:

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

Я считаю так:

Символический язык программирования, представляющий команды процессора (и не более!).


Что такое программа?

Согласно ГОСТ 19781-90:

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

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


Что мы будем называть данными?

Данные - любая цифровая информация за вычетом машинного кода.

Почти всё, что могут хранить байты и биты, регистры и что-нибудь ещё :). Файл, который вы сейчас читаете, - тоже данные. В это понятие мы не включаем только машинный код.

Выходит, непосредственные значения в команде - это не данные, а просто часть команды процессора.


Что такое машинный код?

Совокупность машинных команд.


Что такое машинная команда?

Закодированное по определённым правилам указание процессору на выполнение некоторых действий.


Что такое команда Ассемблера?

Символьное представление машинных команд. Оно объединяет в группы маш.команды под общей мнемоникой по "человеческим" понятиям.


Мнемоника?

Зарезервированное символическое имя.

Мнемоника команды - неотъемлемая часть любой команды Ассемблера, её имя.

Мнемоника регистра - имя регистра.


Операнды?

Предмет, с (или над) которым производится действие.

Их в команде Асма может и не быть или они могут задаваться неявно. То есть некоторые команды всегда используют определённый регистр в качестве приёмника или источника.

Bitfry

<<предыдущая глава     следующая глава>>

Вернуться на главную

Hosted by uCoz