web-dev-qa-db-ja.com

WindowsでアセンブラーでHello Worldを記述する方法

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

WindowsのC関数を使用せずにhello worldを作成およびコンパイルする方法

80
feiroox

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

幸運を。

28
anderstornvig

この例は、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 
 link/subsystem:console/nodefaultlib/entry:main hello.obj 
114
caffiend

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

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

フラットアセンブラー は、追加のリンカーを必要としません。これにより、アセンブラのプログラミングが非常に簡単になります。 Linuxでも利用可能です。

これは hello.asm 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は実行可能ファイルを作成します。

> fasm hello.asm 
フラットアセンブラーバージョン1.70.03(1048575キロバイトメモリ)
 4パス、1536バイト。

そして、これは [〜#〜] ida [〜#〜] のプログラムです:

enter image description here

GetCommandLineMessageBoxExitProcessの3つの呼び出しを確認できます。

13
ceving

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 を参照) 。

10
rarias

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

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

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

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

5

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

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

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

4
tboerman

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

コンソールプログラムが必要な場合は、標準入力と標準出力のリダイレクトも可能です。 GUIを使用せず、コンソールで厳密に動作する(非常に重要な)サンプルプログラムがあります。これは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に対応するasci名を持つテーブルがあります。起動時に、これはプログラムで使用する呼び出し可能なアドレスのテーブルに変換されます。