А теперь посмотрим, как написать простейшее приложение под Windows и на его примере рассмотрим все принципы вызовов API-функций на ассемблере. Все, что будет делать наша программа это выводить окно с сообщением, показанное на рис. 7.1. Рис. 7.1.
Вид окна с сообщением, отображаемого программой Для вывода таких сообщений используется API-функция MessageBox, ниже приведено описание, взятое из MSDN в переводе на русский: int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); Параметры функции: hWnd — дескриптор родительского окна. Если родительского окна нет, то используется нулевое значение; lpText — текст, отображаемый внутри окна; lpCaption — текст в заголовке окна; uType — тип окна сообщений, который позволяет задать, какие кнопки и иконки будут отображаться в окне. Этот параметр может быть комбинацией флагов из следующих групп флагов: MB_ABORTRETRYIGNORE — окно сообщений будет содержать три кнопки: «Abort», «Retry», и «Ignore»; MB_OK — окно сообщений будет содержать только одну кнопку: «OK»; MB_OKCANCEL — окно сообщений будет содержать две кнопки: «OK» и «Cancel»; MB_YESNO — окно сообщений будет содержать две кнопки: «Yes» и «No»; MB_ICONEXCLAMATION — в окне сообщений появится иконка с восклицательным знаком; MB_ICONHAND — в окне сообщений появится иконка с изображением знака «Stop»;
Как написать программу на ассемблере 64 бит
http://www.sklyaroff.ru | 150 |
MB_ICONQUESTION — в окне сообщений появится иконка с изображением вопросительного знака; MB_ICONASTERISK — в окне сообщений появится иконка с изображением буквы «i»; MB_DEFBUTTON1 — фокус находится на первой кнопке; MB_DEFBUTTON2 — фокус находится на второй кнопке; MB_DEFBUTTON3 — фокус находится на третьей кнопке. Все флаги и вообще любые строковые идентификаторы, которые вы можете встретить в описаниях функций API, на самом деле являются именами определенных числовых значений (кодов).
Вы не можете в программе на ассемблере просто использовать, скажем, флаг MB_OK, т. к. на самом деле требуется указать число, которое заменяет этот флаг. Узнать числовые значения флагов и всех прочих идентификаторов API-функций, всегда можно в так называемых заголовочных файлах (имеют расширение .h). В описании каждой API-функции в MSDN обычно указывается, в каком заголовочном файле следует искать такие числовые значения и сразу предоставляется возможность просмотреть этот заголовочный файл. Для функции MessageBox все значения флагов содержатся в заголовочном файле winuser.h. Вот нужный нам отрывок из winuser.h:
#define MB_OK | 0x00000000L |
#define MB_OKCANCEL | 0x00000001L |
#define MB_ABORTRETRYIGNORE | 0x00000002L |
#define MB_YESNOCANCEL | 0x00000003L |
#define MB_YESNO | 0x00000004L |
#define MB_RETRYCANCEL | 0x00000005L |
Оператор языка Си #define является аналогом ассемблерной директивы эквивалентности EQU, которая присваивает имени значение операнда (см. разд. 2.4.2). Как видим, нужному нам флагу MB_OKCANCEL присвоено значение 0x00000001L. Буква L на конце числа означает в языке Си длинное целое (long).
Hello World на Ассемблере (x86)
В ассемблере эта буква не используется, поэтому значение просто будет выглядеть как 1. Мы можем в программе на ассемблере просто использовать это значение или с помощью директивы эквивалентности определить флаг: MB_OKCANCEL EQU 1 К счастью нам не придется это делать самим постоянно, т. к. в пакет MASM32 входит специальный файл WINDOWS.INC (расположен в MASM32include) в котором определены директивы эквивалентности для всевозможных идентификаторов и флагов, которые используют API-функции, в том числе и MB_OKCANCEL. Откройте в любом текстовом редакторе этот файл и посмотрите его содержимое.
Можно (и нужно) подключать файл WINDOWS.INC в своих программах и использовать любые идентификаторы из него, как это сделать мы узнаем ниже. Прежде чем мы начнем составлять программу на ассемблере, вам нужно узнать еще одну важную тонкость, связанную с API-функциями. Все API-функции работающие со строками (как, например, MessageBox) существуют в двух версиях: ANSI-версия, для работы со строками в кодировке ANSI (один байт на символ). В этом случае к имени функции добавляется суффикс A. Например, для функции MessageBox имя ANSI-версии будет MessageBoxA. Unicode-версия, для работы со строками в кодировке Unicode (два байта на символ). К имени функции добавляется суффикс W. Например, для функции MessageBox имя Unicode-версии будет MessageBoxW.
API-функции без суффиксов (например MessageBox), на самом деле является лишь обертками для этих двух функций. Компилятор высокоуровневых языков сам решает,
http://www.sklyaroff.ru | 151 |
какую из двух версий API-функции нужно использовать, однако программист на ассемблере должен точно указывать нужную версию функции ANSI или Unicode в своих программах. ANSI-версию стоит выбирать когда в функцию передаются строки только из 256 символов кодировки ASCII, а Unicode-версию, когда в функцию требуется передавать строки из расширенного набора (65536 символов) кодировки Unicode.
Строки для API-функций ANSI-версий (MessageBoxA) задаются также, как мы это делали в DOS-программах, к примеру: str DB «Ivan Sklyaroff», 0 А для Unicode-версий (MessageBoxW) строки необходимо определять следующим образом: str DW ‘I’, ‘v’, ‘a’, ‘n’, ‘ ‘, ‘S’, ‘k’, ‘l’, ‘y’, ‘a’, ‘r’, ‘o’, ‘f’, ‘f’, 0 Разумеется, можно просто указывать коды непечатаемых символов, например, в следующей строке заданы четыре символа в кодировке Unicode в шестнадцатеричном виде (значок «евро» – код 020ACh, математическая «бесконечность» – код 0221Eh, «больше или равно» – код 02265h, буква арабского языка – код 0642h): str DW 020ACh, 0221Eh, 02265h, 0642h, 0 Мы далее будем использовать строки, а, следовательно, и функции только ANSIверсий. Обратите также внимание, что в Windows строки заканчиваются нулем, а не символом $ как в DOS-программах.
Это правило вытекает из языка Си, так как в нем строки должны заканчиваться нулевым символом. Однако помните о том, что часто один из параметров API-функции задает длину строки (в документации это часто называется буфером), определяемой другим параметром, из-за этого возвращаемая из функции строка может не иметь нуля на конце.
При использовании в дальнейшем строки без нуля на конце, в других функциях могут возникнуть ошибки. И еще одна важная деталь. В начале программы на ассемблере, также как и в программах на Си, нужно задавать так называемые прототипы всех вызываемых функций. Прототип это просто краткое описание функции с указанием точного числа параметров и их типов.
Вызываемая функция должна соответствовать своему прототипу, иначе линкер выдаст ошибку. В языке программирования Си использование прототипов обычная практика, они помогают писать безошибочные программы. Прототип создается с помощью директивы PROTO. Например, для функции MessageBoxA прототип будет выглядеть следующим образом: MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:DWORD Это означает, что в функцию MessageBoxA должны быть переданы четыре 32разрядных параметра. В прототипах всех API-функций для указания типа параметров можно использовать DWORD, т. к. в API-функциях все параметры только 32-разрядные (мы говорили об этом выше). Теперь мы можем написать предварительный вызов функции MessageBoxA на ассемблере: MB_OKCANCEL EQU 1
http://www.sklyaroff.ru | 152 | |
.model flat,stdcall | ||
MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:DWORD | ||
.data | ||
hello_mess | db «Первая программа под Windows на ассемблере», 0 | |
hello_title | db «Hello, World!», 0 |
.code start: push MB_OKCANCEL push offset hello_title push offset hello_mess push 0 call MessageBoxA end start Мы верно составили вызов API-функции, однако как мы говорили в начале этого дня, функции API находятся в dll-библиотеках, и пока не присоединить нужные библиотеки к программе никакие даже правильно составленные вызовы APIфункций работать не будут. Но как присоединить динамические библиотеки к программе на ассемблере?
Напрямую это сделать нельзя и нужно использовать так называемые библиотеки импорта, которые обычно имеют тоже имя, что и соответствующие dll-файлы, но с расширением .lib. В MSDN Library в описании функции MessageBox можно увидеть следующую информацию (табл. 7.1). Как видите, здесь указано, что функция содержится в файле user32.dll, а библиотекой импорта является файл User32.lib. Таблица 7.1. Информация из MSDN Library к описанию функции MessageBox
Minimum DLL Version | user32.dll |
Header | Declared in Winuser.h, include Windows.h |
Import library | User32.lib |
Minimum operating systems | Windows 95, Windows NT 3.1 |
Unicode | Implemented as ANSI and Unicode versions. |
Файлы библиотек импорта входят в дистрибутивы любых средств разработки для Windows, например их можно взять из Visual Studio. Однако пакет MASM32 облегчает нам задачу тем, что в нем уже содержатся все необходимые библиотеки импорта в разделе MASM32lib.
Библиотеки импорта подключается в программе на ассемблере с помощью директивы INCLUDELIB, следующим образом: includelib masm32libuser32.lib Кроме того, в пакет MASM32 входят INC-файлы (расположены в разделе MASM32include) в которых содержатся все прототипы API-функций. Эти INCфайлы имеют имена такие же, как имена соответствующих lib-файлов. Поэтому, подключив к программе INC-файл, вам не придется самостоятельно определять прототипы функций. INC-файлы подключаются с помощью директивы INCLUDE, следующим образом: include masm32includeuser32.inc Посмотрите содержимое файла user32.inc, вы найдете в нем прототип MessageBoxA. Кроме того, как мы уже говорили, в разделе MASM32include существует файл WINDOWS.INC, в котором определены директивы эквивалентности для
http://www.sklyaroff.ru | 153 |
всевозможных идентификаторов и флагов, которые используют API-функции, как, например MB_OKCANCEL. Подключается файл WINDOWS.INC также как и любой INC-файл: include masm32includewindows.inc Файл WINDOWS.INC является большим подспорьем для программистов, т. к. в больших программах число всевозможных флагов, идентификаторов и структур, необходимых функциям API достигает многие десятки.
Подключив этот файл к своей программе, вам не придется больше определять их самостоятельно. Мы далее будем включать этот файл во все наши программы на ассемблере. В листинге 7.1 показана готовая программа, которая выводи окно сообщения. Вы видите, мы задействовали еще одну API-функцию ExitProcess для выхода из программы.
Описание функции: VOID WINAPI ExitProcess(UINT uExitCode ); Она расположена в kernel32.dll, поэтому мы добавляем в программу включения файлов: include masm32includekernel32.inc includelib masm32libkernel32.lib В качестве параметра uExitCode мы должны передать нулевое значение, чтобы завершить текущий процесс. Данная функция не возвращает никакого значения. Функцией ExitProcess мы будем завершать все наши программы на ассемблере под Windows.
Компиляция программы осуществляется следующей строкой: ml /c /coff /Cp hello.asm Назначение параметров: /c компиляция без компоновки. /coff создать .obj файл в формате COFF (Common Object File Format). Применение этого параметра обязательно. /Cр сохранять регистр имен, заданных пользователем.
Вы можете вставить строку «option casemaр:none» в начале исходного кода вашей программы, сразу после директивы .model, чтобы добиться того же эффекта. После успешной компиляции hello.asm , вы получите hello.obj . Это объектный файл, который содержит инструкции и данные в двоичной форме.
Отсутствуют только необходимая корректировка адресов, которая проводится линкером. link.exe /SUBSYSTEM:WINDOWS /LIBPATH:c:masm32lib hello.obj /SUBSYSTEM:WINDOWS информирует линкер о том, что приложение является оконным приложением. Для консольных приложений необходимо использовать параметр /SUBSYSTEM:CONSOLE. /LIBPATH: указывает линкеру местоположение библиотек импорта. Если вы используете MASM32, они будут в MASM32lib. После окончания линковки вы получите файл hello.exe. Листинг 7.1. Простейшая программа под Windows ( hello.asm ) .386P .model flat,stdcall include masm32includewindows.inc include masm32includeuser32.inc include masm32includekernel32.inc
includelib | masm32libuser32.lib | ; Подключаем библиотеки |
includelib | masm32libkernel32.lib |
Источник: studfile.net
Консольная программа для Windows на языке ассемблера
В предыдущих заметках я начал писать программы, которые стартуют на ПК без операционной системы, будучи загруженными с дискеты. Программы эти я компилировал ассемблером flat assembler (FASM). В настоящей заметке мне захотелось сделать небольшое отступление и написать о том, как при помощи FASM можно писать программы пользовательского режима для Windows. Знание того, как это делается позволяет изучать программирование на ассемблере как таковое, не заморачиваясь написанием своей собственной операционной системы.
Я преподаю студентам программирование на ассемблере Intel x86 с использованием компилятора Microsoft Macro Assembler (MASM). Чтобы изучать ассемблер, нам нужно писать на нем программы, а чтобы мы могли вводить в эти программы данные и видеть какие-то результаты на экране, нам нужны какие-то средства ввода-вывода. В языках C/C++ для ввода-вывода мы привычно используем стандартные библиотеки этих языков. А в ассемблере? Есть разные варианты.
Вариант 1-й. Использовать функции WinAPI, например WriteConsole, ReadConsole. Согласитесь, не самое приятное средство ввода-вывода — мы все-таки ассемблер хотим изучать, а не WinAPI.
Вариант 2-й. Использовать привычную стандартную библиотеку языка C (CRT — C Runtime Library) — т. е. такие простые и понятные функции, как printf и scanf. Но вызвать их из кода на ассемблере, как выяснилось, не такое простое дело.
Вызываем printf из программы на MASM в Visual Studio 2015+
Прежде всего, оказывается, что в версиях Visual Studio начиная с 2015 функция printf и еще какие-то стандартные функции уже не экспортируются Microsoft’овской реализацией CRT, а являются встроенными (inline) и определены прямо в заголовочных файлах (например, stdio.h). Поэтому если вы напишите на ассемблере код, который вызывает printf и попробуете скомпоновать его с msvcrt.lib, то получите ошибку unresolved external symbol printf . Однако Microsoft оставила нам лазейку в виде ряда статически компонуемых библиотек (см. код ниже), которые экспортируют те функции, которые Microsoft сделала встроенными. Ниже приведен код, который вы можете скомпилировать компилятором ml.exe, который вы можете запустить например из командной строки Developer Command Prompt for VS, если у вас установлена Visual Studio (или можете создать и настроить проект в Visual Studio для программирования на MASM).
; MASM version of HelloWorld program using printf() function
; We have to link to these libraries since Visual Studio 2015
includelib libcmt . lib
includelib libucrt . lib
includelib libvcruntime . lib
includelib legacy_stdio_definitions . lib
. 686P ; Pentium Pro or later
. MODEL flat , stdcall
. STACK 4096
EXTERN printf : NEAR
.data
mytext BYTE «Hello World!» , 0Dh , 0Ah , 0
; «PROC» directive is mandatory here, we can’t write just «main:» (don’t know why)
main PROC C
push offset mytext
call printf
add esp , 4
ret
main ENDP
; it’s important that we use just «END» istead of «END main»
; as we don’t want to set main as the entry point.
END
В процессе отладки вышеприведенного кода я наткнулся не только на ошибки компоновщика unresolved external symbol , но и на ошибки времени выполнения. Например, стоило мне написать в конце файла END main, а не просто END, как во время выполнения программы возникала ошибка Exception thrown at 0x77621DCA (ntdll.dll) in AsmProject.exe: 0xC0000005: Access violation writing location 0x00000014 . Дело в том, что функция main в данном случае не должна быть точкой входа (директива END задает точку входа), ею должна быть функция mainCRTStartup(), которая инициализирует стандартную библиотеку языка C. Функция mainCRTStartup() в конце концов сама вызовет нашу функцию main.
Если же я пытался не использовать директиву PROC рядом с меткой main, т. е. писал вот так:
main :
push offset mytext
call printf
add esp , 4
ret
Вот еще несколько ресурсов по теме линковки программ со стандартной библиотекой языка C:
- Dynamically linking with MSVCRT.DLL using Visual C++ 2005
- Overview of potential upgrade issues (Visual C++)
- How does the Import Library work? Details?
Вызываем printf из программы на flat assembler (FASM)
flat assembler, в отличие от MASM, практически не делает ничего, что программист не написал явно в исходном коде. В ОС Windows исполняемые файлы и библиотеки имеют формат Portable Executable (PE). Если вы создаете при помощи FASM программу, которая будет выполняться в ОС Windows, то вы должны сами описать в файле исходного кода некоторые части исполняемого файла Portable Executable (чего в MASM вам делать не пришлось бы). Например, вы должны сами описать секции данных, кода, импорта, экспорта, ресурсов и пр. Для программы HelloWorld нужны три секции: кода, данных и импорта.
С секциями кода и данных всё очевидно. В секцию импорта помещается информация о функциях, которые наша программа вызывает из различных библиотек. Нам нужно поместить в секцию импорта данные о функциях стандартной библиотеки языка C (msvcrt.dll), которые мы используем в программе. В программе, показанной ниже, я использую две функции: printf и exit.
Но каков формат данных в секции импорта? К счастью, мы можем об этом не думать — в заголовочном файле /INCLUDE/MACRO/IMPORT32.INC находятся макросы library и import, которые генерируют данные для секции импорта; программисту нужно лишь указать этим макросам имя файла библиотеки и названия функций. Об этих макросах читайте раздел 3.1.2 документа flat assembler Programmer’s Manual. Пример описания секции импорта без использования макросов library и import находится в файле /EXAMPLES/PEDEMO/PEDEMO.ASM.
Достигнуть некоторого просветления в понимании формата Portable Executable мне помогли следующие ресурсы:
- Portable Executable — Wikipedia
- Matt Pietrek — Peering Inside the PE: A Tour of the Win32 Portable Executable File Format
- Matt Pietrek — An In-Depth Look into the Win32 Portable Executable File Format — 2002
- PE Format (Windows) — MSDN — Microsoft
- Румянцев П. В. — Исследование программ Win32: до дизассемблера и отладчика — 2004
- Румянцев П. В. — Работа с файлами в win32 API — 2009 (Раздел «Внутренности исполняемого файла Win32»)
Ниже приведен код программы HelloWorld на FASM:
; FASM version of HelloWorld program using printf() function
format PE console
entry main
use32
; ========== CODE SECTION ==========
section ‘.text’ code readable executable
printf : jmp [ imp_printf ]
exit : jmp [ imp_exit ]
main :
push message
call printf
add esp , 4
push 0
call exit
; ========== DATA SECTION ==========
section ‘.data’ data readable writeable
message db «Hello, World!» , 0
; ========== IMPORT SECTION ==========
section ‘.idata’ data import readable
; The header included below contains «library» and «import» macros that
; generate import data which must be placed in the import section of PE file
include «C:FASMINCLUDEmacroimport32.inc»
library msvcrt , «MSVCRT.DLL»
import msvcrt ,
imp_printf , ‘printf’ ,
imp_exit , ‘exit’
Пояснения к программе HelloWorld на FASM
format PE console определяет формат двоичного файла, который должен сгенерировать компилятор; формат — Portable Executable, подсистема — console.
entry main устанавливает точку входа в программу — функцию main.
use32 заставляет компилятор генерировать 32-разрядный машинный код.
section ‘.text’ code readable executable описывает секцию кода. ‘.text’ — это имя секции (имена секций, насколько я понимаю, не несут никакой функциональной нагрузки и могут быть любыми, но не длиннее 8 символов). code readable executable — атрибуты (флаги), которые характеризуют секцию как секцию кода, содержимое которой можно читать и исполнять.
section ‘.data’ data readable writeable секция данных, для которой разрешены чтение и запись.
section ‘.idata’ data import readable секция импорта, в которой описывается, какие функции из каких библиотек использует наша программа. О том, как устроена секция импорта в файле PE хорошо написано в книге П. В. Румянцева «Работа с файлами в win32 API» в разделе Импорт функций и механизм импорта. Раздел импорта состоит, грубо говоря, из двух массивов.
Массив структур IMAGE_IMPORT_DESCRIPTOR (определения структур см. в заголовочном файле WinNT.h) содержит информацию об используемых нашей программой библиотеках. Количество элементов в массиве равно количеству библиотек. В каждом элементе этого массива содержится имя библиотеки, из которой импортируются функции, и указатель FirstThunk, который указывает на массив структур IMAGE_THUNK_DATA. Каждый элемент массива структур IMAGE_THUNK_DATA содержит информацию об одной импортируемой функции — это либо ее порядковый номер (ordinal), либо указатель на структуру IMAGE_IMPORT_BY_NAME, которая содержит имя функции.
printf : jmp [ imp_printf ]
Команда вида jmp [ metka ] выполняет прыжок по адресу, который берется из участка памяти, на который указывает метка metka. Не путайте эту команду с командой jmp metka , которая выполняет прыжок по адресу, на который указывает метка metka . В приведенной выше команде imp_printf — это метка, которая указывает на структуру IMAGE_THUNK_DATA . При загрузке исполняемого файла в память загрузчик помещает в место в памяти, на которое указывает imp_printf адрес функции printf в библиотеке msvcrt.dll. Так что команда printf : jmp [ imp_printf ] прыгает на функцию printf.
Ну и наконец, я хотел бы показать, как выглядит наша программа без макросов library и import:
; FASM version of HelloWorld program using printf() function
format PE console
entry main
use32
; ========== CODE SECTION ==========
section ‘.text’ code readable executable
main :
push message
call [ printf ]
add esp , 4
push 0
call [ exit ]
; ========== DATA SECTION ==========
section ‘.data’ data readable writeable
message db «Hello, World!» , 0
; ========== IMPORT SECTION ==========
section ‘.idata’ data import readable
; — array of IMAGE_IMPORT_DESCRIPTOR structures —
dd 0 , 0 , 0 , RVA msvcrt_name , RVA msvcrt_table
dd 0 , 0 , 0 , 0 , 0
; —
; — array of IMAGE_THUNK_DATA structures —
msvcrt_table :
printf dd RVA _printf
exit dd RVA _exit
dd 0
; —
msvcrt_name db ‘MSVCRT.DLL’ , 0
; IMAGE_IMPORT_BY_NAME structure
_printf :
dw 0
db ‘printf’ , 0
; IMAGE_IMPORT_BY_NAME structure
_exit :
dw 0
db ‘exit’ , 0
В приведенном коде директива RVA означает Relative Virtual Address — адрес относительно начала файла на диске, а не адрес оперативной памяти. Следует понимать разницу между этими двумя адресами. Файл на диске состоит из байтов (можно еще сказать, является потоком байтов). Байты файла можно условно пронумеровать от 0 до . Такой номер байта — это и есть RVA.
При запуске программы содержимое файла копируется (точнее — проецируется, но не будем об этом сейчас) в оперативную память по определенному адресу, который обычно равен 0x00400000 (этот адрес указывается в заголовке файла Portable Executable — см. поле ImageBase в структуре IMAGE_OPTIONAL_HEADER в файле WinNT.h). Поэтому, условно говоря, байт в файле, у которого RVA = X, будет находиться в оперативной памяти по адресу X + 0x00400000.
Отладка (Debugging)
Отладка — это исследования работы скомпилированной программы во время ее выполнения. Отладка выполняется при помощи специальных программ, называемых отладчиками (debuggers). Отладчики позволяют пользователю
- пошагово выполнять целевую программу
- отображать и, при необходимости, изменять содержимое оперативной памяти и регистров процессора
- ставить в коде так называемые точки останова (breakpoints): дойдя до команды, на которую установлена точка останова, программа остановится, после чего пользователь может просмотреть содержимое памяти и регистров процессора, продолжить выполнение пошагово и т. д.
- изменять порядок выполнения команд в программе
Как работает отладчик? Как я понимаю, для работы отладчику необходима поддержка со стороны операционной системы. Поэтому такие ОС, как Windows и Linux предоставляют API для выполнения отладки (см. например The Debugging Application Programming Interface). ОС может перевести процессор в такой режим, когда после выполнения каждой машинной команды, он будет генерировать прерывание — это и позволяет, как я понимаю, выполнять программу пошагово.
Где взять отладчик? Для Windows стандартным является отладчик от фирмы Microsoft под названием WinDbg, он поставляется в составе Windows SDK (см. Debugging Tools for Windows). О том, как пользоваться отладчиком WinDbg см. статью Getting Started with WinDbg (User-Mode).
Другой вариант — отладчик с открытым исходным кодом OllyDbg. Пользоваться им довольно просто: вы нажимаете на кнопку «Открыть» и выбираете исполняемый файл (exe) программы, которую хотите отлаживать. Программа тут же запускается и происходит останов на ее точке входа (функции main). В окне программы вы видите четыре окошка: дизассемблированный код, регистры процессора, шестнадцатеричный дамп памяти, стек (stack). Интерфейс программы интуитивно понятен, а в составе дистрибутива есть руководство пользователя.
Заключение
Если выбирать между MASM и FASM с точки зрения обучения языку ассемблера, то я пожалуй выбираю FASM. Его можно легко установить (надо просто скачать дистрибутив с сайта). Он позволяет создавать любые двоичные файлы и помогает программисту создавать двоичные файлы форматов PE, ELF и COFF. С MASM мне пришлось помучиться, чтобы разобраться с вызовом функций из стандартной библиотеки языка C, с FASM всё оказалось куда проще.
Источник: dvsav.ru
Как писать программы для windows на ассемблере
Наиболее популярным инструментом для компиляции кода ассемблера для arm представляет компилятор GAS от проекта GNU, который входит в состав комплекта инструментов для разработки под ARM — Arm GNU Toolchain . Итак, вначале установим данный набор инструментов. Для этого перейдем на официальный сайт компании Arm на страницу https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads. Здесь представлены поседние версии Arm GNU Toolchain для разных архитектур.
Каждая версия Arm GNU Toolchain привязана к определенной версии компиляторов GCC. Например, версия Arm GNU Toolchain 12.2.Rel1 привязан к версии 12.2 набора компиляторов gcc.
Для ОС Windows доступно несколько групп пакетов по различные архитектуры:
- AArch32 bare-metal target (arm-none-eabi) : для компиляции программ под 32-битные архитектуры без привязки к конкретной операционной системе
- AArch32 GNU/Linux target with hard float (arm-none-linux-gnueabihf) : для компиляции программ под 32-битную ОС Linux
- AArch64 bare-metal target (aarch64-none-elf) : для компиляции программ под 64-битные архитектуры без привязки к конкретной операционной системе
- AArch64 GNU/Linux target (aarch64-none-linux-gnu) : для компиляции программ под 64-битную ОС Linux
Как видно из названия, наборы компиляторов имеют названия типа arm-none-linux-gnueabi , arm-none-eabi , arm-eabi и т.д. Все эти названия формируются по шаблону
arch[-vendor] [-os] — eabi
- arch : указывает на архитектуру
- vendor : указывает на производителя
- os : указывает на целевую операционную систему
- eabi : сокращение от Embedded Application Binary Interface
Например, пакет инструментов arm-none-eabi предназначен для 32-х битной архитектуры, не зависит от конкретного вендора, операционной системы и компилируется с помощью ARM EABI.
Другой пример: пакет инструментов arm-none-linux-gnueabi предназначен для 32-х битной архитектуры, но создает бинарники непосредственно для ОС Linux и использует GNU EABI.
Поскольку в данном случае в данном случае мы рассматриваем именно arm64, то нас будет интересовать прежде всего те пакеты, которые начинаются на AArch64 . И поскольку пакет AArch64 bare-metal target (aarch64-none-elf) не привязан к определенной ОС, то выберем его. Кроме того, он доступен для всех основных ОС. Однако отмечу, что, если планируется писать код именно под Linux (в том числе Android), то лучше использовать AArch64 GNU/Linux target (aarch64-none-linux-gnu) — он создает более компактные (иногда намного меньшие) исполняемые файлы.
Для Windows доступны пакеты в двух вариантах: установочный файл exe , который устанавливает все необходимые файлы в папку C:Program Files (x86) , и zip -архив — по сути те же самые файлы, которые мы можем распаковать в любое нужное для нас расположение. Большой разницы между файлами из exe и zip нет, но для простоты выберем exe-файл (в моем случае это файл arm-gnu-toolchain-12.2.rel1-mingw-w64-i686-aarch64-none-elf.exe
После загрузки запустим установщик
Прощелкаем по шагам и в конце на последнем окне после установки установим флажок Add path to environment variable , чтобы добавить путь к компилятору и другим инструментам в переменные среды:
Если мы посмотрим на добавленный в переменные среды путь (в данном случае каталог C:Program Files (x86)Arm GNU Toolchain aarch64-none-elf12.2 rel1bin ), то мы найдем файлы компилятора и ряд других файлов:
В этом комплекте нам понадобится прежде всего сам ассемблер — файл aarch64-none-elf-as.exe , который по коду ассемблера Arm64 создает объектный файл. Кроме того, также потребуется файл aarch64-none-elf-as.ld , который также располагается в этой папке и который генерирует из объектного файла исполныемый файл.
Для проверки настройки откроем терминал/командную строку и выведем версию компилятора следующей командой:
aarch64-none-elf-as —version