Windowsのアセンブラーでhello worldを書き込む方法は?


89

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

WindowsでC関数を使用せずにHello Worldを作成してコンパイルする方法は?


2
また、Steve GibsonのSmall Is Beautiful windowsアセンブリスターターキットも確認してください。
ジェレミー

Cライブラリを使用しないことは、やや奇妙な制約です。MS-Windowsオペレーティングシステム内のライブラリを呼び出す必要があります。おそらくkernel32.dllです。Microsoftがこれをcで書いたか、Pascalで書いたかは関係ありません。OSが提供する関数のみを呼び出すことができるという意味ですか、Unixタイプのシステムではシステムコールと呼ばれるものは何ですか?
アルバートファンデルホルスト

Cライブラリでは、GCCやMSVCに付属しているようなCランタイムライブラリを使用しないことを意味します。もちろん、彼または彼女はkernel32.dllのようないくつかの標準的なWindows DLLを使用する必要があります。
Rudy Velthuis、2017年

2
kernel32.dllとgccランタイムライブラリの違いは、形式(両方ともdll)ではなく、言語にもありません(両方ともおそらくcですが、それは非表示になっています)。OS提供かどうかの違いです。
アルバートファンデルホルスト

私はこれを探していましたが、笑いも含まれていないfasmで何も見つけることができませんでした
bluejayke

回答:


37

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

ありますNASMでのHello Worldのクルーレス初心者ガイドは、 Cライブラリを使用せずに。その後、コードは次のようになります。

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

幸運を。


26
質問は「Cライブラリを使用せずに」と明示的に言及しています
Mehrdad Afshari

25
違う。Cライブラリ自体は当然可能ですので、それは可能です。実際、少しだけ難しいです。正しい5つのパラメーターを指定してWriteConsole()を呼び出すだけです。
MSalters 2009年

12
2番目の例はCライブラリ関数を呼び出しませんが、Windowsプログラムでもありません。それを実行するために仮想DOSマシンが起動されます。
ロムロCeccon

7
@Alex Hart、彼の2番目の例はDOS用であり、Windows用ではありません。DOSでは、小さなモードのプログラム(.COMファイル、64Kbの合計コード+データ+スタック)は0x100hから始まります。これは、セグメントの最初の256バイトがPSP(コマンドライン引数など)によって取得されるためです。このリンクを参照してください:en.wikipedia.org/wiki/Program_Segment_Prefix
zvolkov '15 / 01/12

7
これは要求されたものではありません。最初の例はCライブラリを使用し、2番目の例はWindowsではなくMS-DOSです。
Paulo Pinto

127

この例は、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 

21
これをリンクするには、おそらくkernel32.libを含める必要があります(私はそうしました)。リンク/ subsystem:console / nodefaultlib / entry:main hello.obj kernel32.lib
Zach Burlingame

5
objをMinGWのld.exeにリンクする方法は?
DarrenVortex 2013

4
@DarrenVortexgcc hello.obj
2013年

4
これはまたからALINKのような自由なリンカーを使用してうまくいくsourceforge.net/projects/alinkからかGoLinkのgodevtool.com/#linker?そのためだけにVisual Studioをインストールしたくないのですか?
jj_ 14

21

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

これは、標準出力に出力する代わりに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ウィンドウは28hバイトのスタックスペースを予約する必要があるのcallですか? これは、呼び出し規約で必要な32バイト(0x20)のシャドウスペース(ホームスペース)です。呼び出し規約は、RSPを必要とするためとに別の8バイト、16バイトを整列させることが、16でスタックを再合わせの前にcall。(mainCRTスタートアップコードの)呼び出し元がそれを行いました。8バイトの戻りアドレスは、RSPが関数へのエントリ時に16バイト境界から8バイト離れていることを意味します。)

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

おもしろい事実: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を学習します。invokeはfor stdcallまたはfastcallwhile cinvokeはfor cdeclまたはvariable引数fastcallです。アセンブラはどちらを使用するかを認識しています。

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


1
あなたの答えのために+1。ARM(WOA)上のWindows用のアセンブリコードも追加できますか?
Annie

1
rspが0x20ではなく0x28バイトを必要とするのはなぜですか?呼び出し規約に関するすべてのリファレンスは、32である必要があると述べていますが、実際には40を必要とするようです。
douggard

32ビットのメッセージボックスコードで、何らかの理由でtitleラベル名として使用すると、エラーが発生します。ただし、のようなラベル名として他のものを使用するとmytitle、すべてが正常に動作します。
user3405291 2018年

インクルードなしでそれを行う方法?
bluejayke

13

フラットアセンブラには、追加のリンカは必要ありません。これにより、アセンブラのプログラミングが非常に簡単になります。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のプログラムです:

ここに画像の説明を入力してください

3つの呼び出しを確認できます:GetCommandLineMessageBoxおよびExitProcess


これはインクルードとGUIを使用していますが、インクルードなしでCMDを実行するにはどうすればよいですか?
bluejayke

マニュアルを読んでみましたか?flatassembler.net/docs.php?article=manual#2.4.2
ceving

DLLなしでコンソールに書き込むセクションを私に指摘できますか?
bluejayke

11

NASMコンパイラと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を実行するか、またはを参照してください MS C ++ 2010とmspdb100.dllを)。


2
default relファイルの先頭で使用することを強くお勧めします。これらのアドレッシングモード([msg]および[title])は、32ビット絶対ではなくRIP相対アドレッシングを使用します。
Peter Cordes

5

あなたが呼び出す場合を除き、いくつかの機能を、これはすべての些細ではありません。(そして、真剣に、printfを呼び出すこととwin32 api関数を呼び出すことの間の複雑さに実際の違いはありません。)

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

助けなしでやりたい場合は、ビデオハードウェアに直接話しかける必要があります。「Hello world」の文字のビットマップをフレームバッファーに書き込む可能性があります。それでも、ビデオカードはこれらのメモリ値をVGA / DVI信号に変換する作業を行っています。

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


これは優れた点です--- ASMとCはどちらもOS提供の関数(Windowsでは_WriteFile)に依存しています。では、魔法はどこにあるのでしょうか?ビデオカードのデバイスドライバコードにあります。
アサドエブラヒム2015年

2
これは要点の外です。投稿者は、「Windows」で実行されるアセンブラプログラムに質問します。つまり、Windowsの機能(kernel32.dllなど)は使用できますが、Cygwinのlibcなどの他の機能は使用できません。大声で叫ぶために、ポスターはCライブラリを明示的に述べていません。
アルバートファンデルホルスト

5

fasmはリンカを使用しないため、fasmを使用した例が最も良い例です。リンカを使用すると、Windowsプログラミングの複雑さが、不透明な別の複雑な層によって隠されます。GUIウィンドウに書き込むプログラムに満足している場合は、fasmのサンプルディレクトリにその例があります。

コンソールプログラムが必要な場合は、標準入力と標準出力のリダイレクトも可能です。GUIを使用せず、コンソールで厳密に動作する(非常に重要なHelasの)簡単なサンプルプログラムがあり、それ自体がfasmです。これは本質的に間引くことができます。(私は別の非GUIの例である4番目のコンパイラーを作成しましたが、それも簡単ではありません)。

このようなプログラムには、適切な実行可能ヘッダーを生成する次のコマンドがあり、通常はリンカーによって実行されます。

FORMAT PE CONSOLE 

「.idata」というセクションには、起動時にウィンドウが関数の名前をランタイムアドレスに結び付けるのに役立つテーブルが含まれています。また、WindowsオペレーティングシステムであるKERNEL.DLLへの参照も含まれています。

 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

テーブル形式はウィンドウによって課せられ、プログラムの起動時にシステムファイルで検索される名前が含まれます。FASMは、rvaキーワードの背後にある複雑さの一部を隠しています。したがって、_ExitProcess @ 4はfasmラベルであり、_exitProcessはWindowsによって検索される文字列です。

プログラムはセクション「.text」にあります。そのセクションが読み取り可能で書き込み可能および実行可能であると宣言した場合、それは追加する必要がある唯一のセクションです。

    section '.text' code executable readable writable

.idataセクションで宣言したすべての機能を呼び出すことができます。コンソールプログラムの場合、標準入力および標準出力のファイル記述子を見つけるために_GetStdHandleが必要です(fasmがインクルードファイルwin32a.incで見つけるSTD_INPUT_HANDLEのようなシンボリック名を使用)。ファイル記述子を取得したら、WriteFileとReadFileを実行できます。すべての関数はkernel32のドキュメントで説明されています。あなたはおそらくそれを知っているか、アセンブラープログラミングを試そうとはしません。

要約すると、Windows OSに対応するASCII名のテーブルがあります。起動時に、これは呼び出し可能なアドレスのテーブルに変換され、プログラムで使用します。


FASMはリンカーを使用しない場合がありますが、それでもPEファイルをアセンブルする必要があります。つまり、実際にはコードをアセンブルするだけでなく、通常はリンカーが実行するジョブも実行します。そのため、私の考えでは、リンカーの「複雑さを隠している」というのはまったく逆であるという誤解を招くおそれがあります。 -アセンブラの仕事はプログラムをアセンブルすることですが、多くのものに依存する可能性があるプログラムイメージにプログラムを埋め込むのはリンカに任せます。そのため、リンカとアセンブラを分離することは良いことだと私は思います。
午前

@amnこのように考えてください。上記のプログラムを作成するためにリンカーを使用する場合、それはプログラムが何をするか、またはそれが何で構成されるかについてより多くの洞察を与えますか?fasmソースを見ると、プログラムの完全な構造がわかります。
アルバートファンデルホルスト

フェアポイント。反対に、リンクを他のすべてのものから分離することには、メリットもあります。通常はオブジェクトファイルにアクセスできます(これは、プログラムのイメージファイル形式とは関係なく、プログラムの構造も検査するのに大いに役立ちます)。好みの異なるリンカーを、異なるオプションで呼び出すことができます。それは再利用性と構成可能性についてです。そのことを念頭に置いて、FASMは「便利」であるため、すべてのことを実行し、これらの原則を破ります。私は主にそれに反対しているわけではありません-彼らの正当化は私には分かります-しかし、私は、それを必要としません。
午前

fasm 64ビットウィンドウのトップラインでの不正なisntructionのエラーを取得する
bluejayke

3

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

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

これが誰かを助けることを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.