Как программировать на ассемблере для Windows 10

Обновлено: 24.11.2024

Хотите улучшить этот вопрос? Обновите вопрос, чтобы он был сосредоточен только на одной проблеме, отредактировав этот пост.

Закрыт 4 года назад.

Я новичок в ассемблере, и мне интересно, смогу ли я запустить программу ASM в командной строке моего компьютера. Это так, как мне это сделать? Если нет, то какое приложение мне использовать для его запуска?

(1) Соберите свою программу для создания исполняемого файла (EXE-файла), (2) откройте окно командной строки, (3) запустите программу? Не уверен, что понимаю, в чем проблема.

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

Какую команду мне использовать для запуска? Я читал в Интернете, что использую команду MASM, но когда я пытаюсь это сделать, он говорит, что «MASM» не является внутренней или внешней командой.

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

1 Ответ 1

В случае Windows с помощью Visual Studio Express (это бесплатно) или аналогичного набора инструментов вы можете создать проект только для сборки, создать консольную программу Windows в формате .exe, а затем запустить эту программу из консоли Windows. Я предполагаю, что что-то подобное можно сделать с Linux или другими системами типа Posix. Visual Studio может создавать 32-разрядные или 64-разрядные программы.

Если целью является создание 16-битной программы сборки, вам понадобится 16-битный набор инструментов, такой как Masm 6.11 или аналогичный набор инструментов (вам также может понадобиться 16-битный компилятор, такой как Visual C/C++ 1.52). Если вы создаете небольшую 16-битную программу .com, вы можете использовать «отладку» с направленным входным файлом и направленным выходным файлом в качестве грубого ассемблера. 32-разрядные версии Windows могут запускать 16-разрядные программы типа MSDOS, но в 64-разрядной версии Windows вам потребуется использовать виртуальную машину либо в 16-разрядном режиме (обычно MSDOS), либо в 32-разрядном режиме (обычно это какая-то версия 32-разрядной ОС). бит Windows).

Visual Studio включает как 32-разрядные, так и 64-разрядные размещенные версии MASM (Microsoft Macro Assembler) для работы с кодом x64. Названный ml64.exe, это ассемблер, который принимает язык ассемблера x64. Инструменты командной строки MASM устанавливаются при выборе рабочей нагрузки C++ во время установки Visual Studio. Инструменты MASM недоступны для отдельной загрузки. Инструкции по загрузке и установке копии Visual Studio см. в разделе Установка Visual Studio. Если вам нужны только инструменты командной строки, а не полная IDE, загрузите инструменты сборки для Visual Studio.

Чтобы использовать ml64.exe в командной строке, запустите командную строку разработчика для целей x64. Командная строка разработчика устанавливает требуемый путь и другие переменные среды. Сведения о том, как запустить командную строку разработчика, см. в разделе Сборка кода C/C++ в командной строке.

Сведения о параметрах командной строки ml64.exe см. в справочнике по командной строке ML и ML64.

Встроенный ассемблер или использование ключевого слова ASM не поддерживаются для целей x64 или ARM64. Чтобы перенести свой код x86, использующий встроенный ассемблер, на x64 или ARM64, вы можете преобразовать свой код в C++, использовать встроенные функции компилятора или создать исходные файлы на языке ассемблера. Компилятор Microsoft C++ поддерживает встроенные функции, позволяющие использовать инструкции специальных функций, например, привилегированные, побитовое сканирование или тестирование, блокировку и т. д., максимально приближенным к кросс-платформенным. Информацию о доступных встроенных функциях см. в разделе Внутренние функции компилятора.

Добавить файл на языке ассемблера в проект Visual Studio C++

Система проектов Visual Studio поддерживает файлы на языке ассемблера, созданные с помощью MASM в проектах C++. MASM полностью поддерживает исходные файлы на языке ассемблера x64 и встраивает их в объектные файлы. Затем вы можете связать эти объектные файлы с кодом C++, созданным для целей x64. Это один из способов решить проблему отсутствия встроенного ассемблера x64.

Чтобы добавить файл на языке ассемблера в существующий проект Visual Studio C++

Выберите проект в обозревателе решений. В строке меню выберите "Проект", "Настройки сборки".

В диалоговом окне "Файлы настройки сборки Visual C++" установите флажок рядом с masm(.targets,.props). Нажмите OK, чтобы сохранить выбор и закрыть диалоговое окно.

В строке меню выберите «Проект», «Добавить новый элемент».

В диалоговом окне "Добавить новый элемент" выберите файл C++ (.cpp) на центральной панели. В поле ввода имени введите новое имя файла с расширением .asm вместо .cpp . Выберите Добавить, чтобы добавить файл в проект и закрыть диалоговое окно.

Создайте код на языке ассемблера в добавленном вами файле .asm. Когда вы создаете свое решение, вызывается ассемблер MASM для сборки файла .asm в объектный файл, который затем связывается с вашим проектом.Чтобы упростить доступ к символам, объявите свои ассемблерные функции как extern "C" в исходном коде C++, а не используйте соглашения об оформлении имен C++ в исходных файлах на языке ассемблера.

Директивы для мл64

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

Архитектура x86 является наиболее популярной архитектурой для настольных и портативных компьютеров. Давайте посмотрим, как мы можем программировать на языке ассемблера для процессоров этого семейства.

Введение

Этот документ содержит очень краткие примеры программ на ассемблере для платформы x86. Тема программирования на ассемблере x86 запутана, потому что:

Мы приведем примеры, написанные для NASM, MASM и gas как для Win32, так и для Linux. Мы даже включим раздел о программах на ассемблере для DOS для исторического интереса. Эти примечания не предназначены для замены документации, прилагаемой к процессору и ассемблерам, и не предназначены для обучения вас языку ассемблера. Его единственная цель — показать, как собирать и компоновать программы, используя разные ассемблеры и компоновщики.

Ассемблеры и компоновщики

Независимо от используемого ассемблера, формата объектного файла, компоновщика или операционной системы процесс программирования всегда одинаков:

Каждый файл на языке ассемблера собирается в файл, а объектные файлы связываются с другими объектными файлами для формирования исполняемого файла. «Статическая библиотека» на самом деле не что иное, как набор (возможно, связанных) объектных файлов. Разработчики приложений обычно используют библиотеки для таких задач, как ввод-вывод и математика.

Ассемблеры, о которых вы должны знать, включают

    , ассемблер Майкрософт. Он выводит файлы OMF (но компоновщик Microsoft может конвертировать их в формат win32). Он поддерживает массивный и неуклюжий язык ассемблера. Адресация памяти не интуитивно понятна. Директивы, необходимые для настройки программы, делают программирование неприятным. , ассемблер GNU. Здесь используется довольно уродливый синтаксис в стиле AT&T, поэтому многим он не нравится; однако вы можете настроить его для использования и понимания стиля Intel. Он был разработан, чтобы стать частью серверной части коллекции компиляторов GNU (gcc). , «Сетевой ассемблер». Он бесплатный, небольшой и, что самое главное, может выводить миллионы различных типов объектных файлов. Язык гораздо более толковый, чем MASM во многих отношениях.

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

Существует множество форматов объектных файлов. Некоторые из них, о которых вы должны знать, включают

  • OMF: используется в DOS, но имеет 32-разрядные расширения для Windows. Старый.
  • AOUT: использовался в ранних версиях Linux и BSD
  • COFF: "Общий формат объектных файлов"
  • Win, Win32: версия COFF от Microsoft, не совсем такая же! Заменяет OMF.
  • Win64: формат Microsoft для Win64.
  • ELF, ELF32: используется в современных 32-разрядных версиях Linux и других системах.
  • ELF64: используется в 64-разрядной версии Linux и других системах.
  • macho32: NeXTstep/OpenStep/Rhapsody/Darwin/macOS 32-разрядная версия
  • macho64: NeXTstep/OpenStep/Rhapsody/Darwin/macOS 64-разрядная версия

В документации NASM есть отличные описания этого.

Вам потребуется компоновщик, который (1) понимает создаваемые вами форматы объектных файлов и (2) может писать исполняемые файлы для операционных систем, в которых вы хотите запускать код. Некоторые компоновщики включают

  • LINK.EXE для операционных систем Microsoft.
  • ld, существующий во всех системах Unix; Программисты Windows получают это в любом дистрибутиве gcc.

Программирование для Linux

Программирование с использованием системных вызовов

В 64-разрядных установках Linux используется инструкция SYSCALL процессора для перехода к той части памяти, где хранятся службы операционной системы. Чтобы использовать SYSCALL, сначала поместите номер системного вызова в RAX, затем аргументы, если они есть, в RDI, RSI, RDX, R10, R8 и R9 соответственно. В нашем первом примере мы будем использовать системные вызовы для записи в файл (номер вызова 1) и выхода из процесса (номер вызова 60). Вот это на ассемблере NASM:

Вот та же программа для газа:

Поскольку gas является «родным» ассемблером в Linux, сборка и компоновка с помощью gcc выполняются автоматически, как объясняется в комментариях к программе. Если вы просто введёте "gcc hello.s", то gcc соберётся, а затем попытается скомпоноваться с библиотекой C. Вы можете отключить этап компоновки с параметром -c для gcc или выполнить сборку и компоновку за один шаг, указав компоновщику не использовать библиотеку C с параметром -nostdlib .

Системные вызовы в 32-разрядной версии Linux

Есть некоторые системы с 32-разрядными сборками Linux.В этих системах вы вызываете службы операционной системы с помощью инструкции INT и используете разные регистры для аргументов системного вызова (в частности, EAX для номера вызова и EBX, ECX, EDX, EDI и ESI для аргументов). Хотя по историческим причинам было бы интересно показать некоторые примеры, это введение, вероятно, лучше сделать кратким.

Программирование с помощью библиотеки C

Иногда вам может понадобиться использовать ваши любимые библиотечные функции C в вашем ассемблерном коде. Это должно быть тривиально, потому что все библиотечные функции C хранятся в библиотеке C, такой как libc.a. Технически код, вероятно, находится в динамической библиотеке, такой как libc.so , а libc.a просто вызывает динамическую библиотеку. Тем не менее, все, что нам нужно сделать, это поместить вызовы C-функций в нашу программу на ассемблере и связать со статической библиотекой C, и все готово.

Прежде чем рассматривать пример, обратите внимание, что библиотека C уже определяет _start , которая выполняет некоторую инициализацию, вызывает функцию с именем main , выполняет некоторую очистку, а затем вызывает системную функцию exit ! Так что, если мы линкуемся с библиотекой C, все, что нам нужно сделать, это определить main и закончить инструкцией ret! Вот простой пример в NASM, иллюстрирующий колл пут .

И эквивалентная программа в GAS:

В предыдущем примере показано, что первый аргумент функции C, если это целое число или указатель, помещается в регистр RDI. Последующие аргументы помещаются в RSI, RDX, RCX, R8, R9, а затем последующие аргументы (которые ни один здравомыслящий программист никогда бы не использовал) отправляются «в стек» (подробнее об этом стеке позже). Если у вас есть аргументы с плавающей запятой, они перейдут в XMM0, XMM1 и т. д. Вызов функций — это нечто большее; мы увидим это позже.

Программирование для macOS

Вместо того, чтобы углубляться в системные вызовы macOS, давайте просто покажем простую программу hello, использующую библиотеку C. Мы предполагаем, что у вас 64-разрядная ОС, а также предполагается, что вы установили gcc (обычно получаемый путем загрузки xcode).

Здесь есть некоторые отличия! Функции библиотеки C имеют подчеркивание, и по какой-то странной причине нам пришлось указать rel по умолчанию, о чем вы можете прочитать в документации NASM.

Программирование для Win32

Win32 — это основной API операционной системы, используемый в большинстве 32-разрядных операционных систем Microsoft, включая Windows 9x, NT, 2000 и XP. Мы будем следовать плану предыдущего раздела и сначала рассмотрим программы, использующие только системные вызовы, а затем программы, использующие библиотеку C.

Только для исторической справки.

Эти заметки довольно старые. Я никогда не изучал Win64.

Вызов Win32 API напрямую

Win32 определяет тысячи функций! Код этих функций разбросан по множеству различных динамических библиотек, но большинство из них находятся в KERNEL32.DLL, USER32.DLL и GDI32.DLL (которые существуют во всех установках Windows). Прерывание для выполнения системных вызовов на процессоре x86 имеет шестнадцатеричный формат 2E, где EAX содержит номер системного вызова, а EDX указывает на таблицу параметров в памяти. Однако, по словам z0mbie, на самом деле номера системных вызовов не совпадают в разных операционных системах, поэтому для написания переносимого кода вы должны придерживаться вызовов API в различных системных библиотеках DLL.

Вот программа "Hello, World" в NASM, использующая только вызовы Win32.

Здесь вы можете видеть, что используемые нами вызовы Win32

и параметры передаются этим вызовам в стеке. Комментарии предписывают нам собрать объект в формат "win32" (не "coff"!), а затем скомпоновать его с помощью компоновщика ld . Конечно, вы можете использовать любой компоновщик, какой захотите, но ld поставляется с gcc, и вы можете бесплатно загрузить весь порт gcc для Win32. Мы передаем начальный адрес компоновщику и указываем статическую библиотеку libkernel32.a для компоновки. Эта статическая библиотека является частью дистрибутива Win32 gcc и содержит правильные вызовы системных библиотек DLL.

Газовая версия этой программы выглядит очень похоже:

На самом деле различия между двумя программами только синтаксические. Еще один второстепенный момент заключается в том, что газу все равно, определяете ли вы внешние системы с помощью какой-либо «внешней» директивы или нет.

Как и в версии для NASM, мы указали точку входа и будем передавать ее компоновщику в параметре -e. Чтобы собрать этот код, выполните

Опция -c важна! Он говорит gcc собирать но не компоновать. Без параметра -c gcc попытается связать объектный файл с библиотекой времени выполнения C. Поскольку мы не используем библиотеку времени выполнения C и фактически указываем нашу собственную начальную точку и очищаем себя с помощью ExitProcess, мы определенно хотим связать себя. Шаг связывания такой же, как и в примере с NASM; единственное отличие состоит в том, что gcc создает объектные файлы win32 с расширением .o, а не .obj.

Если вы действительно хотите заплатить поставщику за ассемблер и компоновщик, вы можете использовать ассемблер Microsoft MASM. Все, что меньше версии 6.14 будет крайне болезненно использовать. Вот версия программы hello в MASM

Директивы процессора (.386P) и модели (.model) раздражают, но они должны быть там, и директива процессора должна предшествовать директиве модели, иначе ассемблер решит, что процессор работает в 16-битном режиме ( *вздох* ). Как и прежде, мы должны указать точку входа и передать ее компоновщику. Собрать с

Параметр /c обязателен, так как ml попытается связать. Бесплатны не только ассемблер MASM, ml, но и компоновщик Microsoft, link.exe, и статические версии библиотек Win32, такие как kernel32.lib. После того, как вы купите те, с которыми связали свой код

Чтобы это работало, kernel32.lib должен находиться в известном пути к библиотеке, или компоновщику должны быть переданы дополнительные параметры. Параметр /subsystem может показаться вам интересным; оставьте его, чтобы увидеть нелепое сообщение об ошибке при запуске связанного исполняемого файла (по крайней мере, под Win9x).

Большая часть синтаксических странностей MASM, например использование ключевого слова offset для получения адреса переменной, отсутствует в NASM. Хотя NASM, вероятно, набирает популярность, существует гораздо больше кода MASM, и было бы неплохо хотя бы поверхностно познакомиться с MASM, поскольку он используется в большинстве публикаций. Это самое близкое к "стандартному" языку ассемблера x86.

Использование библиотеки времени выполнения C для программирования Win32

Как и в Linux, использование библиотеки времени выполнения C позволяет очень легко писать простые программы на языке ассемблера. Вот один в NASM:

Та же программа в газе выглядит так:

Обратите внимание, что вы можете собрать и связать с

Чтобы получить версию этой программы для MASM, вы также можете приобрести библиотеки времени выполнения C у Microsoft. Существует много версий библиотеки, но для однопоточных программ подойдет libc.lib. Вот программа полномочий в MASM:

При компоновке с помощью libc.lib вы получаете хорошие настройки компоновщика по умолчанию. Чтобы собрать и связать:

Вы должны убедиться, что компоновщик знает, где найти libc.lib, установив некоторые переменные среды, конечно, но вы поняли идею.

Программирование OpenGL в NASM для Win32

Для развлечения вот полная программа на языке ассемблера, которая реализует приложение OpenGL, работающее под управлением GLUT в системах Windows:

Программирование для DOS

И MASM, и NASM могут создавать исполняемые файлы DOS. DOS — это примитивная операционная система (действительно, многие люди, возможно, правильно, отказываются называть ее операционной системой), работающая только в реальном режиме. Адреса в реальном режиме представляют собой 20-битные значения, записанные в форме СЕГМЕНТ:СМЕЩЕНИЕ, где сегмент и смещение имеют ширину 16 бит, а физический адрес равен СЕГМЕНТ * 16 + СМЕЩЕНИЕ.

Программа DOS представляет собой набор сегментов. Когда программа загружается, DS:0 и ES:0 указывают на 256-байтовый раздел памяти, называемый префиксом сегмента программы, за которым сразу же следуют сегменты программы. CS:0 будет указывать на сегмент кода, а SS:0 — на сегмент стека. SP будет загружен с размером стека, заданным программистом, что идеально, потому что на x86 инструкция PUSH уменьшает указатель стека, а затем перемещает помещенное значение в память, адресованную SS:SP. Длина строки аргумента командной строки помещается в байт по смещению 80h префикса, а фактическая строка аргумента начинается со смещения 81h.

Вот простая программа для DOS, отображающая строку аргумента командной строки:

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

Обратите внимание, что все «службы операционной системы», такие как ввод/вывод, доступны через команду прерывания процессора, поэтому нет необходимости связывать вашу программу со специальной библиотекой. Конечно, если вы хотите связать 16-битную библиотеку времени выполнения C, вы, безусловно, можете это сделать.

Эхо-программа определяет только код и сегмент стека; пример программы с определенным программистом сегментом данных:

Несмотря на то, что DOS уже много лет устарела, краткое изучение систем DOS и режима реальной адресации x86 представляет определенный интерес. Во-первых, адреса реального режима соответствуют реальной физической памяти, поэтому с помощью хорошего отладчика можно очень легко наблюдать за тем, что происходит в машине. На самом деле большинство встраиваемых микропроцессоров работают в своего рода «реальном режиме». На настольных ПК, серверах и рабочих станциях работают менее 1% микропроцессоров; большинство из них представляют собой простые встроенные процессоры. Наконец, многие приложения для DOS все еще существуют, поэтому было бы полезно узнать, какая технология лежит в основе всего этого.

Написание оптимизированного кода

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

Различия между NASM, MASM и GAS

Полную синтаксическую спецификацию каждого языка ассемблера можно найти в другом месте, но вы можете узнать 99% того, что вам нужно знать, просмотрев сравнительную таблицу:

RSS
канал

Темы блога

    (15) (16) (24) (131) (106) (45) (29) (170) (15) (22) (2) (18) (2) (36) (5) (1) (33) ) (3) (71)

Архивы

Программирование Windows на языке ассемблера

Кто сказал, что программирование на ассемблере мертво? Продолжая мою недавнюю тему просмотра исполняемых файлов Windows, я решил полностью обойти C++ и попробовать написать программу для Windows полностью на ассемблере. Я был рад обнаружить, что это не сложно, особенно если у вас есть небольшой опыт сборки любого процессора. Мой первый пример ASM-программы состоит всего из 17 строк! Конечно, он не очень много делает, но демонстрирует скелет, который можно расширить, чтобы создать в точности программу, которую я хочу — больше не нужно возиться с параметрами компилятора C, чтобы предотвратить добавление загадочных «функций». к моему коду. Да, я помешан на контроле.

<р>1. Пример минимальной сборки

Вот простой пример:

Если на вашем ПК установлена ​​какая-либо версия Microsoft Visual Studio, включая бесплатные версии Visual Studio Express, значит, у вас уже есть MASM: Microsoft Macro Assembler. Сохраните файл примера как msgbox.asm и используйте MASM для его сборки из командной строки следующим образом:

Это не выглядит слишком сложным. Давайте рассмотрим его построчно.

.686
Это указывает ассемблеру генерировать код x86, совместимый с процессором Intel 686 или более поздней версии, также известным как Pentium Pro. Любая машина на базе Intel за последние 15-20 лет сможет запустить это, так что это хороший общий вариант по умолчанию. Вы также можете использовать здесь .386, .486 или .586, если хотите избежать создания каких-либо инструкций, несовместимых с этими старыми процессорами.

.model flat, stdcall
Модель памяти для всех программ Win32 всегда плоская. Второй параметр задает соглашение о вызовах по умолчанию для процедур, экспортируемых из этого файла, и может быть либо C, либо stdcall. В этом примере ничего не экспортируется, поэтому выбор не имеет особого значения, но я выберу stdcall.

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

Все функции Win32 API используют соглашение __stdcall, тогда как функции C и библиотека C используют соглашение __cdecl (или просто "C"). Вы также можете редко видеть соглашение __fastcall; посмотрите его для более подробной информации. Соглашения stdcall и cdecl аналогичны: оба аргумента передаются в стек, и аргументы помещаются справа налево. Итак, функция, прототип которой выглядит так:

вызывается путем помещения в стек сначала arg3, а затем arg2 и arg1:

Эти два соглашения отличаются только очисткой стека. При использовании cdecl вызывающая функция отвечает за удаление аргументов из стека, тогда как при использовании stdcall за очистку стека перед возвратом отвечает вызываемая функция.

EXTERN MessageBoxA@16 : proc
EXTERN ExitProcess@4 : proc
Эти строки сообщают MASM, что код ссылается на две внешние процедуры. Когда код будет собран в файл .obj, ссылки на эти процедуры останутся незавершенными. Когда файл .obj позже будет связан для создания готового исполняемого файла, он должен быть связан с другими файлами .obj или библиотеками, которые предоставляют определения для этих внешних ссылок. Если определения не найдены, вы увидите знакомое сообщение об ошибке компоновщика с жалобой на «неразрешенный внешний символ».

Забавные @4 и @16 в конце имен функций являются стандартным методом изменения имен для функций stdcall, включая все функции Win32. К имени функции добавляется суффикс с символом @ и общим количеством байтов аргументов, ожидаемых функцией. Это искаженное имя является символом, который появляется в файле .obj или библиотеке, а не исходным именем.Фактическое имя символа также имеет префикс подчеркивания, например. _MessageBox@16 , но MASM обрабатывает это автоматически, добавляя префикс подчеркивания ко всем статически импортируемым или экспортируемым общедоступным символам.

Чтобы узнать количество байтов аргументов, ожидаемых функцией stdcall Win32, вы можете просмотреть онлайн-справку MSDN и добавить размеры аргументов вручную, или вы можете использовать что-то вроде dumpbin /symbols user32.lib для просмотра искаженных имен. функций в библиотеке импорта.

Для функций cdecl изменение имени не допускается. Имя символа — это просто имя функции с префиксом подчеркивания, например. _strlen .

В большинстве случаев такой уровень детализации не виден, поскольку компилятору или ассемблеру известно соглашение о вызовах и список аргументов любых вызываемых вами функций, поэтому он может автоматически изменять имена за кулисами. Но в этом примере я никогда не говорил MASM, какое соглашение о вызовах используется для MessageBox или ExitProcess, а также количество и размеры аргументов, которые они ожидают, поэтому это не может помочь. с искажением имен, и мне приходится вводить искаженные имена вручную. Через минуту я покажу более удобный способ справиться с этим с помощью MASM.

.const
Директива .const указывает, что все, что следует за ней, является постоянными данными только для чтения и должно быть помещено в отдельный раздел исполняемого файла с именем .rdata. Память для этого раздела будет иметь атрибут «только для чтения», установленный диспетчером виртуальной памяти Windows, поэтому ошибочный код не сможет изменить его по ошибке. Другими возможными директивами раздела, связанными с данными, являются .data для чтения-записи данных и .data? для неинициализированных данных чтения-записи.

msgText db «Язык ассемблера Windows жив!», 0
msgCaption db «Hello World», 0
Следующие строки выделяют и инициализируют хранилище для двух частей данных с именами msgText и msgCaption. Поскольку предыдущая строка была директивой .const, эти данные будут помещены в раздел .rdata исполняемого файла. db — это директива ассемблера для «определения байта», за которой следует список значений байтов, разделенных запятыми. Значения могут быть числовыми константами, строковыми литералами или их сочетанием, как показано здесь. 0 после каждого строкового литерала является байтом нулевого конца для строк в стиле C.

.code
.code указывает на начало нового раздела, а все, что следует за ним, является программным кодом, а не данными. Он будет помещен в раздел исполняемого файла с именем .text. Почему директива не соответствует названию раздела?

Main:
Здесь код определяет метку с именем Main, которую затем можно использовать в качестве цели для инструкций перехода или других инструкций, которые ссылаются на память. Main относится к адресу, по которому собирается следующая строка кода. Здесь нет ничего волшебного в слове «Основной», а имена ярлыков могут быть любыми, если только они не являются ключевыми словами MASM.

push 0
push offset msgCaption
push offset msgText
push 0
Этот код помещает аргументы для MessageBox в стек справа налево, как того требует стандартный вызов соглашение. Согласно MSDN, прототип MessageBox:

int WINAPI MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);

Первый аргумент, помещаемый в стек, — это значение для uType, 4-байтовое целое число без знака. Значение 0 здесь соответствует константе MB_OK и означает, что MessageBox должен содержать одну кнопку с надписью «ОК». Затем вставляются адреса констант заголовка и текстовой строки. Ключевое слово offset указывает MASM помещать в память адрес строк, а не сами строки, и аналогично оператору & в C. Наконец, hWnd передается аргумент, который является дескриптором владельца окна сообщения. Используемое здесь значение 0 означает, что у окна сообщения нет владельца.

call MessageBoxA@16
Теперь функция Win32 MessageBox наконец вызывается. call поместит адрес возврата в стек, а затем перейдет к адресу _MessageBoxA@16 . Он будет использовать аргументы, ранее помещенные в стек, отобразит окно сообщения и будет ждать, пока пользователь не нажмет кнопку OK, прежде чем вернуться. Поскольку это функция stdcall, MessageBox также удалит аргументы из стека, прежде чем вернуться к вызывающей программе. Возвращаемое значение при вызове MessageBox будет помещено в регистр EAX, что является стандартным соглашением для функций Win32.

Обратите внимание, что код называется MessageBoxA с суффиксом A, указывающим, что заголовок и текст являются однобайтовыми строками ASCII. Альтернативой является MessageBoxW, который ожидает широкие или двухбайтовые строки Unicode. Многие функции Win32 существуют с вариантами -A и -W, подобными этому.

push eax
call ExitProcess@4
Возвращенное значение из MessageBox помещается в стек и вызывается ExitProcess. Его прототип выглядит так:

VOID ExitProcess(UINT uExitCode);

Для кода выхода программы требуется один аргумент. В этом примере любое значение, возвращаемое MessageBox, будет использоваться в качестве кода выхода. Это конец программы — вызов ExitProcess никогда не возвращается, потому что программа завершена.

End Main
Инструкция end закрывает последний сегмент и отмечает конец исходного кода. Он должен быть в конце каждого файла. Необязательный адрес, следующий за end, указывает точку входа программы, где выполнение начнется после загрузки программы в память. Кроме того, точку входа можно указать в командной строке на этапе связывания с помощью параметра /entry.

ml /coff /c /Cp msgbox.asm
ссылка /subsystem:windows /out:msgbox.exe kernel32.lib user32.lib msgbox.obj
ml имя ассемблера MASM. Запустив его, вы создадите файл msgbox.obj.
/coff указывает MASM создать объектный файл в формате COFF, совместимом с последними компиляторами Microsoft C, чтобы вы могли объединить объекты ассемблера и C в одну программу.
/c указывает MASM выполнять только этап сборки, останавливаясь после создания файла .obj, а не пытаться выполнить связывание.
/Cp сообщает MASM, что во всех идентификаторах нужно использовать заглавные буквы.

link — это компоновщик Microsoft, тот же самый, который вызывается за кулисами при создании программ C или C++ из Visual Studio.
/subsystem:windows означает, что это программа на базе графического интерфейса Windows. Измените это на /subsystem:console для текстовой программы, работающей в окне консоли.
/out:msgbox.exe — это имя для создаваемого исполняемого файла.

В оставшейся части строки указываются библиотеки и объектные файлы, которые необходимо связать. MessageBox реализован в user32, а ExitProcess — в kernel32, поэтому я включил эти библиотеки. Я не указал путь к библиотекам, поэтому компоновщик будет искать каталоги, указанные в переменной среды LIBPATH. Установщик Visual Studio обычно создает ярлык в меню «Пуск», чтобы помочь в этом: он называется «Командная строка разработчика для Visual Studio» и открывает окно консоли с переменными среды LIBPATH и PATH, установленными соответствующим образом для того места, где установлены инструменты разработки. .

<р>2. Усовершенствования макросов MASM и MASM32

MASM – это "макро-ассемблер", который содержит множество макросов, которые значительно упрощают программирование на ассемблере. Для начала я мог бы определить некоторые константы для замены магических нулей в аргументах MessageBox:

В предыдущем примере мне пришлось вручную изменять имена функций Win32 и помещать аргументы в стек по одному. Этого можно избежать, используя директивы MASM PROTO и INVOKE. Подобно прототипу функции в C, PROTO сообщает MASM, какое соглашение о вызовах использует функция, а также количество и типы аргументов, которые она ожидает. Затем функцию можно вызвать в одной строке с помощью INVOKE, которая проверит правильность аргументов, выполнит все необходимые изменения имени и сгенерирует push-инструкции для размещения аргументов в стеке в правильном порядке. . Используя эти директивы, строки, относящиеся к MessageBoxA в программе-примере, можно сжать следующим образом:

Прототип MessageBoxA stdcall :DWORD,:DWORD,:DWORD,:DWORD
вызов MessageBoxA, NULL, смещение msgText, смещение msgCaption, MB_OK

Многие люди, использующие MASM, будут использовать его в сочетании с MASM32, который предоставляет удобный набор включаемых файлов, содержащих прототипы общих функций и констант Windows. Это позволяет упростить соответствующие строки примера MessageBox до:

include \masm32\include\windows.inc
include \masm32\include\user32.inc
вызов MessageBoxA, NULL, смещение msgText, смещение msgCaption, MB_OK

Взгляните на отличный учебник Iczelion для примера программы MessageBox, в которой хорошо используются все удобные функции MASM и MASM32.

<р>3. Структурированное программирование с помощью MASM

Самая большая головная боль при написании любой нетривиальной программы на ассемблере заключается в том, что все мелкие детали быстро становятся утомительными. Простая конструкция if/else должна быть написана как инструкция CMP в сочетании с несколькими условными и безусловными переходами между отдельными предложениями. Выделение и использование локальных переменных в стеке — проблема. Работа с объектами и структурами требует вычисления смещения каждого поля от основания структуры. Это огромная проблема.

Ничто не может облегчить всю скуку (в конце концов, это это язык ассемблера), но MASM очень помогает. Такие директивы, как .IF, .ELSE и .LOCAL, позволяют писать ассемблерный код, почти похожий на C.Автоматически генерируются инструкции для резервирования и освобождения места для локальных переменных на основе стека, и на локальные переменные можно ссылаться по имени, а не с помощью неудобных конструкций, таких как EBP-8. MASM также поддерживает объявление структур в стиле C с именованными и типизированными полями. В результате может получиться ассемблерный код, удивительно читаемый. Заимствование фрагментов из другого учебника Iczelion:

Это почти похоже на C, и вы можете удивиться, насколько это на самом деле отличается от написания кода на C. Несмотря на внешний вид, это все же 100-процентный язык ассемблера, а инструкции в файле .asm — это именно то, что появится в конечном исполняемом файле. Не происходит оптимизации, переупорядочения инструкций и генерации настоящего кода в каком-либо сложном смысле. Такие директивы, как LOCAL, которые скрывают отдельные инструкции по сборке, — это всего лишь сложные макросы.

Если я найду достаточно мотивации, я скоро напишу еще один пост, в котором будет показана более полнофункциональная программа на ассемблере, использующая эти методы. Теперь, если вы хотите знать, ПОЧЕМУ в 21 веке кто-то будет писать программы для Windows на языке ассемблера, у меня нет хорошего ответа. Это может быть полезно, если вам нужно сделать что-то чрезвычайно специфичное или критически важное для производительности. Но если вы похожи на меня, единственная необходимая причина — это тот факт, что она существует, лежит в основе всего, что обычно делается с языками более высокого уровня. Всякий раз, когда я вижу такой черный ящик, мне хочется открыть крышку и заглянуть внутрь.

7 комментариев

Итак, бинарный файл какого размера у вас получился, и как макет выглядит по сравнению с версией C? 🙂

Что касается того, почему я использую его в 2015 году, я пишу компилятор, ориентированный на 64-разрядную платформу x86 (в настоящее время), напрямую генерирующий код и записывающий файл ELF или PE-COFF, так что я нахожу его удобно для тестирования/разборки фрагментов кода. Я написал почти прямой эквивалент вашей программы здесь, на самом деле, с немного большим количеством губбинов 😉

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

Окончательный исполняемый файл был очень похож на тот, который я создал на C с пользовательской точкой входа. После этого я немного пошалил и уменьшил его до 1024 байт, объединив все разделы в один. Я думаю, что это самое малое, что вы можете получить с помощью стандартных инструментов, потому что вам нужен заголовок и по крайней мере один раздел, а раздел должен быть выровнен по 512 байтам и будет дополнен до 512 байтов. Вы можете стать меньше, если создадите PE-заголовок и исполняемый файл вручную, а не с помощью компоновщика, и будете делать хитрые вещи, такие как встраивание кода/данных в сам заголовок.

NASM кажется очень популярным — возможно, даже более популярным, чем MASM. Я еще не пробовал NASM. Первые несколько примеров, которые я рассмотрел, были MASM, поэтому я просто использовал их.

Интересно, что такое соглашение о вызовах Microsoft x64. Я никогда не слышал об этом раньше.

Кроме того, программирование процедурного графического интерфейса на ASM было весьма популярно в Apple IIgs. Это также довольно утомительно без макросов.

С этой веб-страницы… «Он работает на любой платформе Windows (Win3/95/98), кроме NT, поскольку зависит от виртуального таймера vxd (VTD), которого нет в NT. ‘

Очень ретро!

Уважаемый сэр,
Я скачал MASM32 и установил его на свой ноутбук, работающий под управлением Windows Vista 32bit.
Я открыл исходный файл(hello world) в редакторе.
До сих пор я не смог найти ни одной команды, которая при вводе в редакторе запускала программу и показывала выходной файл.
Я буду признателен, если вы дадите мне знать, какие команды я должен ввести, чтобы получить желаемый результат на экране?
С уважением
Вахе

Плоский ассемблер — это быстрый и эффективный самособирающийся ассемблер x86 для операционных систем DOS, Windows и Linux. В настоящее время . оптимизировать коды инструкций по размеру. Плоский ассемблер является самокомпилируемым, а полный исходный код находится в формате .

Цена: БЕСПЛАТНО/бесплатно

Графический интерфейс Turbo Assembler

Turbo Assembler, популярный ассемблер для эмуляции 8086, работает только с командной строкой. . версия Borland Turbo Assembler с графическим пользовательским интерфейсом. Эта программа была разработана с учетом .

Цена: БЕСПЛАТНО / с открытым исходным кодом

ОллиДбг

OllyDbg — это 32-разрядный анализирующий отладчик уровня ассемблера для Microsoft® Windows®. Акцент на анализе двоичного кода делает его особенно полезным в случаях, когда источником является .

Цена: БЕСПЛАТНО/бесплатно

Смали

smali/baksmali — это ассемблер/дизассемблер для формата dex, используемого dalvik, реализацией Java VM для Android. Синтаксис частично основан на синтаксисе Jasmin/dedexer и поддерживает .

Цена: БЕСПЛАТНО / с открытым исходным кодом

MasmTidy

MASMTidy приводит в порядок исходный код ассемблера MASM *.asm, выравнивая его по четырем столбцам. Он также преобразует все ваши операторы в нижний регистр. Он не меняет порядок .

Цена: БЕСПЛАТНО/бесплатно

Netwide Assembler, NASM, представляет собой ассемблер 80x86 и x86-64, разработанный для переносимости и модульности. Он поддерживает . поддержка макросов. Почему еще один ассемблер? Ассемблер Netwide вырос из .

Цена: БЕСПЛАТНО / с открытым исходным кодом

Простой код для GoAsm

<р>. и позволит вам программировать приложение на ассемблере Windows так просто, как никогда раньше. Простой код для GoAsm.

Цена: БЕСПЛАТНО/бесплатно

Простой код

<р>. как и Visual Basic, позволяет программировать приложения на ассемблере Windows (исполняемые файлы, динамические и статические библиотеки, объектные файлы COFF, консольные приложения, драйверы и службы).

Цена: БЕСПЛАТНО/бесплатно

Простой код для MASM

<р>. и позволит вам программировать приложение на ассемблере Windows так просто, как никогда раньше. Простой код для MASM — это .

Цена: БЕСПЛАТНО/бесплатно

Балансировщик MASM

<р>. вы узнаете, где ваша ассемблерная программа MASM Microsoft *.asm, где у вас есть несоответствие сегмента/конца макроса/endm proc/endp или if/endif. Он работает, отображая вашу программу.

Цена: БЕСПЛАТНО/бесплатно

ИНСТРУМЕНТЫ

<р>. вы используете в своих проектах развития. АССЕМБЛЕРЫ: ktop.jpg (986 байт) 6800, 6801/6803, 6805, 6502, 68HC08, 6809, 68HC11, 68HC12, 68HC16, 8048, 8051/52, 80C320, 8080/85, .

Цена: $199,00/Условно-бесплатная версия

Командующий Doszip

<р>. компилируется с помощью Open Watcom 1.8 и ассемблера JWasm. Doszip Commander — это клон Norton Commander, разработанный как дополнение к .

Цена: БЕСПЛАТНО / с открытым исходным кодом

OptiVec для Visual C++

<р>. декартовый и полярный формат. Благодаря векторизованной реализации на ассемблере OptiVec работает в среднем в 2-3 раза быстрее, чем скомпилированный исходный код того же .

Цена: 249 долларов США за условно-бесплатную версию

Турбо C++

<р>. Компилятор MS-DOS, поддерживаемые в версии 3.0 шаблоны C++, встроенный ассемблер Borland и генерация исполняемых файлов режима MS-DOS для обоих . самое последнее на тот момент. Отдельный продукт Turbo Assembler больше не включался, вместо него входил встроенный ассемблер .

Цена: БЕСПЛАТНО/бесплатно

OptiVec для стран Персидского залива

OptiVec содержит более 3500 оптимизированных вручную функций, написанных на ассемблере, для всех типов данных с плавающей запятой и целых чисел . декартовый и полярный формат. Векторизованная реализация на ассемблере усложняет работу OptiVec в среднем в 2-3 раза.

Цена: БЕСПЛАТНО/бесплатно

<р>. нужен скрипт). Оптимизированный интерпретатор P-кода («байт-кода») на ассемблере (Windows и Linux); есть даже JIT для Windows и Linux. Полный исходный код .

Цена: БЕСПЛАТНО/бесплатно

OptiVec для C++ Builder

OptiVec содержит более 3500 оптимизированных вручную функций, написанных на ассемблере для всех типов данных с плавающей запятой и целых чисел из следующих областей: 1. Векторизованная форма арифметических операторов.

Цена: 249 долларов США за условно-бесплатную версию

PowerArchiver 2013

<р>. резервные копии. PowerArchiver Starter был написан на языке, похожем на ассемблер, для минимального размера и использования памяти. . .

Цена: 22,95 доллара США за пробную версию

FTP-библиотека SmartFTP

<р>. Алгоритмы HMAC: hmac-sha2-256, hmac-sha2-512, hmac-sha1, hmac-md5 Производительность: Оптимизированный ассемблер для алгоритмов, критически важных для производительности. Совместимость: проверено на всех общедоступных реализациях серверов SFTP/SSH. Автоматически использует обходные пути .

Цена: 699 долларов США за пробную версию

OptiVec для Lazarus/FreePascal

OptiVec содержит более 3500 оптимизированных вручную функций, написанных на ассемблере, для всех типов данных с плавающей запятой и целых чисел . декартовый и полярный формат. Векторизованная реализация на ассемблере усложняет работу OptiVec в среднем в 2-3 раза.

Цена: 249 долларов США за условно-бесплатную версию

Бесплатный декомпилятор Flash для JPEG

<р>. прямое редактирование исходного кода ActionScript - Редактирование исходного кода на ассемблере - Поддержка ActionScript 1/2 и AS3 - При нажатии на декомпилированный исходный код выделяется инструкция, связанная с P-кодом.

Цена: БЕСПЛАТНО/бесплатно

CMATH для Delphi

CMATH – это обширная библиотека для арифметики комплексных чисел и математики. Следующие функции делают CMATH идеальной заменой для Unit Complex, поставляемого с Delphi: 1. .

Цена: 60 долларов США за условно-бесплатную версию

Бесплатный Паскаль

<р>. у тебя есть друг-программист. · Отличная интеграция с ассемблером Вы думаете, что Паскаль для слабаков, которые . высшие умники среди вас, у нас есть интегрированные ассемблеры. Вы можете легко смешивать ассемблерный код и Паскаль.

Цена: БЕСПЛАТНО / с открытым исходным кодом

Чит-движок

<р>. но он также поставляется с отладчиком, дизассемблером, ассемблером, спидхаком, создателем тренажеров, инструментами прямого 3D-манипулирования, инструментами проверки системы и многим другим. Для новых пользователей .

Цена: БЕСПЛАТНО / с открытым исходным кодом

GMABooster

<р>. желаемая скорость GMA. Он сочетает в себе сложную технологию на уровне ассемблера и удобный графический пользовательский интерфейс, предлагая вам почти удвоить производительность ядра GMA.

Цена: БЕСПЛАТНО/бесплатно

Снес9x

<р>. кусочек). Snes9x написан на C ++ с тремя ядрами эмуляции процессора ассемблера на портах i386 Linux и Windows. В настоящее время существует множество портов.

Цена: БЕСПЛАТНО/бесплатно

Портативный менеджер DevProject Manager

DevProject Manager – это портативное многофункциональное приложение для управления проектами, предназначенное для разработчиков бесплатного и условно-бесплатного программного обеспечения, а также разработчиков с открытым исходным кодом. DevProject Manager позволяет вам управлять общей информацией о проекте, .

Цена: БЕСПЛАТНО/бесплатно

FreeBasic для Windows (64-разрядная версия)

<р>. Андре Виктор, состоящий из компилятора, ассемблера на основе GNU, компоновщика и архиватора, а также вспомогательных библиотек времени выполнения, включая графическую библиотеку на основе программного обеспечения. Компилятор, fbc, в настоящее время .

Цена: БЕСПЛАТНО / с открытым исходным кодом

FreeBasic для Windows

<р>.Андре Виктор, состоящий из компилятора, ассемблера на основе GNU, компоновщика и архиватора, а также вспомогательных библиотек времени выполнения, включая графическую библиотеку на основе программного обеспечения. Компилятор, fbc, в настоящее время .

Цена: БЕСПЛАТНО / с открытым исходным кодом

OptiVec для Delphi

OptiVec содержит более 3500 оптимизированных вручную функций, написанных на ассемблере, для всех типов данных с плавающей запятой и целых чисел . декартовый и полярный формат. Векторизованная реализация на ассемблере усложняет работу OptiVec в среднем в 2-3 раза.

Читайте также: