Wydawnictwo Helion
ul. Koœciuszki 1c
44-100 Gliwice
tel. 032 230 98 63
Hacking. Sztuka
penetracji. Wydanie II
Autor: Jon Erickson
T³umaczenie: Marcin Rogó¿
ISBN: 978-83-246-1802-6
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
Oto elementarz prawdziwego hakera!
S P I S T R E C I
PRZEDMOWA 11
PODZIKOWANIA 12
0X100 WPROWADZENIE
13
0X200 PROGRAMOWANIE
19
0x210 Istota
programowania .....................................................................................20
0x220 Pseudokod ......................................................................................................22
0x230 Struktury sterujce ...........................................................................................22
0x231 If-Then-Else ........................................................................................22
0x232 Ptle
While/Until ................................................................................24
0x233 Ptle for .............................................................................................25
0x240 Podstawowe pojcia programistyczne .............................................................26
0x241 Zmienne .............................................................................................26
0x242 Operatory arytmetyczne .....................................................................27
0x243 Operatory
porównania .......................................................................28
0x244 Funkcje
...............................................................................................30
0x250 Zaczynamy brudzi sobie rce ..........................................................................34
0x251 Wikszy
obraz ....................................................................................35
0x252 Procesor
x86 ......................................................................................38
0x253 Jzyk
asembler ...................................................................................40
0x260 Wracamy do podstaw .....................................................................................53
0x261 acuchy ............................................................................................53
0x262 Ze znakiem, bez znaku, duga i krótka ................................................57
0x263 Wskaniki
...........................................................................................59
0x264 acuchy
formatujce
.........................................................................63
6
Hacking. Sztuka penetracji
0x265 Rzutowanie
typów .............................................................................67
0x266 Argumenty wiersza polece ...............................................................74
0x267 Zasig
zmiennych
...............................................................................78
0x270 Segmentacja
pamici
.......................................................................................85
0x271 Segmenty pamici w jzyku C ............................................................92
0x272 Korzystanie ze sterty ...........................................................................94
0x273 Funkcja malloc() ze sprawdzaniem bdów .........................................96
0x280 Budujemy na podstawach ...............................................................................98
0x281 Dostp do plików ...............................................................................98
0x282 Prawa dostpu do plików .................................................................104
0x283 Identyfikatory
uytkowników
............................................................105
0x284 Struktury ..........................................................................................114
0x285 Wskaniki do funkcji .........................................................................117
0x286 Liczby
pseudolosowe
........................................................................118
0x287 Gry
hazardowe
.................................................................................120
0X300 NADUYCIA
133
0x310 Uogólnione techniki naduy .........................................................................136
0x320 Przepenienia
bufora
......................................................................................137
0x321 Luki zwizane z przepenieniem stosowym .......................................140
0x330 Eksperymenty z BASH ....................................................................................152
0x331 Wykorzystanie
rodowiska ...............................................................161
0x340 Przepenienia w innych segmentach ..............................................................170
0x341 Podstawowe przepenienie na stercie ...............................................170
0x342 Przepenienia wskaników funkcyjnych .............................................176
0x350 acuchy
formatujce
....................................................................................187
0x351 Parametry
formatujce .....................................................................188
0x352 Podatno cigów formatujcych na ataki ........................................190
0x353 Odczyt spod dowolnych adresów pamici ........................................192
0x354 Zapis pod dowolnymi adresami pamici ...........................................193
0x355 Bezporedni dostp do parametrów .................................................201
0x356 Uycie zapisu krótkich liczb cakowitych ...........................................203
0x357 Obejcia z uyciem sekcji dtors .........................................................205
0x358 Kolejny saby punkt programu notesearch ........................................210
0x359 Nadpisywanie globalnej tabeli przesuni ........................................212
0X400 SIECI
217
0x410 Model
OSI .....................................................................................................217
0x420 Gniazda
.........................................................................................................220
0x421 Funkcje
obsugujce
gniazda ............................................................221
0x422 Adresy
gniazd ..................................................................................223
0x423 Sieciowa
kolejno
bajtów
................................................................224
0x424 Konwersja adresu internetowego .....................................................225
Spis treci
7
0x425 Prosty przykad serwera ....................................................................226
0x426 Przykad klienta WWW .....................................................................230
0x427 Maleki serwer WWW ......................................................................235
0x430 Zagldamy do niszych warstw .....................................................................240
0x431 Warstwa
cza
danych
......................................................................240
0x432 Warstwa
sieci
...................................................................................242
0x433 Warstwa
transportowa
.....................................................................244
0x440 Podsuchiwanie
w
sieci
..................................................................................247
0X441 Sniffer surowych pakietów ...............................................................249
0x442 Sniffer
libpcap ..................................................................................251
0x443 Dekodowanie
warstw ......................................................................253
0x444 Aktywne
podsuchiwanie .................................................................263
0x450 Odmowa
usugi
.............................................................................................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 Przejcie
TCP/IP .............................................................................................283
0x461 Rozczanie za pomoc RST ..............................................................283
0x462 Przejcie 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 uyciem bezczynnego komputera ...............................291
0x475 Zabezpieczenie wyprzedzajce (Shroud) ...........................................293
0x480 Id i wam si gdzie .....................................................................................298
0x481 Analiza z uyciem GDB .....................................................................299
0x482 Prawie ma znaczenie ........................................................................301
0x483 Shellcode wicy port .....................................................................304
0X500 SHELLCODE
309
0x510 Asembler a C .................................................................................................309
0x511 Linuksowe wywoania systemowe w asemblerze .............................312
0x520 cieka do shellcode ......................................................................................315
0x521 Instrukcje asemblera wykorzystujce stos .........................................315
0x522 Badanie za pomoc GDB ..................................................................317
0x523 Usuwanie
bajtów
zerowych
..............................................................319
0x530 Kod
wywoujcy
powok
..............................................................................324
0x531 Kwestia
uprawnie
...........................................................................328
0x532 Skracamy
jeszcze
bardziej
.................................................................331
8
Hacking. Sztuka penetracji
0x540 Kod powoki wicy port .............................................................................332
0x541 Duplikowanie standardowych deskryptorów plików .........................336
0x542 Rozgaziajce struktury sterujce ....................................................338
0x550 Shellcode nawizujcy poczenie powrotne .................................................343
0X600 RODKI
ZAPOBIEGAWCZE
349
0x610 rodki zapobiegawcze, które wykrywaj .......................................................350
0x620 Demony
systemowe ......................................................................................351
0x621 Byskawiczne wprowadzenie do sygnaów .......................................352
0x622 Demon
tinyweb
................................................................................354
0x630 Narzdzia
pracy
.............................................................................................358
0x631 Narzdzie naduywajce tinywebd ...................................................359
0x640 Pliki
dziennika ...............................................................................................364
0x641 Wmieszaj si w tum ........................................................................365
0x650 Przeoczywszy
oczywiste ................................................................................366
0x651 Krok po kroku ...................................................................................367
0x652 Ponowne skadanie wszystkiego ......................................................371
0x653 Robocze procesy potomne ................................................................377
0x660 Zaawansowana sztuka kamuflau .................................................................378
0x661 Faszowanie rejestrowanego adresu IP .............................................379
0x662 Nierejestrowane
naduycie
...............................................................383
0x670 Caa
infrastruktura
.........................................................................................386
0x671 Ponowne
wykorzystanie
gniazda
......................................................386
0x680 Przemyt
adunku
............................................................................................390
0x681 Kodowanie
acuchów .....................................................................391
0x682 Jak ukry puapk? ...........................................................................394
0x690 Ograniczenia
bufora ......................................................................................395
0x691 Polimorficzny kod powoki z drukowalnymi znakami ASCII ...............397
0x6a0 Zabezpieczenia
wzmacniajce .......................................................................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 koci ..................................................................................422
0X700 KRYPTOLOGIA
425
0x710 Teoria informacji ............................................................................................426
0x711 Bezwarunkowe
bezpieczestwo .......................................................426
0x712 Szyfr z kluczem jednorazowym .........................................................426
Spis treci
9
0x713 Kwantowa dystrybucja kluczy ...........................................................427
0x714 Bezpieczestwo
obliczeniowe
...........................................................428
0x720 Rozlego
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 rozkadu na czynniki autorstwa Petera Shora ....437
0x750 Szyfry
hybrydowe ..........................................................................................438
0x751 Ataki z ukrytym porednikiem ...........................................................439
0x752 „Odciski palców” komputerów w protokole SSH ...............................443
0x753 Rozmyte „odciski palców” ................................................................447
0x760 amanie
hase ...............................................................................................451
0x761 Ataki
sownikowe
.............................................................................453
0x762 Ataki na zasadzie penego przegldu ................................................455
0x763 Tablica wyszukiwania skrótów .........................................................457
0x764 Macierz
prawdopodobiestwa
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 penego przegldu w trybie offline .......................470
0x782 Ponowne uycie strumienia klucza ....................................................471
0x783 Tablice sownikowe z wektorami IV ..................................................472
0x784 Przekierowanie
IP
.............................................................................473
0x785 Atak Fluhrera, Mantina i Shamira (FMS) ............................................474
0X800 PODSUMOWANIE
485
0x810 Bibliografia i dodatkowe informacje ..............................................................486
0x820 Kody
ródowe ..............................................................................................487
O PYCIE CD
489
SKOROWIDZ 491
0x200
P R O G R A M O W A N I E
Haker to termin uywany do okrelenia zarówno piszcych kod, jak
i tych, którzy wykorzystuj znajdujce si w nim bdy. Cho obie
grupy hakerów maj róne cele, to i jedni, i drudzy stosuj podobne
techniki rozwizywania problemów. A ze wzgldu na fakt, e umiejt-
no programowania pomaga tym, którzy wykorzystuj bdy w kodzie,
za zrozumienie metod zastosowania pomaga tym, którzy programuj,
wielu hakerów robi obie rzeczy jednoczenie. Interesujce rozwizania istniej
i w zakresie technik pisania eleganckiego kodu, i technik sucych do wykorzysty-
wania saboci programów. Hakerstwo to w rzeczywistoci akt znajdowania pomy-
sowych i nieintuicyjnych rozwiza problemów.
Metody stosowane w narzdziach wykorzystujcych saboci programów zwy-
kle s zwizane z zastosowaniem regu funkcjonowania komputera w sposób, któ-
rego nigdy nie brano pod uwag — pozornie magiczne rezultaty osiga si zwykle
po skupieniu si na omijaniu zaimplementowanych zabezpiecze. Metody uywa-
ne podczas pisania programów s podobne, bo równie wykorzystuj reguy funk-
cjonowania komputera w nowy i pomysowy sposób, jednak w tym przypadku koco-
wym celem jest osignicie najbardziej wydajnego lub najkrótszego kodu ródowego.
Istnieje nieskoczenie wiele programów, które mona napisa w celu wykonania do-
wolnego zadania, jednak wikszo z istniejcych rozwiza jest niepotrzebnie
obszerna, zoona i niedopracowana. Pozostaa niewielka ich liczba to programy
niedue, wydajne i estetyczne. Ta waciwo programów nosi nazw elegancji, za
przemylane i pomysowe rozwizania prowadzce do takiej wydajnoci nazywane
s sztuczkami (ang. hacks). Hakerzy stojcy po obu stronach metodyki programowania
doceniaj zarówno pikno eleganckiego kodu, jak i geniusz pomysowych sztuczek.
20
0x200
W wiecie biznesu mniejsz wag przykada si do przemylanych metod pro-
gramowania i eleganckiego kodu, a wiksz do tworzenia funkcjonalnego kodu w
jak najkrótszym czasie i jak najtaniej. Ze wzgldu na gwatowny wzrost mocy obli-
czeniowej oraz dostpnoci pamici powicenie dodatkowych piciu godzin na
opracowanie nieco szybszego i mniej wymagajcego pod wzgldem pamici frag-
mentu kodu po prostu nie opaca si, gdy mamy do czynienia z nowoczesnymi kom-
puterami dysponujcymi gigahercowymi cyklami procesora i gigabajtami pamici.
Podczas gdy optymalizacje czasu i wykorzystania pamici pozostaj niezauwaone
przez wikszo (z wyjtkiem wyrafinowanych) uytkowników, nowa funkcjonal-
no ma potencja marketingowy. Kiedy najwaniejszym kryterium s pienidze,
powicanie czasu na opracowywanie przemylanych sztuczek optymalizacyjnych
nie ma po prostu sensu.
Elegancj programowania pozostawiono hakerom, hobbistom komputerowym,
których celem nie jest zarabianie, lecz wycinicie wszystkiego, co si da, z ich sta-
rych maszyn Commodore 64, autorom programów wykorzystujcych bdy, pisz-
cym niewielkie i niesamowite fragmenty kodu, dziki którym potrafi przedostawa
si przez wskie szczeliny systemów zabezpiecze, oraz wszystkim innym doceniaj-
cym warto szukania i znajdowania najlepszych moliwych rozwiza. S to osoby
zachwycajce si moliwociami oferowanymi przez programowanie i dostrzegajce
pikno eleganckich fragmentów kodu oraz geniusz przemylanych sztuczek. Poznanie
istoty programowania jest niezbdne do zrozumienia, w jaki sposób mona wykorzy-
stywa saboci programów, dlatego te programowanie to naturalny punkt wyjcia
dla naszych rozwaa.
0x210 Istota programowania
Programowanie to pojcie niezmiernie naturalne i intuicyjne. Program jest tylko
seri instrukcji zapisanych w okrelonym jzyku. Programy wystpuj wszdzie
i nawet technofoby codziennie korzystaj z programów. Opisywanie drogi dojazdu
w jakie miejsce, przepisy kulinarne, rozgrywki pikarskie i spirale DNA to s ró-
nego typu programy. Typowy „program” opisu dojazdu samochodem w konkretne
miejsce moe wyglda tak:
Najpierw zjed w dó Alej Kociuszki, prowadzc na wschód. Jed ni
do momentu, kiedy zobaczysz po prawej stronie koció. Jeeli ulica bdzie
zablokowana ze wzgldu na prowadzone roboty, skr w prawo w ul. Moniuszki,
potem skr w lewo w ul. Prusa i w kocu skr w prawo w ul. Orzeszkowej.
W przeciwnym razie moesz po prostu kontynuowa jazd Alej Kociuszki
i skrci w ul. Orzeszkowej. Jed ni i skr w ul. Docelow. Jed ni
jakie 8 kilometrów — nasz dom bdzie si znajdowa po prawej stronie.
Dokadny adres to ul. Docelowa 743.
Kady, kto zna jzyk polski, pojmie podane instrukcje i bdzie móg kierowa
si nimi podczas jazdy. Bez wtpienia nie zapisano ich zbyt byskotliwie, ale s jed-
noznaczne i zrozumiae.
Programowanie
21
Jednak komputer nie zna jzyka polskiego — rozumie jedynie jzyk maszynowy
(ang. machine language). W celu poinstruowania go, aby wykona pewn czynno,
instrukcj t naley zapisa w zrozumiaym dla niego jzyku. Jednake jzyk ma-
szynowy jest bardzo ezoteryczny i trudny do opanowania. Skada si z serii bitów
oraz bajtów i róni si, w zalenoci od danej architektury komputerowej. Tak wic
w celu napisania programu w jzyku maszynowym dla procesora z rodziny Intel x86
naley okreli warto zwizan z kad instrukcj, sposób interakcji poszczególnych
instrukcji oraz mnóstwo innych niskopoziomowych szczegóów. Tego rodzaju pro-
gramowanie jest bardzo niewdzicznym oraz kopotliwym zadaniem i z pewnoci
nie jest intuicyjne.
Do pokonania komplikacji zwizanych z pisaniem programów w jzyku maszy-
nowym potrzebny jest translator. Jedn z form translatora jzyka maszynowego jest
asembler. To program, który tumaczy jzyk asemblera na kod zapisany w jzyku
maszynowym. Jzyk asemblera jest bardziej przystpny od kodu maszynowego,
poniewa dla rónych instrukcji i zmiennych wykorzystuje nazwy (mnemoniki), a nie
same wartoci liczbowe. Jednak jzyk asemblera wci jest daleki od intuicyjnego.
Nazwy instrukcji s ezoteryczne, a sam jzyk wci jest zaleny od architektury.
Oznacza to, e tak samo, jak jzyk maszynowy dla procesorów Intel x86 róni si
od jzyka maszynowego dla procesorów Sparc, jzyk asemblera x86 jest róny od
jzyka asemblera Sparc. aden program napisany przy uyciu jzyka asemblera dla
architektury jednego procesora nie bdzie dziaa w architekturze innego procesora.
Jeeli program zosta napisany w jzyku asemblera x86, musi zosta przepisany, aby
mona byo uruchomi go w architekturze Sparc. Ponadto w celu napisania wydaj-
nego programu w jzyku asemblera naley zna wiele niskopoziomowych szczegó-
ów, dotyczcych architektury danego procesora.
Problemów tych mona unikn przy uyciu kolejnej formy translatora, nosz-
cego nazw kompilatora (ang. compiler). Kompilator konwertuje kod zapisany w jzy-
ku wysokopoziomowym do postaci kodu maszynowego. Jzyki wysokopoziomowe s
o wiele bardziej intuicyjne od jzyka asemblera i mog by konwertowane do wielu
rónych typów jzyka maszynowego dla odmiennych architektur procesorów. Ozna-
cza to, e jeli zapisze si program w jzyku wysokiego poziomu, ten sam kod moe
zosta skompilowany przez kompilator do postaci kodu maszynowego dla rónych
architektur. C, C++ lub FORTRAN to przykady jzyków wysokopoziomowych.
Program napisany w takim jzyku jest o wiele bardziej czytelny
1
od jzyka asem-
blera lub maszynowego, cho wci musi by zgodny z bardzo surowymi reguami
dotyczcymi sposobu wyraania instrukcji, poniewa w przeciwnym razie kompila-
tor nie bdzie w stanie ich zrozumie.
1
Oczywicie pod warunkiem, e przez czytelny rozumiemy taki, który przypomina jzyk angielski
— przyp. tum.
22
0x200
0x220 Pseudokod
Programici dysponuj jeszcze jednym rodzajem jzyka programowania, noszcym
nazw pseudokodu. Pseudokod (ang. pseudo-code) to po prostu wyraenia zapisane
w jzyku naturalnym, ustawione w struktur ogólnie przypominajc jzyk wyso-
kopoziomowy. Nie jest on rozumiany przez kompilatory, asemblery ani kompute-
ry, ale stanowi przydatny sposób skadania instrukcji. Pseudokod nie jest dobrze
zdefiniowany. W rzeczywistoci wiele osób zapisuje pseudokod odmiennie. Jest to
rodzaj nieokrelonego, brakujcego ogniwa midzy jzykami naturalnymi, takimi
jak angielski lub polski, a wysokopoziomowymi jzykami programowania, takimi
jak C lub Pascal. Pseudokod stanowi wietne wprowadzenie do uniwersalnych
zaoe programistycznych.
0x230 Struktury sterujce
Bez struktur sterujcych program byby jedynie seri kolejno wykonywanych instruk-
cji. Jest to wystarczajce w przypadku bardzo prostych programów, ale wikszo
programów, takich jak np. opis dojazdu, taka nie jest. Prezentowany opis dojazdu
zawiera instrukcje: Jed ni do momentu, kiedy zobaczysz po prawej stronie koció.
Jeeli ulica bdzie zablokowana ze wzgldu na prowadzone roboty… Instrukcje te
nazywamy strukturami sterujcymi, poniewa zmieniaj przebieg wykonywania
programu z prostego, sekwencyjnego, na bardziej zoony i uyteczny.
0x231 If-Then-Else
W naszym opisie dojazdu Aleja Kociuszki moe by w remoncie. Do rozwizania
problemu wynikajcego z tej sytuacji potrzebujemy specjalnego zestawu instrukcji.
Jeeli zdefiniowane okolicznoci nie wystpi (remont), bd wykonywane standardo-
we instrukcje. Tego rodzaju specjalne przypadki mona wzi pod uwag w progra-
mie, korzystajc z jednej z najbardziej naturalnych struktur sterujcych:
if
-
then
-
else
(jeeli-wówczas-w przeciwnym przypadku). Ogólnie wyglda to mniej wicej tak:
If (warunek) then
{
Zestaw instrukcji do wykonania, gdy warunek jest speniony;
}
Else
{
Zestaw instrukcji do wykonania, gdy warunek nie jest speniony;
}
W tej ksice bdziemy posugiwali si pseudokodem przypominajcym jzyk C,
wic kada instrukcja bdzie zakoczona rednikiem, a ich zbiory bd grupowane
za pomoc nawiasów klamrowych i wyróniane wciciem. Pseudokod struktury
if-then-else
dla wskazówek dojazdu mógby przedstawia si nastpujco:
Programowanie
23
Jed w dó Alej Kociuszki;
Jeeli (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;
}
Kada instrukcja znajduje si w osobnym wierszu, a róne zestawy instrukcji
warunkowych s zgrupowane wewntrz nawiasów klamrowych i dla zwikszenia
czytelnoci zastosowano dla nich wcicie. W C i wielu innych jzykach programo-
wania sowo kluczowe
then
jest domniemane i tym samym pomijane, wic pomi-
nlimy je równie w pseudokodzie.
Oczywicie, istniej jzyki, których skadnia wymaga zastosowania sowa klu-
czowego
then
— przykadami mog tu by BASIC, Fortran, a nawet Pascal. Tego
typu rónice syntaktyczne midzy jzykami programowania s niemal nieistotne,
jako e podstawowa struktura jest taka sama. Gdy programista zrozumie zaoenia
tych jzyków, nauczenie si rónych odmian syntaktycznych jest stosunkowo atwe.
Poniewa w dalszych rozdziaach bdziemy programowali w jzyku C, prezento-
wany pseudokod bdzie przypomina ten jzyk. Naley jednak pamita, e moe
on przyjmowa róne postaci.
Inn powszechn regu skadni podobnej do C jest sytuacja, w której zestaw
instrukcji umieszczonych w nawiasach klamrowych zawiera tylko jedn instrukcj.
Dla zwikszenia czytelnoci wci warto dla takiej instrukcji zastosowa wcicie,
ale nie jest ono wymagane. Zgodnie z t regu mona wic napisa wczeniej przed-
stawiony opis dojazdu tak, aby uzyska równowany fragment pseudokodu:
Jed w dó Alej Kociuszki;
Jeeli (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;
Regua mówica o zestawach instrukcji dotyczy wszystkich struktur sterujcych
opisywanych w niniejszej ksice, a sam regu równie mona zapisa w pseu-
dokodzie.
24
0x200
If (w zestawie instrukcji znajduje si tylko jedna instrukcja)
Uycie nawiasów klamrowych do zgrupowania instrukcji jest opcjonalne;
Else
{
Uycie nawiasów klamrowych jest niezbdne;
Poniewa musi istnie logiczny sposób zgrupowania tych instrukcji;
}
Nawet opis skadni mona potraktowa jak program. Istnieje wiele odmian struk-
tury
if-then-else
, np. instrukcje
select
i
case
, ale logika pozostaje taka sama: jeeli
stanie si tak, zrób to i to, w przeciwnym przypadku wykonaj, co poniej (a poniej
mog si znajdowa kolejne instrukcje
if-then
).
0x232
Ptle While/Until
Innym podstawowym pojciem programistycznym jest struktura sterujca
while
,
bdca rodzajem ptli. Programista czsto chce wykona zestaw instrukcji kilka razy.
Program moe wykona to zadanie, stosujc ptl, ale wymaga to okrelenia warun-
ków jej zakoczenia, aby nie trwaa w nieskoczono. Ptla
while
wykonuje podany
zestaw instrukcji, dopóki warunek jest prawdziwy. Prosty program dla godnej
myszy mógby wyglda nastpujco:
While (jeste godna)
{
Znajd jakie jedzenie;
Zjedz jedzenie;
}
Te dwa zestawy instrukcji za instrukcj
while
bd powtarzane, dopóki mysz
bdzie godna. Ilo poywienia odnalezionego przez mysz przy kadej próbie moe
waha si od maego okruszka po cay bochen chleba. Podobnie liczba wykona
zestawu instrukcji w ptli
while
zaley od iloci poywienia znalezionego przez mysz.
Odmian ptli
while
jest ptla
until
, wykorzystywana w Perlu (w C skadnia
ta nie jest uywana). Ptla
until
jest po prostu ptl
while
z odwróconym warunkiem.
Ten sam program dla myszy zapisany z uyciem ptli
until
wygldaby nastpujco:
Until (nie jeste godna)
{
Znajd jakie jedzenie;
Zjedz jedzenie;
}
Logicznie kada instrukcja podobna do
until
moe by przeksztacona w ptl
while
. Opis dojazdu zawiera zdanie: „Jed ni do momentu, kiedy zobaczysz po
prawej stronie koció”. Moemy to w prosty sposób przeksztaci w standardow
ptl
while
— wystarczy, e odwrócimy warunek:
Programowanie
25
While (nie ma kocioa po prawej stronie)
Jed Alej Kociuszki;
0x233 Ptle
for
Kolejn struktur sterujc jest ptla
for
, wykorzystywana wtedy, gdy programista
chce, aby ptla wykonaa okrelon liczb iteracji. Instrukcja dojazdu: „Jed ni
jakie 8 kilometrów” przeksztacona do ptli
for
mogaby wyglda nastpujco:
For (8 iteracji)
Jed prosto 1 kilometr;
W rzeczywistoci ptla
for
jest po prostu ptl
while
z licznikiem. Powysz
instrukcj mona równie dobrze zapisa tak:
Ustaw licznik na 0
While (licznik jest mniejszy od 8)
{
Jed prosto 1 kilometr;
Dodaj 1 do licznika;
}
Skadnia pseudokodu dla ptli
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-
cztkow warto — w tym przypadku 0. Druga sekcja przypomina instrukcj
while
z licznikiem — dopóki licznik spenia podany warunek, kontynuuj ptl. Trzecia
i ostatnia sekcja opisuje, co ma si dzia z licznikiem przy kadej iteracji. W tym
przypadku
i++
jest skróconym sposobem na powiedzenie: „Dodaj 1 do licznika
o nazwie i”.
Zastosowanie wszystkich omówionych struktur sterujcych umoliwia przekszta-
cenie opisu dojazdu ze strony 6 w poniszy pseudokod przypominajcy jzyk C:
Rozpocznij jazd na wschód Alej Kociuszki;
While (po prawej stronie nie ma kocioa)
Jed Alej Kociuszki;
If (ulica zablokowana)
{
Skr w prawo w ul. Moniuszki);
Skr w lewo w ul. Prusa);
Skr w prawo w ul. Orzeszkowej);
}
26
0x200
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 pojcia programistyczne
W kolejnych punktach zostan przedstawione podstawowe pojcia programistyczne.
S one wykorzystywane w wielu jzykach programowania, róni si jedynie pod
wzgldem syntaktycznym. Podczas prezentacji bd integrowane z przykadami
w pseudokodzie przypominajcym skadni jzyka C. Na kocu pseudokod bdzie
bardzo przypomina kod napisany w jzyku C.
0x241
Zmienne
Licznik wykorzystywany w ptli
for
jest rodzajem zmiennej. O zmiennej moemy
myle jak o obiekcie przechowujcym dane, które mog by zmieniane — std
nazwa. Istniej równie zmienne, których wartoci nie s zmieniane, i trafnie nazywa
si je staymi. W przykadzie motoryzacyjnym jako zmienn moemy potraktowa
prdko samochodu, natomiast kolor lakieru samochodu byby sta. W pseudo-
kodzie zmienne s po prostu pojciami abstrakcyjnymi, natomiast w C (i wielu in-
nych jzykach) zmienne przed wykorzystaniem musz by zadeklarowane i naley
okreli ich typ. Jest tak dlatego, e program napisany w C bdzie kompilowany
do postaci programu wykonywalnego. Podobnie jak przepis kucharski, w którym
przed faktycznymi instrukcjami wymieniane s wszystkie potrzebne skadniki, de-
klaracje zmiennych pozwalaj dokona przygotowa przed przejciem do faktycznej
treci programu. Ostatecznie wszystkie zmienne s przechowywane gdzie w pamici,
a ich deklaracje pozwalaj kompilatorowi wydajniej uporzdkowa pami. Jednak
na koniec, mimo wszystkich deklaracji typów zmiennych, wszystko odbywa si
w pamici.
W jzyku C dla kadej zmiennej okrelany jest typ, opisujcy informacje, które
bd przechowywane w tej zmiennej. Do najczciej stosowanych typów zalicza-
my
int
(liczby cakowite),
float
(liczby zmiennoprzecinkowe) i
char
(pojedyncze
znaki). Zmienne s deklarowane poprzez uycie tych sów kluczowych przed nazw
zmiennej, co zobrazowano na poniszym przykadzie.
int a, b;
float k;
char z;
Zmienne
a
i
b
s zadeklarowane jako liczby cakowite,
k
moe przyjmowa warto-
ci zmiennoprzecinkowe (np. 3,14), a
z
powinna przechowywa warto znakow,
Programowanie
27
tak jak
A
lub
w
. Wartoci mona przypisywa zmiennym podczas deklaracji lub
w dowolnym póniejszym momencie. Suy do tego operator
=
.
int a = 13, b;
float k;
char z = 'A';
k = 3.14;
z = 'w';
b = a + 5;
Po wykonaniu powyszych instrukcji zmienna
a
bdzie posiadaa warto 13,
zmienna
k
bdzie zawieraa liczb 3,14, a zmienna
b
bdzie miaa warto 18, po-
niewa 13 plus 5 równa si 18. Zmienne s po prostu sposobem pamitania wartoci,
jednak w jzyku C naley najpierw zadeklarowa typ kadej zmiennej.
0x242 Operatory
arytmetyczne
Instrukcja
b = a + 7
jest przykadem bardzo prostego operatora arytmetycznego.
W jzyku C dla rónych operacji matematycznych wykorzystywane s ponisze
symbole.
Pierwsze cztery operatory powinny wyglda znajomo. Redukcja modulo moe
stanowi nowe pojcie, ale jest to po prostu reszta z dzielenia. Jeeli
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 cakowitymi, po wykonaniu instrukcji
b = a / 5
, zmienna
b
bdzie miaa warto 2, poniewa jest to cz wyniku bdca
liczb cakowit. Do przechowania bardziej poprawnego wyniku, czyli 2,6, musi
zosta uyta zmienna typu
float
(zmiennoprzecinkowa).
Operacja
Symbol
Przykad
Dodawanie
+
b = a + 5
Odejmowanie
-
b = a - 5
Mnoenie
*
b = a * 5
Dzielenie
/
b = a / 5
Redukcja modulo
%
b = a % 5
Aby program korzysta z tych zaoe, musimy mówi w jego jzyku. Jzyk C
dostarcza kilku skróconych form zapisu tych operacji arytmetycznych. Jedna z nich
zostaa przedstawiona wczeniej i jest czsto wykorzystywana w ptlach
for
.
Pene wyraenie
Skrót
Wyjanienie
i = i + 1
i++ lub ++i
Dodaje 1 do wartoci zmiennej.
i = i - 1
i-- lub i--
Odejmuje 1 od wartoci zmiennej.
28
0x200
Te skrócone wyraenia mog by czone z innymi operacjami arytmetycznymi
w celu utworzenia bardziej zoonych wyrae. Wówczas staje si widoczna rónica
midzy
i++
i
++i
. Pierwsze wyraenie oznacza: „Zwiksz warto
i
o 1 po przepro-
wadzeniu operacji arytmetycznej”, natomiast drugie: „Zwiksz warto
i
o 1 przed
przeprowadzeniem operacji arytmetycznej”. Poniszy przykad pozwoli to lepiej
zrozumie.
int a, b;
a = 5
b = a++ * 6;
Po wykonaniu tego zestawu instrukcji zmienna
b
bdzie miaa warto 30, nato-
miast zmienna
a
bdzie miaa warto 6, poniewa skrócony zapis
b = a++ * 6
jest
równowany poniszym instrukcjom.
b = a * 6;
a = a + 1;
Jeeli jednak zostanie uyta instrukcja
b = ++a * 6
, kolejno dodawania zostanie
zmieniona i otrzymamy odpowiednik poniszych instrukcji.
a = a + 1;
b = a * 6;
Poniewa kolejno wykonywania dziaa zmienia si, w powyszym przypad-
ku zmienna
b
bdzie miaa warto 36, natomiast warto zmiennej
a
wci bdzie
wynosia 6.
Czsto w programach musimy zmodyfikowa zmienn w danym miejscu. Przy-
kadowo moemy doda dowoln warto, tak jak 12, i przechowa wynik w tej
samej zmiennej (np.
i = i + 12
) Zdarza si to tak czsto, e równie utworzono
skrócon form tej instrukcji.
Pene wyraenie
Skrót
Wyjanienie
i = i + 12
i+=12
Dodaje okrelon warto do wartoci zmiennej.
i = i - 12
i-=12
Odejmuje okrelon warto od wartoci zmiennej.
i = i * 12
i*=12
Mnoy okrelon warto przez warto zmiennej.
i = i / 12
i/=12
Dzieli warto zmiennej przez okrelon warto.
0x243
Operatory porównania
Zmienne s czsto wykorzystywane w instrukcjach warunkowych wczeniej opisanych
struktur sterujcych, a te instrukcje warunkowe opieraj si na jakiego rodzaju
porównaniu. W jzyku C operatory porównania wykorzystuj skrócon skadni,
która jest stosunkowo powszechna w innych jzykach programowania.
Programowanie
29
Warunek
Symbol
Przykad
Mniejsze ni
<
(a < b)
Wiksze ni
>
(a < b)
Mniejsze lub równe
<=
(a <= b)
Wiksze lub równe
>=
(a >= b)
Równe
==
(a == b)
Nie równa si
!=
(a != b)
Wikszo z tych operatorów jest zrozumiaa, jednake naley zauway, e
w skrócie porównania równoci wykorzystywane s dwa znaki równoci. Jest to wane
rozrónienie, poniewa podwójny znak równoci jest stosowany do sprawdzenia
równoci, natomiast jeden znak równoci jest uywany do przypisania wartoci
zmiennej. Instrukcja
a = 7
oznacza: „Umie w zmiennej
a
warto 7”, natomiast
a == 7
oznacza: „Sprawd, czy warto zmiennej
a
wynosi 7”. (Niektóre jzyki pro-
gramowania, takie jak Pascal, wykorzystuj do przypisywania wartoci zmiennym
nastpujcy znak —
:=
— aby wyeliminowa ryzyko bdnego zrozumienia kodu).
Naley te zwróci uwag, e wykrzyknik zwykle oznacza nie. Ten symbol moe by
wykorzystany do odwrócenia dowolnego wyraenia.
!(a < b) jest równowane (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
Przykad
OR
||
((a < b) || (a < c))
AND
&&
((a < b) && !(a < c))
Przykadowa instrukcja zawierajca dwa mniejsze warunki poczone operato-
rem
OR
zostanie oszacowana jako prawdziwa, jeeli warto zmiennej
a
bdzie mniej-
sza od wartoci zmiennej
b
LUB warto zmiennej
a
bdzie mniejsza od wartoci
zmiennej
c
. Podobnie przykadowa instrukcja zawierajca dwa mniejsze warunki
poczone operatorem
AND
zostanie oszacowana jako prawdziwa, jeeli warto
zmiennej
a
bdzie mniejsza od wartoci zmiennej
b
I warto zmiennej
a
nie bdzie
mniejsza od wartoci zmiennej
c
. Takie instrukcje powinny by grupowane za pomoc
nawiasów i mog zawiera wiele rónych odmian.
Wiele rzeczy moemy sprowadzi do zmiennych, operatorów porównania i struk-
tur sterujcych. W przykadzie myszy poszukujcej poywienia gód moemy przed-
stawi jako zmienn boolowsk o wartoci
true
lub
false
(prawda lub fasz). Oczy-
wicie,
1
oznacza
true
, a
0
—
false
.
30
0x200
While (gód == 1)
{
Szukaj poywienia;
Zjedz poywienie;
}
Oto kolejny skrót czsto wykorzystywany przez programistów i hakerów. Jzyk
C nie zawiera adnych operatorów boolowskich, wic kada warto niezerowa jest
uznawana za
true
, a instrukcja jest szacowana jako
false
, gdy zawiera 0. W rzeczy-
wistoci operatory porównania faktycznie zwracaj 1, jeeli porównanie jest spe-
nione, a w przeciwnym przypadku zwracaj 0. Sprawdzenie, czy zmienna
gód
ma
warto 1, zwróci 1, jeeli
gód
ma warto 1, lub zwróci 0, jeeli gód ma warto 0.
Poniewa program wykorzystuje tylko te dwa przypadki, mona w ogóle pomin
operator porównania.
While (gód)
{
Szukaj poywienia;
Zjedz poywienie;
}
Sprytniejszy program sterujcy zachowaniem myszy z uyciem wikszej liczby
danych wejciowych to przykad, jak mona czy operatory porównania ze zmien-
nymi.
While ((gód) && !(kot_obecny))
{
Szukaj poywienia;
If (!(poywienie_znajduje_si_w_puapce_na_myszy;
Zjedz poywienie;
}
W tym przykadzie zaoono, e dostpne s równie zmienne opisujce obec-
no kota oraz pooenie poywienia z wartociami 1 dla spenienia warunku i 0 dla
przypadku przeciwnego. Wystarczy pamita, e warto niezerowa oznacza prawd,
a warto 0 jest szacowana jako fasz.
0x244
Funkcje
Czasami zdarzaj si zestawy instrukcji, których programista bdzie potrzebowa
kilka razy. Takie instrukcje mona zgrupowa w mniejszym podprogramie, zwanym
funkcj. W innych jzykach funkcje nazywane s take procedurami i podprocedu-
rami. Przykadowo wykonanie skrtu samochodem skada si z kilku mniejszych
instrukcji: wcz odpowiedni kierunkowskaz, zwolnij, sprawd, czy nie nadjedaj
inne samochody, skr kierownic w odpowiednim kierunku itd. Opis trasy z poczt-
ku tego rozdziau zawiera cakiem sporo skrtów, jednake wymienianie wszystkich
Programowanie
31
drobnych instrukcji przy kadym skrcie byoby mudne (i zmniejszaoby czytel-
no kodu). W celu zmodyfikowania sposobu dziaania funkcji moemy przesya do
niej zmienne jako argumenty. W tym przypadku do funkcji jest przesyany kierunek
skrtu.
Function Skrt(zmienna_kierunku)
{
Wcz zmienna_kierunku kierunkowskaz;
Zwolnij;
Sprawd, czy nie nadjedaj inne samochody;
while (nadjedaj inne samochody)
{
Zatrzymaj si;
Obserwuj nadjedajce samochody;
}
Obró kierownic w zmienna_kierunku;
while (skrt nie jest ukoczony)
{
if (prdko < 8 km/h)
Przyspiesz;
}
Obró kierownic do pierwotnego pooenia;
Wycz zmienna_kierunku kierunkowskaz;
}
Funkcja ta zawiera wszystkie instrukcje konieczne do wykonania skrtu. Gdy
program, w którym funkcja taka si znajduje, musi wykona skrt, moe po prostu
wywoa t funkcj. Gdy zostaje wywoana, znajdujce si w niej instrukcje s wy-
konywane z przesanymi argumentami. Nastpnie wykonywanie programu powraca
do momentu za wywoaniem funkcji. Do tej funkcji mona przesa warto lewo
albo prawo, co spowoduje wykonanie skrtu w tym kierunku.
Domylnie w jzyku C funkcje mog zwraca wartoci do elementu wywouj-
cego. Dla osób znajcych matematyk jest to zupenie zrozumiae. Wyobramy sobie
funkcj obliczajc silni — jest oczywiste, e zwraca wynik.
W jzyku C funkcje nie s oznaczane sowem kluczowym
function
. Zamiast tego
s deklarowane przez typ danych zwracanej zmiennej. Taki format bardzo przy-
pomina deklaracj zmiennej. Jeeli funkcja w zamierzeniu ma zwraca liczb ca-
kowit (moe to by np. funkcja obliczajca silni jakiej liczby
x
), jej kod mógby
wyglda nastpujco:
int factorial(int x)
{
int i;
for(i=1; i < x; i++)
x *= i;
return x;
}
32
0x200
Funkcja ta jest deklarowana liczb cakowit, poniewa mnoy kad warto
od 1 do
x
i zwraca wynik, który jest liczb cakowit. Instrukcja
return
na kocu
funkcji przesya zawarto zmiennej
x
i koczy wykonywanie funkcji. Funkcj obli-
czajc silni mona wówczas wykorzysta jak kad zmienn typu
integer
w gównej
czci kadego programu, który „wie” o tej funkcji:
int a=5, b;
b = factorial(a);
Po ukoczeniu wykonywania tego krótkiego programu zmienna
b
bdzie miaa
warto 120, poniewa funkcja
factorial()
zostanie wywoana z argumentem 5
i zwróci 120.
Ponadto w jzyku C kompilator musi „wiedzie” o funkcji, zanim jej uyje. W tym
celu mona 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, zwracajcej okrelony typ danych i przyjmujcej argumenty o okrelonym
typie danych. Faktyczny kod funkcji mona umieci pod koniec programu, ale mona
j wykorzysta w dowolnym miejscu, poniewa kompilator bdzie ju wiedzia o jej
istnieniu. Przykadowy prototyp funkcji
factorial()
mógby wyglda nastpujco:
int factorial(int);
Zwykle prototypy funkcji s umieszczane blisko pocztku 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.
Jeeli funkcja nie posiada wartoci, któr moe zwróci (np. wczeniej opisana
funkcja
skr()
), powinna by zadeklarowana jako typ
void
. Jednake funkcja
skr()
nie posiada jeszcze caej funkcjonalnoci wymaganej przez nasz opis dojazdu. Kady
skrt w opisie dojazdu zawiera zarówno kierunek, jak i nazw ulicy. To oznacza, e
funkcja skrcajca powinna przyjmowa dwie zmienne: kierunek skrtu oraz nazw
ulicy, w któr naley skrci. To komplikuje funkcj skrcajc, poniewa przed
wykonaniem skrtu naley zlokalizowa ulic. Bardziej kompletna funkcja skrcajca,
w której wykorzystano skadni podobn do jzyka C, jest zamieszczona w pseu-
dokodzie poniszego listingu.
void skr(zmienna_kierunku, nazwa_docelowej_ulicy)
{
Szukaj tabliczki z nazw ulicy;
nazwa_biecego_skrzyowania = odczytaj tabliczk z nazw ulicy;
while (nazwa_biecego_skrzyowania != nazwa_docelowej_ulicy)
{
Szukaj innej tabliczki z nazw ulicy;
nazwa_biecego_skrzyowania = odczytaj tabliczk z nazw ulicy;
}
Programowanie
33
Wcz zmienna_kierunku kierunkowskaz;
Zwolnij;
Sprawd, czy nie nadjedaj inne samochody;
while (nadjedaj inne samochody)
{
Zatrzymaj si;
Obserwuj nadjedajce samochody;
}
Obró kierownic w zmienna_kierunku;
while (skrt nie jest ukoczony)
{
if (prdko < 8 km/h)
Przyspiesz;
}
Obró kierownic do pierwotnego pooenia;
Wycz zmienna_kierunku kierunkowskaz;
}
Funkcja ta zawiera sekcj, która wyszukuje odpowiednie skrzyowanie poprzez
odnajdywanie tabliczki z nazw ulicy, odczytywanie z niej nazwy i przechowywanie
jej w zmiennej o nazwie
nazwa_biecego_skrzyowania
. Wyszukiwanie i odczyty-
wanie tabliczek z nazwami odbywa si do momentu odnalezienia docelowej ulicy.
Wówczas wykonywane s pozostae instrukcje skrtu. Moemy teraz zmieni pseu-
dokod z instrukcjami dojazdu, aby wykorzystywa funkcj skrtu.
Rozpocznij jazd na wschód Alej Kociuszki;
while (po prawej stronie nie ma kocioa)
{
Jed Alej Kociuszki;
}
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 czsto wykorzystywane w pseudokodzie, bo zwykle jest on sto-
sowany przez programistów do zarysowania zaoe programu przed napisaniem
kompilowalnego kodu. Poniewa pseudokod nie musi dziaa, nie trzeba wypisywa
penych funkcji — wystarczy jedynie krótki zapisek: Tu zrób co zoonego. Jednak
34
0x200
w jzyku programowania, takim jak C, funkcje s czsto wykorzystywane. Wikszo
prawdziwej uytecznoci C pochodzi ze zbiorów istniejcych funkcji, zwanych
bibliotekami.
0x250 Zaczynamy brudzi sobie rce
Gdy zaznajomilimy si ju troch ze skadni jzyka C i wyjanilimy kilka pod-
stawowych poj programistycznych, przejcie do faktycznego programowania w C
nie jest niczym nadzwyczajnym. Kompilatory jzyka C istniej dla niemal kadego
systemu operacyjnego i architektury procesora, jednak w niniejszej ksice bdziemy
wykorzystywali wycznie system Linux i procesor z rodziny x86. Linux jest dar-
mowym systemem operacyjnym, do którego kady ma dostp, a procesory x86 s
najpopularniejszymi procesorami konsumenckimi na wiecie. Poniewa hacking
wie si gównie z eksperymentowaniem, najlepiej bdzie, jeeli w trakcie lektury
bdziesz dysponowa kompilatorem jzyka C.
Dziki dyskowi LiveCD doczonemu do niniejszej ksiki moesz praktycznie
stosowa przekazywane informacje, jeeli tylko dysponujesz procesorem z rodziny
x86. Wystarczy woy dysk CD do napdu i ponownie uruchomi komputer. Uru-
chomione zostanie rodowisko linuksowe, które nie narusza istniejcego systemu ope-
racyjnego. W tym rodowisku moesz wypróbowywa przykady z ksiki, a take
przeprowadza wasne eksperymenty.
Przejdmy do rzeczy. Program firstprog.c jest prostym fragmentem kodu w C,
wypisujcym 10 razy „Hello world!”.
Listing 2.1. firstprog.c
#include <stdio.h>
int main()
{
int i;
for(i=0; i < 10; i++) // Wykonaj ptl 10 razy.
{
puts("Hello, world!\n"); // Przeka acuch do wyjcia.
}
return 0; // Poinformuj OS, e program zakoczy si bez bdów.
}
Gówne wykonywanie programu napisanego w C rozpoczyna si w funkcji
main()
.
Tekst znajdujcy si za dwoma ukonikami jest komentarzem ignorowanym przez
kompilator.
Pierwszy wiersz moe wprawia w zakopotanie, ale jest to jedynie skadnia
informujca kompilator, aby doczy nagówki dla standardowej biblioteki wej-
cia-wyjcia o nazwie stdio. Ten plik nagówkowy jest dodawany do programu
podczas kompilacji. Znajduje si on w /usr/include/stdio.h i definiuje kilka staych
i prototypów funkcji dla odpowiednich funkcji w standardowej bibliotece we-wy.
Poniewa funkcja
main()
wykorzystuje funkcj
printf()
ze standardowej biblioteki
Programowanie
35
we-wy, wymagany jest prototyp funkcji
printf()
przed jej zastosowaniem. Ten
prototyp funkcji (a take wiele innych) znajduje si w pliku nagówkowym stdio.h.
Sporo moliwoci jzyka C to pochodne jego zdolnoci do rozbudowy oraz wyko-
rzystania bibliotek. Reszta kodu powinna by zrozumiaa i przypomina wczeniej
prezentowany pseudokod. S nawet nawiasy klamrowe, które mona byo pomin.
To, co bdzie si dziao po uruchomieniu tego programu, powinno by oczywiste,
ale skompilujmy go za pomoc GCC, aby si upewni.
GNU Compiler Collection jest darmowym kompilatorem jzyka C, tumaczcym
jzyk C na jzyk maszynowy, zrozumiay dla komputera. W wyniku tumaczenia
powstaje wykonywalny plik binarny, któremu domylnie nadawana jest nazwa a.out.
Czy skompilowany program wykonuje to, co chcielimy?
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
Wikszy obraz
Dobrze, tego wszystkiego mona si nauczy na podstawowym kursie programo-
wania — s to podstawy, ale niezbdne. Wikszo pocztkowych kursów progra-
mowania uczy jedynie, jak czyta i pisa w jzyku C. Prosz mnie le nie zrozumie
— pynna znajomo C jest bardzo przydatna i wystarczy do stania si dobrym
programist, ale jest to tylko cz wikszej caoci. Wikszo programistów uczy
si jzyka z góry do dou i nigdy nie ma szerszego obrazu. Hakerzy s tak dobrzy,
poniewa wiedz, jak wszystkie czci tego wikszego obrazu wchodz ze sob
w interakcje. Aby w programowaniu patrze szerzej, wystarczy sobie zda spraw, e
kod w jzyku C bdzie kompilowany. Kod nie moe nic zrobi, dopóki nie zostanie
skompilowany do wykonywalnego pliku binarnego. Mylenie o kodzie ródowym
w C jak o programie jest czstym bdem wykorzystywanym codziennie przez ha-
kerów. Instrukcje w pliku a.out s zapisane w jzyku maszynowym — podstawowym
jzyku zrozumiaym dla procesora. Kompilatory tumacz jzyk kodu C na jzyk
maszynowy rónych architektur procesorów. W tym przypadku procesor pocho-
dzi z rodziny wykorzystujcej architektur x86. Istniej równie architektury pro-
cesorów Sparc (uywane w stacjach roboczych Sun) oraz architektura PowerPC
36
0x200
(wykorzystywana w macintoshach przed zastosowaniem procesorów Intela). Kada
architektura wymaga innego jzyka maszynowego, wic kompilator jest poredni-
kiem — tumaczy kod w jzyku C na jzyk maszynowy docelowej architektury.
Dopóki skompilowany program dziaa, przecitny programista skupia si wy-
cznie na kodzie ródowym. Jednak haker zdaje sobie spraw, e w rzeczywistoci
wykonywany jest program skompilowany. Majc dogbn wiedz na temat dziaania
procesora, haker moe manipulowa dziaajcymi programami. Widzielimy kod
ródowy naszego pierwszego programu i skompilowalimy go do pliku wykony-
walnego dla architektury x86. Ale jak wyglda ten wykonywalny plik binarny? Na-
rzdzia programistyczne GNU zawieraj program o nazwie objdump, który moe by
uyty do badania skompilowanych plików binarnych. Rozpocznijmy od poznania
kodu maszynowego, na który zostaa przetumaczona 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 rozsdnie si im
przyjrze, wic przekazalimy wyjcie do
grep
z opcj powodujc wywietlenie
tylko 20 wierszy za wyraeniem regularnym
main.:
. Kady bajt jest reprezentowany
w notacji szesnastkowej, systemie liczbowym o podstawie 16. System liczbowy, do
którego jestemy najbardziej przyzwyczajeni, wykorzystuje podstaw 10, a przy
liczbie 10 naley umieci dodatkowy symbol. W systemie szesnastkowym stoso-
wane s cyfry od 0 do 9 reprezentujce 0 do 9, ale take 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 kady moe mie warto prawda lub fasz. To oznacza, e bajt ma
256 (2
8
) moliwych wartoci, wic kady bajt moe by opisany za pomoc dwóch
cyfr w systemie szesnastkowym.
Programowanie
37
Liczby szesnastkowe — rozpoczynajc od 0x8048374 po lewej stronie — s ad-
resami pamici. Bity jzyka 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 rzd domów przy lokalnej ulicy, z których kady posiada wasny
adres, pami moemy traktowa jako rzd bajtów, z których kady ma wasny adres.
Do kadego bajta pamici mona uzyska dostp za pomoc jego adresu, a w tym
przypadku procesor siga do tej czci pamici, aby pobra instrukcje jzyka ma-
szynowego skadajce si na skompilowany program. Starsze procesory Intela z rodzi-
ny x86 wykorzystuj 32-bitowy schemat adresacji, natomiast nowsze — 64-bitowy.
Procesory 32-bitowe posiadaj 2
32
(lub te 4 294 967 296) moliwych adresów, na-
tomiast procesory 64-bitowe dysponuj iloci adresów równ 2
64
(1,84467441 × 10
19
).
Procesory 64-bitowe mog pracowa w trybie kompatybilnoci 32-bitowej, co umo-
liwia im szybkie wykonywanie kodu 32-bitowego.
Bajty szesnastkowe znajdujce si porodku powyszego listingu s instrukcjami
w jzyku maszynowym procesora x86. Oczywicie, te wartoci szesnastkowe s
jedynie reprezentacjami binarnych jedynek i zer, które rozumie procesor. Ale po-
niewa 0101010110001001111001011000001111101100111100001
… przydaje si
tylko procesorowi, kod maszynowy jest wywietlany jako bajty szesnastkowe, a kada
instrukcja jest umieszczana w osobnym wierszu, co przypomina podzia akapitu
na zdania.
Gdy si zastanowimy, okae si, e bajty szesnastkowe same w sobie te nie s
przydatne — tutaj dochodzi do gosu jzyk asembler. Instrukcje po prawej stronie
s zapisane w asemblerze. Asembler jest po prostu zbiorem mnemoników dla od-
powiednich instrukcji jzyka maszynowego. Instrukcja
ret
jest znacznie atwiejsza
do zapamitania i zrozumienia ni 0xc3 czy te 11000011. W przeciwiestwie do C
i innych jzyków kompilowanych, instrukcje asemblera wystpuj w relacji jeden-
do-jednego z instrukcjami odpowiednimi jzyka maszynowego. Oznacza to, e skoro
kada architektura procesora posiada wasny zestaw instrukcji jzyka maszynowego,
kada z nich ma równie wasn odmian asemblera. Jzyk asemblera jest dla pro-
gramistów tylko sposobem reprezentacji instrukcji jzyka maszynowego podawa-
nych procesorowi. Dokadny sposób reprezentacji tych instrukcji maszynowych jest
tylko kwesti konwencji i preferencji. Cho teoretycznie mona stworzy wasn
skadni asemblera dla architektury x86, wikszo osób wykorzystuje jeden z gów-
nych typów — skadni AT&T lub Intel. Kod asemblera przedstawiony na stronie 21
jest zgodny ze skadni AT&T, poniewa niemal wszystkie dezasemblery w Linuksie
domylnie wykorzystuj t skadni. Skadni AT&T mona atwo rozpozna dzi-
ki kakofonii symboli % i $ umieszczanych przed wszystkimi elementami (prosz
ponownie spojrze na stron 21). Ten sam kod mona wywietli w skadni Intela,
dodajc 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
38
0x200
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 $
Uwaam, e skadnia Intela jest znacznie bardziej czytelna i atwiejsza do zro-
zumienia, wic bdzie ona stosowana w niniejszej ksice. Niezalenie od repre-
zentacji jzyka asemblera, polecenia rozumiane przez procesor s cakiem proste.
Instrukcje te skadaj si z operacji i czasem dodatkowych operacji opisujcych cel
i (lub) ródo operacji. Operacje te przenosz dane w pamici, aby wykona jakie-
go rodzaju podstawowe dziaania matematyczne, lub przerywaj dziaanie proce-
sora, by wykona co innego. To jest wszystko, co procesor potrafi wykona. Jednak,
podobnie jak za pomoc stosunkowo niewielkiego alfabetu napisano miliony ksiek,
tak samo, korzystajc z niewielkiego zbioru instrukcji maszynowych, mona utworzy
nieskoczenie wiele programów.
Procesory posiadaj równie wasny zestaw specjalnych zmiennych zwanych
rejestrami. Wikszo instrukcji wykorzystuje rejestry do odczytywania lub zapi-
sywania danych, wic ich poznanie jest niezbdne do zrozumienia instrukcji. Duy
obraz wci si powiksza…
0x252
Procesor x86
Procesor 8086 by pierwszym procesorem w architekturze x86. Zosta opracowany
i wyprodukowany przez firm Intel, która póniej wprowadzaa na rynek bardziej
zaawansowane procesory z tej rodziny: 80186, 80286m 80386 i 80486. Jeeli pami-
tasz, e w latach 80. i 90. ubiegego wieku mówiono o procesorach 386 i 486, chodzio
wówczas o te wanie ukady.
Procesor x86 posiada kilka rejestrów, które s dla niego czym w rodzaju we-
wntrznych zmiennych. Mógbym teraz przeprowadzi abstrakcyjny wykad na temat
rejestrów, ale uwaam, e lepiej zobaczy co na wasne oczy. Narzdzia progra-
mistyczne GNU zawieraj równie debuger o nazwie GDB. Debugery s uywane
przez programistów do wykonywania skompilowanych programów krok po kroku,
badania pamici programu i przegldania rejestrów procesora. Programista, który
Programowanie
39
nigdy nie korzysta z debugera do przyjrzenia si wewntrznym mechanizmom
pracy programu, jest jak siedemnastowieczny badacz, który nigdy nie uywa mi-
kroskopu. Debuger, tak jak mikroskop, pozwala hakerowi na precyzyjne przyjrze-
nie si kodowi maszynowemu, ale moliwoci debugera wykraczaj daleko poza t
metafor. W przeciwiestwie do mikroskopu, debuger umoliwia obejrzenie wy-
konywania programu ze wszystkich stron, wstrzymanie go oraz zmian wszelkich
parametrów.
Poniej GDB zosta uyty do wywietlenia stanu rejestrów procesora tu przed
rozpoczciem 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()
, wic wykonywanie
programu zostanie zatrzymane tu przed uruchomieniem naszego kodu. Nastpnie
GDB uruchamia program, zatrzymuje si w punkcie wstrzymania i wywietla wszyst-
kie rejestry procesora i ich biecy 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 su gównie jako zmienne tymcza-
sowe dla procesora, gdy wykonuje instrukcje kodu maszynowego.
40
0x200
Kolejne cztery rejestry (ESP, EBP, ESI i EDI) s równie rejestrami ogólnego
przeznaczenia, ale czasem nazywa si je wskanikami i indeksami. S to odpowiednio:
wskanik, wskanik bazowy, ródo i przeznaczenie. Pierwsze dwa rejestry s nazy-
wane wskanikami, poniewa przechowuj 32-bitowe adresy, wskazujce do miejsc
w pamici. Te rejestry s istotne dla wykonywania programu i zarzdzania pamici.
Póniej omówi je dokadniej. Kolejne dwa rejestry, z technicznego punktu widzenia
równie wskanikowe, czsto wykorzystywane s do wskazywania róda i celu, gdy
dane musz by odczytane lub zapisane. Istniej instrukcje odczytujce i zapisujce,
które uywaj tych rejestrów, ale przewanie mona je traktowa jak zwyke rejestry
ogólnego przeznaczenia.
Rejestr EIP jest rejestrem wskanika instrukcji, wskazujcym aktualnie wykony-
wan instrukcj. Tak jak dziecko podczas czytania pokazuje sobie palcem poszczegól-
ne sowa, tak procesor odczytuje konkretne instrukcje, korzystajc z EIP jak z palca.
Oczywicie, rejestr ten jest bardzo istotny i bdzie czsto wykorzystywany podczas
debugowania. Aktualnie wskazuje adres pamici 0x804838a.
Pozostay rejestr, EFLAGS, zawiera kilka flag bitowych wykorzystywanych do
porówna oraz segmentacji pamici. Pami jest dzielona na kilka rónych seg-
mentów, co opisz póniej, a rejestr ten pozwala je ledzi. Przewanie moemy je
zignorowa, poniewa rzadko konieczny jest bezporedni dostp do nich.
0x253
Jzyk asembler
Poniewa w niniejszej ksice wykorzystujemy skadni Intela w jzyku asembler,
musimy odpowiednio skonfigurowa narzdzia. W GDB skadni dezasemblera
mona ustawi na intelowsk, wpisujc po prostu
set disassembly intel
lub w skrócie
set dis intel
. Moemy skonfigurowa GDB tak, aby to ustawienie byo aktywne
przy kadym uruchomieniu, umieszczajc 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 skadni Intela, zapoznajmy
si z ni. W skadni Intela instrukcje asemblera maj zwykle ponisz posta:
operacja <cel>, <ródo>
Wartociami docelowymi i ródowymi s rejestr, adres pamici lub warto.
Operacje s zwykle intuicyjnie rozumianymi mnemonikami. Operacja
mov
przenosi
warto ze róda do celu,
sub
odejmuje,
inc
inkrementuje itd. Przykadowo ponisze
instrukcje przenosz warto z ESP do EBP, a nastpnie odejmuj 8 od ESP (zapisu-
jc wynik w ESP).
Programowanie
41
8048375: 89 e5 mov ebp,esp
8048377: 83 ec 08 sub esp,0x8
Dostpne s równie operacje wykorzystywane do sterowanie przebiegiem
wykonywania. Operacja
cmp
suy do porównywania wartoci, a niemal wszystkie
operacje, których nazwa rozpoczyna si liter
j
, su do przeskakiwania do innej
czci kodu (w zalenoci od wyniku porównania). W poniszym przykadzie najpierw
4-bajtowa warto znajdujca w ERB minus 4 jest porównywana z liczb 9. Nastpna
instrukcja jest skrótem od przeskocz, jeeli mniejsze lub równe ni (ang. jump if less
than or equal to), odnoszcym si do wyniku wczeniejszego porównania. Jeeli
warto ta jest mniejsza lub równa 9, wykonywanie przeskoczy do instrukcji znaj-
dujcej si pod adresem 0x8048393. W przeciwnym przypadku wykonywana bdzie
kolejna instrukcja z bezwarunkowym przeskokiem. Jeeli warto nie bdzie 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>
Przykady te pochodz z naszej poprzedniej dezasemblacji. Skonfigurowalimy
debuger do wykorzystywania skadni Intela, wic wykorzystajmy go do kroczenia
przez nasz pierwszy program na poziomie instrukcji asemblera.
W kompilatorze GCC mona poda opcj
–g
, dziki czemu zostan doczone
dodatkowe informacje debugowania, co da GDB dostp do kodu ródowego.
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
42
0x200
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ódowy oraz dezasemblacja funkcji
main()
.
Nastpnie na pocztku
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 pocztku funkcji
main()
, program dociera do punktu wstrzy-
mania i zatrzymuje si przed wykonaniem jakichkolwiek instrukcji z funkcji
main()
.
Nastpnie wywietlana jest warto EIP (wskanika instrukcji).
Naley zwróci uwag, e EIP zawiera adres pamici, wskazujcy instrukcj
w dezasemblacji funkcji
main()
(pogrubionej na listingu). Wczeniejsze instrukcje
(zapisane kursyw), razem nazywane prologiem funkcji, s generowane przez kom-
pilator w celu ustawienia pamici dla reszty zmiennych lokalnych funkcji
main()
.
Jednym z powodów koniecznoci deklaracji zmiennych w jzyku C jest pomoc
w konstrukcji tej czci kodu. Debuger „wie”, e ta cz kodu jest generowana
automatycznie i potrafi j pomin. Prologiem funkcji zajmiemy si póniej, a na
razie, wzorujc si na GDB, pominiemy go.
Debuger GDB zawiera bezporedni metod badania pamici za pomoc pole-
cenia
x
, które jest skrótem od examine. Badanie pamici jest bardzo wan umiejtno-
ci kadego hakera. Wikszo technik hakerskich przypomina sztuczki magiczne
— wydaj si niesamowite i magiczne, dopóki nie poznasz oszustwa. Zarówno
w magii, jak i hakerstwie, jeeli 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 wystpu. Jednak za pomoc debugera,
takiego jak GDB, moemy dokadnie zbada kady aspekt wykonywania programu,
wstrzyma go, przej krok dalej i powtórzy tyle razy, ile potrzeba. Poniewa
dziaajcy program to w wikszoci procesor i segmenty pamici, zbadanie pamici
jest pierwsz z metod przekonania si, co naprawd si dzieje.
Programowanie
43
Polecenie
examine
w GDB moe zosta uyte do przyjrzenia si okrelonym adre-
som pamici na róne sposoby. To polecenie oczekuje dwóch argumentów: miejsca
w pamici, które ma by zbadane, oraz okrelenia sposobu wywietlania zawartoci.
Do okrelania formatu wywietlania równie uywany jest jednoliterowy skrót,
który mona poprzedzi liczb elementów do zbadania. Najczciej wykorzystywane
s ponisze skróty formatujce:
o — wywietla w formacie ósemkowym,
x — wywietla w formacie szesnastkowym,
u — wywietla w standardowym systemie dziesitnym, bez znaków,
t — wywietla w formacie binarnym.
Liter tych mona uy z poleceniem
examine
do zbadania okrelonego adresu
pamici. W poniszym przykadzie wykorzystano biecy adres z rejestru EIP.
Skrócone polecenia s czsto wykorzystywane w GDB i nawet
info register eip
mona 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, moe by zbadana za pomoc adresu prze-
chowywanego w EIP. Debuger umoliwia bezporednie odwoywanie si do reje-
strów, wic
$eip
jest równowane wartoci 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 dziesitnym, co z kolei jest
tym samym, co 00000000111111000100010111000111 w systemie dwójkowym.
Format polecenia
examine
mona równie poprzedzi cyfr, okrelajc 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)
44
0x200
Domylnym rozmiarem jednej jednostki jest 4-bajtowa jednostka zwana sowem.
Rozmiar jednostek wywietlania dla polecenia
examine
moe by zmieniany poprzez
dodanie odpowiedniej litery za liter formatujc. Poprawnymi literami rozmiaru s:
b — pojedynczy bajt,
h — pósowo o rozmiarze dwóch bajtów,
w — sowo o rozmiarze czterech bajtów,
g — sowo podwójne o rozmiarze omiu bajtów.
Wprowadza to pewien zamt, poniewa czasami termin sowo okrela równie
wartoci 2-bajtowe. W takim przypadku podwójne sowo lub DWORD oznacza war-
to 4-bajtow. W ksice tej terminy „sowo” i DWORD bd okrelay wartoci
4-bajtowe. Do okrelania wartoci 2-bajtowych bdzie uywany termin „pósowo”.
Ponisze wyjcie z GDB obrazuje pami wywietlan 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)
Jeeli dobrze si przyjrzysz, zauwaysz w powyszych danych co dziwnego.
Pierwsze polecenie
examine
pokazuje pierwsze osiem bajtów i, oczywicie, polece-
nia
examine
wykorzystujce wiksze jednostki wywietlaj wicej danych. Jednake
wynik pierwszego polecenia pokazuje, e pierwszymi dwoma bajtami s 0xc7 i 0x45,
ale gdy badane jest pósowo w dokadnie tym samym adresie pamici, pokazywana
jest warto 0x45c7, czyli bajty s odwrócone. Ten sam efekt moemy zauway
w przypadku penego 4-bajtowego sowa wywietlanego jako 0x00fc45c7. Jeeli
jednak pierwsze cztery bajty s wywietlane bajt po bajcie maj one kolejno 0xc7,
0x45m 0xfc i 0x00.
Jest tak, poniewa w procesorze x86 wartoci s przechowywane w kolejnoci
little endian, co oznacza, e najmniej istotny bajt jest przechowywany jako pierwszy.
Jeeli np. cztery bajty maj by interpretowane jako jedna warto, bajty musz by
uyte w odwrotnej kolejnoci. Debuger GDB jest na tyle mdry, e wie, jak prze-
chowywane s wartoci, wic gdy badane jest sowo lub pósowo, bajty musz by
odwrócone w celu wywietlenia poprawnych wartoci w ukadzie szesnastkowym.
Przejrzenie tych wywietlanych wartoci w systemie szesnastkowym i jako liczb
dziesitnych bez znaku moe uatwi 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
Programowanie
45
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, dziesitnej. Kalkulator dziaajcy w wierszu polece
bc
posuy do wyka-
zania, e jeeli bajty zostan zinterpretowane w nieprawidowej kolejnoci, otrzy-
mamy w wyniku zupenie nieprawidow warto 3343252480. Kolejno bajtów
w danej architekturze jest istotnym czynnikiem, którego naley by wiadomym.
Cho wikszo narzdzi debugujcych i kompilatorów zajmuje si automatycznie
szczegóami zwizanymi z kolejnoci bajtów, czasem bdziesz musia samodzielnie
manipulowa pamici.
Oprócz konwersji kolejnoci bajtów, GDB moe za pomoc polecenia
examine
wykonywa inne konwersje. Widzielimy ju, e GDB moe dezasemblowa instruk-
cje jzyka maszynowego na zrozumiae dla czowieka instrukcje asemblera. Pole-
cenie
examine
przyjmuje równie liter formatujc
i
(skrót od instruction), co po-
woduje wywietlenie pamici jako dezasemblowanych instrukcji jzyka 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)
46
0x200
W powyszym przykadzie 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 jzyku maszynowym, adnie poddaj si one proce-
sowi dezasemblacji.
Wczeniejsza dezasemblacja za pomoc
objectdump
potwierdza, e siedem bajtów,
które wskazuje EIP, to faktycznie jzyk 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 pamici znajdujcej si pod ad-
resem przechowywanym w rejestrze EBP minus 4. To wanie w tym miejscu pa-
mici przechowywana jest zmienna
i
z jzyka C. Zmienna ta zostaa zadeklarowana
jako liczba cakowita (
int
), wykorzystujca 4 bajty pamici w przypadku procesora
x86. To polecenie po prostu zeruje zmienn
i
dla ptli
for
. Gdybymy teraz zbadali
t pami, zawieraaby ona tylko przypadkowe mieci. Pami w tej lokacji mona
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 bdzie zapisywaa
do adresu o tej wartoci pomniejszonej o 4 — 0xbfffff804. Polecenie
examine
moe
zbada bezporednio ten adres pamici lub wykona odpowiednie obliczenia w locie.
Za pomoc polecenia
równie mona wykona proste dziaania arytmetyczne,
a wynik zostanie zapisany w tymczasowej zmiennej w debugerze. Zmienna ta nosi
nazw
$1
i moe póniej zosta uyta do szybkiego uzyskania dostpu do okrelonego
miejsca w pamici. Kada z przedstawionych wyej metod pozwala uzyska ten
sam rezultat: wywietlenie znalezionych w pamici 4 bajtów mieci, które zostan
wyzerowane po wykonaniu biecej instrukcji.
Uruchommy biec instrukcj, korzystajc z polecenia
nexti
, bdcego skrótem
dla next instruction (nastpna instrukcja). Procesor odczyta instrukcj z EIP, wykona
j i przestawi EIP na kolejn instrukcj.
Programowanie
47
(gdb) nexti
0x0804838b 6 for(i=0; i < 10; i++)
(gdb) x/4xb $1
0xbffff804: 0x00 0x00 0x00 0x00
(gdb) x/dw $1
0xbffff804: 0
(gdb) i r eip
eip 0x804838b 0x804838b <main+23>
(gdb) x/i $eip
0x804838b <main+23>: cmp DWORD PTR [ebp-4],0x9
(gdb)
Zgodnie z oczekiwaniami, poprzednia instrukcja zeruje 4 bajty odnalezione
w adresie EBP minus 4, czyli pamici przydzielonej zmiennej
i
z kodu C. EIP
przechodzi do kolejnej instrukcji. Omówienie kolejnych instrukcji warto przepro-
wadzi cznie.
(gdb) x/10i $eip
0x804838b <main+23>: cmp DWORD PTR [ebp-4],0x9
0x804838f <main+27>: jle 0x8048393 <main+31>
0x8048391 <main+29>: jmp 0x80483a6 <main+50>
0x8048393 <main+31>: mov DWORD PTR [esp],0x8048484
0x804839a <main+38>: call 0x80482a0 <printf@plt>
0x804839f <main+43>: lea eax,[ebp-4]
0x80483a2 <main+46>: inc DWORD PTR [eax]
0x80483a4 <main+48>: jmp 0x804838b <main+23>
0x80483a6 <main+50>: leave
0x80483a7 <main+51>: ret
(gdb)
Pierwsza instrukcja,
cmp
, jest instrukcj porównania, która porównuje zawarto
pamici wykorzystywanej przez zmienn i z kodu C z wartoci 9. Kolejna instrukcja,
jle
, oznacza przeskocz, jeeli mniejsze lub równe (ang. jump if less than equal to).
Wykorzystuje ona wynik poprzedniego porównania (który jest przechowywany
w rejestrze EFLAGS) do przestawienia EIP tak, aby wskazywa inn cz kodu,
jeeli cel poprzedniej operacji porównania jest mniejszy lub równy ródu. W tym
przypadku instrukcja powoduje przeskok do adresu 0x8048393, jeeli warto
przechowywana w pamici dla zmiennej
i
jest mniejsza lub równa wartoci 9. W prze-
ciwnym przypadku EIP przejdzie do kolejnej instrukcji, która jest instrukcj prze-
skoku bezwarunkowego. Powoduje ona przeskok EIP do adresu 0x80483a6. Te trzy
instrukcje cznie tworz struktur sterujc
if-then-else
: jeeli
i
jest mniejsze lub
równe 9, przejd do instrukcji pod adresem 0x8048393; w przeciwnym przypadku
przejd do instrukcji pod adresem 0x80483a6. Pierwszy adres, 0x804893 (pogrubiony),
jest po prostu sta instrukcj przeskoku, a drugi adres — 0x80483a6 (zapisany
kursyw) — znajduje si na kocu funkcji.
Poniewa wiemy, e w lokacji pamici porównywanej z 9 przechowywane jest 0
i wiemy, e 0 jest mniejsze lub równe 9, po wykonaniu kolejnych dwóch instrukcji
EIP powinien wskazywa na 0x8048393.
48
0x200
(gdb) nexti
0x0804838f 6 for(i=0; i < 10; i++)
(gdb) x/i $eip
0x804838f <main+27>: jle 0x8048393 <main+31>
(gdb) nexti
8 printf("Hello world!\n");
(gdb) i r eip
eip 0x8048393 0x8048393 <main+31>
(gdb) x/2i $eip
0x8048393 <main+31>: mov DWORD PTR [esp],0x8048484
0x804839a <main+38>: call 0x80482a0 <printf@plt>
(gdb)
Zgodnie z oczekiwaniami, wczeniejsze dwie instrukcje przeniosy wykonywanie
programu do 0x8048393, co doprowadza do kolejnych dwóch instrukcji. Pierwsza
z nich jest kolejn instrukcj
mov
, która zapisze adres 0x8048484 do adresu pamici
znajdujcego si w rejestrze ESP. Ale co wskazuje ESP?
(gdb) i r esp
esp 0xbffff800 0xbffff800
(gdb)
W tej chwili ESP wskazuje adres pamici 0xbffff800, wic po wykonaniu instrukcji
mov
zostanie tutaj zapisany adres 0x8048484. Ale dlaczego? Co takiego specjalnego
jest w adresie pamici 0x8048484? Jest tylko jeden sposób, aby si o tym przekona.
(gdb) x/2xw 0x8048484
0x8048484: 0x6c6c6548 0x6f57206f
(gdb) x/6xb 0x8048484
0x8048484: 0x48 0x65 0x6c 0x6c 0x6f 0x20
(gdb) x/6ub 0x8048484
0x8048484: 72 101 108 108 111 32
(gdb)
Dowiadczony czytelnik moe zauway w tej pamici co szczególnego, zwasz-
cza w zakresie bajtów. Po nabraniu dowiadczenia w badaniu pamici tego typu
wzorce stan si bardziej oczywiste. Bajty te zawieraj si w zakresie znaków ASCII.
ASCII jest uzgodnionym standardem, który odwzorowuje wszystkie znaki z klawia-
tury (a take kilka innych) na stae liczby. Bajty 0x48, 0x65 i 0x6f odpowiadaj literom
alfabetu przedstawionym w tabeli znaków ASCII (tabela 2.1). Tabel mona znale
na stronach podrcznika ASCII dostpnego w wikszoci systemów Unix po wpi-
saniu
man
ascii
.
Na szczcie, polecenie
examine
w GDB posiada równie moliwo przyjrzenia
si tego typu pamici. Litera formatujca
c
umoliwia automatyczne sprawdzenie
bajta w tablicy ASCII, a litera formatujca
s
powoduje wywietlenie caego acucha
danych znakowych.
Programowanie
49
Tabela 2.1. Tablica znaków ASCII
Oct
Dec
Hex
Char
Oct
Dec
Hex
Char
000
0
00
NUL '\0'
100
64
40
@
001
1
01
SOH
101
65
41
A
002
2
02
STX
102
66
42
B
003
3
03
ETX
103
67
43
C
004
4
04
EOT
104
68
44
D
005
5
05
ENQ
105
69
45
E
006
6
06
ACK
106
70
46
F
007
7
07
BEL '\a'
107
71
47
G
010
8
08
BS '\b'
110
72
48
H
011
9
09
HT '\t'
111
73
49
I
012
10
0A
LF '\n'
112
74
4A
J
013
11
0B
VT '\v'
113
75
4B
K
014
12
0C
FF '\f'
114
76
4C
L
015
13
0D
CR '\r'
115
77
4D
M
016
14
0E
SO
116
78
4E
N
017
15
0F
SI
117
79
4F
O
020
16
10
DLE
120
80
50
P
021
17
11
DC1
121
81
51
Q
022
18
12
DC2
122
82
52
R
023
19
13
DC3
123
83
53
S
024
20
14
DC4
124
84
54
T
025
21
15
NAK
125
85
55
U
026
22
16
SYN
126
86
56
V
027
23
17
ETB
127
87
57
W
030
24
18
CAN
130
88
58
X
031
25
19
EM
131
89
59
Y
032
26
1A
SUB
132
90
5A
Z
033
27
1B
ESC
133
91
5B
[
034
28
1C
FS
134
92
5C
\ '\\'
035
29
1D
GS
135
93
5D
]
036
30
1E
RS
136
94
5E
^
037
31
1F
US
137
95
5F
_
040
32
20
SPACE
140
96
60
041
33
21
!
141
97
61
a
042
34
22
"
142
98
62
b
043
35
23
#
143
99
63
c
50
0x200
Tabela 2.1. Tablica znaków ASCII — cig dalszy
Oct
Dec
Hex
Char
Oct
Dec
Hex
Char
044
36
24
$
144
100
64
d
045
37
25
%
145
101
65
e
046
38
26
&
146
102
66
f
047
39
27
'
147
103
67
g
050
40
28
(
150
104
68
h
051
41
29
)
151
105
69
i
052
42
2A
*
152
106
6A
j
053
43
2B
+
153
107
6B
k
054
44
2C
,
154
108
6C
l
055
45
2D
-
155
109
6D
m
056
46
2E
.
156
110
6E
n
057
47
2F
/
157
111
6F
o
060
48
30
0
160
112
70
p
061
49
31
1
161
113
71
q
062
50
32
2
162
114
72
r
063
51
33
3
163
115
73
s
064
52
34
4
164
116
74
t
065
53
35
5
165
117
75
u
066
54
36
6
166
118
76
v
067
55
37
7
167
119
77
w
070
56
38
8
170
120
78
x
071
57
39
9
171
121
79
y
072
58
3A
:
172
122
7A
z
073
59
3B
;
173
123
7B
{
074
60
3C
<
174
124
7C
|
075
61
3D
=
175
125
7D
}
076
62
3E
>
176
126
7E
~
077
63
3F
?
177
127
7F
DEL
(gdb) x/6cb 0x8048484
0x8048484: 72 'H' 101 'e' 108 'l' 108 'l' 111 'o' 32 ' '
(gdb) x/s 0x8048484
0x8048484: "Hello, world!\n"
(gdb)
Polecenia te pokazuj, e pod adresem 0x8048484 przechowywany jest acuch
danych
"Hello, world!\n"
. Ten acuch jest argumentem funkcji
printf()
, co wska-
zuje, e przeniesienie adresu tego acucha do adresu przechowywanego w ESP
Programowanie
51
(0x8048484) ma co wspólnego z t funkcj. Poniszy wynik z
gdb
pokazuje adres
acucha danych przenoszony do adresu, który wskazuje ESP.
(gdb) x/2i $eip
0x8048393 <main+31>: mov DWORD PTR [esp],0x8048484
0x804839a <main+38>: call 0x80482a0 <printf@plt>
(gdb) x/xw $esp
0xbffff800: 0xb8000ce0
(gdb) nexti
0x0804839a 8 printf("Hello, world!\n");
(gdb) x/xw $esp
0xbffff800: 0x08048484
(gdb)
Kolejna instrukcja wywouje funkcj
printf()
. Wypisuje ona acuch danych.
Wczeniej instrukcje stanowiy przygotowania do wywoania tej funkcji, a wynik
tego wywoania zosta pogrubiony poniej.
(gdb) x/i $eip
0x804839a <main+38>: call 0x80482a0 <printf@plt>
(gdb) nexti
Hello, world!
6 for(i=0; i < 10; i++)
(gdb)
Korzystajc dalej z debugera, przyjrzyjmy si kolejnym dwóm instrukcjom.
Ponownie wikszy sens ma opisanie ich razem.
(gdb) x/2i $eip
0x804839f <main+43>: lea eax,[ebp-4]
0x80483a2 <main+46>: inc DWORD PTR [eax]
(gdb)
Te dwie instrukcje po prostu zwikszaj warto zmiennej
i
o 1. Instrukcja
lea
,
akronim od Load Effective Address (wczytaj efektywny adres), wczytuje znany ju
adres EBP minus 4 do rejestru EAX. Wykonanie tej instrukcji przedstawiono poniej.
(gdb) x/ i $eip
0x804839f <main+43>: lea eax,[ebp-4]
(gdb) print $ebp - 4
$2 = (void *) 0xbffff804
(gdb) x/x $2
0xbffff804: 0x00000000
(gdb) i r eax
eax 0xd 13
(gdb) nexti
0x080483a2 6 for(i=0; i < 10; i++)
(gdb) i r eax
eax 0xbffff804 -1073743868
(gdb) x/xw $eax
52
0x200
0xbffff804: 0x00000000
(gdb) x/dw $eax
0xbffff804: 0
(gdb)
Kolejna instrukcja,
inc
, zwikszy warto znalezion pod tym adresem (przecho-
wywan teraz w rejestrze EAX) o 1. Wykonanie tej instrukcji równie przedstawiono
poniej.
(gdb) x/i $eip
0x80483a2 <main+46>: inc DWORD PTR [eax]
(gdb) x/dw $eax
0xbffff804: 0
(gdb) nexti
0x080483a4 6 for(i=0; i < 10; i++)
(gdb) x/dw $eax
0xbffff804: 1
(gdb)
W wyniku warto przechowywana pod adresem pamici EBP minus 4 (0xbfffff804)
jest powikszana o 1. To zachowanie odpowiada fragmentowi kodu C, w którym
warto zmiennej
i
jest powikszana w ptli
for
.
Kolejn instrukcj jest bezwarunkowy przeskok.
(gdb) x/i $eip
0x80483a4 <main+48>: jmp 0x804838b <main+23>
(gdb)
Instrukcja po wykonaniu przekieruje program z powrotem do instrukcji spod
adresu 0x804838b. Dokonuje tego poprzez proste ustawienie EIP na t warto.
Spogldajc ponownie na pen dezasemblacj, powiniene stwierdzi, które
czci kodu C zostay skompilowane do których instrukcji jzyka maszynowego.
(gdb) disass main
Dump of assembler code for function main:
0x08048374 <main+0>: push ebp
0x08048375 <main+1>: mov ebp,esp
0x08048377 <main+3>: sub esp,0x8
0x0804837a <main+6>: and esp,0xfffffff0
0x0804837d <main+9>: mov eax,0x0
0x08048382 <main+14>: sub esp,eax
0x08048384 <main+16>: mov DWORD PTR [ebp-4],0x0
0x0804838b <main+23>: cmp DWORD PTR [ebp-4],0x9
0x0804838f <main+27>: jle 0x8048393 <main+31>
0x08048391 <main+29>: jmp 0x80483a6 <main+50>
0x08048393 <main+31>: mov DWORD PTR [esp],0x8048484
0x0804839a <main+38>: call 0x80482a0 <printf@plt>
0x0804839f <main+43>: lea eax,[ebp-4]
0x080483a2 <main+46>: inc DWORD PTR [eax]
0x080483a4 <main+48>: jmp 0x804838b <main+23>