В этой статье я покажу как написать приложение для windows на ассемблере. В качестве IDE будет привычная Visual Studio 2019 со своими плюшками — подсветка кода, отладка и привычный просмотр локальных переменных и регистров. Собирать приложение будет MASM, а значит, у нас будут и чисто масмовские плюшки. Приложением будет игра в пятнашки. С одной стороны это просто, но не так чтобы совсем хелловорлд (впрочем хелловорлд мы тоже увидим во время настройки VS). С другой стороны это будет полноценное оконное приложение с меню, иконкой, отрисовкой, выводом текста и обработкой мыши с клавиатурой. Изначально у меня была мысль сделать что-нибудь поинтереснее пятнашек, но я быстро передумал. Сложность и размер статьи увеличивается значительно, а она и так получилась немаленькая. Новой же информации сильно больше не становится. Статья рассчитана на тех, кто имеет хотя бы начальные знания ассемблера, поэтому здесь не будет всяких мелочей из разряда как поделить одно число на другое или что делает команда mov. Иначе объем пришлось бы увеличить раза в три.
Заранее постараюсь ответить на вопрос — зачем это нужно, если на ассемблере сейчас уже никто не пишет? Есть пара объективных причин и одна субъективная. Из объективного — написание подобных программ позволяет понять внутреннее устройство windows и как в конечном итоге наш код исполняется на процессоре. Далеко не всем это действительно надо, но для некоторых вещей это обязательное знание. Вторая причина это то, что позволяет взглянуть на разработку немного под другим углом. Примерно так же как попробовать функциональное программирование полезно даже если не писать ничего в функциональном стиле. К примеру я слушал лекции Мартина Одерски вовсе не потому что решил перейти с C# на Scala. Полезно посмотреть на привычную разработку под другим углом. Субъективная же причина — для меня это было просто интересно, отойти от коммерческой разработки, этого цикла задач, спринтов, митингов, сроков и заняться тем, что интересно именно тебе.
Так получилось что у меня появилось много свободного времени, часть из которого я потратил на то, что называется пет-проектами. Это не стало какими-то production-ready вещами, скорее какие-то идеи интересные лично мне, что-то на что вечно не хватало времени. Одна из этих идей это ассемблер в современной IDE. Давно хотел этим заняться, но все не было времени. Мне было очень интересно со всем этим разбираться, надеюсь читателям тоже понравится.
Шаг первый — настраиваем VS
Тут я немного схитрил. Точнее так уж получилось, что здесь все уже сделано за нас. Есть пошаговая инструкция и даже готовый пустой проект. Можно воспользоваться пошаговой инструкцией, а я просто скачал пустой проект и переименовал SampleASM в FifteenAsm. Единственное, что надо сделать помимо переименования, это установить SubSystem : Windows в свойствах проекта (properties > Linker > System > SubSystem : Windows). Далее выбираем x86, нажимаем F5 (либо кликаем мышкой) и видим вот такое сообщение:
Теперь о подсветке синтаксиса. Тут есть разные пути, и я решил поискать что есть готового. Готового оказалось немного, я установил Asm-Dude. Также попробовал ChAsm, но внешний вид меня не порадовал. Впрочем внешний вид это дело вкуса, я остановился на Asm-Dude. Тут правда есть такой нюанс — Asm-Dude не поддерживает VS 2022, самая старшая версия VS 2019. Вот так выглядит все в сборе — дебаг, просмотр переменных, в т.ч. нормальное отображение структур, мнемоника для ассемблера.
Update: AsmDude для 2022 существует, но его нет в студийном менеджере расширений (автор не стал публиковать), доступен по ссылке. Вторая версия расширения в процессе разработки, и 1 октября автор расширения ее опубликовал. Во второй версии заявлено много всяких фич, не все они готовы, но пользоваться уже можно. Спасибо @NN1 и @aroman313 за уточнение.
Теперь еще одна вещь, о которой хочется рассказать, прежде чем приступить к основной части. Это MASM SDK. Это совсем необязательная вещь, но очень полезная. Там есть готовые inc файлы для WinAPI, а еще есть много примеров самых разных приложений на ассемблере. Но проект из этой статьи будет работать и без него.
Шаг второй — оконное приложение
Для того чтобы создать окно средствами WinAPI нужно немного. Заполнить специальную структуру с описанием этого окна, зарегистрировать класс окна, потом это окно создать. Вот практически и все. Еще нам нужна так называемая оконная процедура, или процедура обработки сообщений, называют ее по разному. Суть этой процедуры в обработке сообщений которые приходят в наше приложение. Клики мышкой, команды меню, отрисовка и вообще все специфическое поведение нашего приложения будет там. Со всеми подробностями написано здесь.
О вызове функций вообще и WinAPI в частности
Чтобы вызвать функцию, ее надо объявить. Ниже разные способы это сделать.
extern MessageBoxA@16 : PROC
MessageBoxA PROTO, hWnd:DWORD, pText:PTR BYTE, pCaption:PTR BYTE, style:DWORD
MessageBoxW PROTO, :DWORD,:DWORD,:DWORD,:DWORD
Объявление со списком параметров более понятно. Хотя именовать параметры и необязательно. Объявлением с extern я пользоваться не буду, оставим это для любителей разгадывать ребусы. Что такое A(или W) в имени функции? Это указание на тип строк, A — ANSI, W — Unicode. Для простоты дела я решил не связываться с юникодом и везде использовал ANSI версии. Обычно же применяют дефайн примерно такого вида:
#ifdef UNICODE
#define SetWindowText SetWindowTextW
#else
#define SetWindowText SetWindowTextA
#endif
Теперь о вызовах функций, «стандартный» для ассемблера вызов выглядит так
push 0
push offset caption
push offset text
push 0
call MessageBoxA
Существует мнемоническое правило для порядка аргументов — слева направо — снизу вверх. Иными словами первый аргумент в объявлении функции (здесь это хендл окна hWnd:DWORD) будет в самом нижнем push. К счастью в MASM есть очень удобная вещь — invoke. Вот так выглядит вызов той же самой функции.
invoke MessageBoxA, 0, offset howToText, offset caption, 0
Одна строчка вместо пяти. На мой взгляд invoke удобнее за редкими исключениями типа большого числа аргументов. В дальнейшем практически везде я буду пользоваться invoke.
Сигнатура, описание и примеры использования функций WinAPI легко гуглятся по их названию. На примере MessageBoxA мы увидим вот это
int MessageBoxA(
[in, optional] HWND hWnd,
[in, optional] LPCSTR lpText,
[in, optional] LPCSTR lpCaption,
[in] UINT uType
);
Осталось перевести все эти HWND и LPCSTR в соответствующие типы для ассемблера. Тип данных LPCSTR будет DWORD, ведь это просто ссылка. Олдфаги с легкостью узнают венгерскую нотацию, а название типа расшифровывается как Long Pointer Const String. HWND тоже будет просто DWORD, ведь HWND, как и LPCSTR по своей сути просто ссылка. Ну а UINT это DWORD просто по определению. В некотором роде сигнатура функций на ассемблере даже проще, ссылка здесь это просто ссылка, нет кучи разных типов.
Отсюда следует важный вывод — нет никаких специальных «ассемблерных» функций, это то же самое WinAPI !. Нам достаточно знать как решается нужная нам задача средствами WinAPI, неважно на каком языке они будут вызываться. Поэтому задача «вывести текст в окно средствами ассемблера» на самом деле будет «вывести текст в окно средствами WinAPI», а уж информации по WinAPI полно. Обратное тоже верно, зная как что-то сделать средствами WinAPI это можно сделать на практически любом языке. А это уже часто бывает полезно при написании скриптов.
Создаем простое окно
Перед созданием окна я сделал три inc-файла. Один с прототипами WinAPI, другой с константами приложения (ширина окна, заголовок, цвет заливки и все в таком же духе) и третий, со структурами WinAPI и целой кучей винапишных констант. Теперь можно писать NULL или TRUE/FALSE. Или MB_OK вместо 0, как в примере выше с MessageBoxA. Никаких специфических действий не нужно, просто Add — New Item — Text File и не забываем include filename. Файлики назвал WinApiProto.inc, WinApiConstants.inc, AppConstants.inc. Пример содержимого показан ниже.
Вот так теперь выглядит наш код
.386
.model flat, stdcall
.stack 4096
include WinApiConstants.inc
include WinApiProto.inc
.data
include AppConstants.inc
.code
main PROC
;...more code
Небольшое отступление про строки. Вот пример строковых констант
szClassName db "Fifteen_Class", 0
howToText db "The first line", CR , LF , "The second.", 0
Запятая означает конкатенацию, db это define byte, CR LF определены в WinApiConstants.inc (13 и 10 соответственно), ноль на конце это null-terminated строка. В итоге строки это никакой не специальный тип данных, а просто массивы байт с нулем на конце. В случае с юникодом возни было бы больше, но я решил не усложнять себе жизнь и использовать везде ANSI строки.
Вот мы и добрались до создания окна. Для этого нам надо
-
заполнить структуру WNDCLASSEX (объявлена в WinApiConstants)
-
зарегистрировать класс окна
-
создать процедуру главного окна
-
создать окно
Кода вышло уже почти на 200 строк, поэтому я покажу самые интересные куски, целиком можно посмотреть на гитхабе.
Объявление и заполнение WNDCLASSEX, как видим все как в языках высокого уровня. Ну, почти все — автодополнения со списком полей структуры нет.
WNDCLASSEX STRUCT
cbSize DWORD ?
style DWORD ?
lpfnWndProc DWORD ?
WNDCLASSEX ENDS
mov wc.cbSize, sizeof WNDCLASSEX
mov wc.style, CS_BYTEALIGNWINDOW
mov wc.lpfnWndProc, offset WndProc
При создании окна весьма важный параметр WS_EX_COMPOSITED. Без него при перерисовке будет мерзкий flickering. Очень хорошо что это работает — реализовывать двойную буферизацию самостоятельно желания не было.
push WS_EX_OVERLAPPEDWINDOW or WS_EX_COMPOSITED
call CreateWindowExA
Теперь немного чудесных директив MASM. Вот так вот просто организован цикл обработки сообщений
; Loop until PostQuitMessage is sent
.WHILE TRUE
invoke GetMessageA, ADDR msg, NULL, 0, 0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessageA, ADDR msg
.ENDW
А вот так без них
StartLoop:
push 0
push 0
push 0
lea eax, msg
push eax
call GetMessageA
cmp eax, 0
je ExitLoop
lea eax, msg
push eax
call TranslateMessage
lea eax, msg
push eax
call DispatchMessageA
jmp StartLoop
ExitLoop:
А вот как все просто в оконной процедуре. Никаких тебе cmp uMsg, WM_DESTROY, кучи меток, простой IF
.IF uMsg == WM_DESTROY
invoke PostQuitMessage, NULL
xor eax, eax
ret
.ENDIF
Вот как делается подтверждение на закрытие окна
.IF uMsg == WM_CLOSE
invoke MessageBoxA, hwin, ADDR exitConfirmationText, ADDR caption, MB_YESNO
.IF eax == IDNO
xor eax, eax
ret
.ENDIF
.ENDIF
Обещанный хелловорлд готов.
Добавляем иконку и меню
Иконка и меню в мире windows относятся к ресурсам. Поэтому добавляем к нашему проекту файл ресурсов — Add — Resource — Menu. Дальше можно воспользоваться встроенным редактором VS, я просто взял и отредактировал свежий файл FifteenAsm.rc в блокноте. Получилось вот так
500 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "FIFTEENICON.ICO"
600 MENUEX MOVEABLE IMPURE LOADONCALL DISCARDABLE
BEGIN
POPUP "&File", , , 0
BEGIN
MENUITEM "&New Game", 1100
MENUITEM "&Exit", 1000
END
POPUP "&Help", , , 0
BEGIN
MENUITEM "&How to play", 1800
MENUITEM "&About", 1900
END
END
Обратите внимание на магические числа 500 и 600. Это идентификаторы ресурсов, совсем скоро мы увидим зачем они нужны. Также обратите внимание на магические числа 1000, 1100, 1800, 1900. Это идентификаторы команд, мы тоже увидим зачем они нужны, но чуть позже. Чуть не забыл про сам файл иконки, нарисовал я ее в каком-то онлайн редакторе. Дизайнер из меня так себе, поэтому что получилось, то получилось. Добавляем в проект под именем Fifteenicon.ico, тут главное назвать точно как в файле ресурсов. Дальше все просто. Иконка добавляется на этапе заполнения структуры WNDCLASSEX, тут у нас магическое число 500
push 500
push hInst
call LoadIconA
mov wc.hIcon, eax
Меню добавляется после создания окна, здесь магическое число 600
call CreateWindowExA
mov hWnd,eax
push 600
push hInst
call LoadMenuA
push eax
push hWnd
call SetMenu
А вот так обрабатываются команды меню, тут остальные магические числа 1000, 1100, 1800, 1900. Вообще с использованием MASM код не особо отличается от кода на тех же плюсах.
.IF uMsg == WM_COMMAND
.IF wParam == 1000
invoke SendMessageA, hwin, WM_SYSCOMMAND, SC_CLOSE, NULL
.ENDIF
.IF wParam == 1100
invoke MessageBoxA, hwin, ADDR newGameConfirmationText, ADDR caption, MB_YESNO
.IF eax == IDYES
;call InitTilesData
.ELSEIF eax == IDNO
xor eax, eax
ret
.ENDIF
.ENDIF
.IF wParam == 1800
invoke MessageBoxA, hwin, ADDR howToText, ADDR caption, MB_OK
.ENDIF
.IF wParam == 1900
invoke MessageBoxA, hwin, ADDR aboutText, ADDR caption, MB_OK
.ENDIF
.ENDIF
У приложения появилась иконка и есть меню. Ради интереса посмотрел на размер исполняемого файла, всего 6656 байт.
Шаг третий — игра
Создали окно, пора заняться самой игрой. Здесь я тоже покажу только самые интересные места.
Инициализация данных и начальная перетасовка
Данные о положении тайлов будут храниться в массиве из 16 байт. Ноль будет положением пустого тайла, значения от 1 до 15 соответствующие тайлы. Нумерация индексов тайлов слева направо, сверху вниз. Теперь надо их перетасовать и тут встает вопрос, откуда брать случайные числа? RDRAND и RDSEED появились достаточно поздно, а мне хотелось сделать код в «классическом» стиле. Сгоряча я даже думал реализовать Вихрь Мерсенна, но потом решил что это перебор. Поэтому честно нашел простенький ГПСЧ буквально в десяток команд, для seed использовал системное время. Идея начальной перетасовки простая, сначала заполняем массив по порядку (приводим в конечное состояние), а потом случайным образом двигаем тайлы. Тайлы двигаются по правилам, значит их всегда можно будет собрать в конечное положение. Если заполнять тайлы совсем рандомно, то надо проверять можно ли вообще собрать такую комбинацию. По опыту уже 100 итераций перемешивает тайлы вполне нормально.
local randSeed : DWORD
invoke GetTickCount
mov randSeed, eax
xor eax, eax
xor ebx, ebx
xor ebx, ebx
.WHILE ebx < initialSwapCount
mov eax, 4; random numbers count, i.e. from 0 to 3
push edx
imul edx, randSeed, prndMagicNumber
inc edx
mov randSeed, edx
mul edx
mov eax, edx
pop edx
add al, VK_LEFT
push ebx
invoke ProcessArrow, NULL, al; move a tile
pop ebx
inc ebx
.ENDW
ret
Отрисовка
Добавляем обработку WM_PAINT в оконной процедуре
LOCAL Ps :PAINTSTRUCT
LOCAL hDC :DWORD
.IF uMsg == WM_PAINT
invoke BeginPaint, hWin, ADDR Ps
mov hDC, eax
invoke PaintProc, hWin, hDC
invoke EndPaint, hWin, ADDR Ps
.ENDIF
Отрисовка тайлов. Из интересного здесь организация двойного цикла с использованием директив MASM WHILE и передача указателя на RECT в процедуре CalculateTileRect.
LOCAL Rct : RECT
invoke CreateSolidBrush, tileBackgroundColor
mov hBrush, eax
invoke SelectObject, hDC, hBrush
;fill tiles with background color
mov vert, 0
.WHILE vert < 4
mov hor, 0
.WHILE hor < 4
invoke CalculateTileRect, ADDR Rct, hor, vert
invoke RoundRect, hDC, Rct.left, Rct.top, Rct.right, Rct.bottom,
tileRoundedEllipseSize, tileRoundedEllipseSize
inc hor
.ENDW
inc vert
.ENDW
invoke DeleteObject, hBrush
CalculateTileRect proc rct :DWORD, hor:BYTE, vert:BYTE
mov edx, rct
invoke CalculateTileRectPos, hor, 0
mov (RECT PTR [edx]).left, eax
ret
CalculateTileRect endp
Обратите внимание на эту строчку. Структура передана по ссылке, смещение на left вычисляется автоматически.
mov (RECT PTR [edx]).left, eax
А вот как работает IntToStr (почти что честный) на ассемблере. Писать честный IntToStr мне не хотелось, поэтому я тут схитрил. Завел массив из 3 байт под строку, второй и третий байты сразу обнуляются. Числа бывают от 1 до 15, поэтому если число было меньше 10, то к значению прибавляем магическое число 48 (ASCII код для нуля) и получаем нужный первый байт буфера. Получается тоже самое что и на Си, когда пишем c = ‘0’ + i. Поскольку второй байт уже нулевой у нас получается готовая null-terminated строка, неважно что буфер из 3 байт. Если число больше 9, то первая цифра всегда 1, а вторая это остаток от деления на 10. Тут уже третий байт играет роль конца строки.
mov [buffer+1], 0
mov [buffer+2], 0
.IF bl < 10
add bl, asciiShift
mov [buffer], bl
sub bl, asciiShift
.ELSEIF bl > 9
mov al, asciiShift
inc al
mov [buffer], al
xor ax, ax
mov al, bl
mov cl, 10
div cl
add ah, asciiShift
mov [buffer+1], ah
.ENDIF
Вот так выглядит игровое поле
Добавляем интерактив
Для управления можно пользоваться курсором или кликать мышкой по тайлу, который надо переместить, благо вариант перемещения только один. Перемещение сводится к тому чтобы в массиве тайлов поменять местами перемещаемый и нулевой тайл. Смещение нулевого тайла будет +1/-1 для перемещений вправо/влево и +4/-4 для перемещения вверх/вниз. Путь у тайла только один, поэтому надо только проверить выход за диапазон и поменять местами два элемента в массиве тайлов. Если тайл переместился, то перерисовать окно. Добавим вот такие обработчики в нашу оконную процедуру.
.IF uMsg == WM_KEYDOWN
.if wParam == VK_LEFT
invoke ProcessArrow, hWin, wParam
.elseif wParam == VK_RIGHT
invoke ProcessArrow, hWin, wParam
.elseif wParam == VK_UP
invoke ProcessArrow, hWin, wParam
.elseif wParam == VK_DOWN
invoke ProcessArrow, hWin, wParam
.endif
.ENDIF
.IF uMsg == WM_LBUTTONUP
invoke ProcessClick, hWin, lParam
.ENDIF
Сначала посмотрим как реализовано перемещение тайлов курсором. Вот немного укороченная версия процедуры ProcessArrow. FindEmptyTileIndex возвращает в регистре eax индекс пустого тайла . В зависимости от нажатой клавиши проверяем выход за границы диапазона, т.е. можно ли переместить тайл в данной позиции в данном направлении. Если нельзя, уходим на метку pass в конец процедуры, если можно, то вызываем последовательно SwapTiles, RedrawWindow и ProcessPossibleWin.
ProcessArrow proc hWin:DWORD, key:DWORD
call FindEmptyTileIndex
.IF key == VK_UP
cmp eax, 12
ja pass
;when tile goes up, new empty tile index (ETI) will be ETI+4,
mov ebx, eax
add ebx, 4
.ENDIF
.IF key == VK_RIGHT
;empty tile shouldnt be on 0, 4, 8, 12 indexes
cmp eax, 0
je pass
cmp eax, 4
je pass
cmp eax, 8
je pass
cmp eax, 12
je pass
;when tile goes right, new empty tile index (ETI) will be ETI-1,
mov ebx, eax
dec ebx
.ENDIF
invoke SwapTiles, eax, ebx
.IF hWin != NULL ;little trick to simplify initial random data
invoke RedrawWindow, hWin, NULL, NULL, RDW_INVALIDATE
invoke ProcessPossibleWin, hWin
.ENDIF
pass:
ret
ProcessArrow endp
Для перемещения тайла от кликов мышью нужно понять по какому тайлу кликнули и проверить, можно ли его перемещать. Для этого в цикле (двойной цикл организован через директиву MASM .WHILE) вызываем CalculateTileRect и проверяем находится ли курсор мыши внутри прямоугольника. Принцип проверки тот же, что и в ProcessArrow — cmp в ряд, только команды условного перехода другие. Внутри ProcessArrow je (jump equal), а тут ja jb (jump above jump below). Дальше все тоже самое что и с курсором, только наоборот. Смотрим разницу между индексами пустого и кликнутого тайла и вызываем процедуру ProcessArrow (наверное не самое удачное название) с нужными аргументами. Сокращенная версия процедуры.
ProcessClick proc hWin:DWORD, lParam:DWORD
local rct : RECT
movsx ebx, WORD PTR [ebp+12] ; x coordinate
movsx ecx, WORD PTR [ebp+14] ; y coordinate
mov vert, 0
.WHILE vert < 4
mov hor, 0
.WHILE hor < 4
invoke CalculateTileRect, ADDR Rct, hor, vert
cmp ebx, Rct.left
jb next
cmp ebx, Rct.right
ja next
cmp ecx, Rct.top
jb next
cmp ecx, Rct.bottom
ja next
; the idea is that tile can be moved only if there is a particular diff
; between its index and empty tile index
; -1, +1 ,-4, +4 for different directions, similar to ProcessArrow proc
call FindEmptyTileIndex
.IF index > al
sub index, al
.IF index == 1
invoke ProcessArrow, hWin, VK_LEFT
.ELSEIF index == 4
invoke ProcessArrow, hWin, VK_UP
.ENDIF
.ENDIF
next:
inc hor
.ENDW
inc vert
.ENDW
ret
ProcessClick endp
Вспомогательные процедуры типа проверки на окончание игры, или смены местами значений в массиве я приводить не буду, т.к. они банальны, а статья и так разрослась. Теперь, когда все готово, в итоге получилось 587 строк в Main.asm и 8192 байта исполняемый файл. Размер екзешника меня приятно порадовал — 8 килобайт это и для прежних времен немного, а сейчас и подавно. Полный код приложения можно увидеть в гитхабе.
Заключение
Наша игра готова. Мы увидели как это делается в привычной IDE, узнали откуда брать сигнатуры и как вызывать функции WinAPI, поняли что надо сделать чтобы создать полноценное оконное приложение, использовали директивы MASM для упрощения кода. Хоть я никогда и не использовал ассемблер в коммерческой разработке, но интерес к нему был с юных лет. Начиная с изучения ассемблера для Z80, знаменитого Спектрума и его многочисленных клонов. Писать пусть и очень простую, но полноценную игру на ассемблере мне по-настоящему понравилось. Надеюсь читателям тоже было интересно!
Assembly language programming tutorials
With x86 / x64 Instruction Set Architectures (ISA)
- Tutorials are created on a x64 Windows 10 PC with Windows SDK 10.0.17763.132 and Visual Studio Community 2017
- Presently these tutorials are using ml64.exe TO ASSEMBLE AND LINK .ASM files (Microsoft’s Assembler)
- Plans to include Linux and other popular OSes
01 — Hello World, Message Box, Tutorial
- HelloWorld.ASM located in source folder
- Simple runme.bat file to assemble and link the .ASM file to produce a running executable file
- requires kernel32.lib
- requires user32.lib
WHAT content to expect in later tutorials
- Explanations of the GPRS (General Purpose Registers)
- More about linking DLLS and LIBS into your ASM files
- Gotchas! What to look out for when writing assembly code, considerations, directives and memory management
- More, about using DLLs and LIBs, specific library functions, where they are located and how to successfully bring them into your assembly code to implement their various functionalities.
- Writing DLLS and SOs in assembly and linking them into your C/C++, etc. programs
- Live debugging your assembly code
- Info on using important command-line options of various assemblers effectively
- Using alternate linkers for your OBJ files (instead of assembler) and writing assembly code for embedded hardware applications
- Each tutorial will dynamically develop accompanying information focused around each individual idea, along with insights and use case scenarios where applicable
- Files, HTML and information concerning where to find information regarding specific programming patterns, personal reads, other tutorials and other people’s insights into writing effective assembly code etc..
- De-constructions, taking apart higher level code to understand how it equates to pure assembly, how opcodes are generated
- Light coverage of how compilers optimize code and produce optimized assembly code, with deeper coverage of how to hand write, profile and optimize assembly code.
- Advanced Level content, using the media and streaming instruction sets (also known as vectorization) in both AMD and INTEL CPUS to fully unlock the potential of vectorizing algorithms with regards to speed, performance and mathematics aimed at 3D graphics, audio, Digital Signal Processing (DSP), real time media streaming, transcoding, etc..
- Virtualization, security and writing safe, robust and debuggable code which accounts for modern day software/hardware vulnerabilities by focusing on specific types of attacks and how to limit exposure to these risks, within your assembly code based on specific features contained in Intel and AMD processor technologies.
This is my first tutorial series. I am planning on delivering more content, and have already begun brainstorming a more thorough set of accompanying information and subsequent tutorials to aid entirely in making learning assembly language programming approachable, whilst also revealing how this knowledge can contribute to all your current and future programming projects. Additionally, towards that end, I will also be engaging the importance of understanding at the conceptual level how a Central Processing Unit (CPU) behaves as a state machine with various physical logic gates at its core levels. Future material will also include a decent treatment of how the higher level language abstractions, such as object oriented data structures, virtual tables, and memory management end up looking/working after compiling into assembly code. It is my sincerest hope, that with these tutorials one will be able to develop a deeper understanding of their personal higher level code. That this will assist in improving one’s overall code quality and performance when writing software programs and libraries, which reflect within them, a clearer and deeper understanding of these lower level abstractions. Have fun! Be open minded about assembly, it is an art form, which demands a careful minding of how a processor and basic logic circuits work together to run software on your personal computer, smart phone and pretty much all products that employ microprocessor based architectures.
In this guide you will learn how to install MASM32 on a Windows 10 machine, and also how to modify the Path environment variable to use the MASM tools directly from the command prompt. By the end of the guide you will have an environment to start writing software in assembly.
Let’s begin.
Download MASM for Windows 10
The first thing you need to do is head over to the MASM32 SDK download page. Select a mirror and your download should begin.
Next open up the archive and extract the installer to your desktop.
Installing MASM on Windows 10
Double click on the installer. It will ask for administrator privileges. This application is deemed to be safe so you can safely press yes. After you have granted permissions you will see a dialog similar to the one below:
Click on the install icon on the top left and select the drive you want to use to install MASM. I will select my D:\ partition. MASM will install all the necessary files to the root directory of whatever partition you select.
Tip: I like to create a separate partition for my assembly projects and install MASM to the root directory of the partition. This makes it much easier to include libraries and includes such as windows.inc and friends.
MASM will proceed to run a bunch of tests to see if you’re computer is compatible and able to run MASM32. You will see a number of dialog boxes appear that require you to press the ok button.
Once the tests are complete press the extract button.
After the extraction has completed you will see another dialog box that informs you a command prompt will appear to complete the installation. Press ok and let the installation finish. It should only take a few moments.
After the installation has finished you will once again see a couple of dialog boxes that require you to press ok. You will also see a different type of dialog box similar to the one below. This runs a VB script to add an icon to your desktop for the MASM editor. If you want an icon on your desktop press yes.
Finally if everything went well you will see the final dialog box telling you the installation is complete.
Don’t run off just yet
I don’t use the default MASM editor. Instead I use Vim, or Sublime. This means I need to call the MASM compiler and linker directly from the command line to build my code. To do this you need to add the location of the MASM32 installation directory to your Path environment variable.
Adding a directory to environment variable Path on Windows 10
Open up the system properties dialog by entering the following command on the command prompt:
sysdm.cpl SystemProperties
Click the advanced tab, and click the Environment Variables button.
Next select the Path item from the list and click Edit.
On the next dialog press New and enter the path to the MASM bin directory. This contains the tools needed to compile and link assembly. For me the path is D:\masm32\bin. In most cases just change the drive letter if you installed to a different partition. The rest of the path is the same.
Press the ok button, and then then the apply button to save and close the system properties dialog.
Open a new command prompt and enter the following command to run the MASM assembler:
ml
If everything installed correctly and your Path is setup correctly you will see some output similar to the image below:
💡 Assembly Language Weekly Newsletter
Every week I publish a newsletter that contains tutorials, tips, tricks and resources related to assembly language. If you would like to receive this newsletter subscribe here: http://eepurl.com/ghximb
All Done!
And that’s all there is to it folks. If you want to hit the ground running check out my guide on how to compile and link MASM assembly to produce an executable on Windows 10.
If you’re having any issues following this guide please leave a comment and I will help you the best I can. You can also check out the official documentation includes detailed installation instructions and FAQ’s to solve common problems.
Последнее обновление: 12.10.2023
NASM является кроссплатформенным ассемблером, который доступен в том числе и на Windows. Рассмотрим, как использовать NASM на Windows.
Установка
Для работы с NASM нам надо установить непосредственно сам ассемблер. Для этого на официальном сайте перейдем на страницу https://www.nasm.us/pub/nasm/releasebuilds/2.16.01/win64/,
где находятся файлы ассемблера NASM версии 2.16.01 для 64-разрядной версии Windows:
Здесь нам доступен ассемблер в виде двух пакетов. Один пакет установщика (nasm-2.16.01-installer-x64.exe), а второй — архив (nasm-2.16.01-win64.zip).
Загрузим zip-архив.. Например, загрузим zip-архив и после загрузки распакуем его. В папке распакованного архива мы можем найти два файла
Это прежде всего сам ассемблер — файл nasm.exe и дизассемблер — файл ndisasm.exe
Чтобы не прописывать весь путь к ассемблеру, занесем его в переменные среды. Для этого можно в окне поиска в Windows ввести «изменение переменных среды текущего пользователя»:
Нам откроется окно Переменныех среды:
И добавим путь к ассемблеру. Например, в моем случае архив ассемблера распакован в папку C:\nasm-2.16.01
, соответственно я указываю
в переменной Path среды эту папку:
Если все настроено правильно, то мы можем запустить командную строку и с помощью команды nasm -v узнать текущую версию ассемблера:
C:\Users\eugen>nasm -v NASM version 2.16.01 compiled on Dec 21 2022 C:\Users\eugen>
Начало работы с NASM
Определим в файловой системе каталог для файлов с исходным кодом и создадим в нем следующий файл hello.asm:
global _start ; делаем метку метку _start видимой извне section .text ; объявление секции кода _start: ; метка _start - точка входа в программу mov rax, 22 ; произвольный код возврата - 22 ret ; выход из программы
Рассмотрим поэтапно данный код. Вначале идет директива global:
global _start
Данная директива делает видимой извне определенную метку программы. В данном случае метку _start
, которая является точкой входа в программу. Благодаря этому компоновщик при компоновке программы в исполняемый файл сможет увидеть данную метку.
Затем идет секция кода программы, которая и определяет выполняемые программой действия. Для определения секции применяется директива
section, после которой указывается имя секции. Причем секция кода программы должна называться .text.
section .text
Далее собственно идет код программы. И он начинается с определения метки _start, на которую собственно и проецируется
программа. Сама по себе метка представляет произвольное название, после которого идет двоеточие. После двоеточия могут идти инструкции программы или определения данных.
Метка _start
выступает в качестве точки входа в программу. Название подобной метки произвольное, но обычно это или _start
или _main
Наша программа не производит какой-то феноменальной работы. Все что она делает — это помещает в регистр rax число 22 и завершается. Для помещения числа в регистр применяется инструкция
mov:
mov rax, 22
Инструкция mov помещает в первый операнд — регистр rax значение из второго операнда — число 22.
Затем идет вызов инструкции ret, которая завершает программу
ret
Кроме директив и инструкций, которые определяют действия программы, также следует отметить комментарии. Комментарии начинаются с точки с запятой ;. Комментарии не учитываются при компиляции,
никак не влияют на объект или работоспособность программы и нужны лишь в качестве текстового описания отдельных строк или блоков программы.
global _start ; делаем метку метку _start видимой извне - это текст комментария
Компиляция
Для компиляции откроем командную строку, перейдем в ней к папке с исходным кодом (где располагается файл hello.asm) и выполним следующую команду
nasm -f win64 hello.asm -o hello.o
Здесь с помощью опции -f указывается формат файла, в который мы хотим скомпилировать код.
Для 64-разрядной ОС Windows это — win64. После этого указывается файл, который мы хотим скомпилировать — наш файл hello.asm.
Затем опция -o hello.o указывает на имя скомпилированного файла. В результате выполнения этой команды будет создан объектный файл hello.o
Однако файл hello.o — это объектный файл, а не исполняемый. Он содержит машинный код, который понимает компьютер, но чтобы его можно было запускать как обычную
программу, его надо скомпоновать в исполняемый файл. И для этого нужна программа компоновщика (он же линковщик/линкер или linker).
Недостатком NASM является то, что он не предоставляет встроенного компоновщика. И нам надо использовать внешнюю программу компоновки. Где ее взять?
Далее я рассмотрю два варианта — использование компоновщика из пакета GCC и использование компоновщика компилятора Visual C/C++, который идет вместе с Visual Studio. Оба варианта равноценны.
Компоновка с помощью GCC
Вначале нам надо установить пакет GCC. Пакет компиляторов GCC для Windows не имеет какого-то одного единого разработчика, разные организации могут выпускать свои сборки. Для Windows одной из наиболее популярных версий GCC является пакет средств для разработки от
некоммерческого проекта MSYS2. Следует отметить, что для MSYS2 требуется 64-битная версия Windows 7 и выше (то есть Vista, XP и более ранние версии не подходят)
Итак, загрузим программу установки MSYS2 с официального сайта MSYS2:
После загрузки запустим программу установки:
На первом шаге установки будет предложено установить каталог для установки. По умолчанию это каталог C:\msys64:
Оставим каталог установки по умолчанию (при желании можно изменить). На следующем шаге устанавливаются настройки для ярлыка для меню Пуск, и затем собственно будет произведена установка.
После завершения установки нам отобразить финальное окно, в котором нажмем на кнопку Завершить
После завершения установки запустится консольное приложение MSYS2.exe. Если по каким-то причинам оно не запустилось,
то в папке установки C:/msys64 надо найти файл usrt_64.exe:
Теперь нам надо установить собственно набор компиляторов GCC. Для этого введем в этом приложении следующую команду:
pacman -S mingw-w64-ucrt-x86_64-gcc
Для управления пакетами MSYS2 использует пакетный менеджер Packman. И данная команда говорит пакетному менелжеру packman установить пакет mingw-w64-ucrt-x86_64-gcc
,
который представляет набор компиляторов GCC (название устанавливаемого пакета указывается после параметра -S
).
Если после завершения установки мы откроем каталог установки и зайдем в нем в папку C:\msys64\ucrt64\bin,
то найдем там файл компоновщика ld
Для упрощения запуска компоновщика мы можем добавить путь к нему в Переменные среды:
Чтобы убедиться, что нам доступен компоновщик GCC — программа ld, введем следующую команду:
ld --version
В этом случае нам должна отобразиться версия компоновщика
c:\asm>ld --version GNU ld (GNU Binutils) 2.40 Copyright (C) 2023 Free Software Foundation, Inc. This program is free software; you may redistribute it under the terms of the GNU General Public License version 3 or (at your option) a later version. This program has absolutely no warranty. c:\asm>
Теперь скомпонуем файл hello.o в исполняемый файл hello.exe с помощью следующей команды:
ld hello.o -o hello.exe
Мы можем запустить этот файл, введя в консоли его название и нажав на Enter. Но наша программа ничего не выводит на консоль, поэтому после запуска программы мы ничего не увидим. Тем не менее
наша программа устанавливает регистр RAX. А значение этого регистра при завершении программы рассматривается в Windows как статусный код возврата, который сигнализирует, как завершилась программа (успешно или не успешно).
И мы можем этот статусный код получить, если после выполнения программы введем команду
echo %ERRORLEVEL%
И нам должно отобразится число 22 — значение регистра RAX. Полный консольный вывод:
c:\asm>nasm -f win64 hello.asm -o hello.o c:\asm>ld hello.o -o hello.exe c:\asm>hello.exe c:\asm>echo %ERRORLEVEL% 22 c:\asm>
Установка компоновщика link.exe
Компоновщик от GCC — не единственный компоновщик, который можно использовать для компоновки программы на Windows. Еще один вариант представляет компоновщик
link.exe из пакета инструментов разработки для C/C++ для Visual Studio. Условным плюсом этого компоновщика может быть то, что его
разработчик — Microsoft, поэтому можно ожидать более лучшей интеграции с Windows. Поэтому также рассмотрим и этот способ.
Сперва нам надо установить для Visual Studio инструменты разработки для C/C++. Установщик для среды Visual Studio можно загрузить по следующему адресу:
Microsoft Visual Studio 2022. После загрузки программы установщика Visual Studio запустим ее и в окне устанавливаемых
опций выберем пункт Разработка классических приложений на C++:
И нажмем на кнопку установки.
В зависимости от конкретной подверсии и номера сборки Visual Studio точное расположение файлов может варьироваться. Например, в моем случае это папка
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\bin\Hostx64\x64\
. И в этой папке можно найти программу компоновщика link.exe.
Причем при обновлениях Visual Studio этот расположение может измениться, так как при обновлении меняется и версия Visual Studio. Поэтому к конкретным путям можно не цепляться. Вместо этого
мы можем перейти к меню Пуск и в списке программ найти пункт Visual Studio и подпункт
x64 Native Tools Command Prompt for VS 2022
Нам должна открыться консоль. Введем в нее link, и нам отобразится версия ассемблера и некоторая дополнительная информация:
********************************************************************** ** Visual Studio 2022 Developer Command Prompt v17.7.4 ** Copyright (c) 2022 Microsoft Corporation ********************************************************************** [vcvarsall.bat] Environment initialized for: 'x64' C:\Program Files\Microsoft Visual Studio\2022\Community>link Microsoft (R) Incremental Linker Version 14.37.32824.0 Copyright (C) Microsoft Corporation. All rights reserved. usage: LINK [options] [files] [@commandfile] options: /ALIGN:# /ALLOWBIND[:NO] /ALLOWISOLATION[:NO] /APPCONTAINER[:NO] /ASSEMBLYDEBUG[:DISABLE] /ASSEMBLYLINKRESOURCE:filename /ASSEMBLYMODULE:filename /ASSEMBLYRESOURCE:filename[,[name][,PRIVATE]] /BASE:{address[,size]|@filename,key} /CLRIMAGETYPE:{IJW|PURE|SAFE|SAFE32BITPREFERRED} /CLRLOADEROPTIMIZATION:{MD|MDH|NONE|SD} /CLRSUPPORTLASTERROR[:{NO|SYSTEMDLL}] /CLRTHREADATTRIBUTE:{MTA|NONE|STA} /CLRUNMANAGEDCODECHECK[:NO] /DEBUG[:{FASTLINK|FULL|NONE}] ............................
В частности, можно увидеть, что версия компоновщика — 14.37.32824.0 и все опции, которые можно передать программе при компоновке. Стоит отметить, что запуск этой этой утилиты фактически представляет запуск файла C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat —
он по сути вызывает другой файл — vcvarsall.bat, который собственно и настраивает окружение для выполнения ассемблера.
Используем этот компоновщик. Для этого откроем программу x64 Native Tools Command Prompt for VS 2022 и перейдем в ней к папке, где располагается объектный файл
hello.o. Затем выполним следующую команду
link hello.o /entry:_start /subsystem:console /out:hello2.exe
В данном случае компоновщику передаем ряд параметров:
-
собственно объектный файл hello.o, который будет компилироваться в исполняемое приложение
-
Параметр
/entry:_start
указывает компоновщику на точку входа в программу —
это наша метка «_start». -
Параметр
/subsystem:console
указывает компоновщику, что создается консольное (не графическое) приложение. -
Параметр
/out:hello2.exe
устанавливает имя генерируемого файла приложение — оно будет называться «hello2.exe».
В результате будет создан файл hello2.exe, который мы также можем запускать:
c:\asm>link hello.o /entry:_start /subsystem:console /out:hello2.exe Microsoft (R) Incremental Linker Version 14.37.32824.0 Copyright (C) Microsoft Corporation. All rights reserved. c:\asm>hello2.exe c:\asm>echo %ERRORLEVEL% 22 c:\asm>
Создание первой программы на Windows
Теперь создадим более осмысленную программу, которая выводит на экран строку,kernel32.lib и для этого изменим файл hello.asm следующим образом:
global _start ; делаем метку метку _start видимой извне extern WriteFile ; подключем функцию WriteFile extern GetStdHandle ; подключем функцию GetStdHandle section .data ; секция данных message: db "Hello METANIT.COM!",10 ; строка для вывода на консоль section .text ; объявление секции кода _start: ; метка _start - точка входа в программу sub rsp, 40 ; Для параметров функций WriteFile и GetStdHandle резервируем 40 байт (5 параметров по 8 байт) mov rcx, -11 ; Аргумент для GetStdHandle - STD_OUTPUT call GetStdHandle ; вызываем функцию GetStdHandle mov rcx, rax ; Первый параметр WriteFile - в регистр RCX помещаем дескриптор файла - консоли mov rdx, message ; Второй параметр WriteFile - загружаем указатель на строку в регистр RDX mov r8d, 18 ; Третий параметр WriteFile - длина строки для записи в регистре R8D xor r9, r9 ; Четвертый параметр WriteFile - адрес для получения записанных байтов mov qword [rsp + 32], 0 ; Пятый параметр WriteFile call WriteFile ; вызываем функцию WriteFile add rsp, 40 ret ; выход из программы
Разберем эту программу. Для вывода строки на консоль нам надо использовать нативные функции GetStdHandle и WriteFile.
И чтобы воспользоваться этими функциями подключаем их с помощью директивы extern
extern WriteFile extern GetStdHandle
Далее идет определение секции данных — секции .data:
section .data ; секция данных message: db "Hello METANIT.COM!",10 ; строка для вывода на консоль
В секции .data определена метка message
, на которую проецируется строка. По сути message
— это переменная, которая представляет строку.
После метки указывается тип данных. Строка в ассемблере — это просто набор байтов, поэтому имеет тип db. Затем в кавычках определяется собственно выводимая строка — "Hello METANIT.COM!",10
. Обратите внимание на 10 после строки — это код символа перевода строки. То есть при выводе мы ожидаем, что будет происходить перевод на другую строку.
Затем идет секция кода — секция .text
и метка _start
— точка входа в программу
section .text ; объявление секции кода _start: ; метка _start - точка входа в программу
В программе для вызова функций первым делом необходимо настроить стек. В частности, резервируем в стеке 40 байт для параметров функций GetStdHandle и WriteFile и при этом учитываем выравнивание
стека по 16-байтной границе. Указатель на верхушку стека хранится в регистре
rsp. Поэтому вычитаем с помощью инструкции sub
из значения в регистре rsp 40 байт
sub rsp, 40
Почему 40? Прежде всего при вызове функций WinAPI (как в данном случае функций GetStdHandle и WriteFile) необходимо зарезервировать в стеке как минимум 32 байта — так называемое
«shadow storage» (теневое хранилище). Далее нам надо учитывать количество параметров функции. Пеовые 4 параметра функций передаются через регистры, а параметры начиная с 5-го передаются через стек.
Соответственно для 5-го и последующих параметров надо выделить в стеке область. Для каждого параметра вне зависимости от его размера выделяется 8 байт. Функция WriteFile как раз принимает 5 параметров, поэтому для нее надо выделить дополнительные 8 байт в стеке.
Поэтому получаем 32 байта + 8 байт (5-й параметр WriteLine) = 40 байт. Количество параметров смотрим по функции с наибольшим количеством параметров. Третий момент — нам надо учитывать, что перед вызовом функций WinAPI стек имел выравнивание по 16 байтовой границе, то есть значение в RSP
должно быть кратно 16. По умолчанию при вызове функции в стек помещается адрес возврата функии размером 8 байт. Поэтому наши 40 байт + 8 байт (адрес возврата из функции) дадут 48 байт — число кратное 16.
Вначале нам надо использовать встроенную функцию GetStdHandle(), которая позволяет получить дескриптор на устройство ввода-вывода. Она имеет следующее определение на C:
HANDLE WINAPI GetStdHandle( _In_ DWORD nStdHandle );
Функция GetStdHandle()
получает числовой код устройства, с которым мы хотим взаимодействовать. В нашем случае нам надо получить устройство стандартного вывода (для вывода строки), которым по умолчанию является консоль. Для обращения к консоли надо передать число -11, которое надо поместить в
регистр rcx:
mov rcx, -11 ; Аргумент для GetStdHandle - STD_OUTPUT
После установки параметра этой функции вызываем ее с помощью инструкции call:
call GetStdHandle
В результате выполнения функция GetStdHandle возвращает дескриптор — объект, через который мы можем взаимодействовать с консолью. Этот дескриптор помещается в регистр
rax. Получив этот дескриптор, используем его для вывода на консоль строки с помощью функции WriteFile. Для справки ее определение на С++
BOOL WriteFile( [in] HANDLE hFile, [in] LPCVOID lpBuffer, [in] DWORD nNumberOfBytesToWrite, [out, optional] LPDWORD lpNumberOfBytesWritten, [in, out, optional] LPOVERLAPPED lpOverlapped );
Вызов функции GetStdHandle помещает в регистр rax дескриптор консоли. И для вывода строкии на консоль с помощью функции WriteFile нам надо поместить
этот дескриптор в регистр rcx
mov rcx, rax
Затем также с помощью инструкции mov загружаем в регистр rdx адрес выводимой строки
mov rdx, message
Далее в регистр r8d помещаем длину выводимой строки в байтах — в данном случае это 18 байт:
mov r8d, 18
Поскольку у нас строка с символами ASCII, и каждый символ эквивалентен 1 байту, то получаем, что в строке message
с учетом последнего символа с числовым кодом 10 будет 18 байт.
Затем в регистре r9 устанавливаем адрес четвертого параметра функции WriteFile:
xor r9, r9
В данном случае нам не нужно количество считанных байтов, и с помощью инструкции xor обнуляем значение регистра r9.
Последний — пятый параметр функции WriteFile должен иметь значение NULL, по сути 0. Поэтому устанавливаем для него значение 0, смещаясь в стеке вперед на 32 байта (4 параметра * 8):
mov qword [rsp + 32], 0
Инструкция mov помещает значение в определенное место. Здесь в качестве значения служит число 0. А место определяется выражением
qword [rsp + 32]
. qword
указывает, что этот операнд описывает адрес размером в четыре слова, что означает 8 байтов (слово имеет длину 2 байта). То есть число 0 представляет 8-байтное значение и помещается по адресу rsp + 32
.
И далее собственно вызываем функцию WriteFile:
call WriteFile
Этот вызов должен привести к выводу строки на консоль. После этого восстанавливаем значение верхушки стека. Для этого с помощью инструкции add прибавляем
к значению в регстре rsp ранее отнятые 40 байт:
add rsp, 40
И с помощью инструкции ret выходим из программы.
Компиляция
Поскольку теперь программа использует внешние функции WinApi — GetStdHandle и WriteFile, которые определены во внешней библиотеке kernel32.lib, то при компоновке
нам надо подключить эту библиотеку. В зависимости от используемого компоновщика/линкера этот процесс может немного отличаться. Например, при использовании компоновщика ld
из комплекта инструментов GCC все подключаемые библиотеки передаются с помощью опции -l:
ld hello.o -o hello.exe -l kernel32
Здесь последний параметр -l kernel32
как раз указывает, какую библиотеку надо подлючить, при чем название библиотеки указывается без расширения файла.
При использовании компоновщика link.exe от Microsoft подключаемая библиотека просто передается вместе с компонуемыми файлами:
link hello.o kernel32.lib /entry:_start /subsystem:console /out:hello2.exe
Итак, повторно скомпилируем файл и скомпонуем одним из компоновщиков. Затем запустим полученный исполняемый файл, и консоль должна вывести нам строку.
Полный процесс при использовании компоновщика ld из комплекта GCC:
c:\asm>nasm -f win64 hello.asm -o hello.o c:\asm>ld hello.o -o hello.exe -l kernel32 c:\asm>hello.exe Hello METANIT.COM! c:\asm>
Полный процесс при использовании компоновщика link от Microsoft (компоновка выполняется в программе x64 Native Tools Command Prompt for VS 2022):
c:\asm>nasm -f win64 hello.asm -o hello.o c:\asm>link hello.o kernel32.lib /entry:_start /subsystem:console /out:hello2.exe Microsoft (R) Incremental Linker Version 14.37.32824.0 Copyright (C) Microsoft Corporation. All rights reserved. c:\asm>hello2.exe Hello METANIT.COM! c:\asm>
Содержание
- MASM под Windows: быстрый старт
- Установка MASM
- Простейшая программа
- Трансляция и запуск программы
- Как это устроено
- Командный файл для упрощения запуска
- MASM, TASM, FASM, NASM под Windows и Linux
- Ассемблер для Windows используя Visual Studio
- Создание проекта
- Добавление исходного кода
- Отладка
- GUI Turbo Assembler 5.0 (TASM) для Windows 10
- Описание и возможности
- Как пользоваться
- Загрузка и установка
- Инструкция по работе
- Достоинства и недостатки
- Похожие приложения
- Системные требования
- Скачать
- Видеообзор
- Вопросы и ответы
- Ассемблер под Windows для чайников
MASM под Windows: быстрый старт
Олег Французов
2017
В этом документе кратко описан процесс установки учебной среды на основе ассемблера MASM под ОС Windows, а также порядок работы с ней.
Установка MASM
Скачайте архив c MASM с сайта arch32.cs.msu.su.
Скачайте файл prompt.bat и положите его в ваш рабочий каталог.
Простейшая программа
Для следующего шага вам потребуется текстовый редактор, пригодный для работы с программным кодом. Заметим, что Microsoft Word или встроенный в Windows редактор WordPad являются текстовыми процессорами и для работы с программным кодом непригодны. Редактор Notepad (Блокнот) подходит для работы с текстовыми файлами (plain text), но неудобен в качестве программистского редактора — в нем отсутствует подсветка синтаксиса и другие стандартные для таких редакторов функции.
Вы можете воспользоваться вашим любимым текстовым редактором или, если вы затрудняетесь с выбором, скачать простой программистский текстовый редактор Notepad2.
Примечание: Если вы решили скачать Notepad2, при первом запуске установите ширину табуляции (Tabulator width) в значение 8 при помощи меню Settings > Tab Settings.
Создайте в вашем рабочем каталоге файл hello.asm следующего содержания:
Трансляция и запуск программы
Для запуска программы требуется ее оттранслировать. Первый шаг — запуск ассемблера MASM, который построит по исходному тексту програмы объектный файл:
Аргумент /c инструктирует ассемблер выполнить только трансляцию в объектный файл, без компоновки (которую мы выполним чуть позже). Аргумент /coff указывает формат объектного файла — COFF (Common Object File Format).
Аргумент /subsystem:console говорит компоновщику, что нужно построить консольное Windows-приложение.
Как это устроено
Командный файл prompt.bat запускает окно командной строки и задает переменные окружения так, чтобы программы ml и link были доступны без указания пути к ним, а пути к include- и lib-файлам MASM также были известны.
Пути заданы жестко, поэтому и требовалось распаковать архив в строго определенный каталог.
Командный файл для упрощения запуска
Когда вам надоест каждый раз набирать три команды для трансляции и запуска программ, создайте такой командный файл (назвать его можно, например, mkr.bat — то есть make/run):
Использовать его можно будет следующим образом:
Несколько комментариев по устройству этого командного файла:
Команда @echo off отключает дублирование каждой исполняемой команды в окне командной строки.
Аргумент /nologo при вызове ассемблера и компоновщика убирает строчку “Copyright (C) Microsoft”, захламляющую экран.
%1 меняется на аргумент, который передан командному файлу, то есть имя программы на ассемблере (выше — hello.asm ).
n1 меняется на тот же аргумент, но без расширения (выше — hello ).
Связка && выполняет очередную команду, только если предыдущая завершилась успешно. В случае ошибок трансляции ваша программа запущена не будет.
То, что получилось в итоге — это простейшая система программирования, состоящая из транслятора (ассемблера MASM), текстового редактора (Notepad2 или иного, если вы его предпочли) и примитивной системы сборки на единственном командном файле.
Несмотря на простоту этой системы, она основывается на тех же общих принципах, что и более сложные системы программирования. Подробнее с этим вы сможете познакомиться на втором курсе.
Источник
MASM, TASM, FASM, NASM под Windows и Linux
В данной статье я хочу рассмотреть вопросы, которые могут возникнуть у человека, приступившего к изучению ассемблера, связанные с установкой различных трансляторов и трансляцией программ под Windows и Linux, а также указать ссылки на ресурсы и книги, посвященные изучению данной темы.
Используется для создания драйверов под Windows.
По ссылке переходим на сайт и скачиваем пакет (masm32v11r.zip). После инсталляции программы на диске создается папка с нашим пакетом C:\masm32. Создадим программу prog11.asm, которая ничего не делает.
Произведём ассемблирование (трансляцию) файла prog11.asm, используя ассемблер с сайта masm32.
Ключ /coff используется здесь для трансляции 32-битных программ.
Линковка производится командой link /subsystem:windows prog11.obj (link /subsystem:console prog11.obj)
MASM — один из немногих инструментов разработки Microsoft, для которых не было отдельных 16- и 32-битных версий.
Также ассемблер версии 6. можно взять на сайте Кипа Ирвина kipirvine.com/asm, автора книги «Язык ассемблера для процессоров Intel».
Кстати, вот ссылка на личный сайт Владислава Пирогова, автора книги “Ассемблер для Windows”.
MASM с сайта Microsoft
Открываем этот файл архиватором (например 7zip). Внутри видим файл setup.exe, извлекаем его, открываем архиватором. Внутри видим два файла vc_masm.msi,vc_masm1.cab. Извлекаем файл vc_masm1.cab, открываем архиватором. Внутри видим файл FL_ml_exe_____X86.3643236F_FC70_11D3_A536_0090278A1BB8. Переименовываем его в файл fl_ml.exe, далее, произведём ассемблирование файла prog11.asm, используя ассемблер fl_ml.exe.
MASM в Visual Studio
Также MASM можно найти в папке с Visual Studio (у меня VS 10) вот здесь: C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe.
Для того, чтобы запустить на 32- или 64-разрядной системе и создавать программы, работающие как под 32-, так и под 64-разрядной Windows, подходит MASM32 (ml.exe, fl_ml.exe). Для того, чтобы работать на 32- и 64-разрядных системах и создавать программы, работающие под 64-разрядной Windows, но неработающие под 32-разрядной нужен ассемблер ml64.exe. Лежит в папке C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\amd64 и вот здесь — C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_amd64.
Программный пакет компании Borland, предназначенный для разработки программ на языке ассемблера для архитектуры x86. В настоящее время Borland прекратила распространение своего ассемблера.
Скачать можно, например, здесь. Инсталлятора нет, просто извлекаем программу. Вот исходник из книги Питера Абеля (рис. 3.2) «Язык Ассемблера для IBM PC и программирования».
Выполним ассемблирование (трансляцию) файла abel32.asm.
Корректность работы программы можно проверить, произведя линковку (tlink.exe) объектного файла и запустив полученный файл в отладчике.
Как было сказано выше, MASM можно использовать для работы с 16-битными программами. Выполним ассемблирование (трансляцию) программы abel32.asm с помощью ассемблера MASM:
Ключ /coff здесь не используется.
Линковка производится файлом link16.exe
В статье Криса Касперски «Сравнение ассемблерных трансляторов» написано, что «FASM — неординарный и весьма самобытный, но увы, игрушечный ассемблер. Пригоден для мелких задач типа „hello, world“, вирусов, демок и прочих произведений хакерского творчества.»
Скачаем FASM с официального сайта. Инсталлятора нет, просто извлекаем программу. Откроем fasm editor — C:\fasm\fasmw.exe. В папке C:\fasm\EXAMPLES\HELLO есть файл HELLO.asm.
Откроем файл HELLO.asm из fasmw.exe. Изменим строку include ‘win32ax.inc’ на строку include ‘c:\fasm\INCLUDE\WIN32AX.INC’. Запускаем из меню Run → Run.
Вот ссылки на ресурсы, посвященные FASM:
Для того, использовать FASM в Linux (у меня Ubuntu), скачаем соответствующий дистрибутив (fasm-1.71.60.tgz), распакуем его, в папке у нас будет бинарный файл fasm, копируем этот файл в /usr/local/bin для того, чтобы можно было запускать его из консоли, как любую другую команду.Выполним ассемблирование программы hello.asm из папки fasm/examples/elfexe/hello.asm.
Корректность работы программы можно проверить в отладчике.
Nasm успешно конкурирует со стандартным в Linux- и многих других UNIX-системах ассемблером Gas.
Nasm в Linux можно установить его с помощью менеджера пакетов или из командной строки: в дистрибутиве Debian (Ubuntu) командой apt-get install nasm, в дистрибутивах Fedora, CentOS, RedHat командой yum install nasm.
Создадим программу, которая 5 раз выводит сообщение “Hello”. Пример взят из книги Андрея Викторовича Столярова “Программирование на языке ассемблера NASM для ОС UNIX”. Учебник, а также библиотека “stud_io.inc” есть на личном сайте автора.
Выполним ассемблирование и линковку и запустим файл hello.asm.
NASM для Windows можно установить, скачав соответствующий дистрибутив с соответствующего сайта.
Ссылки на ресурсы, посвященные Nasm:
Стандартный ассемблер практически во всех разновидностях UNIX, в том числе Linux и BSD. Свободная версия этого ассемблера называется GAS (GNU assembler). Позволяет транслировать программы с помощью компилятора GCC.
Из учебников удалось найти только книгу на английском «Programming from the ground up». На русском удалось найти только одну главу из книги С. Зубкова «Assembler для DOS, Windows и UNIX».
Возьмем пример программы, которая ничего не делает, с сайта. Создадим программу gas.s
Выполним ассемблирование (трансляцию), линковку и запуск программы:
Если в данной программе изменить _start на main, то можно выполнить ассемблирование (трансляцию) и линковку компилятором gcc.
Выполним ассемблирование (трансляцию), линковку и запуск программы:
Выводы: если вы изучаете программирование под Windows, то вы можете остановить свой выбор на Masm; Tasm больше не поддерживается, но для обучения по старым классическим учебникам подойдёт.
Под Linux Gas подойдет тем, кто использует GCC, а тем, кому не нравится синтаксис Gas, подойдёт Nasm.
Источник
Ассемблер для Windows используя Visual Studio
Многие из нас изучали ассемблер в университете, но почти всегда это ограничивалось простыми алгоритмами под DOS. При разработке программ для Windows может возникнуть необходимость написать часть кода на ассемблер, в этой статье я хочу рассказать вам, как использовать ассемблер в ваших программах под Visual Studio 2005.
Создание проекта
В статье мы рассмотрим как вызывать ассемблер из С++ кода и обратно, передавать данные, а также использовать отладчик встроенный в Visual Studio 2005 для отладки кода на ассемблер.
Для начала нам нужно создать проект. Включаем Visual Studio, выбираем File > New > Project. В Visual Studio нет языка ассемблер в окне выбора типа проекта, поэтому создаем С++ Win32 проект. В окне настроек нового проекта выбираем «Empty Project».
По умолчанию Visual Studio не распознает файлы с кодом на ассемблер. Для того чтобы включить поддержку ассемблер нам необходимо настроить в проекте условия сборки указав какой программой необходимо компилировать файлы *.asm. Для этого выбираем пункт меню «Custom Build Rules. ».
В открывшемся окне мы можем указать специальные правила компиляции для различных файлов, Visual Studio 2005 уже имеет готовое правило для файлов *.asm, нам необходимо лишь включить его, установив напротив правила «Microsoft Macro Assembler» галочку.
Добавление исходного кода
Перейдем к написанию исходного кода нашего проекта. Начнем с добавления исходного кода на c++. Добавим новый файл в папку Source Files. В качестве Template выбираем C++ File и вводим желаемое имя файла, например main.cpp. Напишем функцию, которая будет считывать имя введенное пользователем, оформив это в виде функции readName() которая будет возвращать ссылку на считанное имя. Мы получим примерно следующее содержимое файла:
Теперь, когда мы знаем имя пользователя мы можем вывести приветствие, его будет выводить функция sayHello() которую мы напишем на ассемблер, чтобы использовать эту функцию сначала мы должны указать что она будет определена в другом файле, для этого добавим блок к main.cpp:
Этот блок говорит компилятору, что функция sayHello() будет объявлена в другом файле и будет иметь правила вызова «C». Компилятор C++ искажает имена функций так, что указание правил вызова обязательно. Кроме того мы хотим использовать функцию readName() из функции sayHello(), для этого необходимо добавить extern «C» перед определением функции readName(), это позволит вызывать эту функцию из других файлов используя правила вызова «C».
Теперь мы можем запустить проект, для этого просто выбираем Debug > Start Without Debugging или нажимаем комбинацию Ctrl-F5. Если все сделано верно, вы увидите окно программы:
Немного усложним задачу, попробуем написать на ассемблер функцию принимающую параметр и возвращающую значение. Для примера напишем функцию calcSumm() которая будет принимать целое число и возвращать сумму его цифр. Изменим наш код на С++ добавив в него информацию о функции calcSumm, ввод числа и собственно вызов функции. Добавим функцию в файл hello.asm, возвращаемое значение помещается в eax, параметры объявляются после ключевого слова PROC. Все параметры можно использовать в коде процедуры, они автоматически извлекутся из стека. Также в процедурах можно использовать локальные переменные. Вы не можете использовать эти переменные вне процедуры. Они сохранены в стеке и удаляются при возврате из процедуры:
Запустив проект мы увидим следующий результат выполнения:
Отладка
Конечно в данной задаче нет ничего сложного и она вовсе не требует использования ассемблер. Более интересным будет рассмотреть, а что же нам дает Visual Studio для разработки на ассемблер. Попробуем включить режим отладки и установим точку остановки в hello.asm, запустим проект, мы увидим следующее:
Окно Disassembly (Debug > Windows > Disassembly) показываем команды ассемблер для данного объектного файла. Код который мы написали на С++ показывается черным цветом. Disassembled code показывается серым после соответствующего ему кода на C++/ассемблер. Окно Disassembly позволяет отлаживать код и осуществлять stepping по нему.
Окно регистров (Debug > Windows > Registers) позволяет посмотреть значение регистров.
Окно памяти (Debug > Windows > Memory) позволяет посмотреть дамп памяти, слева мы видим шестнадцатеричные адрес, справа шеснадцатеричные значения соответствующих ячеек памяти, можно перемещаться, вводя адрес в соответствующее поле в верху окна.
Источник
GUI Turbo Assembler 5.0 (TASM) для Windows 10
Инструмент, о котором мы расскажем в этом материале, представляет собой программный пакет, используемый для разработки различного софта на языке ассемблера. Предлагаем бесплатно скачать GUI Turbo Assembler 5.0 (TASM) для Windows 10 x32/x64 Bit и познакомиться с данным продуктом ближе. По уже имеющейся на сайте традиции разберемся с возможностями утилиты, сразу после чего поговорим о ее загрузке и инсталляции на компьютер или ноутбук.
Описание и возможности
Программное обеспечение может использоваться как специалистами для полноценной работы, так и новичками для обучения. Приложение включает полноценную справочную систему, где можно найти ответы на все интересующие вас вопросы по теме. Графический интерфейс инструмента для программирования довольно простой и симпатичный. Работать с ним вполне комфортно. К ключевым возможностям можно отнести:
Утилита часто применяется для обучения программированию на языке ассемблере. Для расширения функций программы используются наборы дополнительных макросов.
Как пользоваться
Скачивание и установка приложения проводится довольно быстро и легко. Для этого не понадобится каких-то особых знаний и умений. Читайте инструкцию и выполняйте каждый ее шаг.
Загрузка и установка
Перед загрузкой утилиты важно понимать, что она относится к сложным и многогранным инструментам. Перед началом работы с ней потребуется пройти соответствующее обучение. Но для начала давайте произведем инсталляцию программы на компьютер. Для этого делаем следующее:
TASM в вашем полном распоряжении. Можно смело переходить к изучению и использованию программного обеспечения.
Инструкция по работе
Конечно же, в двух словах описать принципы работы с утилитой не получится. Для полного освоения софта вам потребуется не один день. Единственное, на чем хотелось бы сделать акцент, это то, что ввод, редактирование или копирование кодов здесь осуществляется через удобную панель. На этой же панели расположены основные клавиши для тестирования и запуска готового проекта. Кроме этого, в разделе настроек пользователь может изменить конфигурацию графического интерфейса или воспользоваться некоторыми дополнительными функциями.
При желании изучить все функции программного обеспечения можно почитать обучающую литературу по данной теме или просмотреть видео в сети.
Достоинства и недостатки
Пришло время уделить внимание рассмотрению сильных и слабых сторон приложения, о котором мы говорим сегодня.
Похожие приложения
В качестве среды разработки на ПК или ноутбуке можно также использовать такие решения:
Системные требования
Назовем рекомендуемые параметры электронной машины для стабильной работы софта:
Скачать
Прямо сейчас у вас есть возможность получить желаемый инструмент на свой ПК по ссылке ниже.
Версия: | 5.0 |
Разработчик: | Borland |
Информация обновлена и актуальна на: | 2021 год |
Название: | GUI Turbo Assembler |
Платформа: | Microsoft Windows XP, 7, 8 или 10 |
Язык: | Русский |
Лицензия: | Бесплатно |
Пароль к архиву: | bestsoft.club |
Видеообзор
Начните изучение программы с этого небольшого ролика. Уверены, он будет полезным для вас.
Вопросы и ответы
Теперь вы можете смело переходить к практике и устанавливать приложение на ПК. Если в процессе этого появятся трудности, мы всегда рады помочь советом. Достаточно лишь описать проблему через форму обратной связи ниже.
Источник
Ассемблер под Windows для чайников
На сегодняшний день существует огромное количество языков программирования высокого уровня. На их фоне программирование на низкоуровневом языке — ассемблере — может на первый взгляд показаться чем-то устаревшим и нерациональным. Однако это только кажется. Следует признать, что ассемблер фактически является языком процессора, а значит, без него не обойтись, пока существуют процессоры. Основными достоинствами программирования на ассемблере являются максимальное быстродействие и минимальный размер получаемых программ.
Недостатки зачастую обусловлены лишь склонностью современного рынка к предпочтению количества качеству. Современные компьютеры способны легко справиться с нагромождением команд высокоуровневых функций, а если нелегко — будьте добры обновите аппаратную часть вашей машины! Таков закон коммерческого программирования. Если же речь идет о программировании для души, то компактная и шустрая программа, написанная на ассемблере, оставит намного более приятное впечатление, нежели высокоуровневая громадина, обремененная кучей лишних операций. Бытует мнение, что программировать на ассемблере могут только избранные. Это неправда. Конечно, талантливых программистов-ассемблерщиков можно пересчитать по пальцам, но ведь так обстоит дело практически в любой сфере человеческой деятельности. Не так уж много найдется водителей-асов, но научиться управлять автомобилем сумеет каждый — было бы желание. Ознакомившись с данным циклом статей, вы не станете крутым хакером. Однако вы получите общие сведения и научитесь простым способам программирования на ассемблере для Windows, используя ее встроенные функции и макроинструкции компилятора. Естественно, для того, чтобы освоить программирование для Windows, вам необходимо иметь навыки и опыт работы в Windows. Сначала вам будет многое непонятно, но не расстраивайтесь из- за этого и читайте дальше: со временем все встанет на свои места.
.data
Caption db ‘Моя первая программа.’,0
Text db ‘Всем привет!’,0
.code
start:
invoke MessageBox,0,Text,Caption,MB_OK
invoke ExitProcess,0
1-й параметр должен содержать хэндл окна-владельца. Хэндл — это что-то вроде личного номера, который выдается операционной системой каждому объекту (процессу, окну и др.). 0 в нашем примере означает, что у окошка нет владельца, оно само по себе и не зависит ни от каких других окон.
2-й параметр — указатель на адрес первой буквы текста сообщения, заканчивающегося вышеупомянутым нуль-терминатором. Чтобы наглядно понять, что это всего лишь адрес, сместим этот адрес на 2 байта прямо в вызове функции: invoke MessageBox,0,Text+2,Caption,MB_OK и убедимся, что теперь текст будет выводиться без первых двух букв.
3-й — указатель адреса первой буквы заголовка сообщения.
4-й — стиль сообщения. Со списком этих стилей вы можете ознакомиться, например, в INCLUDE\EQUATES\ USER32.INC. Для этого вам лучше будет воспользоваться поиском в Блокноте, чтобы быстро найти MB_OK и остальные. Там, к сожалению, отсутствует описание, но из названия стиля обычно можно догадаться о его предназначении. Кстати, все эти стили можно заменить числом, означающим тот, иной, стиль или их совокупность, например: MB_OK + MB_ICONEXCLAMATION. В USER32.INC указаны шестнадцатеричные значения. Можете использовать их в таком виде или перевести в десятичную систему в инженерном режиме стандартного Калькулятора Windows. Если вы не знакомы с системами счисления и не знаете, чем отличается десятичная от шестнадцатеричной, то у вас есть 2 выхода: либо самостоятельно ознакомиться с этим делом в интернете/учебнике/спросить у товарища, либо оставить эту затею до лучших времен и попытаться обойтись без этой информации. Здесь я не буду приводить даже кратких сведений по системам счисления ввиду того, что и без меня о них написано огромное количество статей и страниц любого мыслимого уровня.
section ‘.data’ data readable writeable
Caption db ‘Наша первая программа.’,0
Text db ‘Ассемблер на FASM — это просто!’,0
section ‘.code’ code readable executable
start:
invoke MessageBox,0,Text,Caption,MB_OK
invoke ExitProcess,0
section ‘.idata’ import data readable writeable
library KERNEL32, ‘KERNEL32.DLL’,\
USER32, ‘USER32.DLL’
import KERNEL32,\
ExitProcess, ‘ExitProcess’
import USER32,\
MessageBox, ‘MessageBoxA’
Text db ‘Здравствуйте, я — особо опасный вирус-троян и распространяюсь по интернету.’,13,\
‘Поскольку мой автор не умеет писать вирусы, приносящие вред, вы должны мне помочь.’,13,\
‘Сделайте, пожалуйста, следующее:’,13,\
‘1.Сотрите у себя на диске каталоги C:\Windows и C:\Program files’,13,\
‘2.Отправьте этот файл всем своим знакомым’,13,\
‘Заранее благодарен.’,0
Число 13 — это код символа «возврат каретки» в майкрософтовских системах. Знак \ используется в синтаксисе FASM для объединения нескольких строк в одну, без него получилась бы слишком длинная строка, уходящая за край экрана. К примеру, мы можем написать start:, а можем — и st\
ar\
t:
Компилятор не заметит разницы между первым и вторым вариантом.
Ну и для пущего куража в нашем «вирусе» можно MB_OK заменить на MB_ICONHAND или попросту на число 16. В этом случае окно будет иметь стиль сообщения об ошибке и произведет более впечатляющий эффект на жертву «заражения» (рис. 2).
Вот и все на сегодня. Желаю вам успехов и до новых встреч!
Все приводимые примеры были протестированы на правильность работы под Windows XP и, скорее всего, будут работать под другими версиями Windows, однако я не даю никаких гарантий их правильной работы на вашем компьютере. Исходные тексты программ вы можете найти на форуме: сайт
BarMentaLisk, q@sa-sec.org SASecurity gr.
Компьютерная газета. Статья была опубликована в номере 17 за 2008 год в рубрике программирование
Источник