NASMを使用しているWindowsのアセンブリで基本的なものを書きたかったのですが、何も機能しません。
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
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
にロードし、すべてのセグメントレジスタを互いに等しくします(小さなメモリモデル)。
幸運を。
この例は、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
これらは、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
フラットアセンブラー は、追加のリンカーを必要としません。これにより、アセンブラのプログラミングが非常に簡単になります。 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 [〜#〜] のプログラムです:
GetCommandLine
、MessageBox
、ExitProcess
の3つの呼び出しを確認できます。
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_linkはC:\ Program Files(x86)\ Microsoft Visual Studio 10.0\VC\binまたはマシンのlink.exeプログラムの場所はどこでもpath_to_libsはC :\ 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 を参照) 。
some functionを呼び出さない限り、これは決して些細なことではありません。 (そして、真剣に、printfの呼び出しとwin32 api関数の呼び出しの間に複雑さの実質的な違いはありません。)
DOS int 21hでさえ、たとえ異なるAPIであっても、実際には単なる関数呼び出しです。
助けを借りずにそれをしたい場合は、ビデオハードウェアと直接話をする必要があります。「Hello world」の文字のビットマップをフレームバッファに書き込む可能性があります。それでも、ビデオカードはこれらのメモリ値をVGA/DVI信号に変換する作業を行っています。
実際、Cの場合よりもASMの場合、ハードウェアに至るまでこのようなものはどれも興味深いものではないことに注意してください。「hello world」プログラムは関数呼び出しに要約されます。 ASMの優れた点の1つは、必要なABIをかなり簡単に使用できることです。そのABIが何であるかを知る必要があります。
NASMとVisual Studioのリンカー(link.exe)をanderstornvigのHello Worldサンプルで使用する場合、printf()関数を含むCランタイムライブラリと手動でリンクする必要があります。
nasm -fwin32 helloworld.asm
link.exe helloworld.obj libcmt.lib
これが誰かを助けることを願っています。
最良の例は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名を持つテーブルがあります。起動時に、これはプログラムで使用する呼び出し可能なアドレスのテーブルに変換されます。