Погружение в ассемблер. Делаем первые шаги в освоении асма — «Хакер»

Найти:

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

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

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

- От редакции
- Готовимся к работе
- Если наборы инструкций у процессоров разные, то на каком учить ассемблер лучше всего?
- Что и как процессор делает после того, как ты запускаешь программу
- Регистры процессора: зачем они нужны, как ими пользоваться
- Готовим рабочее место
- Пишем, компилируем и запускаем программу «Hello, world!»
- Инструкции, директивы
- Метки, условные и безусловные переходы
- Комментарии, алгоритм, выбор регистров
- Получаем данные с клавиатуры
- Полезные мелочи: смотрим машинный код, автоматизируем компиляцию
- Выводы

Ты решил освоить ассемблер, но не знаешь, с чего начать и какие инструменты для этого нужны? Сейчас расскажу и покажу — на примере программы «Hello, world!». А попутно объясню, что процессор твоего компьютера делает после того, как ты запускаешь программу.

### От редакции

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

Читай далее:

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

## Готовимся к работе

Я буду исходить из того, что ты уже знаком с программированием — знаешь какой-нибудь из языков высокого уровня (С, PHP, Java, JavaScript и тому подобные), тебе доводилось в них работать с шестнадцатеричными числами, плюс ты умеешь пользоваться командной строкой под Windows, Linux или macOS.

### Если наборы инструкций у процессоров разные, то на каком учить ассемблер лучше всего?

Знаешь, что такое 8088? Это дедушка всех компьютерных процессоров! Причем живой дедушка. Я бы даже сказал — бессмертный и бессменный. Если с твоего процессора, будь то Ryzen, Core i9 или еще какой-то, отколупать все примочки, налепленные туда под влиянием технологического прогресса, то останется старый добрый 8088.

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

Больше того, когда ты начинаешь с начала — то есть сперва выучиваешь классический набор инструкций 8088 и только потом постепенно знакомишься с современными фичами, — ты в какой-то миг начинаешь видеть нестандартные способы применения этих самых фич. Смотри, например, что я сделал с SGX-анклавами и SIMD-регистрами.

### Что и как процессор делает после того, как ты запускаешь программу

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

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

    90
    B0 77
    B8 AA 77
    C7 06 66 55 AA 77

Вернее, даже так:

    90 B0 77 B8 AA 77 C7 06 66 55 AA 77

Хотя погоди! Только машина может понять такое. Поэтому много лет назад программисты придумали более гуманный способ общения с компьютером: создали ассемблер.

Благодаря ассемблеру ты теперь вместо того, чтобы танцевать с бубном вокруг шестнадцатеричных чисел, можешь те же самые инструкции писать в мнемонике:

    nop
    mov al, 0x77
    mov ax, 0x77AA
    mov word [0x5566], 0x77AA

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

### Регистры процессора: зачем они нужны, как ими пользоваться

Что делает инструкция `mov`? Присваивает число, которое указано справа, переменной, которая указана слева.

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

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

У процессора 8088 регистры 16-битные, их восемь штук (в скобках указаны типичные способы применения регистра):

- `AX` — общего назначения (аккумулятор);
- `BX` — общего назначения (адрес);
- `CX` — общего назначения (счетчик);
- `DX` — общего назначения (расширяет `AX` до 32 бит);
- `SI` — общего назначения (адрес источника);
- `DI` — общего назначения (адрес приемника);
- `BP` — указатель базы (обычно адресует переменные, хранимые на стеке);
- `SP` — указатель стека.

Несмотря на то что у каждого регистра есть типичный способ применения, ты можешь использовать их как заблагорассудится. Четыре первых регистра — `AX`, `BX`, `CX` и `DX` — при желании можно использовать не полностью, а половинками по 8 бит (старшая `H` и младшая `L`): `AH`, `BH`, `CH`, `DH` и `AL`, `BL`, `CL`, `DL`. Например, если запишешь в `AX` число `0x77AA` (`mov ax, 0x77AA`), то в `AH` попадет `0x77`, в `AL` — `0xAA`.

С теорией пока закончили. Давай теперь подготовим рабочее место и напишем программу «Hello, world!», чтобы понять, как эта теория работает вживую.

### Готовим рабочее место

1. Скачай компилятор NASM с www.nasm.us. Обрати внимание, он работает на всех современных ОС: Windows 10, Linux, macOS. Распакуй NASM в какую-нибудь папку. Чем ближе папка к корню, тем удобней. У меня это `c:\nasm` (я работаю в Windows). Если у тебя Linux или macOS, можешь создать папку `nasm` в своей домашней директории.

2. Тебе надо как-то редактировать исходный код. Ты можешь пользоваться любым текстовым редактором, который тебе по душе: Emacs, Vim, Notepad, Notepad++ — сойдет любой. Лично мне нравится редактор, встроенный в Far Manager, с плагином Colorer.

3. Чтобы в современных ОС запускать программы, написанные для 8088, и проверять, как они работают, тебе понадобится DOSBox или VirtualBox.

## Пишем, компилируем и запускаем программу «Hello, world!»

Сейчас ты напишешь свою первую программу на ассемблере. Назови ее как хочешь (например, `first.asm`) и скопируй в папку, где установлен `nasm`.

Если тебе непонятно, что тут написано, — не переживай. Пока просто постарайся привыкнуть к ассемблерному коду, пощупать его пальцами. Чуть ниже я все объясню. Плюс студенческая мудрость гласит: «Тебе что-то непонятно? Перечитай и перепиши несколько раз. Сначала непонятное станет привычным, а затем привычное — понятным».

Теперь запусти командную строку, в Windows это cmd.exe. Потом зайди в папку `nasm` и скомпилируй программу, используя вот такую команду:

    nasm -f bin first.asm -o first.com

Если ты все сделал правильно, программа должна скомпилироваться без ошибок и в командной строке не появится никаких сообщений. `NASM` просто создаст файл `first.com` и завершится.

Чтобы запустить этот файл в современной ОС, открой DOSBox и введи туда вот такие три команды:

    mount c c:\nasm
    c:
    first

Само собой, вместо `c:\nasm` тебе надо написать ту папку, куда ты скопировал компилятор. Если ты все сделал правильно, в консоли появится сообщение «Hello, world!».

### Инструкции, директивы

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

Инструкции. С инструкциями ты уже знаком (мы их разбирали чуть выше) и знаешь, что они представляют собой мнемонику, которую компилятор переводит в машинный код.

Директивы (в нашей программе их две: `org` и `db`) — это распоряжения, которые ты даешь компилятору. Каждая отдельно взятая директива говорит компилятору, что на этапе ассемблирования нужно сделать такое-то действие. В машинный код директива не переводится, но она влияет на то, каким образом будет сгенерирован машинный код.

Директива `org` говорит компилятору, что все инструкции, которые последуют дальше, надо помещать не в начале сегмента кода, а отступив от начала столько-то байтов (в нашем случае 0x0100).

Директива `db` сообщает компилятору, что в коде нужно поместить цепочку байтов. Здесь мы перечисляем через запятую, что туда вставить. Это может быть либо строка (в кавычках), либо символ (в апострофах), либо просто число.

В нашем случае: `db "Hello, world", '!', 0`.

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

    db "Hello, world!", 0

### Метки, условные и безусловные переходы

Метки используются для двух целей: задавать имена переменных, которые хранятся в памяти (такая метка в нашей программе только одна: `string`), и помечать участки в коде, куда можно прыгать из других мест программы (таких меток в нашей программе три штуки — те, которые начинаются с двух символов собаки).

Что значит «прыгать из других мест программы»? В норме процессор выполняет инструкции последовательно, одну за другой. Но если тебе надо организовать ветвление (условие или цикл), ты можешь задействовать инструкцию перехода. Прыгать можно как вперед от текущей инструкции, так и назад.

У тебя в распоряжении есть одна инструкция безусловного перехода (`jmp`) и штук двадцать инструкций условного перехода.

В нашей программе задействованы две инструкции перехода: `je` и `jmp`. Первая выполняет условный переход (Jump if Equal — прыгнуть, если равно), вторая (Jump) — безусловный. С их помощью мы организовали цикл.

Обрати внимание: метки начинаются либо с буквы, либо со знака подчеркивания, либо со знака собаки. Цифры вставлять тоже можно, но только не в начало. В конце метки обязательно ставится двоеточие.

### Комментарии, алгоритм, выбор регистров

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

Как добавлять комментарии? Просто поставь точку с запятой, и все, что напишешь после нее (до конца строки), будет комментарием. Давай добавим комментарии в нашу программу.

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

1. Поместить в `BX` адрес строки.
2. Поместить в `AL` очередную букву из строки.
3. Если вместо буквы там 0, выходим из программы — переходим на 6-й шаг.
4. Выводим букву на экран.
5. Повторяем со второго шага.
6. Конец.

Обрати внимание, мы не можем использовать `AX` для хранения адреса, потому что нет таких инструкций, которые бы считывали память, используя `AX` в качестве регистра-источника.

## Получаем данные с клавиатуры

От программ, которые не могут взаимодействовать с пользователем, толку мало. Так что смотри, как можно считывать данные с клавиатуры. Сохрани вот этот код как `second.asm`.

Потом иди в командную строку и скомпилируй его в NASM:

    nasm -f bin second.asm -o second.com

Затем запусти скомпилированную программу в DOSBox:

    second

Как работает программа? Две строки после метки `@@start` вызывают функцию BIOS, которая считывает символы с клавиатуры. Она ждет, когда пользователь нажмет какую-нибудь клавишу, и затем кладет ASCII-код полученного значения в регистр `AL`. Например, если нажмешь заглавную `A`, в `AL` попадет `0x41`, а если строчную `a` — `0x61`.

Дальше смотрим: если нажата клавиша с кодом 0x1B (клавиша ESC), то выходим из программы. Если же нажата не ESC, вызываем ту же функцию, что и в предыдущей программе, чтобы показать символ на экране. После того как покажем — прыгаем в начало (`jmp`): `start`.

Обрати внимание, инструкция `cmp` (от слова compare — сравнить) выполняет сравнение, инструкция `je` (Jump if Equal) — прыжок в конец программы.

## Полезные мелочи: смотрим машинный код, автоматизируем компиляцию

Если тебе интересно, в какой машинный код преобразуются инструкции программы, скомпилируй исходник вот таким вот образом (добавь опцию `-l`):

    nasm -f bin second.asm -l second.lst -o second.com

Тогда NASM создаст не только исполняемый файл, но еще и листинг: `second.lst`. Листинг будет выглядеть как-то так.

Еще тебе наверняка уже надоело при каждом компилировании вколачивать в командную строку длинную последовательность одних и тех же букв. Если ты используешь Windows, можешь создать батник (например, `m.bat`) и вставить в него вот такой текст.

Теперь ты можешь компилировать свою программу вот так:

    m first

Само собой, вместо `first` ты можешь подставить любое имя файла.

## Выводы

Итак, ты теперь знаешь, как написать простейшую программу на ассемблере, как ее скомпилировать, какие инструменты для этого нужны. Конечно, прочитав одну статью, ты не станешь опытным программистом на ассемблере. Чтобы придумать и написать на нем что-то стоящее — вроде Floppy Bird и «МикроБ», которые написал я, — тебе предстоит еще много пройти. Но первый шаг в эту сторону ты уже сделал.

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

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

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

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

Статья поверхностная Статья слишком сложная Тема мне не интересна Данные устарели Неприятный стиль изложения
← Ранее Роутеры D-Link не получили исправлений для критических багов
Далее → Южноафриканский банк перевыпускает 12 млн карт, так как сотрудник украл мастер-ключ
27 комментариев к записи Погружение в ассемблер. Делаем первые шаги в освоении асма

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

### Краденые миллиарды и плохой рэп. Как арестовали супругов, которые отмывали деньги 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