IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

3. diel - Hello world v ASM vo Windows

V minulej lekcii, Inštalácia kompilátora assembleri vo Windows , sme si nainštalovali kompilátor a vytvorili prvú zatiaľ prázdny ASM projekt.

V dnešnom ASM tutoriálu vytvoríme prvý program v assembleri pre Windows. Pôjde o 32 i 64-Bito aplikáciu používajúci MessageBox a konzolu k výpisu textu.

Hello world v ASM - Desktopová aplikácia

Pri vytváraní nového projektu sme si zvolili, či chceme konzolovú alebo desktopovú aplikáciu. To môžeme kedykoľvek zmeniť aj vo vlastnostiach projektu. Konkrétne v sekcii Linker / System je položka subsystému. U konzolové aplikácie je tam nastavené Console (/SUBSYSTEM:CONSOLE). U desktopové aplikácie tam je Windows (/SUBSYSTEM:WINDOWS).

32-bitová aplikácia

Najskôr si ukážeme 32-bitovou desktopovú aplikáciu a vysvetlíme si základnú štruktúru programu. Do .asm súboru vložíme nasledujúci kód, ktorý zobrazí text Hello, world ! ako vyskakovacie okienko MessageBox:

        .586
        .model flat, stdcall
        include windows.inc
        .data
message db "Hello, world !",0
        .code
main proc
        invoke MessageBox, 0, addr message, addr message, MB_OK
        ret
main endp
end main

Kód si teraz podrobne rozoberieme.

Procesor a model

Na prvom riadku je uvedený procesor, pre ktorý je aplikácia určená, čo je .586:

.586
.model flat, stdcall

Ak chceme, aby aplikácia bežala tiež na procesoroch z deväťdesiatych rokov, môžeme napísať .386 alebo .486. Potom ale nebudeme môcť používať niektoré novšie inštrukcie.

Na druhom riadku je pamäťový model flat a volacie konvencie stdcall. Toto nastavenie používajú systémové funkcie Windows, takže tu nemáme na výber.

include

Ak používame systémové funkcie Windows, musíme si do programu vložiť windows.inc:

include windows.inc

Bez funkcií operačného systému by sme o vypísanie texte museli požiadať napr. Priamo BIOS, čo si tiež ukážeme. Teraz sa nám ale funkcia pre výpis textu užívateľovi bude hodiť:) Cestu k windows.inc nemusíme písať, pretože sme ju už zadali v nastavení kompilátora parametrom /I.

Vo väčších projektoch môžeme používať svoje vlastné .inc súbory. To je vhodné pre definícia spoločných konštánt alebo makier. Tie potom môžeme vložiť do niekoľkých ASM súborov. Ale o tom až neskôr v kurze.

.data, .code

Direktívy .data a .code rozdeľujú program na dáta a kód:

        .data
message db "Hello, world !",0
        .code

To je dôležité hlavne aby fungovalo takzvané "Zamedzenie spustenia údajov" (Data Execution Prevention, DEP). To je ochrana proti vírusom, ktoré sa predtým šírili v obrázkoch alebo dokonca v MP3 súboroch. Ak používateľ otvoril súbor v aplikácii, ktorá mala bezpečnostnú chybu pretečeniu zásobníka (buffer overflow), potom inštrukcie RET skočila do dátovej sekcie, kde sa spustil vírus.

V dnešnej dobe to už nie je problém, pretože aj keby sme si do dátovej sekcie zapísali nejaké inštrukcie, nemôžeme je spustiť (spôsobí to výnimku a aplikácie spadne). Vo 32-bitových aplikáciách si môžete DEP vypnúť v nastaveniach Linker -> Advanced, ale rozhodne to nikdy nerobte.

Za behu programu nemôžeme zapisovať inštrukcie ani do sekcie .code, pretože tá je len na čítanie. To znamená, že program nemôže sám seba modifikovať. Hoci v assembleri programujeme na najnižšej úrovni priamo s inštrukciami procesora, nemôžeme si robiť čo chceme a nemožno obísť bezpečnostné obmedzenia operačného systému (ak píšeme program pre neho).

Ak niektoré dáta nepotrebujeme meniť, je lepšie umiestniť ich do sekcie .const, ktorá je len na čítanie. Ak dáta nepotrebujeme inicializovať, ale iba pre nich vyhradiť miesto v pamäti, použijeme sekcii .data?.

Direktívy db (Define Byte) a dw Define Word)

Direktívou db sa zadávajú dáta typu byte, definujeme teda premennú ktorá môže obsahovať napr. 8-bitové číslo (teda 8 jedničiek / núl, jeden bajt) alebo texty v ANSI kódovanie (v strednej Európe je kódovanie Windows-1250). Kompiler potom 1 bajt založí pre každý znak, čo je aj náš prípad. Viac hodnôt sa oddeľuje čiarkami, tu nulou 0 ukončíme reťazec.

Vo viacjazyčných aplikáciách sa používa Unicode a všetky texty sa dávajú zvyčajne do resources. Môžeme ale použiť aj direktívu dw, ktorá potom v pamäti vyhradí 2 bajty, viď ďalej.

Ďalšie kódovanie

Hoci to naša ukážka pre jednoduchosť neobsahuje, v UASM môžeme bez problémov používať Unicode. Keď je ASM súbor v kódovaní UTF-8, zapíše sa nasledovne:

option literals:on
message    dw "čeština",0

V kompilátora ASMC si navyše môžeme určiť, v akom kódovanie je ASM súbor:

option wstring:on
option codepage:CP_UTF8
message    dw "čeština",0

V MASM od Microsoftu možno Unicode zadať len ťažko a to ako hexadecimálne hodnoty jednotlivých znakov:

message    dw 10dh,'e',161h,'t','i','n','a', 0

Všetky systémové funkcie pracujúce s Unicode textom majú na konci písmeno W, napr. MessageBoxW. Podobne funkcie pracujúce s 8-bitovým (ANSI) kódovaním majú na konci písmeno A, napr. MessageBoxA. V programoch sa zvyčajne suffix A nepíše, pretože ho tam automaticky doplní kompilátor. Ak chceme, aby kompilátor dopĺňal suffix W, do parametrov kompilátora pridáme /D UNICODE.

proc, endp

A máme tu hlavnú funkciu programu, podobne ako je tomu v mnohých vyšších programovacích jazykoch. Funkcia čiže procedúry sa zapisujú tak, že na začiatku funkcia je direktíva proc a na konci funkcia je endp:

main proc
        invoke MessageBox, 0, addr message, addr message, MB_OK
        ret
main endp
end main

Pred oboma direktívami je názov funkcie. Za proc môžu ešte nasledovať parametre funkcie.

Inštrukcia ret (return)

Na konci funkcia je inštrukcia ret.

Vo vyšších programovacích jazykoch sme si zvykli, že sa na konci funkcie nemusia písať return, ale v ASM nesmiete na ret zabudnúť.

invoke

Direktíva invoke vykoná funkciu so zadanými parametrami:

main proc
        invoke MessageBox, 0, addr message, addr message, MB_OK
        ret
main endp

Kompilátor ju preloží na inštrukciu call a parametre vloží na zásobník inštrukciami push. Operátor addr vracia adresu globálne alebo lokálne premenné.

Posledný parameter funkcie MessageBox určuje, aké budú pod textom tlačidla. Môžete skúsiť napríklad MB_YESNOCANCEL. Kód tlačidla, ktoré používateľ stlačil, potom bude v registri eax.

Registre sú už predzaložené "premennej" priamo v procesore a práca s nimi je rýchlejší, než keby sme pristupovali kvôli všetkému do pamäte RAM. Cez registre budeme pracovať napr. S množstvom parametrov a návratových hodnôt.

MessageBox je štandardné funkcie Windows API a pravdepodobne ste s ním už pracovali cez nejaký vyšší programovací jazyk.

end

Na konci každého ASM súboru musí byť direktíva end. Za ňou môže byť nepovinne názov hlavnej funkcie, ktorou sa aplikácia spúšťa:

end main

Hlavné funkciu možno zadať tiež v nastavení projektu - Linker -> Advanced -> Entry Point.

64-bitová aplikácia

Skúsme si teraz vytvoriť aplikáciu ako 64-bitovú:

        option win64:2
        include windows.inc
        .data
message db "Hello, world !",0
        .code
main proc
        invoke MessageBox, 0, addr message, addr message, MB_OK
        ret
main endp
end main

V 64-bitových aplikáciách nie je na začiatku programu typ procesora a model. Namiesto toho je tu direktíva option win64:2, ktorá určuje, ako sa majú kompilovať funkcie. Ak by sme na option zabudli alebo nastaviť hodnotu menšiu ako 2, potom by program spadol, pretože by zásobník nebol zarovnaný na násobok 16. Prečo tomu tak je si vysvetlíme v lekcii o funkciách.

Kompilátor MASM od Microsoftu nevie ani takto jednoduchý program skompilovať. Preto musíme používať kompilátor UASM.

Hello world v ASM - Konzolová aplikácia

Do tretice si vytvoríme 64-bitovú ukážku tej istej aplikácie, ktorá bude však text vypisovať do štandardnej konzoly:

        option win64:2
        include windows.inc
        .data
message db "Hello, world !"
        .code
main proc
        invoke GetStdHandle, STD_OUTPUT_HANDLE
        invoke WriteConsole, rax, addr message, sizeof message, NULL, NULL
        ret
main endp
end main

Handle

Keď chceme používať nejaký systémový objekt vo Windows, napr. Okno konzoly, musíme najskôr získať jeho handle. Funkcia GetStdHandle vracia handle na štandardný vstup alebo výstup. Návratová hodnota je vždy v registri rax.

Handle potom odovzdáme ako prvý parameter funkcie WriteConsole. Operátor sizeof zistí dĺžku textu. Všimnite si, že text nemusí končiť nulou ako u MessageBoxu. Štvrtý parameter je nepovinný, namiesto neho sme dali NULL. To je rovnaké ako 0, ale pre lepšiu čitateľnosť je lepšie používať NULL, aby bolo vidieť, že je to ukazovateľ (adresa).

V konzole sa používa OEM kódovanie (v strednej Európe je code page 852).

32-bitová verzia

32-bitová konzolová aplikácie by vyzerala podobne. Líši sa akurát prvým riadkom a namiesto registra rax by bol register eax. Keďže je 32-bitová architektúra zastaraná, tak nám na ňu v lekcii jeden príklad bohato stačí, prípadne ho nájdete k stiahnutiu v prílohe.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 36x (10.44 kB)
Aplikácia je vrátane zdrojových kódov v jazyku ASM

 

Predchádzajúci článok
Inštalácia kompilátora assembleri vo Windows
Všetky články v sekcii
Základy assembleri
Článok pre vás napísal Petr Laštovička
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje vývoji webových aplikací v ASP.NET a aplikací pro Windows v C++ nebo C#.
Aktivity