Windows altında montajda temel bir şey yazmak istedim, NASM kullanıyorum ama hiçbir şey çalışmıyor.
Windows'ta C işlevlerinin yardımı olmadan merhaba dünya nasıl yazılır ve derlenir?
Windows altında montajda temel bir şey yazmak istedim, NASM kullanıyorum ama hiçbir şey çalışmıyor.
Windows'ta C işlevlerinin yardımı olmadan merhaba dünya nasıl yazılır ve derlenir?
Yanıtlar:
Libc stdio çağrılıyor printf
, uygulamaint 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
O zaman koş
nasm -fwin32 helloworld.asm
gcc helloworld.obj
a
Ayrıca , C kütüphanesi kullanmadan Nasm'da The Clueless Newbies Guide to Hello World var . O zaman kod şöyle görünecektir.
MS-DOS sistem çağrılarıyla 16 bit kod: DOS öykünücülerinde veya NTVDM desteğiyle 32 bit Windows'ta çalışır . Herhangi bir 64 bit Windows altında "doğrudan" (şeffaf bir şekilde) çalıştırılamaz, çünkü bir x86-64 çekirdeği vm86 modunu kullanamaz.
org 100h
mov dx,msg
mov ah,9
int 21h
mov ah,4Ch
int 21h
msg db 'Hello, World!',0Dh,0Ah,'$'
Bunu bir .com
yürütülebilir dosya olarak oluşturun, böylece cs:100h
tüm segment kayıtları birbirine eşit olacak şekilde yüklenecektir (küçük bellek modeli).
İyi şanslar.
Bu örnek, doğrudan Windows API'ye nasıl gidileceğini ve C Standard Library'ye nasıl bağlanılmayacağını gösterir.
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:
Derlemek için NASM ve LINK.EXE'ye ihtiyacınız olacak (Visual studio Standard Edition'dan)
nasm -fwin32 merhaba.asm link / subsystem: console / nodefaultlib / entry: main hello.obj
gcc hello.obj
Bunlar, Windows API çağrılarını kullanan Win32 ve Win64 örnekleridir. NASM'den çok MASM içindir, ama onlara bir bakın. Bu makalede daha fazla ayrıntı bulabilirsiniz .
Bu, standart çıktıya yazdırmak yerine MessageBox kullanır.
;---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
Bunları MASM kullanarak birleştirmek ve bağlamak için bunu 32 bit yürütülebilir dosya için kullanın:
ml.exe [filename] /link /subsystem:windows
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:Main
veya 64 bit çalıştırılabilir dosya için bu:
ml64.exe [filename] /link /subsystem:windows
/defaultlib:kernel32.lib /defaultlib:user32.lib /entry:main
X64 Windows'un neden a'dan önce 28 saatlik yığın alanı ayırması gerekiyor call
? Bu, arama kuralı gereği 32 baytlık (0x20) gölge alanı, yani ev alanıdır. Çağrı kuralı RSP hizalanmış 16 baytlık olması gerektirdiğinden ve başka bir 8 byte 16 ile yığın yeniden hizalamak önce bir call
. ( main
Arayanımız (CRT başlangıç kodunda) bunu yaptı. 8 baytlık dönüş adresi, RSP'nin bir işleve girişte 16 baytlık sınırdan 8 bayt uzakta olduğu anlamına gelir.)
Gölge alanı , herhangi bir yığın değişkeninin (varsa) olacağı yerin yanına kendi yazmaç değiştirgelerini dökmek için bir işlev tarafından kullanılabilir. A system call
, daha önce bahsedilen 4 yazmaçlara ek olarak r10 ve r11 için de yer ayırmak için 30 saat (48 bayt) gerektirir. Ancak DLL çağrıları, syscall
talimatların etrafına sarılmış olsalar bile, yalnızca işlev çağrılarıdır .
Eğlenceli gerçek: Windows dışı, yani x86-64 System V çağrı kuralı (örneğin Linux'ta) gölge alanı kullanmaz ve 6 adede kadar tamsayı / işaretçi kayıt argümanı ve XMM kayıtlarında 8 adede kadar FP argümanı kullanır .
MASM en Kullanılması invoke
(çağırma kuralını bilir) yönergesi, 32-bit veya 64-bit olarak inşa edilebileceği bu bir sürümünü yapmak için bir ifdef kullanabilirsiniz.
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
Makro varyant her ikisi için de aynıdır, ancak montajı bu şekilde öğrenemezsiniz. Bunun yerine C-style asm öğreneceksiniz. invoke
for stdcall
veya fastcall
while cinvoke
is for cdecl
veya değişken bağımsız değişken fastcall
. Montajcı hangisini kullanacağını bilir.
Ne kadar invoke
genişlediğini görmek için çıktıyı parçalarına ayırabilirsiniz .
title
etiket adı olarak kullandığımda hatalarla karşılaşıyorum. Ancak etiket adı gibi başka bir şey kullandığımda mytitle
, her şey yolunda gidiyor .
Flat Assembler'ın ekstra bir bağlayıcıya ihtiyacı yoktur. Bu, assembler programlamasını oldukça kolaylaştırır. Linux için de mevcuttur.
Bu, hello.asm
Fasm örneklerinden:
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 bir yürütülebilir dosya oluşturur:
> fasm merhaba.asm düz montajcı sürüm 1.70.03 (1048575 kilobayt bellek) 4 geçiş, 1536 bayt.
Ve bu IDA'daki program :
Üç aramaları görebilirsiniz: GetCommandLine
, MessageBox
ve ExitProcess
.
NASM derleyicisine ve Visual Studio'nun bağlayıcısına sahip bir .exe almak için bu kod iyi çalışır:
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
Bu kod örneğin "test64.asm" üzerine kaydedilmişse, derlemek için:
nasm -f win64 test64.asm
"Test64.obj" üretir Daha sonra komut isteminden bağlanmak için:
path_to_link\link.exe test64.obj /subsystem:windows /entry:WinMain /libpath:path_to_libs /nodefaultlib kernel32.lib user32.lib /largeaddressaware:no
nerede path_to_link olabilir C: \ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ bin ya da her nerede makinenizin içerisine LINK.EXE program olduğunu path_to_libs olabilir C: \ Program Files (x86) \ Windows Setleri \ 8.1 \ Lib \ winv6.3 \ um \ x64 veya kitaplıklarınız nerede olursa olsun (bu durumda hem kernel32.lib hem de user32.lib aynı yerdedir, aksi takdirde ihtiyacınız olan her yol için bir seçenek kullanın) ve / largeaddressaware: hiçbir seçenek yoktur linker'in uzun adresler hakkında şikayet etmesini önlemek için gereklidir (bu durumda user32.lib için) Ayrıca, burada yapıldığı gibi, Visual'ın bağlayıcı komut isteminden çağrılırsa, ortamı önceden kurmak gerekir (vcvarsall.bat ve / veya MS C ++ 2010 ve mspdb100.dll'ye bakın)).
default rel
Dosyanızın en üstünde kullanmanızı şiddetle tavsiye ederim , böylece bu adresleme modları ( [msg]
ve [title]
) 32-bit mutlak yerine RIP'ye göre adresleme kullanır.
Bir fonksiyon çağırmadıkça , bu hiç de önemsiz değil. (Cidden, printf'i çağırmakla bir win32 api işlevini çağırmak arasında gerçek bir karmaşıklık farkı yok.)
DOS int 21h bile, farklı bir API olsa bile, gerçekten sadece bir işlev çağrısıdır.
Yardım almadan yapmak istiyorsanız, video donanımınızla doğrudan konuşmanız gerekir, muhtemelen "Merhaba dünya" harflerinin bit eşlemlerini bir çerçeve arabelleğine yazmanız gerekir. O zaman bile video kartı bu bellek değerlerini VGA / DVI sinyallerine çevirme işini yapıyor.
Aslına bakılırsa, donanıma kadar bu şeylerin hiçbirinin ASM'de C'deki kadar ilginç olmadığını unutmayın. Bir "merhaba dünya" programı bir işlev çağrısı olarak özetlenebilir. ASM ile ilgili güzel bir şey, istediğiniz herhangi bir ABI'yi oldukça kolay kullanabilmenizdir; sadece ABI'nin ne olduğunu bilmeniz gerekiyor.
En iyi örnekler fasm olanlardır, çünkü fasm, başka bir opak karmaşıklık katmanı tarafından Windows programlamasının karmaşıklığını gizleyen bir bağlayıcı kullanmaz. Bir GUI penceresine yazan bir programdan memnunsanız, fasm'ın örnek dizininde bunun bir örneği vardır.
Standart giriş ve standart çıkışların yeniden yönlendirilmesine izin veren bir konsol programı istiyorsanız, bu da mümkündür. Bir GUI kullanmayan ve kesinlikle konsolla çalışan, yani fasm'ın kendisiyle çalışan (helas oldukça önemsiz olmayan) bir örnek program vardır. Bu, esaslara göre inceltilebilir. (Başka bir gui olmayan örnek olan ancak aynı zamanda önemsiz olmayan bir dördüncü derleyici yazdım).
Böyle bir program, normal olarak bir bağlayıcı tarafından yapılan 32-bit yürütülebilir dosya için uygun bir başlık oluşturmak için aşağıdaki komuta sahiptir.
FORMAT PE CONSOLE
'.İdata' adlı bir bölüm, başlangıç sırasında pencerelerin işlev adlarını çalışma zamanı adresleriyle eşleştirmesine yardımcı olan bir tablo içerir. Ayrıca, Windows İşletim Sistemi olan KERNEL.DLL için bir başvuru içerir.
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
Tablo formatı, pencereler tarafından empoze edilir ve program başlatıldığında sistem dosyalarında aranan isimleri içerir. FASM, rva anahtar kelimesinin arkasındaki karmaşıklığın bir kısmını gizler. Yani _ExitProcess @ 4 bir fasm etiketi ve _exitProcess Windows tarafından aranan bir dizedir.
Programınız '.text' bölümünde. Bu bölümün okunabilir ve çalıştırılabilir olduğunu bildirirseniz, eklemeniz gereken tek bölüm budur.
section '.text' code executable readable writable
.İdata bölümünde beyan ettiğiniz tüm tesisleri arayabilirsiniz. Bir konsol programı için, standart giriş ve standart çıkış için dosya tanımlayıcıları bulmak üzere _GetStdHandle'a ihtiyacınız vardır (fasm'ın win32a.inc içerme dosyasında bulduğu STD_INPUT_HANDLE gibi sembolik isimler kullanarak). Dosya tanımlayıcılara sahip olduğunuzda, WriteFile ve ReadFile'ı yapabilirsiniz. Tüm işlevler kernel32 belgelerinde açıklanmıştır. Muhtemelen bunun farkındasınız veya assembler programlamayı denemeyeceksiniz.
Özetle: Windows işletim sistemi ile eşleşen asci adlarına sahip bir tablo var. Başlatma sırasında bu, programınızda kullandığınız çağrılabilir adresler tablosuna dönüştürülür.
NASM ve Visual Studio'nun bağlayıcısını (link.exe) anderstornvig'in Hello World örneğiyle kullanmak istiyorsanız, printf () işlevini içeren C Runtime Libary ile manuel olarak bağlantı kurmanız gerekecektir.
nasm -fwin32 helloworld.asm
link.exe helloworld.obj libcmt.lib
Umarım bu birine yardımcı olur.