your programing

Windows에서 어셈블러로 Hello World를 작성하는 방법은 무엇입니까?

lovepro 2020. 10. 4. 12:55
반응형

Windows에서 어셈블러로 Hello World를 작성하는 방법은 무엇입니까?


Windows에서 어셈블리에 기본적인 것을 작성하고 싶었는데 NASM을 사용하고 있지만 아무것도 작동하지 않습니다.

Windows에서 C 함수의 도움없이 Hello World를 작성하고 컴파일하는 방법은 무엇입니까?


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

C 라이브러리를 사용하지 않는 Nasm의 Hello World에 대한 The Clueless Newbies Guide있습니다. 그러면 코드는 다음과 같습니다.

MS-DOS 시스템 호출이있는 16 비트 코드 : DOS 에뮬레이터 또는 NTVDM을 지원하는 32 비트 Windows에서 작동합니다 . x86-64 커널은 vm86 모드를 사용할 수 없기 때문에 64 비트 Windows에서 "직접"(투명하게) 실행할 수 없습니다.

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

.com이를 실행 파일 로 빌드 cs:100h하여 모든 세그먼트 레지스터가 서로 동일한 (작은 메모리 모델) 로드되도록합니다 .

행운을 빕니다.


이 예제는 C 표준 라이브러리에서 링크하지 않고 Windows API로 직접 이동하는 방법을 보여줍니다.

    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
   링크 / subsystem : console / nodefaultlib / entry : main hello.obj 

Windows API 호출을 사용하는 Win32 및 Win64 예제입니다. NASM이 아닌 MASM을위한 것이지만 살펴보십시오. 기사 에서 자세한 내용을 찾을 수 있습니다 .

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

플랫 어셈블러 에는 추가 링커가 필요하지 않습니다. 이것은 어셈블러 프로그래밍을 매우 쉽게 만듭니다. Linux에서도 사용할 수 있습니다.

이것은 hello.asmFasm 예제에서 가져온 것입니다.

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
플랫 어셈블러 버전 1.70.03 (1048575 킬로바이트 메모리)
4 패스, 1536 바이트.

그리고 이것은 IDA 의 프로그램입니다 .

enter image description here

GetCommandLine, MessageBox세 가지 호출을 볼 수 있습니다 ExitProcess.


NASM'compiler 및 Visual Studio의 링커를 사용하여 .exe를 얻으려면이 코드가 제대로 작동합니다.

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

where path_to_link could be C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin or wherever is your link.exe program in your machine, path_to_libs could be C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x64 or wherever are your libraries (in this case both kernel32.lib and user32.lib are on the same place, otherwise use one option for each path you need) and the /largeaddressaware:no option is necessary to avoid linker's complain about addresses to long (for user32.lib in this case). Also, as it is done here, if Visual's linker is invoked from command prompt, it is necessary to setup the environment previously (run once vcvarsall.bat and/or see MS C++ 2010 and mspdb100.dll).


Unless you call some function this is not at all trivial. (And, seriously, there's no real difference in complexity between calling printf and calling a win32 api function.)

Even DOS int 21h is really just a function call, even if its a different API.

If you want to do it without help you need to talk to your video hardware directly, likely writing bitmaps of the letters of "Hello world" into a framebuffer. Even then the video card is doing the work of translating those memory values into VGA/DVI signals.

Note that, really, none of this stuff all the way down to the hardware is any more interesting in ASM than in C. A "hello world" program boils down to a function call. One nice thing about ASM is that you can use any ABI you want fairly easy; you just need to know what that ABI is.


The best examples are those with fasm, because fasm doesn't use a linker, which hides the complexity of windows programming by another opaque layer of complexity. If you're content with a program that writes into a gui window, then there is an example for that in fasm's example directory.

If you want a console program, that allows redirection of standard in and standard out that is also possible. There is a (helas highly non-trivial) example program available that doesn't use a gui, and works strictly with the console, that is fasm itself. This can be thinned out to the essentials. (I've written a forth compiler which is another non-gui example, but it is also non-trivial).

Such a program has the following command to generate a proper executable header, normally done by a linker.

FORMAT PE CONSOLE 

A section called '.idata' contains a table that helps windows during startup to couple names of functions to the runtimes addresses. It also contains a reference to KERNEL.DLL which is the Windows Operating System.

 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

The table format is imposed by windows and contains names that are looked up in system files, when the program is started. FASM hides some of the complexity behind the rva keyword. So _ExitProcess@4 is a fasm label and _exitProcess is a string that is looked up by Windows.

Your program is in section '.text'. If you declare that section readable writeable and executable, it is the only section you need to add.

    section '.text' code executable readable writable

You can call all the facilities you declared in the .idata section. For a console program you need _GetStdHandle to find he filedescriptors for standard in and standardout (using symbolic names like STD_INPUT_HANDLE which fasm finds in the include file win32a.inc). Once you have the file descriptors you can do WriteFile and ReadFile. All functions are described in the kernel32 documentation. You are probably aware of that or you wouldn't try assembler programming.

In summary: There is a table with asci names that couple to the windows OS. During startup this is transformed into a table of callable addresses, which you use in your program.


If you want to use NASM and Visual Studio's linker (link.exe) with anderstornvig's Hello World example you will have to manually link with the C Runtime Libary that contains the printf() function.

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

Hope this helps someone.

참고URL : https://stackoverflow.com/questions/1023593/how-to-write-hello-world-in-assembler-under-windows

반응형