107

Windows でのアセンブリで基本的なものを書きたかったのですが、NASM を使用していますが、何も機能しません。

WindowsでC関数の助けを借りずにhello worldを書いてコンパイルする方法は?

4

8 に答える 8

144

この例では、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
于 2009-06-22T19:51:29.407 に答える
39

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ライブラリを使用しないThe Clueless Newbies Guide to Hello World in Nasmもあります。次に、コードは次のようになります。

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、すべてのセグメント レジスタが互いに等しい状態でロードされるようにします (小さなメモリ モデル)。

幸運を。

于 2009-06-21T10:17:49.600 に答える
26

これらは、Windows API 呼び出しを使用した Win32 および Win64 の例です。それらは NASM ではなく MASM 用ですが、それらを見てください。詳細については、この記事を参照してください。

これは、stdout に出力する代わりに MessageBox を使用します。

Win32 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

Win64 MASM

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

x64 Windows が .NET ファイルの前に 28h バイトのスタック領域を確保する必要があるのはなぜcallですか これは、32 バイト (0x20) のシャドウ スペース、別名ホーム スペースであり、呼び出し規則で必要とされます。さらに、スタックを 16 ずつ再整列するためにさらに 8 バイトが必要ですcall。(私たちmainの呼び出し元 (CRT スタートアップ コード内) はそれを行いました。8 バイトの戻りアドレスは、RSP が関数へのエントリで 16 バイト境界から 8 バイト離れていることを意味します。)

関数はシャドウ スペースを使用して、スタック引数 (存在する場合) がある場所の隣にレジスタ引数をダンプできます。Asystem callには、前述の 4 つのレジスタに加えて、r10 と r11 用のスペースも確保するために 30h (48 バイト) が必要です。syscallしかし、DLL 呼び出しは、命令のラッパーであっても、単なる関数呼び出しです。

豆知識: 非 Windows、つまり x86-64 System V 呼び出し規則 (Linux など) はシャドウ スペースをまったく使用せず、最大 6 つの整数/ポインター レジスタ引数XMM レジスタで最大 8 つの FP 引数を使用します。 .


MASM のinvokeディレクティブ (呼び出し規約を認識している) を使用すると、1 つの ifdef を使用して、32 ビットまたは 64 ビットとしてビルドできるバージョンを作成できます。

ifdef rax
    extrn MessageBoxA: PROC
    extrn ExitProcess: PROC
else
    .386
    .model flat, stdcall
    include kernel32.inc
    includelib kernel32.lib
    include user32.inc
    includelib user32.lib
endif
.data
caption db 'WinAPI', 0
text    db 'Hello World', 0
.code
main proc
    invoke MessageBoxA, 0, offset text, offset caption, 0
    invoke ExitProcess, eax
main endp
end

マクロ バリアントはどちらも同じですが、この方法ではアセンブリを学習できません。代わりに、C スタイルの asm を学習します。invokeforstdcallまたはfastcallwhilecinvokeは forcdeclまたは variable argumentfastcallです。アセンブラは、どちらを使用するかを認識しています。

出力を逆アセンブルして、invoke展開方法を確認できます。

于 2009-06-23T12:58:44.697 に答える
14

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

path_to_linkC:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\binまたは、マシン内の link.exe プログラムがどこにあるか、 path_to_libsC :\Program Files (x86)\Windows Kits\8.1\のようになります。 Lib\winv6.3\um\x64またはライブラリがある場所 (この場合、kernel32.lib と user32.lib の両方が同じ場所にあります。それ以外の場合は、必要なパスごとに 1 つのオプションを使用します) および/largeaddressaware:noオプションはアドレスが長すぎるというリンカーの不満を避けるために必要です(この場合はuser32.libの場合)。また、ここで行うように、コマンドプロンプトから Visual のリンカを起動する場合は、事前に環境を設定する必要があります (vcvarsall.bat を 1 回実行するか、MS C++ 2010 および mspdb100.dllを参照してください)。)。

于 2015-01-16T16:53:54.997 に答える
6

いくつかの関数を呼び出さない限り、これは決して些細なことではありません。(そして、真剣に、printfの呼び出しとwin32 api関数の呼び出しの複雑さに実際の違いはありません。)

DOS int 21hでさえ、APIが異なっていても、実際には単なる関数呼び出しです。

ヘルプなしでそれを実行したい場合は、ビデオハードウェアと直接通信する必要があります。おそらく、「Helloworld」の文字のビットマップをフレームバッファーに書き込みます。それでも、ビデオカードはそれらのメモリ値をDisplayPort / HDMI / DVI/VGA信号に変換する作業を行っています。

実際、ハードウェアに至るまでのこれらのものはどれも、CよりもASMで興味深いものではないことに注意してください。「helloworld」プログラムは、関数呼び出しに要約されます。ASMの優れた点の1つは、必要なABIをかなり簡単に使用できることです。あなたはそのABIが何であるかを知る必要があるだけです。

于 2009-06-22T20:34:28.713 に答える
5

NASM と Visual Studio のリンカー (link.exe) を anderstornvig の Hello World の例で使用する場合は、printf() 関数を含む C ランタイム ライブラリと手動でリンクする必要があります。

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

これが誰かに役立つことを願っています。

于 2011-02-26T21:54:17.723 に答える