2009-06-21 7 views
64

Ich wollte etwas Basis in Assembly unter Windows schreiben, ich benutze NASM, aber ich kann nichts arbeiten.Wie schreibe ich Hallo Welt in Assembler unter Windows?

Wie schreibe und kompiliere Hallo Welt ohne die Hilfe von C-Funktionen unter Windows?

+0

Schauen Sie sich auch Steve Gibson's (kleine ist schön) (http://www.grc.com/smgassembly.htm) Windows Assembly Starter-Kit. – Jeremy

+0

Nicht mit C-Bibliotheken ist eine etwas seltsame Einschränkung. Man muss eine Bibliothek innerhalb des MS-Windows-Betriebssystems aufrufen. wahrscheinlich kernel32.dll. Ob Microsoft das in c geschrieben hat oder Pascal scheint irrelevant zu sein. Ist gemeint, dass nur vom Betriebssystem bereitgestellte Funktionen aufgerufen werden können, was in einem Unix-System heißt Systemaufrufe? –

+0

Mit C-Bibliotheken gehe ich davon aus, dass er oder sie meint, ohne eine C-Laufzeitbibliotheken wie die, die mit GCC oder MSVC kommen, zu verwenden. Natürlich muss er oder sie einige Standard-Windows-DLLs wie kernel32.dll verwenden. –

Antwort

29

NASM examples.

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

laufen Dann

nasm -fwin32 helloworld.asm 
gcc helloworld.obj 
a 

Es gibt auch The Clueless Newbies Guide to Hello World in Nasm ohne die Verwendung einer C-Bibliothek. Dann würde der Code so aussehen.

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

Viel Glück.

+13

Die Frage erwähnt explizit "ohne C-Bibliotheken zu verwenden" –

+0

Es gibt keine zuverlässige Möglichkeit, dies zu tun, ohne eine C-Funktion irgendwann aufzurufen. Außer wenn mit "C-Funktion" meinst du "Standard-C-Funktion". –

+20

Falsch. Die C-Bibliothek selbst kann natürlich, so ist es möglich. Es ist tatsächlich nur ein bisschen schwieriger. Sie müssen nur WriteConsole() mit den richtigen 5 Parametern aufrufen. – MSalters

104

Dieses Beispiel zeigt, wie Sie direkt zur Windows-API gehen und keine Verbindung zur C-Standardbibliothek herstellen.

global _main 
    extern [email protected] 
    extern [email protected] 
    extern [email protected] 

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

    ; hStdOut = GetstdHandle(STD_OUTPUT_HANDLE) 
    push -11 
    call [email protected] 
    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 [email protected] 

    ; ExitProcess(0) 
    push 0 
    call [email protected] 

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

zu kompilieren, müssen Sie NASM und Link.exe (aus Visual Studio Standard Edition)

 
    nasm -fwin32 hello.asm 
    link /subsystem:console /nodefaultlib /entry:main hello.obj 
+15

Sie wahrscheinlich müssen die kernel32.lib enthalten, um dies zu verknüpfen (ich tat). link/subsystem: console/nodefaultlib/eintrag: main hello.obj kernel32.lib –

+4

Wie verbinde ich das obj mit ld.exe von MinGW? – DarrenVortex

+3

@DarrenVortex 'gcc hello.obj' – towry

4

Es sei denn, Sie einige Funktion nennen dies durchaus nicht trivial ist. (Und ernsthaft, es gibt keinen wirklichen Unterschied in der Komplexität zwischen dem Aufruf von printf und dem Aufruf einer win32 api-Funktion.)

Sogar DOS int 21h ist wirklich nur ein Funktionsaufruf, auch wenn es eine andere API ist.

Wenn Sie es ohne Hilfe tun möchten, müssen Sie direkt mit Ihrer Videohardware sprechen, wahrscheinlich Bitmaps der Buchstaben von "Hallo Welt" in einen Framebuffer schreiben. Selbst dann verarbeitet die Videokarte diese Speicherwerte in VGA/DVI-Signale.

Beachten Sie, dass wirklich nichts von der ganzen Sache bis zur Hardware in ASM interessanter ist als in C. Ein "Hallo Welt" -Programm läuft auf einen Funktionsaufruf hinaus. Eine schöne Sache an ASM ist, dass Sie jedes gewünschte ABI ganz einfach verwenden können; Sie müssen nur wissen, was ABI ist.

+0

Dies ist ein ausgezeichneter Punkt --- ASM und C beide verlassen sich auf eine Betriebssystem-Funktion (_WriteFile in Windows). Wo ist die Magie? Es befindet sich im Gerätetreibercode für die Grafikkarte. –

+0

Dies ist gründlich neben dem Punkt. Das Poster fragt ein Assembler-Programm, das "unter Windows" läuft. Das bedeutet, dass Windows-Funktionen verwendet werden können (z. B. kernel32.dll), aber keine anderen Funktionen wie libc unter Cygwin. Für lautes Geschrei sagt das Plakat explizit keine C-Bibliotheken. –

15

Dies sind Win32- und Win64-Beispiele, die Windows-API-Aufrufe verwenden. Sie sind eher für MASM als für NASM, aber sehen Sie sich diese an. Sie finden weitere Details in this Artikel.

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

zu montieren und zu verknüpfen diese MASM, verwenden Sie diese für 32-Bit-Programm:

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

oder diese für 64-Bit-Programm:

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

+1 für Ihre Antwort. Können Sie bitte auch Assemblercode für Windows auf ARM (WOA) hinzufügen? – Annie

+0

Warum benötigt rsp 0x28 Bytes und nicht 0x20? Alle Referenzen auf der Aufrufkonvention sagen, dass es 32 sein sollte, aber es scheint in der Praxis 40 zu erfordern. – douggard

+1

Beantwortet hier: https://stackoverflow.com/a/19128544/1176872 – douggard

4

Wenn Wenn Sie den Linker (link.exe) von NASM und Visual Studio mit dem Hello World-Beispiel von anderstornvig verwenden möchten, müssen Sie manuell eine Verknüpfung herstellen mit der C-Laufzeitbibliothek, die die Funktion printf() enthält.

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

Hoffe, das hilft jemandem.

12

Flat Assembler benötigt keinen zusätzlichen Linker. Dies macht die Assembler-Programmierung ziemlich einfach. Es ist auch für Linux verfügbar.

Dies ist hello.asm aus den Fasm Beispiele:

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 entsteht ein ausführbares Programm:

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

Und das ist das Programm in IDA:

enter image description here

Sie können siehe die drei cal ls: GetCommandLine, MessageBox und ExitProcess.

7

Um eine Exe mit NASM'compiler und Visual Studio Linker dieser Code funktioniert zu erhalten fein:

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 

Wenn dieser Code auf beispielsweise gespeichert "Test64.asm", dann zu kompilieren:

nasm -f win64 test64.asm 

Erzeugt "test64.obj" dann über die Eingabeaufforderung zu verknüpfen:

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

wo path_to_linkC sein könnte: \ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ bin oder wo auch immer ist Ihr link.exe Programm in Ihrem Computer, Pfad könnte sein C: \ Programme (x86) \ Windows Kits \ 8.1 \ Lib \ winv6. 3 \ um \ x64 oder wo auch immer Ihre Bibliotheken sind (in diesem Fall befinden sich sowohl kernel32.lib als auch user32.lib an derselben Stelle, andernfalls verwenden Sie eine Option für jeden benötigten Pfad) und die Option /largeaddressaware: no ist erforderlich um zu vermeiden, dass Linker über Adressen zu lange beschweren (für user32.lib in diesem Fall). Wenn der Linker von Visual von der Eingabeaufforderung aus aufgerufen wird, ist es auch hier erforderlich, die Umgebung zuvor einzurichten (einmal vcvarsall.bat ausführen und/oder MS C++ 2010 and mspdb100.dll anzeigen).

2

Die besten Beispiele sind jene mit fasm, weil fasm keinen Linker verwendet, der die Komplexität der Windows-Programmierung durch eine weitere undurchsichtige Komplexitätsschicht verbirgt. Wenn Sie mit einem Programm zufrieden sind, das in ein GUI-Fenster schreibt, dann gibt es dafür im Beispielverzeichnis von fasm ein Beispiel.

Wenn Sie ein Konsolenprogramm wollen, das die Umleitung von Standard- und Standard-Out ermöglicht, ist dies ebenfalls möglich. Es gibt ein (helas sehr nicht-triviales) Beispielprogramm, das kein gui verwendet und ausschließlich mit der Konsole arbeitet, also fasm selbst. Dies kann auf das Wesentliche ausgedünnt werden. (Ich habe einen vierten Compiler geschrieben, der ein anderes Nicht-GUI-Beispiel ist, aber es ist auch nicht-trivial).

Ein solches Programm hat den folgenden Befehl, um einen richtigen ausführbaren Header zu generieren, der normalerweise von einem Linker ausgeführt wird.

FORMAT PE CONSOLE 

Ein Abschnitt namens ‚.idata‘ enthält eine Tabelle, die Fenster während des Starts zu koppeln Namen von Funktionen zu den Runtimes Adressen hilft.Es enthält auch einen Verweis auf KERNEL.DLL, das Windows-Betriebssystem ist.

section '.idata' import data readable writeable 
... 

Ihr Programm wird im Abschnitt '.text'. Wenn Sie diesen Abschnitt schreibbar und ausführbar deklarieren, ist dies der einzige Abschnitt, den Sie benötigen.

section '.text' code executable readable writable 

Sie können alle von Ihnen im Abschnitt .idata deklarierten Einrichtungen aufrufen. Für ein Konsolenprogramm benötigen Sie _GetStdHandle, um die Dateideskriptoren für Standard In und Standardout zu finden (mit symbolischen Namen wie STD_INPUT_HANDLE, die fasm in der Include-Datei win32a.inc findet). Sobald Sie die Dateideskriptoren haben, können Sie WriteFile und ReadFile tun. Alle Funktionen sind in der Dokumentation zu kernel32 beschrieben. Sie sind sich dessen wahrscheinlich bewusst oder Sie würden Assembler-Programmierung nicht versuchen.

Zusammenfassend: Es gibt eine Tabelle mit ASCI-Namen, die an das Windows-Betriebssystem gekoppelt sind. Während des Startvorgangs wird diese in eine Tabelle mit aufrufbaren Adressen umgewandelt, die Sie in Ihrem Programm verwenden.