21:25 <@Gynvael> dobra
21:25 <@Gynvael> Karql mowi ze ok
21:25 <@Gynvael> wiec jedziemy
21:25 <+Karql> dobra nie odzywam sie juz :D
21:25 <@Gynvael> Witam was wszystkich na 3ciej czesci wykladu poswieconego assemblerowi
21:25 <@Gynvael> Dzisiaj bedzie raczej krotki wyklad (z uwagi na to ze dopiero dotarlem na stancje etc)
21:25 <@Gynvael> Ale od nastepnego tygodnia bedzie juz wszystko w normie
21:26 <@Gynvael> Ostatnio mowilismy o stringach ASCIIZ, jak liczyc ich dlugosc
21:26 <@Gynvael> Probowalismy bawic sie debuggerem gdb, oraz chwile pobawilismy sie flagami.
21:26 <@Gynvael> A dokladniej flaga zerowania
21:27 <@Gynvael> Przypomne tylko ze flaga moze byc ustawiona (SET, miec wartosc 1) lub moze byc wyczyszczona (CLEAR, wartosc 0)
21:27 <@Gynvael> flag procesora jest dosc sporo
21:27 <@Gynvael> ostatnio mowilismy o ZF (zero flag)
21:27 <@Gynvael> czyli fladze ktora jest ustawiona jesli ostatnia operacja (arytmetyczna/porownanie) dala w wyniku zero
21:28 <@Gynvael> i ktora jest wyczyszczona jesli ostatnia operacja nie dala w wyniku zero
21:28 <@Gynvael> Przykladowo:
21:28 <@Gynvael> mov eax, 0 <=- to NIE ZMIENI wartosci flagi ZF, powniewaz mov nie jest operacja arytmetyczna (tj sie nie bawi)
21:28 <@Gynvael> mov eax, 5
21:28 <@Gynvael> sub eax, 5
21:29 <@Gynvael> to ZMIENI wartosc flagi ZF na 1, jako ze 5 - 5 == 0
21:29 <@Gynvael> mov eax, 5
21:29 <@Gynvael> sub eax, 3
21:29 <@Gynvael> to rowniez ZMIENI wartosc flagi ZF, tyle ze na 0, jako ze 5-2 == 2, wiec to nie jest zero ;>
21:30 <@Gynvael> przyklady instrukcji ktore ZAWSZE ustawia wartosc flagi ZF na 1:
21:30 <@Gynvael> xor eax, eax <=- xor tego samego z tym samym ZAWSZE daje w wyniku 0, wiec ZF po tym == 1
21:30 <@Gynvael> sub eax, eax <=- odjecie czegos od siebie samego tez ZAWSZE daje w wyniku 0
21:31 <@Gynvael> and eax, 0 <=- binarne AND czegokolwiek z zerem ZAWSZE da w wyniku 0, wiec ZF == 1
21:31 <@Gynvael> teraz tak
21:31 <@Gynvael> instrukcje ktore sie NIE BAWIA, tj nei zmieniaja wartosci flagi ZF (oraz innych flag stanu):
21:31 <@Gynvael> push
21:31 <@Gynvael> pop
21:31 <@Gynvael> mov
21:31 <@Gynvael> jmp
21:31 <@Gynvael> jn*
21:31 <@Gynvael> j*
21:31 <@Gynvael> czyli wszelkie przesuniecia, skoki etc
21:32 <@Gynvael> ok
21:32 <@Gynvael> teraz po co sa flagi ?
21:32 <@Gynvael> no bo fajnie.. costam sie przestawia...
21:32 <@Gynvael> ale...
21:32 <@Gynvael> po co ? ;>
21:32 <@Gynvael> odpowiedzia sa SKOKI WARUNKOWE
21:33 <@Gynvael> jest cala seria SKOKOW WARUNKOWYCH ktore WYKONUJA SKOK POD PODANY ADRES JESLI DANA FLAGA JEST USTAWIONA
21:33 <@Gynvael> od stanu flagi ZF bezposrednio zalezne jest wykonanie skoku instrukcji:
21:33 <@Gynvael> jz (mozna pisac je, to to samo)
21:33 <@Gynvael> jz - jump if zero - wykonaj skok jesli ZF jest ustawione..
21:34 <@Gynvael> czyli na rozum prostego Cprogramisty
21:34 <@Gynvael> if(ZF==1) goto costam
21:34 <@Gynvael> jej przeciwienstwo, czyli wykonaj skok jesli ZF jest wyczyszczone
21:34 <@Gynvael> to jnz
21:34 <@Gynvael> jump if NOT zero ;>
21:35 <@Gynvael> ostanio mowilem rowniez o instrukcji cmp
21:35 <@Gynvael> ktora jest "mentalnym" odejmowanie
21:35 <@Gynvael> cmp a, b wykonuje operacje a-b i ustawia odpowiednio flagi
21:35 <@Gynvael> przykladowo
21:35 <@Gynvael> mov eax, 5
21:35 <@Gynvael> cmp eax, 5
21:35 <@Gynvael> 5-5 == 0, wiec po tym ZF bedzie ustawione
21:35 <@Gynvael> roznica miedzy sub eax, 5 a cmp eax, 5 jest taka
21:35 <@Gynvael> ze sub eax ZAPAMIETUJE WYNIK W EAX
21:35 <@Gynvael> a cmp NIE ZAPAMIETUJE WYNIKU
21:36 <@Gynvael> istnieje rowniez instrukcja test
21:36 <@Gynvael> ktora jest "mentalnym" odpowiednikiem instrukcji and
21:36 <@Gynvael> test a,b wykonuje w pamieci and a,b
21:36 <@Gynvael> nie zapamietujac wyniku
21:36 <@Gynvael> test mozna wykorzystac (i czesto sie to stosuje) aby sprawdzic czy costam == 0
21:37 <@Gynvael> test jest swoja droga szybsze niz cmp
21:37 <@Gynvael> (ale nie nadaje sie do wszystkiego.. patrz zasady dzialania binarnego and)
21:37 <@Gynvael> czyli przykladowo
21:37 <@Gynvael> mov eax, 0
21:37 <@Gynvael> test eax, eax
21:37 <@Gynvael> to ustawi flage ZF = 1
21:37 <@Gynvael> mov eax, 5
21:37 <@Gynvael> test eax, eax
21:37 <@Gynvael> to ustawi flage ZF = 0
21:38 <@Gynvael> przykladowo
21:38 <@Gynvael> mov eax, 5
21:38 <@Gynvael> test eax, eax
21:38 <@Gynvael> jnz eax_nie_jest_rowne_zero
21:38 <@Gynvael> jmp eax_jest_rowne_zero
21:38 <@Gynvael> to taki asmowy odpowiednik if(eax) goto eax_nie_jest...; else goto eax_jest...;
21:38 <@Gynvael> ok
21:38 <@Gynvael> teraz sie troche pobawimy
21:38 <@Gynvael> ja bede pisal fragmenciki kodu
21:39 <@Gynvael> a wy mi na privie bedziedzie pisac czy ZF = 0 czy ZF = 1 czy ZF pozostanie bez zmian
21:39 <@Gynvael> ;>
21:39 <@Gynvael> ok
21:39 <@Gynvael> przyklad pierwszy
21:39 <@Gynvael> mov ecx, 0
21:39 <@Gynvael> mov edx, ecx
21:39 <@Gynvael> koniec ;>
21:39 <@Gynvael> odpowiedzi prosze na priv ;>
21:40 <@Gynvael> ehehhe no dalej ;p
21:40 <@Gynvael> ok
21:40 <@Gynvael> dostalem pare odpowiedzi
21:40 <@Gynvael> poprawna 1dna
21:40 <@Gynvael> mianowicie ZF pozostanie bez zmian
21:41 <@Gynvael> jako ze mov SIE NIE BAWI W JAKIES TAM FLAGI
21:41 <@Gynvael> wiec mov ecx, 0 NIE ZMIENIA FLAG
21:41 <@Gynvael> mowilem o tym ;>
21:41 <@Gynvael> ok
21:41 <@Gynvael> przyklad drugi
21:41 <@Gynvael> mov eax, -10
21:41 <@Gynvael> add eax, 10
21:41 <@Gynvael> push eax
21:41 <@Gynvael> pop eax
21:41 <@Gynvael> koniec
21:41 <@Gynvael> odpowiedzi prosze na priv
21:43 <@Gynvael> hmm
21:43 <@Gynvael> 52 osoby
21:43 <@Gynvael> a ja dostalem jedna odpowiedz ;>
21:43 <@Gynvael> na szczescie poprawna bo bym sie zalamal >_>
21:43 <@Gynvael> ok
21:43 <@Gynvael> przyklad trzeci i ostatni ;>
21:43 <@Gynvael> mov eax, 0x13
21:43 <@Gynvael> and eax, 2
21:43 <@Gynvael> ;>
21:43 <@Gynvael> koniec
21:43 <@Gynvael> odpowiedzi na priv
21:45 <@Gynvael> dostalem pare odpowiedzi
21:45 <@Gynvael> ;>
21:45 <@Gynvael> w tym 2 dobre
21:45 <@Gynvael> ZF = 1 po tym
21:45 <@Gynvael> czemu ?
21:45 <@Gynvael> poniewaz :
21:46 <@Gynvael> e w8
21:46 <@Gynvael> ;p
21:46 <@Gynvael> zamieszalem sie
21:46 <@Gynvael> ZF = 0 ;p
21:46 <@Gynvael> eax = 0x13.. 0x13 binarnie to 0001 0011
21:46 <@Gynvael> 2 binarnie to 0000 0010
21:46 <@Gynvael> wiec and 0x13, 2 to 2 ;>
21:46 <@Gynvael> czyli ZF = 0
21:47 <@Gynvael> ok
21:47 <@Gynvael> wiec jeszcze jedno
21:47 <@Gynvael> mov ecx, 10
21:47 <@Gynvael> dec ecx
21:47 <@Gynvael> inc ecx
21:47 <@Gynvael> koniec
21:50 <@Gynvael> poprawna odpowiedz to ZF = 0
21:50 <@Gynvael> czemu ?
21:50 <@Gynvael> mov ecx, 10 <=- to nie zmienia
21:50 <@Gynvael> dec ecx <=- ale to juz operacja arytmetyczna ktora sie bawi flagami (ecx--)
21:50 <@Gynvael> inc ecx tez sie bawi flagami, to ecx++
21:50 <@Gynvael> czyli mamy 10-1+1, czyli 10
21:50 <@Gynvael> wiec ZF = 0
21:51 <@Gynvael> dobra
21:51 <@Gynvael> tyle o flagach
21:52 <@Gynvael> teraz postarajmy sie napisac jakis przyklad
21:52 <@Gynvael> ktory troche ilustruje te flagi
21:52 <@Gynvael> a raczej ich stany
21:52 <@Gynvael> http://gynvael.vexillium.org/flag_1.asm
21:52 <@Gynvael> tym razem przyklad jest przynaje, dlugi i trudniejszy
21:52 <@Gynvael> ;>
21:52 <@Gynvael> jest w nim pare nowosci
21:53 <@Gynvael> narazie powiem co on robi
21:53 <@Gynvael> gynvael@localhost:wyklady> ./flag_1
21:53 <@Gynvael> Testujemy "xor edx, edx" : Zero Flag == 1 (set)
21:53 <@Gynvael> Testujemy "mov edx, 0" : Zero Flag == 0 (clear)
21:53 <@Gynvael> Testujemy "and edx, 0" : Zero Flag == 1 (set)
21:53 <@Gynvael> Testujemy "sub edx, edx" : Zero Flag == 1 (set)
21:53 <@Gynvael> Testujemy "push 0; pop edx": Zero Flag == 0 (clear)
21:53 <@Gynvael> mianowicie wypisuje on cos takiego
21:53 <@Gynvael> czyli jest to programik testujacy poszczegolne instrukcje
21:53 <@Gynvael> narazie nie zaglebiajac sie w pojedyncze instrukcje
21:53 <@Gynvael> pomyslmy jak on dziala
21:54 <@Gynvael> wiec tak..
21:54 <@Gynvael> _start: to glowna "funkcja"
21:54 <@Gynvael> (nop przypominam ze nic nie robi.. jest on tam ze wzgledu na bug w niektorych gdb powodujacy ze gdb po prostu nie umie breakpointa wyegzekwowac na entry poincie i po intach ;p)
21:54 <@Gynvael> czytajac komentarze widzimy ze jest tam po kolei 5 testow
21:54 <@Gynvael> a potem wyjscie z programu
21:55 <@Gynvael> ; test xor
21:55 <@Gynvael> ; test mov
21:55 <@Gynvael> ; test and
21:55 <@Gynvael> ; test sub
21:55 <@Gynvael> ; test stack
21:55 <@Gynvael> ; wyjscie z procesu
21:55 <@Gynvael> kazdy test to zbitek paru insturkcji
21:55 <@Gynvael> call clear_flag
21:55 <@Gynvael> mov edx, 10
21:55 <@Gynvael> lea eax, [string_xor]
21:55 <@Gynvael> call my_puts
21:55 <@Gynvael> xor edx, edx ; najwazniejsze
21:55 <@Gynvael> call test_zf
21:56 <@Gynvael> nowe tutaj jest call
21:56 <@Gynvael> mov znacie
21:56 <@Gynvael> lea sie pojawilo (load efective address... lea a, [b] to to samo co mov a,b.. ale dla pointerkow tak sie zwyklo robic...)
21:56 <@Gynvael> xor juz poznaliscie (btw w jezyku C pisze sie a^=b.. i to to samo co xor a,b)
21:57 <@Gynvael> rzucmy okiem z troche wiekszej perspektywy
21:57 <@Gynvael> kazdy test to:
21:57 <@Gynvael> 1. wyczyszczenie flagi ZF
21:57 <@Gynvael> 2. wypisanie "Testujemy costam"
21:57 <@Gynvael> 3. wykonanie tego co testujemy
21:57 <@Gynvael> 4. wywolanie funkcji test_zf ktora wypisuje czy flaga jest ustawiona czy wyczyszczona
21:58 <@Gynvael> teraz sie zaglebmy w instrukcje
21:58 <@Gynvael> call nazwa IDZIE POD PODANY ADRES (nazwa to adres), po czym PO NATRAFIENIU NA INSTRUCKJE ret WRACA
21:58 <@Gynvael> wraca na nastepna instruckje
21:58 <@Gynvael> czy call clear_flaga... costam costam... ret; mov edx, 10 etc...
21:59 <@Gynvael> calosc dziala identycznie jak basicowe GOSUB i RETURN
21:59 <@Gynvael> natomiast musimy wiedziec o jednej waznej rzeczy
21:59 <@Gynvael> call KORSZYSTA ZE STOSU DO ZAPAMIETANIA ADRESU POWROTU
21:59 <@Gynvael> call to taki zlepek: push adres_nastepnej_instrukcji, jmp adres_skoku
22:00 <@Gynvael> a ret to cos podobnego...: pop adres_powrotu, jmp adres_powrotu
22:00 <@Gynvael> czyli call wrzuca na stos adres powrotu
22:00 <@Gynvael> a ret go sciaga
22:00 <@Gynvael> wygodny mechanizm jesli sie go nie naduzywa
22:01 <@Gynvael> (btw.. hackerzy bardzo sobie cenia ten adres powrotu na stosie.. pare metod (stack based buffer overflow, format bug, etc) korzysta z mozliwosci nadpisania tego adresu, i powrocenia przy ret tam gdzie hacker, a nie program chce ;>)
22:01 <@Gynvael> ok
22:01 <@Gynvael> o call nazwa mozna myslec jako "WYWOLANIE FUNKCJI nazwa"
22:02 <@Gynvael> (btw do programistow C, pascala, etc... te wasze funkcja() sa tlumaczone wlasnie na call funckja ;>)
22:02 <@Gynvael> wiec
22:02 <@Gynvael> kazdy test to wywolanie paru funkcji
22:02 <@Gynvael> ok
22:02 <@Gynvael> teraz przeanalizujmy wykonanie samej funkcji _start
22:02 <@Gynvael> http://gynvael.lunarii.org/temp/flag_1.gif
22:03 <@Gynvael> tam jest zamieszczona animacja wykonania (wazy pol mega, ale odstep miedzy klatkami jest na tyle duzy ze nie powinno to przeszkadzac nawet na modemach)
22:04 <@Gynvael> tam w "okienku" Trace log sa podawane instrukcje wlasnie wykonane
22:04 <@Gynvael> oraz zamiany w wartosci rejestrow oraz flag
22:04 <@Gynvael> zwroccie prosze uwage przy ktorych testach flaga ZERO sie zmienia
22:05 <@Gynvael> hmm hmm wiecie co wam powiem --;
22:05 <@Gynvael> tam cos sie nei zmienia ;p mimo ze dobrze wypisuje ;>
22:05 <@Gynvael> nooo nic ;>
22:05 <@Gynvael> widac z animacjami trzeba poczekac do nastepnego wyklady
22:05 <@Gynvael> *wykladu
22:06 <@Gynvael> (natomiast na priv bym prosil info co sadzicie o tym pomysle)
22:06 <@Gynvael> ok
22:06 <@Gynvael> wezmy sie za analizowanie poszczegolnych funkcji
22:07 <@Gynvael> ok
22:07 <@Gynvael> na poczatku kazdego testu wykonywane jest call clear_flag
22:07 <@Gynvael> rzucmy okiem na funkcje clear_flag
22:08 <@Gynvael> clear_flag:
22:08 <@Gynvael> mov eax, 10
22:08 <@Gynvael> test eax, eax
22:08 <@Gynvael> ret
22:08 <@Gynvael> jak widac nic specialnego tu nie ma
22:08 <@Gynvael> mov eax, 10
22:08 <@Gynvael> test 10, 10 (z tego wychodzi 10, wiec ZF = 0)
22:09 <@Gynvael> i powrot do funkcji wywolujacej
22:09 <@Gynvael> ;>
22:09 <@Gynvael> nastepnie w testach jest wywolanie call my_puts
22:09 <@Gynvael> ale o tym chcial bym na koncu
22:09 <@Gynvael> wiec rzucmy okiem na nastepne wywolanie
22:09 <@Gynvael> call test_zf
22:09 <@Gynvael> funkcja test_zf jest troche dluzsza
22:09 <@Gynvael> test_zf:
22:09 <@Gynvael> jz zf_set ; jesli ZF == 1, idz do zf_set
22:09 <@Gynvael> lea eax, [string_ZF_clear] ; wypisz ze clear
22:09 <@Gynvael> call my_puts
22:10 <@Gynvael> ret
22:10 <@Gynvael> zf_set:
22:10 <@Gynvael> lea eax, [string_ZF_set] ; wypisz ze set
22:10 <@Gynvael> call my_puts
22:10 <@Gynvael> ret
22:10 <@Gynvael> jak widac pojawia sie tu skok warunkowy jz
22:10 <@Gynvael> czyli skocz jesli ZF == 1
22:10 <@Gynvael> zalozmy ze ZF == 1
22:10 <@phoenix__> ide cya
22:10 <@Gynvael> pa
22:10 <@Gynvael> ;>
22:10 <@Gynvael> wtedy zostanie wykonany skok jz zf_set
22:10 <@Gynvael> zf_set:
22:11 <@Gynvael> lea eax, [string_ZF_set]
22:11 <@phoenix__> Gynvael: jak z logami ty wrzucisz czy wyslesz?
22:11 <@Gynvael> phoenix__wrzuce
22:11 <@Gynvael> call my_puts
22:11 <@Gynvael> ret
22:11 <@Gynvael> czyli do eax laduja adres stringu string_ZF_set: db 'Zero Flag == 1 (set)', 10, 0
22:11 <@phoenix__> tyo lece
22:11 <@Gynvael> po czym jest wywolywana funkcja my_puts, ktora jak sie domyslamy/wiadomo wypisuje cos na stdout
22:12 <@Gynvael> a potem nastepuje powrot do funkcji wywolujacej
22:12 <@Gynvael> gdyby jednak ZF == 0
22:12 <@Gynvael> wtedy jz zf_set NIE WYKONAL BY SIE
22:12 <@Gynvael> wiec zostalo by wykonane
22:12 <@Gynvael> lea eax, [string_ZF_clear] ; wypisz ze clear
22:12 <@Gynvael> call my_puts
22:12 <@Gynvael> ret
22:12 <@Gynvael> czyli wypisanie na stdout stirngu
22:12 <@Gynvael> string_ZF_clear: db 'Zero Flag == 0 (clear)', 10, 0
22:13 <@Gynvael> ok
22:13 <@Gynvael> tyle jesli chodzi o ta funkcje
22:13 <@Gynvael> teraz najdluzsza funkcja
22:13 <@Gynvael> czyli my_puts
22:13 <@Gynvael> pare slow wogole o funkcjach w asmie
22:14 <@Gynvael> funkcje wywoluje sie za pomoca call
22:14 <@Gynvael> tutaj nie ma co myslec, bo inaczej jest po prostu glupio ;>
22:14 <@Gynvael> natomiast problemem moze byc przekazanie parametrow funkcji
22:14 <@Gynvael> programisci nazwali cala impreze dotyczaca wywolywania funkcji konwencja wywolan
22:15 <@Gynvael> dla anglojezycznych, polecam poczytac
22:15 <@Gynvael> http://gynvael.lunarii.org/temp/calling_conventions.pdf
22:15 <@Gynvael> powiem w skrocie
22:15 <@Gynvael> konwencji najczesciej uzywanych jest pare
22:15 <@Gynvael> 1) cdecl - defaultowa konwencja wywolywania dla kompilatorow typu gcc ;>
22:16 <@Gynvael> parametry funkcji ida na stos (od ostatniego do pierwszego)
22:16 <@Gynvael> potem nastepuje wywolanie
22:16 <@Gynvael> a po powrocie nastepuje usuniecie parametrow ze stosu
22:16 <@Gynvael> przykladowo funkcja wywolana w C tak: asdf(1,2,3,4) zostanie na asma przetlumaczona:
22:16 <@Gynvael> push 4
22:16 <@Gynvael> push 3
22:16 <@Gynvael> push 2
22:16 <@Gynvael> push 1
22:16 <@Gynvael> call asdf
22:16 <@Gynvael> add esp, 0x10
22:17 <@Gynvael> jak widac usuniecie robi sie po prostu korygujac adres ostatniego elementu na stosie (o stosie dokladneij bedzie za chwile)
22:17 <@Gynvael> (powiem tylko ze kazdy parametr to 4 bajty, 4*4 to 16, czyli 0x10)
22:18 <@Gynvael> 2) stdcall - z tego korzystaja glownie wywolania WinAPI ;> jest to bardzo podobny pomysl do cdecl, z jedna roznica, ze FUNKCJA CZYSCI STOS PO PARAMETRACH
22:18 <@Gynvael> czyli asdf(1,2,3,4) bylo by zapisane
22:18 <@Gynvael> push 4
22:18 <@Gynvael> push 3
22:18 <@Gynvael> push 2
22:18 <@Gynvael> push 1
22:18 <@Gynvael> call asdf
22:18 <@Gynvael> i tyle
22:18 <@Gynvael> (na koncu takich funkcji zamiast ret mozna znalezc np retn 0x10)
22:19 <@Gynvael> (to jest powrot z usunieciem ze stosu podanej liczby bajtow
22:19 <@Gynvael> (czyli takie.. pop adres_powrotu, add esp, podana_ilosc, jmp adres_powrotu)
22:20 <@Gynvael> 3) fastcall - na to w sumie kazdy kompilator ma wlasna koncepcje.. ale ogolnie pomysl jest taki zeby co sie da upchnac do rejestrow procka/koprocesora, a reszte na stos ;>
22:20 <@Gynvael> kazdy kompilator w sumie inne rejestry sobie wybiera i inaczej nimi zarzadza
22:20 <@Gynvael> (polecam ksiazke Desasemblowanie Programow, bodajze kasperskiego, tam jest porownanie wywolan dla roznych kompilatorow)
22:20 <@Gynvael> z pamieci podam przykald dla kompilatora WATCOM C
22:21 <@Gynvael> asdf(1,2,3,4) zostanie przetlumaczone na
22:21 <@Gynvael> mov eax, 1
22:21 <@Gynvael> mov edx, 2
22:21 <@Gynvael> mov ecx, 3
22:21 <@Gynvael> mov ebx, 4
22:21 <@Gynvael> call asdf
22:21 <@Gynvael> jest jeszcze pare innych konwencji wywolania
22:21 <@Gynvael> ale one sa rzadziej stosowane ;>
22:22 <@Gynvael> (np pascal ktory jest prawie ze stdcall, z tym ze parametry wrzucane sa od przodu, nie od tylu ;>)
22:22 <@Gynvael> (np C++ ma swoje thiscall ;>)
22:22 <@Gynvael> ale ok, wracamy do tego mojego programu
22:22 <@Gynvael> w tym wypadku uzylem fastcalla prawie ze
22:23 <@Gynvael> czyli wrzucilem adres stringu do eax ;>
22:23 <@Gynvael> lea eax, [string_ZF_set] ; wypisz ze set
22:23 <@Gynvael> call my_puts
22:23 <@Gynvael> etc
22:23 <@Gynvael> teraz tak
22:23 <@Gynvael> ogolnie jak dziala ta funkcja
22:23 <@Gynvael> czesciowo ja znacie z hello world, etc
22:23 <@Gynvael> ale pojawia sie tam tez jakas dzika instrukcja ;>
22:23 <@Gynvael> ogolne dzialanie wyglada tak
22:23 <@Gynvael> 1) utworzenie ramki stosu
22:24 <@Gynvael> 2) wrzucenie wartosci rejestrow na stos
22:24 <@Gynvael> 3) wyliczenie dlugosci napisu
22:24 <@Gynvael> 4) wypisanie do na stdout
22:24 <@Gynvael> 5) pobranie zarchiwizowanych rejestrow ze stosu
22:24 <@Gynvael> 6) usuniecie ramki stosu i wyjscie
22:24 <@Gynvael> ok
22:24 <@Gynvael> teraz 3 slowa o stosie
22:25 <@Gynvael> to wazne wiec lepiej zebyscie to sobie zapamietali ;ppp
22:25 <@Gynvael> mowilem juz ze stos jest jak stos monet
22:25 <@Gynvael> jak cos na niego wrzucasz, to na gore
22:25 <@Gynvael> a jak zdejmujesz, to tez z gory
22:25 <@Gynvael> mozesz zobaczyc co jest wsrodku
22:25 <@Gynvael> ale nie mozna tego wyjac
22:25 <@Gynvael> ;>
22:26 <@Gynvael> teraz tak
22:26 <@Gynvael> stos jest fragmentem pamieci
22:26 <@Gynvael> do ktorej mamy dostem: read (do odczytu), write (do zapisu) i (o zgrozo) na niektorych (wiekszosci) systemach execute (wykonaj)
22:26 <@Gynvael> jak by ktos pytal was "a po co execute" (czyli wykonywanie kodu asma)
22:26 <@Gynvael> to mozecie smialo powiedziec
22:27 <@Gynvael> "zeby hackerzy mieli gdzie exploity wsadzac" ;ppppppp
22:27 <@Gynvael> (szczerze, innego zastosowania nie znalazlem ;pp)
22:27 <@Gynvael> ok
22:27 <@Gynvael> teraz tak
22:27 <@Gynvael> mamy sobie rejestr ESP
22:27 <@Gynvael> SP == stack pointer
22:27 <@Gynvael> (E od extended ;p)
22:27 <@Gynvael> jest to ADRES OSTATNIEGO ELEMENTU NA STOSIE
22:28 <@Gynvael> elementy na stosie to 4robajtowe itemki
22:28 <@Gynvael> KAZDY element stosu ma MINIMUM 4 bajty
22:28 <@Gynvael> teraz tak
22:28 <@Gynvael> stos "rosnie" w kierunku zera
22:28 <@Gynvael> czyli
22:29 <@Gynvael> zalozmy ze ESP na poczatku = 0x1000
22:29 <@Gynvael> to po push 1234 ESP przesunie sie na adres 0x0ffc
22:29 <@Gynvael> po koleinym pushu albo callu o kolejne 4 bajty (0x0ff8, potem 0x0ff4 potem 0x0ff0 etc)
22:29 <@Gynvael> natomiast przy zdejmowaniu ze stosu (pop) do ESP dodawane sa 4ry bajty ;>
22:30 <@Gynvael> przeanalizujmy taki przyklado
22:30 <@Gynvael> ESP = 0x1000: STOS: ESP -> pusto
22:30 <@Gynvael> push 1234
22:30 <@Gynvael> ESP = 0xffc: STOS: ESP -> [1024]
22:30 <@Gynvael> ESP = 0xffc: STOS: ESP -> [1324]
22:30 <@Gynvael> eh
22:30 <@Gynvael> ESP = 0xffc: STOS: ESP -> [1234]
22:30 <@Gynvael> ja mam jakeis zboczenie.. zamiast 1234 pisac 1024 --;
22:30 <@Gynvael> push 4321
22:31 <@Gynvael> ESP = 0xff8: STOS: ESP -> [4321]
22:31 <@Gynvael> push 0
22:31 <@Gynvael> ESP = 0xff4: STOS: ESP -> [0] [4321] [1234]
22:31 <@Gynvael> (no, tka jest, 3 itemy na stosie)
22:31 <@Gynvael> i teraz tak
22:31 <@Gynvael> mozemy ze stosu sciagnac jeden item (to [0] jest na wierzchu)
22:31 <@Gynvael> robimy wtedy
22:31 <@Gynvael> pop REJESTR_DOCELOWY
22:31 <@Gynvael> np
22:31 <@Gynvael> pop ecx
22:32 <@Gynvael> wtedy do ecx zostanie wrzucone 0 (bo to jest na wierzchu stosu)
22:32 <@Gynvael> a na stosie zostanie:
22:32 <@Gynvael> ESP = 0xff8: STOS: ESP -> [4321] [1234]
22:32 <@Gynvael> natomiast mozemy rowniez odczytac cos ze stosu
22:32 <@Gynvael> na przyklad:
22:32 <@Gynvael> mov ecx, [0xff8]
22:32 <@Gynvael> wtedy do ecx zostanie wrzucone TO CO JEST POD ADRESEM 0xff8 (a to jest rzecz na wierzchu stosu, czyli 4321)
22:33 <@Gynvael> (ecx ma 4 bajty (32 bity) wiec zostanie przerzucone tam 4 bajty ;>)
22:33 <@Gynvael> (czyli caly item)
22:33 <@Gynvael> ale..
22:33 <@Gynvael> lepiej napisac
22:33 <@Gynvael> mov ecx, [esp]
22:33 <@Gynvael> albo jesli np chcemy druga rzecz ze stosu
22:33 <@Gynvael> mov ecx, [esp+4]
22:33 <@Gynvael> etc
22:34 <@Gynvael> ok
22:34 <@Gynvael> mam nadzieje ze mniej wiecej wiadomo jak stos dziala
22:34 <@Gynvael> teraz tak
22:34 <@Gynvael> jest takie cos jak EBP
22:34 <@Gynvael> (base pointer?)
22:34 <@Gynvael> jest to rejestr jak kazdy inny
22:34 <@Gynvael> a w8
22:34 <@Gynvael> dostalem dobre pytanie na privie
22:34 <@Gynvael> G0blin zapytal czy mozna esp ustawic jakos nieregularnie
22:35 <@Gynvael> tak, mozna
22:35 <@Gynvael> mozna np ustawic ESP na adres swojej sekcji danych (jesli np mamy tam troche wolnego miejsca)
22:35 <@Gynvael> co to da ? no przedewszystkim sekcja danych nie ma tego piepszonego atrybutu wykonywalnej pamieci
22:35 <@Gynvael> wiec na pewno haxxorom popsujemy szyki jesli by chcieli psuc nam progsa ;>
22:36 <@Gynvael> mozna rowniez wykorzystywac ESP i zasade dzialania stosu do zupelnie zwariowanych rzeczy
22:36 <@Gynvael> np moj wykladowca z logiki algorytmicznej mowil kiedys ze za czasow baaaaaaardzo slabych kompow
22:36 <@Gynvael> byl problem w grach z czysczeniem ekranu
22:37 <@Gynvael> po prostu mov [adres], 0 * wielkosc ekranu bylo bardzo wolne (ofc mowie o petli)
22:37 <@Gynvael> i wkoncu jakis hacker wpadl na pomysl zrobienia:
22:37 <@Gynvael> mov esp, adres_konca_ekranu
22:37 <@Gynvael> push 0 * wielkosc ekranu
22:37 <@Gynvael> mov esp, stary adres stosu
22:37 <@Gynvael> i okazalo sie ze to jest duzo szybsze ;>
22:37 <@Gynvael> (nie wiem czy to nadal jest szybsze na naszych kompach ;>)
22:38 <@Gynvael> ok
22:38 <@Gynvael> ale wracamy do EBP
22:38 <@Gynvael> EBP jest normalnym rejestrem
22:38 <@Gynvael> ale niektore kompilatory korzystaja z niego do stworzenia tzw ramki stosu
22:39 <@Gynvael> (podobno juz to zaczyna wychodzic z mody, powniewaz kompilatory pamietaja ile rzeczy jest na stosie i ESP im wystarcza...)
22:39 <@Gynvael> co to znaczy? chodzi mniej wiecej o to
22:39 <@Gynvael> ze przy wejsciu do funkcji robi sie:
22:39 <@Gynvael> push ebp - wrzucenie adresu starej ramki na stos
22:39 <@Gynvael> mov ebp, esp - wrzucenie do ebp adresu aktualnego itemu na stosie (czyli starego ebp)
22:40 <@Gynvael> i nastepnie z esp mozna robic co sie chce
22:40 <@Gynvael> a dzieki ebp mozna adresowac parametry funkcji i zmienne lokalne
22:40 <@Gynvael> przykladowo mov ecx, [ebp+8] <=- to jest przeniesienie do ecx 1szego parametru funkcji
22:41 <@Gynvael> po stworzeniu takiej ramki standardowej funkcji typu stdcall czy cdecl, stos wyglada tak
22:41 <@Gynvael> np wezmy to nasze
22:41 <@Gynvael> asdf(1,2,3,4)
22:41 <@Gynvael> stos po stworzeniu ramki:
22:41 <@Gynvael> ESP, EBP -> [STARA_RAMKA_STOSU] [adres RET (call wrzucil)] [1] [2] [3] [4]
22:42 <@Gynvael> dzieki temu zabiegowi (stworzneiu ramki) esp mozemy sobie przesunac gdzies, powrzucac rozne rzeczy na stos etc
22:42 <@Gynvael> i nie martwic sie ze pierwszy parametr teraz to [esp+8] a po push ecx, to jakeis [esp+0xc]
22:42 <@Gynvael> zniszczenie ramki stosu zazwyczaj odbywa sie tak:
22:43 <@Gynvael> mov esp, ebp
22:43 <@Gynvael> pop ebp
22:43 <@Gynvael> czyli przesuniecie esp na ebp (bo tam bylo przy tworzneiu)
22:43 <@Gynvael> i pobranie ze stosu starej ramki stosu (starej == poprzedniej funkcji)
22:44 <@Gynvael> dokladnie to samo co te 2 insturkcje wczesniej robi insturckja leave
22:44 <@Gynvael> ktora mozna zobaczyc na koncu tej mojej funkcji tam
22:44 <@Gynvael> (do tworzenia ramki jest niby instruckja enter, ale nikt jej nie lubi ;p)
22:45 <@Gynvael> ok
22:45 <@Gynvael> tyle o stosach i ramkach
22:45 <@Gynvael> teraz bierzemy sie za ta funkcje
22:45 <@Gynvael> push ebp ; zachowanie starej ramki stosu na stosie
22:45 <@Gynvael> mov ebp, esp ; utworzenie nowej w aktualnym miejscu stosu
22:45 <@Gynvael> o tym mowilem
22:46 <@Gynvael> nastepnie jest krotki komentarz o tym zeby zachowac rejestry ktore sie zmienia
22:46 <@Gynvael> to dobry nawyk..
22:46 <@Gynvael> bo inaczej potem mozna sie zastanawiac czemu wszystko jest ok, a jak dodalismy call to nagle sie wali ;>
22:46 <@Gynvael> (w tym docu o konwencji wywolan powinno pisac ktore rejestry musza byc zapamietywane)
22:46 <@Gynvael> teraz tak, rejestr EAX nie musi byc zapamietany
22:46 <@Gynvael> rejestr EAX JEST UZYWANY ZAZWYCZAJ DO ZWROCENIA WARTOSCI
22:47 <@Gynvael> przykladowo w C jak mamy return 1234;
22:47 <@Gynvael> to to jest nic innego tylko: mov eax, 1234, jmp koniec_funkcji
22:48 <@Gynvael> wiec potem jest seria push'y ;>
22:48 <@Gynvael> na stos wrzucam sobie stare wartosci rejestrow
22:48 <@Gynvael> stos to calkiem dobra przechowalnia
22:48 <@Gynvael> ;>
22:48 <@Gynvael> i dobre miejsce na zmienne lokalne
22:48 <@Gynvael> ;>
22:48 <@Gynvael> potem mamy takie cos:
22:48 <@Gynvael> mov edi, eax ; pobierz pierwszy argument funkcji
22:48 <@Gynvael> mov esi, edi ; esi = edi
22:48 <@Gynvael> xor eax, eax ; eax = 0
22:49 <@Gynvael> mov ecx, eax ; ecx = 0
22:49 <@Gynvael> not ecx ; ecx = ~ecx (czyli ecx = 0xffffffff)
22:49 <@Gynvael> not zamienia wszystkei bity 1 na 0, i vice versa
22:49 <@Gynvael> to takie xor ecx, 0xffffffff
22:50 <@Gynvael> repnz scasb ; while( *(char*)edi != al && ecx != 0 ) edi++, ecx--;
22:50 <@Gynvael> no wlasnie
22:50 <@Gynvael> to sa w sumie 2 oddzielne insturkcje
22:50 <@Gynvael> repnz <=- POWTARZAJ NASTEPNA INSTRUCJE DOPOKI "nz" ...
22:50 <@Gynvael> nz znamy z jnz.. czyli if not ZF.. czyli
22:50 <@Gynvael> powtarzaj nastepna instruckje dopooki ZF == 0
22:52 <@Gynvael> scasb
22:52 <@Gynvael> to cudo robi cos takiego:
22:53 <@Gynvael> sprawdza czy BAJT na ktory wskazuje edi nie jest rowny zawartosci rejestru al (to dolna czesc rejestru EAX)
22:53 <@Gynvael> jesli rowne, ZF = 1
22:53 <@Gynvael> jesli rozne, ZF = 0
22:53 <@Gynvael> po czym...
22:53 <@Gynvael> robi edi++
22:53 <@Gynvael> i ecx--
22:53 <@Gynvael> aa zapomnialem dodac ze wczesniej sprawdza czy ecx=0
22:54 <@Gynvael> scasb == SCAN BYTE
22:54 <@Gynvael> czyli w sumie "szukaj okreslonego bajtu"
22:54 <@Gynvael> w duzym skrocie
22:54 <@Gynvael> jesli w al mamy 0 (a mamy, patrz xor eax, eax)
22:55 <@Gynvael> to repnz scasb bedzie wykonywane DOPOKI EDI NIE BEDZIE POKAZYWAC NA 0
22:56 <@Gynvael> czyli w skrocie, korzystam z tego zeby edi przesunac na koniec stringa
22:56 <@Gynvael> mam teraz w ESI adres poczatku stringa
22:56 <@Gynvael> i w EDI adres konca
22:56 <@Gynvael> po odjeciu ich dostaje dlugosc stringa
22:56 <@Gynvael> logiczne ne ?;p
22:56 <@Gynvael> mov edx, edi ; edx = edi
22:56 <@Gynvael> sub edx, esi ; edx -= esi
22:56 <@Gynvael> czyli w edx mam dlugosc stringa
22:56 <@Gynvael> ;>
22:56 <@Gynvael> po tym nastepuje wywolanie znanego juz sys_write
22:56 <@Gynvael> (nop po int 80h jest tylko i wylacznie do celow debuggowych, nic nie robi)
22:57 <@Gynvael> potem przywracam rejestry (pop)
22:57 <@Gynvael> a potem leave i ret
22:57 <@Gynvael> tyle
22:57 <@Gynvael> teraz jeszcze jedno uzupelnienie
22:57 <@Gynvael> mowilem ze kazdy element na stosie ma 4 bajty
22:57 <@Gynvael> (min. 4 bajty, ale zawsze iloczyn 4rki)
22:58 <@Gynvael> to znaczy ze
22:58 <@Gynvael> jesli robimy np
22:58 <@Gynvael> mov al, 1
22:58 <@Gynvael> push al
22:58 <@Gynvael> jak pamietacie al ma jeden bajt
22:58 <@Gynvael> to 3 pozostale bajty beda DOPELNIONE ZERAMI na stosie
22:58 <@Gynvael> natomiast jesli wrzucam cos wiekszego
22:58 <@Gynvael> np jakis quadword
22:58 <@Gynvael> to on po prostu zajmuje dwa miejsca na stosie i juz
22:58 <@Gynvael> (double etc nei sa przekazywane przez stos, raczej przez stos koprocesora, etc)
22:59 <@Gynvael> ok
22:59 <@Gynvael> pytanka ?
22:59 <@Gynvael> czy wszyscy zasneli ?;p
22:59 < java> nie
22:59 < java> :D
22:59 < mag7> :p
22:59 <@Gynvael> ohoh ;>
22:59 < mag7> Gynvael: ty to sie napiszesz
22:59 < mag7> D:
22:59 <@Gynvael> ano
22:59 < lav> do czego sluzy funkcja execute stosu
23:00 < lav> ?
23:00 < lav> skoro jest niebezpieczna
23:00 <@Gynvael> ok
23:00 <@Gynvael> lav zadal dobre pytanie
23:00 <@Gynvael> w sumie nie funkcja a flaga
23:00 <@Gynvael> kazdy fragment pamieci ma pare dopuszczalnych "TRYBOW"
23:00 <@Gynvael> sa to (miedzy innymi)
23:00 <@Gynvael> - do odczytu - mozna stamtad czytac dane
23:00 <@Gynvael> - do zapisu - mozna tam zapisywac dane
23:01 <@Gynvael> - wykonanie - mozna wykonywac kod w tym fragmencie
23:01 <@Gynvael> przykladowo sekcja kodu ma flagi "do odczytu" i "wykonanie"
23:01 <@Gynvael> a sekcja danych "do odczytu" i "do zapisu"
23:01 <@Gynvael> to znaczy ze jesli bysmy do sekcji kodu cos chcieli zapisac
23:01 <@Gynvael> to system zabije nam program ;ppp
23:01 <@Gynvael> a jesli bysmy w sekcji danych chcieli cos uruchomic
23:01 <@Gynvael> np zrobic jmp dane
23:01 <@Gynvael> albo call dane
23:01 <@Gynvael> to system rowniez zanije nam program
23:03 <@Gynvael> i teraz tak
23:03 <@Gynvael> no to moze dam przykladzic haxxorski
23:03 <@Gynvael> mamy w C program:
23:04 <@Gynvael> char imie[16];
23:04 <@Gynvael> gets(imie);
23:04 <@Gynvael> taki bardzo prosty
23:04 <@Gynvael> gets jak wiadomo pobiera cos z stdin i zapisuje do imie
23:04 <@Gynvael> z tym ze gets nie zna wielkosci imie
23:04 <@Gynvael> imie jest zmienna lokalna
23:04 <@Gynvael> wiec jest na stosie
23:04 <@Gynvael> mniej wiecej to wyglada tak:
23:05 <@Gynvael> ESP -> [ i ] [ m ] [ i ] [ e ] (EBP ->) [stare ebp] [ret] ...
23:05 <@Gynvael> imie ma 16 bajtow wiec zajmuje 4 miejsca na stosie
23:05 <@Gynvael> ;>
23:05 <@Gynvael> powiedzmy ze jestem Michal
23:05 <@Gynvael> wiec sie w 16 bajtach zmieszcze
23:05 <@Gynvael> ale jak bym napisal np 64 razy "a"
23:06 <@Gynvael> to na pewno na stosie wszystko lacznie z ret zostanie nadpisane
23:06 <@Gynvael> a zalozmy ze jestem pan-zly-haxxor
23:06 <@Gynvael> i bede cwany
23:06 <@Gynvael> wrzuce sobie literki aaaa az do ret
23:06 <@Gynvael> zamiast adresu ret wrzuce adres stosu po RET (to mozna empirycznie znalezc)
23:06 <@Gynvael> a po tym wrzuce jakis ZLY KOD ;>
23:07 <@Gynvael> tzn skompilowany kod asma, np taki ktory wypisuje Zzzz i wychodzi
23:07 <@Gynvael> czyli na stosie bedzie
23:07 <@Gynvael> ESP -> [aaaa] [aaaa] [aaaa] [aaaa] (EBP ->) [aaaa] [adres tego->] [od tad zly kod]
23:07 <@Gynvael> wiec funkcja w momencie "ret" pojdzie nie do funkcji ktora zrobila "call"
23:08 <@Gynvael> tylko wlasnie do zlego kodu pana haxxora
23:08 <@Gynvael> jako ze stos ma prawa wykonania kodu
23:08 <@Gynvael> to kod sie wykona
23:08 <@Gynvael> i pan haxxor zrobi to co chcial
23:08 <@Gynvael> (a co jesli chcial nam dysk sformatowac ?;p albo nasz proces byl suidowalny i pan haxxor sobie konsolke roota odpalil ?;p)
23:08 <@Gynvael> natomiast jesli by nie bylo tego prawa do wykonania na stosie
23:09 <@Gynvael> to proces po "ret" by zostal zabity przez system
23:09 <@Gynvael> wiec szkodliwy kod pana haxxora by sie nie wykonal
23:09 <@Gynvael> i pan hax by nie przejal nam systemu ;>
23:10 <@Gynvael> jako przyklad tego z opisem
23:11 <@Gynvael> moge dac np zadanko z konkursu PROIDEA (ktory byl najgorzej zorganizowanym konkursem jaki widzialem)
23:11 <@Gynvael> http://gynvael.lunarii.org/temp/etap1_zad2.txt
23:11 <@Gynvael> pytania ?
23:11 < java> nie
23:11 < java> :D
23:11 <@Gynvael> o tym jeszcze bedzie btw
23:11 <@Gynvael> za iles tam wykaldow
23:11 < java> yhy
23:11 < G0blin> Gynvael: duzo jeszcze na dzis? :P
23:12 < G0blin> nie chce byc nie mily....
23:12 <@Gynvael> w sumie konczymy
23:12 <@Gynvael> ;>
23:12 < G0blin> ale jestem zmeczony :P
23:12 <@Gynvael> ok
23:12 <@Gynvael> jak brak pytan
23:12 <@Gynvael> to chcial bym zakonczyc dzisiejszy wyklad ;>
23:12 <@Gynvael> i podziekowac wytrwalym za udzial ;>
23:12 < naruto> :]
23:12 <@Gynvael> end ;>