Асемблер за начинаещи

0
700

Благодаря на BG Development и специално на автора на статията, johnfound, че ни предоставиха тази статия

Съдържание:
Глава 0: Необходими инструменти за практическите упражнения.
Глава 1: Бройни системи
Глава 2 Потегляме…
Глава 3 Памет.
Глава 4 Програма в няколко реда…
Глава 5 Процесорът
Глава 6: Малки трикове
Съдържание:
Глава 0: Необходими инструменти за практическите упражнения.
Глава 1: Бройни системи
Глава 2 Потегляме…
Глава 3 Памет.
Глава 4 Програма в няколко реда…
Глава 5 Процесорът
Глава 6: Малки трикове

Глава 0: Необходими инструменти за практическите упражнения.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

За изпълнението на практическите упражнения, които ни предстоят
ще ни трябват някои основни инструменти, както и някои справочни материали.
В тази глава ще разгледаме намирането, инсталирането и настройката
на тези инструменти, така че да работят максимално удобно.

Системни изисквания към компютъра:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Всеки компютър с инсталиран Windows 95 или по-нов ще ви свърши работа.
За справочните файлове за Win32 ще ви трябват и 20..30Mbyte свободно
дисково пространство, но на първо време можем да минем и без тях.
Това е. Асемблерът е удивително непретенциозен към хардуера.

Ще ви трябват следните файлове:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1. “flat assembler 1.52 for Win32 GUI” – името на файла е fasmw152.zip
Евентуално, ако когато четете този текст, има по-нова версия, числото
може да се е сменило, това е без значение. Можете да изтеглите този файл от
http://flatassembler.net/download.php или ако не става, опитайте да отидете
на http://flatassembler.net/ и потърсете секцията “Downloads”.
Размера на файла е около 500..600Кbytes така че няма да имате проблеми със
свалянето.

2. “Win32 API Reference” – името на файла е win32hlp.zip
Можете да го свалите от http://flatassembler.net/docs.php
(секцията “Documentation”) на сайта. Файла е големичък 11Mbytes,
ако нямате възможност, не го сваляйте веднага, но по-нататък и
без друго ще ви потрябва. Ако имате инсталирани Delphi или VC
най-вероятно този файл го имате някъде по диска. Търсете Win32.hlp
Като го намерите, запомнете къде се намира – няма нужда да го копирате
другаде.

3. “OllyDbg 1.10” – име на файла odbg110.zip
Опитайте тази връзка: http://home.t-online.de/home/Ollydbg/odbg110.zip
Ако не се получава, ще трябва да отидете на http://home.t-online.de/home/Ollydbg/
и да потърсите в секцията “Download”. Размерът на файла е около 1Мbyte.
(Въпреки че OllyDbg е shareware програма, тя е БЕЗПЛАТНА и няма някакво
ограничение на функционалността. Ако решите да я ползвате постоянно по-нататък,
направете си труда да я регистрирате на горният сайт.)

За разопаковане на компресираните файлове, ще ви трябва и някакъв
ZIP инструмент, който най-вероятно вече имате. Ако не, потърсете си сами.
Лично аз харесвам UltimateZip v2 ( www.ultimatezip.com )

Инсталиране на “Win32 API Reference”
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Просто разопаковайте zip файла в директория по ваш избор. Както
казах, ако имате този файл някъде, не е нужно да го теглите и да
го инсталирате отделно. Ще ви трябва само мястото където се намира.

Инсталиране на FASMW
~~~~~~~~~~~~~~~~~~~~~

1. Разкомпресирайте файловете от fasmw152.zip в директория по ваше
желание, например “c:/fasmw/”. По нататък, когато давам директории в
директорията на fasm, ще ги записвам във вида: “c:/fasmw/some/path/”, където
трябва да заместите “c:/fasmw/” със директорията, която сте избрали.

2. Влезте в директорията c:/fasmw/, стартирайте fasmw.exe
Отидете в менюто на “Help|Pick help file”. В отвореният “open” прозорец,
намерете и изберете файла Win32.hlp за който говорехме по-горе. Ако сте
решили да не го инсталирате, просто затворете fasmw.

3. В директорията c:/fasmw/ ще се появи файл “fasmw.ini”.
Отворете този файл и добавете в него следният текст:

[Environment]
fasminc=c:/fasmw/include

Запишете го.

Това е. FASMW e инсталиран и готов за работа. Ако сте много
нетърпеливи можете да го стартирате отново и да заредите някои от
примерните програми от директорията “c:/fasmw/examples/” и да ги
компилирате и стартирате.
( F9 – компилира програмата и я стартира. Ctrl+F9 – само компилира )
Всичко би трябвало да върви ОК. С менютата трябва да се оправите лесно –
интерфейса е пределно семпъл.

Също, ако не виждате за пръв път асемблер, не вреди да погледнете и
файла “c:/fasmw/fasm.pdf” – това е документацията на асемблера.
Ако сте съвсем начинаещи, по-добре поизчакайте.

Забележка: FASMW не променя нищо от windows registry и не се регистрира
като програма във Windows. Така че ако ви се прииска да го махнете,
просто изтрийте директорията в която се намира.

Инсталиране на OllyDbg.
~~~~~~~~~~~~~~~~~~~~~~~

1. Разкомпресирайте файла OllyDbg110.zip в директория по ваш
избор. Например “c:/OllyDbg/”.
2. Настройка на OllyDbg като JIT (just-in-time) debugger
(това е дебъгера който ще поема програмите, когато са спрели с някаква
грешка, например GPF). Това не е задължителна процедура, но много помага,
при настройката на програми които гърмят. Вие си решавате. Процедурата е
следната:

– Стартирате OllyDbg
– От менюто избирате: “Options|Just-in-time debugging”
– В появилият се прозорец, натискате бутона:
“Make OllyDbg just-in-time debugger”

С това настройките на средата, която ще използваме за първите си
стъпки в асемблера за Windows е завършена. Разбира се с напредването на
знанията ви и сложността на програмите които пишете, ще се нуждаем и от
други инструменти, но ще ги въвеждаме в процеса на работа. Съдържание:
Глава 0: Необходими инструменти за практическите упражнения.
Глава 1: Бройни системи
Глава 2 Потегляме…
Глава 3 Памет.
Глава 4 Програма в няколко реда…
Глава 5 Процесорът
Глава 6: Малки трикове

Глава 1: Бройни системи

Числа и бройни системи:
~~~~~~~~~~~~~~~~~~~~~~~

В тази глава ще разгледаме въпроса за работата с различните бройни
системи – как се представят числата в десетична, двоична и шестнайсетична
бройна система, защо е важно да можем да работим с тези бройни системи и
дали това е чак пък толкова трудно, колкото някои хора си мислят.

Ако тази материя ви е позната от други езици за програмиране,
спокойно можете да прескочите тази глава и да преминете към следващата,
където ще напишем първата си програма на асемблер.

——————————————————————————

Начина по който записваме едно число се нарича бройна система.
Най-общо казано бройните системи се делят на две големи групи: позиционни
и непозиционни. Пример за непозиционна бройна система са т.н. римски числа:
IV e 4, XII e 12 и т.н. Всеки символ в такава бройна система има една и
съща стойност, където и да се намира в числото. Например “X” е винаги равно
на 10, “V” е винаги равно на 5 и т.н. Поради многото неудобства при запис
и изчисленията в такива бройни системи, те не се използват при
програмирането и няма да се занимаваме повече с тях.

При позиционните бройни системи (нормалната десетична система,
която се изучава в училище е точно такава) записването на всяко число
става с ограничен брой символи, наречени “цифри”. Броя на цифрите се нарича
основа на бройната система. При десетичната система, тези цифри са
десет на брой: “0”..”9″, при двоичната са само 2: “0” и “1” и т.н.

Реалната стойност на една цифра зависи от мястото в числото, където
тази цифра е записана. Например цифрата 2 в числото 5432 е равна на две, в
числото: 1121 има стойност двадесет а в числото 2345 – две хиляди.

Стойността на така записаното число е равна на сума от стойностите
на всяка от цифрите му (не забравяйте, че цифрите имат различна стойност в
зависимост от това, къде в числото са написани).
Например: 9999 = 9000 + 900 + 90 + 9

Как да определим стойността на цифрата, ако знаем мястото и в числото?
Просто. Мястото на цифрата се брои от дясно на ляво. Най-дясната цифра се
нарича “най-младша” и има номер 0. Брои се от дясно на ляво – все пак
този начин на записване е измислен от арабите. И така, за горният пример:

9999 = 9*10^3 + 9*10^2 + 9*10^1 + 9*10^0 ( 10^0 = 1 )

Стойността на всяка цифра е равна на цифрата по 10 на степен
мястото на цифрата в числото. Тук 10 разбира се не е изсмукано от пръстите
число, а е основата на бройната система.
Тази формула може да се обобщи за произволна бройна система и за
произволна дължина на числото. Ако имаме числото abcdе записано в система
с основа N ще имаме:

abcde = a*N^4 + b*N^3 + c*N^2 + d*N^1 + e*N^0

Най-малката основа на бройна система може да бъде числото 2.

Компютри и бройни системи:
~~~~~~~~~~~~~~~~~~~~~~~~~~

По чисто технически причини, информацията в паметта на компютрите
се записва именно в двоична система: тоест, “няма напрежение” – отговаря на
цифрата 0 а “има напрежение” отговаря на цифрата 1.
Една двоична цифра се нарича “бит” (bit). Това всъщност е най-малкото
количество информация, която може въобще да съществува (тъй като най-малката
основа за бройна система е 2, а един бит е едноцифрено число, записано в
двоична система).
Тъй като един бит позволява да се записват много малко стойности,
най-малката клетка от паметта на компютрите съдържа по 8 бита и се нарича
“байт” (byte). Във всеки байт могат да се записват числа от 00000000b до
11111111b – суфикса “b” означава че числото е двоично (binary).
Да видим на какво отговарят тези двоични числа в по-привичната
десетична система, като използваме дадената по-горе формула за изчисляване
на стойността на числата:

11111111b = 1*2^7+1*2^6+1*2^5+1*2^4+1*2^3+1*2^2+1*2^1+1*2^0 = 255

Тоест в един байт могат да се записват числа със стойност от 0 до 255.

Сега е момента да се научите да правите подобни преобразования на ум, или
поне без калкулатор.
Това е по-лесно отколкото си мислите. Погледнете следващата таблица:

0 | 1
1 | 2
2 | 4
3 | 8
4 | 16
5 | 32
6 | 64
7 | 128

Отляво е съответната позиция в байта, а отдясно е степента на двойката,
която отговаря на тази позиция. С тази табличка (която скоро ще научите
наизуст) пред себе си, можете бързо да преобразувате всяко двоично число
в десетична система. Просто поглеждате числото и там където има 1-ца,
сумирате съответната степен на двойката, където има 0 – нищо не сумирате.

Например:

10011011 = 1 + 2 + 8 + 16 + 128 = 155
<------- Сумирате от дясно на ляво.За упражнение се опитайте да пресметнете стойността на следните двоични
числа:

Задача 1:
10101010b = ?
01010101b = ?
11001100b = ?
00110011b = ?

Отговорите ще намерите в края на главата.

Разбира се винаги можете да използвате калкулатора на Windows за да
правите подобни преобразования, но известни упражнения ще ви дадат
разбирането, което няма как по друг начин да получите.

Както видяхме, в един байт могат да се запишат числа от
0 до 255. В много случаи това не е достатъчно и затова се използват по
няколко последователни байта за записване на едно число. Например 2 байта
са т.н. “дума” (word) = 16bit. 4byte = “двойна дума” (double word) или
(dword) = 32bit.

Въпреки простотата си, двоичната система има един голям практически
недостатък. Числата записани в двоична система имат твърде много цифри.
Работата с такива многоцифрени числа просто не е удобно от човешка гледна
точка. Затова в практиката се е наложила шестнайсетичната бройна система.
Тоест с основа 16. За записване на числа в шестнайсетична система ни трябват
съответно 16 цифри. Тъй като арабските цифри са само 10 е прието за останалите
цифри да се използват латинските букви от “а” до “f”.
Тъй като и 2 и 16 са точни степени на 2, то всяка шестнайсетична цифра е
точно равна на някое 4 цифрено двоично число (4 цифрените двоични числа са
точно 16 – от 0000b до 1111b )
Ето и таблицата със стойностите на шестнайсетичните цифри и съответствието
им с двоичните тетради (4 цифрени числа)

+—————-+
| 16 | 2 | 10 |
+—————-+
| 0 | 0000 | 0 |
| 1 | 0001 | 1 |
| 2 | 0010 | 2 |
| 3 | 0011 | 3 |
| 4 | 0100 | 4 |
| 5 | 0101 | 5 |
| 6 | 0110 | 6 |
| 7 | 0111 | 7 |
| 8 | 1000 | 8 |
| 9 | 1001 | 9 |
| a | 1010 | 10 |
| b | 1011 | 11 |
| c | 1100 | 12 |
| d | 1101 | 13 |
| e | 1110 | 14 |
| f | 1111 | 15 |
+—-+——+—-+

Тази таблица е много важна. Най-добре си я разпечатайте или си я напишете
на листче, ще я ползвате постоянно. (Е, много скоро няма да ви трябва
тъй като ще я знаете наизуст.)

За обозначаване на шестнайсетичните числа в програмирането се използват няколко
начина:
1. Суфикс “h” – 29ch
2. Префикс “$” – $29c
3. Префикс “0x” – 0x29c

В повечето случаи в тази книга ще използвам метода 2.

Как се преобразуват двоични числа в шестнайсетични и обратно?
Елементарно.

2 -> 16

Да кажем че имаме някакво двоично число:

1010011100b

1. Разделяме го на групи от по 4 бита от дясно наляво. Ако броят на цифрите
не е кратен на 4 – добавяме нули в най-дясната група.
Тоест: 10 1001 1100b -> 0010 1001 1100b
2. Заместваме всяка група със съответната шестнайсетична цифра от таблицата:

0010 1001 1100 -> $29c

16 -> 2
Сигурно вече се досещате, че обратното преобразуване от шестнайсетична
система в двоична е също проста операция. Просто заместваме всяка
шестнайсетична цифра с двоичният и еквивалент от табличката горе:

$ab25 = 1010 1011 0010 0101b

Как се брои в различните бройни системи:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

“По еднакъв начин” е краткият отговор. За да стане ясно да си
припомним как броим в десетична система.
1. Увеличаваме най-младшата цифра с 1.
2. Ако полученият резултат е по-голям от десет – цифрата “превърта” в
нула, а ние провеждаме същата операция на следващата цифра.

Това по всъщност е начинът на броене във всяка бройна система.
Просто превъртането става не на 10 а на съответната основа на бройната
система. При двоичната система на 2, при шестнайсетичната на 16 и т.н.

Броене в двоична система:
000
001
010 -> тук цифрата превърта и увеличаваме следващата
011
100 -> тук също, но следващата също превърта и увеличаваме третата.
101
110 -> пак превърта
111
000 -> тук превъртат и трите цифри и трябва да въведем четвърта, но
надявам се този пример е достатъчен.

Броене в шестнайсетична система:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, 10 (превъртане), 11, 12
13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, 20 (отново), 21 …
… fa, fb, fc, fd, fe, ff, 100 (отново – тук числото става трицифрено)

Преобразуване от десетична в двоична и шестнайсетична система.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

За съжаление, 10 не е степен на двойката и затова преобразуването
от десетична в двоична или шестнайсетична система не е толкова проста
операция.
Най-общо алгоритъмът е следният: “Делим числото на основата на
бройната система и записваме остатъците в обратен ред.”
Знам че не е ясно но с един пример ще стане:

Да преобразуваме числото 123 в двоична система:

123 : 2 = 61 : 2 = 30 : 2 = 15 : 2 = 7 : 2 = 3 : 2 = 1 : 2 = 0
— — — — — — —
1 1 0 1 1 1 1 <- остатъци
<-------- Записваме ги в обратен ред.Сега записваме остатъците в обратен ред: 1111011b
Да проверим: 1+2+8+16+32+64 = 123

Всъщност, тъй като деленето на 2 е лесно дори на ум, с малко
упражнения и ако числата не са твърде големи, горната операция не е
чак толкова трудна.
А като имаме двоичното представяне, лесно можем да го превърнем в
шестнайсетично:

123 = 0111 1011b = $7b

Упражнение:

Задача 2: Преобразувайте дадените числа от десетична в двоична и
шестнайсетична система. Направете обратно преобразуване в десетична
за да проверите резултата:

423, 321, 111, 255, 128, 192, 65535, 65536

Отговори на задача1: 170, 85, 204, 51

Отговори на задача2:
423 = 110100111b = $1а7
321 = 101000001b = $141
111 = 1101111b = $6f
255 = 11111111b = $ff
128 = 10000000b = $80
192 = 11000000b = $c0
65535 = 1111111111111111b = $ffff
65536 = 10000000000000000b = $10000 Съдържание:
Глава 0: Необходими инструменти за практическите упражнения.
Глава 1: Бройни системи
Глава 2 Потегляме…
Глава 3 Памет.
Глава 4 Програма в няколко реда…
Глава 5 Процесорът
Глава 6: Малки трикове

Глава 2: Потегляме

В тази глава ще напишем първата си Windows програма на асемблер. Ще я стартираме, за да почувстваме удоволствието и да си качим настроението. След това ще я анализираме ред по ред, за да разберем какво точно прави.

1. Написване
Стартирайте FASMW (сполучливо инсталиран от вас, според указанията в Глава 0) и в редактора въведете (или пък копирайте) следните редове:

Код:


format PE GUI 

MB_OK              = 00h 
MB_ICONEXCLAMATION = 30h 

        push    MB_OK + MB_ICONEXCLAMATION 
        push    _caption 
        push    _message 
        push    0 
        call    [MessageBox] 

        push    0 
        call    [ExitProcess] 

_caption db 'Win32 Assembly Programming',0 
_message db 'I feel the power.',0 

data import 
             dd  0, 0, 0, RVA kernel_name, RVA kernel_table 
             dd  0, 0, 0, RVA user_name, RVA user_table 
             dd  0, 0, 0, 0, 0 

kernel_table: 
ExitProcess  dd  RVA _ExitProcess 
             dd  0 

user_table: 
MessageBox   dd  RVA _MessageBoxA 
             dd  0 

kernel_name  db  'KERNEL32.DLL',0 
user_name    db  'USER32.DLL',0 

_ExitProcess dw  0 
             db  'ExitProcess',0 

_MessageBoxA dw  0 
             db  'MessageBoxA',0 
end data 

Запишете файла някъде под името например “hello.asm”. Натиснете Ctrl+F9 за да компилирате програмата. Ако всичко е въведено правилно, FASM ще ви даде прозорче с кратка информация за компилацията. Обърнете внимание на размера – 1024 байта.
Ако се появи грешка, FASM ще позиционира курсора на реда на грешката и ще спре компилацията.
Ако стане така, вероятно имате грешки при въвеждането на програмата. Сравнете реда където е грешката с оригиналният от сорса по-горе.
Сега натистете F9 (или изберете от менюто Run|Run). Програмата се стартира и показва диалогов прозорец с бутон ОК и съобщението: “I feel the power.” А вие почувствахте ли я?
При натискане на бутона, програмата спира.

Сега да анализираме текста по-подробно:

1:

Код:


format PE GUI

Това е директивата на асемблера “format” която указва в какъв формат искаме да получим изпълнимият файл. “PE GUI” са параметрите, които указват, че искаме EXE файл за Win32 (PE означава “portable executable”)
Едно от хубавите неща на FASM е че може да създава директно изпълними файлове.

2:

Код:


MB_OK              = 00h 
MB_ICONEXCLAMATION = 30h

В тези два реда създаваме две числови константи с имена MB_OK и MB_ICONEXCLAMATION. Това се прави с цел да направим текста на програмата по-четлив.
Навсякъде по-нататък, където се използват тези константи с имената им, ще бъдат заместени със стойностите си по време на компилацията.
Дефинирането на такива константи и използването им при писане на големи програми е изключително важно за получаването на добре четим и лесен за поддържане код. В документацията на Win32 API, широко се използват такива константи при задаване на различни аргументи при викането на функциите. За съжаление, в документацията на Майкрософт, тези константи са дадени само с имената си, а числените им стойности са дефинирани в хедър файловете на стандартните библиотеки на езика C на Майкрософт.
Точно това беше и основната пречка пред първите пионери, започнали да пишат на асемблер за Windows. За наше щастие, тези проблеми са вече решени и тези константи са отдавна преведени на асемблер, така че ние просто можем да ги използваме. В пакета FASMW има файлове с такива дефиниции, където са дефинирани хиляди константи и структури за данни, готови за използване.
В следващите глави ние ще ползваме широко тези дефиниции.

3:

Код:


push    MB_OK + MB_ICONEXCLAMATION 
        push    _caption 
        push    _message 
        push    0 
        call    [MessageBox]

Тук стартираме функция от Windows API, която показва модален прозорец със съобщение (т.н. message box) и чака натискането на бутон. Тук за първи път се срещаме с две инструкции на процесора:

push – вкарва стойност в стека. В случая няколко константи. Това е стандартният начин за предаване на параметри към фунциите на Windows. Стекът е, структура от типа LIFO – last-in-first-out. Инструкцията push вкарва стойност на върха на стека, а инструкцията pop – изважда последната вкарана стойност. Стека е важна структура в работата на процесора и в следващата глава ще я разгледаме по-подробно. За сега е достатъчно да знаете, че аргументите към функциите на Windows се предават, като се вкарват в стека, откъдето функцията си ги взима.

call – стартира подпрограма (функция). След изпълнението на подпрограмата,
програмата продължава да се изпълнява на следващата инструкция след call.

За да видите описанието на функцията MessageBox, позиционирайте текстовата каретка в редактора на думата “MessageBox” и натиснете F1. Обърнете внимание че реда в който се вкарват параметрите в стека е обратен на реда в който са описани параметрите във функцията на C.

Това е особенност на т.н. конвенция за викане “stdcall”. Разбира се, да се пише викане на функция на 5 реда, когато в C това се пише на един ред, не е особенно удобно и четливо в контекста на голям проект, когато такива стартирания на функции са хиляди. Но затова пък прави механизма на работа пределно ясен – което е и целта на това упражнение.
По нататък ще разгледаме и по кратки начини за правене на тези неща.

4:

Код:


push    0 
        call    [ExitProcess]

Отново извикване на функция на Windows – този път с един параметър. Функцията ExitProcess спира изпълнението на програмата.
За упражнение, разгледайте описанието на функцията в API документацията, както направихме в предишният случай за MessageBox.

5:

Код:


_caption db 'Win32 Assembly Programming',0 
_message db 'I feel the power.',0

Това е дефиниция на текста, който искаме да се показва в прозореца. _caption и _message са т.н. етикети. По същество това са също константи, които имат за стойност мястото (точният термин е “адресът”) където се намират някакви данни или инструкции в паметта. Тъй като това място зависи от предишните инструкции и данни, то не се знае предварително. Затова се задава и използва с някакво име, а компилаторът, по време на компилацията изчислява точната му стойност.
Както можете да видите в точка 3: програмата използва тези етикети, за да каже на функцията MessageBox къде се намира текстът, който искаме да се покаже.
Директивата db (define byte) служи за дефиниране на данни. Като параметри на тази директива могат да се задават, текст заграден в единична или двойна кавичка или
числа -128..255 отделени със запетайки. Как точно текста се представя в паметта на машината (компютърът работи само с числа) ще разгледаме подробно в следващите теоретични глави.

6:

Код:


data import 
             dd  0, 0, 0, RVA kernel_name, RVA kernel_table 
             dd  0, 0, 0, RVA user_name, RVA user_table 
             dd  0, 0, 0, 0, 0 

kernel_table: 
ExitProcess  dd  RVA _ExitProcess 
             dd  0 
user_table: 
MessageBox   dd  RVA _MessageBoxA 
             dd  0 

kernel_name  db  'KERNEL32.DLL',0 
user_name    db  'USER32.DLL',0 

_ExitProcess dw  0 
             db  'ExitProcess',0 

_MessageBoxA dw  0 
             db  'MessageBoxA',0 
end data

И най-накрая ще разгледаме набързо т.н. импорт таблица. Тази таблица с данни е свързана със структурата на PE exe файла във Windows и с начина по който Windows предоставя на програмите достъп до функциите от API.
Всички API функции на Windows се съдържат в различните системни .dll библиотеки. Например “kernel32.dll”, “user32.dll”, “comctrl32.dll” и много други.
Windows не предоставя на програмите всички тези функции (те са хиляди) едновременно, а само тези функции, които програмата си поиска.
В импорт таблицата се описват именно от кои .dll файлове и кои именно функции, програмата иска да получи за използване (да импортира).
По време на зареждането на програмата в паметта, Windows прочита тази таблица и попълва в съответните полета адресите на функциите, които са му поискани.
Програмата след това използва тези полета за да стартира функциите, когато и трябват.
Дефинирането на импорт таблица, по начина по който е направено в този пример е трудна работа, особенно ако импортираме десетки и стотици функции. В стандартната библиотека на FASM са предвидени файлове, които улесняват и автоматизират тази работа, като импортират само функциите, които са използвани в програмата. И отново – с тези библиотеки ще се запознаем малко по-нататък.
Съдържание:
Глава 0: Необходими инструменти за практическите упражнения.
Глава 1: Бройни системи
Глава 2 Потегляме…
Глава 3 Памет.
Глава 4 Програма в няколко реда…
Глава 5 Процесорът
Глава 6: Малки трикове

Глава 3. Памет

Може да се каже, че паметта е най-важната част от компютъра. Тя е която прави възможно съществуването на програми и осмисля съществуването на процесора и целият компютър.

Какво представлява паметта? Най-общо казано, това е устройство, което може да запомня числа и след това да ги възпроизвежда при поискване. Образно можете да си го представите като множество кутийки (клетки) във всяка от които е поставено по едно число. Числата във всяка клетка могат да сменят стойността си, но не може да изчезнат – във всяка клетка винаги има число.

Паметта в съвременните компютри е организирана на байтове – тоест във всяка клетка може да се постави по едно 8 цифрено двоично число (или 2 цифрено шестнайсетично, или изобщо, число от 0 до 255). Надявам се, си спомняте Глава 1 в която се говореше за числа и бройни системи.

Клетките от паметта трябва да могат да се различават една от друга, за да може процесорът да избира точно от коя да прочете числото което му трябва. Затова клетките от паметта са номерирани. Всяка клетка си има т.н. нареченият “адрес” – това е просто номера на клетката по която тя се различава от всички други.

Когато програмата иска да работи с паметта, тя трябва да укаже
от кой адрес иска да прочете или да запише число. Ще повторя, защото е важно: В паметта ВИНАГИ има нещо записано. Често чувам
начинаещи програмисти да говорят за “празни” клетки от паметта. Питали са ме: “Ами ако няма нищо записано в паметта?” Такова нещо принципно не може да съществува – в паметта винаги има някаква записана стойност. Друг е въпросът дали тази стойност има някакъв смисъл за нас или не.

Запис на големи числа в паметта:

Терминология: Да уточним още веднъж терминологията. “Младша” се нарича цифра (или групи цифри) които се намират по-надясно в числото. “старши” – цифри, които се намират по-наляво в числото. Очевидно е, че понятието е относително. Цифра, която се намира в средата, например, ще бъде по-младша от цифрите, които са по-наляво от нея и по-старша от цифрите които са по-надясно от нея. Колкото по-наляво се намира една цифра (група цифри) – толкова по-старша е тя. Най-лявата цифра се нарича най-старша. В английската литература се използва термина “LSB” за най-младшият байт. Това е съкращение от “least significant byte” – най-малко значещ байт. “MSB” е съкращение от “most significant byte” – най-значещ байт.

Например в числото (двойна дума): $12345678 имаме:

$78 – “LSB” – най-младши байт.
$12 – “MSB” – най-старши байт.
$34 е по-старши байт от $56, а 7 е по-младша цифра от 2.

Надявам се, това би трябвало да изясни терминологията, ако досега не е била ясна.

Както казахме по-горе, в една клетка от паметта се записва един байт. А знаем, че в един байт може да се запише число от 0 до 255.

Програмите обаче трябва да могат да работят и с по-големи числа. Как такива числа се записват в паметта?

Разбира се в няколко байта. Мястото на тези байтове може да е произволно, но като правило големите числа винаги се записват в няколко ПОСЛЕДОВАТЕЛНИ байта. Как точно става това. Нека имаме числото $1234. Това число е четири цифрено (шестнайсетично) и затова се записва в 2 байта (във всеки байт може да се запише до 2 цифрено шестнайсетично число). Ако искаме да запишем това число, да кажем на адреси $1000 и $1001, то тези адреси ще съдържат:

$1000: $34
$1001: $12

Тоест – младшият байт на числото се записва на по-малкият адрес, а старшият байт на по-големият байт от двойката адреси.

(Този метод е възприет от повечето производители на процесори в наше време. Но например в процесорите на Motorola се използва обратният запис – първо старшата и след това младшата част.)

Ако числото трябва да се запише в повече байтове – например 4 (двойна дума) правилото е същото: Първо по-младшите, след това по-старшите байтове.

Ако числото е например: $12345678 – то ще се запише така:

$1000: $78
$1001: $56
$1002: $34
$1003: $12

За по-кратко, горният запис може да се направи така:

$1000: $78, $56, $34, $12

Група от няколко байта в паметта която съдържа едно число, често се нарича променлива. Обикновенно в компютърното програмиране се използват променливи, съставени от брой байтове, който е степен на двойката. Тоест 1, 2, 4, 8, 16 и т.н. байта. Ако групата е по-голяма отколкото трябва, за да се запише даденото число, то числото се попълва с нули от ляво (естествено – само тогава не се променя стойността на числото).

Например, за съхраняване то числото $abcde трябват 2 байта и половина. (2 цифри на байт…) – за да го съхраним по най-естествен за компютъра начин трябва да изберем 4 байтова променлива и в нея да запишем $000abcde по следният начин:

0000: $de, $bc, $0a, $00

Запис на текст в паметта:

Програмите обикновенно работят много с текстова информация. Това е и текста който се показва на екрана, и текста, който се чете от файлове и се записва в бази данни (и даже този текст, който четете в момента.)

Текстовата информация се записва в паметта във формата на числа. Че как иначе, щом в паметта могат да се записват само числа?

Идеята е следната: На всеки символ, който искаме да използваме, трябва да присвоим някакъв номер. Когато записваме текст в паметта, за всеки символ от текста ще записваме неговият номер. След това, програмата, която отговаря за извеждането на текста на екрана е длъжна да превърне всяко число в съответното (правилно) графично изображение на знака.

Тази операция е толкова рутинна, че винаги е част от операционната система, а понякога се реализира дори и хардуерно.
Начинът на кодиране на символите с числа е стандартизиран и в момента се използват общо взето два стандарта. Първият е т.н. ASCII код.

ASCII е съкращение от American Standard Code for Interchange Information – Американски стандартен код за обмен на информация.

При ASCII кодировката, всеки символ се кодира в един байт. Знаем, че в един байт могат да се кодират максимум 256 символа (0..$ff). Първата половина на този обхват (0..127 ($7f) ) е запълнена със стандартните символи (!@#$%№…), цифри (123455…) и буквите от латинската азбука. Тази половина от 128 символа е напълно стандартизирана. Втората половина е оставена за използване от различните национални азбуки – кирилица, разните букви с точки отгоре (умлаути) за немският например, гръцки букви и т.н. Всяка държава има право да си стандартизира своя втора част на ASCII таблицата.

Проблема възниква, когато поискаме да напишем текст в който да се използват букви от няколко езика или просто искаме да напишем програма, която да може да работи с документи на разни езици. Просто ASCII таблицата е много малка и не може да събере повече от латиница и един допълнителен език…

Затова, напоследък започва да се използва широко т.н. UNICODE. Идеята при уникода е същата, само че за всеки символ се отделя не един байт, а една дума (два байта). В една дума могат да се кодират 65536 символа (0..$ffff) – повече отколкото са нужни за кодирането на всички символи от всички азбуки на света, както и много допълнителни графични символи.

Съдържанието на таблицата UNICODE се стандартизира от “The Unicode Consortium” – oрганизация, която се грижи за дефинирането на таблицата – тоест на кои символи, кои номера да се присвоят. Можете да прочетете подробно за UNICODE на: http://www.unicode.org/

Недостатък на UNICODE е, че таблицата е много голяма – изключително трудно е да се обработва напълно – обикновенно се работи с малка част от нея, която ни засяга в момента.

По-нататък в тези уроци ще работим само с ASCII кодировка. Работата с UNICODE, определено не е за начинаещи.

Ето и кратко описание на ASCII таблицата, която се използва за кодиране на кирилица във Windows:

Код:


 $00 .. $1f:   Управляващи  символи  за  принтери  и  конзоли.
Тези символи не се виждат на екрана, а служат
за командване на поведението на принтери, както
и на начинът по който се форматира текста,
когато се показва на екрана. В частност тук са
символите за нов ред: CR ( $0d ) и за нова
линия: LF( $0а ). Във Windows всяко преминаване
на нов ред се кодира с двата символа един след
друг: $0d, $0a (13, 10). Причината за това е
историческа и се дължи на факта, че принтерите
при получаване на CR ($0d), преместват печатащата
глава крайно в ляво без да преместят хартията, а
при получаване на LF ($0а) преместват хартията с
един ред, без да местят главата. Тоест ако искаме
да отидем в началото на следващият ред, трябва да
изпратим последователно двата символа. Тук няма
да разглеждаме останалите символи от тази група.

$20 - Интервал " "
$21..$2f: ! " # $ % & ' ( ) * + , - . /
$30..$3f: 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
$40..$4f: @ A B C D E F G H I J K L M N O
$50..$5F: P Q R S T U V W X Y Z [ ] ^ _
$60..$6F: ` a b c d e f g h i j k l m n o
$70..$7e: p q r s t u v w x y z { | } ~ 

Следващата част на таблицата съдържа националните символи.
Аз я виждам като кирилица, но вие може да я виждате и по друг начин,
в зависимост от кодировката на браузърът ви и от шрифта, който се използва.

$80..$8f: Ђ Ѓ ‚ ѓ „ … † ‡ ? ‰ Љ ‹ Њ Ќ Ћ Џ
$90..$9f: ђ ‘ ’ “ ” • – — ? ™ љ › њ ќ ћ џ
$a0..�

ДОБАВИ КОМЕНТАР

Коментирай това преди всички други

Извести ме за
avatar
wpDiscuz