Как вывести hello world на ассемблере
Перейти к содержимому

Как вывести hello world на ассемблере

  • автор:

Hello World на Ассемблере

После примеров простых программ на Паскале и С++ вы, наверно, не ожидали что я сразу перепрыгну на Ассемблер. Но вот перепрыгнул. И сегодня мы поприветствуем мир на языке ассемблера.

Итак, вот сразу пример, а потом его рассмотрим:

.model tiny .code ORG 100h begin: MOV AH, 9 ; Функция вывода строки на экран MOV DX, OFFSET Msg ; Адрес строки INT 21h ; Выполнить функцию RET ; Вернуться в операционную систему Msg DB 'Hello, World. $' ; Строка для вывода END begin

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

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

Во всех подробностях разбирать этот код не будем — подробности в другой раз. Рассмотрим только основные инструкции.

Программа начинается с метки begin . В отличие, например, от Паскаля, это слово может быть каким угодно, например, start . Это всего лишь метка, которая обозначает начало какого-то участка кода.

К конце программы мы видим END begin . Инструкция END говорит ассемблеру, что следующее за ней слово означает конец блока кода, обозначенного этим словом. В нашем примере это означает, что здесь кончается блок кода, начинающийся со слова begin .

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

Затем в регистр DX мы записываем адрес строки. Адрес вычисляется с помощью оператора OFFSET . Например:

Здесь мы получаем адрес первого байта блока данных, обозначенного идентификатором Msg .

Затем мы вызываем прерывание 21h . Это прерывание выполняет функцию, номер которой записан в регистре АН. Поскольку у нас там записана функция 9, то прерывание выведет на экран строку.

Команда RET выполняет выход из процедуры или из программы. В нашем случае из программы. Таким образом программа завершается и мы возвращаемся в операционную систему.

Ещё несколько слов об объявлении строки:

Msg DB ‘Hello, World. $’

Вначале мы записываем идентификатор (в нашем случае Msg , но может быть и любой дугой), чтобы было проще работать со строкой. Затем пишем DBDefine Byte — Определить Байт. В нашем случае это будет массив байтов, в котором каждый элемент имеет размер один байт.

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

Ну вот мы и написали свою первую программу на языке ассемблера. Если что-то пропустили, то посмотрите видео:

«Hello world» on TASM или учим ассемблер как 20 лет назад

Если вы дадите человеку программу, то займете его на один день. Если вы научите человека программировать, то займете его на всю жизнь.

Чтобы что-то понять лучше нужно это делать, именно поэтому я нашёл книгу, которая мне немного поможет в этом — «Основы языка ассемблера» К.Г.Финогенов. Сегодня этому учебнику уже 20 лет, но защищенный режим процессора х86 по-прежнему не измена. Детище интел до сих пор на коне, что делает его привлекательным для изучения. А современные компьютеры позволяют довольно успешно эмулировать работу своих предков. В статье больше пойдет речь о том как можно подобрать инструмент для изучения ассемблера, и для примера мы соберем «Hello world» из книги.

TASM до сих пор изучают в некоторых университетах, поэтому будем считать что он справляется с образовательной целью. Где же взять исходники? В статье «MASM, TASM, FASM, NASM под Windows и Linux«, находим необходимый нам TASM, успешно работающий из Dosbox. Однако, мы не сможем проделать линкование, так как нам не будет хватать библиотеки DPMI16BI.OVL, которую необходимо поместить в папку с TASM.

Далее нам необходимо установить DOSbox — программа хороша тем, что одинаково успешно работает на Windows, Linux, Mac, а также сразу имеет установленную операционную систему. Если вы никогда не работали с DOSBox, то в интернете полно гайдов о том как её настроить для своей системы ( например настройка для linux). В целом настройка конфигурационного файла и горячих клавиш будет одинаковой на разных системах.

После того как вы добавите папку (жесткий диск) для вашей DosBox, нам необходимо будет создать файл ассемблера. (Также стоит помнить команды DOS для отображения файлов и перехода в каталог — DIR CD). Для графической навигации можете установить файловый менеджер вроде Norton Commander.

Создадим наш hello.asm в любом текстовом редакторе

; Ex 1-1. Hello world
assume CS:code,DS:data
; Describe the segment of code
code segment ; Open segment of code
begin:
mov AX,data ; Configure DS
mov DS,AX ; on segment on date
; Print on display text string
mov AH, 09h ; DOS function print to display
mov DX, offset msg ; Addres of printing srting
int 21h ; System call DOS
; Ending programm
mov AX, 4C00h ; DOS function to ending of programm
int 21h ; System call DOS
code ends ; Close code segment
; Describe the segment of date
data segment ; Open date segment
msg db "Hello world!$" ; Text string
data ends ; Close date segment
; Describe the stack segment
stk segment stack ; Open stack segment
db 256 dup (?) ; Stack size now 256 byte
stk ends ; Close stack segment
end begin ; End text with enter point.

Для простых случаев можно запускать транслятор и компоновщик TASM без параметров. После запуска tasm hello.asm в нашем каталоге появится два файла — hello.obj hello.map. Их легко можно открыть в редакторе и посмотреть что происходит с программой. После этого можно пропустить наш объектный файл через транслятор, чтобы получить исполняемый файл.

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

Туториал по FASM (Windows x32 API/Win32API), «Hello world!»

На нем можно сделать все что угодно, от ОС до 3D игр.

Вот плюсы ассемблера:

  • Он очень быстрый.
  • На нем можно сделать любую программу.

А вот минусы ассемблера:

  • Долго делать программу. (относительно)
  • Сложен в освоении.

Что нужно для программирования на ассемблере (FASM)?

  • FASM компилятор — https://flatassembler.net/
  • FASM Editor 2.0 — Удобная IDE для FASM, от fasmworld.ru (asmworld), качаем от сюда: https://fasmworld.ru/content/files/tools/FEditor-v2.0.rar
  • OlyDbg — удобный отладчик ассемблера от ollydbg.de: https://www.ollydbg.de/odbg201.zip Это все мероприятие весит всего лишь 8.5MB.

Установка компонентов (если можно так назвать)

Архив FASM-а распаковуем в C:\\FASM\ или любой другой, но потом не забудьте настроить FASMEditor.

Архив FASMEdit-a распаковуем куда-то, в моем случае C:\\FASM Editor 2.0\

Архив OlyDbg распаковуем тоже куда-то, в моем случае C:\\Users\****\Documents\FasmEditorProjects\

Настройка FASM Editor-a

Для этого его нужно запустить.

Сразу вас приветствует FASM Editor соей заставкой.

Теперь вам нужно зайти в вкладку «Сервис» (на картинке выделил синим) -> «Настройки. «

Жмем на кнопку с названием «. » и выбираем путь к файлам или папкам.

Теперь мы полностью готовы. К началу.

Пишем «Hello world!» на FASM

В Fasm Editor нужно нажать на кнопку слева сверху или «файл» -> «новый». Выбираем любое, но можно выбрать «Console»

По началу вас это может напугать, но не боимся и разбираемся.

format PE Console ; говорим компилятору FASM какой файл делать entry start ; говорим windows-у где из этой каши стартовать программу. include 'win32a.inc' ; подключаем библиотеку FASM-а ;можно и без нее но будет очень сложно. section '.data' data readable writeable ; секция данных hello db 'hello world!',0 ; наша строка которую нужно вывести section '.code' code readable writeable executable ; секция кода start: ; метка старта invoke printf, hello ; вызываем функцию printf invoke getch ; вызываем её для того чтоб программа не схлопнулась ;то есть не закрылась сразу. invoke ExitProcess, 0 ; говорим windows-у что у нас программа закончилась ; то есть нужно программу закрыть (завершить) section '.idata' data import readable ; секция импорта library kernel, 'kernel32.dll',\ ; тут немного сложней, объясню чуть позже msvcrt, 'msvcrt.dll' import kernel,\ ExitProcess, 'ExitProcess' import msvcrt,\ printf, 'printf',\ getch, '_getch'

На самом деле из всей этой каши текста, команд всего 3: на 16, 18, 21 строках. (и то это не команды, а макросы. Мы к командам даже не подобрались)

Все остальное это просто подготовка программы к запуску.

Программа при запуске должна выглядеть так:

Самое интересное то что программа весит 2КБ. (Можно сократить и до 1КБ, но для упрощения и так пойдет)

Разбор: что значат этот весь текст?

На 1 строчке: «format PE Console» — это строчка говорит FASM-у какой файл скомпилировать, точнее 1 слово, все остальные слова это аргументы (можно так сказать).

PE — EXE файл, программа.

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

Но есть кроме это остальные:

  • format MZ — EXE-файл НО под MS-DOS
  • format PE — EXE-файл под Windows, аналогично format PE GUI 4.0
  • format PE64 — EXE-файл под Windows, 64 битное приложение.
  • format PE GUI 4.0 — EXE-файл под Windows, графическое приложение.
  • format PE Console — EXE-файл под Windows, консольная программа. (просто подключается заранее консоль)
  • format PE Native — драйвер
  • format PE DLL — DLL-файл Windows, поясню позднее.
  • format COFF — OBJ-файл Linux
  • format MS COFF — аналогично предыдущему
  • format ELF — OBJ-файл для gcc (Linux)
  • format ELF64 — OBJ-файл для gcc (Linux), 64-bit

Сразу за командой (для компилятора) format PE Console идет ; это значит комментарий. К сожалению он есть только однострочный.

3 строка: entry start

  • Говорим windows-у где\в каком месте стартовать. «start» это метка, но о метках чуть позже.

5 строка: include ‘win32a.inc’

  • Подключает к проекту файл, в данном случае «win32a.inc» он находиться в папке INCLUDE (в папке с FASM). этот файл создает константы и создает макросы для облегчения программирования.

8 строка: section ‘.data’ data readable writeable

  • Секция данных, то есть программа делиться на секции (части), к этим секциям мы можем дать разрешение, имя.

Флаг «data» (Флаг это бит\байт\аргумент хранившей в себе какую-то информацию) говорит то что эта секция данных.

Флаги «readable writeable» говорят то что эта секция может читаться кем-то и записываться кем-то.

Текст ‘.data’ — имя секции

10 строка: hello db ‘hello world!’,0

hello — это метка, она может быть любого имени (почти, есть некоторые зарезервированные имена), эта метка хранит в себе адрес строки, это не переменная, а просто адрес, но чтобы не запоминать адреса в ручную, помогает FASM он запоминает адрес и потом когда видит эту метку снова, то он заменяет слово на адрес.

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

‘hello world!’ — наша строка в кодировке ASCII

Что значит «,0» в конце строки? — это символ с номером 0 (или просто ноль), у вас на клавиатуре нет клавиши которая имела символ с номером 0, по этому этот символ используют как показатель конца строки. То есть это значит конец строки. Просто ноль записываем в байт после строки.

12 строка: section ‘.code’ code readable writeable executable

Флаг «code» — говорит то что это секция кода.

Флаг «executable» — говорит то что эта секция исполняема, то есть в этой секции может выполняться код.

Все остальное уже разобрали.

14 строка: start:

Это второй вид меток. Просто эта метка указывает на следующую команду. Обратите внимание на то что в 3 строке мы указали start как метку входа в программу, это она и есть. Может иметь эта метка любое имя, главное не забудьте ваше новое имя метки вписать в entry

15 строка: invoke printf, hello

  • Функция printf — выводит текст\число в консоль. В данном случае текст по адресу «hello»

Это штото на подобие команды, но это и близко не команда ассемблера, а просто макрос.

Макрос — Это макро команда для компилятора, то есть вместо имени макроса подставляется что-то другое.

Например, макро команда invoke делиться на такие команды: (взят в пример команда с 15 строки)

push hello call [printf]

Не переживайте если нечего не поняли.

17 строка: invoke getch

  • getch — функция получения нажатой кнопки, то есть просто ждет нажатия кнопки и потом возвращает нажатую кнопку.

20 строка: invoke ExitProcess, 0

  • ExitProcess — WinAPI функция, она завершает программу. Она принимает значение, с которым завершиться, то есть код ошибки, ноль это нет ошибок.

23 строка: section ‘.idata’ data import readable

Флаг «import» — говорит то что это секция импорта библиотек.

24-25 строки:

library kernel, 'kernel32.dll',\ msvcrt, 'msvcrt.dll'
  • Макро команда «library» загружает DLL библиотеки в виртуальную память (не в ОЗУ, вам ОЗУ не хватит чтоб хранить всю виртуальную память).

Что такое DLL объясню позже.

kernel — имя которое привязывается к библиотеке, оно может быть любым.

Следующий текст после запятой: ‘kernel32.dll’ — это имя DLL библиотеки который вы хотите подключить.

Дальше есть знак \ это значит что текст на следующей строке нужно подставить в эту строку.

library kernel, 'kernel32.dll',\ msvcrt, 'msvcrt.dll'
library kernel, 'kernel32.dll', msvcrt, 'msvcrt.dll'

Это нужно потому что у ассемблера 1 строка это 1 команда.

27-28 строка:

import kernel,\ ExitProcess, 'ExitProcess'

import — Макро команда, которая загружает функции из DLL.

kernel — Имя к которой привязана DLL, может быть любым.

ExitProcess — Как будет называться функция в программе, это имя будет только в вашей программе, и по этому имени вы будете вызывать функцию. (WinAPI функция)

‘ExitProcess’ — Это имя функции которое будет загружено из DLL, то есть это имя функции которое прописано в DLL.

Дальше думаю не стоит объяснять, вроде все понятно.

Что такое DLL библиотека?

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

Подводим итог

На ассемблере писать можно не зная самого языка, а используя всего лишь макро команды компилятора. За всю статью я упомянул всего 2 команды ассемблера это push hello и call [printf] . Что это значит расскажу в следующей статье.

Ассемблер Hello World

результатом является краш программы. В отладчике показывает что возникает ошибка ACCESS_VIOLATION в момент исполнения инструкции int 21h. Изначально думал что это из-за особенностей windows 7. Но сделав то же самое на си, и разобрав потом файл с помощью IDA pro увидел там тот же самый int 21h который довольно успешно исполнялся. Подскажите пожалуйста как быть?

Отслеживать
задан 4 фев 2018 в 22:52
Андрей Беспалов Андрей Беспалов
90 1 1 серебряный знак 9 9 бронзовых знаков

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

4 фев 2018 в 23:11

Спасибо большое за Ваш ответ. Но почему отладчики выдают ту же самую int 21h в работающем приложении?

4 фев 2018 в 23:21

Простите уж, но напомнило старую байку, как двое спорят до хрипоты — натурная съемка или постановка — мол, глянь, как тут вдарил! так он не мог полететь — явная постановка! Да ты, болван, физику не знаешь! — ну и так далее. Приходит третий, офигевает и говорит «Ребята, это ж «Том и Джерри»». Ребята, это ж функция DOS! Если 32-разрядная Windows и сделать выполнимый файл DOS, ну, тогда.

5 фев 2018 в 6:38
@АндрейБеспалов вечером постараюсь подробный ответ дать.
5 фев 2018 в 9:16
Благодарю Вас. Буду ждать
5 фев 2018 в 13:40

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Во-первых, вы компилируете ваш код как 32-битное консольное приложение (ключ /subsystem:console ). В обычном пользовательском приложении (не ядре или драйвере) под Windows использовать исключения DOS или BIOS нельзя.

Можно скомпилировать программу как 16-битный досовский исполняемый файл, но рабочий код будет выглядеть немного по-другому:

.model small .stack 100h .data message db 'Hello World',13,10,'$' ; для функции 9h прерывания 21h конец строки должен быть обозначен как символ '$', а не 0 .code start: mov ax, @data mov ds, ax mov dx, offset message mov ah, 09h int 21h mov ah, 4Ch int 21h end start 
C:\masm32\bin\ml /c hello.asm C:\masm32\bin\link16 hello.obj,,nul,nul,nul 

Выполнить полученный исполняемый файл можно несколькими способами:

  1. Запустив его под DOS
  2. Запустив его под Windows линейки Win 9x (от 95 до Me). Это настоящее выполнение кода без эмуляции.
  3. Запустив его под 32-разрядной Windows линейки NT (в том числе Win XP, Win 7 и даже Win 10, насколько я понимаю — но проверить это не могу). Это будет запуск через встроенный в систему эмулятор NTVDM.
  4. На любой Windows (и не только) системе с помощью эмулятора DosBox.

По поводу прерывания int 21h в программах под Windows. В каждом (почти каждом) приложении Windows есть код-«заглушка», который выполняется при попытке запуска приложения из-под DOS. В этом случае на экран выведется сообщение типа «This program cannot be run in DOS mode.» Вывод там происходит как раз с помощью 21h прерывания. При обычном запуске приложения под Windows этот код не выполнится. Если попытаться использовать прерывание под Windows, вы получите ошибку.

В качестве исключения можно использовать прерывание int3 (команда так и выглядит, без пробела), которое специально предназначено для остановки выполнения программы, и вызова отладчика.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *