CZĘŚĆ 1. - PODSTAWY
Autor nie ponosi odpowiedzialności za jakiekolwiek szkody wynikłe z niewłaściwego użytkowania zarówno Macro Assemblera, jak i programów przykładowych użytych w tym kursie. Został on napisany wyłącznie w celach edukacyjnych. Mam nadzieje, że "kursanci" wybaczą mi również brak polskich liter w niektórych wyrazach (nie jestem w stanie sprawdzić wszystkiego, a bez "krzaczków" piszę z przyzwyczajenia).
1. Co jest potrzebne?
- Macro Assembler pod Windows (dowolna wersja) - można znaleźć w sieci (nie podaję adresu, bo go nie pamiętam - ja znalazłem na jakiejś stronie crackerskiej)
- Mózg ;-)
- Windows - najlepiej 98
- Pojęcie o binarnym (dwójkowym) i szesnastkowym systemie liczbowym
- WINAPI.HLP - niedługo będzie można pobrać z mojej strony - http://www.antywin.ko.pl ale jest również dołączany np. do Delphi i nazywa się "Win32 Programmer's Reference"
- Jakiś edytor tekstu (może być ten co jest razem z MASM)
2. Co się może przydać?
- Disasembler/Debugger (na pewno jest na stronach crackerskich, polecam SoftIce)
- Znajomość podstaw ASM
3. Dlaczego warto pisać programy w Asemblerze?
- Są szybkie
- Mało zajmują po kompilacji
- Można w nich zrealizować to co w językach wyższego rzędu byłoby niemożliwe lub bardzo utrudnione
- Nie wymagają wiele pamięci i innych zasobów (procesor)
4. Rejestry procesora
Każdy 32-bitowy procesor posiada rejestry 32-bitowe (czyli 4-bajtowe), które dzielą się na 16-bitowe, a te z kolei na 8-bitowe (aczkolwiek nie wszystkie).
4.1 Rejestry danych (ogólnego przeznaczenia)
Zasadniczo rejestry danych są 4 (EAX, EBX, ECX, EDX), ale jak pisałem wyżej - można je jeszcze podzielić. Poniższy rysunek zawiera nazwy wszystkich rejestrów danych:

Czyli - jak widać - mniej znacząca połowa rejestru EAX nosi nazwę AX, a AX dzieli się na AH i AL. Podobnie jest z rejestrami EBX, ECX i EDX. W rejestrach ośmiobitowych można zapisać liczby od 0 do 255, 16-bitowych - od 0 do 65535, a w 32-bitowych - od 0 do 4294967295 (ale naprawdę nie ma sensu tego pamiętać).
Mogą się za to przydać nazwy rejestrów:
- AX - accumulator (akumulator)
- BX - base (bazowy)
- CX - count (licznik)
- DX - data (danych)
4.2 Rejestry indeksowe
Czyli EDI i ESI (dzielą się jeszcze na DI i SI, podobnie jak przy rejestrach danych) stosuje się przy operacjach na ciągach oraz jako rejestry ogólnego przeznaczenia. Warto zaznaczyć, że Windows używa ich do swoich (niecnych ;-)) celów i zmiania ich zawartości _może_ spowodować (znany wszystkim skądinąd) komunikat "Program wykonał...". Dlatego też po każdej modyfikacji tych rejestrów należy przywrócić ich poprzednią wartość.
4.3 Niektóre rejestry zostały celowo pominięte w tym opisie, ze względu na rzadkość ich stosowania w aplikacjach Windows.
5 Pierwszy program (!)
Jak to zwykle w tego typu kursach bywa, zaczniemy od programu wyświetlającego prosty komunikat tekstowy.
5.1 Struktura programu
W każdym programie napisanym w Macro Assemblerze powtarzają się te same fragmenty kodu (nazwane tutaj przeze mnie "strukturą"). Tak więc pisanie programu zaczynamy od następującego fragmentu (ktory musi występować w prawie każdej aplikacji):
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
;deklaracje bibliotek, które chcemy uzyć
.const
;stałe
.data
;zmienne, które są dane od początku działania programu
.data?
;zmienne niezdefiniowane (podajemy tylko typ zmiennej)
.code
start:
;kod assemblerowy
end start
W pierwszej linii okreslamy zestaw instrukcji ktore chcemy uzyc. 386 jest najbardziej popularny i daje gwarancje ze napisane programy będą "chodzić" na każdym komputerze z Windows. Zamiast 386 moze byc 486, 586 itp.
5.2
Aby wyświetlić komunikat, musimy wiedzieć która funkcja API jest za to odpowiedzialna i w jakiej bibliotece się znajduje. Te informacje można wziąc z WINAPI.HLP. Ponieważ jest to pierwsza częśc kursu, oszczedze wam szukania - ta funkcja to MessageBox. W opisie do niej (winapi.hlp) czytamy:
| HWND hWnd, |
handle of owner window |
| LPCTSTR lpText, |
address of text in message box |
| LPCTSTR lpCaption, |
address of title of message box |
| UINT uType |
style of message box |
Są to parametry, które należy podać przy wywoływaniu tej funkcji.
hWnd to tzw. "uchwyt okna", który pozwala Windzie określić o które okno nam chodzi. My jeszcze nie mamy okna, więc będziemy musieli wpisać tam 0 (czyli NULL).
lpText to adres w pamięci do tekstu, który ma się wyświetlić w okienku.
lpCaption to adres w pamięci do tekstu, który ma się wyświetlić w polu tytułowym okienka z komunikatem.
uType zawiera informację o tym, jakie przyciski i ikona mają się znaleźć w oknie.
A więc tworzymy plik tekstowy z rozszerzeniem ASM i wpisujemy do niego strukture programu (powyżej).
Funkcje, których bedziemy używać znajdują się w bibliotekach kernel32 i user32, musimy wiec je zadeklarować, aby kompilator wiedział skąd wziąć informacje o nich:
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
W sekcji data definiujemy zmienne zawierające to, co chcemy wyświetlić w okienku, np.
msgTytul db "MASM Tutorial",0
msgTekst db "Assembler jest wspaniały!",0
Pare słów wyjaśnienia: msgTytul i msgTekst to nazwy zmiennych, db oznacza typ zmiennej (w tym przypadku byte), natomiast 0 musi konczyc kazdą zmienną tekstową.
W miejscu gdzie ma byc kod należałoby wywołać funkcję MessageBox. Macro Assembler posiada specjalną instrukcję służącą do wywoływania funkcji API - invoke. Parametry do funkcji podaje się wtedy po przecinku. Piszemy więc:
invoke MessageBox,0,addr msgTekst,addr msgTytul,MB_OK
"Addr" przed nazwami zmiennych jest potrzebny po to, by kompilator zamienił to na adres, a nie na zawartość zmiennej. Skąd wiemy, że jako parametr należy podać adres? Z winapi.hlp (patrz wyżej - jest tam napisane np. address of text in message box).
Aby program działał prawidłowo, należy go zakończyć. Służy do tego funkcja ExitProcess. Dopisujemy więc jeszcze fragment:
invoke ExitProcess,0
Sekcje ".const" oraz ".data?" są puste więc można je spokojnie usunąć. Cały kod wygląda więc tak:
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data
msgTytul db "MASM Tutorial",0
msgTekst db "Assembler jest wspaniały!",0
.code
start:
invoke MessageBox,0,addr msgTekst,addr msgTytul,MB_OK
invoke ExitProcess,0
end start
Po kompilacji programik ten wyświetli okienko z komunikatem i przyciskiem OK. Jeżeli chcemy, aby zamiast OK były inne przyciski, wpisujemy odpowiednie wartości zamiast MB_OK:
- MB_ABORTRETRYIGNORE - przerwij, ponów próbę, zignoruj
- MB_OKCANCEL - ok, anuluj
- MB_RETRYCANCEL - ponów próbę, anuluj
- MB_YESNO - tak, nie
- MB_YESNOCANCEL - tak, nie, anuluj
Gdy chcemy dodatkowo wyswietlic ikonkę, dopisujemy do kodu przycisku "or" oraz odpowiednią wartość z listy poniżej.
- MB_ICONEXCLAMATION
MB_ICONWARNING - wykrzyknik
- MB_ICONINFORMATION
MB_ICONASTERISK - mała litera "i" w niebieskim kółku
- MB_ICONQUESTION - znak zapytania
- MB_ICONSTOP
MB_ICONERROR
MB_ICONHAND - krzyżyk
Przykład - wyświetlanie komunikatu z przyciskami przerwij, ponów próbę, zignoruj i znakiem zapytania:
invoke MessageBox, 0, addr msgTekst, addr msgTytul, MB_ABORTRETRYIGNORE or MB_ICONQUESTION
Okienku z komunikatem można nadać jeszcze kilka innych właściwości. Zainteresowanych odsyłam do winapi.hlp. Przykładowy program do tej części kursu znajduje się tutaj.
Arkadiusz "Czajnick" Robiński
arekrob@kki.net.pl
http://www.antywin.ko.pl