Погружение в ассемблер. Учимся работать с памятью — «Хакер»

Найти:

- Взлом
- Приватность
- Трюки
- Кодинг
- Админ
- Geek
- Обучение
- Магазин

- Windows
- Linux
- Android
- Железо
- Мессенджеры
- Утечки
- Коронавирус
- Все выпуски «Хакера»

### Содержание статьи

- Другие статьи курса
- Знакомимся с сегментными регистрами
- Как ПК распределяет память
- Прямой доступ к видеопамяти в текстовом режиме
- Рисуем психоделические круги
- Выводы

В этой статье я познакомлю тебя с сегментной адресацией и сегментными регистрами, расскажу, как распределяется первый мегабайт оперативной памяти компьютера, затем мы обсудим получение прямого доступа к видеопамяти в текстовом режиме, а самое главное — поностальгируем по фильму «Хакер»! Под конец напишем психоделическую программу, которой позавидовали бы его герои.

### Другие статьи курса

- Зачем учить ассемблер в 2020 году
- Делаем первые шаги в освоении асма
- Осваиваем арифметические инструкции
- Как работают переменные, режимы адресации, инструкции условного перехода
- Работаем с большими числами и делаем сложные математические вычисления
- Сокращаем размер программы
- Пишем клон игры Flappy Bird, который уместится в бутсектор
- Пишем бейсик и умещаем его в 512 байт

## Знакомимся с сегментными регистрами

Для начала разберемся, что такое сегментные регистры. Процессор 8088 умеет адресовать один мегабайт оперативной памяти, несмотря на то что регистры у него 16-битные. В норме 16 битами можно адресовать только 64 Кбайт. И как же тогда выкручивается 8088? Как ему удается адресовать целый мегабайт? Для этого в 8088 есть сегментные регистры! Четыре сегментных регистра: `CS`, `ES`, `DS` и `SS`, по 16 бит каждый. У каждого из этих регистров есть свое назначение.

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

Регистры `DS` и `ES` указывают на сегмент данных. К этим регистрам процессор обращается, когда выполняемая инструкция считывает или сохраняет данные. Имей в виду: `DS` используется чаще, чем `ES`. `ES` обычно вступает в игру, когда ты обрабатываешь массивы данных, индексируя их регистром `DI`.

Регистр `SS` указывает на сегмент стека. К этому регистру процессор обращается, когда выполняет инструкции, взаимодействующие со стеком: `push`, `pop`, `call` и `ret`.

Значения, хранимые в сегментных регистрах, — это базовый адрес, поделенный на `16`. Если в `CS` записано значение `0x0000`, процессор будет считывать инструкции из области памяти `0x00000 — 0x0FFFF`. Если в регистре `CS` записано значение `0x1000`, то процессор будет считывать инструкции из области памяти `0x10000 — 0x1FFFF`.

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

Поместить какое-то число в сегментные регистры напрямую нельзя. Для этого надо:

- либо сначала записать число в какой-нибудь регистр и уже этот регистр присвоить сегментному регистру;
- либо воспользоваться парой инструкций `push/pop`;
- либо воспользоваться инструкциями `lds/les`.

Процессор 8088 настолько лоялен, что позволит тебе даже вот такую инструкцию: `pop cs`. Но только имей в виду, что это нарушит поток выполнения инструкций. В более новых процессорах, начиная с 80286, такую диверсию сделать уже не получится. И слава богу!

Ну вот вроде бы и все, что тебе надо знать о сегментных регистрах. Теперь ты с их помощью (в основном с помощью регистров `DS` и `ES`) можешь получать доступ ко всему первому мегабайту памяти ПК.

## Как ПК распределяет память

Понимание того, как распределяется память в ПК, поможет тебе делать разные интересные вещи.

Начало загрузки у всех компьютеров одинаковое: они сначала переходят в текстовый цветной режим `80x25`. К видеопамяти экрана, который работает в таком режиме, можно обращаться напрямую, через вот этот диапазон адресов: `0xB8000 — 0xB8FFF`.

Первый байт диапазона — это первый символ в верхнем левом углу экрана. Второй байт — это цвет фона под символом и цвет самого символа. Затем (третьим байтом) идет второй символ. И так для всех 25 строк по 80 символов каждая.

## INFO

Исторический факт. IBM PC образца 1981 года поставлялся в двух модификациях: с монохромным режимом и с цветным режимом. Тогдашним разработчикам игрушек приходилось придумывать разные эвристики, чтобы понять, какая у компьютера графика — монохромная или цветная. Несколько старых добрых игрушек для этого писали какую-нибудь цифру по адресу `0xB8000` и считывали ее обратно, чтобы узнать, в каком режиме сейчас идет работа — в цветном или в монохромном.

## Прямой доступ к видеопамяти в текстовом режиме

Перед тем как мы сможем напрямую обращаться к видеопамяти экрана, то есть без использования сервисов BIOS, надо задать нужный нам видеорежим.

В этом режиме видеопамять экрана доступна по адресу `0xB8000`. Теперь ты можешь легко получить доступ к ней. Примерно так, как в коде ниже.

Обрати внимание, что после того, как ты выполнил этот кусок кода, ты не сможешь обращаться к переменным, которые сохраняешь в сегменте кода, как мы это делали в примерах из прошлых уроков. Потому что `DS` и `ES` теперь нацелены не на сегмент кода, а на видеопамять.

Как же быть? Как вариант, можно перед тем, как нацеливать `DS` и `ES` на видеопамять, сохранить их значение на стеке. Потом нацелиться на видеопамять и вывести нужные данные на экран. А после этого снять со стека сохраненный указатель и снова поместить его в `DS` и `ES`.

Видеопамять, которая отображается на экран, организована вот таким вот образом.

Здесь на каждый символ отведено по два байта. Первый байт — это ASCII-код буквы. А второй — цвет фона и символа.

Кодировка цветов следующая.

А теперь давай посмотрим все это на живом примере. Напиши и выполни вот такой код.

Мы здесь задействовали парочку новых инструкций: `cld` и `stosw`. Инструкция `cld` очищает флаг направления. Зачем нужен этот флаг? Некоторые продвинутые инструкции (многошаговые) используют его, чтобы понять, что нужно делать с регистрами `SI` и `DI` после очередного шага выполнения: увеличивать их или уменьшать.

Инструкция `stosw` записывает значение регистра `AX` по адресу `ES:DI` и увеличивает регистр `DI` на два (размер слова — сдвоенного байта). Обрати внимание: если флаг направления установлен в единицу (это можно сделать инструкцией `sed`), то та же самая инструкция будет не увеличивать `DI`, а уменьшать. Тоже на два. Поэтому мы сначала используем `cld`, поскольку не знаем, какое на данный момент значение у флага направления.

Всякий раз, когда мы загружаем `AX`, у нас `AL` содержит саму букву, которую надо нарисовать на экране, а `AH` — цвет символа и цвет его фона (в данном случае синий фон и разные цвета для каждой буквы).

Когда мы пишем слово (сдвоенный байт) в память, младший байт всегда пишется первым (в данном случае значение регистра `AL`), а старший байт идет следом (в данном случае значение регистра `AH`).

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

Теперь ты умеешь рисовать на экране цветные буквы. При желании можешь поэкспериментировать с символами псевдографики, которые доступны в BIOS.

Кстати, на всякий случай имей в виду, что в 8088 еще есть инструкция `stosb`. Она работает так же, как `stosw`, но только не со словами (сдвоенными байтами), а с байтами. `stosb` пишет значение регистра `AL` по адресу `ES:DI` и увеличивает `DI` на единицу (или уменьшает, если флаг направления установлен в единицу).

## Рисуем психоделические круги

А теперь переходим к самому интересному! Сейчас напишем программу, которая рисует психоделические круги, как в фильме «Хакер». Помнишь там эпизод, где наши с тобой коллеги восхищаются «миллионом психоделических оттенков» на ноуте Кислотного Ожога?

Мы нарисуем то же самое, но в классическом текстовом режиме, в разрешении 80 × 25 с 16 цветами для фона и текста. Итак, приступим.

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

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

Что тут происходит? Сервис `0x00` прерывания `0x1A` считывает, сколько тиков прошло с того момента, как была загружена система. Результат возвращается в 32-битном виде, помещается в пару регистров: `CX:DX`.

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

Затем инструкцией `and al, 0x3F` выделяем шесть младших битов и при помощи вычитания приводим знаковое 8-битное значение к диапазону `-32..+31`, которое затем расширяем до слова (до сдвоенного байта). Результат помещаем в `CX`.

А теперь самое интересное. Вычисляем параметры текущего шага анимации и отрисовываем ее.

Что мы тут делаем? В `DI` у нас будет храниться текущая позиция на экране, куда выводить символ. Сначала сбрасываем ее в `0`, чтобы рисовать начиная с левого верхнего угла. В `DH` и `DL` будем хранить номер строки и столбца. А в `BX` у нас будет адрес таблицы синусов (ее покажу чуть позже).

Теперь, когда мы проинициализировали все нужные переменные, нам надо вычислить с помощью таблицы синусов, каким цветом отрисовывать символ (у нас это будет звездочка) в очередной позиции экрана. Чтобы в итоге получились круги.

Для этого делаем вот что. Берем номер текущей строки (из `DH`). Умножаем его на два, чтобы круги были кругами, а не овалами. По этому значению извлекаем синус из таблицы символов (смотри инструкцию `xlat`).

Что это за инструкция такая, `xlat`? Ее мнемоника — это сокращение от слова translate. Она считывает байт с адреса `BX+AL` и помещает его в регистр `AL`. То есть делает то же самое, что инструкция `mov, al, [bx+al]`, но только более лаконично. Перед тем как обращаться к `xlat`, надо сначала загрузить в `BX` адрес таблицы, а в `AL` — индекс нужного нам элемента из этой таблицы.

Обрати внимание на префикс `CS`, который стоит перед `xlat`. Он говорит процессору, что сейчас при считывании данных надо обращаться к кодовому сегменту. Без префикса `CS` данные будут считаны из сегмента, на который указывает регистр `DS`. И получится белиберда, потому что `DS` сейчас указывает на экран, а не на код, где размещена таблица синусов. После того как мы извлекли значение синуса, сохраняем его на стеке (смотри инструкцию `push ax`).

На этом полдела сделано. Мы взяли номер текущей строки (`DH`) и вычислили по нему синус. Дальше делаем то же самое для номера текущего столбца (`DL`). В итоге у нас получается два значения синуса. Одно сейчас хранится в регистре `AX`, а другое лежит на стеке.

Поэтому мы снимаем со стека предыдущее значение синуса (смотри инструкцию `pop DX`) и складываем с текущим значением синуса, которое сейчас хранится в `AX`. И еще плюсуем к ним текущее время (в тиках, от момента включения компьютера). Младший байт полученного результата — это и есть искомое значение цвета (цвет фона + цвет текста).

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

Итак, нужный цвет мы вычислили и теперь выводим звездочку на экран: `mov [di], ax`. Затем добавляем к `DI` двойку, чтобы перейти на следующую позицию экрана — где будем рисовать новую звездочку, но уже другим цветом. Ты ведь помнишь, что у нас на каждый символ отводится по два байта?

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

Что мы тут делаем? Сначала берем номера текущих строки и столбца (их мы сохранили на стеке). Помещаем их в `DX`. Затем прибавляем единицу к `DL`. Если результат получился меньше 80 (столько столбцов у нас на экране), то повторяем цикл. А если досчитали до 80, то переходим на следующую строку: увеличиваем `DH` на единицу, а `DL` обнуляем. Но делаем этот последний шаг, только если не досчитали до 25. А если в `DH` у нас уже 25, то переходим в самое начало программы (смотри метку `@@main_loop`) и начинаем все заново.

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

Кадр из нашей программы
Другие статьи в выпуске:

### Xakep #258. NPM Hijacking

- Содержание выпуска
- Подписка на «Хакер»

## Выводы

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

#### Оцени статью:

###### Что тебе понравилось больше всего?

Статья легкая и понятная Статья глубокая и подробная Интересуюсь этой темой Данные новые и актуальные Приятный стиль изложения

###### Что тебе не понравилось больше всего?

Статья поверхностная Статья слишком сложная Тема мне не интересна Данные устарели Неприятный стиль изложения
← Ранее Исследователь много лет держал в тайне баг в Bitcoin Core
Далее → Компания Razer допустила утечку данных
11 комментариев к записи Погружение в ассемблер. Учимся работать с памятью

#### Последние взломы

### Краденые миллиарды и плохой рэп. Как арестовали супругов, которые отмывали деньги Bitfinex и снимали тиктоки

### HTB EarlyAccess. Эксплуатируем веб-уязвимости и мисконфиги Docker

### HTB Horizontall. Захватываем веб-сервер через уязвимости в Strapi и Laravel

### Препарируем P-Code. Как реверсить старый софт на Visual Basic

#### Компьютерные трюки

### Терминальное счастье. Колонка главреда

### Анатомия терминала. Осваиваем текстуальную графику

### Змей-стражник. Сигнализация для велосипеда из старого смартфона на Android

### M1 для хакера. Тестируем Homebrew, виртуалки, Docker и Node на новой архитектуре Apple

#### Свежие новости

- 2 дня назад

  ### Для критической уязвимости в Magento появился эксплоит, а Adobe исправила второй аналогичный баг

- 2 дня назад

  ### Вредоносные письма могут вывести Cisco Email Security Appliance из строя

- 2 дня назад

  ### WordPress-плагин с 3 000 000 установок позволял всем желающим скачивать бэкапы сайтов

- 3 дня назад

  ### Француз случайно оставил без интернета целый город, пытаясь отключить интернет своим детям

- 3 дня назад

  ### Google представила «легкую» Chrome OS Flex, подходящую для старых ПК

Вопросы по материалам и подписке: support@glc.ru
Отдел рекламы и спецпроектов: yakovleva.a@glc.ru
Контент 18+
Сайт защищен Qrator —
самой забойной защитой от DDoS в мире
Подписка для физлиц
Подписка для юрлиц
Реклама на «Хакере»
Контакты
Posting....
wpDiscuz
FooBox
…