BLACK FRIDAY! Slevy až 80 % jsou všude. Tak je nepropásni a přejdi do rostoucího IT oboru!
The real BF 2020

Lekce 1 - Úvod do programování v Assembleru

Vítejte u kurzu, ve kterém se spolu ponoříme až do hloubek, kam se vůbec jako programátoři můžeme dostat. Obejdeme programovací jazyk i kompiler a zjistíme, jak funguje samotný stroj. Budeme posílat přímo instrukce procesoru, pochopíme architekturu x86, BIOS, jak se adresuje paměť a zavádí operační systém. Tato problematika souvisí také s reverse-engineeringem, crackováním softwaru a samozřejmě hackingem. Naučí vás používat nástroje jako disassemblery, cheat enginy a lépe pracovat s debuggery.

Hacking

Kurz zahajme slavnou citací:

Don't learn to hack, hack to learn!

Předpoklady

Kurz předpokládá znalost alespoň základů fungování počítače po hardwarové stránce a zkušenost s libovolným vysokoúrovňovým programovacím jazykem (např. C nebo Java).

Proč se dnes učit Assembler?

Vždy mě zajímalo, jak věci v základu fungují a namísto toho, abych používal něco, co už existuje, jsem si raději udělal něco svého. Například máme doma rádio. A já, namísto toho, abych ho používal, začal jsem se stavbou vlastního AM rádia. A nebo většina z nás používá operační systém Windows... No a co jsem asi udělal... Ano, začal jsem pracovat na vlastním operačním systému v Assembleru.

Co se tu budeme učit je samozřejmě velmi speciální a úzké zaměření. Komerčně dnes tyto znalosti uplatníte ve sféře kyberbezpečnosti nebo při programování pro některá nízkoúrovňová embedded zařízení, případně pro high-performance programování. A nebo když se budete chtít pokusit donutit dělat nějakou cizí aplikaci to, co od ní chcete, případně si naprogramovat zavaděč operačního systému. To samozřejmě není tak snadné, ale někde se začít musí :)

Abychom pochopili, jak vůbec ASM funguje, podívejme se ve zkratce na historii programovacích jazyků.

Vývoj programovacích jazyků

1. generace jazyků - Strojový kód

Procesor počítače umí vykonávat jen omezené množství jednoduchých instrukcí, které jsou uloženy jako sekvence bitů, jsou to tedy čísla. Ta se mu obvykle zadávají v hexadecimální (šestnáctkové) soustavě. Instrukce jsou tak elementární, že umožňují pouze např. práci s registry procesoru (to je paměť v CPU) nebo skoky. Nelze např. jednoduše sečíst dvě čísla, musíme se na čísla dívat jako na adresy v paměti a takové sečtení čísel zabere několik instrukcí. Program sčítající dvě čísla by vypadal např. takto:

B853000000
BBFEFFFFFF
03C3
C3
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Instrukce se procesoru předloží v binární podobě. Takovýto kód je samozřejmě extrémně nečitelný a závisí na instrukční sadě daného CPU. Každý počítačový program musí být nakonec do tohoto jazyka přeložen, aby mohl být na procesoru počítače spuštěn.

Strojový kód

2. generace jazyků - Assembler

Assembler neboli JSA (Jazyk Symbolických Adres) se objevil někdy v polovině 20. století. Konkrétně se jednalo o jazyk druhé generace. Od jazyků první generace se lišil tím, že namísto toho, abychom si museli pamatovat číselné kódy instrukcí, jsme mohli psát jejich slovní kódy (například: MOV, CMP, ADD).

Díky Assembleru bylo psaní programů jednodušší a přehlednější, než kdybychom je psali v číslech. Další výhodou bylo, že adresy v programu se nemusely měnit přepisováním celého programu jako tomu bylo u první generace. Co se týče kódu samotného, nemusely se opět např. složitě vypočítávat adresy skoků. Kód tedy začal být vůbec lidsky čitelný, i když není o nic jednodušší, než původní strojový kód.

Stejný program by v ASM vypadal takto:

.code
main:
mov eax,83
mov ebx,-2
add eax,ebx
ret
end main

Vidíme, že je to poněkud lidštější, ale stále nezasvěcení lidé vůbec netuší, jak program funguje.

3. generace jazyků

Jazyky v třetí generaci konečně nabízí uživateli určitou abstrakci nad tím, jak program vidí počítač, zaměřují se na to, jak program vidí člověk. Jsou označovány jako tzv. vyšší programovací jazyky (anglicky High Level Languages, někdy zkráceně HLL). Naše čísla jsou vnímána již jako proměnné, zdrojový kód připomíná matematický zápis.

Jedním z prvních vysokoúrovňových programovacích jazyků byl jazyk C. I když jej předběhl Fortran nebo Pascal, tak to byl právě jazyk C, který dobyl svět. Opět ten samý programy by v jazyce C vypadal takto:

int main(void)
{
    int a, b, c;
    a = 83;
    b = -2;
    c = a + b;
    return c;
}

V kontrastu současných objektově orientovaných moderních jazyků je jazyk C často označován jako jazyk nízkoúrovňový. Jak to tedy je? Od příchodu jazyka C zkrátka uběhlo již několik dekád a záleží s čím jej porovnáváme. V našem kontextu s ASM je nazýván vysokoúrovňovým, v kontextu např. s jazykem C# .NET je naopak nízkoúrovňový.

Všichni asi tušíme, co program dělá, sečte čísla 83 a -2 a výsledek uloží do proměnné c. U všech jazyků třetí generace je samozřejmě výhodou vysoká čitelnost.

Překlad vyšších programovacích jazyků

Tyto jazyky tedy mají svůj zdrojový kód v jazyce, kterému lidé dobře rozumí. Zdrojový kód se samozřejmě musí přeložit do binárního kódu, aby ho bylo možné na procesoru spustit. Tento překlad zajišťuje překladač (kompiler), který přeloží najednou celý program do strojového kódu. Některé kompilátory umějí kromě strojového kódu vygenerovat také kód assembleru.

Kompiler

Některé moderní programovací jazyky (např. C#, Java) se nepřekládají přímo do strojového kódu, ale používají binární mezikód. Ten se kompiluje do strojového kódu až na počítači uživatele (buď hned po instalaci aplikace nebo při jejím spuštění). To má výhodu v tom, že binární mezikód je nezávislý na procesoru. Vývojář aplikace tedy nemusí dávat ke stažení zvlášť verze pro x86, x64, nebo ARM. Nevýhodou je to, že uživatel musí mít nainstalovaný framework pro daný programovací jazyk.

Pokud budeme vědět, jak vypadají v ASM rutiny např. C překladače, budeme schopní pochopit, jak tyto programy fungují nebo je dokonce modifikovat, aniž bychom od nich měli zdrojový kód. Jedná se ale samozřejmě o velmi komplexní problematiku, protože sebemenší funkce vyústí ve velké množství ASM instrukcí a kompiler ve výsledné podobě ještě provádí četné optimalizace. Pojďme jim věnovat alespoň krátký odstavec.

Optimalizace překladače

Když bychom psali v čistém ASM, velmi pravděpodobně bude náš program pomalejší než ten samý program zkompilovaný např. z jazyka C. Jak je to možné? Kompiler totiž kód upraví tak, aby byl velmi rychlý a to často za cenu, že je pro člověka pak špatně čitelný. Určitě jste všichni někdy použili cyklus. Ten má nějakou řídící proměnnou, podmínku a zkrátka režii navíc. Proto se může někdy optimizer rozhodnout, že bude lepší cyklus nepřeložit, ale namísto toho kód jen několikrát zopakovat za sebou. Pokud vás problematika zajímá, doporučuji krásný článek Překladače pod pokličkou - optimalizace.

Specifika programování v Assembleru

A jak tedy takové programování v Assembleru vypadá?

Pokud si budeme programovat svůj vlastní operační systém, pak nemáme k dispozici žádné předem vytvořené funkce. Nebudeme moct použít žádné PrintString(), ClearScreen(), ani SetCursorPosition(). Úplně všechno si budeme muset vytvořit sami.

Pokud bychom vytvářeli aplikaci pro historický operační systém MS-DOS, pak máme k dispozici několik funkcí, které se volají přerušením INT 21H (k přerušením se v kurzu brzy dostaneme). Toto přerušení je dostupné pouze v operačním systému MS-DOS, což znamená, že ho (ani ostatní) nebudeme používat. My si vystačíme s přerušeními, která nabízí BIOS.

Při programování aplikací pro moderní operační systémy (např. Windows nebo Linux) můžeme v Assembleru využívat všechna API konkrétního operačního systému. Například můžeme číst a zapisovat data do souborů, ovládat aplikaci klávesnicí a myší, zobrazit text na obrazovce. Naopak ale nesmíme používat přerušení BIOSu. Také nemůžeme používat všechny instrukce procesoru. Některé instrukce jsou totiž privilegované a jsou povoleny pouze pro jádro operačního systému a ovladače. V naší aplikaci nemůžeme přímo přistupovat k hardware, používat přerušení nebo přepínat režimy procesoru.

Ukázka kódu

Na závěr úvodního dílu si ukážeme a popíšeme zdrojový kód metody PrintString pro výpis textu jen pomocí rutin BIOSu. Metodu budeme používat příště, kde si ji také vysvětlíme. Zde se na ni můžete podívat jen proto, abyste věděli, jak programování v ASM vypadá a do čeho se to vlastně pouštíme.

Metoda PrintString:

PrintString:    ; Začátek metody pro vypisování zprávy
lodsb           ; Přesuneme řetězec do registru AX
or al, al       ; Zkontrolujeme, jestli je v registru AL ještě nějaký znak, který bychom mohli vytisknout
jz short .Done  ; Pokud ne, skočíme na .Done
mov ah, 0eh     ; Registr AH naplníme hodnotou 0eh
mov bx, 7h      ; Registr BX naplníme hodnotou 7h
int 10h         ; Vytiskneme znak
jmp PrintString ; Vrátíme se na začátek metody
.Done:          ; Popisek pro návrat
ret             ; Vrátíme se na místo, odkud jsme metodu zavolali

No a to je pro dnešek vše.

V příští lekci, Instalace kompilátoru assembleru ve Windows, si nainstalujeme kompilátor a vytvoříme první zatím prázdný ASM projekt.


 

Všechny články v sekci
Základy assembleru
Článek pro vás napsal Jakub Verner
Avatar
Jak se ti líbí článek?
4 hlasů
Autor se věnuje programování v Assembleru a v C#. Rád se zlepšuje, rozšiřuje si znalosti a věří, že když člověk chce, dokáže cokoliv.
Aktivity (9)

 

 

Komentáře

Avatar
Milos Simek
Člen
Avatar
Milos Simek:2. dubna 0:12

Díky za článek. Těším se na další díly.

 
Odpovědět
2. dubna 0:12
Avatar
Richard Tichý
Redaktor
Avatar
Richard Tichý:2. dubna 23:33

Díky za článek, zajímavý úvod do ASM :-)

Odpovědět
2. dubna 23:33
Když už něco rozděláš, měl bys to dokončit.
Avatar
Filip
Člen
Avatar
Filip:3. dubna 1:45

Diky, potesilo... v assembleru som programoval este na C-64 (CPU 6510, 8bit)

 
Odpovědět
3. dubna 1:45
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
paja
Člen
Avatar
paja:3. dubna 8:00

Perfektne vysvetlene , diky 8-)

 
Odpovědět
3. dubna 8:00
Avatar
Nositelka Změny:9. června 16:42

Skvělý článek, konečně se naučím používat Assembler :-) Ale mám takový pocit, že C je vedle Fortranu poněkud mladé. A to mezi nimi vznikla ještě řada jazyků. Nejsem si tím úplně jistá, ale mám pocit, že posun z ASM na Céčko je větší než posun z Céčka na Javu nebo další podobné jazyky. Takže C je vlastně vysokoúrovňonější, než si o něm někteří myslí :-) A i kdyby, stále je na tom mnohem líp než ten Fortran...

Odpovědět
9. června 16:42
j.k.j
Avatar
Jakub Verner
Redaktor
Avatar
Odpovídá na Nositelka Změny
Jakub Verner:9. června 16:52

Já osobně považuji C za vysokoúrovňový. :-) Díky. ;-)

 
Odpovědět
9. června 16:52
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 6 zpráv z 6.