web-dev-qa-db-fra.com

Comment écrire bonjour monde en assembleur sous Windows?

Je voulais écrire quelque chose de basique dans Assembly sous Windows. J'utilise NASM, mais rien ne fonctionne.

Comment écrire et compiler hello world sans l'aide de fonctions C sous Windows?

80
feiroox

exemples de MSNA .

Appeler libc stdio printf, implémenter 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

Puis courir

nasm -fwin32 helloworld.asm
gcc helloworld.obj
a

Il y a aussi Guide des débutants de Clueless pour Hello World in Nasm sans l'utilisation d'une bibliothèque C. Alors le code ressemblerait à ceci.

Code 16 bits avec appels système MS-DOS: fonctionne dans les émulateurs DOS ou dans Windows 32 bits avec prise en charge de NTVDM . Ne peut pas être exécuté "directement" (de manière transparente) sous n’importe quel Windows 64 bits, car un noyau x86-64 ne peut pas utiliser le mode vm86.

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

Construisez ceci dans un .com exécutable afin qu’il soit chargé à cs:100h avec tous les registres de segments égaux les uns aux autres (petit modèle de mémoire).

Bonne chance.

28
anderstornvig

Cet exemple montre comment accéder directement à l'API Windows et non à un lien dans la bibliothèque standard 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:

Pour compiler, vous aurez besoin de NASM et LINK.EXE (à partir de Visual studio Standard Edition).

 nasm -fwin32 hello.asm 
 lien/sous-système: console/nodefaultlib/entry: main hello.obj 
114
caffiend

Ce sont des exemples Win32 et Win64 utilisant des appels API Windows. Ils sont pour MASM plutôt que pour NASM, mais regardez-les. Vous pouvez trouver plus de détails dans l'article this .

;---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

Pour assembler et lier ceux-ci à l'aide de MASM, utilisez ceci pour un exécutable 32 bits:

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

ou ceci pour un exécutable 64 bits:

ml64.exe [filename] /link /subsystem:windows 
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:main
17
PhiS

Flat Assembler n'a pas besoin d'un éditeur de liens supplémentaire. Cela rend la programmation en assembleur assez facile. Il est également disponible pour Linux.

C'est hello.asm à partir des exemples 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 crée un exécutable:

> fasm hello.asm 
 assembleur à plat version 1.70.03 (1048575 kilo-octets en mémoire) 
 4 passes, 1536 octets. 

Et voici le programme en IDA :

enter image description here

Vous pouvez voir les trois appels: GetCommandLine, MessageBox et ExitProcess.

13
ceving

Pour obtenir un fichier .exe avec NASM'Compiler et l'éditeur de liens de Visual Studio, ce code fonctionne correctement:

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

Si ce code est enregistré sur, par exemple, "test64.asm", puis à compiler:

nasm -f win64 test64.asm

Produit "test64.obj" Ensuite, pour lier à partir de la commande Invite:

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 pourrait être C:\Fichiers de programme (x86)\Microsoft Visual Studio 10.0\VC\bin ou où se trouve votre programme link.exe sur votre machine, path_to_libs pourrait être C :\Program Files (x86)\Kits Windows\8.1\Lib\winv6.3\um\x64 ou n’importe où se trouvent vos bibliothèques (dans ce cas, kernel32.lib et user32.lib sont activés.) au même endroit, sinon utilisez une option pour chaque chemin dont vous avez besoin) et l'option /largeaddressaware: no est nécessaire pour éviter que l'éditeur de liens se plaindre des adresses trop longues ( pour user32.lib dans ce cas). De plus, comme c'est le cas ici, si l'éditeur de liens de Visual est appelé à partir de la commande Invite, il est nécessaire de configurer l'environnement précédemment (exécuter une fois vcvarsall.bat et/ou voir MS C++ 2010 et mspdb100.dll ) .

10
rarias

Sauf si vous appelez certains fonction, ce n'est pas du tout trivial. (Et sérieusement, il n'y a pas de réelle différence de complexité entre appeler printf et appeler une fonction win32 api.)

Même DOS int 21h n'est en réalité qu'un appel de fonction, même s'il s'agit d'une API différente.

Si vous souhaitez le faire sans aide, vous devez parler directement à votre matériel vidéo, écrivez probablement des bitmaps des lettres de "Hello world" dans un framebuffer. Même dans ce cas, la carte vidéo est en train de convertir ces valeurs de mémoire en signaux VGA/DVI.

Notez que rien de tout cela jusqu’au matériel n’est plus intéressant en ASM que en C. Un programme "hello world" se résume à un appel de fonction. Une bonne chose à propos de ASM est que vous pouvez utiliser n'importe quelle ABI que vous voulez assez facilement; vous avez juste besoin de savoir ce qu'est ABI.

5
Captain Segfault

Si vous souhaitez utiliser l'éditeur de liens de NASM et de Visual Studio (link.exe) avec l'exemple Hello World d'Anderstornvig, vous devrez vous connecter manuellement à la bibliothèque C Runtime Libary contenant la fonction printf ().

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

J'espère que ça aide quelqu'un.

4
tboerman

Les meilleurs exemples sont ceux avec fasm, car fasm n'utilise pas d'éditeur de liens, ce qui masque la complexité de la programmation Windows par une autre couche opaque de complexité. Si vous vous contentez d'un programme qui écrit dans une fenêtre graphique, il existe un exemple à cet effet dans le répertoire exemple de fasm.

Si vous voulez un programme console, cela permet la redirection des entrées standard et des sorties standard, ce qui est également possible. Il existe un exemple de programme (très peu trivial) disponible qui n’utilise pas d’interface graphique et fonctionne strictement avec la console, c’est fasm lui-même. Cela peut être réduit à l'essentiel. (J'ai écrit un quatrième compilateur qui est un autre exemple non-graphique, mais il est également non-trivial).

Un tel programme a la commande suivante pour générer un en-tête exécutable approprié, normalement effectué par un éditeur de liens.

FORMAT PE CONSOLE 

Une section appelée '.idata' contient un tableau qui aide Windows au démarrage à coupler les noms de fonctions aux adresses d'exécution. Il contient également une référence à KERNEL.DLL, qui est le système d'exploitation 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

Le format de tableau est imposé par Windows et contient des noms qui sont recherchés dans les fichiers système au démarrage du programme. FASM cache une partie de la complexité du mot clé rva. Ainsi, _ExitProcess @ 4 est une étiquette fasm et _exitProcess est une chaîne qui est recherchée par Windows.

Votre programme est dans la section '.text'. Si vous déclarez que cette section est lisible en écriture et qu’elle est exécutable, c’est la seule section à ajouter.

    section '.text' code executable readable writable

Vous pouvez appeler toutes les installations que vous avez déclarées dans la section .idata. Pour un programme console, vous avez besoin de _GetStdHandle pour trouver les fichiers archivés pour l'entrée standard et l'entrée standard (en utilisant des noms symboliques tels que STD_INPUT_HANDLE que fasm trouve dans le fichier d'inclusion win32a.inc). Une fois que vous avez les descripteurs de fichier, vous pouvez faire WriteFile et ReadFile. Toutes les fonctions sont décrites dans la documentation de kernel32. Vous êtes probablement au courant de cela ou vous ne voudriez pas essayer la programmation par assembleur.

En résumé: Il existe une table avec des noms asci qui se couplent au système d'exploitation Windows. Au démarrage, cela se transforme en une table d'adresses appelables, que vous utilisez dans votre programme.

4