Zdobądź wiedzę godną hakera!
* Co trzeba wiedzieć, aby być hakerem?
* Jak łamać hasła?
* Jak uzyskać dostęp do zabezpieczonej sieci bezprzewodowej?
Słowo haker kojarzy nam się z komputerowym mistrzem manipulacji Kevinem Mitnickiem. Pojęcie to jednak ewoluowało od czasu jego spektakularnych akcji. Zatem kim jest dziś haker? Wbrew obiegowej opinii, większość hakerów nie wykorzystuje swej wiedzy do niecnych celów. Dzięki swej wiedzy i dociekliwości przyczyniają się do rozwoju bezpieczeństwa sieci i programów. Nikt bowiem nie potrafi tak jak oni zgłębiać sposoby działania zaawansowanego oprogramowania i sprzętu i tropić luki pozwalające na atak lub wyciek danych oraz przewidywać możliwe problemy w ich działaniu.
Jon Erickson w książce Hacking. Sztuka penetracji. Wydanie II omawia te zagadnienia ze świata informatyki, które nie mogą być obce hakerowi. Dzięki tej książce poznasz m.in. podstawy języka C oraz dowiesz się, jak wykorzystać jego słabości oraz potknięcia programistów piszących w tym języku. Zapoznasz się z podstawami funkcjonowania sieci i zdobędziesz wiedzę o modelu OSI, a także nauczysz się podsłuchiwać transmitowane w sieci dane, skanować porty i łamać hasła.
* Programowanie w języku C
* Model OSI
* Podsłuchiwanie sieci
* Skanowanie portów
* Sposoby łamania haseł
* Szyfrowanie danych i połączeń
* Sposoby atakowania sieci bezprzewodowych
Oto elementarz prawdziwego hakera!
Książkę poleca dystrybutor programów antywirusowych:
1. Hacking. Sztuka
penetracji. Wydanie II
Autor: Jon Erickson
T³umaczenie: Marcin Rogó¿
ISBN: 978-83-246-1802-6
Tytu³ orygina³u: Hacking:
The Art of Exploitation, 2nd Edition
Format: 170x230, stron: 520
Zdob¹dŸ wiedzê godn¹ hakera!
• Co trzeba wiedzieæ, aby byæ hakerem?
• Jak ³amaæ has³a?
• Jak uzyskaæ dostêp do zabezpieczonej sieci bezprzewodowej?
S³owo haker kojarzy nam siê z komputerowym mistrzem manipulacji Kevinem
Mitnickiem. Pojêcie to jednak ewoluowa³o od czasu jego spektakularnych akcji. Zatem
kim jest dziœ haker? Wbrew obiegowej opinii, wiêkszoœæ hakerów nie wykorzystuje swej
wiedzy do niecnych celów. Dziêki swej wiedzy i dociekliwoœci przyczyniaj¹ siê do
rozwoju bezpieczeñstwa sieci i programów. Nikt bowiem nie potrafi tak jak oni zg³êbiaæ
sposoby dzia³ania zaawansowanego oprogramowania i sprzêtu i tropiæ luki pozwalaj¹ce
na atak lub wyciek danych oraz przewidywaæ mo¿liwe problemy w ich dzia³aniu.
Jon Erickson w ksi¹¿ce Hacking. Sztuka penetracji. Wydanie II omawia te zagadnienia
ze œwiata informatyki, które nie mog¹ byæ obce hakerowi. Dziêki tej ksi¹¿ce poznasz
m.in. podstawy jêzyka C oraz dowiesz siê, jak wykorzystaæ jego s³aboœci
oraz potkniêcia programistów pisz¹cych w tym jêzyku. Zapoznasz siê z podstawami
funkcjonowania sieci i zdobêdziesz wiedzê o modelu OSI, a tak¿e nauczysz siê
pods³uchiwaæ transmitowane w sieci dane, skanowaæ porty i ³amaæ has³a.
• Programowanie w jêzyku C
• Model OSI
• Pods³uchiwanie sieci
• Skanowanie portów
• Sposoby ³amania hase³
• Szyfrowanie danych i po³¹czeñ
• Sposoby atakowania sieci bezprzewodowych
Wydawnictwo Helion
Oto elementarz prawdziwego hakera!
ul. Koœciuszki 1c
44-100 Gliwice
tel. 032 230 98 63
e-mail: helion@helion.pl
2. SPIS TRE¥CI
PRZE DMOWA 11
P ODZI} KO W ANI A 12
0X100 WPROWAD ZENIE 13
0X200 PR OGR AMOWANIE 19
0x210 Istota programowania .....................................................................................20
0x220 Pseudokod ......................................................................................................22
0x230 Struktury sterujÈce ...........................................................................................22
0x231 If-Then-Else ........................................................................................22
0x232 PÚtle While/Until ................................................................................24
0x233 PÚtle for .............................................................................................25
0x240 Podstawowe pojÚcia programistyczne .............................................................26
0x241 Zmienne .............................................................................................26
0x242 Operatory arytmetyczne .....................................................................27
0x243 Operatory porównania .......................................................................28
0x244 Funkcje ...............................................................................................30
0x250 Zaczynamy brudziÊ sobie rÚce ..........................................................................34
0x251 WiÚkszy obraz ....................................................................................35
0x252 Procesor x86 ......................................................................................38
0x253 JÚzyk asembler ...................................................................................40
0x260 Wracamy do podstaw .....................................................................................53
0x261 ’añcuchy ............................................................................................53
0x262 Ze znakiem, bez znaku, dïuga i krótka ................................................57
0x263 Wska niki ...........................................................................................59
0x264 ’añcuchy formatujÈce .........................................................................63
3. 0x265 Rzutowanie typów .............................................................................67
0x266 Argumenty wiersza poleceñ ...............................................................74
0x267 ZasiÚg zmiennych ...............................................................................78
0x270 Segmentacja pamiÚci .......................................................................................85
0x271 Segmenty pamiÚci w jÚzyku C ............................................................92
0x272 Korzystanie ze sterty ...........................................................................94
0x273 Funkcja malloc() ze sprawdzaniem bïÚdów .........................................96
0x280 Budujemy na podstawach ...............................................................................98
0x281 DostÚp do plików ...............................................................................98
0x282 Prawa dostÚpu do plików .................................................................104
0x283 Identyfikatory u ytkowników ............................................................105
0x284 Struktury ..........................................................................................114
0x285 Wska niki do funkcji .........................................................................117
0x286 Liczby pseudolosowe ........................................................................118
0x287 Gry hazardowe .................................................................................120
0X300 NADU¿YCI A 133
0x310 Uogólnione techniki nadu yÊ .........................................................................136
0x320 Przepeïnienia bufora ......................................................................................137
0x321 Luki zwiÈzane z przepeïnieniem stosowym .......................................140
0x330 Eksperymenty z BASH ....................................................................................152
0x331 Wykorzystanie rodowiska ...............................................................161
0x340 Przepeïnienia w innych segmentach ..............................................................170
0x341 Podstawowe przepeïnienie na stercie ...............................................170
0x342 Przepeïnienia wska ników funkcyjnych .............................................176
0x350 ’añcuchy formatujÈce ....................................................................................187
0x351 Parametry formatujÈce .....................................................................188
0x352 Podatno Ê ciÈgów formatujÈcych na ataki ........................................190
0x353 Odczyt spod dowolnych adresów pamiÚci ........................................192
0x354 Zapis pod dowolnymi adresami pamiÚci ...........................................193
0x355 Bezpo redni dostÚp do parametrów .................................................201
0x356 U ycie zapisu krótkich liczb caïkowitych ...........................................203
0x357 Obej cia z u yciem sekcji dtors .........................................................205
0x358 Kolejny sïaby punkt programu notesearch ........................................210
0x359 Nadpisywanie globalnej tabeli przesuniÚÊ ........................................212
0X400 SIEC I 217
0x410 Model OSI .....................................................................................................217
0x420 Gniazda .........................................................................................................220
0x421 Funkcje obsïugujÈce gniazda ............................................................221
0x422 Adresy gniazd ..................................................................................223
0x423 Sieciowa kolejno Ê bajtów ................................................................224
0x424 Konwersja adresu internetowego .....................................................225
6 Hacking. Sztuka penetracji
4. 0x425 Prosty przykïad serwera ....................................................................226
0x426 Przykïad klienta WWW .....................................................................230
0x427 Maleñki serwer WWW ......................................................................235
0x430 ZaglÈdamy do ni szych warstw .....................................................................240
0x431 Warstwa ïÈcza danych ......................................................................240
0x432 Warstwa sieci ...................................................................................242
0x433 Warstwa transportowa .....................................................................244
0x440 Podsïuchiwanie w sieci ..................................................................................247
0X441 Sniffer surowych pakietów ...............................................................249
0x442 Sniffer libpcap ..................................................................................251
0x443 Dekodowanie warstw ......................................................................253
0x444 Aktywne podsïuchiwanie .................................................................263
0x450 Odmowa usïugi .............................................................................................276
0x451 Zalew pakietów SYN (SYN Flooding) .................................................276
0x452 Atak Ping of Death ...........................................................................281
0x453 Atak Teardrop ..................................................................................281
0x454 Zalew pakietów ping (Ping Flooding) ................................................281
0x455 Atak ze wzmocnieniem (Amplification) .............................................282
0x456 Rozproszony atak DoS ......................................................................283
0x460 PrzejÚcie TCP/IP .............................................................................................283
0x461 RozïÈczanie za pomocÈ RST ..............................................................283
0x462 PrzejÚcie z kontynuacjÈ .....................................................................289
0x470 Skanowanie portów ......................................................................................289
0x471 Ukryte skanowanie SYN ....................................................................289
0x472 Skanowanie FIN, X-mas i Null ...........................................................290
0x473 Skanowanie z ukrycia .......................................................................290
0x474 Skanowanie z u yciem bezczynnego komputera ...............................291
0x475 Zabezpieczenie wyprzedzajÈce (Shroud) ...........................................293
0x480 Id i wïam siÚ gdzie .....................................................................................298
0x481 Analiza z u yciem GDB .....................................................................299
0x482 Prawie ma znaczenie ........................................................................301
0x483 Shellcode wiÈ Ècy port .....................................................................304
0X500 SHE LLCODE 309
0x510 Asembler a C .................................................................................................309
0x511 Linuksowe wywoïania systemowe w asemblerze .............................312
0x520 ¥cie ka do shellcode ......................................................................................315
0x521 Instrukcje asemblera wykorzystujÈce stos .........................................315
0x522 Badanie za pomocÈ GDB ..................................................................317
0x523 Usuwanie bajtów zerowych ..............................................................319
0x530 Kod wywoïujÈcy powïokÚ ..............................................................................324
0x531 Kwestia uprawnieñ ...........................................................................328
0x532 Skracamy jeszcze bardziej .................................................................331
Spis tre ci 7
5. 0x540 Kod powïoki wiÈ Ècy port .............................................................................332
0x541 Duplikowanie standardowych deskryptorów plików .........................336
0x542 RozgaïÚziajÈce struktury sterujÈce ....................................................338
0x550 Shellcode nawiÈzujÈcy poïÈczenie powrotne .................................................343
0X600 ¥R OD KI ZAPOBI EGAWC ZE 349
0x610 ¥rodki zapobiegawcze, które wykrywajÈ .......................................................350
0x620 Demony systemowe ......................................................................................351
0x621 Bïyskawiczne wprowadzenie do sygnaïów .......................................352
0x622 Demon tinyweb ................................................................................354
0x630 NarzÚdzia pracy .............................................................................................358
0x631 NarzÚdzie nadu ywajÈce tinywebd ...................................................359
0x640 Pliki dziennika ...............................................................................................364
0x641 Wmieszaj siÚ w tïum ........................................................................365
0x650 Przeoczywszy oczywiste ................................................................................366
0x651 Krok po kroku ...................................................................................367
0x652 Ponowne skïadanie wszystkiego ......................................................371
0x653 Robocze procesy potomne ................................................................377
0x660 Zaawansowana sztuka kamufla u .................................................................378
0x661 Faïszowanie rejestrowanego adresu IP .............................................379
0x662 Nierejestrowane nadu ycie ...............................................................383
0x670 Caïa infrastruktura .........................................................................................386
0x671 Ponowne wykorzystanie gniazda ......................................................386
0x680 Przemyt ïadunku ............................................................................................390
0x681 Kodowanie ïañcuchów .....................................................................391
0x682 Jak ukryÊ puïapkÚ? ...........................................................................394
0x690 Ograniczenia bufora ......................................................................................395
0x691 Polimorficzny kod powïoki z drukowalnymi znakami ASCII ...............397
0x6a0 Zabezpieczenia wzmacniajÈce .......................................................................408
0x6b0 Niewykonywalny stos ....................................................................................408
0x6b1 Powracanie do funkcji biblioteki libc .................................................409
0x6b2 Powrót do funkcji system() ...............................................................409
0x6c0 Randomizacja przestrzeni stosu .....................................................................411
0x6c1 Badanie za pomocÈ BASH i GDB .......................................................413
0x6c2 Odbijanie od linux-gate ....................................................................417
0x6c3 Wiedza stosowana ...........................................................................420
0x6c4 Pierwsza próba .................................................................................421
0x6c5 Gramy w ko ci ..................................................................................422
0X700 KRYP TOLOGIA 425
0x710 Teoria informacji ............................................................................................426
0x711 Bezwarunkowe bezpieczeñstwo .......................................................426
0x712 Szyfr z kluczem jednorazowym .........................................................426
8 Hacking. Sztuka penetracji
6. 0x713 Kwantowa dystrybucja kluczy ...........................................................427
0x714 Bezpieczeñstwo obliczeniowe ...........................................................428
0x720 Rozlegïo Ê algorytmów .................................................................................429
0x721 Notacja asymptotyczna .....................................................................430
0x730 Szyfrowanie symetryczne ...............................................................................430
0x731 Kwantowy algorytm przeszukiwania autorstwa Lova Grovera ...........432
0x740 Szyfrowanie asymetryczne .............................................................................432
0x741 RSA ..................................................................................................433
0x742 Kwantowy algorytm rozkïadu na czynniki autorstwa Petera Shora ....437
0x750 Szyfry hybrydowe ..........................................................................................438
0x751 Ataki z ukrytym po rednikiem ...........................................................439
0x752 „Odciski palców” komputerów w protokole SSH ...............................443
0x753 Rozmyte „odciski palców” ................................................................447
0x760 ’amanie haseï ...............................................................................................451
0x761 Ataki sïownikowe .............................................................................453
0x762 Ataki na zasadzie peïnego przeglÈdu ................................................455
0x763 Tablica wyszukiwania skrótów .........................................................457
0x764 Macierz prawdopodobieñstwa haseï ................................................457
0x770 Szyfrowanie w sieci bezprzewodowej 802.11b ..............................................467
0x771 Protokóï Wired Equivalent Privacy (WEP) ..........................................468
0x772 Szyfr strumieniowy RC4 ....................................................................469
0x780 Ataki na WEP ................................................................................................470
0x781 Ataki na zasadzie peïnego przeglÈdu w trybie offline .......................470
0x782 Ponowne u ycie strumienia klucza ....................................................471
0x783 Tablice sïownikowe z wektorami IV ..................................................472
0x784 Przekierowanie IP .............................................................................473
0x785 Atak Fluhrera, Mantina i Shamira (FMS) ............................................474
0X800 P ODSUMOWANIE 485
0x810 Bibliografia i dodatkowe informacje ..............................................................486
0x820 Kody ródïowe ..............................................................................................487
O P ’YC IE C D 489
SKOR OWID Z 491
Spis tre ci 9
7. 0x200
PROGRAMOWANIE
Haker to termin u ywany do okre lenia zarówno piszÈcych kod, jak
i tych, którzy wykorzystujÈ znajdujÈce siÚ w nim bïÚdy. ChoÊ obie
grupy hakerów majÈ ró ne cele, to i jedni, i drudzy stosujÈ podobne
techniki rozwiÈzywania problemów. A ze wzglÚdu na fakt, e umiejÚt-
no Ê programowania pomaga tym, którzy wykorzystujÈ bïÚdy w kodzie,
za zrozumienie metod zastosowania pomaga tym, którzy programujÈ,
wielu hakerów robi obie rzeczy jednocze nie. InteresujÈce rozwiÈzania istniejÈ
i w zakresie technik pisania eleganckiego kodu, i technik sïu Ècych do wykorzysty-
wania sïabo ci programów. Hakerstwo to w rzeczywisto ci akt znajdowania pomy-
sïowych i nieintuicyjnych rozwiÈzañ problemów.
Metody stosowane w narzÚdziach wykorzystujÈcych sïabo ci programów zwy-
kle sÈ zwiÈzane z zastosowaniem reguï funkcjonowania komputera w sposób, któ-
rego nigdy nie brano pod uwagÚ — pozornie magiczne rezultaty osiÈga siÚ zwykle
po skupieniu siÚ na omijaniu zaimplementowanych zabezpieczeñ. Metody u ywa-
ne podczas pisania programów sÈ podobne, bo równie wykorzystujÈ reguïy funk-
cjonowania komputera w nowy i pomysïowy sposób, jednak w tym przypadku koñco-
wym celem jest osiÈgniÚcie najbardziej wydajnego lub najkrótszego kodu ródïowego.
Istnieje nieskoñczenie wiele programów, które mo na napisaÊ w celu wykonania do-
wolnego zadania, jednak wiÚkszo Ê z istniejÈcych rozwiÈzañ jest niepotrzebnie
obszerna, zïo ona i niedopracowana. Pozostaïa niewielka ich liczba to programy
niedu e, wydajne i estetyczne. Ta wïa ciwo Ê programów nosi nazwÚ elegancji, za
przemy lane i pomysïowe rozwiÈzania prowadzÈce do takiej wydajno ci nazywane
sÈ sztuczkami (ang. hacks). Hakerzy stojÈcy po obu stronach metodyki programowania
doceniajÈ zarówno piÚkno eleganckiego kodu, jak i geniusz pomysïowych sztuczek.
8. W wiecie biznesu mniejszÈ wagÚ przykïada siÚ do przemy lanych metod pro-
gramowania i eleganckiego kodu, a wiÚkszÈ do tworzenia funkcjonalnego kodu w
jak najkrótszym czasie i jak najtaniej. Ze wzglÚdu na gwaïtowny wzrost mocy obli-
czeniowej oraz dostÚpno ci pamiÚci po wiÚcenie dodatkowych piÚciu godzin na
opracowanie nieco szybszego i mniej wymagajÈcego pod wzglÚdem pamiÚci frag-
mentu kodu po prostu nie opïaca siÚ, gdy mamy do czynienia z nowoczesnymi kom-
puterami dysponujÈcymi gigahercowymi cyklami procesora i gigabajtami pamiÚci.
Podczas gdy optymalizacje czasu i wykorzystania pamiÚci pozostajÈ niezauwa one
przez wiÚkszo Ê (z wyjÈtkiem wyrafinowanych) u ytkowników, nowa funkcjonal-
no Ê ma potencjaï marketingowy. Kiedy najwa niejszym kryterium sÈ pieniÈdze,
po wiÚcanie czasu na opracowywanie przemy lanych sztuczek optymalizacyjnych
nie ma po prostu sensu.
ElegancjÚ programowania pozostawiono hakerom, hobbistom komputerowym,
których celem nie jest zarabianie, lecz wyci niÚcie wszystkiego, co siÚ da, z ich sta-
rych maszyn Commodore 64, autorom programów wykorzystujÈcych bïÚdy, piszÈ-
cym niewielkie i niesamowite fragmenty kodu, dziÚki którym potrafiÈ przedostawaÊ
siÚ przez wÈskie szczeliny systemów zabezpieczeñ, oraz wszystkim innym doceniajÈ-
cym warto Ê szukania i znajdowania najlepszych mo liwych rozwiÈzañ. SÈ to osoby
zachwycajÈce siÚ mo liwo ciami oferowanymi przez programowanie i dostrzegajÈce
piÚkno eleganckich fragmentów kodu oraz geniusz przemy lanych sztuczek. Poznanie
istoty programowania jest niezbÚdne do zrozumienia, w jaki sposób mo na wykorzy-
stywaÊ sïabo ci programów, dlatego te programowanie to naturalny punkt wyj cia
dla naszych rozwa añ.
0x210 Istota programowania
Programowanie to pojÚcie niezmiernie naturalne i intuicyjne. Program jest tylko
seriÈ instrukcji zapisanych w okre lonym jÚzyku. Programy wystÚpujÈ wszÚdzie
i nawet technofoby codziennie korzystajÈ z programów. Opisywanie drogi dojazdu
w jakie miejsce, przepisy kulinarne, rozgrywki piïkarskie i spirale DNA to sÈ ró -
nego typu programy. Typowy „program” opisu dojazdu samochodem w konkretne
miejsce mo e wyglÈdaÊ tak:
Najpierw zjed w dó Alejæ Ko ciuszki, prowadzæcæ na wschód. Jed niæ
do momentu, kiedy zobaczysz po prawej stronie ko ció . Je eli ulica búdzie
zablokowana ze wzglúdu na prowadzone roboty, skrúè w prawo w ul. Moniuszki,
potem skrúè w lewo w ul. Prusa i w ko cu skrúè w prawo w ul. Orzeszkowej.
W przeciwnym razie mo esz po prostu kontynuowaè jazdú Alejæ Ko ciuszki
i skrúciè w ul. Orzeszkowej. Jed niæ i skrúè w ul. Docelowæ. Jed niæ
jakie 8 kilometrów — nasz dom búdzie siú znajdowa po prawej stronie.
Dok adny adres to ul. Docelowa 743.
Ka dy, kto zna jÚzyk polski, pojmie podane instrukcje i bÚdzie mógï kierowaÊ
siÚ nimi podczas jazdy. Bez wÈtpienia nie zapisano ich zbyt bïyskotliwie, ale sÈ jed-
noznaczne i zrozumiaïe.
20 0x200
9. Jednak komputer nie zna jÚzyka polskiego — rozumie jedynie jÚzyk maszynowy
(ang. machine language). W celu poinstruowania go, aby wykonaï pewnÈ czynno Ê,
instrukcjÚ tÚ nale y zapisaÊ w zrozumiaïym dla niego jÚzyku. Jednak e jÚzyk ma-
szynowy jest bardzo ezoteryczny i trudny do opanowania. Skïada siÚ z serii bitów
oraz bajtów i ró ni siÚ, w zale no ci od danej architektury komputerowej. Tak wiÚc
w celu napisania programu w jÚzyku maszynowym dla procesora z rodziny Intel x86
nale y okre liÊ warto Ê zwiÈzanÈ z ka dÈ instrukcjÈ, sposób interakcji poszczególnych
instrukcji oraz mnóstwo innych niskopoziomowych szczegóïów. Tego rodzaju pro-
gramowanie jest bardzo niewdziÚcznym oraz kïopotliwym zadaniem i z pewno ciÈ
nie jest intuicyjne.
Do pokonania komplikacji zwiÈzanych z pisaniem programów w jÚzyku maszy-
nowym potrzebny jest translator. JednÈ z form translatora jÚzyka maszynowego jest
asembler. To program, który tïumaczy jÚzyk asemblera na kod zapisany w jÚzyku
maszynowym. JÚzyk asemblera jest bardziej przystÚpny od kodu maszynowego,
poniewa dla ró nych instrukcji i zmiennych wykorzystuje nazwy (mnemoniki), a nie
same warto ci liczbowe. Jednak jÚzyk asemblera wciÈ jest daleki od intuicyjnego.
Nazwy instrukcji sÈ ezoteryczne, a sam jÚzyk wciÈ jest zale ny od architektury.
Oznacza to, e tak samo, jak jÚzyk maszynowy dla procesorów Intel x86 ró ni siÚ
od jÚzyka maszynowego dla procesorów Sparc, jÚzyk asemblera x86 jest ró ny od
jÚzyka asemblera Sparc. ¿aden program napisany przy u yciu jÚzyka asemblera dla
architektury jednego procesora nie bÚdzie dziaïaï w architekturze innego procesora.
Je eli program zostaï napisany w jÚzyku asemblera x86, musi zostaÊ przepisany, aby
mo na byïo uruchomiÊ go w architekturze Sparc. Ponadto w celu napisania wydaj-
nego programu w jÚzyku asemblera nale y znaÊ wiele niskopoziomowych szczegó-
ïów, dotyczÈcych architektury danego procesora.
Problemów tych mo na uniknÈÊ przy u yciu kolejnej formy translatora, noszÈ-
cego nazwÚ kompilatora (ang. compiler). Kompilator konwertuje kod zapisany w jÚzy-
ku wysokopoziomowym do postaci kodu maszynowego. JÚzyki wysokopoziomowe sÈ
o wiele bardziej intuicyjne od jÚzyka asemblera i mogÈ byÊ konwertowane do wielu
ró nych typów jÚzyka maszynowego dla odmiennych architektur procesorów. Ozna-
cza to, e je li zapisze siÚ program w jÚzyku wysokiego poziomu, ten sam kod mo e
zostaÊ skompilowany przez kompilator do postaci kodu maszynowego dla ró nych
architektur. C, C++ lub FORTRAN to przykïady jÚzyków wysokopoziomowych.
Program napisany w takim jÚzyku jest o wiele bardziej czytelny1 od jÚzyka asem-
blera lub maszynowego, choÊ wciÈ musi byÊ zgodny z bardzo surowymi reguïami
dotyczÈcymi sposobu wyra ania instrukcji, poniewa w przeciwnym razie kompila-
tor nie bÚdzie w stanie ich zrozumieÊ.
1
Oczywi cie pod warunkiem, e przez czytelny rozumiemy taki, który przypomina jÚzyk angielski
— przyp. tïum.
Programowanie 21
10. 0x220 Pseudokod
Programi ci dysponujÈ jeszcze jednym rodzajem jÚzyka programowania, noszÈcym
nazwÚ pseudokodu. Pseudokod (ang. pseudo-code) to po prostu wyra enia zapisane
w jÚzyku naturalnym, ustawione w strukturÚ ogólnie przypominajÈcÈ jÚzyk wyso-
kopoziomowy. Nie jest on rozumiany przez kompilatory, asemblery ani kompute-
ry, ale stanowi przydatny sposób skïadania instrukcji. Pseudokod nie jest dobrze
zdefiniowany. W rzeczywisto ci wiele osób zapisuje pseudokod odmiennie. Jest to
rodzaj nieokre lonego, brakujÈcego ogniwa miÚdzy jÚzykami naturalnymi, takimi
jak angielski lub polski, a wysokopoziomowymi jÚzykami programowania, takimi
jak C lub Pascal. Pseudokod stanowi wietne wprowadzenie do uniwersalnych
zaïo eñ programistycznych.
0x230 Struktury sterujÈce
Bez struktur sterujÈcych program byïby jedynie seriÈ kolejno wykonywanych instruk-
cji. Jest to wystarczajÈce w przypadku bardzo prostych programów, ale wiÚkszo Ê
programów, takich jak np. opis dojazdu, taka nie jest. Prezentowany opis dojazdu
zawieraï instrukcje: Jed niÈ do momentu, kiedy zobaczysz po prawej stronie ko cióï.
Je eli ulica bÚdzie zablokowana ze wzglÚdu na prowadzone roboty… Instrukcje te
nazywamy strukturami sterujÈcymi, poniewa zmieniajÈ przebieg wykonywania
programu z prostego, sekwencyjnego, na bardziej zïo ony i u yteczny.
0x231 If-Then-Else
W naszym opisie dojazdu Aleja Ko ciuszki mo e byÊ w remoncie. Do rozwiÈzania
problemu wynikajÈcego z tej sytuacji potrzebujemy specjalnego zestawu instrukcji.
Je eli zdefiniowane okoliczno ci nie wystÈpiÈ (remont), bÚdÈ wykonywane standardo-
we instrukcje. Tego rodzaju specjalne przypadki mo na wziÈÊ pod uwagÚ w progra-
mie, korzystajÈc z jednej z najbardziej naturalnych struktur sterujÈcych: if-then-else
(je eli-wówczas-w przeciwnym przypadku). Ogólnie wyglÈda to mniej wiÚcej tak:
If (warunek) then
{
Zestaw instrukcji do wykonania, gdy warunek jest spe niony;
}
Else
{
Zestaw instrukcji do wykonania, gdy warunek nie jest spe niony;
}
W tej ksiÈ ce bÚdziemy posïugiwali siÚ pseudokodem przypominajÈcym jÚzyk C,
wiÚc ka da instrukcja bÚdzie zakoñczona rednikiem, a ich zbiory bÚdÈ grupowane
za pomocÈ nawiasów klamrowych i wyró niane wciÚciem. Pseudokod struktury
if-then-else dla wskazówek dojazdu mógïby przedstawiaÊ siÚ nastÚpujÈco:
22 0x200
11. Jed w dó Alejæ Ko ciuszki;
Je eli (ulica zablokowana)
{
Skrúè w prawo w ul. Moniuszki;
Skrúè w lewo w ul. Prusa;
Skrúè w prawo w ul. Orzeszkowej;
}
else
{
Skrúè w prawo w ul. Orzeszkowej;
}
Ka da instrukcja znajduje siÚ w osobnym wierszu, a ró ne zestawy instrukcji
warunkowych sÈ zgrupowane wewnÈtrz nawiasów klamrowych i dla zwiÚkszenia
czytelno ci zastosowano dla nich wciÚcie. W C i wielu innych jÚzykach programo-
wania sïowo kluczowe then jest domniemane i tym samym pomijane, wiÚc pomi-
nÚli my je równie w pseudokodzie.
Oczywi cie, istniejÈ jÚzyki, których skïadnia wymaga zastosowania sïowa klu-
czowego then — przykïadami mogÈ tu byÊ BASIC, Fortran, a nawet Pascal. Tego
typu ró nice syntaktyczne miÚdzy jÚzykami programowania sÈ niemal nieistotne,
jako e podstawowa struktura jest taka sama. Gdy programista zrozumie zaïo enia
tych jÚzyków, nauczenie siÚ ró nych odmian syntaktycznych jest stosunkowo ïatwe.
Poniewa w dalszych rozdziaïach bÚdziemy programowali w jÚzyku C, prezento-
wany pseudokod bÚdzie przypominaï ten jÚzyk. Nale y jednak pamiÚtaÊ, e mo e
on przyjmowaÊ ró ne postaci.
InnÈ powszechnÈ reguïÈ skïadni podobnej do C jest sytuacja, w której zestaw
instrukcji umieszczonych w nawiasach klamrowych zawiera tylko jednÈ instrukcjÚ.
Dla zwiÚkszenia czytelno ci wciÈ warto dla takiej instrukcji zastosowaÊ wciÚcie,
ale nie jest ono wymagane. Zgodnie z tÈ reguïÈ mo na wiÚc napisaÊ wcze niej przed-
stawiony opis dojazdu tak, aby uzyskaÊ równowa ny fragment pseudokodu:
Jed w dó Alejæ Ko ciuszki;
Je eli (ulica zablokowana)
{
Skrúè w prawo w ul. Moniuszki;
Skrúè w lewo w ul. Prusa;
Skrúè w prawo w ul. Orzeszkowej;
}
else
Skrúè w prawo w ul. Orzeszkowej;
Reguïa mówiÈca o zestawach instrukcji dotyczy wszystkich struktur sterujÈcych
opisywanych w niniejszej ksiÈ ce, a samÈ reguïÚ równie mo na zapisaÊ w pseu-
dokodzie.
Programowanie 23
12. If (w zestawie instrukcji znajduje siú tylko jedna instrukcja)
U ycie nawiasów klamrowych do zgrupowania instrukcji jest opcjonalne;
Else
{
U ycie nawiasów klamrowych jest niezbúdne;
Poniewa musi istnieè logiczny sposób zgrupowania tych instrukcji;
}
Nawet opis skïadni mo na potraktowaÊ jak program. Istnieje wiele odmian struk-
tury if-then-else, np. instrukcje select i case, ale logika pozostaje taka sama: je eli
stanie siÚ tak, zrób to i to, w przeciwnym przypadku wykonaj, co poni ej (a poni ej
mogÈ siÚ znajdowaÊ kolejne instrukcje if-then).
0x232 PÚtle While/Until
Innym podstawowym pojÚciem programistycznym jest struktura sterujÈca while,
bÚdÈca rodzajem pÚtli. Programista czÚsto chce wykonaÊ zestaw instrukcji kilka razy.
Program mo e wykonaÊ to zadanie, stosujÈc pÚtlÚ, ale wymaga to okre lenia warun-
ków jej zakoñczenia, aby nie trwaïa w nieskoñczono Ê. PÚtla while wykonuje podany
zestaw instrukcji, dopóki warunek jest prawdziwy. Prosty program dla gïodnej
myszy mógïby wyglÈdaÊ nastÚpujÈco:
While (jeste g odna)
{
Znajd jakie jedzenie;
Zjedz jedzenie;
}
Te dwa zestawy instrukcji za instrukcjÈ while bÚdÈ powtarzane, dopóki mysz
bÚdzie gïodna. Ilo Ê po ywienia odnalezionego przez mysz przy ka dej próbie mo e
wahaÊ siÚ od maïego okruszka po caïy bochen chleba. Podobnie liczba wykonañ
zestawu instrukcji w pÚtli while zale y od ilo ci po ywienia znalezionego przez mysz.
OdmianÈ pÚtli while jest pÚtla until, wykorzystywana w Perlu (w C skïadnia
ta nie jest u ywana). PÚtla until jest po prostu pÚtlÈ while z odwróconym warunkiem.
Ten sam program dla myszy zapisany z u yciem pÚtli until wyglÈdaïby nastÚpujÈco:
Until (nie jeste g odna)
{
Znajd jakie jedzenie;
Zjedz jedzenie;
}
Logicznie ka da instrukcja podobna do until mo e byÊ przeksztaïcona w pÚtlÚ
while. Opis dojazdu zawieraï zdanie: „Jed niÈ do momentu, kiedy zobaczysz po
prawej stronie ko cióï”. Mo emy to w prosty sposób przeksztaïciÊ w standardowÈ
pÚtlÚ while — wystarczy, e odwrócimy warunek:
24 0x200
13. While (nie ma ko cio a po prawej stronie)
Jed Alejæ Ko ciuszki;
0x233 PÚtle for
KolejnÈ strukturÈ sterujÈcÈ jest pÚtla for, wykorzystywana wtedy, gdy programista
chce, aby pÚtla wykonaïa okre lonÈ liczbÚ iteracji. Instrukcja dojazdu: „Jed niÈ
jakie 8 kilometrów” przeksztaïcona do pÚtli for mogïaby wyglÈdaÊ nastÚpujÈco:
For (8 iteracji)
Jed prosto 1 kilometr;
W rzeczywisto ci pÚtla for jest po prostu pÚtlÈ while z licznikiem. Powy szÈ
instrukcjÚ mo na równie dobrze zapisaÊ tak:
Ustaw licznik na 0
While (licznik jest mniejszy od 8)
{
Jed prosto 1 kilometr;
Dodaj 1 do licznika;
}
Skïadnia pseudokodu dla pÚtli for sprawia, e jest to jeszcze bardziej widoczne:
For (i=0; i<8; i++)
Jed prosto 1 kilometr;
W tym przypadku licznik nosi nazwÚ i, a instrukcja for jest podzielona na trzy
sekcje oddzielone rednikami. Pierwsza sekcja deklaruje licznik i ustawia jego po-
czÈtkowÈ warto Ê — w tym przypadku 0. Druga sekcja przypomina instrukcjÚ while
z licznikiem — dopóki licznik speïnia podany warunek, kontynuuj pÚtlÚ. Trzecia
i ostatnia sekcja opisuje, co ma siÚ dziaÊ z licznikiem przy ka dej iteracji. W tym
przypadku i++ jest skróconym sposobem na powiedzenie: „Dodaj 1 do licznika
o nazwie i”.
Zastosowanie wszystkich omówionych struktur sterujÈcych umo liwia przeksztaï-
cenie opisu dojazdu ze strony 6 w poni szy pseudokod przypominajÈcy jÚzyk C:
Rozpocznij jazdú na wschód Alejæ Ko ciuszki;
While (po prawej stronie nie ma ko cio a)
Jed Alejæ Ko ciuszki;
If (ulica zablokowana)
{
Skrúè w prawo w ul. Moniuszki);
Skrúè w lewo w ul. Prusa);
Skrúè w prawo w ul. Orzeszkowej);
}
Programowanie 25
14. Else
Skrúè(prawo, ul. Orzeszkowej);
Skrúè(lewo, ul. Docelowa);
For (i=0; i<8; i++)
{
Jed prosto 1 kilometr;
}
Zatrzymaj siú przy ul. Docelowej 743;
0x240 Podstawowe pojÚcia programistyczne
W kolejnych punktach zostanÈ przedstawione podstawowe pojÚcia programistyczne.
SÈ one wykorzystywane w wielu jÚzykach programowania, ró niÈ siÚ jedynie pod
wzglÚdem syntaktycznym. Podczas prezentacji bÚdÈ integrowane z przykïadami
w pseudokodzie przypominajÈcym skïadniÚ jÚzyka C. Na koñcu pseudokod bÚdzie
bardzo przypominaï kod napisany w jÚzyku C.
0x241 Zmienne
Licznik wykorzystywany w pÚtli for jest rodzajem zmiennej. O zmiennej mo emy
my leÊ jak o obiekcie przechowujÈcym dane, które mogÈ byÊ zmieniane — stÈd
nazwa. IstniejÈ równie zmienne, których warto ci nie sÈ zmieniane, i trafnie nazywa
siÚ je staïymi. W przykïadzie motoryzacyjnym jako zmiennÈ mo emy potraktowaÊ
prÚdko Ê samochodu, natomiast kolor lakieru samochodu byïby staïÈ. W pseudo-
kodzie zmienne sÈ po prostu pojÚciami abstrakcyjnymi, natomiast w C (i wielu in-
nych jÚzykach) zmienne przed wykorzystaniem muszÈ byÊ zadeklarowane i nale y
okre liÊ ich typ. Jest tak dlatego, e program napisany w C bÚdzie kompilowany
do postaci programu wykonywalnego. Podobnie jak przepis kucharski, w którym
przed faktycznymi instrukcjami wymieniane sÈ wszystkie potrzebne skïadniki, de-
klaracje zmiennych pozwalajÈ dokonaÊ przygotowañ przed przej ciem do faktycznej
tre ci programu. Ostatecznie wszystkie zmienne sÈ przechowywane gdzie w pamiÚci,
a ich deklaracje pozwalajÈ kompilatorowi wydajniej uporzÈdkowaÊ pamiÚÊ. Jednak
na koniec, mimo wszystkich deklaracji typów zmiennych, wszystko odbywa siÚ
w pamiÚci.
W jÚzyku C dla ka dej zmiennej okre lany jest typ, opisujÈcy informacje, które
bÚdÈ przechowywane w tej zmiennej. Do najczÚ ciej stosowanych typów zalicza-
my int (liczby caïkowite), float (liczby zmiennoprzecinkowe) i char (pojedyncze
znaki). Zmienne sÈ deklarowane poprzez u ycie tych sïów kluczowych przed nazwÈ
zmiennej, co zobrazowano na poni szym przykïadzie.
int a, b;
float k;
char z;
Zmienne a i b sÈ zadeklarowane jako liczby caïkowite, k mo e przyjmowaÊ warto-
ci zmiennoprzecinkowe (np. 3,14), a z powinna przechowywaÊ warto Ê znakowÈ,
26 0x200
15. takÈ jak A lub w. Warto ci mo na przypisywaÊ zmiennym podczas deklaracji lub
w dowolnym pó niejszym momencie. Sïu y do tego operator =.
int a = 13, b;
float k;
char z = 'A';
k = 3.14;
z = 'w';
b = a + 5;
Po wykonaniu powy szych instrukcji zmienna a bÚdzie posiadaïa warto Ê 13,
zmienna k bÚdzie zawieraïa liczbÚ 3,14, a zmienna b bÚdzie miaïa warto Ê 18, po-
niewa 13 plus 5 równa siÚ 18. Zmienne sÈ po prostu sposobem pamiÚtania warto ci,
jednak w jÚzyku C nale y najpierw zadeklarowaÊ typ ka dej zmiennej.
0x242 Operatory arytmetyczne
Instrukcja b = a + 7 jest przykïadem bardzo prostego operatora arytmetycznego.
W jÚzyku C dla ró nych operacji matematycznych wykorzystywane sÈ poni sze
symbole.
Pierwsze cztery operatory powinny wyglÈdaÊ znajomo. Redukcja modulo mo e
stanowiÊ nowe pojÚcie, ale jest to po prostu reszta z dzielenia. Je eli a ma warto Ê
13, to 13 podzielone przez 5 równa siÚ 2, a reszta wynosi 3, co oznacza, e a % 5 = 3.
Ponadto skoro zmienne a i b sÈ liczbami caïkowitymi, po wykonaniu instrukcji
b = a / 5, zmienna b bÚdzie miaïa warto Ê 2, poniewa jest to czÚ Ê wyniku bÚdÈca
liczbÈ caïkowitÈ. Do przechowania bardziej poprawnego wyniku, czyli 2,6, musi
zostaÊ u yta zmienna typu float (zmiennoprzecinkowa).
Operacja Symbol Przykïad
Dodawanie + b = a + 5
Odejmowanie - b = a - 5
Mno enie * b = a * 5
Dzielenie / b = a / 5
Redukcja modulo % b = a % 5
Aby program korzystaï z tych zaïo eñ, musimy mówiÊ w jego jÚzyku. JÚzyk C
dostarcza kilku skróconych form zapisu tych operacji arytmetycznych. Jedna z nich
zostaïa przedstawiona wcze niej i jest czÚsto wykorzystywana w pÚtlach for.
Peïne wyra enie Skrót Wyja nienie
i = i + 1 i++ lub ++i Dodaje 1 do warto ci zmiennej.
i = i - 1 i-- lub i-- Odejmuje 1 od warto ci zmiennej.
Programowanie 27
16. Te skrócone wyra enia mogÈ byÊ ïÈczone z innymi operacjami arytmetycznymi
w celu utworzenia bardziej zïo onych wyra eñ. Wówczas staje siÚ widoczna ró nica
miÚdzy i++ i ++i. Pierwsze wyra enie oznacza: „ZwiÚksz warto Ê i o 1 po przepro-
wadzeniu operacji arytmetycznej”, natomiast drugie: „ZwiÚksz warto Ê i o 1 przed
przeprowadzeniem operacji arytmetycznej”. Poni szy przykïad pozwoli to lepiej
zrozumieÊ.
int a, b;
a = 5
b = a++ * 6;
Po wykonaniu tego zestawu instrukcji zmienna b bÚdzie miaïa warto Ê 30, nato-
miast zmienna a bÚdzie miaïa warto Ê 6, poniewa skrócony zapis b = a++ * 6 jest
równowa ny poni szym instrukcjom.
b = a * 6;
a = a + 1;
Je eli jednak zostanie u yta instrukcja b = ++a * 6, kolejno Ê dodawania zostanie
zmieniona i otrzymamy odpowiednik poni szych instrukcji.
a = a + 1;
b = a * 6;
Poniewa kolejno Ê wykonywania dziaïañ zmieniïa siÚ, w powy szym przypad-
ku zmienna b bÚdzie miaïa warto Ê 36, natomiast warto Ê zmiennej a wciÈ bÚdzie
wynosiïa 6.
CzÚsto w programach musimy zmodyfikowaÊ zmiennÈ w danym miejscu. Przy-
kïadowo mo emy dodaÊ dowolnÈ warto Ê, takÈ jak 12, i przechowaÊ wynik w tej
samej zmiennej (np. i = i + 12) Zdarza siÚ to tak czÚsto, e równie utworzono
skróconÈ formÚ tej instrukcji.
Peïne wyra enie Skrót Wyja nienie
i = i + 12 i+=12 Dodaje okre lonÈ warto Ê do warto ci zmiennej.
i = i - 12 i-=12 Odejmuje okre lonÈ warto Ê od warto ci zmiennej.
i = i * 12 i*=12 Mno y okre lonÈ warto Ê przez warto Ê zmiennej.
i = i / 12 i/=12 Dzieli warto Ê zmiennej przez okre lonÈ warto Ê.
0x243 Operatory porównania
Zmienne sÈ czÚsto wykorzystywane w instrukcjach warunkowych wcze niej opisanych
struktur sterujÈcych, a te instrukcje warunkowe opierajÈ siÚ na jakiego rodzaju
porównaniu. W jÚzyku C operatory porównania wykorzystujÈ skróconÈ skïadniÚ,
która jest stosunkowo powszechna w innych jÚzykach programowania.
28 0x200
17. Warunek Symbol Przykïad
Mniejsze ni < (a < b)
WiÚksze ni > (a < b)
Mniejsze lub równe <= (a <= b)
WiÚksze lub równe >= (a >= b)
Równe == (a == b)
Nie równa siÚ != (a != b)
WiÚkszo Ê z tych operatorów jest zrozumiaïa, jednak e nale y zauwa yÊ, e
w skrócie porównania równo ci wykorzystywane sÈ dwa znaki równo ci. Jest to wa ne
rozró nienie, poniewa podwójny znak równo ci jest stosowany do sprawdzenia
równo ci, natomiast jeden znak równo ci jest u ywany do przypisania warto ci
zmiennej. Instrukcja a = 7 oznacza: „Umie Ê w zmiennej a warto Ê 7”, natomiast
a == 7 oznacza: „Sprawd , czy warto Ê zmiennej a wynosi 7”. (Niektóre jÚzyki pro-
gramowania, takie jak Pascal, wykorzystujÈ do przypisywania warto ci zmiennym
nastÚpujÈcy znak — := — aby wyeliminowaÊ ryzyko bïÚdnego zrozumienia kodu).
Nale y te zwróciÊ uwagÚ, e wykrzyknik zwykle oznacza nie. Ten symbol mo e byÊ
wykorzystany do odwrócenia dowolnego wyra enia.
!(a < b) jest równowa ne (a >= b)
Operatory porównania mogÈ byÊ ïÈczone ze sobÈ za pomocÈ skrótów dla ope-
ratorów logicznych OR i AND.
Operator logiczny Symbol Przykïad
OR || ((a < b) || (a < c))
AND && ((a < b) && !(a < c))
Przykïadowa instrukcja zawierajÈca dwa mniejsze warunki poïÈczone operato-
rem OR zostanie oszacowana jako prawdziwa, je eli warto Ê zmiennej a bÚdzie mniej-
sza od warto ci zmiennej b LUB warto Ê zmiennej a bÚdzie mniejsza od warto ci
zmiennej c. Podobnie przykïadowa instrukcja zawierajÈca dwa mniejsze warunki
poïÈczone operatorem AND zostanie oszacowana jako prawdziwa, je eli warto Ê
zmiennej a bÚdzie mniejsza od warto ci zmiennej b I warto Ê zmiennej a nie bÚdzie
mniejsza od warto ci zmiennej c. Takie instrukcje powinny byÊ grupowane za pomocÈ
nawiasów i mogÈ zawieraÊ wiele ró nych odmian.
Wiele rzeczy mo emy sprowadziÊ do zmiennych, operatorów porównania i struk-
tur sterujÈcych. W przykïadzie myszy poszukujÈcej po ywienia gïód mo emy przed-
stawiÊ jako zmiennÈ boolowskÈ o warto ci true lub false (prawda lub faïsz). Oczy-
wi cie, 1 oznacza true, a 0 — false.
Programowanie 29
18. While (g ód == 1)
{
Szukaj po ywienia;
Zjedz po ywienie;
}
Oto kolejny skrót czÚsto wykorzystywany przez programistów i hakerów. JÚzyk
C nie zawiera adnych operatorów boolowskich, wiÚc ka da warto Ê niezerowa jest
uznawana za true, a instrukcja jest szacowana jako false, gdy zawiera 0. W rzeczy-
wisto ci operatory porównania faktycznie zwracajÈ 1, je eli porównanie jest speï-
nione, a w przeciwnym przypadku zwracajÈ 0. Sprawdzenie, czy zmienna g ód ma
warto Ê 1, zwróci 1, je eli g ód ma warto Ê 1, lub zwróci 0, je eli gïód ma warto Ê 0.
Poniewa program wykorzystuje tylko te dwa przypadki, mo na w ogóle pominÈÊ
operator porównania.
While (g ód)
{
Szukaj po ywienia;
Zjedz po ywienie;
}
Sprytniejszy program sterujÈcy zachowaniem myszy z u yciem wiÚkszej liczby
danych wej ciowych to przykïad, jak mo na ïÈczyÊ operatory porównania ze zmien-
nymi.
While ((g ód) && !(kot_obecny))
{
Szukaj po ywienia;
If (!(po ywienie_znajduje_siú_w_pu apce_na_myszy;
Zjedz po ywienie;
}
W tym przykïadzie zaïo ono, e dostÚpne sÈ równie zmienne opisujÈce obec-
no Ê kota oraz poïo enie po ywienia z warto ciami 1 dla speïnienia warunku i 0 dla
przypadku przeciwnego. Wystarczy pamiÚtaÊ, e warto Ê niezerowa oznacza prawdÚ,
a warto Ê 0 jest szacowana jako faïsz.
0x244 Funkcje
Czasami zdarzajÈ siÚ zestawy instrukcji, których programista bÚdzie potrzebowaï
kilka razy. Takie instrukcje mo na zgrupowaÊ w mniejszym podprogramie, zwanym
funkcjÈ. W innych jÚzykach funkcje nazywane sÈ tak e procedurami i podprocedu-
rami. Przykïadowo wykonanie skrÚtu samochodem skïada siÚ z kilku mniejszych
instrukcji: wïÈcz odpowiedni kierunkowskaz, zwolnij, sprawd , czy nie nadje d ajÈ
inne samochody, skrÚÊ kierownicÚ w odpowiednim kierunku itd. Opis trasy z poczÈt-
ku tego rozdziaïu zawieraï caïkiem sporo skrÚtów, jednak e wymienianie wszystkich
30 0x200
19. drobnych instrukcji przy ka dym skrÚcie byïoby mudne (i zmniejszaïoby czytel-
no Ê kodu). W celu zmodyfikowania sposobu dziaïania funkcji mo emy przesyïaÊ do
niej zmienne jako argumenty. W tym przypadku do funkcji jest przesyïany kierunek
skrÚtu.
Function Skrút(zmienna_kierunku)
{
W æcz zmienna_kierunku kierunkowskaz;
Zwolnij;
Sprawd , czy nie nadje d ajæ inne samochody;
while (nadje d ajæ inne samochody)
{
Zatrzymaj siú;
Obserwuj nadje d ajæce samochody;
}
Obróè kierownicú w zmienna_kierunku;
while (skrút nie jest uko czony)
{
if (prúdko è < 8 km/h)
Przyspiesz;
}
Obróè kierownicú do pierwotnego po o enia;
Wy æcz zmienna_kierunku kierunkowskaz;
}
Funkcja ta zawiera wszystkie instrukcje konieczne do wykonania skrÚtu. Gdy
program, w którym funkcja taka siÚ znajduje, musi wykonaÊ skrÚt, mo e po prostu
wywoïaÊ tÚ funkcjÚ. Gdy zostaje wywoïana, znajdujÈce siÚ w niej instrukcje sÈ wy-
konywane z przesïanymi argumentami. NastÚpnie wykonywanie programu powraca
do momentu za wywoïaniem funkcji. Do tej funkcji mo na przesïaÊ warto Ê lewo
albo prawo, co spowoduje wykonanie skrÚtu w tym kierunku.
Domy lnie w jÚzyku C funkcje mogÈ zwracaÊ warto ci do elementu wywoïujÈ-
cego. Dla osób znajÈcych matematykÚ jest to zupeïnie zrozumiaïe. Wyobra my sobie
funkcjÚ obliczajÈcÈ silniÚ — jest oczywiste, e zwraca wynik.
W jÚzyku C funkcje nie sÈ oznaczane sïowem kluczowym function. Zamiast tego
sÈ deklarowane przez typ danych zwracanej zmiennej. Taki format bardzo przy-
pomina deklaracjÚ zmiennej. Je eli funkcja w zamierzeniu ma zwracaÊ liczbÚ caï-
kowitÈ (mo e to byÊ np. funkcja obliczajÈca silniÚ jakiej liczby x), jej kod mógïby
wyglÈdaÊ nastÚpujÈco:
int factorial(int x)
{
int i;
for(i=1; i < x; i++)
x *= i;
return x;
}
Programowanie 31
20. Funkcja ta jest deklarowana liczbÈ caïkowitÈ, poniewa mno y ka dÈ warto Ê
od 1 do x i zwraca wynik, który jest liczbÈ caïkowitÈ. Instrukcja return na koñcu
funkcji przesyïa zawarto Ê zmiennej x i koñczy wykonywanie funkcji. FunkcjÚ obli-
czajÈcÈ silniÚ mo na wówczas wykorzystaÊ jak ka dÈ zmiennÈ typu integer w gïównej
czÚ ci ka dego programu, który „wie” o tej funkcji:
int a=5, b;
b = factorial(a);
Po ukoñczeniu wykonywania tego krótkiego programu zmienna b bÚdzie miaïa
warto Ê 120, poniewa funkcja factorial() zostanie wywoïana z argumentem 5
i zwróci 120.
Ponadto w jÚzyku C kompilator musi „wiedzieÊ” o funkcji, zanim jej u yje. W tym
celu mo na po prostu napisaÊ caïÈ funkcjÚ przed pó niejszym wykorzystaniem jej
w programie lub skorzystaÊ z prototypu funkcji. Prototyp funkcji jest prostym spo-
sobem poinformowania kompilatora, i powinien spodziewaÊ siÚ funkcji o podanej
nazwie, zwracajÈcej okre lony typ danych i przyjmujÈcej argumenty o okre lonym
typie danych. Faktyczny kod funkcji mo na umie ciÊ pod koniec programu, ale mo na
jÈ wykorzystaÊ w dowolnym miejscu, poniewa kompilator bÚdzie ju wiedziaï o jej
istnieniu. Przykïadowy prototyp funkcji factorial() mógïby wyglÈdaÊ nastÚpujÈco:
int factorial(int);
Zwykle prototypy funkcji sÈ umieszczane blisko poczÈtku programu. W proto-
typie nie ma potrzeby definiowania nazw zmiennych, poniewa dzieje siÚ to w sa-
mej funkcji. Kompilator musi jedynie znaÊ nazwÚ funkcji, typ zwracanych danych
i typy danych argumentów funkcjonalnych.
Je eli funkcja nie posiada warto ci, którÈ mo e zwróciÊ (np. wcze niej opisana
funkcja skrúè()), powinna byÊ zadeklarowana jako typ void. Jednak e funkcja skrúè()
nie posiada jeszcze caïej funkcjonalno ci wymaganej przez nasz opis dojazdu. Ka dy
skrÚt w opisie dojazdu zawiera zarówno kierunek, jak i nazwÚ ulicy. To oznacza, e
funkcja skrÚcajÈca powinna przyjmowaÊ dwie zmienne: kierunek skrÚtu oraz nazwÚ
ulicy, w którÈ nale y skrÚciÊ. To komplikuje funkcjÚ skrÚcajÈcÈ, poniewa przed
wykonaniem skrÚtu nale y zlokalizowaÊ ulicÚ. Bardziej kompletna funkcja skrÚcajÈca,
w której wykorzystano skïadniÚ podobnÈ do jÚzyka C, jest zamieszczona w pseu-
dokodzie poni szego listingu.
void skrúè(zmienna_kierunku, nazwa_docelowej_ulicy)
{
Szukaj tabliczki z nazwæ ulicy;
nazwa_bie æcego_skrzy owania = odczytaj tabliczkú z nazwæ ulicy;
while (nazwa_bie æcego_skrzy owania != nazwa_docelowej_ulicy)
{
Szukaj innej tabliczki z nazwæ ulicy;
nazwa_bie æcego_skrzy owania = odczytaj tabliczkú z nazwæ ulicy;
}
32 0x200
21. W æcz zmienna_kierunku kierunkowskaz;
Zwolnij;
Sprawd , czy nie nadje d ajæ inne samochody;
while (nadje d ajæ inne samochody)
{
Zatrzymaj siú;
Obserwuj nadje d ajæce samochody;
}
Obróè kierownicú w zmienna_kierunku;
while (skrút nie jest uko czony)
{
if (prúdko è < 8 km/h)
Przyspiesz;
}
Obróè kierownicú do pierwotnego po o enia;
Wy æcz zmienna_kierunku kierunkowskaz;
}
Funkcja ta zawiera sekcjÚ, która wyszukuje odpowiednie skrzy owanie poprzez
odnajdywanie tabliczki z nazwÈ ulicy, odczytywanie z niej nazwy i przechowywanie
jej w zmiennej o nazwie nazwa_bie æcego_skrzy owania. Wyszukiwanie i odczyty-
wanie tabliczek z nazwami odbywa siÚ do momentu odnalezienia docelowej ulicy.
Wówczas wykonywane sÈ pozostaïe instrukcje skrÚtu. Mo emy teraz zmieniÊ pseu-
dokod z instrukcjami dojazdu, aby wykorzystywaï funkcjÚ skrÚtu.
Rozpocznij jazdú na wschód Alejæ Ko ciuszki;
while (po prawej stronie nie ma ko cio a)
{
Jed Alejæ Ko ciuszki;
}
If (ulica zablokowana)
{
Skrúè(prawo, ul. Moniuszki);
Skrúè(lewo, ul. Prusa);
Skrúè(prawo, ul. Orzeszkowej);
}
else
{
Skrúè(prawo, ul. Orzeszkowej);
}
Skrúè(lewo, ul. Docelowa);
For (i=0; i<5; i++)
{
Jed prosto 1 kilometr;
}
Zatrzymaj siú przy ul. Docelowej 743;
Funkcje nie sÈ czÚsto wykorzystywane w pseudokodzie, bo zwykle jest on sto-
sowany przez programistów do zarysowania zaïo eñ programu przed napisaniem
kompilowalnego kodu. Poniewa pseudokod nie musi dziaïaÊ, nie trzeba wypisywaÊ
peïnych funkcji — wystarczy jedynie krótki zapisek: Tu zrób co zïo onego. Jednak
Programowanie 33
22. w jÚzyku programowania, takim jak C, funkcje sÈ czÚsto wykorzystywane. WiÚkszo Ê
prawdziwej u yteczno ci C pochodzi ze zbiorów istniejÈcych funkcji, zwanych
bibliotekami.
0x250 Zaczynamy brudziÊ sobie rÚce
Gdy zaznajomili my siÚ ju trochÚ ze skïadniÈ jÚzyka C i wyja nili my kilka pod-
stawowych pojÚÊ programistycznych, przej cie do faktycznego programowania w C
nie jest niczym nadzwyczajnym. Kompilatory jÚzyka C istniejÈ dla niemal ka dego
systemu operacyjnego i architektury procesora, jednak w niniejszej ksiÈ ce bÚdziemy
wykorzystywali wyïÈcznie system Linux i procesor z rodziny x86. Linux jest dar-
mowym systemem operacyjnym, do którego ka dy ma dostÚp, a procesory x86 sÈ
najpopularniejszymi procesorami konsumenckimi na wiecie. Poniewa hacking
wiÈ e siÚ gïównie z eksperymentowaniem, najlepiej bÚdzie, je eli w trakcie lektury
bÚdziesz dysponowaï kompilatorem jÚzyka C.
DziÚki dyskowi LiveCD doïÈczonemu do niniejszej ksiÈ ki mo esz praktycznie
stosowaÊ przekazywane informacje, je eli tylko dysponujesz procesorem z rodziny
x86. Wystarczy wïo yÊ dysk CD do napÚdu i ponownie uruchomiÊ komputer. Uru-
chomione zostanie rodowisko linuksowe, które nie narusza istniejÈcego systemu ope-
racyjnego. W tym rodowisku mo esz wypróbowywaÊ przykïady z ksiÈ ki, a tak e
przeprowadzaÊ wïasne eksperymenty.
Przejd my do rzeczy. Program firstprog.c jest prostym fragmentem kodu w C,
wypisujÈcym 10 razy „Hello world!”.
Listing 2.1. firstprog.c
#include <stdio.h>
int main()
{
int i;
for(i=0; i < 10; i++) // Wykonaj pútlú 10 razy.
{
puts("Hello, world!n"); // Przeka a cuch do wyj cia.
}
return 0; // Poinformuj OS, e program zako czy siú bez b údów.
}
Gïówne wykonywanie programu napisanego w C rozpoczyna siÚ w funkcji main().
Tekst znajdujÈcy siÚ za dwoma uko nikami jest komentarzem ignorowanym przez
kompilator.
Pierwszy wiersz mo e wprawiaÊ w zakïopotanie, ale jest to jedynie skïadnia
informujÈca kompilator, aby doïÈczyï nagïówki dla standardowej biblioteki wej-
cia-wyj cia o nazwie stdio. Ten plik nagïówkowy jest dodawany do programu
podczas kompilacji. Znajduje siÚ on w /usr/include/stdio.h i definiuje kilka staïych
i prototypów funkcji dla odpowiednich funkcji w standardowej bibliotece we-wy.
Poniewa funkcja main() wykorzystuje funkcjÚ printf() ze standardowej biblioteki
34 0x200
23. we-wy, wymagany jest prototyp funkcji printf() przed jej zastosowaniem. Ten
prototyp funkcji (a tak e wiele innych) znajduje siÚ w pliku nagïówkowym stdio.h.
Sporo mo liwo ci jÚzyka C to pochodne jego zdolno ci do rozbudowy oraz wyko-
rzystania bibliotek. Reszta kodu powinna byÊ zrozumiaïa i przypomina wcze niej
prezentowany pseudokod. SÈ nawet nawiasy klamrowe, które mo na byïo pominÈÊ.
To, co bÚdzie siÚ dziaïo po uruchomieniu tego programu, powinno byÊ oczywiste,
ale skompilujmy go za pomocÈ GCC, aby siÚ upewniÊ.
GNU Compiler Collection jest darmowym kompilatorem jÚzyka C, tïumaczÈcym
jÚzyk C na jÚzyk maszynowy, zrozumiaïy dla komputera. W wyniku tïumaczenia
powstaje wykonywalny plik binarny, któremu domy lnie nadawana jest nazwa a.out.
Czy skompilowany program wykonuje to, co chcieli my?
reader@hacking:~/booksrc $ gcc firstprog.c
reader@hacking:~/booksrc $ ls -l a.out
-rwxr-xr-x 1 reader reader 6621 2007-09-06 22:16 a.out
reader@hacking:~/booksrc $ ./a.out
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
reader@hacking:~/booksrc $
0x251 WiÚkszy obraz
Dobrze, tego wszystkiego mo na siÚ nauczyÊ na podstawowym kursie programo-
wania — sÈ to podstawy, ale niezbÚdne. WiÚkszo Ê poczÈtkowych kursów progra-
mowania uczy jedynie, jak czytaÊ i pisaÊ w jÚzyku C. ProszÚ mnie le nie zrozumieÊ
— pïynna znajomo Ê C jest bardzo przydatna i wystarczy do stania siÚ dobrym
programistÈ, ale jest to tylko czÚ Ê wiÚkszej caïo ci. WiÚkszo Ê programistów uczy
siÚ jÚzyka z góry do doïu i nigdy nie ma szerszego obrazu. Hakerzy sÈ tak dobrzy,
poniewa wiedzÈ, jak wszystkie czÚ ci tego wiÚkszego obrazu wchodzÈ ze sobÈ
w interakcje. Aby w programowaniu patrzeÊ szerzej, wystarczy sobie zdaÊ sprawÚ, e
kod w jÚzyku C bÚdzie kompilowany. Kod nie mo e nic zrobiÊ, dopóki nie zostanie
skompilowany do wykonywalnego pliku binarnego. My lenie o kodzie ródïowym
w C jak o programie jest czÚstym bïÚdem wykorzystywanym codziennie przez ha-
kerów. Instrukcje w pliku a.out sÈ zapisane w jÚzyku maszynowym — podstawowym
jÚzyku zrozumiaïym dla procesora. Kompilatory tïumaczÈ jÚzyk kodu C na jÚzyk
maszynowy ró nych architektur procesorów. W tym przypadku procesor pocho-
dzi z rodziny wykorzystujÈcej architekturÚ x86. IstniejÈ równie architektury pro-
cesorów Sparc (u ywane w stacjach roboczych Sun) oraz architektura PowerPC
Programowanie 35
24. (wykorzystywana w macintoshach przed zastosowaniem procesorów Intela). Ka da
architektura wymaga innego jÚzyka maszynowego, wiÚc kompilator jest po redni-
kiem — tïumaczy kod w jÚzyku C na jÚzyk maszynowy docelowej architektury.
Dopóki skompilowany program dziaïa, przeciÚtny programista skupia siÚ wy-
ïÈcznie na kodzie ródïowym. Jednak haker zdaje sobie sprawÚ, e w rzeczywisto ci
wykonywany jest program skompilowany. MajÈc dogïÚbnÈ wiedzÚ na temat dziaïania
procesora, haker mo e manipulowaÊ dziaïajÈcymi programami. Widzieli my kod
ródïowy naszego pierwszego programu i skompilowali my go do pliku wykony-
walnego dla architektury x86. Ale jak wyglÈda ten wykonywalny plik binarny? Na-
rzÚdzia programistyczne GNU zawierajÈ program o nazwie objdump, który mo e byÊ
u yty do badania skompilowanych plików binarnych. Rozpocznijmy od poznania
kodu maszynowego, na który zostaïa przetïumaczona funkcja main().
reader@hacking:~/booksrc $ objdump -D a.out | grep -A20 main.:
08048374 <main>:
8048374: 55 push %ebp
8048375: 89 e5 mov %esp,%ebp
8048377: 83 ec 08 sub $0x8,%esp
804837a: 83 e4 f0 and $0xfffffff0,%esp
804837d: b8 00 00 00 00 mov $0x0,%eax
8048382: 29 c4 sub %eax,%esp
8048384: c7 45 fc 00 00 00 00 movl $0x0,0xfffffffc(%ebp)
804838b: 83 7d fc 09 cmpl $0x9,0xfffffffc(%ebp)
804838f: 7e 02 jle 8048393 <main+0x1f>
8048391: eb 13 jmp 80483a6 <main+0x32>
8048393: c7 04 24 84 84 04 08 movl $0x8048484,(%esp)
804839a: e8 01 ff ff ff call 80482a0 <printf@plt>
804839f: 8d 45 fc lea 0xfffffffc(%ebp),%eax
80483a2: ff 00 incl (%eax)
80483a4: eb e5 jmp 804838b <main+0x17>
80483a6: c9 leave
80483a7: c3 ret
80483a8: 90 nop
80483a9: 90 nop
80483aa: 90 nop
reader@hacking:~/booksrc $
Program objdump zwróciï zbyt wiele wierszy wynikowych, aby rozsÈdnie siÚ im
przyjrzeÊ, wiÚc przekazali my wyj cie do grep z opcjÈ powodujÈcÈ wy wietlenie
tylko 20 wierszy za wyra eniem regularnym main.:. Ka dy bajt jest reprezentowany
w notacji szesnastkowej, systemie liczbowym o podstawie 16. System liczbowy, do
którego jeste my najbardziej przyzwyczajeni, wykorzystuje podstawÚ 10, a przy
liczbie 10 nale y umie ciÊ dodatkowy symbol. W systemie szesnastkowym stoso-
wane sÈ cyfry od 0 do 9 reprezentujÈce 0 do 9, ale tak e litery A do F, które repre-
zentujÈ w nim liczby od 10 do 15. Jest to wygodny zapis, poniewa bajt zawiera 8 bi-
tów, z których ka dy mo e mieÊ warto Ê prawda lub faïsz. To oznacza, e bajt ma
256 (28) mo liwych warto ci, wiÚc ka dy bajt mo e byÊ opisany za pomocÈ dwóch
cyfr w systemie szesnastkowym.
36 0x200
25. Liczby szesnastkowe — rozpoczynajÈc od 0x8048374 po lewej stronie — sÈ ad-
resami pamiÚci. Bity jÚzyka maszynowego muszÈ byÊ gdzie przechowywane, a tym
„gdzie ” jest pamiÚÊ. PamiÚÊ jest po prostu zbiorem bajtów z tymczasowej przestrzeni
magazynowej, którym przypisano adresy liczbowe.
Podobnie jak rzÈd domów przy lokalnej ulicy, z których ka dy posiada wïasny
adres, pamiÚÊ mo emy traktowaÊ jako rzÈd bajtów, z których ka dy ma wïasny adres.
Do ka dego bajta pamiÚci mo na uzyskaÊ dostÚp za pomocÈ jego adresu, a w tym
przypadku procesor siÚga do tej czÚ ci pamiÚci, aby pobraÊ instrukcje jÚzyka ma-
szynowego skïadajÈce siÚ na skompilowany program. Starsze procesory Intela z rodzi-
ny x86 wykorzystujÈ 32-bitowy schemat adresacji, natomiast nowsze — 64-bitowy.
Procesory 32-bitowe posiadajÈ 232 (lub te 4 294 967 296) mo liwych adresów, na-
tomiast procesory 64-bitowe dysponujÈ ilo ciÈ adresów równÈ 264 (1,84467441 × 1019).
Procesory 64-bitowe mogÈ pracowaÊ w trybie kompatybilno ci 32-bitowej, co umo -
liwia im szybkie wykonywanie kodu 32-bitowego.
Bajty szesnastkowe znajdujÈce siÚ po rodku powy szego listingu sÈ instrukcjami
w jÚzyku maszynowym procesora x86. Oczywi cie, te warto ci szesnastkowe sÈ
jedynie reprezentacjami binarnych jedynek i zer, które rozumie procesor. Ale po-
niewa 0101010110001001111001011000001111101100111100001… przydaje siÚ
tylko procesorowi, kod maszynowy jest wy wietlany jako bajty szesnastkowe, a ka da
instrukcja jest umieszczana w osobnym wierszu, co przypomina podziaï akapitu
na zdania.
Gdy siÚ zastanowimy, oka e siÚ, e bajty szesnastkowe same w sobie te nie sÈ
przydatne — tutaj dochodzi do gïosu jÚzyk asembler. Instrukcje po prawej stronie
sÈ zapisane w asemblerze. Asembler jest po prostu zbiorem mnemoników dla od-
powiednich instrukcji jÚzyka maszynowego. Instrukcja ret jest znacznie ïatwiejsza
do zapamiÚtania i zrozumienia ni 0xc3 czy te 11000011. W przeciwieñstwie do C
i innych jÚzyków kompilowanych, instrukcje asemblera wystÚpujÈ w relacji jeden-
do-jednego z instrukcjami odpowiednimi jÚzyka maszynowego. Oznacza to, e skoro
ka da architektura procesora posiada wïasny zestaw instrukcji jÚzyka maszynowego,
ka da z nich ma równie wïasnÈ odmianÚ asemblera. JÚzyk asemblera jest dla pro-
gramistów tylko sposobem reprezentacji instrukcji jÚzyka maszynowego podawa-
nych procesorowi. Dokïadny sposób reprezentacji tych instrukcji maszynowych jest
tylko kwestiÈ konwencji i preferencji. ChoÊ teoretycznie mo na stworzyÊ wïasnÈ
skïadniÚ asemblera dla architektury x86, wiÚkszo Ê osób wykorzystuje jeden z gïów-
nych typów — skïadniÚ AT&T lub Intel. Kod asemblera przedstawiony na stronie 21
jest zgodny ze skïadniÈ AT&T, poniewa niemal wszystkie dezasemblery w Linuksie
domy lnie wykorzystujÈ tÚ skïadniÚ. SkïadniÚ AT&T mo na ïatwo rozpoznaÊ dziÚ-
ki kakofonii symboli % i $ umieszczanych przed wszystkimi elementami (proszÚ
ponownie spojrzeÊ na stronÚ 21). Ten sam kod mo na wy wietliÊ w skïadni Intela,
dodajÈc do polecenia objdump dodatkowÈ opcjÚ -M:
reader@hacking:~/booksrc $ objdump -M intel -D a.out | grep -A20 main.:
08048374 <main>:
8048374: 55 push ebp
8048375: 89 e5 mov ebp,esp
Programowanie 37
26. 8048377: 83 ec 08 sub esp,0x8
804837a: 83 e4 f0 and esp,0xfffffff0
804837d: b8 00 00 00 00 mov eax,0x0
8048382: 29 c4 sub esp,eax
8048384: c7 45 fc 00 00 00 00 mov DWORD PTR [ebp-4],0x0
804838b: 83 7d fc 09 cmp DWORD PTR [ebp-4],0x9
804838f: 7e 02 jle 8048393 <main+0x1f>
8048391: eb 13 jmp 80483a6 <main+0x32>
8048393: c7 04 24 84 84 04 08 mov DWORD PTR [esp],0x8048484
804839a: e8 01 ff ff ff call 80482a0 <printf@plt>
804839f: 8d 45 fc lea eax,[ebp-4]
80483a2: ff 00 inc DWORD PTR [eax]
80483a4: eb e5 jmp 804838b <main+0x17>
80483a6: c9 leave
80483a7: c3 ret
80483a8: 90 nop
80483a9: 90 nop
80483aa: 90 nop
reader@hacking:~/booksrc $
Uwa am, e skïadnia Intela jest znacznie bardziej czytelna i ïatwiejsza do zro-
zumienia, wiÚc bÚdzie ona stosowana w niniejszej ksiÈ ce. Niezale nie od repre-
zentacji jÚzyka asemblera, polecenia rozumiane przez procesor sÈ caïkiem proste.
Instrukcje te skïadajÈ siÚ z operacji i czasem dodatkowych operacji opisujÈcych cel
i (lub) ródïo operacji. Operacje te przenoszÈ dane w pamiÚci, aby wykonaÊ jakie-
go rodzaju podstawowe dziaïania matematyczne, lub przerywajÈ dziaïanie proce-
sora, by wykonaÊ co innego. To jest wszystko, co procesor potrafi wykonaÊ. Jednak,
podobnie jak za pomocÈ stosunkowo niewielkiego alfabetu napisano miliony ksiÈ ek,
tak samo, korzystajÈc z niewielkiego zbioru instrukcji maszynowych, mo na utworzyÊ
nieskoñczenie wiele programów.
Procesory posiadajÈ równie wïasny zestaw specjalnych zmiennych zwanych
rejestrami. WiÚkszo Ê instrukcji wykorzystuje rejestry do odczytywania lub zapi-
sywania danych, wiÚc ich poznanie jest niezbÚdne do zrozumienia instrukcji. Du y
obraz wciÈ siÚ powiÚksza…
0x252 Procesor x86
Procesor 8086 byï pierwszym procesorem w architekturze x86. Zostaï opracowany
i wyprodukowany przez firmÚ Intel, która pó niej wprowadzaïa na rynek bardziej
zaawansowane procesory z tej rodziny: 80186, 80286m 80386 i 80486. Je eli pamiÚ-
tasz, e w latach 80. i 90. ubiegïego wieku mówiono o procesorach 386 i 486, chodziïo
wówczas o te wïa nie ukïady.
Procesor x86 posiada kilka rejestrów, które sÈ dla niego czym w rodzaju we-
wnÚtrznych zmiennych. Mógïbym teraz przeprowadziÊ abstrakcyjny wykïad na temat
rejestrów, ale uwa am, e lepiej zobaczyÊ co na wïasne oczy. NarzÚdzia progra-
mistyczne GNU zawierajÈ równie debuger o nazwie GDB. Debugery sÈ u ywane
przez programistów do wykonywania skompilowanych programów krok po kroku,
badania pamiÚci programu i przeglÈdania rejestrów procesora. Programista, który
38 0x200
27. nigdy nie korzystaï z debugera do przyjrzenia siÚ wewnÚtrznym mechanizmom
pracy programu, jest jak siedemnastowieczny badacz, który nigdy nie u ywaï mi-
kroskopu. Debuger, tak jak mikroskop, pozwala hakerowi na precyzyjne przyjrze-
nie siÚ kodowi maszynowemu, ale mo liwo ci debugera wykraczajÈ daleko poza tÚ
metaforÚ. W przeciwieñstwie do mikroskopu, debuger umo liwia obejrzenie wy-
konywania programu ze wszystkich stron, wstrzymanie go oraz zmianÚ wszelkich
parametrów.
Poni ej GDB zostaï u yty do wy wietlenia stanu rejestrów procesora tu przed
rozpoczÚciem programu:
reader@hacking:~/booksrc $ gdb -q ./a.out
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) break main
Breakpoint 1 at 0x804837a
(gdb) run
Starting program: /home/reader/booksrc/a.out
Breakpoint 1, 0x0804837a in main ()
(gdb) info registers
eax 0xbffff894 -1073743724
ecx 0x48e0fe81 1222704769
edx 0x1 1
ebx 0xb7fd6ff4 -1208127500
esp 0xbffff800 0xbffff800
ebp 0xbffff808 0xbffff808
esi 0xb8000ce0 -1207956256
edi 0x0 0
eip 0x804837a 0x804837a <main+6>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) quit
The program is running. Exit anyway? (y or n) y
reader@hacking:~/booksrc $
Punkt wstrzymania zostaï ustawiony przy funkcji main(), wiÚc wykonywanie
programu zostanie zatrzymane tu przed uruchomieniem naszego kodu. NastÚpnie
GDB uruchamia program, zatrzymuje siÚ w punkcie wstrzymania i wy wietla wszyst-
kie rejestry procesora i ich bie Ècy stan.
Pierwsze cztery rejestry (EAX, ECX, EDX i EBX) sÈ rejestrami ogólnego prze-
znaczenia. SÈ to odpowiednio rejestry: akumulatorowy, licznika, danych i bazowy.
SÈ one wykorzystywane do ró nych celów, ale sïu È gïównie jako zmienne tymcza-
sowe dla procesora, gdy wykonuje instrukcje kodu maszynowego.
Programowanie 39
28. Kolejne cztery rejestry (ESP, EBP, ESI i EDI) sÈ równie rejestrami ogólnego
przeznaczenia, ale czasem nazywa siÚ je wska nikami i indeksami. SÈ to odpowiednio:
wska nik, wska nik bazowy, ródïo i przeznaczenie. Pierwsze dwa rejestry sÈ nazy-
wane wska nikami, poniewa przechowujÈ 32-bitowe adresy, wskazujÈce do miejsc
w pamiÚci. Te rejestry sÈ istotne dla wykonywania programu i zarzÈdzania pamiÚciÈ.
Pó niej omówiÚ je dokïadniej. Kolejne dwa rejestry, z technicznego punktu widzenia
równie wska nikowe, czÚsto wykorzystywane sÈ do wskazywania ródïa i celu, gdy
dane muszÈ byÊ odczytane lub zapisane. IstniejÈ instrukcje odczytujÈce i zapisujÈce,
które u ywajÈ tych rejestrów, ale przewa nie mo na je traktowaÊ jak zwykïe rejestry
ogólnego przeznaczenia.
Rejestr EIP jest rejestrem wska nika instrukcji, wskazujÈcym aktualnie wykony-
wanÈ instrukcjÚ. Tak jak dziecko podczas czytania pokazuje sobie palcem poszczegól-
ne sïowa, tak procesor odczytuje konkretne instrukcje, korzystajÈc z EIP jak z palca.
Oczywi cie, rejestr ten jest bardzo istotny i bÚdzie czÚsto wykorzystywany podczas
debugowania. Aktualnie wskazuje adres pamiÚci 0x804838a.
Pozostaïy rejestr, EFLAGS, zawiera kilka flag bitowych wykorzystywanych do
porównañ oraz segmentacji pamiÚci. PamiÚÊ jest dzielona na kilka ró nych seg-
mentów, co opiszÚ pó niej, a rejestr ten pozwala je ledziÊ. Przewa nie mo emy je
zignorowaÊ, poniewa rzadko konieczny jest bezpo redni dostÚp do nich.
0x253 JÚzyk asembler
Poniewa w niniejszej ksiÈ ce wykorzystujemy skïadniÚ Intela w jÚzyku asembler,
musimy odpowiednio skonfigurowaÊ narzÚdzia. W GDB skïadniÚ dezasemblera
mo na ustawiÊ na intelowskÈ, wpisujÈc po prostu set disassembly intel lub w skrócie
set dis intel. Mo emy skonfigurowaÊ GDB tak, aby to ustawienie byïo aktywne
przy ka dym uruchomieniu, umieszczajÈc to polecenie w pliku .gdbinit w katalogu
domowym.
reader@hacking:~/booksrc $ gdb -q
(gdb) set dis intel
(gdb) quit
reader@hacking:~/booksrc $ echo "set dis intel" > ~/.gdbinit
reader@hacking:~/booksrc $ cat ~/.gdbinit
set dis intel
reader@hacking:~/booksrc $
Po skonfigurowaniu GDB tak, eby wykorzystywaï skïadniÚ Intela, zapoznajmy
siÚ z niÈ. W skïadni Intela instrukcje asemblera majÈ zwykle poni szÈ postaÊ:
operacja <cel>, < ród o>
Warto ciami docelowymi i ródïowymi sÈ rejestr, adres pamiÚci lub warto Ê.
Operacje sÈ zwykle intuicyjnie rozumianymi mnemonikami. Operacja mov przenosi
warto Ê ze ródïa do celu, sub odejmuje, inc inkrementuje itd. Przykïadowo poni sze
instrukcje przenoszÈ warto Ê z ESP do EBP, a nastÚpnie odejmujÈ 8 od ESP (zapisu-
jÈc wynik w ESP).
40 0x200
29. 8048375: 89 e5 mov ebp,esp
8048377: 83 ec 08 sub esp,0x8
DostÚpne sÈ równie operacje wykorzystywane do sterowanie przebiegiem
wykonywania. Operacja cmp sïu y do porównywania warto ci, a niemal wszystkie
operacje, których nazwa rozpoczyna siÚ literÈ j, sïu È do przeskakiwania do innej
czÚ ci kodu (w zale no ci od wyniku porównania). W poni szym przykïadzie najpierw
4-bajtowa warto Ê znajdujÈca w ERB minus 4 jest porównywana z liczbÈ 9. NastÚpna
instrukcja jest skrótem od przeskocz, je eli mniejsze lub równe ni (ang. jump if less
than or equal to), odnoszÈcym siÚ do wyniku wcze niejszego porównania. Je eli
warto Ê ta jest mniejsza lub równa 9, wykonywanie przeskoczy do instrukcji znaj-
dujÈcej siÚ pod adresem 0x8048393. W przeciwnym przypadku wykonywana bÚdzie
kolejna instrukcja z bezwarunkowym przeskokiem. Je eli warto Ê nie bÚdzie mniejsza
ani równa 9, procesor przeskoczy do 0x80483a6.
804838b: 83 7d fc 09 cmp DWORD PTR [ebp-4],0x9
804838f: 7e 02 jle 8048393 <main+0x1f>
8048391: eb 13 jmp 80483a6 <main+0x32>
Przykïady te pochodzÈ z naszej poprzedniej dezasemblacji. Skonfigurowali my
debuger do wykorzystywania skïadni Intela, wiÚc wykorzystajmy go do kroczenia
przez nasz pierwszy program na poziomie instrukcji asemblera.
W kompilatorze GCC mo na podaÊ opcjÚ –g, dziÚki czemu zostanÈ doïÈczone
dodatkowe informacje debugowania, co da GDB dostÚp do kodu ródïowego.
reader@hacking:~/booksrc $ gcc -g firstprog.c
reader@hacking:~/booksrc $ ls -l a.out
-rwxr-xr-x 1 matrix users 11977 Jul 4 17:29 a.out
reader@hacking:~/booksrc $ gdb -q ./a.out
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) list
1 #include <stdio.h>
2
3 int main()
4 {
5 int i;
6 for(i=0; i < 10; i++)
7 {
8 printf("Hello, world!n");
9 }
10 }
(gdb) disassemble main
Dump of assembler code for function main():
0x08048384 <main+0>: push ebp
0x08048385 <main+1>: mov ebp,esp
0x08048387 <main+3>: sub esp,0x8
0x0804838a <main+6>: and esp,0xfffffff0
0x0804838d <main+9>: mov eax,0x0
0x08048392 <main+14>: sub esp,eax
Programowanie 41
30. 0x08048394 <main+16>: mov DWORD PTR [ebp-4],0x0
0x0804839b <main+23>: cmp DWORD PTR [ebp-4],0x9
0x0804839f <main+27>: jle 0x80483a3 <main+31>
0x080483a1 <main+29>: jmp 0x80483b6 <main+50>
0x080483a3 <main+31>: mov DWORD PTR [esp],0x80484d4
0x080483aa <main+38>: call 0x80482a8 <_init+56>
0x080483af <main+43>: lea eax,[ebp-4]
0x080483b2 <main+46>: inc DWORD PTR [eax]
0x080483b4 <main+48>: jmp 0x804839b <main+23>
0x080483b6 <main+50>: leave
0x080483b7 <main+51>: ret
End of assembler dump.
(gdb) break main
Breakpoint 1 at 0x8048394: file firstprog.c, line 6.
(gdb) run
Starting program: /hacking/a.out
Breakpoint 1, main() at firstprog.c:6
6 for(i=0; i < 10; i++)
(gdb) info register eip
eip 0x8048394 0x8048394
(gdb)
Najpierw wypisywany jest kod ródïowy oraz dezasemblacja funkcji main().
NastÚpnie na poczÈtku main() ustawiany jest punkt wstrzymania i program jest
uruchamiany. Ten punkt wstrzymania po prostu informuje debuger, aby wstrzymaï
wykonywanie programu, gdy dojdzie do tego punktu. Poniewa punkt wstrzymania
zostaï ustawiony na poczÈtku funkcji main(), program dociera do punktu wstrzy-
mania i zatrzymuje siÚ przed wykonaniem jakichkolwiek instrukcji z funkcji main().
NastÚpnie wy wietlana jest warto Ê EIP (wska nika instrukcji).
Nale y zwróciÊ uwagÚ, e EIP zawiera adres pamiÚci, wskazujÈcy instrukcjÚ
w dezasemblacji funkcji main() (pogrubionej na listingu). Wcze niejsze instrukcje
(zapisane kursywÈ), razem nazywane prologiem funkcji, sÈ generowane przez kom-
pilator w celu ustawienia pamiÚci dla reszty zmiennych lokalnych funkcji main().
Jednym z powodów konieczno ci deklaracji zmiennych w jÚzyku C jest pomoc
w konstrukcji tej czÚ ci kodu. Debuger „wie”, e ta czÚ Ê kodu jest generowana
automatycznie i potrafi jÈ pominÈÊ. Prologiem funkcji zajmiemy siÚ pó niej, a na
razie, wzorujÈc siÚ na GDB, pominiemy go.
Debuger GDB zawiera bezpo redniÈ metodÚ badania pamiÚci za pomocÈ pole-
cenia x, które jest skrótem od examine. Badanie pamiÚci jest bardzo wa nÈ umiejÚtno-
ciÈ ka dego hakera. WiÚkszo Ê technik hakerskich przypomina sztuczki magiczne
— wydajÈ siÚ niesamowite i magiczne, dopóki nie poznasz oszustwa. Zarówno
w magii, jak i hakerstwie, je eli popatrzymy w odpowiednie miejsce, sztuczka stanie
siÚ oczywista. Jest to jeden z powodów, dla których dobry magik nigdy nie wykonuje
dwa razy tego samego triku w czasie jednego wystÚpu. Jednak za pomocÈ debugera,
takiego jak GDB, mo emy dokïadnie zbadaÊ ka dy aspekt wykonywania programu,
wstrzymaÊ go, przej Ê krok dalej i powtórzyÊ tyle razy, ile potrzeba. Poniewa
dziaïajÈcy program to w wiÚkszo ci procesor i segmenty pamiÚci, zbadanie pamiÚci
jest pierwszÈ z metod przekonania siÚ, co naprawdÚ siÚ dzieje.
42 0x200
31. Polecenie examine w GDB mo e zostaÊ u yte do przyjrzenia siÚ okre lonym adre-
som pamiÚci na ró ne sposoby. To polecenie oczekuje dwóch argumentów: miejsca
w pamiÚci, które ma byÊ zbadane, oraz okre lenia sposobu wy wietlania zawarto ci.
Do okre lania formatu wy wietlania równie u ywany jest jednoliterowy skrót,
który mo na poprzedziÊ liczbÈ elementów do zbadania. NajczÚ ciej wykorzystywane
sÈ poni sze skróty formatujÈce:
o — wy wietla w formacie ósemkowym,
x — wy wietla w formacie szesnastkowym,
u — wy wietla w standardowym systemie dziesiÚtnym, bez znaków,
t — wy wietla w formacie binarnym.
Liter tych mo na u yÊ z poleceniem examine do zbadania okre lonego adresu
pamiÚci. W poni szym przykïadzie wykorzystano bie Ècy adres z rejestru EIP.
Skrócone polecenia sÈ czÚsto wykorzystywane w GDB i nawet info register eip
mo na skróciÊ do i r eip.
(gdb) i r eip
eip 0x8048384 0x8048384 <main+16>
(gdb) x/o 0x8048384
0x8048384 <main+16>: 077042707
(gdb) x/x $eip
0x8048384 <main+16>: 0x00fc45c7
(gdb) x/u $eip
0x8048384 <main+16>: 16532935
(gdb) x/t $eip
0x8048384 <main+16>: 00000000111111000100010111000111
(gdb)
PamiÚÊ, którÈ wskazuje rejestr EIP, mo e byÊ zbadana za pomocÈ adresu prze-
chowywanego w EIP. Debuger umo liwia bezpo rednie odwoïywanie siÚ do reje-
strów, wiÚc $eip jest równowa ne warto ci przechowywanej aktualnie w EIP. Warto Ê
077042707 w systemie ósemkowym jest tym samym, co 0x00fc45c7 w systemie szes-
nastkowym, co jest tym samym, co 16532935 w systemie dziesiÚtnym, co z kolei jest
tym samym, co 00000000111111000100010111000111 w systemie dwójkowym.
Format polecenia examine mo na równie poprzedziÊ cyfrÈ, okre lajÈcÈ liczbÚ
badanych jednostek w docelowym adresie.
(gdb) x/2x $eip
0x8048384 <main+16>: 0x00fc45c7 0x83000000
(gdb) x/12x $eip
0x8048384 <main+16>: 0x00fc45c7 0x83000000 0x7e09fc7d 0xc713eb02
0x8048394 <main+32>: 0x84842404 0x01e80804 0x8dffffff 0x00fffc45
0x80483a4 <main+48>: 0xc3c9e5eb 0x90909090 0x90909090 0x5de58955
(gdb)
Programowanie 43
32. Domy lnym rozmiarem jednej jednostki jest 4-bajtowa jednostka zwana sïowem.
Rozmiar jednostek wy wietlania dla polecenia examine mo e byÊ zmieniany poprzez
dodanie odpowiedniej litery za literÈ formatujÈcÈ. Poprawnymi literami rozmiaru sÈ:
b — pojedynczy bajt,
h — póïsïowo o rozmiarze dwóch bajtów,
w — sïowo o rozmiarze czterech bajtów,
g — sïowo podwójne o rozmiarze o miu bajtów.
Wprowadza to pewien zamÚt, poniewa czasami termin sïowo okre la równie
warto ci 2-bajtowe. W takim przypadku podwójne sïowo lub DWORD oznacza war-
to Ê 4-bajtowÈ. W ksiÈ ce tej terminy „sïowo” i DWORD bÚdÈ okre laïy warto ci
4-bajtowe. Do okre lania warto ci 2-bajtowych bÚdzie u ywany termin „póïsïowo”.
Poni sze wyj cie z GDB obrazuje pamiÚÊ wy wietlanÈ w ró nych rozmiarach.
(gdb) x/8xb $eip
0x8048384 <main+16>: 0xc7 0x45 0xfc 0x00 0x00 0x00 0x00 0x83
(gdb) x/8xh $eip
0x8048384 <main+16>: 0x45c7 0x00fc 0x0000 0x8300 0xfc7d 0x7e09 0xeb02 0xc713
(gdb) x/8xw $eip
0x8048384 <main+16>: 0x00fc45c7 0x83000000 0x7e09fc7d 0xc713eb02
0x8048394 <main+32>: 0x84842404 0x01e80804 0x8dffffff 0x00fffc45
(gdb)
Je eli dobrze siÚ przyjrzysz, zauwa ysz w powy szych danych co dziwnego.
Pierwsze polecenie examine pokazuje pierwsze osiem bajtów i, oczywi cie, polece-
nia examine wykorzystujÈce wiÚksze jednostki wy wietlajÈ wiÚcej danych. Jednak e
wynik pierwszego polecenia pokazuje, e pierwszymi dwoma bajtami sÈ 0xc7 i 0x45,
ale gdy badane jest póïsïowo w dokïadnie tym samym adresie pamiÚci, pokazywana
jest warto Ê 0x45c7, czyli bajty sÈ odwrócone. Ten sam efekt mo emy zauwa yÊ
w przypadku peïnego 4-bajtowego sïowa wy wietlanego jako 0x00fc45c7. Je eli
jednak pierwsze cztery bajty sÈ wy wietlane bajt po bajcie majÈ one kolejno Ê 0xc7,
0x45m 0xfc i 0x00.
Jest tak, poniewa w procesorze x86 warto ci sÈ przechowywane w kolejno ci
little endian, co oznacza, e najmniej istotny bajt jest przechowywany jako pierwszy.
Je eli np. cztery bajty majÈ byÊ interpretowane jako jedna warto Ê, bajty muszÈ byÊ
u yte w odwrotnej kolejno ci. Debuger GDB jest na tyle mÈdry, e wie, jak prze-
chowywane sÈ warto ci, wiÚc gdy badane jest sïowo lub póïsïowo, bajty muszÈ byÊ
odwrócone w celu wy wietlenia poprawnych warto ci w ukïadzie szesnastkowym.
Przejrzenie tych wy wietlanych warto ci w systemie szesnastkowym i jako liczb
dziesiÚtnych bez znaku mo e uïatwiÊ zrozumienie tej kwestii.
(gdb) x/4xb $eip
0x8048384 <main+16>: 0xc7 0x45 0xfc 0x00
(gdb) x/4ub $eip
0x8048384 <main+16>: 199 69 252 0
(gdb) x/1xw $eip
44 0x200
33. 0x8048384 <main+16>: 0x00fc45c7
(gdb) x/1uw $eip
0x8048384 <main+16>: 16532935
(gdb) quit
The program is running. Exit anyway? (y or n) y
reader@hacking:~/booksrc $ bc -ql
199*(256^3) + 69*(256^2) + 252*(256^1) + 0*(256^0)
3343252480
0*(256^3) + 252*(256^2) + 69*(256^1) + 199*(256^0)
16532935
quit
reader@hacking:~/booksrc $
Pierwsze cztery bajty sÈ pokazane zarówno w notacji szesnastkowej, jak i standar-
dowej, dziesiÚtnej. Kalkulator dziaïajÈcy w wierszu poleceñ bc posïu yï do wyka-
zania, e je eli bajty zostanÈ zinterpretowane w nieprawidïowej kolejno ci, otrzy-
mamy w wyniku zupeïnie nieprawidïowÈ warto Ê 3343252480. Kolejno Ê bajtów
w danej architekturze jest istotnym czynnikiem, którego nale y byÊ wiadomym.
ChoÊ wiÚkszo Ê narzÚdzi debugujÈcych i kompilatorów zajmuje siÚ automatycznie
szczegóïami zwiÈzanymi z kolejno ciÈ bajtów, czasem bÚdziesz musiaï samodzielnie
manipulowaÊ pamiÚciÈ.
Oprócz konwersji kolejno ci bajtów, GDB mo e za pomocÈ polecenia examine
wykonywaÊ inne konwersje. Widzieli my ju , e GDB mo e dezasemblowaÊ instruk-
cje jÚzyka maszynowego na zrozumiaïe dla czïowieka instrukcje asemblera. Pole-
cenie examine przyjmuje równie literÚ formatujÈcÈ i (skrót od instruction), co po-
woduje wy wietlenie pamiÚci jako dezasemblowanych instrukcji jÚzyka asembler.
reader@hacking:~/booksrc $ gdb -q ./a.out
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) break main
Breakpoint 1 at 0x8048384: file firstprog.c, line 6.
(gdb) run
Starting program: /home/reader/booksrc/a.out
Breakpoint 1, main () at firstprog.c:6
6 for(i=0; i < 10; i++)
(gdb) i r $eip
eip 0x8048384 0x8048384 <main+16>
(gdb) x/i $eip
0x8048384 <main+16>: mov DWORD PTR [ebp-4],0x0
(gdb) x/3i $eip
0x8048384 <main+16>: mov DWORD PTR [ebp-4],0x0
0x804838b <main+23>: cmp DWORD PTR [ebp-4],0x9
0x804838f <main+27>: jle 0x8048393 <main+31>
(gdb) x/7xb $eip
0x8048384 <main+16>: 0xc7 0x45 0xfc 0x00 0x00 0x00 0x00
(gdb) x/i $eip
0x8048384 <main+16>: mov DWORD PTR [ebp-4],0x0
(gdb)
Programowanie 45
34. W powy szym przykïadzie program a.out zostaï uruchomiony w GDB z punktem
wstrzymania ustawionym w main(). Poniewa rejestr EIP wskazuje na pamiÚÊ, która
faktycznie zawiera instrukcje w jÚzyku maszynowym, ïadnie poddajÈ siÚ one proce-
sowi dezasemblacji.
Wcze niejsza dezasemblacja za pomocÈ objectdump potwierdza, e siedem bajtów,
które wskazuje EIP, to faktycznie jÚzyk maszynowy dla odpowiedniej instrukcji
asemblera.
8048384: c7 45 fc 00 00 00 00 mov DWORD PTR [ebp-4],0x0
Ta instrukcja asemblera przenosi warto Ê 0 do pamiÚci znajdujÈcej siÚ pod ad-
resem przechowywanym w rejestrze EBP minus 4. To wïa nie w tym miejscu pa-
miÚci przechowywana jest zmienna i z jÚzyka C. Zmienna ta zostaïa zadeklarowana
jako liczba caïkowita (int), wykorzystujÈca 4 bajty pamiÚci w przypadku procesora
x86. To polecenie po prostu zeruje zmiennÈ i dla pÚtli for. Gdyby my teraz zbadali
tÚ pamiÚÊ, zawieraïaby ona tylko przypadkowe mieci. PamiÚÊ w tej lokacji mo na
zbadaÊ na ró ne sposoby:
(gdb) i r ebp
ebp 0xbffff808 0xbffff808
(gdb) x/4xb $ebp - 4
0xbffff804: 0xc0 0x83 0x04 0x08
(gdb) x/4xb 0xbffff804
0xbffff804: 0xc0 0x83 0x04 0x08
(gdb) print $ebp - 4
$1 = (void *) 0xbffff804
(gdb) x/4xb $1
0xbffff804: 0xc0 0x83 0x04 0x08
(gdb) x/xw $1
0xbffff804: 0x080483c0
(gdb)
Rejestr EBP zawiera adres 0xbffff808, a instrukcja asemblera bÚdzie zapisywaïa
do adresu o tej warto ci pomniejszonej o 4 — 0xbfffff804. Polecenie examine mo e
zbadaÊ bezpo rednio ten adres pamiÚci lub wykonaÊ odpowiednie obliczenia w locie.
Za pomocÈ polecenia print równie mo na wykonaÊ proste dziaïania arytmetyczne,
a wynik zostanie zapisany w tymczasowej zmiennej w debugerze. Zmienna ta nosi
nazwÚ $1 i mo e pó niej zostaÊ u yta do szybkiego uzyskania dostÚpu do okre lonego
miejsca w pamiÚci. Ka da z przedstawionych wy ej metod pozwala uzyskaÊ ten
sam rezultat: wy wietlenie znalezionych w pamiÚci 4 bajtów mieci, które zostanÈ
wyzerowane po wykonaniu bie Ècej instrukcji.
Uruchommy bie ÈcÈ instrukcjÚ, korzystajÈc z polecenia nexti, bÚdÈcego skrótem
dla next instruction (nastÚpna instrukcja). Procesor odczyta instrukcjÚ z EIP, wykona
jÈ i przestawi EIP na kolejnÈ instrukcjÚ.
46 0x200