winapi assembly x86 nasm

winapi - Cómo escribir hello world en ensamblador en Windows?



assembly x86 (8)

Quería escribir algo básico en ensamblado en Windows, estoy usando NASM, pero no puedo hacer que funcione nada.

¿Cómo escribir y compilar hello world sin la ayuda de las funciones C en Windows?


A menos que llame a alguna función, esto no es para nada trivial. (Y, en serio, no existe una diferencia real en la complejidad entre llamar a printf y llamar a una función api win32).

Incluso DOS int 21h es realmente solo una llamada a función, incluso si es una API diferente.

Si desea hacerlo sin ayuda, debe hablar directamente con su hardware de video, probablemente escribiendo bitmaps de las letras de "Hola mundo" en un framebuffer. Incluso entonces, la tarjeta de video está haciendo el trabajo de traducir esos valores de memoria en señales VGA / DVI.

Tenga en cuenta que, en realidad, nada de esto hasta el hardware es más interesante en ASM que en C. Un programa de "mundo hello" se reduce a una llamada de función. Una cosa buena de ASM es que puedes usar cualquier ABI que quieras con bastante facilidad; solo necesitas saber qué es ese ABI.


Este ejemplo muestra cómo ir directamente a la API de Windows y no enlazar en la Biblioteca estándar de 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:

Para compilar, necesitará NASM y LINK.EXE (de Visual Studio Standard Edition)

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


Estos son ejemplos de Win32 y Win64 que usan llamadas a la API de Windows. Son para MASM en lugar de NASM, pero échales un vistazo. Puedes encontrar más detalles en this artículo.

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

Para ensamblar y vincular estos usando MASM, use esto para el ejecutable de 32 bits:

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

o esto para el ejecutable de 64 bits:

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


Los mejores ejemplos son aquellos con fasm, porque fasm no usa un enlazador, que oculta la complejidad de la programación de Windows por otra capa opaca de complejidad. Si está satisfecho con un programa que escribe en una ventana gui, entonces hay un ejemplo para eso en el directorio de ejemplo de fasm.

Si desea un programa de consola, eso permite la redirección de entrada estándar y estándar que también es posible. Hay un programa de ejemplo (helas altamente no trivial) disponible que no utiliza una interfaz gráfica de usuario, y funciona estrictamente con la consola, que es muy rápido. Esto se puede diluir a lo esencial. (He escrito un cuarto compilador que es otro ejemplo no gui, pero tampoco es trivial).

Tal programa tiene el siguiente comando para generar un encabezado ejecutable apropiado, normalmente hecho por un enlazador.

FORMAT PE CONSOLE

Una sección llamada ''.idata'' contiene una tabla que ayuda a las ventanas durante el inicio a asociar los nombres de las funciones a las direcciones de los tiempos de ejecución. También contiene una referencia a KERNEL.DLL que es el sistema operativo de 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

El formato de tabla lo imponen las ventanas y contiene los nombres que se buscan en los archivos del sistema cuando se inicia el programa. FASM oculta parte de la complejidad detrás de la palabra clave rva. Así que _ExitProcess @ 4 es una etiqueta fasm y _exitProcess es una cadena que busca Windows.

Tu programa está en la sección ''.text''. Si declara que esa sección es legible y puede ejecutarse, es la única sección que necesita agregar.

section ''.text'' code executable readable writable

Puede llamar a todas las instalaciones que declaró en la sección .idata. Para un programa de consola necesita _GetStdHandle para encontrar los descriptores de archivo para entrada estándar y salida estándar (usando nombres simbólicos como STD_INPUT_HANDLE que fasm encuentra en el archivo de inclusión win32a.inc). Una vez que tenga los descriptores de archivos, puede hacer WriteFile y ReadFile. Todas las funciones se describen en la documentación kernel32. Probablemente lo sabe o no probaría la programación del ensamblador.

En resumen: hay una tabla con nombres asci que se asocian con el sistema operativo Windows. Durante el inicio, esto se transforma en una tabla de direcciones invocables, que usted usa en su programa.


Para obtener un .exe con NASM''compiler y el enlazador de Visual Studio, este código funciona bien:

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 este código se guarda en, por ejemplo, "test64.asm", entonces para compilar:

nasm -f win64 test64.asm

Produce "test64.obj" Luego para vincular desde el símbolo del sistema:

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

donde path_to_link podría ser C: / Archivos de programa (x86) / Microsoft Visual Studio 10.0 / VC / bin o donde sea que esté su programa link.exe en su máquina, path_to_libs podría ser C: / Archivos de programa (x86) / Windows Kits / 8.1 / Lib / winv6.3 / um / x64 o donde sea que estén sus bibliotecas (en este caso, tanto kernel32.lib como user32.lib están en el mismo lugar, de lo contrario use una opción para cada ruta que necesite) y / granaddressaware: no hay opción Es necesario evitar que el enlazador se queje de las direcciones a long (para user32.lib en este caso). Además, como se hace aquí, si el enlazador de Visual se invoca desde el símbolo del sistema, es necesario configurar previamente el entorno (ejecutar una vez vcvarsall.bat y / o ver MS C ++ 2010 y mspdb100.dll ).


Si desea utilizar el enlazador de NASM y Visual Studio (link.exe) con el ejemplo de Hello World de anderstornvig, deberá vincular manualmente con C Runtime Libary que contiene la función printf ().

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

Espero que esto ayude a alguien.


Flat Assembler no necesita un enlazador adicional. Esto hace que la programación del ensamblador sea bastante fácil. También está disponible para Linux.

Esto es hello.asm de los ejemplos de 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 crea un ejecutable:

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

Y este es el programa en IDA :

Puede ver las tres llamadas: GetCommandLine , MessageBox y ExitProcess .


Ejemplos de NASM .

Llamar a libc stdio printf , implementando int main(){ return printf(message); } 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

Entonces corre

nasm -fwin32 helloworld.asm gcc helloworld.obj a

También existe The Newbie Clueless Guide to Hello World en Nasm sin el uso de una biblioteca C. Entonces el código se vería así.

Código de 16 bits con llamadas al sistema MS-DOS: funciona en emuladores DOS o en Windows de 32 bits con soporte NTVDM . No se puede ejecutar "directamente" (de forma transparente) en ningún Windows de 64 bits, porque un kernel x86-64 no puede usar el modo vm86.

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

Constrúyalo en un ejecutable .com para que se cargue en cs:100h con todos los registros de segmentos iguales (modelo de memoria pequeña).

Buena suerte.