събота, 14 март 2009 г.

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




Здравейте аз сам нов в програмирането но реших да започна да пиша уроци на български за това което сам научил/уча, това помага и на мен и се надявам че ще помогне и на други. :)  
Този Урок е в начален стадий просто написах една малка въвеждаща част за да видя дали има интерес кам такива уроци и ако има да продалжа да го пиша (ако има интерес не сам си поставил крайна точка кога да спра... ).

Това е парвият урок който пиша за каквото и да е такаче ако има някакви тематични грешки докладвайте за да ги оправя (няма нужда да ми казвате за правописните грешки :D ) 
   
Ассемблера е език за програмиране от ниско ниво.Той е специфичен за определеният модел процесор.
Трудно е да се програмира на ассемблер защото в него има много малко и прости инструкций с които можем да боравим
и трудното е това как да саберем всички малки и прости парченца за да се получи нещо сложно.
Писането на ассемблер е много трудуемко но пак за сметка на това ассемблера ти дава много по голям контрол върхо машината отколкото 
езиците от високо ниво като C\C++,Pascal,Java,Python,Perl. Ассемблера се ползва там кадето трябва оптимизация (ассемблер може да се вгражда в Delphi,C\C++,Visual Basic и други).
Ассемблер се използва за саздаване на драйвери,ассемблер се използва за саздаване на вируси, използвасе за писане на експлойти, ассемблера е главното оръжие на кракерите.

За да овладеете ассемблер а и който и да е друг език за програмиране трябва да знаете как се представят данните в компютара.
Компютара не е нищо повече от един много мощен калкулатор който може само да сабира и изважда числа.Числата които компютара използва са само нули и единици
това е така наречената двоична бройна система.Двоична е защото се състои само от две числа 0 и 1.Едно число от двоичната система се нарича БИТ (Binary digIT)
Самоче един бит не ни е от голяма полза и затова компютъра гупира няколко бита и ето какво се получава. Четири бит става нибъл ( Nibble) 0101 но един нибъл също
не ни е от полза затова нека групираме два нибъла за да получим най малката единица данни която може да бъде записана, това е байта (Byte) 00101101.
А ако обединим два байта се получава дума (word) а ако обединим две думи се получава двойна дума (dword).
Тази система от единици и нули е изключително удобна за компютрите но изключително неодобно за нас хората.Например 00000101 е 5 в десетична бройна система (decimal - бройната система
която ползваме в нашето ежедневие).А превращането от десетична в двоична и обратното е трудуемко.Затовае е изобретена шеснайсетична бройна система (hexdecimal) която използва числата
от 1 до 9 и буквите от латинската азбука от A до F.Тя е измислена защото ни позволява по леко да работим с двоичната бройна система, защото превращането от двоична в шеснайсетична и обратното е
много лесно.Просто трябва да разделите двоичното число на части по 4 бита в всяка част и след това просто записвате кое число от шеснайсетичната система отговаря на дадената група от 4 бита.
За да видите това може да използвате следващата таблица, а ето ви и един пример как става на практика:
имаме това двоично число -> 111100010111 първа крачка -> 1111 0001 0111 втора крачка просто трябва да намерим числата в таблицата
1111 = F 0001 = 1 0111 = 7 и ето че се поличава 0xF17 (0x се използва за да индикираме че това е шеснайсетично число).


Dec Hex Bin 
0 0 0000
1 1 0001
2 2 0010
3 3 0011
4 4 0100
5 5 0101
6 6 0110
7 7 0111
8 8 1000
9 9 1001
10 A 1010
11 B 1011
12 C 1100
13 D 1101
14 E 1110
15 F 1111
Превращането от шеснайсетична в двоична става по аналогичен начин, мисля че се досещате как.

Сега е време да стане малко по сложно като се запознаем с това как се превръща от шеснайсетична в десетична и обратното.
Например имаме 333h (h накрая на числото също индикира че това е шеснайсетично число).За да го преварнем в десетично се извършват следните действия.

1x(16^0) = 1  
2x(16^1) = 32
3x(16^2) = 768
  -------
  801 
Първо се взима най дясната единица в нашия случай това е 1 и се умножава по 16 на степен 0 и си запазваме резултата за после.
После както виждате взимаме второто число и извършваме същото действие, самоче този път 16 не е на степен 0 а на степен 1.Мисля че, вече разбирате
че с всяко следващо число степента на която се повдига 16 се повишава с 1 :) . 

Сега е време да разберем как става превръщането от десетична в шеснайсетична.
Ако имаме десетичното число 1911 ето какви действия трябва да извършим за да го превърнем в шеснайсетично.
1911 - десетично.
1911/16 = 119.4 
119.4 - 119 = 0.4375
0.4375x16 = 7 <- запазваме го за накрая 
   
119/16 = 7.4375  
7.4375 - 7 = 0.4375  
0.4375x16 = 7 <- запазваме го за накрая  
   
7/16 = 0.4375  
втората съпка тук е безсмислена  
0.4375x16 = 7 <- запазваме го за накрая  
   
Първо делим числото на 16 след това обикновено числото което се получава е с десетична запетая затова го закръгляме към най-близката единица.
След това взимаме остатъка и го уножава ме по 16 и полученият резултат се запазва за накрая.
При второто деление на 16 взимаме закрагленото число което се е получило при предишното деление на 16.
Може да ви звучи малко объркващо и сложно но като разгледате добре примера и парктикувате малко сичко ще ви стане по ясно.
Накрая числата които казах да се запазят за накрая се съединяват като първото получено число е от страната на единиците, второто получено от
страната на десетиците а третото получено от страната на стотиците и т.н.  

Сега е време да се запознаем с превръщането от двоична в десетична и обратното.
Превръщането от двоична в десетична става по следния начин.
Имаме следното двоично число 1010011.

(1*1) + (1*2) + (0*4) + (0*8) + (1*16) + (0*32) + (1*64) =
  1 + 2 + 0 + 0 + 16 + 0 + 64 = 83

Взимаме най лявата единица от двоичното число и я умножава по 1 и записваме резултата.
След това взимаме втората единица и този път я умножава ме по 2 ( с всяка следваща единица числото множител
се умеличава като се умножи по 2 ).
Накрая получените числа от умножението се сабират.Мисля че примерът който съм дал е достатъчно ясен за да разберете как става :).

Сега остана само да разберем как става превръщанет от десетична в двоична.Ето ви един пример

273/2 = 136.5 | 1
136/2 = 68 | 0
68/2 = 34 | 0
34/2 = 17 | 0
17/2 = 8.5 | 1
8/2 = 4 | 0
4/2 = 2 | 0
2/2 = 1 | 0
1/2 = 0.5 | 1

Тук всичко е много просто, просто делим числото на 2 и ако има остатък отстрани си записваме 1 ако няма остатък си записваме 0.
При следващото деление взимаме числото получено от предишното деление без остатъка, и така докато не се получи 0.
След това четем редицата от нули и единици като започнем от най долната на горе и ги записваме.
и се получава 100010001.

Първо реших да се запознаем с регистрите в асамблера.Можете да гледате на тях като кутийки в които можете да запазвате нещо времено.
Регистрите на повечето компютри в днешно време са 32 бита.Вие вече знаете че един бит това е един бинариен.Значи един регистар може да съдържа по 32 бита.
Самоче както знаете е неодобно и не практично да се работи с битове затова нека разглеждаме регистрите като променливи които побират по 8(=32 бита) байта.
Един байт може да приема до 256 различни значения.За да разберете какви значения може да приема един байт ето ви линк към ascii таблица.
[URL=http://www.asciitable.com/]http://www.asciitable.com/[/URL] 

Урока който разглеждате в момента се отнася за семейството процесори x86 на Intel.Това са най използваните процесори днешно време.
Регистрите в асамблера са следните:
EAX(Extendet Accumulator Register)
ECX(Extendet Count Register)
EDX(Extendet Data Register)
EBX(Extendet Base Register)
ESP(Extendet Stack pointer)
EBP(Extendet Base Pointer)
ESI(Extendet Source Index)
EDI(Extendet Destination Index)
EIP(Extendet Instruction Pointer)
Можеби се чудите защо пред всеки от регистрите има сложено E(Extendet), Този префикс е бил поставен когато са изобретили 32 битовите процесори.
Преди регистрите са били 16 битови и саотвено следните:AX,CX,DX,BX,SP,BP,SI,DI,IP.
Всеки регистър се състой от висок ред (high-order) и нисък ред (low-order).
Нека вземем EAX и да го разгледаме по от близо.
|0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31| <- EAX = 32 бита
|_____________________________________|_______________________________________________| <- Първъта половина се нарича AX и е ниският ред на EAX, а втората половина няма определено име и неможе да се
използва самостоятелно, тя е високият ред на EAX, и е била добавена когато са били измислени 32 битовите процесори.
Нека вземем ниският ред на EAX към който можем да се обръщаме с AX.
|0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15| <- AX. AX също се състои от нисък ред и висок ред съответно AH за висок ред и AL за нисък ред.
|_______________|_____________________|

Това което научихме до момента се отнася и за девете главни регистъра.Наричат се главни защото може да изполват за всичко.
Но преди когато те не са били 32 битови всеки е имал строго определено предазначение.Ще се спрем на тези подтробности по късно когато започнем да пишем програми.

Има още няколко регистара със строго определено значение това са:
CS(Code Segment) <- Сочи винаги към сегмента на кода.
DS(Data Segment) <- Сочи винаги към сегмента на данните.
SS(Stack Segment) <- Сочи винаги към стека.
ES(Extra Segment) <- Допалнителен регистър.
FS <- Допалнителен регистър.
GS <- Допалнителен регистър.

Има още един регистер EFLAGS който ще разгледаме когато започнем да програмираме.
Има още един куп регистри които не са важни за нас поне засега.

Следващата програма работи върхо всички версий на MS-DOS и Windows (не сам сигурен за Vista).
Написана е на 16 битов ассамблер и ассамблирана с TASM.
За да ассамблирате програмата програмата трябва да я запишете в .asm формат в директорията BIN на TASM.
И да извършите следните действия през cmd в директорията BIN.
tasm ImeNaFaila.asm
tlink /t ImeNaFaila.obj
 
.model tiny;казва на TASM да използва само един сегмент за цялата програма.. едвали ще ви се
;наложи да използвате .model small, .model huge ... (това не е важно засега)
.code;казва на TASM откаде започва кода 
org 100h;казва откаде да започне стартирането на програмата (в нашия случай това е 100h, можеби се чудите защо
;не от самото начало от 1h  защото .COM програмите имат нужда от първите 100h (256 байта) които се наричат PSP (по нататък ще раз
;гледаме тази структура подробно)
start:;просто заглавие
mov dx,OFFSET STRING;mov премества данни от едно място в друго, mov дестинация,източник, дестинацията в нашич случай е регистърът dx
;а източника е низ терминиран с $ (ако не е терминиран с $ то тогава програмата ще изведе всичко което стрештне в секцията с данни докато не
;срещне $)
mov ah,09h;премества в ah шеснайсетичната стойност 09h която е необходима за да кажем кое прекасване (Interrupt) ще използваме
;Прекъсване това е нещо като извикване на даден вече дефинирана функция. MS-DOS ползва главно int 21 vector table прекъсвания . Извикването на дадено прекъсване
;става така, първо в регистрите се попълват дадените стойности искани от даденото прекъсване и след това се извиква с инструкцията int 21h.
;Например в нашия случей прекъсването за изкарване на екрана даден низ изисква dx да съдържа адреса към низ терминиран с $.
;Листата с всички прекъсвания може да намерите тук [URL=http://www.ctyme.com/intr/int-21.htm]http://www.ctyme.com/intr/int-21.htm[/URL] .
int 21h;извикване на прекъсване 
int 20h;извиква прекъсване от int 20h vector table което е единственото в тази векторна таблица и не изисква никакви параметри записани в регистрите 
;и се използва за излизане от програмата и връщане контрола на MS-DOS.
STRING db "Hello H4x0rs$";Низ терминиран с $.
end start;слага край на заглавието start. *изисква се от TASM*.

Дано не останете разочаровани.

Няма коментари:

Публикуване на коментар