Как написать привет в ассемблере под Windows? - PullRequest
78 голосов
/ 21 июня 2009

Я хотел написать что-то базовое в ассемблере под Windows, я использую NASM, но ничего не получается.

Как написать и скомпилировать hello world без помощи функций C в Windows?

Ответы [ 8 ]

114 голосов
/ 22 июня 2009

В этом примере показано, как перейти непосредственно к Windows API, а не к ссылке в стандартной библиотеке C.

    global _main
    extern  _GetStdHandle@4
    extern  _WriteFile@20
    extern  _ExitProcess@4

    section .text
_main:
    ; DWORD  bytes;    
    mov     ebp, esp
    sub     esp, 4

    ; hStdOut = GetstdHandle( STD_OUTPUT_HANDLE)
    push    -11
    call    _GetStdHandle@4
    mov     ebx, eax    

    ; WriteFile( hstdOut, message, length(message), &bytes, 0);
    push    0
    lea     eax, [ebp-4]
    push    eax
    push    (message_end - message)
    push    message
    push    ebx
    call    _WriteFile@20

    ; ExitProcess(0)
    push    0
    call    _ExitProcess@4

    ; never here
    hlt
message:
    db      'Hello, World', 10
message_end:

Для компиляции вам понадобятся NASM и LINK.EXE (из Visual studio Standard Edition)

   nasm -fwin32 hello.asm
   link /subsystem:console /nodefaultlib /entry:main hello.obj 
27 голосов
/ 21 июня 2009

Примеры NASM .

Вызов libc stdio printf, реализация int main(){ return printf(message); }

; ----------------------------------------------------------------------------
; helloworld.asm
;
; This is a Win32 console program that writes "Hello, World" on one line and
; then exits.  It needs to be linked with a C library.
; ----------------------------------------------------------------------------

    global  _main
    extern  _printf

    section .text
_main:
    push    message
    call    _printf
    add     esp, 4
    ret
message:
    db  'Hello, World', 10, 0

Затем запустите

nasm -fwin32 helloworld.asm
gcc helloworld.obj
a

Существует также Руководство для начинающих пользователей Hello World в Nasm без использования библиотеки языка Си. Тогда код будет выглядеть следующим образом.

16-битный код с системными вызовами MS-DOS: работает в эмуляторах DOS или в 32-битной Windows с поддержкой NTVDM . Не может быть запущен "напрямую" (прозрачно) в любой 64-битной Windows, потому что ядро ​​x86-64 не может использовать режим vm86.

org 100h
mov dx,msg
mov ah,9
int 21h
mov ah,4Ch
int 21h
msg db 'Hello, World!',0Dh,0Ah,'$'

Создайте его в .com исполняемый файл, чтобы он был загружен в cs:100h со всеми сегментными регистрами, равными друг другу (модель крошечной памяти).

Удачи.

17 голосов
/ 23 июня 2009

Это примеры Win32 и Win64, использующие вызовы Windows API. Они для MASM, а не NASM, но посмотрите на них. Вы можете найти более подробную информацию в этой статье.

;---ASM Hello World Win32 MessageBox

.386
.model flat, stdcall
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib

.data
title db 'Win32', 0
msg db 'Hello World', 0

.code

Main:
push 0            ; uType = MB_OK
push offset title ; LPCSTR lpCaption
push offset msg   ; LPCSTR lpText
push 0            ; hWnd = HWND_DESKTOP
call MessageBoxA
push eax          ; uExitCode = MessageBox(...)
call ExitProcess

End Main

;---ASM Hello World Win64 MessageBox

extrn MessageBoxA: PROC
extrn ExitProcess: PROC

.data
title db 'Win64', 0
msg db 'Hello World!', 0

.code
main proc
  sub rsp, 28h  
  mov rcx, 0       ; hWnd = HWND_DESKTOP
  lea rdx, msg     ; LPCSTR lpText
  lea r8,  title   ; LPCSTR lpCaption
  mov r9d, 0       ; uType = MB_OK
  call MessageBoxA
  add rsp, 28h  
  mov ecx, eax     ; uExitCode = MessageBox(...)
  call ExitProcess
main endp

End

Чтобы собрать и связать их с помощью MASM, используйте это для 32-битного исполняемого файла:

ml.exe [filename] /link /subsystem:windows 
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:Main

или это для 64-битного исполняемого файла:

ml64.exe [filename] /link /subsystem:windows 
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:main
13 голосов
/ 17 ноября 2013

Flat Assembler не требует дополнительного компоновщика.Это делает программирование на ассемблере довольно простым.Это также доступно для Linux.

Это hello.asm из примеров Fasm:

include 'win32ax.inc'

.code

  start:
    invoke  MessageBox,HWND_DESKTOP,"Hi! I'm the example program!",invoke GetCommandLine,MB_OK
    invoke  ExitProcess,0

.end start

Fasm создает исполняемый файл:

>fasm hello.asm
flat assembler  version 1.70.03  (1048575 kilobytes memory)
4 passes, 1536 bytes.

И этопрограмма на IDA :

enter image description here

Вы можете увидеть три вызова: GetCommandLine, MessageBox и ExitProcess.

9 голосов
/ 16 января 2015

Чтобы получить .exe с компилятором NASM и компоновщиком Visual Studio, этот код работает нормально:

global WinMain
extern ExitProcess  ; external functions in system libraries 
extern MessageBoxA

section .data 
title:  db 'Win64', 0
msg:    db 'Hello world!', 0

section .text
WinMain:
    sub rsp, 28h  
    mov rcx, 0       ; hWnd = HWND_DESKTOP
    lea rdx,[msg]    ; LPCSTR lpText
    lea r8,[title]   ; LPCSTR lpCaption
    mov r9d, 0       ; uType = MB_OK
    call MessageBoxA
    add rsp, 28h  

    mov  ecx,eax
    call ExitProcess

    hlt     ; never here

Если этот код сохранен, например, на. "test64.asm", затем скомпилировать:

nasm -f win64 test64.asm

Создает "test64.obj" Затем ссылку из командной строки:

path_to_link\link.exe test64.obj /subsystem:windows /entry:WinMain  /libpath:path_to_libs /nodefaultlib kernel32.lib user32.lib /largeaddressaware:no

где path_to_link может быть C: \ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ bin или где находится ваша программа link.exe на вашем компьютере, path_to_libs может быть C: \ Program Files (x86) \ Windows Kits \ 8.1 \ Lib \ winv6.3 \ um \ x64 или где угодно - ваши библиотеки (в данном случае оба ядра32. lib и user32.lib находятся в одном месте, в противном случае используйте один параметр для каждого необходимого пути), а параметр / largeaddressaware: нет , чтобы избежать жалоб компоновщика на длинные адреса (для user32.lib в этот случай). Кроме того, как это делается здесь, если компоновщик Visual вызывается из командной строки, необходимо предварительно настроить среду (запустите один раз vcvarsall.bat и / или см. MS C ++ 2010 и mspdb100.dll ).

5 голосов
/ 23 июня 2009

Если вы не вызываете некоторую функцию, это совсем не тривиально. (И, если серьезно, нет реальной разницы в сложности между вызовом printf и вызовом API-функции win32.)

Даже DOS int 21h на самом деле является просто вызовом функции, даже если это другой API.

Если вы хотите сделать это без посторонней помощи, вам нужно напрямую поговорить с вашим видеооборудованием, вероятно, записав растровые изображения букв «Hello world» в кадровый буфер. Даже тогда видеокарта выполняет преобразование этих значений памяти в сигналы VGA / DVI.

Обратите внимание, что на самом деле ни одна из этих вещей вплоть до аппаратного обеспечения не является более интересной в ASM, чем в C. Программа "hello world" сводится к вызову функции. Хорошая вещь в ASM - это то, что вы можете использовать любой ABI, какой захотите; вам просто нужно знать, что это за ABI.

4 голосов
/ 18 января 2017

Лучшими примерами являются примеры с fasm, потому что fasm не использует компоновщик, который скрывает сложность программирования Windows другим непрозрачным слоем сложности. Если вы довольны программой, которая пишет в окно графического интерфейса, то для этого есть пример в каталоге примеров fasm.

Если вам нужна консольная программа, которая позволяет перенаправление стандартного входа и стандартного выхода, что также возможно. Существует примерная (helas крайне нетривиальная) программа, которая не использует графический интерфейс и работает строго с консолью, то есть с самим fasm. Это можно прореживать до самого необходимого. (Я написал четвертый компилятор, который является еще одним примером, отличным от графического интерфейса, но он также нетривиален).

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

FORMAT PE CONSOLE 

Раздел с именем .idata содержит таблицу, которая помогает окнам при запуске связывать имена функций с адресами времени выполнения. Он также содержит ссылку на KERNEL.DLL, которая является операционной системой Windows.

 section '.idata' import data readable writeable
    dd 0,0,0,rva kernel_name,rva kernel_table
    dd 0,0,0,0,0

  kernel_table:
    _ExitProcess@4    DD rva _ExitProcess
    CreateFile        DD rva _CreateFileA
        ...
        ...
    _GetStdHandle@4   DD rva _GetStdHandle
                      DD 0

Формат таблицы определяется окнами и содержит имена, которые ищутся в системных файлах при запуске программы. FASM скрывает некоторые из сложность позади ключевого слова rva. Таким образом, _ExitProcess @ 4 - это метка fasm, а _exitProcess - строка, которую ищет Windows.

Ваша программа находится в разделе «.text». Если вы объявляете этот раздел читабельным для записи и выполнения, это единственный раздел, который вам нужно добавить.

    section '.text' code executable readable writable

Вы можете позвонить на все объекты, которые вы заявили в разделе .idata. Для консольной программы вам нужно _GetStdHandle, чтобы найти файловые дескрипторы для стандартных in и standardout (используя символические имена, такие как STD_INPUT_HANDLE, которые fasm находит во включаемом файле win32a.inc). Когда у вас есть файловые дескрипторы, вы можете использовать WriteFile и ReadFile. Все функции описаны в документации к kernel32. Вы, вероятно, знаете об этом, или не пытались бы программировать на ассемблере.

В итоге: есть таблица с именами asci, которые связаны с ОС Windows. Во время запуска это преобразуется в таблицу вызываемых адресов, которую вы используете в своей программе.

4 голосов
/ 27 февраля 2011

Если вы хотите использовать NASM и компоновщик Visual Studio (link.exe) с примером Hello World от anderstornvig, вам придется вручную связать их с библиотекой времени выполнения C, которая содержит функцию printf ().

nasm -fwin32 helloworld.asm
link.exe helloworld.obj libcmt.lib

Надеюсь, это кому-нибудь поможет.

...